summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/core/xbbcd.cpp301
-rwxr-xr-xsrc/core/xbblockread.cpp279
-rwxr-xr-xsrc/core/xbdate.cpp867
-rwxr-xr-xsrc/core/xbdbf.cpp4533
-rwxr-xr-xsrc/core/xbdbf3.cpp768
-rwxr-xr-xsrc/core/xbdbf4.cpp885
-rwxr-xr-xsrc/core/xbexp.cpp2721
-rwxr-xr-xsrc/core/xbexpnode.cpp562
-rwxr-xr-xsrc/core/xbfields.cpp1189
-rwxr-xr-xsrc/core/xbfile.cpp2217
-rwxr-xr-xsrc/core/xbfilter.cpp544
-rwxr-xr-xsrc/core/xbfuncs.cpp851
-rwxr-xr-xsrc/core/xbixbase.cpp789
-rwxr-xr-xsrc/core/xbixmdx.cpp4844
-rwxr-xr-xsrc/core/xbixndx.cpp2834
-rwxr-xr-xsrc/core/xbixtdx.cpp661
-rwxr-xr-xsrc/core/xblog.cpp227
-rwxr-xr-xsrc/core/xbmemo.cpp219
-rwxr-xr-xsrc/core/xbmemo3.cpp585
-rwxr-xr-xsrc/core/xbmemo4.cpp1336
-rwxr-xr-xsrc/core/xbssv.cpp658
-rwxr-xr-xsrc/core/xbstring.cpp2000
-rwxr-xr-xsrc/core/xbtag.cpp121
-rwxr-xr-xsrc/core/xbtblmgr.cpp312
-rwxr-xr-xsrc/core/xbuda.cpp78
-rwxr-xr-xsrc/core/xbxbase.cpp803
-rwxr-xr-xsrc/examples/xb_ex_date.cpp212
-rwxr-xr-xsrc/examples/xb_ex_expression.cpp231
-rwxr-xr-xsrc/examples/xb_ex_log.cpp77
-rwxr-xr-xsrc/examples/xb_ex_sql.cpp111
-rwxr-xr-xsrc/examples/xb_ex_ssv.cpp136
-rwxr-xr-xsrc/examples/xb_ex_string.cpp381
-rwxr-xr-xsrc/examples/xb_ex_v3_create_dbf.cpp110
-rwxr-xr-xsrc/examples/xb_ex_v3_upd_dbf.cpp326
-rwxr-xr-xsrc/examples/xb_ex_v4_create_dbf.cpp100
-rwxr-xr-xsrc/examples/xb_ex_v4_upd_dbf.cpp294
-rwxr-xr-xsrc/include/xbase.h100
-rwxr-xr-xsrc/include/xbbcd.h93
-rwxr-xr-xsrc/include/xbblkread.h70
-rwxr-xr-xsrc/include/xbconfig.h.in128
-rwxr-xr-xsrc/include/xbcrix.cpp292
-rwxr-xr-xsrc/include/xbdate.h125
-rwxr-xr-xsrc/include/xbdbf.h568
-rwxr-xr-xsrc/include/xbexp.h216
-rwxr-xr-xsrc/include/xbexpnode.h120
-rwxr-xr-xsrc/include/xbfile.h198
-rwxr-xr-xsrc/include/xbfilter.h78
-rwxr-xr-xsrc/include/xbindex.h613
-rwxr-xr-xsrc/include/xbindex.h.nope605
-rwxr-xr-xsrc/include/xblnklst.h257
-rwxr-xr-xsrc/include/xblnklstord.h367
-rwxr-xr-xsrc/include/xblnknod.h94
-rwxr-xr-xsrc/include/xblog.h67
-rwxr-xr-xsrc/include/xbmemo.h224
-rwxr-xr-xsrc/include/xbretcod.h98
-rwxr-xr-xsrc/include/xbsql.h161
-rwxr-xr-xsrc/include/xbssv.h194
-rwxr-xr-xsrc/include/xbstring.h188
-rwxr-xr-xsrc/include/xbtag.h74
-rwxr-xr-xsrc/include/xbtblmgr.h63
-rwxr-xr-xsrc/include/xbtypes.h55
-rwxr-xr-xsrc/include/xbuda.h52
-rwxr-xr-xsrc/include/xbxbase.h235
-rwxr-xr-xsrc/sql/xbalttbl.cpp120
-rwxr-xr-xsrc/sql/xbcrix.cpp301
-rwxr-xr-xsrc/sql/xbcrtbl.cpp274
-rwxr-xr-xsrc/sql/xbdelete.cpp148
-rwxr-xr-xsrc/sql/xbdrpix.cpp155
-rwxr-xr-xsrc/sql/xbdrptbl.cpp129
-rwxr-xr-xsrc/sql/xbinsert.cpp190
-rwxr-xr-xsrc/sql/xbselect.cpp97
-rwxr-xr-xsrc/sql/xbset.cpp90
-rwxr-xr-xsrc/sql/xbsql.cpp176
-rwxr-xr-xsrc/sql/xbstmt.cpp679
-rwxr-xr-xsrc/tests/tstfuncs.cpp583
-rwxr-xr-xsrc/tests/xb_test_bcd.cpp127
-rwxr-xr-xsrc/tests/xb_test_blockread.cpp168
-rwxr-xr-xsrc/tests/xb_test_date.cpp214
-rwxr-xr-xsrc/tests/xb_test_dbf_v3_memos.cpp259
-rwxr-xr-xsrc/tests/xb_test_dbf_v3_nomemos.cpp327
-rwxr-xr-xsrc/tests/xb_test_dbf_v4_memos.cpp357
-rwxr-xr-xsrc/tests/xb_test_dbf_v4_nomemos.cpp373
-rwxr-xr-xsrc/tests/xb_test_expnode.cpp123
-rwxr-xr-xsrc/tests/xb_test_expression.cpp816
-rwxr-xr-xsrc/tests/xb_test_file.cpp209
-rwxr-xr-xsrc/tests/xb_test_filter.cpp193
-rwxr-xr-xsrc/tests/xb_test_funcs.cpp296
-rwxr-xr-xsrc/tests/xb_test_linklist.cpp342
-rwxr-xr-xsrc/tests/xb_test_lock.cpp1066
-rwxr-xr-xsrc/tests/xb_test_lock2.cpp233
-rwxr-xr-xsrc/tests/xb_test_log.cpp111
-rwxr-xr-xsrc/tests/xb_test_mdx.cpp305
-rwxr-xr-xsrc/tests/xb_test_ndx.cpp450
-rwxr-xr-xsrc/tests/xb_test_sql.cpp270
-rwxr-xr-xsrc/tests/xb_test_string.cpp459
-rwxr-xr-xsrc/tests/xb_test_tblmgr.cpp125
-rwxr-xr-xsrc/tests/xb_test_tdx.cpp162
-rwxr-xr-xsrc/tests/xb_test_uda.cpp97
-rwxr-xr-xsrc/tests/xb_test_xbase.cpp217
-rwxr-xr-xsrc/utils/xb_cfg_check.cpp411
-rwxr-xr-xsrc/utils/xb_copydbf.cpp100
-rwxr-xr-xsrc/utils/xb_dbfutil.cpp3836
-rwxr-xr-xsrc/utils/xb_deletall.cpp85
-rwxr-xr-xsrc/utils/xb_dumpdbt.cpp109
-rwxr-xr-xsrc/utils/xb_dumprecs.cpp98
-rwxr-xr-xsrc/utils/xb_execsql.cpp130
-rwxr-xr-xsrc/utils/xb_import.cpp242
-rwxr-xr-xsrc/utils/xb_pack.cpp84
-rwxr-xr-xsrc/utils/xb_tblinfo.cpp104
-rwxr-xr-xsrc/utils/xb_undelall.cpp82
110 files changed, 54019 insertions, 0 deletions
diff --git a/src/core/xbbcd.cpp b/src/core/xbbcd.cpp
new file mode 100755
index 0000000..f86e74f
--- /dev/null
+++ b/src/core/xbbcd.cpp
@@ -0,0 +1,301 @@
+/* xbbcd.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+ BCD class
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_INDEX_SUPPORT
+///@cond DOXYOFF
+namespace xb{
+
+
+
+xbBool bcdBitSet( unsigned char c, xbInt16 iBitNo ){
+ return c & 1 << iBitNo;
+}
+void bcdBitDump( unsigned char c ){
+ for( int i = 7; i >= 0; i-- )
+ std::cout << (bcdBitSet( c, i ) ? 1 : 0);
+}
+void bcdBitDump( char c ){
+ bcdBitDump( (unsigned char) c );
+}
+
+
+/***********************************************************************/
+void xbBcd::ctor(){
+ memset( &bcd, 0x00, sizeof( xbBcdStruct ));
+}
+/***********************************************************************/
+xbBcd::xbBcd( xbDouble d ) {
+ Set( d );
+}
+/***********************************************************************/
+xbBcd::xbBcd( const xbString &sNumIn ) {
+ Set( sNumIn );
+}
+/***********************************************************************/
+xbBcd::xbBcd( const void *vBcdIn ) {
+ memcpy( &bcd, vBcdIn, sizeof( xbBcdStruct ));
+}
+/***********************************************************************/
+void xbBcd::Set( xbDouble d ) {
+ ctor();
+ xbString s( d );
+ StringToBcd( s );
+}
+/***********************************************************************/
+void xbBcd::Set( const xbString &sNumIn ) {
+ ctor();
+ StringToBcd( sNumIn );
+}
+/***********************************************************************/
+void xbBcd::Set( const void *vBcdIn ) {
+ memcpy( &bcd, vBcdIn, sizeof( xbBcdStruct ));
+}
+/***********************************************************************/
+void xbBcd::StringToBcd( const xbString &sIn )
+{
+
+ unsigned char cEdc = 0; // encoded digit count
+ xbUInt32 iPos; // current position in source string
+ xbBool bDecHit = xbFalse; // decimal position hit?
+ unsigned char *p = bcd.cData; // pointer to cData
+ xbInt16 iBytePos = 0; // next load position in xbs structure
+ xbInt16 iBcdDataPos = 0; // current position in output structure
+ xbByteSplit xbs; // combiner
+
+
+ ctor();
+ xbString sNum( sIn );
+ xbBool bSign = xbFalse;
+
+ sNum.Trim();
+ if( sNum[1] == '-' ){
+ bSign = 1;
+ sNum.ZapLeadingChar( '-' );
+ }
+ sNum.ZapLeadingChar( '0' );
+
+ xbInt16 iDecPos = sNum.Pos( '.' );
+ if( iDecPos > 0 ){
+ sNum.ZapTrailingChar( '0' );
+ cEdc = (unsigned char) (sNum.Len() - 1);
+ } else {
+ cEdc = (unsigned char) (sNum.Len());
+ }
+ if( cEdc > 31 ) cEdc = 31; // max 5 bit number
+
+
+ if( sNum[1] == '.' ){
+ iPos = 2;
+ bDecHit = xbTrue;
+ while( sNum[iPos] == '0' && iPos <= sNum.Len()){
+ bcd.cSigDigits--;
+ iPos++;
+ }
+ } else {
+ iPos = 1;
+ }
+
+ while( iPos <= sNum.Len() ){
+ if( sNum[iPos] == '.' )
+ bDecHit = true;
+ else{
+ if( !bDecHit ){
+ bcd.cSigDigits++;
+ }
+ if( iBytePos++ == 0 ){
+ xbs.c2 = (unsigned) sNum[iPos] - 0x30;
+ } else {
+ xbs.c1 = (unsigned) sNum[iPos] - 0x30;
+ iBytePos = 0;
+ if( iBcdDataPos++ < 10 ){
+ memcpy( p++, &xbs, 1 );
+ xbs.c1 = 0x00;
+ }
+ }
+ }
+ iPos++;
+ }
+
+ if( iBytePos == 1 && iBcdDataPos < 10 ){
+ memcpy( p, &xbs, 1 );
+ }
+
+ bcd.cSigDigits += 52;
+ bcd.cEncDigits = cEdc << 2;
+ bcd.cEncDigits = bcd.cEncDigits | 0x01;
+ if( bSign )
+ bcd.cEncDigits = bcd.cEncDigits | 0x80;
+
+}
+/***********************************************************************/
+void xbBcd::ToChar( char * cOut ){
+
+ memcpy( cOut, &bcd, sizeof( xbBcdStruct ));
+}
+/***********************************************************************/
+void xbBcd::ToDouble( xbDouble &d ){
+ xbString s;
+ ToString( s );
+ d = atof( s.Str());
+}
+/***********************************************************************/
+void xbBcd::ToString( xbString &sStringOut ){
+
+// printf( "\n\n\nToString " );
+// printf( "Sig digits [%d] EncodedDigits [%d] sign [%d] sizeof struct [%d]\n",
+// bcd.cSigDigits - 52 , bcd.EncDigits, bcd.Sign, sizeof( xbBcdStruct ));
+
+// this routine converts a bcd numeric key value to a base 10 number in a string
+
+
+ xbBool bHasDot = false;
+ xbInt16 iSig = bcd.cSigDigits - 52;
+ xbByteSplit xbs;
+ unsigned char *p = bcd.cData;
+ unsigned char c;
+
+// xbInt16 iEnc = bcd.cEncDigits;
+ xbInt16 iEnc = GetEncDigitsNoSign();
+
+ // set the sign
+ // if( bcd.Sign )
+ if( bcd.cEncDigits >> 7 )
+ sStringOut = "-";
+ else
+ sStringOut = "";
+
+ // do add any needed zeroes after the decimal
+ if( iSig <= 0 ){
+ if( iEnc > 0 ){
+ sStringOut.Append( "." );
+ bHasDot = true;
+ }
+ for( xbInt16 i = iSig; i < 0; i++ )
+ sStringOut.Append( "0" );
+ }
+
+ // do the encoded digits
+ while( iEnc > 0 ){
+ if( iSig == 0 && !bHasDot )
+ sStringOut.Append( "." );
+ c = *p++;
+ memcpy( &xbs, &c, 1 );
+ c = xbs.c2 + 0x30;
+ sStringOut.Append((char) c );
+ iSig--;
+ iEnc--;
+ if( iEnc > 0 ){
+ if( iSig == 0 && !bHasDot )
+ sStringOut.Append( "." );
+ c = xbs.c1 + 0x30;
+ sStringOut.Append((char) c );
+ iSig--;
+ iEnc--;
+ }
+ }
+ // do the trailing zeroes
+ while( iSig-- > 0 )
+ sStringOut.Append( "0" );
+ if( sStringOut == "" )
+ sStringOut = "0";
+
+}
+
+/***********************************************************************/
+xbInt16 xbBcd::Compare( xbDouble d ){
+ xbBcd bcdIn( d );
+
+ xbString s;
+ bcdIn.ToString( s );
+ return Compare( bcdIn );
+}
+
+/***********************************************************************/
+xbInt16 xbBcd::Compare( const xbBcd &bcdIn ){
+ // if this == bcdIn return 0
+ // if this < bcdIn return -1
+ // if this > bcdIn return 1
+
+
+ xbInt16 iRc = 0;
+// if( bcd.Sign != bcdIn.GetSign() ){
+// bcd.Sign > 0 ? iRc = -1 : iRc = 1;
+
+ if( (unsigned)(bcd.cEncDigits >> 7 ) != bcdIn.GetSign() ){
+ (bcd.cEncDigits >> 7 ) > 0 ? iRc = -1 : iRc = 1;
+ return iRc;
+ }
+
+ if( (unsigned) bcd.cSigDigits != bcdIn.GetSigDigits()){
+ // if( !bcd.Sign ){ // positive numbers
+ if( !(bcd.cEncDigits >> 7 )){ // positive numbers
+ if( (unsigned) bcd.cSigDigits > bcdIn.GetSigDigits())
+ return 1;
+ else
+ return -1;
+ } else { // negative numbers
+ if( (unsigned) bcd.cSigDigits > bcdIn.GetSigDigits())
+ return -1;
+ else
+ return 1;
+ }
+ }
+
+// iRc = xbXBase::xbMemcmp( bcd.cData, bcdIn.GetData(), (size_t)((bcd.cEncDigits + 1) / 2) );
+ iRc = xbXBase::xbMemcmp( bcd.cData, bcdIn.GetData(), (size_t)((GetEncDigitsNoSign() + 1) / 2) );
+ if( iRc == 0 )
+ return 0;
+ // else if((!bcd.Sign && iRc > 0) || (bcd.Sign && iRc < 0 ))
+ else if((!(bcd.cEncDigits >> 7) && iRc > 0) || ((bcd.cEncDigits >> 7) && iRc < 0 ))
+ return 1;
+ else
+ return -1;
+}
+
+/***********************************************************************/
+unsigned char xbBcd::GetEncDigitsNoSign() const {
+ unsigned char c = bcd.cEncDigits << 1;
+ return c >> 3;
+}
+/***********************************************************************/
+unsigned xbBcd::GetSign() const {
+ //return bcd.Sign;
+ return (unsigned) bcd.cEncDigits >> 7;
+}
+/***********************************************************************/
+unsigned xbBcd::GetSigDigits() const {
+ return bcd.cSigDigits;
+}
+/***********************************************************************/
+unsigned xbBcd::GetActualSigDigits() const {
+ return bcd.cSigDigits - (xbUInt32) 52;
+}
+/***********************************************************************/
+const unsigned char * xbBcd::GetData() const {
+ const unsigned char *p = bcd.cData;
+ return p;
+}
+/***********************************************************************/
+const void * xbBcd::GetBcd() const {
+ return &bcd;
+}
+} /* namespace */
+///@endcond DOXYOFF
+#endif /* XB_INDEX_SUPPORT */
+
diff --git a/src/core/xbblockread.cpp b/src/core/xbblockread.cpp
new file mode 100755
index 0000000..7e2c5fc
--- /dev/null
+++ b/src/core/xbblockread.cpp
@@ -0,0 +1,279 @@
+/* xbblockread.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+This module handles block read methods. Block reading is used for performance improvement
+during sequential access processing.
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_BLOCKREAD_SUPPORT
+
+namespace xb{
+
+/************************************************************************/
+xbBlockRead::xbBlockRead( xbDbf * d ) {
+ pBlock = NULL;
+ ulBlkSize = 0;
+ ulFirstBlkRec = 0;
+ ulRecCnt = 0;
+ ulMaxRecs = 0;
+ bEof = xbFalse;
+ this->dbf = d;
+ tFmTime = 0;
+}
+/************************************************************************/
+xbBlockRead::~xbBlockRead(){
+ if( pBlock ){
+ free( pBlock );
+ pBlock = NULL;
+ }
+}
+
+/************************************************************************/
+//! @brief Dump read block internals to stdout.
+/*!
+ Dump the current read block internals to stdout.
+*/
+
+
+#ifdef XB_DEBUG_SUPPORT
+void xbBlockRead::DumpReadBlockInternals(){
+
+ xbUInt32 ulRecCnt;
+ dbf->GetRecordCnt( ulRecCnt );
+
+ std::cout << "------- DumpBlockInternals ---------" << std::endl;
+ std::cout << "Dflt Blk Size = [" << dbf->GetXbasePtr()->GetDefaultBlockReadSize()
+ << "]" << std::endl;
+ std::cout << "Dbf Record Count = [" << ulRecCnt << "]" << std::endl;
+ std::cout << "Dbf Record Len = [" << dbf->GetRecordLen() << "]" << std::endl;
+ std::cout << "ulBlkSize = [" << ulBlkSize << "]" << std::endl;
+ std::cout << "ulMaxRecs = [" << ulMaxRecs << "]" << std::endl;
+ std::cout << "ulFirstBlkRec = [" << ulFirstBlkRec << "]" << std::endl;
+ std::cout << "ulRecCnt = [" << ulRecCnt << "]" << std::endl;
+ std::cout << "bEof = [" << bEof << "]" << std::endl;
+}
+#endif // XB_DEBUG_SUPPORT
+/************************************************************************/
+//! @brief Get the first record number in the current block.
+/*!
+ Retrieve the first record numer in the current block.<br>
+
+ \returns First record number in the current block.
+*/
+
+xbUInt32 xbBlockRead::GetBlockFirstRecNo() const{
+ return ulFirstBlkRec;
+}
+/************************************************************************/
+//! @brief Get record for specified record number.
+/*!
+ Retrieve a record from read block buffer and copy it into the record buffer.
+ If the current record is not in the current block, the routine calls
+ GetBlockForRecNo to load the currect block from disk.<br><br>
+ For performance reasons, this method assumes a valid record number has been
+ passed.<br><br>
+
+ \param ulRecNo - Record number to retrieve.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbBlockRead::GetRecord( xbUInt32 ulRecNo ){
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ // std::cout << "xbBlockRead::GetRecord( " << ulRecNo << ")\n";
+
+ try{
+ if( !( ulRecNo >= ulFirstBlkRec && ulRecNo < (ulFirstBlkRec + ulRecCnt))){
+ if(( iRc = GetBlockForRecNo( ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ char *s = pBlock;
+ s += (ulRecNo - ulFirstBlkRec) * dbf->GetRecordLen();
+ char *t = dbf->RecBuf;
+ xbUInt32 ulRecLen = dbf->GetRecordLen();
+ for( xbUInt32 l = 0; l < ulRecLen; l++ ){
+ *t = *s;
+ t++;
+ s++;
+ }
+ dbf->ulCurRec = ulRecNo;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbBlockRead::GetBlockForRecNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Get record for specified record number.
+/*!
+ Retrieve a block containing specified record. This routine calculates the
+ correct block in the DBF file, updates the internal block fields and retrieves
+ the block of records from disk and loads into the block buffer.<br><br>
+
+ \param ulRecNo - Record number to retrieve.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbBlockRead::GetBlockForRecNo( xbUInt32 ulRecNo ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbUInt32 ulDbfRecCnt;
+
+ if(( iRc = dbf->GetRecordCnt( ulDbfRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // calc to determine block number for the requested record, 0 based offset
+ xbUInt32 ulBlockNo = (xbUInt32)(ulRecNo / ulMaxRecs);
+ if( ulRecNo % ulMaxRecs == 0 ) ulBlockNo--;
+
+ // calc the first record
+ ulFirstBlkRec = (ulBlockNo * ulMaxRecs);
+
+ // calc the record count
+ if(( ulFirstBlkRec + ulMaxRecs) > ulDbfRecCnt ){
+ ulRecCnt = ulDbfRecCnt - ulFirstBlkRec;
+ bEof = xbTrue;
+ } else {
+ ulRecCnt = ulMaxRecs;
+ bEof = xbFalse;
+ }
+
+ // position accordingly
+ xbInt64 ulStartPos = dbf->GetHeaderLen() + ((xbInt64) ulFirstBlkRec * dbf->GetRecordLen());
+ if(( dbf->xbFseek( ulStartPos, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+
+ if(( dbf->GetFileMtime( tFmTime )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ // read it
+ if(( dbf->xbFread( (void *) pBlock, ulRecCnt * dbf->GetRecordLen(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ ulFirstBlkRec++; // zero offset in the routine, regular record number from ths point forward
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbBlockRead::GetBlockForRecNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Get the current block size.
+/*!
+ Retrieve the current block size.<br><br>
+
+ \returns Current Block Size.
+*/
+
+xbUInt32 xbBlockRead::GetBlockSize() const{
+ return ulBlkSize;
+}
+
+/************************************************************************/
+//! @brief Get the current block record count.
+/*!
+ Retrieve the current number of records loaded in the block.<br><br>
+
+ \returns Current Block Size.
+*/
+
+xbUInt32 xbBlockRead::GetBlockRecCnt() const {
+ return ulRecCnt;
+}
+
+/************************************************************************/
+//! @brief Init the block processing for a iven DBF file.
+/*!
+ Initialize the settings for a given DBF file.<br>
+ This routine may adjust the block size as needed to eliminate unused
+ memory or adjust it bigger if too small.
+
+ \param ulRecNo - ulBlockSize - Block size to allocate. If 0 or missing, it uses default block size of 32K.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbBlockRead::Init(xbUInt32 ulBlockSize ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ // calculate the block size
+ if( ulBlockSize == 0 )
+ ulBlkSize = dbf->GetXbasePtr()->GetDefaultBlockReadSize();
+
+ // if not big enough to handle more than one record, bump it up to something meaningful
+ if( ulBlkSize < (xbUInt32)(dbf->GetRecordLen() * 2 ))
+ ulBlkSize = (xbUInt32) dbf->GetRecordLen() * 10;
+
+ ulMaxRecs = (xbUInt32) ulBlkSize / dbf->GetRecordLen();
+ ulBlkSize = ulMaxRecs * dbf->GetRecordLen();
+
+ // allocate memory for the block
+ if(( pBlock = (char *) calloc( 1, ulBlkSize )) == NULL ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbBlockRead::Init() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Set the block size.
+/*!
+ St block size for this DBF file.<br>.
+
+ \param ulBlkSize - Block Size.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbBlockRead::SetBlockSize( xbUInt32 ulBlkSize ){
+ this->ulBlkSize = ulBlkSize;
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+} /* namespace */
+#endif /* XB_BLOCKREAD_SUPPORT */ \ No newline at end of file
diff --git a/src/core/xbdate.cpp b/src/core/xbdate.cpp
new file mode 100755
index 0000000..54834ac
--- /dev/null
+++ b/src/core/xbdate.cpp
@@ -0,0 +1,867 @@
+/* xbdate.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+#include <time.h>
+
+namespace xb{
+
+int xbDate::iDaysInMonths[2][13];
+int xbDate::iAggregatedDaysInMonths[2][13];
+
+/*************************************************************************/
+//! @brief Constructor.
+
+xbDate::xbDate() {
+ sDate8.Set( "" );
+ SetDateTables();
+}
+
+/*************************************************************************/
+//! @brief Constructor.
+/*!
+ \param iInitOpt - Constructor to use to initialize date static variables
+ Called by the main xbXbase::xbXBase constructor
+*/
+
+xbDate::xbDate( xbUInt16 ) {
+
+ SetDateTables();
+ Sysdate();
+}
+/*************************************************************************/
+//! @brief Constructor.
+/*!
+ \param sDate8In - Input date.
+*/
+
+xbDate::xbDate( const xbString & sDate8In ) {
+
+ if( DateIsValid( sDate8In ))
+ sDate8.Set( sDate8In );
+ else
+ sDate8.Set( "" );
+
+ // SetDateTables();
+}
+
+/*************************************************************************/
+//! @brief Constructor.
+/*!
+ \param sDate8In - Input date.
+*/
+xbDate::xbDate( const char * sDate8In ) {
+
+ if( DateIsValid( sDate8In ))
+ sDate8.Set( sDate8In );
+ else
+ sDate8.Set( "" );
+
+ // SetDateTables();
+}
+
+/*************************************************************************/
+//! @brief Constructor.
+/*!
+ \param lJulDate - Input julian date.
+*/
+xbDate::xbDate( xbInt32 lJulDate ) {
+ // SetDateTables();
+ JulToDate8( lJulDate );
+}
+
+/*************************************************************************/
+//! @brief Destructor.
+xbDate::~xbDate(){}
+/*************************************************************************/
+//! @brief Set operator=
+/*!
+ \param dt - Date value for set operation.
+*/
+void xbDate::operator=( const xbDate & dt ){
+ sDate8.Set( dt.Str());
+}
+/*************************************************************************/
+//! @brief operator +=
+/*!
+ This routine adds lDays to the date if the date is not null.
+ \param lDays - Number of days to add to the date.
+*/
+void xbDate::operator+=( xbInt32 lDays ){
+ if( !IsNull() )
+ JulToDate8( JulianDays() + lDays );
+}
+/*************************************************************************/
+//! @brief operator -=
+/*!
+ This routine subtracts lDays from the date if the date is not null.
+ \param lDays - Number of days to subtract from the date.
+*/
+void xbDate::operator-=( xbInt32 lDays ){
+ if( !IsNull() )
+ JulToDate8( JulianDays() - lDays );
+}
+/*************************************************************************/
+//! @brief operator ++
+/*!
+ This routine adds one day to the date field if the date is not null.
+*/
+void xbDate::operator++(xbInt32){
+ if( !IsNull() )
+ *this+=1;
+}
+/*************************************************************************/
+//! @brief operator --
+/*!
+ This routine subtracts one day from the date field if the date is not null.
+*/
+void xbDate::operator--(xbInt32){
+ if( !IsNull())
+ *this-=1;
+}
+/*************************************************************************/
+//! @brief operator -
+/*!
+ This routine subtracts one date from another date returning the difference.
+ \param dt - Date to subtract
+ \returns Number of days difference or zero if one of the dates is null.
+*/
+xbInt32 xbDate::operator-( const xbDate &dt ) const{
+ if( !IsNull() && !dt.IsNull() )
+ return JulianDays() - dt.JulianDays();
+ else
+ return 0;
+}
+/*************************************************************************/
+//! @brief operator +
+/*!
+ This routine adds additional days to a valid date field.
+ \param lCount - Number of days to add.
+ \returns New date in CCYYMMDD format.
+*/
+const char *xbDate::operator+( xbInt32 lCount ){
+ if( !IsNull() )
+ JulToDate8( JulianDays() + lCount );
+ return sDate8.Str();
+}
+/*************************************************************************/
+//! @brief operator -
+/*!
+ This routine subtracts days from a valid date field.
+ \param lCount - Number of days to subtract.
+ \returns New date in CCYYMMDD format.
+*/
+const char *xbDate::operator-( xbInt32 lCount ){
+ if( !IsNull() )
+ JulToDate8( JulianDays() - lCount );
+ return sDate8;
+}
+/*************************************************************************/
+//! @brief operator ==
+/*!
+ This routine compares two dates for equality.
+ \param dt - Date to compare.
+ \returns xbTrue - Dates match.<br>xbFalse - Dates don't match.
+*/
+xbBool xbDate::operator==( const xbDate &dt ) const{
+ if( JulianDays() == dt.JulianDays() )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief operator !=
+/*!
+ This routine compares two dates for inequality.
+ \param dt - Date to compare.
+ \returns xbTrue - Dates don't match.<br>xbFalse - Dates match.
+*/
+xbBool xbDate::operator!=( const xbDate &dt ) const{
+ if( JulianDays() != dt.JulianDays() )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief operator <
+/*!
+ This routine compares two dates
+ \param dt - Date to compare.
+ \returns xbTrue - Left date is less than right date.<br>
+ xbFalse - Left date is not less than right date.
+*/
+xbBool xbDate::operator<( const xbDate &dt ) const {
+ if( JulianDays() < dt.JulianDays() )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief >
+/*!
+ This routine compares two dates
+ \param dt - Date to compare.
+ \returns xbTrue - Left date is greater than right date.<br>
+ xbFalse - Left date is not greater than right date.
+*/
+xbBool xbDate::operator>( const xbDate &dt ) const {
+ if( JulianDays() > dt.JulianDays() )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief operator <=
+/*!
+ This routine compares two dates
+ \param dt - Date to compare.
+ \returns xbTrue - Left date is less than or equal to right date.<br>
+ xbFalse - Left date is not less than or equal to right date.
+*/
+xbBool xbDate::operator<=( const xbDate &dt ) const {
+ if( JulianDays() <= dt.JulianDays() )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief operator >=
+/*!
+ This routine compares two dates
+ \param dt - Date to compare.
+ \returns xbTrue - Left date is greater than or equal to right date.<br>
+ xbFalse - Left date is not greater than or equal to right date.
+*/
+xbBool xbDate::operator>=( const xbDate &dt ) const {
+ if( JulianDays() >= dt.JulianDays() )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief Calculate century for a given year.
+/*!
+ This routine calculates a century for a given year. It uses an 80/20
+ rolling date window to calculate the century.
+
+ \param iCalcYear - Two digit year to calculate a century for.
+ \returns Century calculated for the two digit year.
+*/
+xbInt16 xbDate::CalcRollingCenturyForYear( xbInt16 iCalcYear ) const {
+
+ xbDate d;
+ d.Sysdate();
+ xbInt16 iThisYear = d.YearOf();
+ xbInt16 iThisCentury = d.CenturyOf();
+ iThisYear -= (iThisCentury * 100);
+ if( iThisYear < 80 && iCalcYear < (iThisYear+20) )
+ return iThisCentury;
+ else if( iThisYear >= 80 &&
+ iCalcYear < iThisYear &&
+ iCalcYear >= (iThisYear-80))
+ return iThisCentury;
+ else
+ return iThisCentury - 1;
+}
+/*************************************************************************/
+//! @brief Get century for date.
+/*!
+ \returns the century from the valid date.\ or 0 for a null date.
+*/
+xbInt16 xbDate::CenturyOf() const {
+ if( !IsNull() ){
+ char Century[3];
+ Century[0] = sDate8[1];
+ Century[1] = sDate8[2];
+ Century[2] = 0x00;
+ return( atoi( Century ));
+ } else {
+ return 0;
+ }
+}
+/*************************************************************************/
+//! @brief Get the day of the week.
+/*!
+ \param sOutCharDay - Output character day of week (Sun-Sat).
+ \returns XB_INVALID_DATE<br>XB_NO_ERROR
+*/
+xbInt16 xbDate::CharDayOf( xbString &sOutCharDay ) {
+
+ if( !IsNull()){
+ struct tm tblock;
+ char buf[25];
+
+ tblock.tm_year = YearOf() - 1900;
+ tblock.tm_mon = MonthOf() - 1;
+ tblock.tm_mday = DayOf( XB_FMT_MONTH );
+ tblock.tm_hour = 0;
+ tblock.tm_min = 0;
+ tblock.tm_sec = 1;
+ tblock.tm_isdst = -1;
+ if( mktime( &tblock ) == -1 ){
+ sOutCharDay = "" ;
+ return XB_INVALID_DATE;
+ } else {
+ strftime( buf, 25, "%A", &tblock );
+ sOutCharDay = buf;
+ }
+ }
+ return XB_NO_ERROR;;
+}
+/*************************************************************************/
+//! @brief Get the month from the date.
+/*!
+ \param sOutCharMonth - Output character month.
+ \returns XB_INVALID_DATE<br>XB_NO_ERROR
+*/
+xbInt16 xbDate::CharMonthOf( xbString &sOutCharMonth ) {
+
+ if( !IsNull()){
+ struct tm tblock;
+ char buf[25];
+ tblock.tm_year = YearOf() - 1900;
+ tblock.tm_mon = MonthOf() - 1;
+ tblock.tm_mday = DayOf( XB_FMT_MONTH );
+ tblock.tm_hour = 0;
+ tblock.tm_min = 0;
+ tblock.tm_sec = 1;
+ tblock.tm_isdst = -1;
+ if( mktime( &tblock ) == -1 ){
+ sOutCharMonth = "";
+ return XB_INVALID_DATE;
+ } else {
+ strftime( buf, 25, "%B", &tblock );
+ sOutCharMonth = buf;
+ }
+ }
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Check a date for valid data.
+/*!
+ \param sDateIn - Date to check for valid formaat of CCYYMMDD.
+ \returns xbTrue - Valid date.<br>xbFalse - Not a valid date.
+*/
+xbBool xbDate::DateIsValid( const xbString &sDateIn ) const {
+
+ xbInt16 iYear, iMonth, iDay;
+ char sYear[5];
+ char sMonth[3];
+ char sDay[3];
+
+ if( sDateIn.Len() != 8 )
+ return xbFalse;
+
+ if(!isdigit( sDateIn[1] ) || !isdigit( sDateIn[2] ) || !isdigit( sDateIn[3] ) ||
+ !isdigit( sDateIn[4] ) || !isdigit( sDateIn[5] ) || !isdigit( sDateIn[6] ) ||
+ !isdigit( sDateIn[7] ) || !isdigit( sDateIn[8] ) )
+ return xbFalse;
+
+ sDay[0] = sDateIn[7];
+ sDay[1] = sDateIn[8];
+ sDay[2] = 0x00;
+ iDay = atoi( sDay );
+
+ sMonth[0] = sDateIn[5];
+ sMonth[1] = sDateIn[6];
+ sMonth[2] = 0x00;
+ iMonth = atoi( sMonth );
+
+ sYear[0] = sDateIn[1];
+ sYear[1] = sDateIn[2];
+ sYear[2] = sDateIn[3];
+ sYear[3] = sDateIn[4];
+ sYear[4] = 0x00;
+ iYear = atoi( sYear );
+
+ // valid years are 0001 thru 9999
+ if( iYear < 1 || iYear > 9999 || iMonth < 1 || iMonth > 12 || iDay < 1 || iDay > 31 )
+ return xbFalse;
+
+ // April, June, September and November have 30 days
+ if(( iMonth==4 || iMonth==6 || iMonth==9 || iMonth==11 )&& iDay > 30 )
+ return xbFalse;
+
+ // check for February with leap year
+ if( iMonth == 2 ){
+ if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 ){
+ if( iDay > 29 ){
+ return xbFalse;
+ }
+ } else if( iDay > 28 ){
+ return xbFalse;
+ }
+ }
+ return xbTrue;
+}
+
+/*************************************************************************/
+//! @brief
+/*!
+ This routine returns the numeric day.
+ \param iFormat
+ XB_FMT_WEEK Number of day in WEEK 0-6 ( Sat - Fri )<br>
+ XB_FMT_MONTH Number of day in MONTH 1-31<br>
+ XB_FMT_YEAR Number of day in YEAR 1-366
+ \returns XB_INVALID_OPTION<br>XB_NO_ERROR
+*/
+
+xbInt16 xbDate::DayOf( xbInt16 iFormat ) const {
+
+ if( !IsNull()){
+ xbInt16 iOutDay = 0;
+ char sDay[3];
+ xbInt16 iDay, iMonth, iYear, iDay2;
+
+ // check for valid format switch
+ if( iFormat!=XB_FMT_WEEK && iFormat!=XB_FMT_MONTH && iFormat!=XB_FMT_YEAR )
+ return XB_INVALID_OPTION;
+
+ if( iFormat == XB_FMT_WEEK ){
+ //DayOf( XB_FMT_MONTH, iDay );
+ iDay = DayOf( XB_FMT_MONTH );
+ iMonth = MonthOf();
+ iYear = YearOf();
+
+ // The following formula uses Zeller's Congruence to determine the day of the week
+ if( iMonth > 2 ) // init to February
+ iMonth -= 2;
+ else {
+ iMonth += 10;
+ iYear--;
+ }
+ iDay2 = ((13 * iMonth - 1) / 5) + iDay + ( iYear % 100 ) +
+ (( iYear % 100 ) / 4) + ((iYear /100 ) / 4 ) - 2 *
+ ( iYear / 100 ) + 77 ;
+
+ iOutDay = iDay2 - 7 * ( iDay2 / 7 );
+ iOutDay == 6 ? iOutDay = 0 : iOutDay++;
+ }
+ else if( iFormat == XB_FMT_MONTH ){
+ sDay[0] = sDate8[7];
+ sDay[1] = sDate8[8];
+ sDay[2] = 0x00;
+ iOutDay = atoi( sDay );
+ } else {
+ iOutDay = iAggregatedDaysInMonths[IsLeapYear()][MonthOf()-1] + DayOf( XB_FMT_MONTH );
+ }
+ return iOutDay;
+ } else {
+ return 0;
+ }
+}
+/*************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! @brief Dump date information to stdout.
+/*!
+ \param sTitle - Title for output.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+void xbDate::Dump( const char *sTitle ){
+ fprintf( stdout, "%s\n sDate = [%s]\n", sTitle, sDate8.Str() );
+}
+
+/*************************************************************************/
+//! @brief Dump the date tables.
+/*!
+ This dumps the internal date structures to stdout.
+ \returns void
+*/
+
+void xbDate::DumpDateTables(){
+ fprintf( stdout, "Date Tables\n" );
+ fprintf( stdout, "Month *-Aggragated Days-* *--Days In Month--*\n" );
+ fprintf( stdout, " *-NonLeap Leap-* *--NonLeap Leap--*\n" );
+ for( int i = 1; i < 13; i++ )
+ fprintf( stdout, " %2d %3d %3d %3d %3d\n", i,
+ iAggregatedDaysInMonths[0][i],iAggregatedDaysInMonths[1][i],
+ iDaysInMonths[0][i], iDaysInMonths[1][i]);
+}
+#endif
+
+/*************************************************************************/
+//! @brief Format MM/DD/YY date
+/*!
+ This routine takes an MM/DD/YY format date as input and populates a
+ date class with the appropriate YYYYMMDD data.
+
+ \param sCtodInDate - MM/DD/YY formatted date as input.
+ \returns XB_INVALID_OPTION<br>XB_NO_ERROR
+*/
+xbInt16 xbDate::CTOD( const xbString &sCtodInDate ){
+
+ if( sCtodInDate[1] != ' ' && ( sCtodInDate[3] == '\\' || sCtodInDate[3] == '/') ){
+ char yy[3];
+ yy[0] = sCtodInDate[7];
+ yy[1] = sCtodInDate[8];
+ yy[2] = 0x00;
+ sDate8.Sprintf( "%02d%c%c%c%c%c%c", CalcRollingCenturyForYear( atoi( yy )),
+ sCtodInDate[7], sCtodInDate[8], sCtodInDate[1], sCtodInDate[2], sCtodInDate[4], sCtodInDate[5] );
+ return XB_NO_ERROR;
+ }
+ else{
+ return XB_INVALID_DATE;
+ }
+}
+/*************************************************************************/
+//! @brief
+/*!
+ This routine will reformat a date based on the format specifiers entered
+ in sFmtIn. If no input format is specified, the routine will use the
+ system default date format.
+
+ \param sFmtIn - A format specifier with the following paramaters:<br>
+
+ 1) YYDDD - A julian date format
+ 2) YY or YYYY will print a 2 or 4 digit year
+ 3) M,MM,MMM or MMMM
+ M - one digit month if no leading zero
+ MM - two digit month, contains leading zero
+ MMM - Jan through Dec
+ MMMM - January through December
+ 4) D,DD,DDD or DDDD
+ D - one digit dayif no leading zero
+ DD - two digit day, contains leading zero
+ DDD - Sun through Sat (or julian if YYDDD)
+ DDDD - Sunday through Saturday
+
+ \param sOutFmtDate - Reformatted output date.
+ \returns XB_NO_ERROR
+ <br><br>
+ Format Examples:<br>
+ MM/DD/YY<br>
+ YYYY-MM-DD<br>
+ DDDDDDDDDDD MMMMMMMMMMM DD,YYYY
+*/
+xbInt16 xbDate::FormatDate( const xbString &sFmtIn, xbString &sOutFmtDate ){
+ xbUInt32 FmtCtr;
+ char type;
+ xbUInt32 iTypeCtr;
+ xbString ws;
+ xbString sWrkFmt;
+ sOutFmtDate = "";
+
+ if( IsNull())
+ return XB_NO_ERROR;
+
+ /* use format for this specific string if available, else use default format */
+ if( strlen( sFmtIn ) > 0 )
+ sWrkFmt = sFmtIn;
+ else
+ sWrkFmt = GetDefaultDateFormat();
+
+ if( strstr( sWrkFmt.Str(), "YYDDD" )){
+ sOutFmtDate.Sprintf( "%c%c%03d", sDate8[3], sDate8[4], DayOf( XB_FMT_YEAR ));
+ } else {
+ FmtCtr = 1;
+ while( FmtCtr <= sWrkFmt.Len() ){
+ if( sWrkFmt[FmtCtr] != 'D' && sWrkFmt[FmtCtr] != 'M' && sWrkFmt[FmtCtr] != 'Y' ){
+ sOutFmtDate += sWrkFmt[FmtCtr];
+ FmtCtr++;
+ iTypeCtr = 0;
+ } else {
+ type = sWrkFmt[FmtCtr];
+ iTypeCtr = 0;
+ while( sWrkFmt[FmtCtr] == type ) {
+ iTypeCtr++;
+ FmtCtr++;
+ }
+ switch( type ){
+ case 'D':
+
+ if( iTypeCtr == 1 ){
+ sOutFmtDate += ws.Sprintf( "%d", DayOf( XB_FMT_MONTH ));
+ }
+ else if( iTypeCtr == 2 ){
+ sOutFmtDate += ws.Sprintf( "%c%c", sDate8[7], sDate8[8] );
+ } else {
+ xbString sCDO;
+ CharDayOf( sCDO );
+ ws.Assign( sCDO, 1, iTypeCtr );
+ sOutFmtDate += ws.Str();
+ }
+ break;
+
+ case 'M':
+ if( iTypeCtr == 1 ){
+ sOutFmtDate += ws.Sprintf( "%d", MonthOf());
+ }
+ else if( iTypeCtr == 2 ){
+ sOutFmtDate += ws.Sprintf( "%c%c", sDate8[5], sDate8[6] );
+ } else {
+ xbString sCMO;
+ CharMonthOf( sCMO );
+ ws.Assign( sCMO, 1, iTypeCtr );
+ sOutFmtDate += ws.Str();
+ }
+ break;
+
+ case 'Y':
+ if( iTypeCtr == 2 ){
+ sOutFmtDate += ws.Sprintf( "%c%c", sDate8[3], sDate8[4] );
+ }
+ else if( iTypeCtr == 4 ){
+ sOutFmtDate += ws.Sprintf( "%c%c%c%c", sDate8[1], sDate8[2], sDate8[3], sDate8[4] );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Return the date value.
+/*!
+ \returns char ptr to date value.
+*/
+const char * xbDate::Str() const{
+ return sDate8.Str();
+};
+/*************************************************************************/
+//! @brief Determine if date is a leap year.
+/*!
+ \returns xbTrue - Is leapyear.<br> xbFalse - Not a leap year.
+*/
+xbBool xbDate::IsLeapYear() const {
+ if( IsNull() )
+ return xbFalse;
+ xbInt16 iYear = YearOf();
+ if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief Determine if date is a leap year.
+/*!
+ \param iYear - Year to check for leap year status.
+ \returns xbTrue - Is leapyear.<br> xbFalse - Not a leap year.
+*/
+xbBool xbDate::IsLeapYear( xbInt16 iYear ) const {
+ if(( iYear % 4 == 0 && iYear % 100 != 0 ) || iYear % 400 == 0 )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief Determine if date is null date
+/*!
+ \returns xbTrue - If null date.<br> xbFalse - Not a null date.
+*/
+xbBool xbDate::IsNull() const {
+ if( sDate8.Len() < 8 )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/*************************************************************************/
+//! @brief Calculate julian days for a given date.
+/*!
+ \returns The number of days since 01/01/0001 + JUL_OFFSET.
+*/
+xbInt32 xbDate::JulianDays() const{
+ if( !IsNull()){
+ xbInt32 ly = YearOf() - 1;
+ xbInt32 lDays = ly * 365L + ly / 4L - ly / 100L + ly / 400L;
+ lDays += DayOf( XB_FMT_YEAR );
+ return lDays + JUL_OFFSET;
+ } else {
+ return 0;
+ }
+}
+/*************************************************************************/
+//! @brief Convert the number of julian days to gregorian date.
+/*!
+ \param lJulDays - Julian days.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbDate::JulToDate8( xbInt32 lJulDays )
+{
+ lJulDays -= JUL_OFFSET;
+ // calculate the year
+ xbInt16 iYear = (xbInt16)(lJulDays / 365.24 );
+ lJulDays -= (iYear * 365L) + (iYear / 4L) - (iYear / 100L) + (iYear / 400L);
+ iYear++;
+ while( lJulDays <= 0 ){
+ iYear--;
+ lJulDays += (365L + IsLeapYear( iYear ));
+ }
+ // this for loop calculates the month by comparing the number of days remaining to one of the tables
+ xbInt16 iIsLeap = IsLeapYear(iYear);
+ xbInt16 iMonth = 1;
+ while( ((xbInt16) lJulDays > iAggregatedDaysInMonths[iIsLeap][iMonth]) && (iMonth < 12) )
+ iMonth++;
+ lJulDays -= iAggregatedDaysInMonths[iIsLeap][iMonth-1];
+ sDate8.Sprintf( "%04d%02d%02ld", iYear, iMonth, lJulDays );
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Set the date to the last day of month for a given date.
+/*!
+ This routine sets the last date of the month.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbDate::LastDayOfMonth(){
+ if( !IsNull())
+ sDate8.Sprintf( "%4.4d%2.2d%2.2d", YearOf(), MonthOf(), iDaysInMonths[IsLeapYear()][MonthOf()]);
+ return XB_NO_ERROR;
+};
+/*************************************************************************/
+//! @brief Return the month for the date.
+/*!
+ \returns The month of the date.
+*/
+xbInt16 xbDate::MonthOf() const {
+ if( !IsNull()){
+ xbInt16 iOutMonth;
+ char month[3];
+ month[0] = sDate8[5];
+ month[1] = sDate8[6];
+ month[2] = 0x00;
+ iOutMonth = atoi( month );
+ return iOutMonth;
+ } else {
+ return 0;
+ }
+}
+
+/*************************************************************************/
+//! @brief Set the date.
+/*!
+ \param sDateIn - Input date.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbDate::Set( const xbString & sDateIn ){
+
+ if( DateIsValid( sDateIn )){
+ sDate8 = sDateIn;
+ } else {
+ sDate8 = ""; // set to null date if invalid date
+ }
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief This routine sets up static data tables on startup.
+/*!
+ \returns void
+*/
+void xbDate::SetDateTables() {
+ if( iAggregatedDaysInMonths[1][12] != 366 ){ /* first time called ? */
+
+ iAggregatedDaysInMonths[0][0] = 0;
+ iAggregatedDaysInMonths[0][1] = 31;
+ iAggregatedDaysInMonths[0][2] = 59;
+ iAggregatedDaysInMonths[0][3] = 90;
+ iAggregatedDaysInMonths[0][4] = 120;
+ iAggregatedDaysInMonths[0][5] = 151;
+ iAggregatedDaysInMonths[0][6] = 181;
+ iAggregatedDaysInMonths[0][7] = 212;
+ iAggregatedDaysInMonths[0][8] = 243;
+ iAggregatedDaysInMonths[0][9] = 273;
+ iAggregatedDaysInMonths[0][10] = 304;
+ iAggregatedDaysInMonths[0][11] = 334;
+ iAggregatedDaysInMonths[0][12] = 365;
+ iAggregatedDaysInMonths[1][0] = 0;
+ iAggregatedDaysInMonths[1][1] = 31;
+ iAggregatedDaysInMonths[1][2] = 60;
+ iAggregatedDaysInMonths[1][3] = 91;
+ iAggregatedDaysInMonths[1][4] = 121;
+ iAggregatedDaysInMonths[1][5] = 152;
+ iAggregatedDaysInMonths[1][6] = 182;
+ iAggregatedDaysInMonths[1][7] = 213;
+ iAggregatedDaysInMonths[1][8] = 244;
+ iAggregatedDaysInMonths[1][9] = 274;
+ iAggregatedDaysInMonths[1][10] = 305;
+ iAggregatedDaysInMonths[1][11] = 335;
+ iAggregatedDaysInMonths[1][12] = 366;
+
+ iDaysInMonths[0][0] = 0;
+ iDaysInMonths[0][1] = 31;
+ iDaysInMonths[0][2] = 28;
+ iDaysInMonths[0][3] = 31;
+ iDaysInMonths[0][4] = 30;
+ iDaysInMonths[0][5] = 31;
+ iDaysInMonths[0][6] = 30;
+ iDaysInMonths[0][7] = 31;
+ iDaysInMonths[0][8] = 31;
+ iDaysInMonths[0][9] = 30;
+ iDaysInMonths[0][10] = 31;
+ iDaysInMonths[0][11] = 30;
+ iDaysInMonths[0][12] = 31;
+ iDaysInMonths[1][0] = 0;
+ iDaysInMonths[1][1] = 31;
+ iDaysInMonths[1][2] = 29;
+ iDaysInMonths[1][3] = 31;
+ iDaysInMonths[1][4] = 30;
+ iDaysInMonths[1][5] = 31;
+ iDaysInMonths[1][6] = 30;
+ iDaysInMonths[1][7] = 31;
+ iDaysInMonths[1][8] = 31;
+ iDaysInMonths[1][9] = 30;
+ iDaysInMonths[1][10] = 31;
+ iDaysInMonths[1][11] = 30;
+ iDaysInMonths[1][12] = 31;
+ }
+}
+/*************************************************************************/
+//! @brief Set the date equal to the system date.
+/*!
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbDate::Sysdate(){
+
+ #ifdef HAVE__LOCALTIME64_S_F
+ __time64_t timer;
+ _time64( &timer );
+ struct tm tblock;
+ _localtime64_s( &tblock, &timer );
+ tblock.tm_year += 1900;
+ tblock.tm_mon++;
+ sDate8.Sprintf( "%4d%02d%02d", tblock.tm_year, tblock.tm_mon, tblock.tm_mday );
+ #else
+ time_t timer;
+ timer = time( &timer );
+ struct tm *tblock;
+ tblock = localtime( &timer );
+ tblock->tm_year += 1900;
+ tblock->tm_mon++;
+ sDate8.Sprintf( "%4d%02d%02d",tblock->tm_year,tblock->tm_mon,tblock->tm_mday );
+ #endif
+
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Returns the year of the date.
+/*!
+ \returns The year of the date.
+*/
+xbInt16 xbDate::YearOf() const {
+ if( !IsNull()){
+ char year[5];
+ year[0] = sDate8[1];
+ year[1] = sDate8[2];
+ year[2] = sDate8[3];
+ year[3] = sDate8[4];
+ year[4] = 0x00;
+ xbInt16 iOutYear = atoi( year );
+ return iOutYear;
+ } else {
+ return 0;
+ }
+};
+} /* namespace */ \ No newline at end of file
diff --git a/src/core/xbdbf.cpp b/src/core/xbdbf.cpp
new file mode 100755
index 0000000..8904a6d
--- /dev/null
+++ b/src/core/xbdbf.cpp
@@ -0,0 +1,4533 @@
+/* xbdbf.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+
+namespace xb{
+
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param x Pointer to xbXbase
+*/
+xbDbf::xbDbf( xbXBase * x ) : xbFile( x ){
+ xbase = x;
+ SchemaPtr = NULL;
+ RecBuf = NULL;
+ RecBuf2 = NULL;
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ pRb = NULL;
+ bBlockReadEnabled = xbFalse; // batch read switch, if xbTrue, then ON
+ #endif // XB_BLOCKREAD_SUPPORT
+
+ InitVars();
+}
+/************************************************************************/
+void xbDbf::InitVars()
+{
+ iNoOfFields = 0;
+ iDbfStatus = XB_CLOSED;
+ ulCurRec = 0L;
+ cVersion = 0x00;
+ cUpdateYY = 0x00;
+ cUpdateMM = 0x00;
+ cUpdateDD = 0x00;
+ ulNoOfRecs = 0L;
+ uiHeaderLen = 0x00;
+ uiRecordLen = 0x00;
+ cTransactionFlag = 0x00;
+ cEncryptionFlag = 0x00;
+ cIndexFlag = 0x00;
+ cLangDriver = 0x00;
+ iFileVersion = 0; /* Xbase64 file version */
+ iAutoCommit = -1;
+
+ SetFileName ( "" );
+ sAlias.Set ( "" );
+ SetDirectory ( GetDataDirectory());
+
+ #ifdef XB_LOCKING_SUPPORT
+ iLockFlavor = -1;
+ bTableLocked = xbFalse;
+ bHeaderLocked = xbFalse;
+ ulAppendLocked = 0;
+ SetAutoLock( -1 );
+ lloRecLocks.SetDupKeys( xbFalse );
+ #endif // XB_LOCKING_SUPPORT
+
+ #ifdef XB_INDEX_SUPPORT
+ ixList = NULL;
+ pCurIx = NULL;
+ vpCurIxTag = NULL;
+ sCurIxType = "";
+ ClearTagList();
+ #endif // XB_INDEX_SUPPORT
+
+ #ifdef XB_MEMO_SUPPORT
+ Memo = NULL;
+ #endif // XB_MEMO_SUPPORT
+
+ #ifdef XB_INF_SUPPORT
+ llInfData.Clear();
+ #endif // XB_INF_SUPPORT
+}
+
+/************************************************************************/
+//! @brief Destructor
+xbDbf::~xbDbf(){
+
+ // is there is an uncommited update, discard it.
+ // as we don't know if it is an append or an update
+ if( iDbfStatus == XB_UPDATED )
+ Abort();
+
+ if( iDbfStatus != XB_CLOSED )
+ Close();
+
+ if( SchemaPtr ){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+ if( RecBuf ){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if( RecBuf2){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+ if( RecBuf ){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ if( bBlockReadEnabled )
+ DisableBlockReadProcessing();
+ #endif // XB_BLOCKREAD_SUPPORT
+
+ Close();
+}
+/************************************************************************/
+//! @brief Abort any uncommited changes for the current record buffer.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Abort(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iDbfStatus == XB_UPDATED ){
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist()){
+ if(( iRc = Memo->Abort()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ #endif
+ memcpy( RecBuf, RecBuf2, uiRecordLen );
+ iDbfStatus = XB_OPEN;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Abort() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Add an index to the internal list of indices for this table.
+/*!
+ The index list is used during any table update process to update any open
+ index file. Index files can contain one or more tags. Temporary tags
+ are not included here because they are created after a table is open
+ and will be deleted when the table is closed.
+
+ \param ixIn Pointer to index object for a given index file.
+ \param sFmt NDX, MDX or TDX.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbDbf::AddIndex( xbIx * ixIn, const xbString &sFmt ){
+
+ xbIxList *ixt; // this
+ if(( ixt = (xbIxList *) malloc( sizeof( xbIxList ))) == NULL )
+ return XB_NO_ERROR;
+
+ ixt->ix = ixIn;
+ ixt->next = NULL;
+ ixt->sFmt = new xbString( sFmt );
+ ixt->sFmt->ToUpperCase();
+
+ if( ixList ){
+ xbIxList *ixn = ixList; // next
+ while( ixn->next ){
+ ixn = ixn->next;
+ }
+ ixn->next = ixt;
+ } else {
+ ixList = ixt;
+ }
+ return XB_NO_ERROR;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+/************************************************************************/
+//! @brief Append the current record to the data file.
+/*!
+ This method attempts to append the contents of the current record buffer
+ to the end of the DBF file, updates the file date, number of records in the file
+ and updates any open indices associated with this data file.<br>
+
+ To add a record, an application would typically blank the record buffer,
+ update various fields in the record buffer, then append the record.<br>
+
+ The append method performs the following tasks:<br>
+ 1) Create new index key values<br>
+ 2) Lock the table<br>
+ 3) Lock append bytes<br>
+ 4) Lock indices<br>
+ 5) Read the dbf header<br>
+ 6) Check for dup keys<br>
+ 7) Calc last update date, no of recs<br>
+ 8) Add keys<br>
+ 9) Unlock indices<br>
+ 10) Update file header<br>
+ 11) Unlock file header<br>
+ 12) Append record<br>
+ 13) Unlock append bytes<br>
+
+Note: Locking memo files is not needed as the memo file updates are handled outside of the append method.<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::AppendRecord(){
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbUInt32 ulSaveCurRec = 0;
+
+ try{
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *ixList = GetIxList();
+ // do this step first before anything is locked, reduce lock time as much as possible
+ while( ixList ){
+
+ // std::cout << "xbDbf::CreateKeys(x)\n";
+ if(( iRc = ixList->ix->CreateKeys( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ ixList = ixList->next;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ // lock everything up for an update
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ) {
+ return iRc;
+ } else {
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ if(( iRc = LockAppend( XB_LOCK )) != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ){
+ LockHeader( XB_UNLOCK );
+ return iRc;
+ } else {
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ }
+ #endif // XB_LOCKING_SUPPORT
+ if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ #ifdef XB_INDEX_SUPPORT
+ ixList = GetIxList();
+
+ while( ixList ){
+ if(( iRc = ixList->ix->CheckForDupKeys()) != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ return XB_KEY_NOT_UNIQUE;
+ }
+ ixList = ixList->next;
+ }
+
+ #endif // XB_INDEX_SUPPORT
+
+ // calculate the latest header information
+ xbDate d;
+ d.Sysdate();
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulSaveCurRec = ulCurRec;
+ ulNoOfRecs++;
+ ulCurRec = ulNoOfRecs;
+
+ #ifdef XB_INDEX_SUPPORT
+
+
+ ixList = GetIxList();
+ while( ixList ){
+ if(( iRc = ixList->ix->AddKeys( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockIndices( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+ #endif // XB_INDEX_SUPPORT
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockHeader( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ }
+ #endif
+
+ // write the last record
+ if(( iRc = xbFseek( (uiHeaderLen+((xbInt64)(ulNoOfRecs-1)*uiRecordLen)), 0 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+
+ if(( iRc = xbFwrite( RecBuf, uiRecordLen, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+
+ // write the end of file marker
+ if(( iRc = xbFputc( XB_CHAREOF )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockAppend( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw( iRc );
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ }
+ catch (xbInt16 iRc ){
+ if( ulSaveCurRec != 0 ){
+ ulCurRec = ulSaveCurRec;
+ ulNoOfRecs--;
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ #ifdef XB_INDEX_SUPPORT
+ LockIndices( XB_UNLOCK );
+ #endif // XB_INDEX_SUPPORT
+ LockAppend( XB_UNLOCK );
+ LockHeader( XB_UNLOCK );
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( iRc != XB_LOCK_FAILED && iRc != XB_KEY_NOT_UNIQUE ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Append() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ if( iRc == XB_NO_ERROR )
+ iDbfStatus = XB_OPEN;
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_INF_SUPPORT
+//! @brief Asscoiate a non production index to a DBF file.
+/*!
+
+ The original Dbase (TM) software supported non production indices (NDX) and production indices (MDX).
+ The production indices are opened automatically when the DBF file is opened but the non-production
+ indices are not. This method is specific to the Xbas64 library and providex a means to link non production
+ NDX index files to the DBF file so they will be opened automatically when the DBF file is opened.<br>
+
+ This routine requires INF support be enabled when building the library.<br>
+ This routine creates a file with the same name as the DBF file, but with an extension of INF.<br>
+
+
+ \param sIxType Currently only NDX. Future versions will support additional non prod index types.
+ \param sIxName The index name.
+ \param iOpt 0 - Add index to .INF if not already there<br>
+ 1 - Remove index from .INF if exists
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbDbf::AssociateIndex( const xbString &sIxType, const xbString &sIxName, xbInt16 iOpt ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbString sIxTypeIn = sIxType;
+ sIxTypeIn.Trim();
+ xbString sIxNameIn = sIxName;
+ sIxNameIn.Trim();
+
+ if( sIxTypeIn != "NDX" || sIxName == "" )
+ return XB_INVALID_INDEX;
+
+ if(( iRc = LoadInfData()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // check if entry exists
+ xbLinkListNode<xbString> * llN = llInfData.GetHeadNode();
+ xbBool bFound = xbFalse;
+ xbString s;
+
+ while( llN && !bFound ){
+ s = llN->GetKey();
+ if( s.Len() > 0 ){
+ if( sIxNameIn == s )
+ bFound = xbTrue;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ xbBool bUpdated = xbFalse;
+ if( iOpt == 0 && !bFound ){
+ s.Sprintf( "%s%c%c", sIxName.Str(), 0x0d, 0x0a );
+ llInfData.InsertAtEnd( s );
+ bUpdated = xbTrue;
+
+ } else if( iOpt == 1 && bFound ){
+ llInfData.RemoveByVal( s );
+ bUpdated = xbTrue;
+ }
+
+ if( bUpdated ){
+ if(( iRc = SaveInfData()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::AssociateIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INF_SUPPORT
+
+/************************************************************************/
+//! @brief Blank the record buffer.
+/*!
+
+ This method would typically be called to initialize the record buffer before
+ updates are applied to append a new record.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::BlankRecord()
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 100;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+
+ if( iDbfStatus == XB_UPDATED ){
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ }
+ ulCurRec = 0;
+ memset( RecBuf, 0x20, uiRecordLen );
+ memset( RecBuf2, 0x20, uiRecordLen );
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::BlankRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+/*!
+ This method is used to check an index tag's intgerity.
+
+ \param iTagOpt 0 - Check current tag<br>
+ 1 - Check all tags<br>
+
+ \param iOutputOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::CheckTagIntegrity( xbInt16 iTagOpt, xbInt16 iOutputOpt ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iTagOpt == 0 ){
+ if( pCurIx )
+ return pCurIx->CheckTagIntegrity( vpCurIxTag, iOutputOpt );
+ else
+ return XB_INVALID_TAG;
+
+ } else {
+
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+
+ while( llN ){
+ pTag = llN->GetKey();
+ if(( iRc = pTag->GetIx()->CheckTagIntegrity( pTag->GetVpTag(), iOutputOpt )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+/*!
+ This method is used to reindex / rebuild index tag.
+
+ \param iTagOpt 0 - Reindex current tag<br>
+ 1 - Reindex all tags<br>
+ 2 - Reindex for tag identified by vpTag
+ \param iErrorOpt 0 - Don't delete tag on reindex failure<br>
+ 1 - Delete tag on reindex failure
+ \param vpTag if option 2 used, pointer to tag to reindex
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Reindex( xbInt16 iTagOpt, xbInt16 iErrorOpt, xbIx **ppIx, void **vppTag ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ void *vp;
+
+ xbString sType;
+ xbString sTagName;
+
+ if( iTagOpt < 0 || iTagOpt > 2 || (iTagOpt == 2 && (!ppIx || !vppTag )))
+ return XB_INVALID_OPTION;
+
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ xbBool bOriginalBlockReadSts = GetBlockReadStatus();
+ #endif
+
+ try{
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ if( !bOriginalBlockReadSts )
+ EnableBlockReadProcessing();
+ #endif
+
+ if( iTagOpt == 0 ){
+ if( pCurIx ){
+
+ if(( iRc = pCurIx->Reindex( &vpCurIxTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ return iRc;
+ } else {
+ return XB_INVALID_TAG;
+ }
+
+ } else if( iTagOpt == 1 ) {
+
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+
+ while( llN ){
+ pTag = llN->GetKey();
+ vp = pTag->GetVpTag();
+ if(( iRc = pTag->GetIx()->Reindex( &vp )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+ } else if( iTagOpt == 2 ){
+
+ // xbIx *pIx;
+ // pIx = *ppIx;
+ xbIx *pIx = *ppIx;
+ // void *vpTag;
+ // vpTag = *vppTag;
+ void *vpTag = *vppTag;
+
+ if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ if( !bOriginalBlockReadSts )
+ DisableBlockReadProcessing();
+ #endif
+
+ return iRc;
+}
+
+/************************************************************************/
+// @brief Clear the index tag list.
+/*
+ Protected method. Clears the list inf index tags.
+ \returns void.
+*/
+void xbDbf::ClearTagList(){
+
+ xbTag *pTag;
+ xbBool bDone = xbFalse;
+ while( llTags.GetNodeCnt() > 0 && !bDone ){
+ if( llTags.RemoveFromFront( pTag ) != XB_NO_ERROR ){
+ bDone = xbTrue;
+ } else {
+ if( pTag )
+ delete pTag;
+ }
+ }
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Close DBF file/table.
+/*!
+ This routine flushes any remaining updates to disk, closes the DBF file and
+ any associated memo and index files.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Close(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if(iDbfStatus == XB_CLOSED)
+ return XB_NO_ERROR;
+
+ else if( iDbfStatus == XB_UPDATED ){
+
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ }
+
+ if(SchemaPtr){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+ if(RecBuf){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if(RecBuf2){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ Memo->CloseMemoFile();
+ delete Memo;
+ Memo = NULL;
+ }
+ #endif
+
+ // close any open index files, remove from the ix list
+ #ifdef XB_INDEX_SUPPORT
+ while( ixList ){
+ ixList->ix->Close();
+ RemoveIndex( ixList->ix );
+ }
+ #endif
+
+ if(( iRc = xbase->RemoveTblFromTblList( this )) != XB_NO_ERROR ){
+ xbString sMsg;
+ sMsg.Sprintf( "Alias = [%s]", sAlias.Str());
+ xbase->WriteLogMessage( sMsg.Str() );
+ iErrorStop = 120;
+ throw iRc;
+ }
+ xbFclose();
+ InitVars();
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Close an open index file
+/*!
+ All index files are automatically closed when the DBF file is closed.
+ Under normal conditions, it is not necessary to explicitly close an index file
+ with this routine. Any updates posted to a DBF file while an index is closed
+ will not be reflected in the closed index file.
+
+ \param pIx Pointer to index object to close.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::CloseIndexFile( xbIx *pIx ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+
+ try{
+
+ // verify index is open and in the list
+ xbBool bFound = xbFalse;
+ xbIxList *p = GetIxList();
+ while( p && !bFound ){
+ if( pIx == p->ix )
+ bFound = xbTrue;
+ p = p->next;
+ }
+ if( !bFound ){
+ iErrorStop = 100;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+ // close it
+ if(( iRc = pIx->Close()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // remove it from the list
+ if(( iRc = RemoveIndex( pIx )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ // refresh the tag list
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( pIx == pCurIx ){
+ pCurIx = NULL;
+ vpCurIxTag = NULL;
+ sCurIxType = "";
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CloseIndexFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Commit updates to disk
+/*!
+
+ This routine commits any pending updates to disk.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Commit(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( iDbfStatus == XB_UPDATED ){
+ if( ulCurRec == 0 ){
+ if(( iRc = AppendRecord()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = PutRecord( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Commit() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Copy table (dbf) file structure.
+/*!
+
+ This routine will copy the structure of a dbf file and if successful
+ return a pointer to the new table in an open state.
+
+ \param dNewTable Reference to new table object.
+ \param sNewTableName New table (dbf) name.
+ \param sNewTableAlias Alias name of new table.
+ \param iOverlay xbTrue - Overlay existing file.<br>
+ xbFalse - Don't overlay existing file.
+ \param iShareMode XB_SINGLE_USER<br>
+ XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+//! Copy DBF structure
+/*!
+*/
+xbInt16 xbDbf::CopyDbfStructure( xbDbf * dNewTable, const xbString &sNewTableName,
+ const xbString & sNewTableAlias, xbInt16 iOverlay, xbInt16 iShareMode ) {
+
+// If successful, the table is returned in an open state after executing this method
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbSchema *newTableSchema = NULL;
+
+ try{
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 100;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+
+ if( !dNewTable ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_OBJECT;
+ throw iRc;
+ }
+
+ // Get the number of schema entries for this table
+ xbInt32 lSchemaRecCnt = GetFieldCnt() + 1;
+
+ // Allocate a Schema = No Of Fields + 1
+ if((newTableSchema=(xbSchema *)malloc( (size_t) lSchemaRecCnt * sizeof(xbSchema)))==NULL){
+ iErrorStop = 120;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ // Populate the Schema
+ xbInt32 l;
+ for( l = 0; l < lSchemaRecCnt-1; l++ ){
+ memset( newTableSchema[l].cFieldName, 0x00, 11 );
+ for( int x = 0; x < 10 && SchemaPtr[l].cFieldName[x]; x++ )
+ newTableSchema[l].cFieldName[x] = SchemaPtr[l].cFieldName[x];
+ newTableSchema[l].cType = SchemaPtr[l].cType;
+ newTableSchema[l].iFieldLen = SchemaPtr[l].cFieldLen;
+ newTableSchema[l].iNoOfDecs = SchemaPtr[l].cNoOfDecs;
+ }
+
+ // set the last one to zeroes
+ memset( newTableSchema[l].cFieldName, 0x00, 11 );
+ newTableSchema[l].cType = 0;
+ newTableSchema[l].iFieldLen = 0;
+ newTableSchema[l].iNoOfDecs = 0;
+
+ dNewTable->SetVersion();
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist())
+ dNewTable->SetCreateMemoBlockSize( Memo->GetBlockSize() );
+ #endif
+
+ // Call the create a table function
+ if(( iRc = dNewTable->CreateTable( sNewTableName, sNewTableAlias, newTableSchema, iOverlay, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::CopyDbfStructure() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ if( newTableSchema )
+ free( newTableSchema );
+
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Create a new tag (index) for this dbf file (table).
+/*!
+ This routine creates a new tag (index) on a dbf file. The library currently supports NDX, MDX ans TDX.
+ indices. If you don't have a specific need for an NDX file, use MDX.
+
+ \param sIxType "MDX", "NDX" or "NTX".
+ \param sName Index or tag name.
+ \param sKey Index key expression,
+ \param sFilter Filter expression. Not applicable for NDX indices.
+ \param iDescending xbTrue for descending. Not available for NDX indices.<br>
+ xbFalse - ascending
+ \param iUnique xbTrue - Unique index<br>xbFalse - Not unique index.
+ \param iOverLay xbTrue - Overlay if exists<br>
+ xbFalse - Don't overlay if it exists.
+ \param pIxOut Pointer to pointer of output index object.
+ \param vpTagOut Pointer to pointer of newly created tag,
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter,
+ xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **pIxOut, void **vpTagOut ){
+
+ // this routine is used to open indices and link to files
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif
+
+ try{
+ xbString sType = sIxType;
+ sType.ToUpperCase();
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw( iRc );
+ }
+ bLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( sIxType == "" ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sIxType == "NDX" ){
+ xbIxNdx *ixNdx = new xbIxNdx( this );
+
+ if(( iRc = ixNdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = AddIndex( ixNdx, sIxType )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ *pIxOut = ixNdx;
+
+
+
+ // Set the current tag if one not already set
+ if( sCurIxType == "" ){
+ sCurIxType = "NDX";
+ pCurIx = ixNdx;
+ vpCurIxTag = ixNdx->GetTag(0);
+ }
+
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sIxType == "MDX" ){
+
+ if( GetVersion() == 3 ){ // MDX indexes were version 4 and higher
+ iErrorStop = 140;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ xbIxMdx *ixMdx;
+ xbString s;
+ // look through the index list and see if there is an mdx pointer we can grab
+ xbBool bMdxFound = xbFalse;
+ xbIxList *ixList = GetIxList();
+ while( ixList && !bMdxFound ){
+ s = ixList->sFmt->Str();
+ if( s == "MDX" ){
+ ixMdx = (xbIxMdx *) ixList->ix;
+ bMdxFound = xbTrue;
+ }
+ ixList = ixList->next;
+ }
+
+ if( !bMdxFound )
+ ixMdx = new xbIxMdx( this );
+
+ if(( iRc = ixMdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ if( !bMdxFound ){
+ if(( iRc = AddIndex( ixMdx, "MDX" )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+ cIndexFlag = 0x01;
+ if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ *pIxOut = ixMdx;
+
+ // set the current tag if one not already set
+ if( sCurIxType == "" ){
+ sCurIxType = "MDX";
+ pCurIx = ixMdx;
+ vpCurIxTag = ixMdx->GetTag(0);
+ }
+ #endif
+
+ #ifdef XB_TDX_SUPPORT
+ } else if( sIxType == "TDX" ){
+
+ if( GetVersion() == 3 ){ // TDX indexes were version 4 and higher
+ iErrorStop = 140;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ xbIxTdx *ixTdx;
+ xbString s;
+ // look through the index list and see if there is an mdx pointer we can grab
+ xbBool bTdxFound = xbFalse;
+ xbIxList *ixList = GetIxList();
+ while( ixList && !bTdxFound ){
+ s = ixList->sFmt->Str();
+ if( s == "TDX" ){
+ ixTdx = (xbIxTdx *) ixList->ix;
+ bTdxFound = xbTrue;
+ }
+ ixList = ixList->next;
+ }
+ if( !bTdxFound )
+ ixTdx = new xbIxTdx( this );
+
+ if(( iRc = ixTdx->CreateTag( sName, sKey, sFilter, iDescending, iUnique, iOverLay, vpTagOut )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if( !bTdxFound ){
+ if(( iRc = AddIndex( ixTdx, "TDX" )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ }
+ *pIxOut = ixTdx;
+
+ // set the current tag if one not already set
+ if( sCurIxType == "" ){
+
+ sCurIxType = "TDX";
+ pCurIx = ixTdx;
+ vpCurIxTag = ixTdx->GetTag(0);
+ }
+
+ #endif
+
+ } else {
+ iErrorStop = 200;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked )
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Delete or undelete all records in a dbf file (table).
+/*!
+ This routine deletes or un-deletes all records. The xbase file format contains
+ a leading one byte character used for flagging a record as deleted. When a record
+ is deleted, it's not physically removed from the file, the first byte is flagged as deleted.
+
+ \param iOption 0 - Delete all records.<br>
+ 1 - Un-delete all deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteAll( xbInt16 iOption )
+{
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbUInt32 ulRecCnt;
+
+ try{
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if( ulRecCnt == 0 )
+ return XB_NO_ERROR;
+ for( xbUInt32 ul = 0; ul < ulRecCnt; ul++ ){
+ if(( iRc = GetRecord( ul+1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ if( iOption == 0 ){ /* delete all option */
+ if( !RecordDeleted()){
+ if(( iRc = DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ } else { /* undelete all option */
+ if( RecordDeleted()){
+ if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::DeleteAll() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief
+/*!
+ This routine deletes all indices associated with the dbf file.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DeleteAllIndexFiles(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif // XB_LOCKING_SUPPORT
+
+ #ifdef XB_INF_SUPPORT
+ xbString sIxName;
+ #endif // XB_INF_SUPPORT
+
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw( iRc );
+ }
+ bLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ // close any open index files, delete it, remove from the ix list
+ while( ixList ){
+
+ // next two lines for debugging
+ ixList->ix->GetFileNamePart( sIxName );
+ ixList->ix->Close();
+ if(( iRc = ixList->ix->xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ #ifdef XB_INF_SUPPORT
+ // if XB_INF_SUPPORT is enabled, all open non prod indices should be in here
+ if( *ixList->sFmt != "MDX" && *ixList->sFmt != "TDX" ){ // production and temp indices not stored in .INF dataset
+ if(( iRc = ixList->ix->GetFileNamePart( sIxName )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = AssociateIndex( *ixList->sFmt, sIxName, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ #endif
+ RemoveIndex( ixList->ix );
+ }
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteAllIndexFiles() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked )
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Delete all records.
+/*!
+ This routine deletes all the records in a table / dbf file.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteAllRecords(){
+ return DeleteAll( 0 );
+}
+
+/************************************************************************/
+#ifdef XB_INF_SUPPORT
+//! @brief Delete .INF File
+/*!
+ The original Dbase (TM) software supported non production indices (NDX) and production indices (MDX).
+ The production indices are opened automatically when the DBF file is opened but the non-production
+ indices are not. This method is specific to the Xbas64 library and providex a means to link non production
+ NDX index files to the DBF file so they will be opened automatically when the DBF file is opened.<br>
+
+ This routine requires INF support be enabled when building the library.<br>
+ This routine deletes the .INF file.<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DeleteInfData(){
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbString sInfFileName;
+ if(( iRc = GetInfFileName( sInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ xbFile f( xbase );
+ f.SetFileName( sInfFileName );
+ if( f.FileExists()){
+ if(( iRc = f.xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INF_SUPPORT
+
+/************************************************************************/
+//! @brief Delete the current record.
+/*!
+ This routine flags the current record for deletion if it's not already flagged.
+
+ \returns XB_NO_ERROR<br>
+ XB_INVALID_RECORD
+*/
+
+xbInt16 xbDbf::DeleteRecord(){
+ if( RecBuf && ulCurRec > 0 ){
+ if( RecBuf[0] != 0x2a){
+ if( iDbfStatus != XB_UPDATED ){
+ iDbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, uiRecordLen ); // save off original before making any updates
+ }
+ RecBuf[0] = 0x2a;
+ }
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_INVALID_RECORD;
+}
+/************************************************************************/
+//! @brief Delete a table.
+/*!
+ This routine deletes a given table, associated index files if any, the
+ memo file if any and the .INF file if any.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteTable(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bTableLocked = xbFalse;
+ #endif
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ } else {
+ bTableLocked = xbTrue;
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = DeleteAllIndexFiles()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ #ifdef XB_INF_SUPPORT
+ if(( iRc = DeleteInfData()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ #endif // XB_INF_SUPPORT
+ #endif // XB_INDEX_SUPPORT
+
+ #ifdef XB_MEMO_SUPPORT
+ xbInt16 iMemoFldCnt = GetMemoFieldCnt();
+ xbString sMemoFileName;
+ if(iMemoFldCnt > 0 ){
+ sMemoFileName = Memo->GetFqFileName();
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ if(( iRc = Close()) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ if(( iRc = xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ xbFile f( xbase );
+ if(( iRc = f.xbRemove( sMemoFileName )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteTable() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( bTableLocked )
+ LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Delete an index tag.
+
+/*!
+ This routine deletes an index tag
+ \param sIxType Either "NDX", "MDX" or "TDX".<br>
+ \param sName Tag name to delete.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DeleteTag( const xbString &sIxType, const xbString &sName ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbIx *pIx = NULL;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bTableLocked = xbFalse;
+ #endif
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !GetTableLocked() ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ } else {
+ bTableLocked = xbTrue;
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if( sIxType == "" ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sIxType == "NDX" ){
+
+ xbIxList *ixl = ixList;
+ xbBool bDone = xbFalse;
+ while( ixl && !bDone ){
+
+ if( ixl->ix->GetTagName( NULL ) == sName ){
+ bDone = xbTrue;
+
+ // remove from .INF if it's there
+ #ifdef XB_INF_SUPPORT
+ if(( iRc = AssociateIndex( "NDX", sName, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ #endif // XB_INF_SUPPORT
+
+ if(( iRc = ixl->ix->DeleteTag( ixl->ix->GetTag( 0 ))) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if( !ixList || ixl->ix == pCurIx )
+ SetCurTag( "", NULL, NULL );
+
+ }
+ ixl = ixl->next;
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sIxType == "MDX" ){
+ xbIxList *ixl = ixList;
+ xbIxList *ixlNext;
+ xbIxList *ixlPrev = NULL;
+ xbBool bDone = xbFalse;
+ xbIxMdx *pMdx;
+ xbMdxTag *pMdxTag;
+ xbInt16 iTagCnt = 0;
+
+ while( ixl && !bDone ){
+ ixlNext = ixl->next;
+ pMdx = (xbIxMdx *) ixl->ix;
+ iTagCnt = pMdx->GetTagCount();
+ for( xbInt16 i = 0; i < iTagCnt && !bDone; i++ ){
+ pMdxTag = (xbMdxTag *) pMdx->GetTag( i );
+ if( pMdx->GetTagName( pMdxTag ) == sName ){
+ bDone = xbTrue;
+ iRc = pMdx->DeleteTag( pMdxTag );
+ if( iRc > 0 ){
+ // Successful delete of only tag in production mdx file - need to remove it from the list, update the dbf header
+ cIndexFlag = 0x00;
+ if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if( ixlPrev == NULL ){
+ // std::cout << "setting ixList to null or should be\n";
+ ixList = ixlNext;
+ } else {
+ ixlPrev = ixlNext;
+ }
+ } else if( iRc < 0 ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ if( !ixList || ixl->ix == pCurIx )
+ SetCurTag( "", NULL, NULL );
+ }
+ }
+ ixlPrev = ixl;
+ ixl = ixlNext;
+ }
+
+ if( !bDone )
+ return XB_INVALID_TAG;
+ #endif
+
+ #ifdef XB_TDX_SUPPORT
+ } else if( sIxType == "TDX" ){
+ xbIxList *ixl = ixList;
+ xbIxList *ixlNext;
+ xbIxList *ixlPrev = NULL;
+ xbBool bDone = xbFalse;
+ xbIxTdx *pTdx;
+ xbMdxTag *pMdxTag;
+ xbInt16 iTagCnt = 0;
+
+ while( ixl && !bDone ){
+ ixlNext = ixl->next;
+ pTdx = (xbIxTdx *) ixl->ix;
+ iTagCnt = pTdx->GetTagCount();
+ for( xbInt16 i = 0; i < iTagCnt && !bDone; i++ ){
+ pMdxTag = (xbMdxTag *) pTdx->GetTag( i );
+ if( pTdx->GetTagName( pMdxTag ) == sName ){
+ bDone = xbTrue;
+ iRc = pTdx->DeleteTag( pMdxTag );
+ if( iRc > 0 ){
+ // Successful delete of only tag in production mdx file - need to remove it from the list, update the dbf header
+ cIndexFlag = 0x00;
+ if(( iRc = WriteHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if(( iRc = RemoveIndex( ixl->ix )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if( ixlPrev == NULL ){
+ // std::cout << "setting ixList to null or should be\n";
+ ixList = ixlNext;
+ } else {
+ ixlPrev = ixlNext;
+ }
+ } else if( iRc < 0 ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ if( ixList )
+ std::cout << "ixlist not null\n";
+ else
+ std::cout << "ixlist null\n";
+
+ if( !ixList || ixl->ix == pCurIx )
+ SetCurTag( "", NULL, NULL );
+ }
+ }
+ ixlPrev = ixl;
+ ixl = ixlNext;
+ }
+
+ if( !bDone )
+ return XB_INVALID_TAG;
+
+ #endif
+
+
+
+ } else {
+ iErrorStop = 180;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+
+ }
+ catch (xbInt16 iRc ){
+ if( pIx ) delete pIx;
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && GetTableLocked() ){
+ LockTable( XB_UNLOCK );
+ }
+ #endif // XB_LOCKING_SUPPORT
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bTableLocked ){
+ LockTable( XB_UNLOCK );
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+/************************************************************************/
+//! @brief Dump dbf file header.
+/*!
+ This routine dumps dbf header information to the console.
+
+ \param iOption 1 = Print header only<br>
+ 2 = Field data only<br>
+ 3 = Header and Field data<br>
+ 4 = Header, Field and Memo header data if applicable
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::DumpHeader( xbInt16 iOption ){
+ int i;
+ int iMemoCtr = 0;
+
+ if( iOption < 1 || iOption > 4 )
+ return XB_INVALID_OPTION;
+
+ xbInt16 iRc = ReadHeader( xbTrue, 0 );
+ if( iRc != XB_NO_ERROR )
+ return iRc;
+
+// if( iDbfStatus == XB_CLOSED )
+// return XB_NOT_OPEN;
+
+ std::cout << "\nDatabase file " << GetFqFileName() << std::endl << std::endl;
+
+ if( iOption != 2 ){
+ std::cout << "File header data:" << std::endl;
+
+ xbInt16 sVer = DetermineXbaseTableVersion( cVersion );
+
+ if( sVer == 3 )
+ std::cout << "Dbase III file" << std::endl;
+ else if ( sVer == 4 )
+ std::cout << "Dbase IV file" << std::endl << std::endl;
+ else if ( sVer == 5 )
+ std::cout << "Dbase V file" << std::endl << std::endl;
+ else if ( sVer == 7 )
+ std::cout << "Dbase VII file" << std::endl << std::endl;
+ else
+ std::cout << "Unknown Version" << std::endl;
+
+ /* display the bit stream */
+ unsigned char c, tfv, displayMask = 1 << 7;
+ tfv = cVersion;
+ std::cout << "File descriptor bits = ";
+ for( c = 1; c<= 8; c++ ){
+ std::cout << (tfv & displayMask ? '1' : '0');
+ tfv <<= 1;
+ }
+ std::cout << std::endl;
+
+ std::cout << "Descriptor bits legend:" << std::endl;
+ std::cout << " 0-2 = version number" << std::endl;
+ std::cout << " 3 = presence of dBASE IV memo" << std::endl;
+ std::cout << " 4-6 = SQL table presence" << std::endl;
+ std::cout << " 7 = Presence of any memo file (dBASE III PLUS or dBASE IV)" << std::endl << std::endl;
+
+ std::cout << "Last update date = "
+ << (int) cUpdateMM << "/" << (int) cUpdateDD << "/" << (int) cUpdateYY % 100 << std::endl;
+
+ std::cout << "Header length = " << uiHeaderLen << std::endl;
+ std::cout << "Record length = " << uiRecordLen << std::endl;
+ std::cout << "Records in file = " << ulNoOfRecs << std::endl << std::endl << std::endl;
+
+ std::cout << "Transaction Flag = ";
+ xbase->BitDump( cTransactionFlag );
+ std::cout << std::endl;
+
+ std::cout << "Encryption Flag = ";
+ xbase->BitDump( cEncryptionFlag );
+ std::cout << std::endl;
+
+ std::cout << "Index Flag = ";
+ xbase->BitDump( cIndexFlag );
+ std::cout << std::endl;
+
+ std::cout << "Lang Driver = " << (int) cIndexFlag << " - ";
+ xbase->BitDump( cIndexFlag );
+ std::cout << std::endl;
+ #ifdef XB_INDEX_SUPPORT
+ std::cout << "Open Index Files = " << GetPhysicalIxCnt() << std::endl;
+ #endif // XB_INDEX_SUPPORT
+ }
+
+ if( iOption != 1 ){
+ char c;
+ std::cout << "Field Name Type Length Decimals IxFlag" << std::endl;
+ std::cout << "---------- ---- ------ -------- ------" << std::endl;
+ for( i = 0; i < iNoOfFields; i++ ){
+
+ SchemaPtr[i].cIxFlag ? c = 'Y' : c = ' ';
+
+ if( SchemaPtr[i].cType == 'C' && SchemaPtr[i].cNoOfDecs > 0 )
+ printf( "%10s %1c %4d %4d %c\n", SchemaPtr[i].cFieldName,
+ SchemaPtr[i].cType, SchemaPtr[i].cFieldLen, 0, c );
+ else
+ printf( "%10s %1c %4d %4d %c\n", SchemaPtr[i].cFieldName,
+ SchemaPtr[i].cType, SchemaPtr[i].cFieldLen, SchemaPtr[i].cNoOfDecs, c );
+
+ if( SchemaPtr[i].cType == 'M' )
+ iMemoCtr++;
+ }
+ }
+ std::cout << std::endl;
+
+#ifdef XB_MEMO_SUPPORT
+ if( iOption > 3 && iMemoCtr > 0 )
+ Memo->DumpMemoHeader();
+#endif
+
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Dump record
+/*!
+ Dump the contents of the specified record
+
+
+ \param ulRecNo Record number of record to be dumped.
+ \param iOutputDest 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+
+ \param iOutputFmt 0 = with field names<br>
+ 1 = 1 line per rec, no field names<br>
+ 2 = 1 line per rec, first line is a list of field names.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::DumpRecord( xbUInt32 ulRecNo, xbInt16 iOutputDest, xbInt16 iOutputFmt ) {
+ int i, iRc = XB_NO_ERROR;
+
+ xbString sTemp;
+ xbString s2;
+ if( ulRecNo == 0 || ulRecNo > ulNoOfRecs )
+ return XB_INVALID_RECORD;
+
+ if( ulCurRec != ulRecNo ){
+ iRc = GetRecord( ulRecNo );
+ if( iRc != XB_NO_ERROR )
+ return iRc;
+ }
+
+ if( iOutputFmt >= 1 ){
+ if( iOutputFmt == 2 ){
+ sTemp = "RecNo,DEL";
+ for( i = 0; i < iNoOfFields; i++ ){
+ s2 = SchemaPtr[i].cFieldName;
+ s2.Trim();
+ sTemp += ",";
+ sTemp += s2;
+ }
+ xbase->WriteLogMessage( sTemp.Str(), iOutputDest );
+ }
+
+ if( RecordDeleted() )
+ s2.Sprintf( "%ld,DEL", ulRecNo );
+ else
+ s2.Sprintf( "%ld,", ulRecNo );
+
+ for( i = 0; i < iNoOfFields; i++ ){
+ GetField( i, sTemp );
+ sTemp.Trim();
+ s2.Sprintf( "%s,'%s'", s2.Str(), sTemp.Str());
+ }
+ xbase->WriteLogMessage( s2.Str(),iOutputDest );
+ return XB_NO_ERROR;
+ }
+
+ sTemp.Sprintf( "\nRec Number: %ld", ulRecNo );
+ xbase->WriteLogMessage( sTemp.Str(),iOutputDest);
+
+ if( RecordDeleted())
+ xbase->WriteLogMessage( "Record flagged as deleted", iOutputDest );
+
+
+ #ifdef XB_MEMO_SUPPORT
+ xbString sMemo;
+ #endif
+
+ for( i = 0; i < iNoOfFields; i++ ){
+
+ #ifdef XB_MEMO_SUPPORT
+ GetField( i, sTemp );
+ sTemp.Trim();
+
+ if(SchemaPtr[i].cType == 'M'){
+ GetMemoField( i, sMemo );
+ if( sMemo.Len() > 70 )
+ sMemo.Resize( 70 );
+ s2.Sprintf ( "%c %s = '%s'", SchemaPtr[i].cType, SchemaPtr[i].cFieldName, sMemo.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+
+ /*
+ xbUInt32 ulMlen;
+ if( MemoFieldExists( i )){
+ Memo->GetMemoFieldLen( i, ulMlen );
+ s2.Sprintf( " Len = %d", ulMlen );
+ }
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ */
+
+ }
+ else{
+ s2.Sprintf ( "%c %s = '%s'", SchemaPtr[i].cType, SchemaPtr[i].cFieldName, sTemp.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ }
+ #else
+ GetField( i, sTemp );
+ sTemp.Trim();
+ s2.Sprintf( "%s = '%s'", SchemaPtr[i].cFieldName, sTemp.Str());
+ xbase->WriteLogMessage( s2.Str(), iOutputDest);
+ #endif
+
+ }
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+
+
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Dump the table lock status
+/*!
+ Debugging routine. Dumps the table lock status to the console.
+ \returns void
+*/
+
+void xbDbf::DumpTableLockStatus() const {
+
+ std::cout << "File Lock Retry Count = [" << GetLockRetryCount() << "]" << std::endl;
+ std::cout << "File Lock Flavor = [";
+ switch (GetLockFlavor()){
+ case 1:
+ std::cout << "Dbase]" << std::endl;
+ break;
+ case 2:
+ std::cout << "Clipper]" << std::endl;
+ break;
+ case 3:
+ std::cout << "Fox]" << std::endl;
+ break;
+ case 9:
+ std::cout << "Xbase64]" << std::endl;
+ break;
+ default:
+ std::cout << "Unknown]" << std::endl;
+ break;
+ }
+ std::cout << "File Auto Lock = [";
+
+ if( GetAutoLock())
+ std::cout << "ON]" << std::endl;
+ else
+ std::cout << "OFF]" << std::endl;
+ if( GetHeaderLocked())
+ std::cout << "Header Locked = [TRUE]\n";
+ else
+ std::cout << "Header Locked = [FALSE]\n";
+
+ if( GetTableLocked())
+ std::cout << "Table Locked = [TRUE]\n";
+ else
+ std::cout << "Table Locked = [FALSE]\n";
+
+ if( GetAppendLocked() > 0 )
+ std::cout << "Append Locked = [" << GetAppendLocked() << "]\n";
+ else
+ std::cout << "Append Locked = [FALSE]\n";
+
+ #ifdef XB_MEMO_SUPPORT
+ if( GetMemoLocked())
+ std::cout << "Memo Locked = [TRUE]\n";
+ else
+ std::cout << "Memo Locked = [FALSE]\n";
+ #endif // XB_MEMO_SUPPORT
+
+ xbLinkListNode<xbUInt32> * llN = GetFirstRecLock();
+ if( llN ){
+ while( llN ){
+ std::cout << "Record Locked = [" << llN->GetKey() << "]\n";
+ llN = llN->GetNextNode();
+ }
+ } else {
+ std::cout << "Record Locked = [None]\n";
+ }
+}
+#endif // XB_LOCKING_SUPPORT
+#endif // XB_DEBUG_SUPPORT
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get the append locked bytes status
+/*!
+ \returns The record number of the new record for the append lock operation.
+*/
+
+xbUInt32 xbDbf::GetAppendLocked() const {
+ return this->ulAppendLocked;
+}
+
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+//! @brief Get auto commit setting.
+/*!
+
+ This routine returns the table setting if set, otherwise returns the system
+ level setting.
+
+ \returns Not 0 - Auto commit on for this table.<br>
+ 0 - Auto commit off for this table.
+*/
+
+xbInt16 xbDbf::GetAutoCommit() const {
+ return GetAutoCommit( 1 );
+}
+
+/************************************************************************/
+//! @brief Get auto commit setting.
+/*!
+
+ \param iOption 0 - Specific setting for this table<br>
+ 1 - If this table should be auto updated (takes DBMS setting into account)
+ \returns Not 0 - Auto commit on for this table.<br>
+ 0 - Auto commit off for this table.
+*/
+
+xbInt16 xbDbf::GetAutoCommit( xbInt16 iOption ) const {
+ if( iOption == 1 && iAutoCommit == -1 )
+ return xbase->GetDefaultAutoCommit();
+ else
+ return iAutoCommit;
+}
+
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get Auto Lock setting.
+/*!
+ \returns Auto lock setting.
+*/
+xbInt16 xbDbf::GetAutoLock() const{
+ return iAutoLock;
+}
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Get the memo file block size used when creating a memo file.
+/*!
+ \returns Memo block size.
+*/
+xbUInt32 xbDbf::GetCreateMemoBlockSize() const {
+ return ulCreateMemoBlockSize;
+}
+#endif // XB_MEMO_SUPPORT
+
+/************************************************************************/
+//! @brief Get a pointer to the current index object.
+/*!
+ \returns Pointer to current index.
+*/
+#ifdef XB_INDEX_SUPPORT
+xbIx *xbDbf::GetCurIx() const {
+ return pCurIx;
+}
+/************************************************************************/
+//! @brief Get pointer to current tag for the current index.
+/*!
+ An index file can have one or more tags
+ NDX index files have one tag per file.
+ MDX index files can can have up to 47 tags per file.
+ TDX index files can can have up to 47 tags per file.
+
+ \returns Pointer to current tag.
+*/
+void *xbDbf::GetCurTag() const {
+ return vpCurIxTag;
+}
+/************************************************************************/
+//! @brief Get the current index type.
+/*!
+ \returns NDX for single tag index.<br>
+ MDX for production multi tag index.<br>
+ TDX for temporary tag index.
+*/
+const xbString &xbDbf::GetCurIxType() const {
+ return sCurIxType;
+}
+
+/************************************************************************/
+//! @brief Get the current tag name.
+/*!
+ \returns Current Tag Name.<br>
+*/
+
+const xbString &xbDbf::GetCurTagName() const {
+
+ if( pCurIx )
+ return pCurIx->GetTagName( vpCurIxTag );
+ else
+ return sNullString;
+}
+
+/************************************************************************/
+//! @brief GetFirstKey for tag.
+/*!
+
+ Position to the first key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetFirstKey(){
+ if( pCurIx )
+ return pCurIx->GetFirstKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetHeaderLen for dbf
+/*!
+
+ Returns the length of the header portion of the dbf file
+ \returns Length of header protion of dbf file
+
+*/
+xbUInt16 xbDbf::GetHeaderLen() const {
+ return uiHeaderLen;
+}
+
+/************************************************************************/
+//! @brief GetLastKey for tag.
+/*!
+
+ Position to the last key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetLastKey(){
+ if( pCurIx )
+ return pCurIx->GetLastKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetNextKey for tag.
+/*!
+
+ Position to the next key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetNextKey(){
+ if( pCurIx )
+ return pCurIx->GetNextKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief GetPrevKey for tag.
+/*!
+
+ Position to the previous key for the current tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::GetPrevKey(){
+ if( pCurIx )
+ return pCurIx->GetPrevKey( vpCurIxTag, 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param sKey String key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbString &sKey ){
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, sKey.Str(), (xbInt32) sKey.Len(), 1 );
+ else
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param dtKey Date key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbDate &dtKey ){
+
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, dtKey, 1 );
+ else
+ return XB_INVALID_TAG;
+
+}
+/************************************************************************/
+//! @brief Find record for key.
+/*!
+
+ Find a key and position to record if key found
+
+ \param dtKey Date key to find
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf::Find( xbDouble &dKey ){
+
+ if( pCurIx )
+ return pCurIx->FindKey( vpCurIxTag, dKey, 1 );
+ else
+ return XB_INVALID_TAG;
+
+}
+
+
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Return true if dbf file empty or positioned to the first record
+/*!
+ \returns Returns true if dbf file is empty or positioned on the first record.
+*/
+xbBool xbDbf::GetBof() {
+/*
+ if( GetRecordCount() == 0 || GetCurRecNo() == 1 )
+ return xbTrue;
+ else
+ */
+ return xbFalse;
+}
+/************************************************************************/
+//! @brief Return true if dbf file empty or positioned to the last record
+/*!
+ \returns Returns true if error, dbf file is empty or positioned on the last record.
+*/
+xbBool xbDbf::GetEof() {
+
+ // xbUInt32 ulRecCnt = GetRecordCount();
+
+ xbUInt32 ulRecCnt;
+ xbInt16 iRc = GetRecordCnt( ulRecCnt );
+
+ if( iRc != XB_NO_ERROR || ulRecCnt == 0 || GetCurRecNo() == ulRecCnt )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/************************************************************************/
+//! @brief Return the current record number.
+/*!
+ \returns Returns the current record number.
+*/
+xbUInt32 xbDbf::GetCurRecNo() const {
+ return ulCurRec;
+}
+
+/************************************************************************/
+//! @brief Return the current dbf status.
+/*!
+ \returns 0 = closed<br>
+ 1 = open<br>
+ 2 = updates pending<br>
+*/
+xbInt16 xbDbf::GetDbfStatus() const {
+ return iDbfStatus;
+}
+/************************************************************************/
+//! @brief Return the number of fields in the table.
+/*!
+ \returns The number of fields in the table.
+*/
+xbInt32 xbDbf::GetFieldCnt() const {
+ return iNoOfFields;
+}
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get the first first record lock.
+/*!
+ Get the first record lock from a linked list of record locks.
+ \returns First record lock.
+*/
+xbLinkListNode<xbUInt32> * xbDbf::GetFirstRecLock() const {
+ return lloRecLocks.GetHeadNode();
+}
+#endif // XB_LOCKING_SUPPORT
+/************************************************************************/
+//! @brief Get the first record.
+/*!
+ Get the first not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetFirstRecord()
+{
+ return GetFirstRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the first record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the first record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the first active record.<br>
+ XB_DELETED_RECS - Get the first deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetFirstRecord( xbInt16 iOption )
+{
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+
+ xbInt16 iRc = GetRecord( 1L );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec < ulNoOfRecs )
+ iRc = GetRecord( ulCurRec + 1 );
+ else
+ return XB_EOF;
+
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Return lock status of the table header
+/*! \returns DBF header lock status
+*/
+
+xbBool xbDbf::GetHeaderLocked() const {
+ return this->bHeaderLocked;
+}
+#endif // XB_LOCKING_SUPPORT
+
+#ifdef XB_INDEX_SUPPORT
+//! @brief Return pointer to list of index files for the table.
+/*!
+ \returns Returns an xbIxList * pointer to list of open index files.
+*/
+
+xbIxList *xbDbf::GetIxList() const{
+ return ixList;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+/************************************************************************/
+//! @brief Get the last record.
+/*!
+ Get the last not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetLastRecord()
+{
+ return GetLastRecord( XB_ACTIVE_RECS );
+}
+/************************************************************************/
+//! @brief Get the last record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the last record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the last active record.<br>
+ XB_DELETED_RECS - Get the last deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetLastRecord( xbInt16 iOption )
+{
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+
+ xbInt16 iRc = GetRecord( ulNoOfRecs );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec > 1 )
+ iRc = GetRecord( ulCurRec - 1 );
+ else
+ return XB_EOF;
+
+ return iRc;
+}
+
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get lock flavor.
+/*!
+ This routine is currently in place to provide structure for future locking
+ schemes that may differ from the legacy DBase (TM) locking scheme.
+ \returns Always 1.
+*/
+
+xbInt16 xbDbf::GetLockFlavor() const{
+ if( iLockFlavor == -1 )
+ return xbase->GetDefaultLockFlavor();
+ else
+ return iLockFlavor;
+}
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get the lock status of the memo file.
+/*!
+ \returns Lock status of memo file.
+*/
+xbBool xbDbf::GetMemoLocked() const {
+ if( MemoFieldsExist())
+ return Memo->GetMemoLocked();
+ else
+ return xbFalse;
+}
+#endif // XB_LOCKING_SUPPORT
+
+/************************************************************************/
+//! @brief Get pointer to Memo object.
+/*!
+ \returns This routine returns the pointer to the memo object.
+*/
+
+xbMemo * xbDbf::GetMemoPtr(){
+ return Memo;
+}
+
+#endif // XB_MEMO_SUPPORT
+
+
+/************************************************************************/
+#ifdef XB_INF_SUPPORT
+//! @brief Return the .INF file name
+/*!
+ If NDXIDX support is enabled in the library, and a non production (ndx)
+ has been associated with the dbf file, the .INF file name can be retrieved
+ with this routine.
+
+ \param sInfFileName Output string containing .INF file name.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetInfFileName( xbString &sInfFileName ){
+
+ sInfFileName = GetFqFileName();
+ xbUInt32 lLen = sInfFileName.Len();
+ if( lLen < 5 )
+ return XB_FILE_NOT_FOUND;
+ sInfFileName.PutAt(lLen-2, 'I');
+ sInfFileName.PutAt(lLen-1, 'N');
+ sInfFileName.PutAt(lLen, 'F');
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Return first node of linked list of .INF items.
+/*!
+ \returns List of .INF entries.
+*/
+
+xbLinkListNode<xbString> * xbDbf::GetInfList() const{
+ return llInfData.GetHeadNode();
+}
+#endif // XB_INF_SUPPORT
+
+
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+ Get the next not deleted record. This routines skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetNextRecord(){
+ return GetNextRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+ \param iOption XB_ALL_RECS - Get the next record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the next active record.<br>
+ XB_DELETED_RECS - Get the next deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetNextRecord( xbInt16 iOption ){
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+ else if( ulCurRec >= ulNoOfRecs )
+ return XB_EOF;
+ xbInt16 iRc = GetRecord( ulCurRec + 1 );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec < ulNoOfRecs )
+ iRc = GetRecord( ulCurRec + 1 );
+ else
+ return XB_EOF;
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get the next record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the next record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the next active record.<br>
+ XB_DELETED_RECS - Get the next deleted record.<br>
+ \param ulStartRec Get next record, starting from ulStartRec.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetNextRecord( xbInt16 iOption , xbUInt32 ulStartRec ){
+
+ if( iOption == 0 )
+ return GetNextRecord();
+ else if( iOption == 1 ){
+ if( ulStartRec > 0 )
+ ulCurRec = ulStartRec;
+ xbInt16 iRc = GetNextRecord();
+ while( iRc == XB_NO_ERROR && RecordDeleted())
+ iRc = GetNextRecord();
+ return iRc;
+ }
+ else
+ return XB_INVALID_OPTION;
+}
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Physical count of open index files.
+/*!
+
+ Returns a physical count of open index files for the dbf file. An index file
+ can contain one or more tags.
+ \returns Count of open index files.
+*/
+
+xbInt32 xbDbf::GetPhysicalIxCnt() const {
+
+ xbInt32 lCnt = 0;
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *p = ixList;
+ while( p ){
+ lCnt++;
+ p = p->next;
+ }
+ #endif
+ return lCnt;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+/************************************************************************/
+//! @brief Get the previous record.
+/*!
+ Get the previous not deleted record. This routine skips over any deleted records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::GetPrevRecord()
+{
+ return GetPrevRecord( XB_ACTIVE_RECS );
+}
+
+/************************************************************************/
+//! @brief Get the previous record.
+/*!
+
+ \param iOption XB_ALL_RECS - Get the previous record, deleted or not.<br>
+ XB_ACTIVE_RECS - Get the previous active record.<br>
+ XB_DELETED_RECS - Get the previous deleted record.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetPrevRecord( xbInt16 iOption ){
+ if( ulNoOfRecs == 0 )
+ return XB_EMPTY;
+ else if( ulCurRec <= 1L )
+ return XB_BOF;
+ xbInt16 iRc = GetRecord( ulCurRec - 1 );
+ while( iRc == XB_NO_ERROR &&
+ ((RecordDeleted() && iOption == XB_ACTIVE_RECS) ||
+ (!RecordDeleted() && iOption == XB_DELETED_RECS)))
+ if( ulCurRec > 1 )
+ iRc = GetRecord( ulCurRec - 1 );
+ else
+ return XB_BOF;
+
+ return iRc;
+}
+
+
+/************************************************************************/
+//! @brief Get record for specified record number.
+/*!
+ Retrieve a record from disk and load it into the record buffer. If auto commit
+ is enabled and there are pending updates, this routine will flush the updates
+ to disk before proceeding to ulRecNo.
+
+ \param ulRecNo - Record number to retrieve.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetRecord( xbUInt32 ulRecNo ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ /* verify the file is open */
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 100;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+ if( iDbfStatus == XB_UPDATED ){
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ }
+
+
+ // std::cout << "xbdbf::GetRecord: " << ulRecNo << " " << ulNoOfRecs << "\n";
+ if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){
+ iErrorStop = 130;
+ iRc = XB_INVALID_RECORD;
+ throw iRc;
+ }
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ if( bBlockReadEnabled )
+ return pRb->GetRecord( ulRecNo );
+ #endif // XB_BLOCK_READ_SUPPORT
+
+
+ if(( xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+
+ if( xbFread( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ iRc = XB_READ_ERROR;
+ throw iRc;
+ }
+ ulCurRec = ulRecNo;
+ }
+
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetRecord() Exception Caught. Error Stop = [%d] iRc = [%d] record = [%d]", iErrorStop, iRc, ulRecNo );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get pointer to record buffer
+/*!
+ \param iOpt 0 for RecBuf (current) or 1 for RecBuf2 (original contents)
+
+ \returns Pointer to record buffer.
+*/
+char * xbDbf::GetRecordBuf( xbInt16 iOpt ) const {
+ if( iOpt )
+ return RecBuf2;
+ else
+ return RecBuf;
+}
+
+/************************************************************************/
+//! @brief Get the current number of records in the dbf data file.
+/*!
+ \returns Record count or <a href="xbretcod_8h.html">Return Codes</a>
+*/
+/*
+xbUInt32 xbDbf::GetRecordCount(){
+
+ xbUInt32 ulCnt;
+ xbInt16 iRc = GetRecordCnt( ulCnt );
+ if( iRc < 0 )
+ return (xbUInt32) iRc;
+ else
+ return ulCnt;
+}
+*/
+/************************************************************************/
+//! @brief Get the current number of records in the dbf data file.
+/*!
+ \param ulRecCnt Output number of records in file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::GetRecordCnt( xbUInt32 & ulRecCnt )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif // XB_LOCKING_SUPPORT
+
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ } else
+ bLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if((iRc = ReadHeader(1,1)) != XB_NO_ERROR){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetRecordCnt() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked ){
+ LockHeader( XB_UNLOCK );
+ }
+ #endif
+
+ ulRecCnt = ulNoOfRecs;
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get the dbf record length.
+/*!
+ \returns Record length.
+*/
+xbUInt16 xbDbf::GetRecordLen() const {
+ return uiRecordLen;
+}
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Get table locked status
+/*!
+ \returns Table lock status.
+*/
+xbBool xbDbf::GetTableLocked() const {
+ return this->bTableLocked;
+}
+#endif // XB_LOCKING_SUPPORT
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Get tag list for dbf file.
+/*!
+ This routine returns a list of tags for the file.<br>
+
+ The library is structured to support one or more files of the same or differing
+ index types (NDX/MDX/TDX), with each file supporting one or more index tags.<br>
+
+ \returns Tag list for the file/table.
+*/
+xbLinkListNode<xbTag *> *xbDbf::GetTagList() const {
+ return llTags.GetHeadNode();
+}
+#endif // XB_INDEX_SUPPORT
+/************************************************************************/
+//! @brief Get the table alias.
+/*!
+ This routine returns the table alias.
+ \returns Table alias
+*/
+const xbString & xbDbf::GetTblAlias() const {
+ return this->sAlias;
+}
+
+/************************************************************************/
+//! @brief Get the pointer to the xbXbase structure,
+/*!
+ \returns Pointer to xbXbase structure.
+*/
+xbXBase * xbDbf::GetXbasePtr() const {
+ return xbase;
+}
+/************************************************************************/
+#ifdef XB_INF_SUPPORT
+//! @brief Load .INF data file,
+/*!
+ Protected method. This routine loads the ndx inf file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LoadInfData(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ // create file name
+ xbString sInfFileName;
+ if(( iRc = GetInfFileName( sInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // if file does not exist, return no error
+ xbFile fMd( xbase );
+ if( !fMd.FileExists( sInfFileName ))
+ return XB_NO_ERROR;
+
+ // open file file in read only mode
+ if(( iRc = fMd.xbFopen( "r", sInfFileName, GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // clear the linked list
+ llInfData.Clear();
+
+ // for each entry in the file, add a linked list item
+ xbString sRec;
+ xbString sLeft3;
+ xbString sFn;
+
+ while( iRc == XB_NO_ERROR ){
+ sRec = "";
+ if(( iRc = fMd.xbFgets( 132, sRec )) == XB_NO_ERROR ){
+ sLeft3 = sRec;
+ sLeft3.Left( 3 );
+ sLeft3.ToUpperCase();
+ if( sLeft3 == "NDX"){
+ sFn.ExtractElement(sRec.Str(), '=', 2 );
+ sFn.ZapChar( 0x0d );
+ sFn.ZapChar( 0x0a );
+ llInfData.InsertAtEnd( sFn );
+ }
+ }
+ }
+ // close the file
+ if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LoadInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INF_SUPPORT
+
+/************************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Lock append bytes.
+/*!
+ This routine locks the append bytes and is used by the AppendRecord function.
+
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockAppend( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulAppendRec;
+
+ try{
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( ulAppendLocked > 0 ) /* already have an append lock */
+ return XB_NO_ERROR;
+
+ ulAppendRec = ulNoOfRecs + 1; /* record number needing to be locked */
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_LOCK, LK4026531839, 1 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ xbInt64 llAppendRecLockByte = (xbInt64) LK4026531838 - ulAppendRec;
+ iRc = xbLock( XB_LOCK, llAppendRecLockByte, 1 );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531839, 1 );
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ ulAppendLocked = ulAppendRec; /* set the append lock switch */
+
+
+ // } else { - other lock-table flavor options go here Clipper, Fox, etc - }
+
+ } else {
+ iErrorStop = 130;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } else if( iLockFunction == XB_UNLOCK ){
+ iErrorStop = 200;
+
+ if( ulAppendLocked == 0 ) /* verify we have an active append lock */
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+ xbInt64 llAppendRecLockByte =(xbInt64) LK4026531838 - ulAppendLocked;
+ iRc = xbLock( XB_UNLOCK, llAppendRecLockByte, 1 );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531839, 1 );
+ iErrorStop = 220;
+ throw iRc;
+ }
+ iRc = xbLock( XB_UNLOCK, LK4026531839, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+
+ ulAppendLocked = 0; /* release the append lock switch */
+
+ // } else { - other unlock-table flavor options go here Clipper, Fox, etc - }
+
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockAppendBytes() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Lock Header
+/*!
+ This routine locks the file header.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockHeader( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( GetHeaderLocked())
+ return XB_NO_ERROR;
+
+ iErrorStop = 110;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ iRc = xbLock( XB_LOCK, LK4026531838, 1 );
+
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ } else {
+ iErrorStop = 130;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ SetHeaderLocked( xbTrue );
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+ if( !GetHeaderLocked())
+ return XB_NO_ERROR;
+
+ iErrorStop = 210;
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_UNLOCK, LK4026531838, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 230;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetHeaderLocked( xbFalse );
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockHeader() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Lock Index files.
+/*!
+ This routine locks all the index files.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockIndices( xbInt16 iLockFunction )
+{
+ // this function doesn't take into account any Lack Flavors other than DBASE,
+ // would need updated to supprot other lock flavors - Clipper, FoxPro etc
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbIxList *ixLI = GetIxList(); // index list item
+
+ while( ixLI ){
+ if( iLockFunction == XB_LOCK ){
+
+ #ifdef XB_NDX_SUPPORT
+ if( *ixLI->sFmt == "NDX" ){
+ if( !ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_LOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 );
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbTrue );
+ }
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ if( *ixLI->sFmt == "MDX" ){
+ if( !ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_LOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 );
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbTrue );
+ }
+ }
+ #endif
+
+ #ifdef XB_TDX_SUPPORT
+ if( *ixLI->sFmt == "TDX" ){
+ if( !ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_LOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 );
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbTrue );
+ }
+ }
+ #endif
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ #ifdef XB_NDX_SUPPORT
+ if( *ixLI->sFmt == "NDX" ){
+ if( ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbFalse );
+ }
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ if( *ixLI->sFmt == "MDX" ){
+ if( ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbFalse );
+ }
+ }
+ #endif
+
+ #ifdef XB_TDX_SUPPORT
+ if( *ixLI->sFmt == "MDX" ){
+ if( ixLI->ix->GetLocked()){
+ if(( iRc = ixLI->ix->xbLock( XB_UNLOCK, LK4026531838, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ixLI->ix->SetLocked( xbFalse );
+ }
+ }
+ #endif
+
+ }
+ ixLI = ixLI->next;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockIndices() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Lock Memo file.
+/*!
+ This routine locks the memo file for updates.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::LockMemo( xbInt16 iLockFunction ){
+ if( MemoFieldsExist())
+ return Memo->LockMemo( iLockFunction );
+ else
+ return XB_NO_ERROR;
+}
+#endif // XB_MEMO_SUPPORT
+
+
+
+/************************************************************************/
+//! @brief Loc Record
+/*!
+ This routine locks a record for update.
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \param ulRecNo Record number to lock
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LockRecord( xbInt16 iLockFunction, xbUInt32 ulRecNo )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( ulRecNo > ulNoOfRecs )
+ return XB_INVALID_RECORD;
+
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+
+ if( lloRecLocks.KeyExists( ulRecNo ))
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+
+ iRc = xbLock( XB_LOCK, LK4026531838 - ulRecNo, 1 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ // other lock-table flavor options go here Clipper, Fox, etc
+
+ } else {
+ iErrorStop = 120;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ // add the record lock info to the linked list chain of record locks
+ iRc = lloRecLocks.InsertKey( ulRecNo );
+ if( iRc != XB_NO_ERROR ){
+ xbLock( XB_UNLOCK, LK4026531838 - ulRecNo, 1 );
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+
+ if( !lloRecLocks.KeyExists( ulRecNo ) )
+ return XB_NO_ERROR;
+
+ if( GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_UNLOCK, LK4026531838 - ulRecNo, 1 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 220;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ // remove the record lock info to the linked list chain of record locks
+ // next line is crashing
+ iRc = lloRecLocks.RemoveKey( ulRecNo );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Lock table.
+/*!
+ This routine locks the table for updates.
+
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::LockTable( xbInt16 iLockFunction )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iLockFunction == XB_LOCK ){
+ iErrorStop = 100;
+ if( GetTableLocked())
+ return XB_NO_ERROR; // table already locked
+
+ iErrorStop = 110;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ iRc = xbLock( XB_LOCK, LK4026531838, 2 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else{
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ // lOffset = LK3026531838;
+ // iLen = LK1000000000;
+ iRc = xbLock( XB_LOCK, LK3026531838, LK1000000000);
+ if( iRc != XB_NO_ERROR ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ xbLock( XB_UNLOCK, LK4026531838, 2 );
+ if( iRc == XB_LOCK_FAILED ){
+ return iRc;
+ } else {
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ // iRc = xbLock( XB_UNLOCK, lOffset, iLen );
+ iRc = xbLock( XB_UNLOCK, LK3026531838, LK1000000000);
+ if( iRc != XB_NO_ERROR ){
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ xbLock( XB_UNLOCK, LK4026531838, 2 );
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ // other lock-table flavor options go here Clipper, Fox, etc
+
+ } else {
+ iErrorStop = 190;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetTableLocked( xbTrue );
+
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ iErrorStop = 200;
+ if( !GetTableLocked())
+ return XB_NO_ERROR; // table already unlocked
+
+ iErrorStop = 210;
+ if( GetLockFlavor() == LK_DBASE ){
+
+ // lOffset = LK4026531838;
+ // iLen = 2;
+ iRc = xbLock( XB_UNLOCK, LK4026531838, 2 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 290;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ SetTableLocked( xbFalse );
+
+ } else {
+ iErrorStop = 300;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ } catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::LockFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+
+}
+#endif // XB_LOCKING_SUPPORT
+
+
+/************************************************************************/
+//! @brief Check for existence of any memo fields.
+/*!
+ \returns xbTrue - Memo fields exist.<br>xbFalse - Memo fields don't exist.
+*/
+xbBool xbDbf::MemoFieldsExist() const {
+
+
+#ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 )
+ return xbTrue;
+#endif
+ return xbFalse;
+}
+
+/************************************************************************/
+//! @brief Open a table/dbf file.
+/*!
+ This routine sets the alias name to the same as the table name.
+
+ \param sTableName Table name to open, Include the .dbf or .DBF extension.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Open( const xbString & sTableName ) {
+ return Open( sTableName, sTableName );
+}
+
+/************************************************************************/
+//! @brief Open a table/dbf file.
+/*!
+ \param sTableName Table name to open, Include the .dbf or .DBF extension.
+ \param sAlias Alias name to assign to this entry.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Open( const xbString & sTableName, const xbString & sAlias ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if(( iRc = Open( sTableName, sAlias, XB_READ_WRITE, XB_MULTI_USER )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // do any .INF data things on the file, like open indices
+ #ifdef XB_INF_SUPPORT
+ if(( iRc = LoadInfData()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ xbUInt32 llNodeCnt = llInfData.GetNodeCnt();
+ if( llNodeCnt > 0 ){
+ xbString s2;
+ xbLinkListNode<xbString> * llN = llInfData.GetHeadNode();
+ for( xbUInt32 i = 0; i < llNodeCnt; i++ ){
+ s2 = llN->GetKey();
+ #ifdef XB_NDX_SUPPORT
+ if(( iRc = OpenIndex( "NDX", s2 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc ;
+ }
+ #endif // XB_NDX_SUPPORT
+ llN = llN->GetNextNode();
+ }
+ }
+ #endif // XB_INF_SUPPORT
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Open() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Open an index.
+/*!
+ Open an index file for the dbf file.
+
+ \param sIxType - "NDX" or "MDX"
+ \param sFileName - File name of index,
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::OpenIndex( const xbString &sIxType, const xbString &sFileName ){
+
+ // this routine is used to open indices and set up linkages
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbIx *pIx = NULL;
+
+ try{
+ xbString sType = sIxType;
+ sType.ToUpperCase();
+
+ if( sType == "" ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+
+ #ifdef XB_NDX_SUPPORT
+ } else if( sType == "NDX" ){
+ pIx = new xbIxNdx( this );
+ if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ } else if( sType == "MDX" ){
+ pIx = new xbIxMdx( this );
+ if(( iRc = pIx->Open( sFileName )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ #endif
+
+ } else {
+ iErrorStop = 130;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = AddIndex( pIx, sIxType )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = UpdateTagList()) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ pCurIx = pIx;
+ sCurIxType = sIxType;
+ vpCurIxTag = pIx->GetTag( 0 );
+ }
+ catch (xbInt16 iRc ){
+ if( pIx ) delete pIx;
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::OpenIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+
+/************************************************************************/
+//! @brief Pack dbf file.
+/*!
+ This routine eliminates all deleted records from the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Pack()
+{
+ xbUInt32 ulDeletedRecCnt;
+ return Pack( ulDeletedRecCnt );
+}
+
+
+/************************************************************************/
+//! @brief Pack dbf file.
+/*!
+ This routine eliminates all deleted records from the file and clears
+ out any unused blocks in the memo file if one exists.
+ \param ulDeletedRecCnt - Output - number of recrods removed from the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::Pack( xbUInt32 &ulDeletedRecCnt )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulLastMovedRec = 0;
+ xbUInt32 ulStartPos = 0;
+ xbUInt32 ulLastPackedRec = 0;
+ xbUInt32 ulMoveRec = 0;
+ xbUInt32 ulRecCnt = 0;
+ ulDeletedRecCnt = 0;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif // XB_LOCKING_SUPPORT
+
+ try{
+ if( !FileIsOpen() ){
+ iErrorStop = 100;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ } else {
+ bLocked = xbTrue;
+ }
+ }
+ #endif
+
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ xbBool bDone = xbFalse;
+ for( xbUInt32 ulI = 1; ulI <= ulRecCnt && !bDone; ulI++ ){
+
+ if(( iRc = GetRecord( ulI )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( RecordDeleted()){
+ ulDeletedRecCnt++;
+ if( ulI > ulLastMovedRec )
+ ulStartPos = ulI;
+ else
+ ulStartPos = ulLastMovedRec;
+
+ iRc = GetNextRecord( 1, ulStartPos );
+
+ if( iRc == XB_NO_ERROR ){
+ ulMoveRec = ulCurRec;
+ }
+ else if( iRc == XB_EOF ){
+ ulMoveRec = 0;
+ bDone = xbTrue;
+ }
+ else{
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if( ulMoveRec > 0 ){
+ if(( iRc = DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if(( iRc = PutRecord( ulMoveRec )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = UndeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ if(( iRc = PutRecord( ulI )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ ulLastPackedRec = ulI;
+ }
+
+ } else {
+ ulLastPackedRec = ulI;
+ }
+ }
+
+ if( ulLastPackedRec < ulRecCnt ){
+ // update header record count
+
+ xbDate d;
+ d.Sysdate();
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulNoOfRecs = ulLastPackedRec;
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ // truncate the file to the new size
+ if(( iRc = xbTruncate( uiHeaderLen + uiRecordLen * ulLastPackedRec )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+
+ if( ulNoOfRecs > 0 ){
+ if(( iRc = GetRecord( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ } else {
+ BlankRecord();
+ ulCurRec = 0;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+ if(( iRc = Memo->PackMemo( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ }
+ #endif // XB_MEMO_SUPPORT
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Pack() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked ){
+ LockTable( XB_UNLOCK );
+ }
+ #endif
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Write the current record to disk.
+/*!
+ This routine is used to write any updates to the current record buffer to disk.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::PutRecord() {
+ return PutRecord(ulCurRec);
+}
+
+/************************************************************************/
+//! @brief Write record to disk.
+/*!
+ This routine is used to write a copy of the current record buffer to disk
+ for a given record number.
+
+ \param ulRecNo Record number to update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::PutRecord(xbUInt32 ulRecNo)
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( ulRecNo < 1 ){
+ iErrorStop = 100;
+ throw XB_INVALID_RECORD;
+ }
+
+ xbUInt32 ulRecCnt;
+ if(( iRc = GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if( ulRecNo > ulRecCnt ){
+ iErrorStop = 120;
+ throw XB_INVALID_RECORD;
+ }
+
+ if( iDbfStatus == XB_CLOSED ){
+ iErrorStop = 130;
+ iRc = XB_NOT_OPEN;
+ throw iRc;
+ }
+ /* lock the database */
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockHeader( XB_LOCK )) != XB_NO_ERROR ){
+ throw iRc;
+ }
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ if(( iRc = ReadHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ // verify valid record number request
+ if( ulRecNo > ulNoOfRecs || ulRecNo == 0L ){
+ iErrorStop = 160;
+ iRc = XB_INVALID_RECORD;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock && !bTableLocked ){
+ if(( iRc = LockRecord( XB_LOCK, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = LockIndices( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ #endif // XB_INDEX_SUPPORT
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ // build keys, check for duplicate keys, add keys
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *ixList = GetIxList();
+
+ while( ixList ){
+ if(( iRc = ixList->ix->CreateKeys( 2 )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ iRc = ixList->ix->CheckForDupKeys();
+ if( iRc != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ throw XB_KEY_NOT_UNIQUE;
+ }
+ ixList = ixList->next;
+ }
+
+ ixList = GetIxList();
+ while( ixList ){
+
+ if(( iRc = ixList->ix->AddKeys( ulCurRec )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+
+ ixList = GetIxList();
+ while( ixList ){
+ if(( iRc = ixList->ix->DeleteKeys()) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ ixList = ixList->next;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ // update latest header date if changed
+ xbDate d;
+ d.Sysdate();
+ if( (cUpdateYY != (char)(d.YearOf() - 1900)) || (cUpdateMM != (char) d.MonthOf()) || (cUpdateDD != (char)d.DayOf( XB_FMT_MONTH))){
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ // rewrite the header record - first 8 bytes
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 70;
+ throw iRc;
+ }
+ }
+
+ // update record
+ iRc = xbFseek( (uiHeaderLen+(( (xbInt64) ulRecNo-1L ) * uiRecordLen )),0 );
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+
+ if( xbFwrite( RecBuf, uiRecordLen, 1 ) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ iRc = XB_WRITE_ERROR;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( MemoFieldsExist() ){
+ if(( iRc = Memo->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+ }
+ #endif
+
+ ulCurRec = ulRecNo;
+ iDbfStatus = XB_OPEN;
+ }
+
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED && iRc != XB_KEY_NOT_UNIQUE ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::PutRecord() Exception Caught. Error Stop = [%d] iRc = [%d] record = [%d]", iErrorStop, iRc, ulRecNo );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ LockHeader( XB_UNLOCK );
+ LockAppend( XB_UNLOCK );
+ LockRecord( XB_UNLOCK, ulRecNo );
+ #ifdef XB_INDEX_SUPPORT
+ LockIndices( XB_UNLOCK );
+ #endif // XB_INDEX_SUPPORT
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Read dbf file header information.
+/*!
+ This method assumes the header has been locked appropriately
+ in a multi user environment
+
+
+ \param iPositionOption 0 - Don't fseek to beginning of file before read.<br>
+ 1 - Start from beginning of file.
+
+ \param iReadOption 0 - Read entire 32 byte header<br>
+ 1 - Read first eight bytes which includes the last update date and number of records.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::ReadHeader( xbInt16 iPositionOption, xbInt16 iReadOption ){
+
+ char buf[32];
+ size_t iReadSize;
+
+ if(iPositionOption)
+ xbRewind();
+ if( iReadOption == 1 )
+ iReadSize = 8;
+ else
+ iReadSize = 32;
+
+ if(xbFread(buf, iReadSize, 1) != XB_NO_ERROR)
+ return XB_READ_ERROR;
+ memcpy(&cVersion, buf, 4);
+ ulNoOfRecs = eGetUInt32(&buf[4]);
+ if( iReadOption == 1 )
+ return XB_NO_ERROR;
+
+ uiHeaderLen = eGetUInt16(&buf[8]);
+ uiRecordLen = eGetUInt16(&buf[10]);
+ cTransactionFlag = buf[14];
+ cEncryptionFlag = buf[15];
+ cIndexFlag = buf[28];
+ cLangDriver = buf[29];
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Return record deletion status.
+/*!
+ This routine returns the record deletion status.
+ \param iOpt 0 = Current record buffer, 1 = Original record buffer
+ \returns xbTrue - Record deleted.<br>xbFalse - Record not deleted.
+*/
+xbInt16 xbDbf::RecordDeleted( xbInt16 iOpt ) const {
+ if( !iOpt && RecBuf && RecBuf[0] == 0x2a )
+ return xbTrue;
+ else if( iOpt && RecBuf2 && RecBuf2[0] == 0x2a )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Remove an index from the internal list of indices for this table
+/*
+ The index list is used during any table update process to update any open
+ index file. Index files can contain one or more tags.
+
+ \param ixIn Pointer to index object for a given index file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::RemoveIndex( xbIx * ixIn ){
+
+ xbIxList *p = ixList;
+ // if index is the first entry in the list
+ if( ixList->ix == ixIn ){
+ ixList = ixList->next;
+ delete p->sFmt;
+ delete p->ix;
+ free( p );
+ return XB_NO_ERROR;
+ }
+
+ // spin down to the correct ix
+ xbIxList *p2 = NULL;
+ while( p && p->ix != ixIn ){
+ p2 = p;
+ p = p->next;
+ }
+ if( p ){
+ p2->next = p->next;
+ delete p->sFmt;
+ delete p->ix;
+ free( p );
+ }
+ return XB_NO_ERROR;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+// @brief Reset number of records.
+/*
+ Protected method. Resets number of records to 0.
+ \returns void
+*/
+void xbDbf::ResetNoOfRecords() {
+ ulNoOfRecs = 0UL;
+}
+
+/************************************************************************/
+#ifdef XB_INF_SUPPORT
+// @brief Update .INF data file.
+/*
+ Protected method.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf::SaveInfData(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbFile fMd( xbase );
+
+ try{
+
+ xbUInt32 llNodeCnt = llInfData.GetNodeCnt();
+
+ xbString sInfFileName;
+ if(( iRc = GetInfFileName( sInfFileName )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // open the file
+ if(( iRc = fMd.xbFopen( "w", sInfFileName, GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ xbString s1;
+ xbString s2;
+ s2.Sprintf( "[dbase]%c%c", 0x0d, 0x0a );
+ if(( iRc = fMd.xbFputs( s2 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ // for each entry in the linked list, write a line
+ xbLinkListNode<xbString> * llN = llInfData.GetHeadNode();
+ for( xbUInt32 i = 0; i < llNodeCnt; i++ ){
+ s2 = llN->GetKey();
+ if( i > 0 )
+ s1.Sprintf( "NDX%d=%s%c%c", i, s2.Str(), 0x0d, 0x0a );
+ else
+ s1.Sprintf( "NDX=%s%c%c", s2.Str(), 0x0d, 0x0a );
+
+ if(( iRc = fMd.xbFputs( s1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ // close the file
+ if(( iRc = fMd.xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ } catch( xbInt16 iRc ){
+ if( fMd.FileIsOpen())
+ fMd.xbFclose();
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::SaveInfData() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INF_SUPPORT
+/************************************************************************/
+//! @brief Set auto commit.
+/*!
+ This routine sets the auto commit setting for this table.
+ \returns XB_NO_ERROR;
+*/
+xbInt16 xbDbf::SetAutoCommit( xbBool iAutoCommit ) {
+ this->iAutoCommit = iAutoCommit;
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+//! @brief Set auto lock.
+/*!
+ This routine sets the auto lock setting for this table.
+ There is an overall system level auto lock default setting and each table
+ can have it's own autolock setting. This method controls the table level
+ auto lock setting.
+
+ \param iAutoLock 1 - Use auto lock for this table.<br>
+ 0 - Don't use auto lock for this table.<br>
+ -1 - (minus one) Use system default.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+#ifdef XB_LOCKING_SUPPORT
+void xbDbf::SetAutoLock( xbInt16 iAutoLock ){
+ if( iAutoLock == -1 )
+ this->iAutoLock = xbase->GetDefaultAutoLock();
+ else
+ this->iAutoLock = iAutoLock;
+}
+#endif
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Set the current tag for the dbf file.
+/*!
+ \param sTagName - Tag Name
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::SetCurTag( const xbString &sTagName ){
+
+ if( sTagName == "" ){
+ SetCurTag( "", 0, 0 );
+ return XB_NO_ERROR;
+
+ } else {
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+ while( llN ){
+ pTag = llN->GetKey();
+ if( pTag->GetTagName() == sTagName ){
+ SetCurTag( pTag->GetType(), pTag->GetIx(), pTag->GetVpTag());
+ return XB_NO_ERROR;
+ }
+ llN = llN->GetNextNode();
+ }
+ }
+
+ return XB_INVALID_TAG;
+}
+
+/************************************************************************/
+//! @brief Set the current tag for the dbf file.
+/*!
+
+ \param sIxType - One of "NDX", MDX or TDX",
+ \param pIx - Pointer to index object.
+ \param vpTag - Pointer to tag object.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+void xbDbf::SetCurTag( const xbString &sIxType, xbIx *pIx, void *vpTag ){
+ pCurIx = pIx;
+ vpCurIxTag = vpTag;
+ sCurIxType.Set( sIxType );
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+//! @brief Set the header locked status.
+/*!
+ \param bHeaderLocked xbTrue - Locked<br>xbFalse - Not locked.
+ \returns void
+*/
+#ifdef XB_LOCKING_SUPPORT
+void xbDbf::SetHeaderLocked( xbBool bHeaderLocked ){
+ this->bHeaderLocked = bHeaderLocked;
+}
+#endif
+
+/************************************************************************/
+//! @brief Set lock flavor.
+/*!
+ This routine is for future expansion.
+ \param iLockFlavor 1 - Use Dbase (tm) style locking.
+ \returns void
+*/
+#ifdef XB_LOCKING_SUPPORT
+void xbDbf::SetLockFlavor( xbInt16 iLockFlavor ){
+ this->iLockFlavor = iLockFlavor;
+}
+#endif
+
+/************************************************************************/
+//! @brief Set table locked status.
+/*!
+ \param bTableLocked - xbTrue Table locked.<br>xbFalse Table unlocked.
+ \returns void
+*/
+#ifdef XB_LOCKING_SUPPORT
+void xbDbf::SetTableLocked( xbBool bTableLocked ){
+ this->bTableLocked = bTableLocked;
+}
+#endif
+/************************************************************************/
+//! @brief Undelete all records.
+/*!
+ This routine will remove the deletion flag on any deleted records in the table.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UndeleteAllRecords(){
+ return DeleteAll( 1 );
+}
+
+/************************************************************************/
+//! @brief Undelete one record.
+/*!
+ This routine will undelete the current record, if it is deleted.
+ \returns XB_NO_ERROR<br>XB_INVALID_RECORD
+*/
+xbInt16 xbDbf::UndeleteRecord()
+{
+ if( RecBuf && ulCurRec > 0 ){
+ if( RecBuf[0] != 0x20 ){
+ if( iDbfStatus != XB_UPDATED ){
+ iDbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, uiRecordLen ); // save off original before making updates
+ }
+ RecBuf[0] = 0x20;
+ }
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_INVALID_RECORD;
+}
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Update memo field
+/*!
+ This routine updates a memo field.
+ \param iFieldNo - Memo field number to update.
+ \param sMemoData - Memo data for update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateMemoField( xbInt16 iFieldNo, const xbString &sMemoData ){
+ return Memo->UpdateMemoField( iFieldNo, sMemoData );
+}
+/************************************************************************/
+//! @brief Update memo field
+/*!
+ This routine updates a memo field.
+ \param sFieldName - Memo field name to update.
+ \param sMemoData - Memo data for update.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateMemoField( const xbString & sFieldName, const xbString & sMemoData ){
+ return Memo->UpdateMemoField( GetFieldNo( sFieldName ), sMemoData );
+}
+#endif
+
+
+/************************************************************************/
+#ifdef XB_INDEX_SUPPORT
+//! @brief Update SchemaIxFlag
+/*!
+ This routine can be called from the DeleteTag routine if a tag has been deleted and the flag needs reset
+ \param iFldNo - Which field the index flag needs changed on
+ \param cVal - Value to change it to
+*/
+
+void xbDbf::UpdateSchemaIxFlag( xbInt16 iFldNo, unsigned char cVal ){
+ if( cVal != 0x00 || cVal != 0x01 )
+ SchemaPtr[iFldNo].cIxFlag = cVal;
+}
+
+/************************************************************************/
+
+//! @brief Update tag list.
+/*!
+ This routine updates the internal tag list of open index tags.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::UpdateTagList(){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt32 lTagCnt;
+
+ try{
+ ClearTagList();
+
+ // For each active index
+ xbIxList *p = GetIxList();
+ xbIx *ixp;
+ while( p ){
+ ixp = p->ix;
+ // for each tag within the file
+ lTagCnt = ixp->GetTagCount();
+ for( xbInt32 l = 0; l < lTagCnt; l++ ){
+ xbTag *pTag = new xbTag( ixp, ixp->GetTag( l ), *p->sFmt, ixp->GetTagName( ixp->GetTag( l )),
+ ixp->GetKeyExpression( ixp->GetTag( l )), ixp->GetKeyFilter( ixp->GetTag( l )),
+ ixp->GetUnique( ixp->GetTag( l )), ixp->GetSortOrder( ixp->GetTag( l )));
+
+ // append it to the llTags list
+ llTags.InsertAtEnd( pTag );
+ }
+ p = p->next;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::UpdateTagList() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+/************************************************************************/
+// @brief Write Header
+/*
+ Protected method.
+
+ \param iPositionOption 0 - Don't fseek to beginning of file before read.<br>
+ 1 - Go to beginning of file before read.
+ \param iWriteOption 0 - Write entire 32 byte header.<br>
+ 1 - Write first eight bytes needed for table updates - last update date and number of records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::WriteHeader( xbInt16 iPositionOption, xbInt16 iWriteOption )
+{
+ char buf[32];
+ xbInt32 lWriteLen;
+
+ if(iPositionOption)
+ xbRewind();
+
+ memset(buf, 0, 32);
+ if( iWriteOption == 1 )
+ lWriteLen = 8;
+ else{
+ lWriteLen = 32;
+ ePutUInt16( &buf[8], uiHeaderLen );
+ ePutUInt16( &buf[10], uiRecordLen );
+ buf[14] = cTransactionFlag;
+ buf[15] = cEncryptionFlag;
+ buf[28] = cIndexFlag;
+ buf[29] = cLangDriver;
+ }
+ memcpy(&buf[0], &cVersion, 4);
+ ePutUInt32( &buf[4], ulNoOfRecs);
+ if(xbFwrite(buf, (size_t) lWriteLen, 1) != XB_NO_ERROR)
+ return XB_WRITE_ERROR;
+
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! @brief Zap (remove) everything from the file,
+/*!
+ This routine eliminates everything from the dbf file and dbt memo file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf::Zap(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif // XB_LOCKING_SUPPORT
+
+ try{
+ if( iDbfStatus != XB_OPEN ){
+ iErrorStop = 100;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( iAutoLock ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ #endif
+
+ xbDate d;
+ d.Sysdate();
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ ulNoOfRecs = 0;
+
+ // rewrite the header record
+ if(( iRc = WriteHeader( 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ // truncate the file to the new size
+ if(( iRc = xbTruncate( uiHeaderLen )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ BlankRecord();
+ ulCurRec = 0;
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt ){
+ if(( iRc = Memo->Zap()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ #endif
+ #ifdef XB_INDEX_SUPPORT
+ xbLinkListNode<xbTag *> *llN = GetTagList();
+ xbTag *pTag;
+ xbIx *pIx;
+ void *vpTag;
+ while( llN ){
+ pTag = llN->GetKey();
+ pIx = pTag->GetIx();
+ vpTag = pTag->GetVpTag();
+ if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 60;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+
+ #endif // XB_INDEX_SUPPORT
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_LOCK_FAILED ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::Zap() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked ){
+ LockTable( XB_UNLOCK );
+ }
+ #endif // XB_LOCKING_SUPPORT
+ return iRc;
+}
+/************************************************************************/
+#ifdef XB_BLOCKREAD_SUPPORT
+// block read processing is designed to provide a way to rapidly retrieve
+// all the records from a .DBF file in sequential order.
+
+// It is not designed for doing any read/write processing or data retrieval based on a tag sort.
+// It is designed for doing a fast sequentil block read out of a table
+
+
+xbInt16 xbDbf::DisableBlockReadProcessing(){
+
+ if( bBlockReadEnabled ){
+ bBlockReadEnabled = xbFalse;
+ delete pRb;
+ pRb = NULL;
+ }
+ return XB_NO_ERROR;
+}
+
+xbBool xbDbf::GetBlockReadStatus() const {
+ return bBlockReadEnabled;
+}
+
+xbInt16 xbDbf::EnableBlockReadProcessing(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( !bBlockReadEnabled ){
+ if( iDbfStatus == XB_UPDATED ){
+ if( GetAutoCommit() == 1 ){
+ if(( iRc = Commit()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = Abort()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ }
+ pRb = new xbBlockRead( this );
+ if( !pRb ){
+ iErrorStop = 120;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if(( iRc = pRb->Init()) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ bBlockReadEnabled = xbTrue;
+ }
+
+ } catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf::EnableBlockReadProcessing() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+#endif // XB_BLOCKREAD_SUPPORT
+/************************************************************************/
+} /* namespace */ \ No newline at end of file
diff --git a/src/core/xbdbf3.cpp b/src/core/xbdbf3.cpp
new file mode 100755
index 0000000..926cc0d
--- /dev/null
+++ b/src/core/xbdbf3.cpp
@@ -0,0 +1,768 @@
+/* xbdbf3.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+
+#ifdef XB_DBF3_SUPPORT
+
+namespace xb{
+
+
+/************************************************************************/
+//! @brief Constructor.
+
+xbDbf3::xbDbf3(xbXBase * x) : xbDbf( x ) {
+ #ifdef XB_MEMO_SUPPORT
+ ulCreateMemoBlockSize = 512;
+ #endif
+ iFileVersion = 3;
+};
+
+/************************************************************************/
+//! @brief Destructor.
+
+xbDbf3::~xbDbf3() {};
+
+/************************************************************************/
+//! @brief Create Version 3 table.
+/*!
+ This routine creates a Dbase III Plus (tm) DBF file.
+
+ \param sTableName DBF table name.
+ \param sAlias Table alias
+ \param pSchema Pointer to schema structure with field definitions.
+ \param iOverlay xbTrue - Overlay.<br> xbFalse - Don't overlay.
+ \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbDbf3::CreateTable( const xbString & sTableName, const xbString & sAlias, xbSchema *pSchema, xbInt16 iOverlay, xbInt16 iShareMode ){
+
+ xbInt16 i, k, k2;
+ xbInt16 rc = 0;
+ xbInt16 iErrorStop = 0;
+ iDbfStatus = XB_CLOSED;
+
+ xbString sNfn;
+ try{
+ sNfn = sTableName;
+ xbase->GetLogStatus();
+
+ rc = NameSuffixMissing( sNfn, 1 );
+ if( rc > 0 )
+ sNfn += ".DBF";
+
+ SetFileName( sNfn );
+ this->sAlias = sAlias;
+
+ /* check if the file already exists */
+ if( FileExists( 0 )){
+ if( !iOverlay ){
+ iErrorStop = 100;
+ rc = XB_FILE_EXISTS;
+ throw rc;
+ }
+
+ // remove other files if they exist
+ xbString sMname = sNfn;
+ xbUInt32 iMnameLen = sMname.Len();
+ sMname.PutAt( iMnameLen-2, 'I' );
+ sMname.PutAt( iMnameLen-1, 'N' );
+ sMname.PutAt( iMnameLen, 'F' );
+ xbFile fTemp( xbase );
+ fTemp.SetFileName( sMname );
+ if( fTemp.FileExists() )
+ fTemp.xbRemove();
+
+ sMname.PutAt( iMnameLen-2, 'D' );
+ sMname.PutAt( iMnameLen-1, 'B' );
+ sMname.PutAt( iMnameLen, 'T' );
+ fTemp.SetFileName( sMname );
+ if( fTemp.FileExists() )
+ fTemp.xbRemove();
+
+ sMname.PutAt( iMnameLen-2, 'M' );
+ sMname.PutAt( iMnameLen-1, 'D' );
+ sMname.PutAt( iMnameLen, 'X' );
+ fTemp.SetFileName( sMname );
+ if( fTemp.FileExists() )
+ fTemp.xbRemove();
+ }
+
+ /* check if we already have a file with this alias */
+ if(( rc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+
+ rc = ValidateSchema( pSchema );
+ if( rc < 0 ){
+ iErrorStop = 120;
+ throw rc;
+ } else
+ iNoOfFields = rc;
+
+ #ifdef XB_MEMO_SUPPORT
+ // if we have memo fields
+ iMemoFieldCnt = 0;
+ i = 0;
+ while( pSchema[i].cType != 0){
+ if( pSchema[i].cType == 'M' )
+ iMemoFieldCnt++; /* number of memo fields in the incoming definition */
+ i++;
+ }
+
+ if( iMemoFieldCnt > 0 ){
+ xbString sMfn = sNfn; /* memo file name, same as filename except ends with a "t", not an "f" */
+ xbUInt32 iMfnLen = sMfn.Len();
+ sMfn.PutAt( iMfnLen, 'T' );
+
+ // dont overlay the memo file if it exists, and Overlay switch is off
+ xbFile fTemp( xbase );
+ fTemp.SetFileName( sMfn );
+ if( fTemp.FileExists() && !iOverlay ){
+ iErrorStop = 130;
+ rc = XB_FILE_EXISTS;
+ throw rc;
+ }
+
+ Memo = new xbMemoDbt3( this, fTemp.GetFqFileName());
+
+ if(( rc = Memo->CreateMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw rc;
+ }
+ }
+ #endif
+
+ /* this is the dBase III version of the class */
+ cVersion = 0x03; // 0x03 for Dbase level 5
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+// cVersion = cVersion |= 0x80; // Version III memo, compiler complaints
+ cVersion |= 0x80; // Version III memo
+ }
+ #endif
+
+ if(( rc = xbFopen( "w+b", iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw rc;
+ }
+ uiRecordLen++; /* add one byte for 0x0D */
+
+ if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 160;
+ throw rc;
+ }
+
+ if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 170;
+ rc = XB_NO_MEMORY;
+ throw rc;
+ }
+
+ /* BlankRecord(); */
+ memset( RecBuf, 0x20, uiRecordLen );
+ memset( RecBuf2, 0x20, uiRecordLen );
+ ulCurRec = 0L;
+ uiHeaderLen = 33 + iNoOfFields * 32;
+ xbDate d;
+ d.Sysdate();
+ cUpdateYY = (char) (d.YearOf() - 1900);
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+
+ /* write the header prolog */
+ if(( rc = WriteHeader( 0, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+ if((SchemaPtr = (xbSchemaRec *) malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){
+ iErrorStop = 190;
+ rc = XB_NO_MEMORY;
+ throw rc;
+ }
+
+ /* write the field information into the header */
+ for( i = 0, k = 1; i < iNoOfFields; i++ ){
+
+ memset( SchemaPtr[i].cFieldName, 0x00, 11 );
+ for( int x = 0; x < 10 && pSchema[i].cFieldName[x]; x++ )
+ SchemaPtr[i].cFieldName[x] = pSchema[i].cFieldName[x];
+
+ SchemaPtr[i].cType = pSchema[i].cType;
+ SchemaPtr[i].cFieldLen = (unsigned char) pSchema[i].iFieldLen;
+ SchemaPtr[i].cNoOfDecs = (unsigned char) pSchema[i].iNoOfDecs;
+
+ if( SchemaPtr[i].cNoOfDecs > SchemaPtr[i].cFieldLen ){
+ iErrorStop = 200;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ k2 = k;
+ k += SchemaPtr[i].cFieldLen;
+
+ if(( xbFwrite( &SchemaPtr[i].cFieldName, 1, 11 )) != XB_NO_ERROR ) {
+ iErrorStop = 210;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ if(( xbFwrite( &SchemaPtr[i].cType, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 220;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ for( int j = 0; j < 4; j++ )
+ xbFputc( 0x00 );
+
+ if(( xbFwrite( &SchemaPtr[i].cFieldLen, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 230;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ if(( xbFwrite( &SchemaPtr[i].cNoOfDecs, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 240;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ /* 14 bytes reserved */
+ for( int j = 0; j < 14; j++ )
+ xbFputc( 0x00 );
+
+ SchemaPtr[i].pAddress = RecBuf + k2;
+ SchemaPtr[i].pAddress2 = RecBuf2 + k2;
+ }
+
+ /* write the header terminator */
+ if(( xbFputc( XB_CHARHDR )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+ }
+ catch( xbInt16 rc )
+ {
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf3::CreateTable() Exception Caught Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+
+ sMsg.Sprintf( "Table Name = [%s]", GetFqFileName().Str());
+ xbase->WriteLogMessage( sMsg );
+ sMsg.Sprintf( "Alias Name = [%s]", sAlias.Str());
+ xbase->WriteLogMessage( sMsg );
+
+ xbFclose();
+ if( RecBuf ){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if( RecBuf2 ){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+ if( SchemaPtr ){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+
+ InitVars();
+ if( rc != XB_FILE_EXISTS )
+ xbase->RemoveTblFromTblList( sAlias );
+ }
+
+ if( rc == XB_NO_ERROR )
+ iDbfStatus = XB_OPEN;
+
+ return rc;
+}
+
+/************************************************************************/
+//! @brief Get version.
+/*!
+ The version info can be retrieved to determine
+ which class is being used for a given dbf instance.
+ \returns 3
+*/
+
+xbInt16 xbDbf3::GetVersion() const {
+ return 3;
+}
+
+
+
+/************************************************************************/
+//! @brief Open dbf file/table.
+/*!
+ \param sTableName DBF table name.
+ \param sAlias Table alias
+ \param iOpenMode XB_READ<br>XB_READ_WRITE
+ \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf3::Open( const xbString & sTableName, const xbString & sAlias,
+ xbInt16 iOpenMode, xbInt16 iShareMode ){
+ xbInt16 i, j, iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char buf[33];
+ char *p;
+
+ #ifdef XB_MEMO_SUPPORT
+ iMemoFieldCnt = 0;
+ #endif
+
+ try{
+
+ /* verify the file is not already open */
+ if( iDbfStatus != XB_CLOSED ){
+ iErrorStop = 100;
+ iRc = XB_ALREADY_OPEN;
+ throw iRc;
+ }
+ /* copy the file name to the class variable */
+ SetFileName( sTableName );
+ this->sAlias = sAlias;
+
+ if( !FileExists()){
+ iErrorStop = 110;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+
+ if(( iRc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ /* open the file */
+ if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ /* copy the header into memory */
+ if(( iRc = ReadHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ /* check the version */
+ if(( cVersion & 0x07 ) != 3 ){
+ // if( xFileVersion != 3 && xFileVersion != 4 ){
+ iErrorStop = 150;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ }
+ iFileVersion = 3;
+
+ /* calculate the number of fields */
+ if( cVersion == (char)0x30 ) {
+ iNoOfFields = ( uiHeaderLen - 296 ) / 32 ;
+ } else {
+ iNoOfFields = ( uiHeaderLen - 33 ) / 32;
+ }
+
+ if(( RecBuf = (char *) malloc( (size_t) uiRecordLen )) == NULL ){
+ iErrorStop = 160;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ) {
+ iErrorStop = 170;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if((SchemaPtr=(xbSchemaRec *)malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){
+ iErrorStop = 180;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ memset( SchemaPtr, 0x00, ( (size_t) iNoOfFields * sizeof(xbSchemaRec) ));
+ /* copy field info into memory */
+ for( i = 0, j = 1; i < iNoOfFields; i++ ){
+ xbFseek( ((xbInt64)i*32+32), SEEK_SET );
+ xbFread( &buf, 1, 32 );
+ p = buf;
+ for( int x = 0; x < 10 && buf[x]; x++ ){
+ SchemaPtr[i].cFieldName[x] = buf[x];
+ }
+ p = buf + 11;
+ SchemaPtr[i].cType = *p++;
+ SchemaPtr[i].pAddress = RecBuf + j;
+ SchemaPtr[i].pAddress2 = RecBuf2 + j;
+ SchemaPtr[i].cFieldLen = (unsigned char) *( p + 4 );
+ SchemaPtr[i].cNoOfDecs = (unsigned char) *( p + 5 );
+ j += SchemaPtr[i].cFieldLen;
+ #ifdef XB_MEMO_SUPPORT
+ if( (SchemaPtr[i].cType == 'M' || SchemaPtr[i].cType == 'B' || SchemaPtr[i].cType == 'O' ))
+ iMemoFieldCnt++;
+ #endif
+ }
+ ulCurRec = 0L;
+ iDbfStatus = XB_OPEN;
+ if(( iRc = BlankRecord()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){ /* does this table have memo fields ? */
+
+ // build the file name
+ xbString sMfn = GetFqFileName(); /* memo file name, same as filename except ends with a "t", not an "f" */
+ xbUInt32 ulMfnLen = sMfn.Len();
+ if( sMfn[ulMfnLen] == 'F' )
+ sMfn.PutAt( ulMfnLen, 'T' );
+ else
+ sMfn.PutAt( ulMfnLen, 't' );
+ xbFile fTemp( xbase );
+ fTemp.SetFileName( sMfn );;
+
+ Memo = new xbMemoDbt3( this, fTemp.GetFqFileName());
+
+ if(( iRc = Memo->OpenMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ }
+ catch ( xbInt16 iRc )
+ {
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf3::Open() Exception Caught Error Stop = [%d] iRc = [%d] ShareMode = [%d] OpenMode = [%d]", iErrorStop, iRc, iShareMode, iOpenMode );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ xbFclose();
+ if( RecBuf ){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if( RecBuf2 ){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+ if( SchemaPtr ){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+ InitVars();
+
+ #ifdef XB_MEMO_SUPPORT
+ if( Memo ){
+ Memo->CloseMemoFile();
+ delete Memo;
+ Memo = NULL;
+ }
+ #endif // XB_MEMO_SUPPORT
+ }
+
+ if( iRc == XB_NO_ERROR )
+ iDbfStatus = XB_OPEN;
+
+ return iRc;
+}
+/************************************************************************/
+//! @brief Rename table.
+/*!
+ This routine renames a give table, associated memo and inf files
+ \param sNewName - New file name.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf3::Rename( const xbString sNewName ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbString sNewDbf;
+
+ xbBool bDbfRenamed = xbFalse;
+
+ #ifdef XB_INF_SUPPORT
+ xbString sNewInf;
+ xbString sThisInf;
+ xbBool bInfRenamed = xbFalse;
+ #endif
+
+ #ifdef XB_MEMO_SUPPORT
+ xbString sNewDbt;
+ xbString sThisDbt;
+ xbBool bDbtRenamed = xbFalse;
+ #endif // XB_MEMO_SUPPORT
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif
+
+ try{
+
+ xbString sDir;
+ xbString sFile;
+ xbString sExt;
+
+ xbString sNewNameWoExt;
+ sNewNameWoExt.Set( sNewName );
+ if( sNewName.Pos( ".DBF" ) > 0 )
+ sNewNameWoExt.Left( sNewName.Len() - 4 );
+
+ GetFileDirPart ( sDir );
+ GetFileNamePart( sFile );
+ GetFileExtPart ( sExt );
+
+ if( FileExists( sNewDbf )) return XB_FILE_EXISTS;
+ sNewDbf.Sprintf( "%s%s.DBF", sDir.Str(), sNewNameWoExt.Str());
+
+ #ifdef XB_MEMO_SUPPORT
+ sNewDbt.Sprintf( "%s%s.DBT", sDir.Str(), sNewNameWoExt.Str());
+ if( FileExists( sNewDbt )) return XB_FILE_EXISTS;
+ sThisDbt.Sprintf( "%s%s.DBT", sDir.Str(), sFile.Str());
+ #endif
+
+ #ifdef XB_INF_SUPPORT
+ sNewInf.Sprintf( "%s%s.INF", sDir.Str(), sNewNameWoExt.Str());
+ if( FileExists( sNewInf )) return XB_FILE_EXISTS;
+ sThisInf.Sprintf( "%s%s.INF", sDir.Str(), sFile.Str());
+ #endif // XB_INF_SUPPORT
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( GetAutoLock() && GetTableLocked() ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ bLocked = xbTrue;
+ }
+ #endif
+
+ xbInt16 iOpenMode = GetOpenMode();
+ xbInt16 iShareMode = GetShareMode();
+ xbBool bWasOpen = xbFalse;
+ if( FileIsOpen() ){
+ bWasOpen = xbTrue;
+ if(( iRc = xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ if(( iRc = xbRename( GetFqFileName().Str(), sNewDbf.Str())) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ } else {
+ bDbfRenamed = xbTrue;
+ }
+ xbString sNameWext;
+ sNameWext.Sprintf( "%s.DBF", sNewNameWoExt.Str());
+ SetFileName( sNameWext );
+
+ if( bWasOpen ){
+ if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( FileExists( sThisDbt )){
+ if( bWasOpen ){
+ if(( iRc = Memo->xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ if(( xbRename( sThisDbt.Str(), sNewDbt.Str())) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ Memo->SetFileName( sNewDbt );
+ if( bWasOpen ){
+ if(( iRc = Memo->xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ }
+ bDbtRenamed = xbTrue;
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ #ifdef XB_INF_SUPPORT
+ if( FileExists( sThisInf )){
+ if(( iRc = xbRename( sThisInf.Str(), sNewInf.Str())) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ } else {
+ bInfRenamed = xbTrue;
+ }
+ }
+ #endif // XB_INF_SUPPORT
+
+ // rname the table in the table list
+ xbTblList *tle = xbase->GetTblListEntry( this );
+ if( tle ){
+ // std::cout << "setting [" << GetFqFileName().Str() << "][" << sNewNameWoExt.Str() << "]\n";
+ tle->psFqTblName->Set( GetFqFileName().Str());
+ tle->psTblAlias->Set( sNewNameWoExt.Str());
+ }
+ }
+ catch ( xbInt16 iRc )
+ {
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf3::Rename() Exception Caught Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+
+ // attempt to reverse things out if unsuccessful
+ if( bDbfRenamed ){
+ #ifdef XB_MEMO_SUPPORT
+ if( bDbtRenamed ){
+ xbRename( sNewDbt.Str(), sThisDbt.Str());
+ }
+ #endif // XB_MEMO_SUPPORT
+ #ifdef XB_INF_SUPPORT
+ if( bInfRenamed ){
+ xbRename( sNewInf.Str(), sNewInf.Str());
+ }
+ #endif // XB_INF_SUPPORT
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked ){
+ LockTable( XB_UNLOCK );
+ }
+ #endif
+
+ }
+ return iRc;
+}
+
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Create memo block size.
+/*!
+ This routine sets the memo file block size. This value is used when
+ the memo file is created so you if you want to change it, this must be
+ called before creating the table.
+
+ DBF III Plus uses a block size of 512.
+
+ \param ulBlockSize - Block size, must be evenly divisible by 512.
+ \returns XB_INVALID_BLOCK_SIZE<br>XB_NO_ERROR
+*/
+
+xbInt16 xbDbf3::SetCreateMemoBlockSize( xbUInt32 ulBlockSize ){
+
+ if( ulBlockSize != 512 )
+ return XB_INVALID_BLOCK_SIZE;
+ else
+ ulCreateMemoBlockSize = 512;
+
+ return XB_NO_ERROR;
+}
+#endif
+
+/************************************************************************/
+//! @brief Set version.
+/*!
+ Sets the version to 3. The version info can be retrieved to determine
+ which class is being used for a given dbf instance.
+ \returns 3
+*/
+
+xbInt16 xbDbf3::SetVersion() {
+ iFileVersion = 3;
+ return iFileVersion;
+}
+
+
+/************************************************************************/
+//! @brief Validate schema
+/*!
+ This routine verifies the field types are valid for Dbase III Plus (tm).
+
+ \param s Pointer to schema structure with field definitions.
+
+ \returns Number of fields or XB_INVALID_FIELD_TYPE.
+*/
+
+xbInt16 xbDbf3::ValidateSchema( xbSchema * s ){
+
+// This routine validates an input schema
+// Negative return value is an error
+// Positive return value is the number of fields
+// On success, the class variable uiRecordLen will be updated with the record length of the combined total of the fields
+
+ xbInt16 iFieldCnt = 0;
+ uiRecordLen = 0;
+
+ /* count the number of fields and check paramaters */
+ xbInt16 i = 0;
+ while( s[i].cType != 0 ){
+ iFieldCnt++;
+ // Version 3 field types
+ if( s[i].cType != 'C' &&
+ s[i].cType != 'N' &&
+ s[i].cType != 'D' &&
+ #ifdef XB_MEMO_SUPPORT
+ s[i].cType != 'M' &&
+ #endif // XB_MEMO_SUPPORT
+ s[i].cType != 'L' ){
+ return XB_INVALID_FIELD_TYPE;
+ }
+
+ if(s[i].cType == 'D'){
+ s[i].iFieldLen = 8;
+ s[i].iNoOfDecs = 0;
+ }
+
+ else if(s[i].cType == 'C')
+ s[i].iNoOfDecs = 0;
+
+ // check for numeric fields which are too long
+ else if( s[i].cType == 'N' && s[i].iFieldLen > 19 ){
+ return XB_INVALID_FIELD_LEN;
+ }
+ // field len must be >= no of decimals
+ else if( s[i].cType == 'N' && s[i].iFieldLen < s[i].iNoOfDecs ){
+ return XB_INVALID_FIELD_LEN;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ else if(s[i].cType == 'M'){
+ s[i].iFieldLen = 10;
+ s[i].iNoOfDecs = 0;
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ uiRecordLen += s[i].iFieldLen;
+ i++;
+ }
+ return iFieldCnt;
+}
+
+} /* namespace */
+#endif /* XB_DBF3_SUPPORT */ \ No newline at end of file
diff --git a/src/core/xbdbf4.cpp b/src/core/xbdbf4.cpp
new file mode 100755
index 0000000..a1f770e
--- /dev/null
+++ b/src/core/xbdbf4.cpp
@@ -0,0 +1,885 @@
+/* xbdbf4.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+
+#ifdef XB_DBF4_SUPPORT
+
+namespace xb{
+
+
+/************************************************************************/
+//! @brief Constructor.
+xbDbf4::xbDbf4(xbXBase * x) : xbDbf( x ) {
+
+ iFileVersion = 4;
+ #ifdef XB_MEMO_SUPPORT
+ ulCreateMemoBlockSize = 1024;
+ #endif
+};
+
+/************************************************************************/
+//! @brief Destructor.
+xbDbf4::~xbDbf4() {};
+
+/************************************************************************/
+//! @brief Create Version 4 table.
+/*!
+ This routine creates a Dbase IV (tm) DBF file.
+
+ \param sTableName DBF table name.
+ \param sAlias Table alias
+ \param pSchema Pointer to schema structure with field definitions.
+ \param iOverlay xbTrue - Overlay.<br> xbFalse - Don't overlay.
+ \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf4::CreateTable( const xbString &sTableName, const xbString &sAlias, xbSchema * pSchema, xbInt16 iOverlay, xbInt16 iShareMode ){
+
+ xbInt16 i, k, k2;
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ iDbfStatus = XB_CLOSED;
+
+ xbString sNfn;
+
+ try{
+ sNfn = sTableName;
+ xbase->GetLogStatus();
+
+ rc = NameSuffixMissing( sNfn, 1 );
+ if( rc > 0 )
+ sNfn += ".DBF";
+
+ SetFileName( sNfn );
+ this->sAlias = sAlias;
+
+ /* check if the file already exists */
+ if( FileExists( 0 )){
+ if( !iOverlay ){
+ iErrorStop = 100;
+ rc = XB_FILE_EXISTS;
+ throw rc;
+ }
+
+ // remove other files if they exist
+ xbString sMname = sNfn;
+ xbUInt32 iMnameLen = sMname.Len();
+ sMname.PutAt( iMnameLen-2, 'I' );
+ sMname.PutAt( iMnameLen-1, 'N' );
+ sMname.PutAt( iMnameLen, 'F' );
+ xbFile fTemp( xbase );
+ fTemp.SetFileName( sMname );
+ if( fTemp.FileExists() )
+ fTemp.xbRemove();
+
+ sMname.PutAt( iMnameLen-2, 'D' );
+ sMname.PutAt( iMnameLen-1, 'B' );
+ sMname.PutAt( iMnameLen, 'T' );
+ fTemp.SetFileName( sMname );
+ if( fTemp.FileExists() )
+ fTemp.xbRemove();
+
+ sMname.PutAt( iMnameLen-2, 'M' );
+ sMname.PutAt( iMnameLen-1, 'D' );
+ sMname.PutAt( iMnameLen, 'X' );
+ fTemp.SetFileName( sMname );
+ if( fTemp.FileExists() )
+ fTemp.xbRemove();
+ }
+
+ /* check if we already have a file with this alias */
+ if(( rc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+
+ rc = ValidateSchema( pSchema );
+ if( rc < 0 ){
+ iErrorStop = 120;
+ throw rc;
+ } else
+ iNoOfFields = rc;
+
+ #ifdef XB_MEMO_SUPPORT
+ // if we have memo fields
+ iMemoFieldCnt = 0;
+ i = 0;
+ while( pSchema[i].cType != 0){
+ if( pSchema[i].cType == 'M' )
+ iMemoFieldCnt++; /* number of memo fields in the incoming definition */
+ i++;
+ }
+
+ if( iMemoFieldCnt > 0 ){
+ xbString sMfn = sNfn; /* memo file name, same as filename except ends with a "t", not an "f" */
+ xbUInt32 ulMfnLen = sMfn.Len();
+ sMfn.PutAt( ulMfnLen, 'T' );
+
+ // dont overlay the memo file if it exists, and Overlay switch is off
+ xbFile fTemp( xbase );
+ fTemp.SetFileName( sMfn );
+ if( fTemp.FileExists() && !iOverlay ){
+ iErrorStop = 130;
+ rc = XB_FILE_EXISTS;
+ throw rc;
+ }
+
+ Memo = new xbMemoDbt4( this, fTemp.GetFqFileName());
+
+ if(( rc = Memo->CreateMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw rc;
+ }
+ }
+ #endif
+
+ /* this is the dBase IV version of the class */
+ cVersion = 0x03; // 0x03 for Dbase level 5
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){
+// cVersion = cVersion |= 0x88; // version IV memos, compiler complains about this
+ cVersion |= 0x88; // version IV memos
+ }
+ #endif
+
+ if(( rc = xbFopen( "w+b", iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw rc;
+ }
+ uiRecordLen++; /* add one byte for 0x0D */
+
+ if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 160;
+ throw rc;
+ }
+
+ if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 170;
+ rc = XB_NO_MEMORY;
+ throw rc;
+ }
+
+ /* BlankRecord(); */
+ memset( RecBuf, 0x20, uiRecordLen );
+ memset( RecBuf2, 0x20, uiRecordLen );
+ ulCurRec = 0L;
+ uiHeaderLen = 33 + iNoOfFields * 32;
+ xbDate d;
+ d.Sysdate();
+ cUpdateYY = (char) (d.YearOf() - 1900);
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+ cIndexFlag = 0;
+ // Default language driver to 0x1b
+ cLangDriver = 0x1b;
+
+ /* write the header prolog */
+ if(( rc = WriteHeader( 0, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+ if((SchemaPtr=(xbSchemaRec *)malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){
+ iErrorStop = 190;
+ rc = XB_NO_MEMORY;
+ throw rc;
+ }
+
+ /* write the field information into the header */
+ for( i = 0, k = 1; i < iNoOfFields; i++ ){
+
+ memset( SchemaPtr[i].cFieldName, 0x00, 11 );
+ for( int x = 0; x < 10 && pSchema[i].cFieldName[x]; x++ )
+ SchemaPtr[i].cFieldName[x] = pSchema[i].cFieldName[x];
+
+ SchemaPtr[i].cType = pSchema[i].cType;
+ SchemaPtr[i].cFieldLen = (unsigned char) pSchema[i].iFieldLen;
+ SchemaPtr[i].cNoOfDecs = (unsigned char) pSchema[i].iNoOfDecs;
+
+ if( SchemaPtr[i].cNoOfDecs > SchemaPtr[i].cFieldLen ){
+ iErrorStop = 110;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ k2 = k;
+ k += SchemaPtr[i].cFieldLen;
+
+ if(( xbFwrite( &SchemaPtr[i].cFieldName, 1, 11 )) != XB_NO_ERROR ) {
+ iErrorStop = 200;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ if(( xbFwrite( &SchemaPtr[i].cType, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 210;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ for( int j = 0; j < 4; j++ )
+ xbFputc( 0x00 );
+
+ if(( xbFwrite( &SchemaPtr[i].cFieldLen, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 220;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ if(( xbFwrite( &SchemaPtr[i].cNoOfDecs, 1, 1 )) != XB_NO_ERROR ) {
+ iErrorStop = 230;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+
+ /* 14 bytes reserved */
+ for( int j = 0; j < 14; j++ )
+ xbFputc( 0x00 );
+
+ SchemaPtr[i].pAddress = RecBuf + k2;
+ SchemaPtr[i].pAddress2 = RecBuf2 + k2;
+ }
+
+ /* write the header terminator */
+ if(( xbFputc( XB_CHARHDR )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ rc = XB_WRITE_ERROR;
+ throw rc;
+ }
+ }
+ catch( xbInt16 rc )
+ {
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf4::CreateTable() Exception Caught Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+
+ sMsg.Sprintf( "Table Name = [%s]", GetFqFileName().Str());
+ xbase->WriteLogMessage( sMsg );
+ sMsg.Sprintf( "Alias Name = [%s]", sAlias.Str());
+ xbase->WriteLogMessage( sMsg );
+
+ xbFclose();
+ if( RecBuf ){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if( RecBuf2 ){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+ if( SchemaPtr ){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+
+ InitVars();
+ if( rc != XB_FILE_EXISTS )
+ xbase->RemoveTblFromTblList( sAlias );
+ }
+
+ if( rc == XB_NO_ERROR )
+ iDbfStatus = XB_OPEN;
+
+ return rc;
+}
+
+/************************************************************************/
+//! @brief Get version.
+/*!
+ The version info can be retrieved to determine
+ which class is being used for a given dbf instance.
+ \returns 4
+*/
+
+xbInt16 xbDbf4::GetVersion() const {
+ return 4;
+}
+
+/************************************************************************/
+//! @brief Open dbf file/table.
+/*!
+ \param sTableName DBF table name.
+ \param sAlias Table alias
+ \param iOpenMode XB_READ<br>XB_READ_WRITE
+ \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbDbf4::Open( const xbString & sTableName, const xbString & sAlias,
+ xbInt16 iOpenMode, xbInt16 iShareMode ){
+ xbInt16 i, j, iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char buf[33];
+ char *p;
+
+ #ifdef XB_MEMO_SUPPORT
+ iMemoFieldCnt = 0;
+ #endif
+
+ try{
+ /* verify the file is not already open */
+ if( iDbfStatus != XB_CLOSED ){
+ iErrorStop = 100;
+ iRc = XB_ALREADY_OPEN;
+ throw iRc;
+ }
+ /* copy the file name to the class variable */
+ SetFileName( sTableName );
+ this->sAlias = sAlias;
+
+ if( !FileExists()){
+ iErrorStop = 110;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+
+ if(( iRc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ /* open the file */
+ if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ /* copy the header into memory */
+ if(( iRc = ReadHeader( 1, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ /* check the version */
+ //if(( xFileVersion = DetermineXbaseTableVersion( cVersion )) != 4 ){
+ if(( cVersion & 0x07 ) != 3 ){
+ iErrorStop = 150;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ }
+ iFileVersion = 4;
+
+ /* calculate the number of fields */
+ if( cVersion == (char)0x30 ) {
+ iNoOfFields = ( uiHeaderLen - 296 ) / 32 ;
+ } else {
+ iNoOfFields = ( uiHeaderLen - 33 ) / 32;
+ }
+
+ if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){
+ iErrorStop = 160;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ) {
+ iErrorStop = 170;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if((SchemaPtr=(xbSchemaRec *)malloc((size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){
+ iErrorStop = 180;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ memset( SchemaPtr, 0x00, (size_t) iNoOfFields * (size_t) sizeof( xbSchemaRec ));
+ /* copy field info into memory */
+ for( i = 0, j = 1; i < iNoOfFields; i++ ){
+ xbFseek( ((xbInt64)i*32+32), SEEK_SET );
+ xbFread( &buf, 1, 32 );
+ p = buf;
+ for( int x = 0; x < 10 && buf[x]; x++ ){
+ SchemaPtr[i].cFieldName[x] = buf[x];
+ }
+ p = buf + 11;
+ SchemaPtr[i].cType = *p++;
+ SchemaPtr[i].pAddress = RecBuf + j;
+ SchemaPtr[i].pAddress2 = RecBuf2 + j;
+ SchemaPtr[i].cFieldLen = (unsigned char) *( p + 4 );
+ SchemaPtr[i].cNoOfDecs = (unsigned char) *( p + 5 );
+ SchemaPtr[i].cIxFlag = (unsigned char) *( p + 19 );
+ j += SchemaPtr[i].cFieldLen;
+ #ifdef XB_MEMO_SUPPORT
+ if( (SchemaPtr[i].cType == 'M' || SchemaPtr[i].cType == 'B' || SchemaPtr[i].cType == 'O' ))
+ iMemoFieldCnt++;
+ #endif
+ }
+ ulCurRec = 0L;
+ iDbfStatus = XB_OPEN;
+ if(( iRc = BlankRecord()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iMemoFieldCnt > 0 ){ /* does this table have memo fields ? */
+
+ // build the file name
+ xbString sMfn = GetFqFileName(); /* memo file name, same as filename except ends with a "t", not an "f" */
+ xbUInt32 ulMfnLen = sMfn.Len();
+ if( sMfn[ulMfnLen] == 'F' )
+ sMfn.PutAt( ulMfnLen, 'T' );
+ else
+ sMfn.PutAt( ulMfnLen, 't' );
+ xbFile fTemp( xbase );
+ fTemp.SetFileName( sMfn );
+
+ Memo = new xbMemoDbt4( this, fTemp.GetFqFileName());
+
+ if(( iRc = Memo->OpenMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+
+// printf( "cIndexFlag [%x]\n", cIndexFlag );
+
+ if( cIndexFlag ){
+ // create the file name
+ xbString sIxFileName = GetFqFileName();
+ sIxFileName.Trim();
+ xbUInt32 lLen = sIxFileName.Len();
+ sIxFileName.PutAt( lLen-2, 'M' );
+ sIxFileName.PutAt( lLen-1, 'D' );
+ sIxFileName.PutAt( lLen, 'X' );
+ if(( iRc = OpenIndex( "MDX", sIxFileName )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ }
+ #endif
+
+ }
+ catch ( xbInt16 iRc )
+ {
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf4::Open() Exception Caught Error Stop = [%d] iRc = [%d] ShareMode = [%d] OpenMode = [%d]", iErrorStop, iRc, iShareMode, iOpenMode );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ xbFclose();
+ if( RecBuf ){
+ free( RecBuf );
+ RecBuf = NULL;
+ }
+ if( RecBuf2 ){
+ free( RecBuf2 );
+ RecBuf2 = NULL;
+ }
+ if( SchemaPtr ){
+ free( SchemaPtr );
+ SchemaPtr = NULL;
+ }
+ InitVars();
+
+#ifdef XB_MEMO_SUPPORT
+ if( Memo ){
+ Memo->CloseMemoFile();
+ delete Memo;
+ Memo = NULL;
+ }
+#endif
+ }
+
+ if( iRc == XB_NO_ERROR )
+ iDbfStatus = XB_OPEN;
+
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Rename table.
+/*!
+ This routine renames a table, associated memo, mdx and inf files
+ \param sNewName - New file name.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbDbf4::Rename( const xbString sNewName ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbString sNewDbf;
+
+ #ifdef XB_MEMO_SUPPORT
+ xbString sNewDbt;
+ xbBool bDbtRenamed = xbFalse;
+ xbString sThisDbt;
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ xbString sNewMdx;
+ xbBool bMdxRenamed = xbFalse;
+ xbString sThisMdx;
+ #endif
+
+ #ifdef XB_INF_SUPPORT
+ xbString sNewInf;
+ xbString sThisInf;
+ xbBool bInfRenamed = xbFalse;
+ #endif // XB_INF_SUPPORT
+
+ xbBool bDbfRenamed = xbFalse;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif // XB_LOCKIN_SUPPORT
+
+
+ try{
+
+ xbString sDir;
+ xbString sFile;
+ xbString sExt;
+ xbString sNewNameWoExt;
+ sNewNameWoExt.Set( sNewName );
+ if( sNewName.Pos( ".DBF" ) > 0 )
+ sNewNameWoExt.Left( sNewName.Len() - 4 );
+
+// std::cout << "NewName wo ext = [" << sNewNameWoExt.Str() << "]\n";
+
+ GetFileDirPart ( sDir );
+ GetFileNamePart( sFile );
+ GetFileExtPart ( sExt );
+
+ sNewDbf.Sprintf( "%s%s.DBF", sDir.Str(), sNewNameWoExt.Str());
+
+ #ifdef XB_MEMO_SUPPORT
+ sNewDbt.Sprintf( "%s%s.DBT", sDir.Str(), sNewNameWoExt.Str());
+ if( FileExists( sNewDbt )) return XB_FILE_EXISTS;
+ sThisDbt.Sprintf( "%s%s.DBT", sDir.Str(), sFile.Str());
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ sNewMdx.Sprintf( "%s%s.MDX", sDir.Str(), sNewNameWoExt.Str());
+ if( FileExists( sNewMdx )) return XB_FILE_EXISTS;
+ sThisMdx.Sprintf( "%s%s.MDX", sDir.Str(), sFile.Str());
+ #endif
+
+ #ifdef XB_INF_SUPPORT
+ sNewInf.Sprintf( "%s%s.INF", sDir.Str(), sNewNameWoExt.Str());
+ if( FileExists( sNewInf )) return XB_FILE_EXISTS;
+ sThisInf.Sprintf( "%s%s.INF", sDir.Str(), sFile.Str());
+ #endif // XB_INF_SUPPORT
+
+
+/*
+ std::cout << "xbDbf3::Rename dir = [" << sDir.Str() << "] file = [" << sFile.Str() << "] ext = [" << sExt.Str() << "]\n";
+ std::cout << "xbDbf3::Rename new dbf = [" << sNewDbf.Str() << "]\n";
+ std::cout << "xbDbf3::Rename new dbt = [" << sNewDbt.Str() << "]\n";
+ std::cout << "xbDbf3::Rename new inf = [" << sNewInf.Str() << "]\n";
+ std::cout << "xbDbf3::Rename new mdx = [" << sNewMdx.Str() << "]\n";
+*/
+
+ if( FileExists( sNewDbf )) return XB_FILE_EXISTS;
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( GetAutoLock() ){
+ if(( iRc = LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ bLocked = xbTrue;
+ }
+ #endif
+
+ xbInt16 iOpenMode = GetOpenMode();
+ xbInt16 iShareMode = GetShareMode();
+ xbBool bWasOpen = xbFalse;
+ if( FileIsOpen() ){
+ bWasOpen = xbTrue;
+ if(( iRc = xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ if(( iRc = xbRename( GetFqFileName().Str(), sNewDbf.Str())) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ } else {
+ bDbfRenamed = xbTrue;
+ }
+ xbString sNameWext;
+ sNameWext.Sprintf( "%s.DBF", sNewNameWoExt.Str());
+ SetFileName( sNameWext );
+
+
+ if( bWasOpen ){
+ if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( FileExists( sThisDbt )){
+ if(( iRc = Memo->xbFseek( 8, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ sNewNameWoExt.PadRight( ' ', 8 );
+ for( int i = 1; i < 9; i++ )
+ Memo->xbFputc( sNewNameWoExt[i] );
+
+ if( bWasOpen ){
+ if(( iRc = Memo->xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+
+ Memo->SetFileName( sNewDbt );
+ if(( xbRename( sThisDbt.Str(), sNewDbt.Str())) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+ if( bWasOpen ){
+ if(( iRc = Memo->xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+
+ bDbtRenamed = xbTrue;
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ if( FileExists( sThisMdx )){
+ xbIxMdx *ixMdx;
+ xbString s;
+ xbBool bMdxFound = xbFalse;
+ xbIxList *ixList = GetIxList();
+ while( ixList && !bMdxFound ){
+ s = ixList->sFmt->Str();
+ if( s == "MDX" ){
+ ixMdx = (xbIxMdx *) ixList->ix;
+ bMdxFound = xbTrue;
+ }
+ }
+
+ if( bMdxFound ){
+ if(( iRc = ixMdx->xbFseek( 4, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ sNewNameWoExt.PadRight( ' ', 8 );
+ for( int i = 1; i < 9; i++ )
+ ixMdx->xbFputc( sNewNameWoExt[i] );
+
+ if( bWasOpen ){
+ if(( iRc = ixMdx->xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ }
+
+ ixMdx->SetFileName( sNewMdx );
+ if(( xbRename( sThisMdx.Str(), sNewMdx.Str())) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+
+ if( bWasOpen ){
+ if(( iRc = ixMdx->xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ }
+ bMdxRenamed = xbTrue;
+ }
+ }
+ #endif // XB_MDX_SUPPORT
+
+ #ifdef XB_INF_SUPPORT
+ if( FileExists( sThisInf )){
+ if(( xbRename( sThisInf.Str(), sNewInf.Str())) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ } else {
+ bInfRenamed = xbTrue;
+ }
+ }
+ #endif // XB_INF_SUPPORT
+
+ // rename the table in the table list
+ xbTblList *tle = xbase->GetTblListEntry( this );
+ if( tle ){
+ tle->psFqTblName->Set( GetFqFileName().Str());
+ tle->psTblAlias->Set( sNewNameWoExt.Str());
+ }
+
+
+ }
+ catch ( xbInt16 iRc )
+ {
+ xbString sMsg;
+ sMsg.Sprintf( "xbdbf4::Rename() Exception Caught Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+
+ // attempt to reverse things out if unsuccessful
+ if( bDbfRenamed ){
+
+ #ifdef XB_MEMO_SUPPORT
+ if( bDbtRenamed ){
+ xbRename( sNewDbt.Str(), sThisDbt.Str());
+ }
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ if( bMdxRenamed ){
+ xbRename( sNewMdx.Str(), sThisMdx.Str());
+ }
+ #endif
+
+ #ifdef XB_INF_SUPPORT
+ if( bInfRenamed ){
+ xbRename( sNewInf.Str(), sNewInf.Str());
+ }
+ #endif // XB_INF_SUPPORT
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( GetAutoLock() ){
+ iRc = LockTable( XB_UNLOCK );
+ }
+ #endif
+
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked ){
+ LockTable( XB_UNLOCK );
+ }
+ #endif
+
+ return iRc;
+}
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+
+//! @brief Create memo block size.
+/*!
+ This routine sets the memo file block size. This value is used when
+ the memo file is created so you if you want to change it, this must be
+ called before creating the table.
+
+ The default size for version 4 is 1024.
+
+ \param ulBlockSize - Block size, must be evenly divisible by 512.
+ \returns XB_INVALID_BLOCK_SIZE<br>XB_NO_ERROR
+*/
+
+xbInt16 xbDbf4::SetCreateMemoBlockSize( xbUInt32 ulBlockSize ){
+
+ if( ulBlockSize % 512 )
+ return XB_INVALID_BLOCK_SIZE;
+ else
+ ulCreateMemoBlockSize = ulBlockSize;
+
+ return XB_NO_ERROR;
+}
+#endif // XB_MEMO_SUPPORT
+/************************************************************************/
+//! @brief Set version.
+/*!
+ Sets the version to 4. The version info can be retrieved to determine
+ which class is being used for a given dbf instance.
+ \returns 4
+*/
+xbInt16 xbDbf4::SetVersion() {
+ iFileVersion = 4;
+ return iFileVersion;
+}
+/************************************************************************/
+//! @brief Validate schema
+/*!
+ This routine verifies the field types are valid for Dbase IV (tm).
+
+ \param s Pointer to schema structure with field definitions.
+
+ \returns Number of fields or XB_INVALID_FIELD_TYPE.
+*/
+
+
+xbInt16 xbDbf4::ValidateSchema( xbSchema * s ){
+
+ xbInt16 iFieldCnt = 0;
+ uiRecordLen = 0;
+
+ // Count the number of fields and check paramaters
+ xbInt16 i = 0;
+ while( s[i].cType != 0 ){
+ iFieldCnt++;
+ // Version IV field types
+ if( s[i].cType != 'C' &&
+ s[i].cType != 'N' &&
+ s[i].cType != 'F' &&
+ s[i].cType != 'D' &&
+ #ifdef XB_MEMO_SUPPORT
+ s[i].cType != 'M' &&
+ #endif /* XB_MEMO_SUPPORT */
+ s[i].cType != 'L' ){
+ return XB_INVALID_FIELD_TYPE;
+ }
+
+ if(s[i].cType == 'D'){
+ s[i].iFieldLen = 8;
+ s[i].iNoOfDecs = 0;
+ }
+
+ else if(s[i].cType == 'C')
+ s[i].iNoOfDecs = 0;
+
+ // check for numeric fields which are too long
+ else if((s[i].cType == 'N' || s[i].cType == 'F') && s[i].iFieldLen > 19 ){
+ return XB_INVALID_FIELD_LEN;
+ }
+
+ // field len must be greater then number of decimals
+ else if((s[i].cType == 'N' || s[i].cType == 'F') && s[i].iFieldLen < s[i].iNoOfDecs ){
+ return XB_INVALID_FIELD_LEN;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ else if(s[i].cType == 'M'){
+ s[i].iFieldLen = 10;
+ s[i].iNoOfDecs = 0;
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ uiRecordLen += s[i].iFieldLen;
+ i++;
+ }
+ return iFieldCnt;
+}
+
+} /* namespace */
+#endif /* XB_DBF4_SUPPORT */ \ No newline at end of file
diff --git a/src/core/xbexp.cpp b/src/core/xbexp.cpp
new file mode 100755
index 0000000..b2d4db9
--- /dev/null
+++ b/src/core/xbexp.cpp
@@ -0,0 +1,2721 @@
+/* xbexp.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2017,2021,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under
+the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+
+This module is part of the expression logic and has the code
+for parsing various tokens out of an expression
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_EXPRESSION_SUPPORT
+
+namespace xb{
+
+/*************************************************************************/
+//! Constructor
+/*!
+ \param x Pointer to xbXBase instance.
+*/
+
+xbExp::xbExp( xbXBase *x ){
+ xbase = x;
+ dbf = NULL;
+ nTree = NULL;
+}
+
+/*************************************************************************/
+//! Constructor
+/*!
+ \param x Pointer to xbXBase instance.
+ \param d Pointer to xbDbf instance.
+*/
+xbExp::xbExp( xbXBase *x, xbDbf *d ){
+ xbase = x;
+ dbf = d;
+ nTree = NULL;
+}
+
+/*************************************************************************/
+//! Deconstrucor.
+
+xbExp::~xbExp() {
+
+ if( nTree )
+ delete nTree;
+}
+
+/*************************************************************************/
+//! Calulate expression return length
+/*!
+
+ This function returns the maximum possible length of an expression
+ The create index functions use this for determining the fixed length keys
+ It sets the return length field in the node.
+
+ \param n Start node
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::CalcFunctionResultLen( xbExpNode * n ) const{
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iReturnLenCalc = 0;;
+ xbInt32 lReturnLenVal = 0;
+ xbString sNodeText;
+
+
+ try{
+
+ n->GetNodeText( sNodeText );
+ char cReturnType = 0;
+ if(( iRc = xbase->GetFunctionInfo( sNodeText, cReturnType, iReturnLenCalc, lReturnLenVal )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if( iReturnLenCalc == 1 ){
+ // use the value from iReturnLenVal
+ n->SetResultLen( (xbUInt32) lReturnLenVal );
+ }
+ else if( iReturnLenCalc == 2 ){
+ // use the length from the child node identified in lReturnLenVal
+ xbExpNode *nChild = n->GetChild( (xbUInt32) lReturnLenVal - 1 );
+ if( !nChild ){
+ iErrorStop = 110;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ n->SetResultLen( nChild->GetResultLen());
+ }
+
+ else if( iReturnLenCalc == 3 ){
+ // use the length from the child node identified in lReturnLenVal
+ xbExpNode *nChild = n->GetChild( (xbUInt32) lReturnLenVal - 1 );
+ if( !nChild ){
+ iErrorStop = 120;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ n->SetResultLen( (xbUInt32) nChild->GetNumericResult());
+ }
+ else if( iReturnLenCalc == 4 ){
+ // use the value from the length in parm 1 multiplied by the value in parm 2 (REPLICATE)
+ xbExpNode *nChild1 = n->GetChild( 0 );
+ xbExpNode *nChild2 = n->GetChild( 1 );
+ if( !nChild1 || !nChild2 ){
+ iErrorStop = 130;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ n->SetResultLen( nChild1->GetResultLen() * (xbUInt32) nChild2->GetNumericResult());
+ }
+ else if( iReturnLenCalc == 5 ){
+ // use the larger of the length of the value in parm2 or parm 3 (IIF statement)
+ xbExpNode *nChild2 = n->GetChild( 1 );
+ xbExpNode *nChild3 = n->GetChild( 2 );
+ if( !nChild2 || !nChild3 ){
+ iErrorStop = 140;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ if( nChild2->GetResultLen() >= nChild3->GetResultLen())
+ n->SetResultLen( nChild2->GetResultLen());
+ else
+ n->SetResultLen( nChild3->GetResultLen());
+ }
+
+ else if( iReturnLenCalc == 6 ){
+
+ if( n->GetChildCnt() >= 2 ){
+ xbExpNode *nChild2 = n->GetChild( 1 );
+ n->SetResultLen( (xbUInt32) nChild2->GetNumericResult());
+ } else {
+ n->SetResultLen( (xbUInt32) lReturnLenVal );
+ }
+
+ } else {
+ iErrorStop = 150;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::GetFunctionResultLen() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+
+/*************************************************************************/
+//! Check parens and quotes
+/*!
+ This routine looks for unbalanced parens and quotes
+
+ \param sExpression Expression to examine.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbExp::CheckParensAndQuotes( const xbString &sExpression ){
+
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbBool bInQuotes = xbFalse;
+ xbInt16 iLparenCtr = 0;
+ xbInt16 iRparenCtr = 0;
+ xbInt16 iQuoteType = 0;
+ const char *s = sExpression;
+
+ try{
+
+ while( *s ){
+ if( !bInQuotes ){
+ if( *s == '(' ){
+ iLparenCtr++;
+ } else if( *s == ')' ){
+ iRparenCtr++;
+ } else if( *s == '\'' ){
+ bInQuotes++;
+ iQuoteType = 0;
+ } else if( *s == '"' ){
+ bInQuotes++;
+ iQuoteType = 1;
+ }
+ } else {
+ if(( *s == '\'' && iQuoteType == 0 ) || (*s == '"' && iQuoteType == 1 ))
+ bInQuotes--;
+ }
+ s++;
+ }
+ if( iLparenCtr != iRparenCtr ){
+ iErrorStop = 100;
+ iRc = XB_UNBALANCED_PARENS;
+ throw iRc;
+ }
+ if( bInQuotes ){
+ iErrorStop = 110;
+ iRc = XB_UNBALANCED_QUOTES;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::CheckParensAndQuots() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( sExpression );
+ }
+ return iRc;
+}
+/*************************************************************************/
+//! Clear tree handle.
+/*!
+ This routine clears the expression tree and frees any associated memory.
+ \returns void.
+*/
+
+void xbExp::ClearTreeHandle(){
+ if( nTree ){
+ nTree = NULL;
+ }
+}
+
+/*************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! Dump the tree.
+/*!
+ \param iOption - Output opton.
+ \returns void.
+*/
+
+void xbExp::DumpTree( xbInt16 iOption ){
+ nTree->DumpNode( iOption );
+}
+
+//! Dump token
+/*!
+ \param iOption - Output opton.
+ \returns void.
+*/
+
+
+void xbExp::DumpToken( xbExpToken &t, xbInt16 iOption ){
+
+ xbString sMsg;
+ sMsg = "Processing Token";
+ xbase->WriteLogMessage( sMsg.Str(), iOption );
+
+ sMsg.Sprintf( "Expression = [%s]", t.sExpression.Str());
+ xbase->WriteLogMessage( sMsg.Str(), iOption );
+
+ sMsg.Sprintf( "Token = [%s]", t.sToken.Str());
+ xbase->WriteLogMessage( sMsg.Str(), iOption );
+
+ sMsg.Sprintf( "NodeType = [%c]", t.cNodeType );
+ xbase->WriteLogMessage( sMsg.Str(), iOption );
+
+ sMsg.Sprintf( "ReturnType = [%c]", t.cReturnType );
+ xbase->WriteLogMessage( sMsg.Str(), iOption );
+
+ sMsg.Sprintf( "Sts = [%d]", t.iSts );
+ xbase->WriteLogMessage( sMsg.Str(), iOption );
+
+ sMsg.Sprintf( "PrevNodeType = [%c]", t.cPrevNodeType );
+ xbase->WriteLogMessage( sMsg.Str(), iOption );
+
+ sMsg.Sprintf( "PrevReturnType = [%c]", t.cPrevReturnType );
+ xbase->WriteLogMessage( sMsg.Str(), iOption );
+}
+
+#endif
+
+/*************************************************************************/
+//! Get date result.
+/*!
+ If the expression generates a date return type, this method retrieves the date value.
+ \param dtResult - Output date value.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::GetDateResult( xbDate &dtResult ){
+ if( nTree ){
+ dtResult.JulToDate8( (xbInt32) nTree->GetNumericResult() );
+ return XB_NO_ERROR;
+ }
+ else{
+ //dtResult = ?;
+ return XB_PARSE_ERROR;
+ }
+}
+/*************************************************************************/
+//! Get bool result.
+/*!
+ If the expression generates a boolean return type, this method retrieves the boolean value.
+ \param bResult - Output boolean value.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbExp::GetBoolResult( xbBool &bResult){
+ if( nTree ){
+ bResult = nTree->GetBoolResult();
+ return XB_NO_ERROR;
+ }
+ else{
+ return XB_PARSE_ERROR;
+ }
+}
+
+/*************************************************************************/
+//! Get the next node in the tree.
+/*!
+ \param n Node to starting point. To get the first node of the entire tree, set n = NULL
+ \returns Pointer to next node.
+*/
+
+xbExpNode *xbExp::GetNextNode( xbExpNode * n ) const {
+
+ // to get the first node of the entire tree, set n = NULL
+ // std::cout << "In GetNextNode\n";
+
+ if( n == nTree )
+ return NULL;
+
+ else if( !n ){
+ if( !nTree )
+ return NULL;
+ else
+ return nTree->GetFirstNode();
+ }
+ return n->GetNextNode();
+}
+
+/*************************************************************************/
+//! GetNextToken
+/*! This method returns the next token in an expression of one or more tokens
+ \param t Token
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::GetNextToken( xbExpToken &t ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ t.iSts = XB_NO_ERROR;
+ t.sExpression.Ltrim();
+
+ if( t.sExpression.Len() == 0 ){
+ t.iSts = XB_END_OF_EXPRESSION;
+ return XB_NO_ERROR;
+ }
+
+ // Check for date constant
+ if((t.sExpression.Len() >= 10 && t.sExpression[1] == '{' && t.sExpression[4] == '/' && t.sExpression[7] == '/') &&
+ (t.sExpression[10] == '}' || (t.sExpression.Len() >= 12 && t.sExpression[12] == '}'))){
+ if(( iRc = GetTokenDateConstant( t )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ // Check for parens
+ else if( t.sExpression[1] == '(' || t.sExpression[1] == '{' ){
+ if(( iRc = GetTokenParen( t )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ // Check for a char constant
+ else if( t.sExpression[1] == '"' || t.sExpression[1] == '\'' ){
+ if(( iRc = GetTokenCharConstant( t )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ // Check for logical constant
+ else if( IsLogicalConstant( t.sExpression )){
+ if(( iRc = GetTokenLogicalConstant( t )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ // check for numeric constant
+ else if( IsNumericConstant( t.sExpression, t.cPrevNodeType )){
+ if(( iRc = GetTokenNumericConstant( t )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ // check for operator
+ else if( IsOperator( t.sExpression )){
+ if(( iRc = GetTokenOperator( t )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ // check for function
+ else if( IsFunction( t.sExpression, t.cReturnType )){
+ if(( iRc = GetTokenFunction( t )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ else if(( iRc = GetTokenDatabaseField( t )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+
+/*************************************************************************/
+//! Get numeric result.
+/*!
+ If the expression generates a numeric return type, this method retrieves the numeric value.
+ \param dResult - Output numeric value.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::GetNumericResult( xbDouble &dResult){
+ if( nTree ){
+ dResult = nTree->GetNumericResult();
+ return XB_NO_ERROR;
+ }
+ else{
+ dResult = 0;
+ return XB_PARSE_ERROR;
+ }
+}
+/*************************************************************************/
+//! Get result length.
+/*!
+ This routine returns the result length.
+ \returns Result length.
+*/
+
+xbInt16 xbExp::GetResultLen() const{
+ if( nTree )
+ return nTree->GetResultLen();
+ else
+ return 0;
+}
+
+/*************************************************************************/
+//! Get return type.
+/*!
+ \returns Expression return type.
+*/
+
+char xbExp::GetReturnType() const{
+ if( nTree )
+ return nTree->GetReturnType();
+ else
+ return ' ';
+}
+
+/*************************************************************************/
+//! Get string result.
+/*!
+ If the expression generates a string return type, this method retrieves the string value.
+ \param sResult - Output string value.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::GetStringResult( xbString &sResult){
+ if( nTree ){
+ sResult = nTree->GetStringResult();
+ return XB_NO_ERROR;
+ }
+ else{
+ sResult = "";
+ return XB_PARSE_ERROR;
+ }
+}
+
+/*************************************************************************/
+//! Get string result.
+/*!
+ If the expression generates a string return type, this method retrieves the string value.
+ \param vpResult - Pointer to user supplied buffer for result.
+ \param ulLen - Max size of buffer.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbExp::GetStringResult( char * vpResult, xbUInt32 ulLen ){
+ if( nTree ){
+ nTree->GetStringResult().strncpy((char *) vpResult, ulLen );
+ return XB_NO_ERROR;
+ }
+ else{
+ return XB_PARSE_ERROR;
+ }
+}
+
+
+
+/*************************************************************************/
+//! GetTokenCharConstant
+/*! This method returns the character constant in a pair of quotes
+
+ This routine returns the tokens inside a set of matching quotes in sOutToken
+ If there is nothing between the quotes then sOutToken is returned empty
+ sOutRemainder contains whatever remains to the right of the right quote
+
+ \param t Token
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbExp::GetTokenCharConstant( xbExpToken &t ){
+
+ const char *s = t.sExpression;
+ const char *sToken; // pointer to beginning of token
+ xbInt16 iQuoteType;
+ xbUInt32 ulTokenLen = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbBool bDone = xbFalse;
+
+ try{
+ if( *s == '"' )
+ iQuoteType = 0;
+ else
+ iQuoteType = 1;
+ s++;
+ sToken = s;
+ while( *s && !bDone ){
+ if(( *s == '"' && iQuoteType == 0 ) || (*s == '\'' && iQuoteType == 1 ))
+ bDone = xbTrue;
+ s++;
+ ulTokenLen++;
+ }
+ if( bDone ){ // found matching paren
+ t.cNodeType = XB_EXP_CONSTANT;
+ t.cReturnType = XB_EXP_CHAR;
+ t.sToken.Set( sToken, ulTokenLen - 1 );
+ t.sExpression.Ltrunc( ulTokenLen + 1 );
+ } else {
+ iRc = XB_PARSE_ERROR;
+ t.iSts = XB_UNBALANCED_QUOTES;
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::GetTokenCharConstant() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+
+
+/*************************************************************************/
+//! GetTokenDateConstant
+/*! This method returns the date constant in a pair of {}
+
+ Date format is one of {mm/dd/yy} or {mm/dd/yyyy}
+ \param t Token.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::GetTokenDateConstant( xbExpToken &t ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char wBuf[13];
+ xbDate dt;
+
+ try{
+ memset( wBuf, 0x00, 13 );
+ t.cNodeType = XB_EXP_CONSTANT;
+ t.cReturnType = XB_EXP_DATE;
+
+ if( t.sExpression[10] == '}' ){
+ for( xbInt16 i = 0; i < 8; i++ )
+ wBuf[i] = t.sExpression[i+2];
+
+ if(( iRc = dt.CTOD( wBuf )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ t.sToken.Set( dt.Str() );
+ t.sExpression.Ltrunc( 10 );
+
+ } else if( t.sExpression[12] == '}' ){
+
+ wBuf[0] = t.sExpression[8];
+ wBuf[1] = t.sExpression[9];
+ wBuf[2] = t.sExpression[10];
+ wBuf[3] = t.sExpression[11];
+ wBuf[4] = t.sExpression[2];
+ wBuf[5] = t.sExpression[3];
+ wBuf[6] = t.sExpression[5];
+ wBuf[7] = t.sExpression[6];
+
+ t.sToken.Set( wBuf );
+ t.sExpression.Ltrunc( 12 );
+ } else {
+ iRc = XB_PARSE_ERROR;
+ t.iSts = XB_INVALID_DATE;
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::GetTokenDateConstant() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+/*************************************************************************/
+//! GetTokenField
+/*! This method gets a database field token
+
+ Looks for a xbase field in one of the following formats
+
+ FIELDNAME
+ or
+ TABLENAME->FIELDNAME
+
+ \param t Token.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::GetTokenDatabaseField( xbExpToken &t ){
+
+ const char *s = t.sExpression;
+ xbUInt32 ulTokenLen = 0;
+ xbUInt32 ulTokenLen2 = 0;
+
+ while( *s && !IsTokenSeparator( *s ) && !IsWhiteSpace( *s )) {
+ ulTokenLen++;
+ s++;
+ }
+
+ // go past any white space
+ while( *s && !IsTokenSeparator( *s ) && IsWhiteSpace( *s )) {
+ ulTokenLen2++;
+ s++;
+ }
+
+ // look for ->
+ // remove the table name from before the ->
+ if( strncmp( s, "->", 2 ) == 0 ){
+ ulTokenLen2+=2;
+ s+=2;
+
+/*
+ if( strncmp( s, "->", 2 ) == 0 || strncmp( s, ".", 1 ) == 0){
+ if( *s == '.' ){
+ ulTokenLen2+=1;
+ s+=1;
+ } else {
+ ulTokenLen2+=2;
+ s+=2;
+ }
+*/
+
+ // go past white space
+ while( *s && !IsTokenSeparator( *s ) && IsWhiteSpace( *s )) {
+ ulTokenLen2++;
+ s++;
+ }
+ // go to the end
+ while( *s && !IsTokenSeparator( *s ) && !IsWhiteSpace( *s )) {
+ ulTokenLen2++;
+ s++;
+ }
+ ulTokenLen += ulTokenLen2;
+ }
+ t.cNodeType = XB_EXP_FIELD;
+ t.cReturnType = XB_EXP_UNKNOWN;
+ t.sToken.Set( t.sExpression, ulTokenLen );
+ t.sExpression.Ltrunc( ulTokenLen );
+
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! GetTokenFunction
+/*!
+ This method gets a function and everything between the following quotes
+ \param t Token
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbExp::GetTokenFunction( xbExpToken &t ){
+
+ xbUInt32 lPos = t.sExpression.Pos( '(' );
+ if( lPos == 0 )
+ return XB_PARSE_ERROR;
+
+ xbBool bDone = xbFalse;
+ xbUInt32 lLen = t.sExpression.Len();
+ xbInt16 iDepthCtr = 1;
+
+ while( ++lPos <= lLen && !bDone ){
+ if( t.sExpression[lPos] == ')' ){
+ iDepthCtr--;
+ if( iDepthCtr == 0 )
+ bDone = xbTrue;
+ } else if( t.sExpression[lPos] == '(' ){
+ iDepthCtr++;
+ }
+ }
+
+ t.cNodeType = XB_EXP_FUNCTION;
+ t.sToken.Set( t.sExpression, lPos-1 );
+ t.sExpression.Ltrunc( lPos-1 );
+
+// std::cout << "lPos = [" << lPos << "] done= [" << bDone << "][" << t.sExpression << "] len=[" << lLen << "] return type = [" << t.cReturnType << "]\n";
+
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! GetTokenCharConstant
+/*! This method returns the character constant in a pair of quotes
+
+ This routine returns the tokens inside a set of matching quotes in sOutToken
+ If there is nothing between the quotes then sOutToken is returned empty
+ sOutRemainder contains whatever remains to the right of the right quote
+
+ \param t Token
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::GetTokenLogicalConstant( xbExpToken &t ){
+
+ t.cNodeType = XB_EXP_CONSTANT;
+ t.cReturnType = XB_EXP_LOGICAL;
+ t.sToken = t.sExpression[2];
+
+ if( t.sExpression[3] == '.' )
+ t.sExpression.Ltrunc( 3 );
+ else if( t.sExpression[6] == '.' )
+ t.sExpression.Ltrunc( 6 );
+ else if( t.sExpression[7] == '.' )
+ t.sExpression.Ltrunc( 7 );
+
+ return XB_NO_ERROR;
+}
+
+
+/*************************************************************************/
+//! GetTokenNumericConstant
+/*! This method returns a numeric constant in
+
+ This routine returns a numeric constant token
+ sOutRemainder contains whatever remains to the right of the right quote
+
+ \param t Token
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::GetTokenNumericConstant( xbExpToken &t ){
+
+ const char * s = t.sExpression;
+ xbUInt32 ulTokenLen = 0;
+ t.sToken = "";
+
+ t.cNodeType = XB_EXP_CONSTANT;
+ t.cReturnType = XB_EXP_NUMERIC;
+
+ // capture the leading sign
+ if( *s == '-' || *s == '+' || *s == '.' ){
+ t.sToken = *s;
+ ulTokenLen++;
+ s++;
+
+ // go past any white space between sign and number
+ while( *s && IsWhiteSpace( *s )){
+ s++;
+ ulTokenLen++;
+ }
+ }
+
+ // add the number to the token
+ while( *s && (isdigit( *s ) || *s == '.' )){
+ t.sToken += *s;
+ s++;
+ ulTokenLen++;
+ }
+ t.sExpression.Ltrunc( ulTokenLen );
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! GetTokenOperator
+/*! This method returns the operator
+
+ \param t Token
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbExp::GetTokenOperator( xbExpToken &t ){
+
+ const char *s = t.sExpression;
+
+ // Logical operators
+ if((strncmp( s, "<>", 2 ) == 0 ) || (strncmp( s, "!=", 2 ) == 0 ) ||
+ (strncmp( s, "<=", 2 ) == 0 ) || (strncmp( s, ">=", 2 ) == 0 )){
+ t.cReturnType = XB_EXP_LOGICAL;
+ t.sToken.Assign( s, 1, 2 );
+ t.sExpression.Ltrunc( 2 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+
+ if( *s == '=' || *s == '<' || *s == '>' || *s == '$' || *s == '#' ){
+ t.cReturnType = XB_EXP_LOGICAL;
+ t.sToken.Assign( s, 1, 1 );
+ t.sExpression.Ltrunc( 1 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+
+ if( (strncmp( s, ".NOT.", 5 ) == 0 ) || (strncmp( s, ".AND.", 5 ) == 0 )){
+ t.cReturnType = XB_EXP_LOGICAL;
+ t.sToken.Assign( s, 1, 5 );
+ t.sExpression.Ltrunc( 5 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+
+ if( (strncmp( s, "NOT ", 4 ) == 0 ) || (strncmp( s, "AND ", 4 ) == 0 )){
+ t.cReturnType = XB_EXP_LOGICAL;
+ t.sToken.Assign( s, 1, 3 );
+ t.sExpression.Ltrunc( 3 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+
+ if( strncmp( s, ".OR.", 4 ) == 0 ) {
+ t.cReturnType = XB_EXP_LOGICAL;
+ t.sToken.Assign( s, 1, 4 );
+ t.sExpression.Ltrunc( 4 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+
+ if( strncmp( s, "OR ", 3 ) == 0 ) {
+ t.cReturnType = XB_EXP_LOGICAL;
+ t.sToken.Assign( s, 1, 2 );
+ t.sExpression.Ltrunc( 2 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+
+ // Numeric operators
+ if(( strncmp( s, "**", 2 ) == 0 ) || ( strncmp( s, "+=", 2 ) == 0 ) ||
+ ( strncmp( s, "-=", 2 ) == 0 ) || ( strncmp( s, "*=", 2 ) == 0 ) || ( strncmp( s, "/=", 2 ) == 0 )){
+ t.cReturnType = XB_EXP_NUMERIC;
+ t.sToken.Assign( s, 1, 2 );
+ t.sExpression.Ltrunc( 2 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+
+ // Pre/post increment/decrement operators ++ or --
+ if(( strncmp( s, "--", 2 ) == 0 ) || ( strncmp( s, "++", 2 ) == 0 )){
+ t.cReturnType = XB_EXP_NUMERIC;
+ t.sToken.Assign( s, 1, 2 );
+ t.sExpression.Ltrunc( 2 );
+ if( t.sExpression.Len() > 0 && (isdigit( t.sExpression[1] ) || isalpha( t.sExpression[1] )))
+ t.cNodeType = XB_EXP_PRE_OPERATOR;
+ else
+ t.cNodeType = XB_EXP_POST_OPERATOR;
+
+ return XB_NO_ERROR;
+ }
+
+ if( *s == '*' || *s == '/' || *s == '%' || *s == '^' ){
+ t.cReturnType = XB_EXP_NUMERIC;
+ t.sToken.Assign( s, 1, 1 );
+ t.sExpression.Ltrunc( 1 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+
+ // multi return type operators
+ t.cReturnType = XB_EXP_UNKNOWN;
+ if( *s == '+' || *s == '-' ){
+ t.sToken.Assign( s, 1, 1 );
+ t.sExpression.Ltrunc( 1 );
+ t.cNodeType = XB_EXP_OPERATOR;
+ return XB_NO_ERROR;
+ }
+ return XB_PARSE_ERROR;
+}
+
+/*************************************************************************/
+//! GetTokenParen
+/*! This method returns the tokens in a pair of enclosed parens
+
+ This routine returns the tokens inside a set of matching parens in sOutToken
+ If there is nothing between the parens then sOutToken is returned empty
+ sOutRemainder contains whatever remains to the right of the right paren
+
+ \param t Token
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbExp::GetTokenParen( xbExpToken &t ){
+
+ const char * s = t.sExpression;
+ const char * sToken; // pointer to beginning of token
+ xbInt16 iParenType = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iDepthCtr = 0; // depth inside of nested parens
+ xbUInt32 ulTokenLen = 0;
+ xbBool bDone = xbFalse;
+
+ try{
+ if( *s == '{' )
+ iParenType = 0;
+ else
+ iParenType = 1;
+ iDepthCtr = 1;
+ s++;
+ sToken = s;
+
+ while( *s && !bDone ){
+ if(( *s == ')' && iParenType == 1 ) || (*s == '}' && iParenType == 0 )){
+ iDepthCtr--;
+ if( iDepthCtr == 0 )
+ bDone = xbTrue;
+ } else if(( *s == '(' && iParenType == 1 ) || (*s == '{' && iParenType == 0 )){
+ iDepthCtr++;
+ }
+ s++;
+ ulTokenLen++;
+ }
+
+ if( bDone ){ // found matching paren
+ t.cNodeType = XB_EXP_NOTROOT;
+ t.cReturnType = XB_EXP_UNKNOWN;
+ t.sToken.Set( sToken, ulTokenLen - 1 );
+ t.sExpression.Ltrunc( ulTokenLen + 1 );
+ } else {
+ t.sToken = "";
+ t.cNodeType = XB_EXP_NOTROOT;
+ t.cReturnType = XB_EXP_UNKNOWN;
+ t.iSts = XB_UNBALANCED_PARENS;
+ iRc = XB_PARSE_ERROR;
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::GetTokenParen() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ }
+ return iRc;
+}
+
+/*************************************************************************/
+//! Get the expression tree handle.
+/*!
+ \returns Pointer to the top most node in the expression tree.
+*/
+xbExpNode *xbExp::GetTreeHandle(){
+ return nTree;
+}
+
+/*************************************************************************/
+//! Is Function
+/*! This method determines if the next token is a function.
+
+ \param sExpression - String expression to be evaluated.
+ \param cReturnType Output - Return type.
+ \returns xbTrue - Is a function.<br>
+ xbFalse - Is not a function.
+*/
+
+xbBool xbExp::IsFunction( const xbString & sExpression, char &cReturnType ){
+
+ xbInt16 i = 0;
+ xbInt32 l = 0;
+ if( sExpression.Pos( '(' ) > 0 ){
+ if( xbase->GetFunctionInfo( sExpression, cReturnType, i, l ) == XB_NO_ERROR )
+ return xbTrue;
+ }
+ return xbFalse;
+}
+
+/*************************************************************************/
+//! Is Logical constant
+/*! This method determines if the next token is a logical constant (T/F, etc).
+
+ \param sExpression - String expression to be evaluated.
+ \returns xbTrue - Is a logical constant.<br>
+ xbFalse - Is not a logical constant.
+*/
+
+xbBool xbExp::IsLogicalConstant( const xbString & sExpression ){
+
+ const char *s = sExpression;
+ if(( strncmp( s, ".T.", 3 ) == 0 ) || ( strncmp( s, ".F.", 3 ) == 0 ))
+ return xbTrue;
+ else if( strncmp( s, ".TRUE.", 6 ) == 0 )
+ return xbTrue;
+ else if( strncmp( s, ".FALSE.", 7 ) == 0 )
+ return xbTrue;
+
+ return xbFalse;
+}
+
+/*************************************************************************/
+//! Is Numeric constant
+/*! This method determines if the next token is a numeric constant.
+
+ \param sExpression - String expression to be evaluated.
+ \param cPrevNodeType - Type of previous node.
+ \returns xbTrue - Is a numeric constant.<br>
+ xbFalse - Is not a numeric constant.
+*/
+xbBool xbExp::IsNumericConstant( const xbString & sExpression, char cPrevNodeType ){
+
+ // check for positive, negative or decimal number constants
+
+ const char *s = sExpression;
+ if(( *s == '-' && ( cPrevNodeType == 'O' || cPrevNodeType == 0 )) ||
+ ( *s == '+' && ( cPrevNodeType == 'O' || cPrevNodeType == 0 ))){
+ s++;
+ while( *s && IsWhiteSpace( *s ))
+ s++;
+ }
+ if( *s == '.' )
+ s++;
+
+ if( isdigit( *s ))
+ return xbTrue;
+ else
+ return xbFalse;
+}
+
+/*************************************************************************/
+//! Is Operator.
+/*! This method determines if the next token is an operator.
+
+ \param sExpression - String expression to be evaluated.
+ \returns xbTrue - Is an operator.<br>
+ xbFalse - Is not an operator.
+*/
+xbBool xbExp::IsOperator( const xbString & sExpression ){
+
+ const char *s = sExpression;
+ if( *s == '+' || *s == '-' || *s == '/' || *s == '^' || *s == '=' || *s == '$' ||
+ *s == '#' || *s == '*' || *s == '<' || *s == '>' || *s == '%' )
+ return xbTrue;
+
+ if( strncmp( s, "!=", 2 ) == 0 )
+ return xbTrue;
+
+ if((strncmp( s, ".NOT.", 5 ) == 0 ) || (strncmp( s, ".OR.", 4 ) == 0 ) || (strncmp( s, ".AND.", 5 ) == 0 ))
+ return xbTrue;
+
+ if((strncmp( s, "NOT ", 4 ) == 0 ) || (strncmp( s, "OR ", 3 ) == 0 ) || (strncmp( s, "AND ", 4 ) == 0 ))
+ return xbTrue;
+
+ return xbFalse;
+}
+
+/*************************************************************************/
+//! Is Token separator
+/*! This method determines if the next token is a separator.
+
+ \param sExpression - String expression to be evaluated.
+ \returns xbTrue - Is a token separator.<br>
+ xbFalse - Is not a token separator.
+*/
+char xbExp::IsTokenSeparator( char c ){
+ if( c == '-' || c == '+' || c == '*' || c == '/' || c == '$' || c == '#' ||
+ c == '<' || c == '>' || c == '^' || c == '=' || c == '.' || c == '!' )
+ return c;
+ else
+ return 0;
+}
+/*************************************************************************/
+//! Is White space
+/*! This method determines if a given character is white space.
+
+ \param c - Character to be evaluated.
+ \returns xbTrue - Is white space.<br>
+ xbFalse - Is not white space.
+*/
+xbBool xbExp::IsWhiteSpace( char c ){
+ return(( c == 0x20 )? 1 : 0 );
+}
+
+/*************************************************************************/
+//! Get operator weight.
+/*!
+ This method determines the priority of an operator
+
+ Operator precendence
+ 10 .AND. .OR. .NOT. (not really an operator)
+ 9 > or < (includes <= or >=)
+ 6 unary plus or minus (+,-) -- not passed this routine
+ 5 prefix increment and/or decrement (++,--)
+ 4 exponentiation ** or ^
+ 3 multiplication,division or modulus (*,/,%)
+ 2 Addition, subtraction (+,-)
+ 1 Postfix increment and/or decrement (++,--)
+
+ \param sOper - Operator.
+ \returns Operator weight
+
+*/
+
+xbInt16 xbExp::OperatorWeight( const xbString &sOper ){
+
+ if( sOper == "" || sOper.Len() > 5 )
+ return 0;
+
+ else if( sOper == "--0" || sOper == "++0" ) // 0 is prefix
+ return 9;
+ else if( sOper == "**" || sOper == "^" )
+ return 8;
+ else if( sOper == "*" || sOper == "/" || sOper == "%" || sOper == "*=" || sOper == "/=" )
+ return 7;
+ else if( sOper == "+" || sOper == "-" || sOper == "+=" || sOper == "-=" )
+ return 6;
+ else if( sOper == "--1" || sOper == "++1" ) // 1 is post fix
+ return 5;
+ else if( sOper == ">" || sOper == ">=" || sOper == "<" || sOper == "<=" ||
+ sOper == "<>" || sOper == "!=" || sOper == "#" || sOper == "$" || sOper == "=" )
+ return 4;
+ else if( sOper == ".NOT." || sOper == "NOT" )
+ return 3;
+ else if( sOper == ".AND." || sOper == "AND" )
+ return 2;
+ else if( sOper == ".OR." || sOper == "OR" )
+ return 1;
+
+ return 0;
+}
+
+
+/*************************************************************************/
+//! Parse expression.
+/*!
+ \param sExpression - Expression to parse.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::ParseExpression( const xbString &sExpression ){
+ return ParseExpression( sExpression, (xbInt16) 0 );
+}
+
+/*************************************************************************/
+//! Parse expression.
+/*!
+ \param dbf - Pointer to xbDbf instance.
+ \param sExpression - Expression to parse.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::ParseExpression( xbDbf *dbf, const xbString &sExpression ){
+ this->dbf = dbf;
+ return ParseExpression( sExpression, (xbInt16) 0 );
+}
+
+/*************************************************************************/
+//! Parse expression.
+/*!
+ \param sExpression - Expression to parse.
+ \param iWeight.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){
+
+ xbExpNode *n = NULL;
+ xbExpNode *nLastNode = NULL; // pointer to the last node processed
+ xbExpToken t;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbString s;
+ xbBool bNewNode = xbFalse;
+
+ try {
+
+ if( nTree )
+ delete nTree;
+
+ if(( iRc = CheckParensAndQuotes( sExpression )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ t.sExpression = sExpression;
+
+ xbString sOriginalExp;
+ while( t.iSts == XB_NO_ERROR && iRc == XB_NO_ERROR ){
+
+ sOriginalExp = t.sExpression; // test code
+ iRc = GetNextToken( t );
+ if( !iRc && !t.iSts ){
+
+ // comment / uncomment debug / live
+ // DumpToken( t, 0 );
+
+ if( t.cNodeType == XB_EXP_NOTROOT ){
+ xbExp enr( xbase, dbf );
+ if(( iRc = enr.ParseExpression( t.sToken, iWeight + 10 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ n = enr.GetTreeHandle();
+ enr.ClearTreeHandle();
+
+ } else {
+
+ switch( t.cNodeType ) {
+
+ case XB_EXP_CONSTANT:
+ n = new xbExpNode( t.sToken, t.cNodeType );
+ bNewNode = xbTrue;
+ if(( iRc = ParseExpressionConstant( t, n )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ break;
+
+ case XB_EXP_FUNCTION:
+ n = new xbExpNode( t.cNodeType );
+ bNewNode = xbTrue;
+ if(( iRc = ParseExpressionFunction( t, n, iWeight )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ break;
+
+ case XB_EXP_FIELD:
+ n = new xbExpNode( t.cNodeType );
+ bNewNode = xbTrue;
+ if(( iRc = ParseExpressionField( t, n )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ break;
+
+ case XB_EXP_OPERATOR:
+ case XB_EXP_PRE_OPERATOR:
+ case XB_EXP_POST_OPERATOR:
+ n = new xbExpNode( t.sToken, t.cNodeType );
+ bNewNode = xbTrue;
+ if(( iRc = ParseExpressionOperator( t, n, iWeight )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ break;
+
+ default:
+ iErrorStop = 160;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ }
+ t.cPrevNodeType = t.cNodeType;
+ t.cPrevReturnType = t.cReturnType;
+
+ // determine where in the expression tree to insert the latest node "n"
+ // Is this the first node to be added to the tree?
+ if( !nTree ){
+ nTree = n;
+ }
+
+ // else if last node was XB_EXB_PRE_OPERATOR then append this as child to last node
+ else if( nLastNode && nLastNode->GetNodeType() == XB_EXP_PRE_OPERATOR ){
+ n->SetParent( nLastNode );
+ nLastNode->AddChild( n );
+ }
+
+ // else if last node was XB_EXB_POST_OPERATOR then append this as child to last node
+ else if( nLastNode && n->GetNodeType() == XB_EXP_POST_OPERATOR ){
+ n->AddChild( nLastNode );
+ nLastNode->SetParent( n );
+ if( nLastNode == nTree ){
+ nTree = n;
+ } else {
+ nLastNode->GetParent()->RemoveLastChild();
+ nLastNode->GetParent()->AddChild( n );
+ n->SetParent( nLastNode->GetParent() );
+ }
+ }
+
+ else if( n->GetNodeType() == XB_EXP_OPERATOR ){
+ xbExpNode * nWorkNode = nLastNode;
+ while( nWorkNode && ( nWorkNode->GetNodeType() != XB_EXP_OPERATOR || n->GetWeight() <= nWorkNode->GetWeight())){
+ nWorkNode = nWorkNode->GetParent();
+ }
+
+ if( !nWorkNode ){ // we are at the top
+ nTree->SetParent( n );
+ n->AddChild( nTree );
+ nTree = n;
+
+ } else if( nWorkNode->GetChildCnt() == 1 ){
+ nWorkNode->AddChild( n );
+ n->SetParent( nWorkNode );
+
+ } else if( nWorkNode->GetChildCnt() == 2 ){
+ xbExpNode * nChild2 = nWorkNode->GetChild(1);
+ n->AddChild( nChild2 );
+ nWorkNode->RemoveLastChild();
+ nWorkNode->AddChild( n );
+ n->SetParent( nWorkNode );
+
+ } else{
+ // should not be stopping on anything but an operator node with one or two children
+ iErrorStop = 170;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ } else {
+ n->SetParent( nLastNode );
+ nLastNode->AddChild( n );
+ }
+ nLastNode = n;
+ n = NULL;
+ }
+ }
+
+ // for each node in the tree, calculate the length if it's not already set
+ xbExpNode * nWork = GetNextNode( NULL );
+ xbExpNode * nChild1;
+ xbExpNode * nChild2;
+
+ while( nWork ){
+ if( nWork->GetReturnType() == XB_EXP_UNKNOWN ){
+ nWork->GetNodeText( s );
+
+ // std::cout << "XB_EXP_UNKNOWN logic [" << s << "][" << nWork->GetChildCnt() << "]\n";
+ // if this is "-" and child 1 and child 2 are both dates, set this result type to numeric
+ if( s == "-" && nWork->GetChildCnt() == 2 &&
+ nWork->GetChild(0)->GetReturnType() == XB_EXP_DATE && nWork->GetChild(1)->GetReturnType() == XB_EXP_DATE )
+ nWork->SetReturnType( XB_EXP_NUMERIC );
+ else if( nWork->GetChildCnt() > 0 )
+ nWork->SetReturnType( nWork->GetChild(0)->GetReturnType());
+ else{
+ iErrorStop = 180;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+ if( nWork->GetResultLen() == 0 ){
+
+ switch( nWork->GetReturnType() ){
+
+ case XB_EXP_NUMERIC:
+ nWork->SetResultLen( 4 );
+ break;
+
+ case XB_EXP_CHAR:
+ if( nWork->GetNodeType() != XB_EXP_OPERATOR ){
+ iErrorStop = 190;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ if( nWork->GetChildCnt() < 2 ){
+ iErrorStop = 200;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ nChild1 = nWork->GetChild( 0 );
+ nChild2 = nWork->GetChild( 1 );
+ nWork->SetResultLen( nChild1->GetResultLen() + nChild2->GetResultLen());
+ break;
+
+ case XB_EXP_DATE:
+ nWork->SetResultLen( 8 );
+ break;
+
+ case XB_EXP_LOGICAL:
+ nWork->SetResultLen( 1 );
+ break;
+
+ default:
+ iErrorStop = 210;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ }
+ if( nWork->IsUnaryOperator() ){
+ if( nWork->GetChildCnt() != 1 ){
+ iErrorStop = 220;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ } else if( nWork->IsOperator() && nWork->GetChildCnt() != 2 ){
+ iErrorStop = 230;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ nWork = GetNextNode( nWork );
+ }
+ }
+ catch (xbInt16 iRc ){
+ if( bNewNode && n )
+ delete n;
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::ParseExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+/*************************************************************************/
+//! Parse expression constant.
+/*!
+ \param t - Token.
+ \param n - Node.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbExp::ParseExpressionConstant( xbExpToken &t, xbExpNode *n ){
+
+ xbDate dtWork;
+ n->SetReturnType( t.cReturnType );
+
+ // std::cout << "parse expression constant[" << t.sToken << "]\n";
+
+ switch( t.cReturnType ){
+ case XB_EXP_CHAR:
+ n->SetResultLen( t.sToken.Len() );
+ n->SetResult( t.sToken );
+ break;
+
+ case XB_EXP_DATE:
+ n->SetResultLen( 8 );
+ dtWork.Set( t.sToken );
+ n->SetResult( dtWork );
+ break;
+
+ case XB_EXP_LOGICAL:
+ n->SetResultLen( 1 );
+ if( strncmp( t.sToken, "T", 1 ) == 0 )
+ n->SetResult( (xbBool) xbTrue );
+ else
+ n->SetResult( (xbBool) xbFalse );
+ break;
+
+ case XB_EXP_NUMERIC:
+ n->SetResultLen( 4 );
+ n->SetResult( strtod( t.sToken, 0 ));
+ n->SetResult( t.sToken );
+ break;
+
+ default:
+ return XB_PARSE_ERROR;
+ // break;
+ }
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! Parse expression field.
+/*!
+ \param t - Token.
+ \param n - Node.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbExp::ParseExpressionField( xbExpToken &t, xbExpNode *n ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbDbf * pDbf;
+ xbString sFieldName;
+
+ // do the db lookup and set the field number for the field
+
+ try{
+
+ xbUInt32 lPos;
+
+ if(( lPos = t.sToken.Pos( "->" )) > 0 ){
+ // table name is part of the token
+ xbString sTableName = t.sToken;
+ sTableName.Left( lPos-1 );
+ sFieldName = t.sToken;
+ sFieldName.Mid( lPos + 2, t.sToken.Len() - lPos - 1 );
+ pDbf = (xbDbf *) xbase->GetDbfPtr( sTableName );
+
+
+/*
+ // updated 1/2/23 to support either table.field or table->field
+ if((( lPos = t.sToken.Pos( "->" )) > 0) || (( lPos = t.sToken.Pos( "." )) > 0) ){
+ // table name is part of the token
+ xbString sTableName = t.sToken;
+ sTableName.Left( lPos-1 );
+ sFieldName = t.sToken;
+ if( t.sToken[lPos] == '.' )
+ sFieldName.Mid( lPos + 1, t.sToken.Len() - lPos );
+ else // ->
+ sFieldName.Mid( lPos + 2, t.sToken.Len() - lPos - 1 );
+ pDbf = (xbDbf *) xbase->GetDbfPtr( sTableName );
+*/
+
+ } else {
+ // table name is not part of the token
+ pDbf = dbf;
+ sFieldName = t.sToken;
+ }
+ if( !pDbf ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_FIELD;
+ throw iRc;
+ }
+ xbInt16 iFieldNo = 0;
+
+ if(( iRc = pDbf->GetFieldNo( sFieldName, iFieldNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ char cFieldType;
+ if(( iRc = pDbf->GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ n->SetDbfInfo( pDbf, iFieldNo );
+ switch( cFieldType ){
+ case XB_CHAR_FLD:
+ n->SetReturnType( XB_EXP_CHAR );
+ break;
+
+ case XB_LOGICAL_FLD:
+ n->SetReturnType( XB_EXP_LOGICAL );
+ break;
+
+ case XB_NUMERIC_FLD:
+ case XB_FLOAT_FLD:
+ n->SetReturnType( XB_EXP_NUMERIC );
+ break;
+
+ case XB_DATE_FLD:
+ n->SetReturnType( XB_EXP_DATE );
+ break;
+
+ case XB_MEMO_FLD:
+ default:
+ iErrorStop = 130;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ n->SetNodeText( sFieldName );
+ xbInt16 iResultLen = 0;
+ if(( iRc = pDbf->GetFieldLen( iFieldNo, iResultLen )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ n->SetResultLen( (xbUInt32) iResultLen );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::ParseExpressionField() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+
+/*************************************************************************/
+//! Parse expression function.
+/*!
+ \param t - Token.
+ \param n - Node.
+ \param iWeight
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbExp::ParseExpressionFunction( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ // find the first "("
+ xbUInt32 lPos = t.sToken.Pos( '(' );
+ if( lPos == 0 ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_FUNCTION;
+ throw iRc;
+ }
+ // Get the function name and look it up in the table
+
+
+ xbString sFunc = t.sToken;
+ sFunc.Left( lPos - 1 ).Trim();
+ char cReturnType;
+ xbInt16 i = 0;
+ xbInt32 l = 0;
+ if(( iRc = xbase->GetFunctionInfo( sFunc, cReturnType, i, l )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ n->SetNodeText( sFunc );
+
+
+ // Get the function parms
+ xbString sParms = t.sToken;
+ sParms.Mid( lPos+1, t.sToken.Len() - lPos );
+ lPos = sParms.GetLastPos( ')' );
+ if( lPos == 0 ){
+ iErrorStop = 120;
+ iRc = XB_INVALID_FUNCTION;
+ throw iRc;
+ }
+
+ // remove the trailing ")" paren
+ sParms.Left( lPos - 1 ).Trim();
+
+ // if this function has parms, put them in the tree
+ if( sParms.Len() > 0 ){
+ xbExp enr( xbase, dbf );
+
+ // create a linked list of parms
+ xbLinkList<xbString> llParms;
+ if(( iRc = ParseExpressionFunctionParms( sParms, llParms )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ // for each function parm, recursively process it
+ xbLinkListNode<xbString> * llN = llParms.GetHeadNode();
+ xbString sParm;
+ while( llN ){
+ sParm = llN->GetKey();
+ if(( iRc = enr.ParseExpression( sParm, iWeight + 10 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ n->AddChild( enr.GetTreeHandle());
+ enr.ClearTreeHandle();
+ llN = llN->GetNextNode();
+ }
+ llParms.Clear();
+ }
+
+ if( cReturnType == '1' ){
+ if( n->GetChildCnt() > 0 ){
+ xbExpNode *n1 = n->GetChild( 0 );
+ n->SetReturnType( n1->GetReturnType());
+ }
+
+ } else {
+ n->SetReturnType( cReturnType );
+ }
+
+ if(( iRc = CalcFunctionResultLen( n )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::ParseExpressionFunction() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+
+/*************************************************************************/
+//! Parse expression function.
+/*!
+
+ Creates a linked list of function parms as xbStrings
+ This function pulls out the parms and addresses embedded parens and quotes within the parms
+
+ \param sParms
+ \param lParms
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::ParseExpressionFunctionParms( const xbString &sParms, xbLinkList<xbString> & llParms ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iParenCtr = 0;
+ xbInt16 iInQuotes = 0;
+ xbInt16 iDoubleQuotes = 0;
+ xbInt16 iSingleQuotes = 0;
+ xbInt32 lStartPos = 0;
+ xbInt32 lParmLen = 0;
+
+ xbString sParm;
+
+ try{
+ const char *sp = sParms;
+
+ while( *sp ){
+ if( *sp == '(')
+ iParenCtr++;
+ else if( *sp == ')' )
+ iParenCtr--;
+ else if( !iInQuotes && *sp == '"' ){
+ iInQuotes++;
+ iDoubleQuotes++;
+ } else if( iInQuotes && *sp == '"' ){
+ iInQuotes--;
+ iDoubleQuotes--;
+ }
+ else if( !iInQuotes && *sp == '\'' ){
+ iInQuotes++;
+ iSingleQuotes++;
+ } else if( iInQuotes && *sp == '\'' ){
+ iInQuotes--;
+ iSingleQuotes--;
+
+ } else if( !iInQuotes && !iParenCtr && *sp == ',' ){
+ // found a valid comma - at the end of a parm
+ // add it to the end of the linked list
+ sParm = sParms;
+ sParm.Mid( (xbUInt32) lStartPos + 1, (xbUInt32) lParmLen ).Trim(); // mid is one based
+ llParms.InsertAtEnd( sParm );
+
+ // set the start pos for the next one on the list
+ lStartPos += lParmLen + 1;
+ lParmLen = -1;
+ // lParmLen = 0;
+ }
+ lParmLen++;
+ sp++;
+ }
+ if( lParmLen > 0 ){
+ // get the last parm, it didn't end with a comma
+ sParm = sParms;
+ sParm.Mid( (xbUInt32) lStartPos + 1, (xbUInt32) lParmLen ).Trim();
+ llParms.InsertAtEnd( sParm );
+ }
+
+ }
+ // try / catch not used in this method, structure added for potential future use
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::ParseExpressionFunctionParms() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+/*************************************************************************/
+//! Parse expression operator.
+/*!
+ \param t - Token.
+ \param n - Node.
+ \param iWeight
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbExp::ParseExpressionOperator( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ){
+
+ n->SetResult( t.sToken );
+ n->SetWeight( iWeight + OperatorWeight( t.sToken) );
+
+// std::cout << "ParseExpressionOperator [" << t.cPrevNodeType << "][" << t.sToken << "] Weight = [" << iWeight;
+// std::cout << "] PrevReturnType [" << t.cPrevReturnType;
+// std::cout << "] Operator weight [" << OperatorWeight( t.sToken ) << "] getweight [" << n->GetWeight() << "]\n";
+
+ if( t.sToken == "**" || t.sToken == "^" ||
+ t.sToken == "*" || t.sToken == "/" || t.sToken == "%" || t.sToken == "*=" || t.sToken == "/=" )
+ n->SetReturnType( XB_EXP_NUMERIC );
+
+ else if( t.sToken == "--" || t.sToken == "++" || t.sToken == "+=" || t.sToken == "-=" ) // could be date or numeric
+ n->SetReturnType( XB_EXP_UNKNOWN );
+
+ else if( t.cPrevReturnType == XB_EXP_CHAR && ( t.sToken == "+" || t.sToken == "-" ))
+ n->SetReturnType( XB_EXP_CHAR );
+
+ else if( t.sToken == ".AND." || t.sToken == ".OR." || t.sToken == ".NOT." ||
+ t.sToken == "AND" || t.sToken == "OR" || t.sToken == "NOT" ||
+ t.sToken == ">" || t.sToken == ">=" || t.sToken == "<" ||
+ t.sToken == "<=" || t.sToken == "<>" || t.sToken == "!=" ||
+ t.sToken == "$" || t.sToken == "#" || t.sToken == "=" )
+ n->SetReturnType( XB_EXP_LOGICAL );
+
+
+ else if( t.cPrevReturnType == XB_EXP_UNKNOWN )
+ n->SetReturnType( XB_EXP_UNKNOWN );
+
+ // added for date constant logic 10/28/17
+ else if(( t.sToken == "+" || t.sToken == "-" ) && t.cPrevReturnType == XB_EXP_DATE )
+ n->SetReturnType( XB_EXP_DATE );
+
+ else if( t.sToken == "+" || t.sToken == "-" )
+ n->SetReturnType( XB_EXP_NUMERIC );
+
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+//! ProcessExpression
+/*! This method processes an expression tree leaving the result in the
+ root node of the tree
+*/
+xbInt16 xbExp::ProcessExpression(){
+ return ProcessExpression( 0 );
+}
+/************************************************************************/
+//! ProcessExpression
+/*! This method processes a parsed expression tree leaving the result in the
+ root node of the tree
+ \param iRecBufSw Record buffer to use when evaluating expression.<br>
+ 0 - Current record buffer.<br>
+ 1 - Original record buffer.
+*/
+
+xbInt16 xbExp::ProcessExpression( xbInt16 iRecBufSw ){
+
+// iRecBufSw 0 - Record Buffer
+// 1 - Original Record Buffer
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ xbExpNode * nWork = GetNextNode( NULL );
+ xbExpNode * nChild1;
+ xbDbf * dbf;
+ xbString sWork1;
+ xbString sWork2;
+ xbString sOperator;
+ xbDate dtWork1;
+
+ xbBool bWork;
+ xbDouble dWork;
+
+ while( nWork ){
+ switch( nWork->GetNodeType() ){
+
+ case XB_EXP_CONSTANT:
+ break;
+
+ case XB_EXP_PRE_OPERATOR: // increment value before setting in head node
+ if( nWork->GetChildCnt() != 1 ){
+ iErrorStop = 100;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ nChild1 = nWork->GetChild( 0 );
+ //if( nChild1->GetReturnType() == XB_EXP_DATE )
+ // nChild1->SetResult( (xbDouble) nChild1->GetDateResult().JulianDays());
+
+ nWork->GetNodeText( sWork1 );
+ if( sWork1 == "++" )
+ nChild1->SetResult( nChild1->GetNumericResult() + 1 );
+ else
+ nChild1->SetResult( nChild1->GetNumericResult() - 1 );
+
+ nWork->SetResult( nChild1->GetNumericResult());
+
+ //if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ // dtWork1.JulToDate8( (xbInt32) nChild1->GetNumericResult());
+ // nChild1->SetResult( dtWork1 );
+ // nWork->SetResult( dtWork1 );
+ // }
+ break;
+
+ case XB_EXP_POST_OPERATOR: // increment value after setting in head node
+ if( nWork->GetChildCnt() != 1 ){
+ iErrorStop = 110;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ nChild1 = nWork->GetChild( 0 );
+ //if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ // nWork->SetResult( nChild1->GetDateResult());
+ // nChild1->SetResult( (xbDouble) nChild1->GetDateResult().JulianDays());
+ //}
+ //else
+ nWork->SetResult( nChild1->GetNumericResult());
+
+ nWork->GetNodeText( sWork1 );
+ if( sWork1 == "++" )
+ nChild1->SetResult( nChild1->GetNumericResult() + 1 );
+ else
+ nChild1->SetResult( nChild1->GetNumericResult() - 1 );
+
+ //if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ // dtWork1.JulToDate8( (xbInt32) nChild1->GetNumericResult());
+ // nChild1->SetResult( dtWork1 );
+ // }
+ break;
+
+ case XB_EXP_FIELD:
+
+ if(( dbf = nWork->GetDbf()) == NULL ){
+ iErrorStop = 120;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ switch( nWork->GetReturnType()){
+ case XB_EXP_CHAR:
+ if(( iRc = dbf->GetField( nWork->GetFieldNo(), sWork1, iRecBufSw )) < XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ nWork->SetResult( sWork1 );
+ break;
+
+ case XB_EXP_DATE:
+
+ if(( iRc = dbf->GetField( nWork->GetFieldNo(), sWork1, iRecBufSw )) < XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if( sWork1 == " " ){
+ // std::cout << "xbExp::ProcessExpression() line 1938 sWork is spaces\n";
+ //nWork->SetResult( (xbDouble) 21474835648 ); // dbase sets a date value in both ndx and mdx index files to this if spaces on dbf record
+ nWork->SetResult( (xbDouble) XB_NULL_DATE );
+ } else {
+ dtWork1.Set( sWork1 );
+ nWork->SetResult( (xbDouble) dtWork1.JulianDays() );
+ }
+ break;
+
+ case XB_EXP_LOGICAL:
+ if(( iRc = dbf->GetLogicalField( nWork->GetFieldNo(), bWork, iRecBufSw )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ nWork->SetResult( bWork );
+ break;
+
+ case XB_EXP_NUMERIC:
+ if(( iRc = dbf->GetDoubleField( nWork->GetFieldNo(), dWork, iRecBufSw )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ nWork->SetResult( dWork );
+ break;
+
+ default:
+ iErrorStop = 170;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ break;
+
+
+ case XB_EXP_OPERATOR:
+ if(( iRc = ProcessExpressionOperator( nWork )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ break;
+
+ case XB_EXP_FUNCTION:
+ if(( iRc = ProcessExpressionFunction( nWork, iRecBufSw )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ break;
+
+ default:
+ iErrorStop = 200;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ nWork = GetNextNode( nWork );
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::ProcessExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+/*************************************************************************/
+//! ProcessExpression
+/*! This method processes an expression tree for a given node.
+*/
+
+xbInt16 xbExp::ProcessExpressionFunction( xbExpNode * n, xbInt16 iRecBufSw ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ xbString sFunction;
+ xbString sResult;
+ xbDouble dResult;
+ xbDate dtResult;
+ xbBool bResult;
+
+ n->GetNodeText( sFunction );
+
+ // process functions with no children first
+ xbExpNode * nChild1;
+ if( n->GetChildCnt() == 0 ){
+ if( sFunction == "DATE" ){
+ if(( iRc = xbase->DATE( dtResult )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ n->SetResult( dtResult );
+ } else if( sFunction == "DEL" ){
+ if(( iRc = xbase->DEL( dbf, sResult, iRecBufSw )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "DELETED" ){
+ if(( iRc = xbase->DELETED( dbf, bResult, iRecBufSw )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ n->SetResult( bResult );
+ } else if( sFunction == "RECCOUNT" ){
+ if(( iRc = xbase->RECCOUNT( dbf, dResult )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "RECNO" ){
+ if(( iRc = xbase->RECNO( dbf, dResult )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ }
+ // process functions with one child
+ } else if( n->GetChildCnt() == 1 ){
+
+ nChild1 = n->GetChild( 0 );
+
+ if( sFunction == "ABS" ){
+ if(( iRc = xbase->ABS( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "ALLTRIM" ){
+ if(( iRc = xbase->ALLTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "ASC" ){
+ if(( iRc = xbase->ASC( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "CDOW" ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->CDOW( d, sResult )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "CHR" ){
+ if(( iRc = xbase->CHR( nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "CMONTH" ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->CMONTH( d, sResult )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "CTOD" ){
+ if(( iRc = xbase->CTOD( nChild1->GetStringResult(), dtResult )) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+ n->SetResult( dtResult );
+ } else if( sFunction == "DAY" ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->DAY( d, dResult )) != XB_NO_ERROR ){
+ iErrorStop = 270;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "DESCEND" ){
+
+ if( n->GetReturnType() == XB_EXP_CHAR ){
+ if(( iRc = xbase->DESCEND( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 280;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+
+ } else if( n->GetReturnType() == XB_EXP_DATE ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->DESCEND( d, dtResult )) != XB_NO_ERROR ){
+ iErrorStop = 290;
+ throw iRc;
+ }
+ n->SetResult( dtResult );
+
+ } else if( n->GetReturnType() == XB_EXP_NUMERIC ){
+ if(( iRc = xbase->DESCEND( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+
+ } else {
+ iErrorStop = 310;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+
+ } else if( sFunction == "DOW" ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->DOW( d, dResult )) != XB_NO_ERROR ){
+ iErrorStop = 320;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "DTOC" ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->DTOC( d, sResult )) != XB_NO_ERROR ){
+ iErrorStop = 330;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "DTOS" ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->DTOS( d, sResult )) != XB_NO_ERROR ){
+ iErrorStop = 340;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "EXP" ){
+ if(( iRc = xbase->EXP( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 350;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "INT" ){
+ if(( iRc = xbase->INT( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 360;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "ISALPHA" ){
+ if(( iRc = xbase->ISALPHA( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){
+ iErrorStop = 370;
+ throw iRc;
+ }
+ n->SetResult( bResult );
+ } else if( sFunction == "ISLOWER" ){
+ if(( iRc = xbase->ISLOWER( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){
+ iErrorStop = 380;
+ throw iRc;
+ }
+ n->SetResult( bResult );
+ } else if( sFunction == "ISUPPER" ){
+ if(( iRc = xbase->ISUPPER( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){
+ iErrorStop = 390;
+ throw iRc;
+ }
+ n->SetResult( bResult );
+ } else if( sFunction == "LEN" ){
+ if(( iRc = xbase->LEN( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 400;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "LOG" ){
+ if(( iRc = xbase->LOG( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 410;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "LTRIM" ){
+ if(( iRc = xbase->LTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 420;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "LOWER" ){
+ if(( iRc = xbase->LOWER( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 430;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "MONTH" ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->MONTH( d, dResult )) != XB_NO_ERROR ){
+ iErrorStop = 440;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "RTRIM" ){
+ if(( iRc = xbase->RTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 450;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "STOD" ){
+ if(( iRc = xbase->STOD( nChild1->GetStringResult(), dtResult )) != XB_NO_ERROR ){
+ iErrorStop = 460;
+ throw iRc;
+ }
+ n->SetResult( dtResult );
+ } else if( sFunction == "SPACE" ){
+ if(( iRc = xbase->SPACE( (xbInt32) nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 470;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "SQRT" ){
+ if(( iRc = xbase->SQRT( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 480;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "STR" ){
+ if(( iRc = xbase->STR( nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 490;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "TRIM" ){
+ if(( iRc = xbase->TRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 500;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "UPPER" ){
+ if(( iRc = xbase->UPPER( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 510;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "VAL" ){
+ if(( iRc = xbase->VAL( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 520;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "YEAR" ){
+ xbDate d( (xbInt32) nChild1->GetNumericResult());
+ if(( iRc = xbase->YEAR( d, dResult )) != XB_NO_ERROR ){
+ iErrorStop = 530;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else {
+ iErrorStop = 540;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ } else if( n->GetChildCnt() == 2 ){
+ xbExpNode * nChild2;
+ nChild1 = n->GetChild( 0 );
+ nChild2 = n->GetChild( 1 );
+
+ if( sFunction == "AT" ){
+ if(( iRc = xbase->AT( nChild1->GetStringResult(), nChild2->GetStringResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 700;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "LEFT" ){
+ if(( iRc = xbase->LEFT( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 710;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "MAX" ){
+ if(( iRc = xbase->MAX( nChild1->GetNumericResult(), nChild2->GetNumericResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 720;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ } else if( sFunction == "MIN" ){
+ if(( iRc = xbase->MIN( nChild1->GetNumericResult(), nChild2->GetNumericResult(), dResult )) != XB_NO_ERROR ){
+ iErrorStop = 730;
+ throw iRc;
+ }
+ n->SetResult( dResult );
+ }
+ else if( sFunction == "REPLICATE" ){
+ if(( iRc = xbase->REPLICATE( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 800;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "RIGHT" ){
+ if(( iRc = xbase->RIGHT( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 810;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "STR" ){
+ if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 820;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else {
+ iErrorStop = 830;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ } else if( n->GetChildCnt() == 3 ){
+ xbExpNode * nChild2;
+ xbExpNode * nChild3;
+ nChild1 = n->GetChild( 0 );
+ nChild2 = n->GetChild( 1 );
+ nChild3 = n->GetChild( 2 );
+
+ if( sFunction == "IIF" ){
+ if(( iRc = xbase->IIF( nChild1->GetBoolResult(), nChild2->GetStringResult(), nChild3->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 900;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "STR" ){
+ if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 910;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "STRZERO" ){
+ if(( iRc = xbase->STRZERO( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 920;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else if( sFunction == "SUBSTR" ){
+ if(( iRc = xbase->SUBSTR( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 930;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else {
+ iErrorStop = 950;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ } else if( n->GetChildCnt() == 4 ){
+ xbExpNode * nChild2;
+ xbExpNode * nChild3;
+ xbExpNode * nChild4;
+ nChild1 = n->GetChild( 0 );
+ nChild2 = n->GetChild( 1 );
+ nChild3 = n->GetChild( 2 );
+ nChild4 = n->GetChild( 3 );
+
+ if( sFunction == "STR" ){
+ if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(),
+ (xbUInt32) nChild3->GetNumericResult(), nChild4->GetStringResult(), sResult )) != XB_NO_ERROR ){
+ iErrorStop = 1000;
+ throw iRc;
+ }
+ n->SetResult( sResult );
+ } else {
+ iErrorStop = 1010;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+
+ } else {
+ iErrorStop = 2000;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::ProcessExpressionFunction() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+/*************************************************************************/
+//! Process Expression Operator
+/*! This method processes an expression operator for a given node.
+*/
+
+xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbExpNode * nChild1 = NULL;
+ xbExpNode * nChild2 = NULL;
+ xbString sOperator;
+ xbString sWork1;
+ xbString sWork2;
+ xbDate dtWork1;
+
+ xbString sMsg;
+
+ try{
+ n->GetNodeText( sOperator );
+ nChild1 = n->GetChild( 0 );
+ if( !n->IsUnaryOperator())
+ nChild2 = n->GetChild( 1 );
+
+ switch( n->GetReturnType()){
+ case XB_EXP_CHAR:
+ if( sOperator == "+" ){
+ sWork1 = nChild1->GetStringResult();
+ sWork1 += nChild2->GetStringResult();
+ n->SetResult( sWork1 );
+ } else if( sOperator == "-" ){
+ sWork1 = nChild1->GetStringResult();
+ sWork1.Rtrim();
+ sWork1 += nChild2->GetStringResult();
+ sWork1.PadRight( ' ', n->GetResultLen());
+ n->SetResult( sWork1 );
+ } else {
+ iErrorStop = 100;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ break;
+
+ case XB_EXP_NUMERIC:
+ if( sOperator == "+" )
+ n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult());
+ else if( sOperator == "-" ){
+ n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult());
+
+ }
+ else if( sOperator == "*" )
+ n->SetResult( nChild1->GetNumericResult() * nChild2->GetNumericResult());
+ else if( sOperator == "/" )
+ n->SetResult( nChild1->GetNumericResult() / nChild2->GetNumericResult());
+ else if( sOperator == "^" || sOperator == "**" )
+ n->SetResult( pow( nChild1->GetNumericResult(), nChild2->GetNumericResult()));
+ else if( sOperator == "+=" ){
+ n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult());
+ nChild1->SetResult( n->GetNumericResult() );
+ }
+ else if( sOperator == "-=" ){
+ n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult());
+ nChild1->SetResult( n->GetNumericResult() );
+ }
+ else if( sOperator == "*=" ){
+ n->SetResult( nChild1->GetNumericResult() * nChild2->GetNumericResult());
+ nChild1->SetResult( n->GetNumericResult() );
+ }
+ else if( sOperator == "/=" ){
+ n->SetResult( nChild1->GetNumericResult() / nChild2->GetNumericResult());
+ nChild1->SetResult( n->GetNumericResult() );
+ } else {
+ iErrorStop = 200;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ break;
+
+
+ case XB_EXP_DATE:
+ // if date values in the leaf nodes, convert to numeric for operator logic
+
+ if( sOperator == "+" )
+ n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult());
+ else if( sOperator == "-" ){
+ n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult());
+ xbDate d( (xbInt32) n->GetNumericResult());
+ }
+ else if( sOperator == "+=" ){
+ n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult());
+ nChild1->SetResult( n->GetNumericResult() );
+ }
+ else if( sOperator == "-=" ){
+ n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult());
+ nChild1->SetResult( n->GetNumericResult() );
+ } else {
+ iErrorStop = 300;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ break;
+
+ case XB_EXP_LOGICAL:
+
+ if( !n->IsUnaryOperator() && (nChild1->GetReturnType() != nChild2->GetReturnType())){
+ iErrorStop = 400;
+ iRc = XB_INCOMPATIBLE_OPERANDS;
+ throw iRc;
+ }
+
+ if( sOperator == ".AND." || sOperator == "AND" )
+ n->SetResult((xbBool) (nChild1->GetBoolResult() && nChild2->GetBoolResult()) );
+
+ else if( sOperator == ".OR." || sOperator == "OR" )
+ n->SetResult((xbBool) (nChild1->GetBoolResult() || nChild2->GetBoolResult()) );
+
+ else if( sOperator == ".NOT." || sOperator == "NOT" ){
+ if( nChild1->GetBoolResult())
+ n->SetResult((xbBool) xbFalse );
+ else
+ n->SetResult((xbBool) xbTrue );
+ }
+
+ else if( sOperator == ">" ){
+
+ if( nChild1->GetReturnType() == XB_EXP_CHAR )
+ n->SetResult((xbBool)(nChild1->GetStringResult() > nChild2->GetStringResult()));
+
+ else if( nChild1->GetReturnType() == XB_EXP_NUMERIC )
+ n->SetResult((xbBool)(nChild1->GetNumericResult() > nChild2->GetNumericResult()));
+
+ else if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d1 = nChild1->GetNumericResult();
+ xbDouble d2 = nChild2->GetNumericResult();
+ if( d1 == XB_NULL_DATE ) d1 = 0;
+ if( d2 == XB_NULL_DATE ) d2 = 0;
+ n->SetResult((xbBool)( d1 > d2));
+ // n->SetResult((xbBool)(nChild1->GetNumericResult() > nChild2->GetNumericResult()));
+
+ } else {
+ iErrorStop = 410;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+
+ else if( sOperator == ">=" ){
+ if( nChild1->GetReturnType() == XB_EXP_CHAR )
+ n->SetResult((xbBool)(nChild1->GetStringResult() >= nChild2->GetStringResult()));
+
+ else if( nChild1->GetReturnType() == XB_EXP_NUMERIC )
+ n->SetResult((xbBool)(nChild1->GetNumericResult() >= nChild2->GetNumericResult()));
+
+ else if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d1 = nChild1->GetNumericResult();
+ xbDouble d2 = nChild2->GetNumericResult();
+ if( d1 == XB_NULL_DATE ) d1 = 0;
+ if( d2 == XB_NULL_DATE ) d2 = 0;
+ n->SetResult((xbBool)( d1 >= d2));
+ //n->SetResult((xbBool)(nChild1->GetNumericResult() >= nChild2->GetNumericResult()));
+
+ } else {
+ iErrorStop = 420;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+
+ else if( sOperator == "<" ){
+
+ if( nChild1->GetReturnType() == XB_EXP_CHAR ){
+ n->SetResult((xbBool)( nChild1->GetStringResult() < nChild2->GetStringResult()));
+
+ } else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ){
+ n->SetResult((xbBool)( nChild1->GetNumericResult() < nChild2->GetNumericResult()));
+
+ } else if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d1 = nChild1->GetNumericResult();
+ xbDouble d2 = nChild2->GetNumericResult();
+ if( d1 == XB_NULL_DATE ) d1 = 0;
+ if( d2 == XB_NULL_DATE ) d2 = 0;
+
+ n->SetResult((xbBool)( d1 < d2));
+
+ // std::cout << "xbexp() line 2567 [" << nChild1->GetNumericResult() << "][" << nChild2->GetNumericResult() << "]\n";
+
+ } else {
+ iErrorStop = 430;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+
+ else if( sOperator == "<=" ){
+
+ if( nChild1->GetReturnType() == XB_EXP_CHAR )
+ n->SetResult((xbBool)( nChild1->GetStringResult() <= nChild2->GetStringResult()));
+
+ else if( nChild1->GetReturnType() == XB_EXP_NUMERIC )
+ n->SetResult((xbBool)( nChild1->GetNumericResult() <= nChild2->GetNumericResult()));
+
+ else if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d1 = nChild1->GetNumericResult();
+ xbDouble d2 = nChild2->GetNumericResult();
+ if( d1 == XB_NULL_DATE ) d1 = 0;
+ if( d2 == XB_NULL_DATE ) d2 = 0;
+ n->SetResult((xbBool)( d1 <= d2));
+ // n->SetResult((xbBool)( nChild1->GetNumericResult() <= nChild2->GetNumericResult()));
+
+ } else {
+ iErrorStop = 440;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+
+ else if( sOperator == "<>" || sOperator == "#" || sOperator == "!=" ){
+
+ if( nChild1->GetReturnType() == XB_EXP_CHAR )
+ n->SetResult((xbBool)( nChild1->GetStringResult() != nChild2->GetStringResult()));
+
+ else if( nChild1->GetReturnType() == XB_EXP_NUMERIC )
+ n->SetResult((xbBool)( nChild1->GetNumericResult() != nChild2->GetNumericResult()));
+
+ else if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d1 = nChild1->GetNumericResult();
+ xbDouble d2 = nChild2->GetNumericResult();
+ if( d1 == XB_NULL_DATE ) d1 = 0;
+ if( d2 == XB_NULL_DATE ) d2 = 0;
+ n->SetResult((xbBool)( d1 != d2));
+ // n->SetResult((xbBool)( nChild1->GetNumericResult() != nChild2->GetNumericResult()));
+
+ } else {
+ iErrorStop = 450;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ }
+
+ else if( sOperator == "$" ){
+ if( nChild1->GetReturnType() == XB_EXP_CHAR )
+ if( nChild2->GetStringResult().Pos( nChild1->GetStringResult()) > 0 )
+ n->SetResult((xbBool) xbTrue );
+ else
+ n->SetResult((xbBool) xbFalse );
+ else {
+ iErrorStop = 460;
+ iRc = XB_INCOMPATIBLE_OPERANDS;
+ throw iRc;
+ }
+ }
+
+ else if( sOperator == "=" ){
+
+ if( nChild1->GetReturnType() == XB_EXP_CHAR ){
+ xbString sChld1 = nChild1->GetStringResult();
+ xbString sChld2 = nChild2->GetStringResult();
+ sChld1.Rtrim();
+ sChld2.Rtrim();
+ n->SetResult((xbBool)( sChld1 == sChld2 ));
+
+ } else if( nChild1->GetReturnType() == XB_EXP_NUMERIC ){
+ n->SetResult((xbBool)( nChild1->GetNumericResult() == nChild2->GetNumericResult()));
+
+ } else if( nChild1->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d1 = nChild1->GetNumericResult();
+ xbDouble d2 = nChild2->GetNumericResult();
+ if( d1 == XB_NULL_DATE ) d1 = 0;
+ if( d2 == XB_NULL_DATE ) d2 = 0;
+ n->SetResult((xbBool)( d1 == d2));
+ // n->SetResult((xbBool)( nChild1->GetNumericResult() == nChild2->GetNumericResult()));
+
+ } else {
+ iErrorStop = 470;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+
+
+ } else {
+ iErrorStop = 500;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+
+ break;
+
+ default:
+ iErrorStop = 600;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ // break;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbexp::ProcessExpressionOperator() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ }
+ return iRc;
+}
+
+/*************************************************************************/
+}; // namespace
+#endif // XB_EXPRESSION_SUPPORT
+/*************************************************************************/
diff --git a/src/core/xbexpnode.cpp b/src/core/xbexpnode.cpp
new file mode 100755
index 0000000..d11e8cc
--- /dev/null
+++ b/src/core/xbexpnode.cpp
@@ -0,0 +1,562 @@
+/* xbexpnode.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2017,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under
+the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_EXPRESSION_SUPPORT
+
+namespace xb{
+/************************************************************************/
+//! @brief Constructor
+xbExpNode::xbExpNode(){
+ sNodeText = "";
+ cReturnType = 0;
+ cNodeType = 0;
+ dResult = 0;
+ iFieldNo = 0;
+ ulResultLen = 0;
+ iWeight = 0;
+ nParent = NULL;
+ dbf = NULL;
+}
+
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param sNodeText Node text.
+ \param cReturnType Return type.
+ \param cNodeType Node type.
+*/
+
+xbExpNode::xbExpNode( xbString &sNodeText, char cReturnType, char cNodeType ){
+ this->sNodeText = sNodeText;
+ this->cReturnType = cReturnType;
+ this->cNodeType = cNodeType;
+ dResult = 0;
+ iFieldNo = 0;
+ ulResultLen = 0;
+ iWeight = 0;
+ nParent = NULL;
+ dbf = NULL;
+}
+
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param sNodeText Node text.
+ \param cNodeType Node type.
+*/
+xbExpNode::xbExpNode( xbString &sNodeText, char cNodeType ){
+ this->sNodeText = sNodeText;
+ this->cReturnType = 0x00;
+ this->cNodeType = cNodeType;
+ dResult = 0;
+ iFieldNo = 0;
+ ulResultLen = 0;
+ iWeight = 0;
+ nParent = NULL;
+ dbf = NULL;
+}
+
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param cNodeType Node type.
+*/
+xbExpNode::xbExpNode( char cNodeType ){
+ this->cReturnType = 0x00;
+ this->cNodeType = cNodeType;
+ dResult = 0;
+ iFieldNo = 0;
+ ulResultLen = 0;
+ iWeight = 0;
+ nParent = NULL;
+ dbf = NULL;
+}
+
+/************************************************************************/
+//! @brief Deconstructor
+xbExpNode::~xbExpNode(){
+
+ xbExpNode * n;
+ while( llChildren.GetNodeCnt() > 0 ){
+ llChildren.RemoveFromFront( n );
+ delete n;
+ }
+}
+
+/************************************************************************/
+//! @brief Add child node to linked list.
+/*!
+ \param n Pointer to node to add to linked list.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbExpNode::AddChild( xbExpNode *n ){
+ n->SetParent( this );
+ return llChildren.InsertAtEnd( n );
+}
+
+/************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+
+//! @brief Dump Node.
+/*!
+ \param iOption xbTrue - Print child info if they exist.<bt>
+ xbFalse - Don't print child info.
+ \returns void.
+*/
+void xbExpNode::DumpNode( xbInt16 iOption ) const {
+ xbString sMsg;
+ std::cout << "Me=[" << this << "] ";
+ if( nParent )
+ std::cout << "Par=[" << nParent << "] ";
+
+ std::cout << "nTyp=[" << cNodeType << "] ";
+ std::cout << "dTyp=[" << cReturnType << "] ";
+ if( iWeight != 0 )
+ std::cout << "W=[" << iWeight << "] ";
+
+ if( cNodeType == XB_EXP_FIELD )
+ std::cout << "FieldNo=[" << iFieldNo << "] ";
+
+ std::cout << "Txt=[" << sNodeText << "] ";
+ if( sResult != "" )
+ std::cout << "sVal=[" << sResult << "] ";
+
+ if( ulResultLen > 0 )
+ std::cout << "Len=[" << ulResultLen << "] ";
+ if( cReturnType == XB_EXP_DATE ){
+ xbDate d( (xbInt32) dResult );
+ std::cout << "dtVal=[" << d.Str() << "] ";
+ }
+
+ if( cReturnType == XB_EXP_DATE || cReturnType == XB_EXP_NUMERIC ){
+ sMsg.Sprintf( "dVal=[%f]\n", dResult );
+ std::cout << sMsg.Str();
+ }
+
+ if( cReturnType == XB_EXP_LOGICAL ){
+ sMsg.Sprintf( "lVal=[%d]\n", (xbInt32) dResult );
+ std::cout << sMsg.Str();
+ }
+
+ if( iOption ){
+ xbLinkListNode<xbExpNode *> *lln = llChildren.GetHeadNode();
+ xbExpNode *n;
+ if( lln ){
+ std::cout << " Children: ";
+ while( lln ){
+ n = lln->GetKey();
+ std::cout << " [" << n << "]";
+ lln = lln->GetNextNode();
+ }
+ std::cout << std::endl;
+
+ lln = llChildren.GetHeadNode();
+ while( lln ){
+ n = lln->GetKey();
+ n->DumpNode( iOption );
+ lln = lln->GetNextNode();
+ }
+ }
+ }
+
+ std::cout << std::endl;
+}
+#endif
+
+/************************************************************************/
+//! @brief Get boolean result.
+/*!
+ \returns Boolean result.
+*/
+
+xbBool xbExpNode::GetBoolResult() const {
+ return (xbBool) dResult;
+}
+
+/************************************************************************/
+//! @brief Get pointer to child.
+/*!
+ \param ulChildNo - Which child? 1,2 or3
+ \returns Pointer to child node or null if none exists.
+*/
+xbExpNode *xbExpNode::GetChild( xbUInt32 ulChildNo ) const {
+
+ xbLinkListNode<xbExpNode *> *lln = llChildren.GetNodeForNo( ulChildNo );
+ if( lln )
+ return lln->GetKey();
+ else
+ return 0x00;
+}
+/************************************************************************/
+//! @brief Get child count.
+/*!
+ \returns Child count.
+*/
+xbUInt32 xbExpNode::GetChildCnt() const{
+ return llChildren.GetNodeCnt();
+}
+
+/************************************************************************/
+//! @brief Get the current child number for this node.
+/*!
+ \returns Child number: 1, 2 or 3.
+*/
+
+xbUInt32 xbExpNode::GetChildNo() const {
+
+ if( !nParent )
+ return 0;
+
+ for( xbUInt32 i = 0; i < nParent->GetChildCnt(); i++ ){
+ if( this == nParent->GetChild( i )){
+ // std::cout << "get child no [" << this << "][" << nParent->GetChild(i) << "]\n";
+ return i;
+ }
+ }
+ return 0;
+}
+
+/************************************************************************/
+//! @brief Get the dbf pointer.
+/*!
+ \returns Pointer to dbf.
+*/
+xbDbf *xbExpNode::GetDbf() const {
+ return dbf;
+}
+/************************************************************************/
+//! @brief Get the field number.
+/*!
+ \returns Field number for field node.
+*/
+
+xbInt16 xbExpNode::GetFieldNo() const {
+ return iFieldNo;
+}
+
+/*************************************************************************/
+//! @brief Get the first node.
+/*!
+ \returns Pointer to left most child node or *this if childless.
+*/
+
+xbExpNode *xbExpNode::GetFirstNode() {
+ xbExpNode *n = this;
+ while( n && n->GetChildCnt() > 0 )
+ n = n->GetChild(0);
+ return n;
+}
+
+/*************************************************************************/
+//! @brief Get the next node.
+/*!
+ \returns Pointer to right node or parent if right node does not exist.
+*/
+
+xbExpNode *xbExpNode::GetNextNode() const {
+
+ if( HasRightSibling())
+ return GetRightSibling()->GetFirstNode();
+ else
+ return nParent;
+}
+
+/************************************************************************/
+//! @brief Get the node text.
+/*!
+ \param sOutText Output string containing node text.
+ \returns void
+*/
+
+void xbExpNode::GetNodeText( xbString &sOutText ) const{
+ sOutText = sNodeText;
+}
+
+/************************************************************************/
+//! @brief Get the node type.
+/*!
+ \returns Node type.
+*/
+
+char xbExpNode::GetNodeType() const{
+ return cNodeType;
+}
+
+/************************************************************************/
+//! @brief Get numeric result.
+/*!
+ \returns Numeric result.
+*/
+
+xbDouble xbExpNode::GetNumericResult() const {
+ return dResult;
+}
+
+/************************************************************************/
+//! @brief Get parent.
+/*!
+ \returns Pointer to parent node.
+*/
+
+xbExpNode *xbExpNode::GetParent() const{
+ return nParent;
+}
+
+/************************************************************************/
+//! @brief Get result length.
+/*!
+ \returns Result length.
+*/
+xbUInt32 xbExpNode::GetResultLen() const{
+ return ulResultLen;
+}
+/************************************************************************/
+//! @brief Get result type.
+/*!
+ \returns Result type.
+*/
+char xbExpNode::GetReturnType() const{
+ return cReturnType;
+}
+
+/*************************************************************************/
+//! @brief Get right sibling.
+/*!
+ \returns Pointer to right sibling.
+*/
+
+xbExpNode *xbExpNode::GetRightSibling() const {
+
+ xbExpNode * nParent;
+ if(( nParent = GetParent()) == NULL )
+ return NULL;
+
+ if( nParent->GetChildCnt() <= 0 )
+ return NULL;
+
+ xbUInt32 ulChildNo = GetChildNo();
+
+ if( ulChildNo < (nParent->GetChildCnt() - 1))
+ return nParent->GetChild( ulChildNo + 1 );
+ else
+ return NULL;
+}
+/************************************************************************/
+//! @brief Get string result.
+/*!
+ \returns String result.
+*/
+xbString &xbExpNode::GetStringResult(){
+ return sResult;
+}
+/************************************************************************/
+//! @brief Get node weight.
+/*!
+ Each node is assigned a weight used internally to detmerine processing sequence.
+ \returns Node weight.
+*/
+
+xbInt16 xbExpNode::GetWeight() const {
+ return iWeight;
+}
+
+/*************************************************************************/
+//! @brief Determine if node has a right sibling.
+/*!
+ \returns xbTrue - Node has right sibling.<br>
+ xbFalse - Node has no right sibling.
+*/
+
+xbBool xbExpNode::HasRightSibling() const {
+
+ // std::cout << "in HasRightSibling [" << sNodeText << "]\n";
+
+ if( nParent == NULL )
+ return xbFalse;
+
+ xbUInt32 ulChildNo = GetChildNo();
+
+ if( ulChildNo < (nParent->GetChildCnt() - 1)){
+ // std::cout << "Has Right Sibling = " << iChildNo << "] of [" << nParent->GetChildCnt() << "]\n";
+ return xbTrue;
+}
+ else
+ return xbFalse;
+}
+
+
+/*************************************************************************/
+//! @brief Determine if node is an operator.
+/*!
+ \returns xbTrue - Node is an operator.<br>
+ xbFalse - Node is not an operator.
+*/
+
+xbBool xbExpNode::IsOperator() const {
+
+ if( cNodeType == XB_EXP_OPERATOR )
+ return xbTrue;
+ else
+ return xbFalse;
+}
+
+/*************************************************************************/
+//! @brief Determine if node is a unary operator.
+/*!
+ \returns xbTrue - Node is a unary operator.<br>
+ xbFalse - Node is not a unary operator.
+*/
+xbBool xbExpNode::IsUnaryOperator() const {
+
+ if( cNodeType == XB_EXP_PRE_OPERATOR || cNodeType == XB_EXP_POST_OPERATOR )
+ return xbTrue;
+ else if( cNodeType == XB_EXP_OPERATOR && (sNodeText == ".NOT." || sNodeText == "NOT" ))
+ return xbTrue;
+ else
+ return xbFalse;
+}
+/************************************************************************/
+//! @brief Remove last child from node.
+/*!
+ \returns void.
+*/
+
+void xbExpNode::RemoveLastChild(){
+ xbExpNode *n;
+ llChildren.RemoveFromEnd( n );
+}
+
+/************************************************************************/
+//! @brief Set dbf info on node.
+/*!
+ \param dbf Pointer to dbf.
+ \param iFieldNo Field number of field.
+ \returns void.
+*/
+void xbExpNode::SetDbfInfo( xbDbf *dbf, xbInt16 iFieldNo ){
+ this->dbf = dbf;
+ this->iFieldNo = iFieldNo;
+}
+
+/************************************************************************/
+//! @brief Set dbf info on node.
+/*!
+ \param dbf Pointer to dbf.
+ \returns void.
+*/
+void xbExpNode::SetDbfInfo( xbDbf *dbf ){
+ this->dbf = dbf;
+}
+/************************************************************************/
+//! @brief Set node type.
+/*!
+ \param cNodeType Node type.
+ \returns void.
+*/
+void xbExpNode::SetNodeType( char cNodeType ){
+ this->cNodeType = cNodeType;
+}
+
+/************************************************************************/
+//! @brief Set node text.
+/*!
+ \param sNodeText Node text.
+ \returns void.
+*/
+void xbExpNode::SetNodeText( xbString & sNodeText ){
+ this->sNodeText = sNodeText;
+}
+
+/************************************************************************/
+//! @brief Set parent.
+/*!
+ \param n Pointer to parent.
+ \returns void.
+*/
+void xbExpNode::SetParent( xbExpNode *n ){
+ this->nParent = n;
+}
+
+/************************************************************************/
+//! @brief Set date result.
+/*!
+ \param dtResult Date result.
+ \returns void.
+*/
+void xbExpNode::SetResult( xbDate &dtResult ){
+ this->dResult = dtResult.JulianDays();
+}
+
+/************************************************************************/
+//! @brief Set boolean result.
+/*!
+ \param bResult Boolean result.
+ \returns void.
+*/
+void xbExpNode::SetResult( xbBool bResult ){
+ this->dResult = bResult;
+}
+/************************************************************************/
+//! @brief Set numeric result.
+/*!
+ \param dResult Double numeric result.
+ \returns void.
+*/
+void xbExpNode::SetResult( xbDouble dResult ){
+ this->dResult = dResult;
+}
+/************************************************************************/
+//! @brief Set string result.
+/*!
+ \param sResult String result.
+ \returns void.
+*/
+void xbExpNode::SetResult( xbString &sResult ){
+ this->sResult = sResult;
+}
+/************************************************************************/
+//! @brief Set result length.
+/*!
+ \param ulResultLen Set result length.
+ \returns void.
+*/
+void xbExpNode::SetResultLen( xbUInt32 ulResultLen ){
+ this->ulResultLen = ulResultLen;
+}
+/************************************************************************/
+//! @brief Set return type.
+/*!
+ \param cReturnType Return Type.
+ \returns void.
+*/
+void xbExpNode::SetReturnType( char cReturnType ){
+ this->cReturnType = cReturnType;
+}
+/************************************************************************/
+//! @brief Set weight.
+/*!
+ \param iWeight Weight to set this node at.
+ \returns void.
+*/
+void xbExpNode::SetWeight( xbInt16 iWeight ){
+ this->iWeight = iWeight;
+}
+/*************************************************************************/
+}; // namespace
+#endif // XB_EXPRESSION_SUPPORT
+/*************************************************************************/
diff --git a/src/core/xbfields.cpp b/src/core/xbfields.cpp
new file mode 100755
index 0000000..85ac145
--- /dev/null
+++ b/src/core/xbfields.cpp
@@ -0,0 +1,1189 @@
+/* xbfields.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+namespace xb{
+
+/************************************************************************/
+//! @brief Get xbDouble field for field name.
+/*!
+ \param sFieldName Name of field to retrieve.
+ \param dFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetDoubleField( const xbString &sFieldName, xbDouble &dFieldValue ) const {
+ return GetDoubleField( GetFieldNo( sFieldName ), dFieldValue );
+}
+
+/************************************************************************/
+//! @brief Get xbDouble field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param dFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetDoubleField( xbInt16 iFieldNo, xbDouble &dFieldValue ) const {
+ xbInt16 iRc = XB_NO_ERROR;
+ char buf[21];
+ memset( buf, 0x00, 21 );
+ if(( iRc = GetRawField( iFieldNo, buf, 21, 0 )) >= XB_NO_ERROR ){
+ dFieldValue = strtod( buf, NULL );
+ return XB_NO_ERROR;
+ } else
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get xbDouble field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param dFieldValue Output field value.
+ \param iRecBufSw 0 - Record buffer with any updates.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetDoubleField( xbInt16 iFieldNo, xbDouble &dFieldValue, xbInt16 iRecBufSw ) const {
+ xbInt16 iRc = XB_NO_ERROR;
+ char buf[21];
+ memset( buf, 0x00, 21 );
+ if(( iRc = GetRawField( iFieldNo, buf, 21, iRecBufSw )) >= XB_NO_ERROR ){
+ dFieldValue = strtod( buf, NULL );
+ return XB_NO_ERROR;
+ } else
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Get xbDate field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param dtFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetDateField( xbInt16 iFieldNo, xbDate &dtFieldValue ) const{
+ xbString s;
+ xbInt16 iRc;
+ if(( iRc = GetField( iFieldNo, s )) != XB_NO_ERROR )
+ return iRc;
+ return dtFieldValue.Set( s );
+ // return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Get xbDate field for field name.
+/*!
+ \param sFieldName Name of field to retrieve.
+ \param dtFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetDateField( const xbString &sFieldName, xbDate &dtFieldValue ) const{
+ xbString s;
+ xbInt16 iRc;
+ if(( iRc = GetField( sFieldName, s )) != XB_NO_ERROR )
+ return iRc;
+ return dtFieldValue.Set( s );
+ // return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Get xbString field for field name.
+/*!
+ \param sFieldName Name of field to retrieve
+ \param sFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetField( const xbString &sFieldName, xbString &sFieldValue ) const{
+ return GetField( GetFieldNo( sFieldName ), sFieldValue, 0 );
+}
+/************************************************************************/
+//! @brief Get field data for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param sFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetField( xbInt16 iFieldNo, xbString &sFieldValue ) const{
+ return GetField( iFieldNo, sFieldValue, 0 );
+}
+/************************************************************************/
+//! @brief Get field data for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param sFieldValue Output field value.
+ \param iRecBufSw 0 - Record buffer with any updates.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetField( xbInt16 iFieldNo, xbString &sFieldValue, xbInt16 iRecBufSw) const
+{
+ xbUInt32 iLen;
+ if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) {
+ sFieldValue = "";
+ return XB_INVALID_FIELD_NO;
+ }
+ iLen = SchemaPtr[iFieldNo].cFieldLen;
+ sFieldValue = "";
+ if( iRecBufSw )
+ sFieldValue.Append( (char *) SchemaPtr[iFieldNo].pAddress2, iLen ); // original record buffer
+ else
+ sFieldValue.Append( (char *) SchemaPtr[iFieldNo].pAddress, iLen ); // current record buffer
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+//! @brief Get decimal for field name.
+/*!
+ This routine retreives a field's decimal length.
+ \param sFieldName Name of field to retrieve
+ \param iFieldDecimal Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetFieldDecimal( const xbString &sFieldName, xbInt16 & iFieldDecimal ) const {
+ return GetFieldDecimal( GetFieldNo( sFieldName ), iFieldDecimal );
+}
+
+
+/************************************************************************/
+//! @brief Get decimal for field number.
+/*!
+ This routine retreives a field's decimal length.
+ \param iFieldNo Number of field to retrieve
+ \param iFieldDecimal Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetFieldDecimal( xbInt16 iFieldNo, xbInt16 & iFieldDecimal ) const {
+
+ if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) {
+ return XB_INVALID_FIELD_NO;
+ }
+ iFieldDecimal = SchemaPtr[iFieldNo].cNoOfDecs;
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Get field length for field name.
+/*!
+
+ This function retrieves a field's length.
+
+ \param sFieldName Name of field to retrieve
+ \param iFieldLen Output field length value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetFieldLen( const xbString &sFieldName, xbInt16 &iFieldLen ) const {
+ return GetFieldLen( GetFieldNo( sFieldName ), iFieldLen );
+}
+
+
+/************************************************************************/
+//! @brief Get field length for field number.
+/*!
+ This function retrieves a field's length.
+
+ \param iFieldNo Name of field to retrieve
+ \param iFieldLen Output field length value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetFieldLen( xbInt16 iFieldNo, xbInt16 &iFieldLen ) const {
+ if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){
+ iFieldLen = SchemaPtr[iFieldNo].cFieldLen;
+ return XB_NO_ERROR;
+ } else
+ return XB_INVALID_FIELD_NO;
+}
+
+/************************************************************************/
+//! @brief Get the field number for name.
+/*! Returns the field number for the named field.
+
+ All field get/put methods require either a field number or field name as
+ one of the parameters. Using the methods that take the field numbers will
+ yield slightly better performance because the methods that take a name, have
+ to look up the number.
+
+ \param sFieldName Name of field.
+ \param iFieldNo Output field number for the given name.
+ \returns Number of field named fldName.
+*/
+
+xbInt16 xbDbf::GetFieldNo( const xbString & sFieldName, xbInt16 &iFieldNo ) const
+{
+ int i;
+
+ if( sFieldName.Len() > 10 )
+ return XB_INVALID_FIELD_NAME;
+
+ for( i = 0; i < iNoOfFields; i++ ){
+ if( sFieldName == SchemaPtr[i].cFieldName ){
+ iFieldNo = i;
+ return XB_NO_ERROR;
+ }
+ }
+ return XB_INVALID_FIELD_NAME;
+}
+
+/************************************************************************/
+//! Get field ID number for a given field name.
+/*! Returns the field number for the named field.
+
+ \param sFieldName Name of field.
+ \returns Number of field or XB_INVALID_FIELD_NAME.
+*/
+
+xbInt16 xbDbf::GetFieldNo( const xbString &sFieldName ) const {
+ int i;
+
+ if( sFieldName.Len() > 10 )
+ return XB_INVALID_FIELD_NAME;
+
+ for( i = 0; i < iNoOfFields; i++ ){
+ if( sFieldName == SchemaPtr[i].cFieldName )
+ return i;
+ }
+ return XB_INVALID_FIELD_NAME;
+}
+
+/************************************************************************/
+//! Get field type for field number.
+/*!
+ \param iFieldNo Field number.
+ \param cFieldType Output field type.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetFieldType( xbInt16 iFieldNo, char & cFieldType ) const{
+
+ if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){
+ cFieldType = SchemaPtr[iFieldNo].cType;
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_INVALID_FIELD_NO;
+}
+
+/************************************************************************/
+//! Get field type for field name.
+/*!
+ \param sFieldName Field name.
+ \param cFieldType Output field type.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetFieldType( const xbString &sFieldName, char & cFieldType ) const{
+ return( GetFieldType( GetFieldNo( sFieldName ), cFieldType));
+}
+
+/************************************************************************/
+//! @brief Returns the name of the specified field.
+/*! Returns a pointer to the name for the field specified by iFieldNo.
+
+ \param iFieldNo Number of field.
+ \param sFieldName Output variable containing the field name.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetFieldName( xbInt16 iFieldNo, xbString &sFieldName ) const{
+ if( iFieldNo >= 0 && iFieldNo < iNoOfFields ){
+ sFieldName = SchemaPtr[iFieldNo].cFieldName;
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_INVALID_FIELD_NO;
+}
+
+/************************************************************************/
+//! @brief Get xbFloat field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param fFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetFloatField( xbInt16 iFieldNo, xbFloat & fFieldValue ) const {
+
+ char cFieldType;
+ xbInt16 rc = GetFieldType( iFieldNo, cFieldType );
+ if( rc < 0 )
+ return rc;
+
+ if( cFieldType != 'N' && cFieldType != 'F' )
+ return XB_INVALID_FIELD_TYPE;
+
+ xbString sTemp;
+ rc = GetField( iFieldNo, sTemp, 0 );
+ if( rc < 0 )
+ return rc;
+
+ fFieldValue = (xbFloat) atof( sTemp.Str());
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+//! @brief Get xbFloat field for field name.
+/*!
+ \param sFieldName Number of field to retrieve.
+ \param fFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetFloatField( const xbString & sFieldName, xbFloat & fFieldValue ) const {
+ return GetFloatField( GetFieldNo(sFieldName ), fFieldValue );
+}
+
+/************************************************************************/
+//! @brief Get logical field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param sFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbString &sFieldValue ) const {
+
+ char cFieldType;
+ xbInt16 iRc = GetFieldType( iFieldNo, cFieldType );
+ if( iRc < 0 )
+ return iRc;
+ else if( cFieldType != 'L' )
+ return XB_INVALID_FIELD_TYPE;
+
+ if(( iRc = GetField( iFieldNo, sFieldValue )) < XB_NO_ERROR )
+ return iRc;
+ else
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Get logical field for field name.
+/*!
+ \param sFieldName Name of field to retrieve.
+ \param sFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetLogicalField( const xbString &sFieldName, xbString &sFieldValue ) const {
+ return GetLogicalField( GetFieldNo( sFieldName ), sFieldValue );
+}
+
+
+/************************************************************************/
+//! @brief Get logical field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param bFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbBool &bFieldValue ) const {
+ return GetLogicalField( iFieldNo, bFieldValue, 0 );
+}
+
+/************************************************************************/
+//! @brief Get logical field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param bFieldValue Output field value.
+ \param iRecBufSw 0 - Record buffer with any updates.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetLogicalField( xbInt16 iFieldNo, xbBool &bFieldValue, xbInt16 iRecBufSw ) const {
+
+ char cFieldType;
+ xbInt16 iRc = GetFieldType( iFieldNo, cFieldType );
+ if((iRc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR )
+ return iRc;
+
+ if( cFieldType != 'L' )
+ return XB_INVALID_FIELD_TYPE;
+
+ xbString sFieldValue;
+ if(( iRc = GetField( iFieldNo, sFieldValue, iRecBufSw )) < XB_NO_ERROR )
+ return iRc;
+
+ if( sFieldValue == 'T' || sFieldValue == 't' || sFieldValue == 'Y' || sFieldValue == 'y' )
+ bFieldValue = xbTrue;
+ else
+ bFieldValue = xbFalse;
+
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Get logical field for field name.
+/*!
+ \param sFieldName Name of field to retrieve.
+ \param bFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetLogicalField( const xbString &sFieldName, xbBool &bFieldValue ) const {
+ return GetLogicalField( GetFieldNo( sFieldName ), bFieldValue );
+}
+/************************************************************************/
+//! @brief Get long field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param lFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetLongField( xbInt16 iFieldNo, xbInt32 & lFieldValue ) const {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ char cFieldType;
+ xbString sTemp;
+
+ try{
+
+ if(( iRc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if( cFieldType != 'N' && cFieldType != 'F' && cFieldType != 'M' ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_FIELD_TYPE;
+ throw iRc;
+ }
+
+ if(( iRc = GetField( iFieldNo, sTemp, 0 )) < 0 ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ sTemp.Trim();
+
+ if( !sTemp.ValidNumericValue() ){
+ iErrorStop = 130;
+ iRc = XB_INVALID_DATA;
+ throw iRc;
+ }
+
+ if( sTemp.Pos( '.' ) > 0){
+ iErrorStop = 140;
+ iRc = XB_INVALID_DATA;
+ throw iRc;
+ }
+
+ lFieldValue = atol( sTemp.Str());
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetLongField() Exception Caught. Error Stop = [%d] rc = [%d] [%s]", iErrorStop, iRc, sTemp.Str() );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Get memo field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param sMemoValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetMemoField( xbInt16 iFieldNo, xbString &sMemoValue ){
+ return Memo->GetMemoField( iFieldNo, sMemoValue );
+}
+/************************************************************************/
+//! @brief Get memo field for field name.
+/*!
+
+ \param sFieldName Name of field to retrieve.
+ \param sMemoValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetMemoField( const xbString & sFieldName, xbString & sMemoValue ){
+ return Memo->GetMemoField( GetFieldNo( sFieldName ), sMemoValue );
+}
+
+/************************************************************************/
+//! @brief Get the memo field count for this table.
+/*!
+ \returns Returns the number of memo fields in the table,
+*/
+xbInt16 xbDbf::GetMemoFieldCnt() const {
+ return iMemoFieldCnt;
+}
+/************************************************************************/
+//! @brief Get memo field length for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param ulMemoFieldLen Output memo field value length.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 & ulMemoFieldLen ){
+ return Memo->GetMemoFieldLen( iFieldNo, ulMemoFieldLen );
+}
+/************************************************************************/
+//! @brief Get memo field length for field name.
+/*!
+ \param sFieldName Name of field to retrieve.
+ \param ulMemoFieldLen Output memo field value length.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetMemoFieldLen( const xbString &sFieldName, xbUInt32 &ulMemoFieldLen ){
+ return Memo->GetMemoFieldLen( GetFieldNo( sFieldName ), ulMemoFieldLen );
+}
+
+#endif // XB_MEMO_SUPPORT
+
+/************************************************************************/
+//! @brief Get field null status
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param bIsNull Output field value. If field is all spaces on record buffer, returns true.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull ) const {
+ return GetNullSts( iFieldNo, bIsNull, 0 );
+}
+
+/************************************************************************/
+//! @brief Get field null status
+/*!
+ \param iFieldName Field Name of field to retrieve.
+ \param bIsNull Output field value. If field is all spaces on record buffer, returns true.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetNullSts( const xbString &sFieldName, xbBool &bIsNull ) const {
+ return GetNullSts( GetFieldNo( sFieldName ), bIsNull, 0 );
+}
+
+/************************************************************************/
+//! @brief Get field null status
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param bIsNull Output field value. If field is all spaces on record buffer, returns true.
+ \param iRecBufSw 0 - Record buffer with any updates.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull, xbInt16 iRecBufSw ) const
+{
+ if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) {
+ return XB_INVALID_FIELD_NO;
+ }
+ bIsNull = xbTrue;
+ char *p;
+ if( iRecBufSw )
+ p = (char *) SchemaPtr[iFieldNo].pAddress2;
+ else
+ p = (char *) SchemaPtr[iFieldNo].pAddress;
+
+ xbUInt32 ulLen = SchemaPtr[iFieldNo].cFieldLen;
+ xbUInt32 ul = 0;
+
+ while( ul < ulLen && bIsNull ){
+ if( *p++ != ' ' )
+ bIsNull = xbFalse;
+ else
+ ul++;
+ }
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Get unsigned long field for field number.
+/*!
+ \param iFieldNo Number of field to retrieve.
+ \param ulFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::GetULongField( xbInt16 iFieldNo, xbUInt32 & ulFieldValue ) const {
+
+ xbInt16 rc = 0;
+ xbInt16 iErrorStop = 0;
+ char cFieldType;
+
+ try{
+
+ if(( rc = GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ if( cFieldType != 'N' && cFieldType != 'F' && cFieldType != 'M' ){
+ iErrorStop = 110;
+ rc = XB_INVALID_FIELD_TYPE;
+ throw rc;
+ }
+ xbString sTemp;
+ if(( rc = GetField( iFieldNo, sTemp, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+ sTemp.Trim();
+ if( !sTemp.ValidNumericValue() || ((int) sTemp.Pos( '.' ) > 0)){
+ iErrorStop = 130;
+ rc = XB_INVALID_DATA;
+ throw rc;
+ }
+ ulFieldValue = strtoul( sTemp.Str(), NULL, 10 );
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbDbf::GetULongField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Get long field for field name.
+/*!
+ \param sFieldName Number of field to retrieve.
+ \param lFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetLongField( const xbString &sFieldName, xbInt32 &lFieldValue ) const {
+ return GetLongField( GetFieldNo( sFieldName ), lFieldValue );
+}
+
+/************************************************************************/
+//! @brief Get unsigned long field for field name.
+/*!
+ \param sFieldName Number of field to retrieve.
+ \param ulFieldValue Output field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::GetULongField( const xbString &sFieldName, xbUInt32 &ulFieldValue ) const {
+ return GetULongField( GetFieldNo( sFieldName ), ulFieldValue );
+}
+
+/************************************************************************/
+//! @brief Get raw field data for field number.
+/*!
+
+ This is a protected method, used by other methods. This method would be
+ subject to buffer overflows if made public.
+
+ \param iFieldNo Number of field to retrieve.
+ \param cBuf Pointer to buffer area provided by calling application program.
+ \param ulBufSize Size of data to copy
+ \param iRecBufSw 0 - Record buffer with any updates.<br>1 - Record buffer with original data.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+
+xbInt16 xbDbf::GetRawField( xbInt16 iFieldNo, char *cBuf, xbUInt32 ulBufSize, xbInt16 iRecBufSw ) const
+{
+ if( iFieldNo < 0 || iFieldNo >= iNoOfFields ) {
+ return XB_INVALID_FIELD_NO;
+ }
+
+ size_t stCopySize;
+ if( ulBufSize > (size_t) (SchemaPtr[iFieldNo].cFieldLen ))
+ stCopySize = (size_t) (SchemaPtr[iFieldNo].cFieldLen );
+ else
+ stCopySize = ulBufSize - 1;
+
+ if( iRecBufSw )
+ memcpy( cBuf, SchemaPtr[iFieldNo].pAddress2, stCopySize );
+ else
+ memcpy( cBuf, SchemaPtr[iFieldNo].pAddress, stCopySize );
+
+ cBuf[stCopySize] = 0x00;
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+//! @brief Put double field for field name.
+/*!
+ \param sFieldName Name of field to update.
+ \param dFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::PutDoubleField( const xbString &sFieldName, xbDouble dFieldValue ){
+ return PutDoubleField( GetFieldNo( sFieldName ), dFieldValue );
+}
+
+/************************************************************************/
+//! @brief Put double field for field number.
+/*!
+ \param iFieldNo Number of field to update.
+ \param dFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutDoubleField( xbInt16 iFieldNo, xbDouble dFieldValue ){
+
+ xbInt16 rc;
+ xbString sDoubleFmt;
+ xbString sDoubleFmt2;
+ xbString sDoubleVal;
+ xbInt16 iFieldLen;
+ xbInt16 iNoOfDecs;
+
+ if(( rc = GetFieldDecimal( iFieldNo, iNoOfDecs )) != XB_NO_ERROR )
+ return rc;
+
+ if(( rc = GetFieldLen( iFieldNo, iFieldLen )) != XB_NO_ERROR )
+ return rc;
+
+ sDoubleFmt.Sprintf( "%d.%df", iFieldLen, iNoOfDecs );
+ sDoubleFmt2 = "%-";
+ sDoubleFmt2 += sDoubleFmt;
+ sDoubleVal.Sprintf( sDoubleFmt2.Str(), dFieldValue );
+ sDoubleVal.Rtrim();
+ return PutField( iFieldNo, sDoubleVal );
+}
+
+/************************************************************************/
+//! @brief Put date field for field name.
+/*!
+ \param sFieldName Name of field to update.
+ \param dtFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutDateField(const xbString &sFieldName, const xbDate &dtFieldValue ){
+ return PutField( GetFieldNo( sFieldName ), dtFieldValue.Str() );
+}
+
+/************************************************************************/
+//! @brief Put date field for field number.
+/*!
+ \param iFieldNo Number of field to update.
+ \param dtFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::PutDateField( xbInt16 iFieldNo, const xbDate &dtFieldValue ){
+ return PutField( iFieldNo, dtFieldValue.Str() );
+}
+
+/************************************************************************/
+//! @brief Put field for field name.
+/*!
+ \param sFieldName Name of field to update.
+ \param sFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::PutField(const xbString &sFieldName, const xbString &sFieldValue ) {
+ return PutField( GetFieldNo( sFieldName ), sFieldValue );
+}
+/************************************************************************/
+//! @brief Put field for field number.
+/*!
+ \param iFieldNo Number of field to update.
+ \param sFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutField( xbInt16 iFieldNo, const xbString &sFieldValue ) {
+ xbInt16 iLen;
+ xbInt16 iDecPos; /* Decimal Position */
+ char * startpos;
+ char * tp; /* Target Pointer */
+
+ if( iFieldNo < 0 || iFieldNo >= iNoOfFields )
+ return XB_INVALID_FIELD_NO;
+
+ if( SchemaPtr[iFieldNo].cType == 'L' && !sFieldValue.ValidLogicalValue())
+ return XB_INVALID_DATA;
+
+ else if( SchemaPtr[iFieldNo].cType == 'D' ){
+ xbDate d;
+ if( !d.DateIsValid( sFieldValue ))
+ return XB_INVALID_DATA;
+ iLen = 8;
+ }
+ else
+ {
+ iLen = (xbInt16) sFieldValue.Len();
+ if( SchemaPtr[iFieldNo].cType == 'F' || SchemaPtr[iFieldNo].cType == 'N' || SchemaPtr[iFieldNo].cType == 'M' ){
+ if( !sFieldValue.ValidNumericValue()) {
+ return XB_INVALID_DATA;
+ }
+ else {
+ iDecPos = (xbInt16) sFieldValue.Pos( "." ); // 0 is no decimal
+ int mlod; // max no of digits left of decimal point digit count
+
+
+ if( SchemaPtr[iFieldNo].cNoOfDecs > 0 )
+ mlod = SchemaPtr[iFieldNo].cFieldLen - SchemaPtr[iFieldNo].cNoOfDecs - 1;
+ else
+ mlod = iLen;
+
+ if( iDecPos == 0 ){ // no decimal in incoming data
+
+ // check digits to the left of the decimal
+ if( SchemaPtr[iFieldNo].cNoOfDecs > 0 && iLen > mlod ) /* no decimal in incoming data */
+ return XB_INVALID_DATA;
+
+ else if( SchemaPtr[iFieldNo].cNoOfDecs == 0 && iLen > SchemaPtr[iFieldNo].cFieldLen )
+ return XB_INVALID_DATA;
+
+ }
+ else // decimal in incoming data
+ {
+ if( (iDecPos-1) > mlod ) // too many digits to the left of dec in incoming data
+ return XB_INVALID_DATA;
+
+ // check digits to the right of the decimal
+ else if((iLen - iDecPos) > SchemaPtr[iFieldNo].cNoOfDecs )
+ return XB_INVALID_DATA;
+ }
+ }
+ }
+ }
+
+ // do all field edits before this point
+ if( iDbfStatus != XB_UPDATED ){
+ iDbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, uiRecordLen ); // save the original record bufer before making updates
+ }
+
+ memset( SchemaPtr[iFieldNo].pAddress, 0x20, SchemaPtr[iFieldNo].cFieldLen );
+
+ if( iLen > SchemaPtr[iFieldNo].cFieldLen )
+ iLen = SchemaPtr[iFieldNo].cFieldLen;
+
+ if( SchemaPtr[iFieldNo].cType == 'F' || SchemaPtr[iFieldNo].cType == 'N'
+ || SchemaPtr[iFieldNo].cType == 'M') {
+
+ xbInt16 iDecPos = (xbInt16) sFieldValue.Pos( "." );
+ if( iDecPos == 0 ){
+ iLen = (xbInt16) sFieldValue.Len();
+ iDecPos = 0;
+ }
+ else{
+ iLen = iDecPos - 1;
+ }
+
+ if( SchemaPtr[iFieldNo].cNoOfDecs > 0 ){
+ tp = SchemaPtr[iFieldNo].pAddress;
+ tp += SchemaPtr[iFieldNo].cFieldLen - SchemaPtr[iFieldNo].cNoOfDecs - 1;
+ *tp++ = '.';
+
+ if( iDecPos == 0 ){
+ for( xbInt32 i = 0; i < SchemaPtr[iFieldNo].cNoOfDecs; i++ )
+ *tp++ = '0';
+ } else {
+ xbInt32 j = iDecPos + 1;
+ for( xbInt32 i = 0; i < SchemaPtr[iFieldNo].cNoOfDecs; i++, j++ ){
+ if( j <= (xbInt32) sFieldValue.Len())
+ *tp++ = sFieldValue[j];
+ else
+ *tp++ = '0';
+ }
+ }
+ startpos= SchemaPtr[iFieldNo].pAddress +
+ SchemaPtr[iFieldNo].cFieldLen -
+ SchemaPtr[iFieldNo].cNoOfDecs - iLen - 1;
+
+ }
+ else
+ startpos=SchemaPtr[iFieldNo].pAddress+SchemaPtr[iFieldNo].cFieldLen-iLen;
+ }
+ else
+ startpos = SchemaPtr[iFieldNo].pAddress;
+
+ memcpy( startpos, sFieldValue.Str(), (size_t) iLen );
+ return 0;
+}
+
+/************************************************************************/
+//! @brief Put float field for field number.
+/*!
+ \param iFieldNo Number of field to update.
+ \param fFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::PutFloatField( xbInt16 iFieldNo, xbFloat fFieldValue ){
+
+ xbInt16 rc;
+ xbString sFloatFmt;
+ xbString sFloatFmt2;
+ xbString sFloatVal;
+ xbInt16 iFieldLen;
+ xbInt16 iNoOfDecs;
+
+ if(( rc = GetFieldDecimal( iFieldNo, iNoOfDecs )) != XB_NO_ERROR )
+ return rc;
+
+ if(( rc = GetFieldLen( iFieldNo, iFieldLen )) != XB_NO_ERROR )
+ return rc;
+
+ sFloatFmt.Sprintf( "%d.%df", iFieldLen, iNoOfDecs );
+ sFloatFmt2 = "%-";
+ sFloatFmt2 += sFloatFmt;
+ sFloatVal.Sprintf( sFloatFmt2.Str(), fFieldValue );
+ sFloatVal.Rtrim();
+ return PutField( iFieldNo, sFloatVal );
+}
+
+/************************************************************************/
+//! @brief Put float field for field name.
+/*!
+ \param sFieldName Name of field to update.
+ \param fFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutFloatField( const xbString &sFieldName, xbFloat fFieldValue ){
+ return PutFloatField( GetFieldNo( sFieldName ), fFieldValue );
+}
+
+/************************************************************************/
+//! @brief Put logical field for field number.
+/*!
+ \param iFieldNo Number of field to update.
+ \param sFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutLogicalField( xbInt16 iFieldNo, const xbString &sFieldValue ) {
+ return PutField( iFieldNo, sFieldValue );
+}
+
+/************************************************************************/
+//! @brief Put logical field for field name.
+/*!
+ \param sFieldName Name of field to update.
+ \param sFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::PutLogicalField( const xbString &sFieldName, const xbString &sFieldValue ) {
+ return PutField( GetFieldNo( sFieldName ), sFieldValue );
+}
+
+/************************************************************************/
+//! @brief Put logical field for field number.
+/*!
+ \param iFieldNo Number of field to update.
+ \param bFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutLogicalField( xbInt16 iFieldNo, xbBool bFieldValue ) {
+
+ if( bFieldValue )
+ return PutField( iFieldNo, "T" );
+ else
+ return PutField( iFieldNo, "F" );
+}
+
+/************************************************************************/
+//! @brief Put logical field for field name.
+/*!
+ \param sFieldName Name of field to update.
+ \param bFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+xbInt16 xbDbf::PutLogicalField( const xbString &sFieldName, xbBool bFieldValue ) {
+ return PutLogicalField( GetFieldNo( sFieldName ), bFieldValue );
+}
+
+/************************************************************************/
+//! @brief Put long field for field number.
+/*!
+ \param iFieldNo Number of field to update.
+ \param lFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutLongField( xbInt16 iFieldNo, xbInt32 lFieldValue ) {
+ xbString sLong;
+ sLong.Sprintf( "%ld", (xbInt32) lFieldValue );
+ return PutField( iFieldNo, sLong.Str() );
+}
+
+/************************************************************************/
+//! @brief Put long field for field name.
+/*!
+ \param sFieldName Name of field to update.
+ \param lFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutLongField( const xbString &sFieldName, xbInt32 lFieldValue ) {
+ return PutLongField( GetFieldNo( sFieldName ), lFieldValue );
+}
+
+/************************************************************************/
+//! @brief Put unsigned long field for field number.
+/*!
+ \param iFieldNo Number of field to update.
+ \param ulFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutULongField( xbInt16 iFieldNo, xbUInt32 ulFieldValue ) {
+ xbString sLong;
+ sLong.Sprintf( "%lu", (xbInt32) ulFieldValue );
+ return PutField( iFieldNo, sLong.Str() );
+}
+
+/************************************************************************/
+//! @brief Put unsigned long field for field name.
+/*!
+ \param sFieldName Name of field to update.
+ \param ulFieldValue Field value.
+ \returns The field routines return one of:<br>
+ XB_NO_ERROR<br>XB_INVALID_DATA<br>XB_INVALID_FIELD_NO<br>XB_INVALID_FIELD_NAME<br>
+ XB_INVALID_FIELD_TYPE<br>XB_INVALID_DATA
+*/
+
+xbInt16 xbDbf::PutULongField( const xbString &sFieldName, xbUInt32 ulFieldValue ) {
+ return PutLongField( GetFieldNo( sFieldName ), (xbInt32) ulFieldValue );
+}
+
+/************************************************************************/
+#ifdef XB_MEMO_SUPPORT
+//! @brief Check if memo field exists for field name.
+/*!
+ \param sFieldName Name of field to check.
+ \returns xbTrue Field exists.<br> xbFale Field does not exist.
+*/
+
+xbBool xbDbf::MemoFieldExists( const xbString &sFieldName ) const{
+ return MemoFieldExists( GetFieldNo( sFieldName ));
+}
+
+/************************************************************************/
+//! @brief Check if memo field exists for field number.
+/*!
+ \param iFieldNo Number of field to check.
+ \returns xbTrue Field exists.<br> xbFale Field does not exist.
+*/
+
+xbBool xbDbf::MemoFieldExists( xbInt16 iFieldNo ) const{
+
+ xbInt32 lFld = 0L;
+ GetLongField( iFieldNo, lFld );
+ if( lFld == 0L )
+ return xbFalse;
+ else
+ return xbTrue;
+}
+
+#endif
+
+} /* namespace */
+
diff --git a/src/core/xbfile.cpp b/src/core/xbfile.cpp
new file mode 100755
index 0000000..6376e9a
--- /dev/null
+++ b/src/core/xbfile.cpp
@@ -0,0 +1,2217 @@
+/* xbfile.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+This module handles all the low level file I/O and is the base class
+for the table, memo and index classes
+
+*/
+
+#include "xbase.h"
+
+namespace xb{
+
+/************************************************************************/
+//! @brief Class Constructor.
+xbFile::xbFile( xbXBase * x ){
+ fp = NULL;
+ bFileOpen = xbFalse;
+ ulBlockSize = 0;
+ iFileNo = 0;
+ xbase = x;
+ if( GetMultiUser() == xbTrue )
+ iShareMode = XB_MULTI_USER;
+ else
+ iShareMode = XB_SINGLE_USER;
+ iOpenMode = 0;
+ #ifdef XB_LOCKING_SUPPORT
+ iLockRetries = -1;
+ #endif
+ #ifdef HAVE_SETENDOFFILE_F
+ fHandle = NULL;
+ #endif
+}
+
+/************************************************************************/
+//! @brief Class Destructor.
+xbFile::~xbFile(){
+ if( bFileOpen )
+ xbFclose();
+}
+
+/************************************************************************/
+//! @brief Create a unique file name.
+/*!
+ Given a directory and file extension as inputs, create a unique file name.
+
+ \param sDirIn Directory
+ \param sExtIn File Extension
+ \param sFqnOut A fully qualifed unique file name as output
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+/*
+xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut ){
+ return CreateUniqueFileName( sDirIn, sExtIn, sFqnOut, 0 );
+}
+*/
+/************************************************************************/
+//! @brief Create a unique file name.
+/*!
+ Given a directory and file extension as inputs, create a unique file name.
+
+ \param sDirIn Directory
+ \param sExtIn File Extension
+ \param iOption 0 - look only for one file for a given directory and extension<br>
+ 1 - if file name extension is "dbf" or "DBF", verify unique dbt or DBT (memo) file is also available<br>
+ \param sFqnOut A fully qualifed unique file name as output
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::CreateUniqueFileName( const xbString & sDirIn, const xbString & sExtIn, xbString &sFqnOut, xbInt16 iOption ){
+
+ xbBool bUniqueFileNameFound = xbFalse;
+ xbFile f( xbase);
+ xbInt32 l = 1;
+ xbString sMemoFileName;
+
+ xbString sDir = sDirIn;
+ char c = GetPathSeparator();
+ if( sDirIn.Len() > 0 && sDirIn[sDirIn.Len()] != c )
+ sDir += c;
+
+ while( !bUniqueFileNameFound ){
+ sFqnOut.Sprintf( "%sxbTmp%03d.%s", sDir.Str(), l, sExtIn.Str());
+ if( iOption == 1 && sExtIn == "DBF" ){
+ sMemoFileName.Sprintf( "%sxbTmp%03d.DBT", sDirIn.Str(), l );
+ }
+ else if( iOption == 1 && sExtIn == "dbf" ){
+ sMemoFileName.Sprintf( "%sxbTmp%03d.dbt", sDirIn.Str(), l );
+ }
+ if( f.FileExists( sFqnOut ) || ( sMemoFileName.Len() > 0 && f.FileExists( sMemoFileName ))){
+ l++;
+ }
+ else
+ {
+ bUniqueFileNameFound = xbTrue;
+ }
+ if( l > 999 )
+ return XB_FILE_EXISTS;
+ }
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Determine which version the memo (dbt) file is.
+/*!
+
+ This routine uses the first byte in the dbf file to determine which memo
+ file version is in use. The main difference between version 3 and 4 is that
+ version 4 will reuse blocks if they become available. Version 3 does not.
+
+ \param cFileTypeByte is an output field and is one of:<br>
+ <br>
+ 0 - none<br>
+ 3 - Dbase III+<br>
+ 4 - Dbase IV<br>
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::DetermineXbaseMemoVersion( unsigned char cFileTypeByte ) const {
+
+
+ if( BitSet( cFileTypeByte, 3 ) && BitSet( cFileTypeByte, 7 ))
+ return 4;
+ else if( BitSet( cFileTypeByte, 7 ))
+ return 3;
+
+ return 0;
+}
+/*************************************************************************/
+//! @brief Determine xbase dbf version.
+/*!
+
+ This routine is used to determine which version of the Xbase classes can
+ be used for a given DBF file.<br>
+
+ It attempts to use the highest version compiled into the library.<br>
+
+ References:<br>
+ This routine uses the first byte from the dbf file.<br>
+ Per DBase documentation:<br>
+ Valid dBASE for Windows table file, bits 0-2 indicate version number: 3 for dBASE Level 5, 4 for dBASE Level 7.<br>
+ Bit 3 and bit 7 indicate presence of a dBASE IV or dBASE for Windows memo file;<br>
+ Bits 4-6 indicate the presence of a dBASE IV SQL table;<br>
+ Bit 7 indicates the presence of any .DBT memo file (either a dBASE III PLUS type or a dBASE IV or dBASE for Windows memo file).<br>
+<br>
+ Bachmann spec (used extensively in library build), page 7 - does not match DBase documentation<br>
+<br>
+ returns<br>
+ 0 - unknown<br>
+ 3 - Dbase level 3<br>
+ 4 - Dbase level 4<br>
+ 5 - Dbase Level 5 (future)<br>
+ 7 - Dbase Level 7 (future)<br>
+<br>
+ 1x - Clipper files (future)<br>
+ 2x - Foxbase files (future)<br>
+<br>
+*/
+
+xbInt16 xbFile::DetermineXbaseTableVersion( unsigned char cFileTypeByte ) const {
+
+ xbInt16 iMemoVersion = DetermineXbaseMemoVersion(cFileTypeByte);
+ char cDbfLevel = cFileTypeByte & 0x07;
+
+ #ifdef XB_DBF4_SUPPORT
+ if( cDbfLevel == 3 && iMemoVersion != 3 )
+ return 4;
+ #endif
+
+ #ifdef XB_DBF3_SUPPORT
+ if( cDbfLevel == 3 && iMemoVersion != 4 )
+ return 3;
+ #endif
+
+ return 0;
+}
+
+/*************************************************************************/
+//! @brief Get a portable double value.
+/*!
+
+ This routine returns a double value from an 8 byte character stream,
+ accounting for endian type.
+
+ Converts a double (64 bit floating point) value stored at p from a portable
+ format to the machine format.
+
+ \param p pointer to memory containing the portable double value
+
+ \returns the double value.
+*/
+
+xbDouble xbFile::eGetDouble( const char *p ) const {
+ xbDouble d;
+ const char *sp;
+ char *tp;
+ xbInt16 i;
+
+ tp = (char *) &d;
+ sp = p;
+ if( iEndianType == 'L' )
+ for( i = 0; i < 8; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp+=7;
+ for( i = 0; i < 8; i++ ) *tp++ = *sp--;
+ }
+
+ return d;
+}
+
+/*************************************************************************/
+//! @brief Get a portable long value.
+
+/*!
+ This routine returns a long int value from a 4 byte character stream,
+ accounting for endian type.
+
+ \param p pointer to memory containing the portable long value
+
+ \returns the long value.
+*/
+
+xbInt32 xbFile::eGetInt32( const char *p ) const {
+ xbInt32 l;
+ char *tp;
+ xbInt16 i;
+
+ tp = (char *) &l;
+ if( iEndianType == 'L' )
+ for( i = 0; i < 4; i++ ) *tp++ = *p++;
+ else {
+ p+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *p--;
+ }
+ return l;
+
+}
+/*************************************************************************/
+//! @brief Get a portable unsigned long value.
+
+/*!
+ This routine returns an unsigned long int value from a 4 byte character stream,
+ accounting for endian type.
+
+ \param p pointer to memory containing the portable long value
+
+ \returns the unsigned long value.
+*/
+
+xbUInt32 xbFile::eGetUInt32( const char *p ) const {
+ xbUInt32 ul;
+ xbInt16 i;
+ char *tp;
+
+ tp = (char *) &ul;
+ if( iEndianType == 'L' )
+ for( i = 0; i < 4; i++ ) *tp++ = *p++;
+ else{
+ p+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *p--;
+ }
+ return ul;
+}
+
+/*************************************************************************/
+//! @brief Get a portable short value.
+
+/*!
+ This routine returns a short int value from a 2 byte character stream,
+ accounting for endian type.
+
+ \param p pointer to memory containing the portable long value
+
+ \returns the short value.
+*/
+
+xbInt16 xbFile::eGetInt16(const char *p) const {
+ xbInt16 s, i;
+ char *tp;
+ tp = (char *) &s;
+ if( iEndianType == 'L' )
+ for( i = 0; i < 2; i++ ) *tp++ = *p++;
+ else{
+ p++;
+ for( i = 0; i < 2; i++ ) *tp++ = *p--;
+ }
+ return s;
+}
+/*************************************************************************/
+//! @brief Get a portable unsigned short value.
+
+/*!
+ This routine returns a short unsigned int value from a 2 byte character stream,
+ accounting for endian type.
+
+ \param p pointer to memory containing the portable long value
+
+ \returns the short value.
+*/
+
+xbUInt16 xbFile::eGetUInt16(const char *p) const {
+ xbInt16 i;
+ xbUInt16 uI;
+ char *tp;
+
+ tp = (char *) &uI;
+ if( iEndianType == 'L' )
+ for( i = 0; i < 2; i++ ) *tp++ = *p++;
+ else{
+ p++;
+ for( i = 0; i < 2; i++ ) *tp++ = *p--;
+ }
+ return uI;
+}
+
+
+/*************************************************************************/
+//! @brief Write a portable double value to memory location.
+/*!
+ Converts a double (64 bit floating point) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ This routine puts a double value to an 8 byte character stream
+
+ \param c Pointer to memory to hold converted value
+ \param d Input value to be converted
+*/
+
+void xbFile::ePutDouble( char *c, xbDouble d ){
+ const char *sp;
+ char *tp;
+ xbInt16 i;
+
+ tp = c;
+ sp = (const char *) &d;
+ if( iEndianType == 'L' )
+ for( i = 0; i < 8; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp+=7;
+ for( i = 0; i < 8; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+
+/*************************************************************************/
+//! @brief Write a portable short value to memory location.
+/*!
+ Converts a short (16 bit integer) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ This routine puts a short value to a 2 byte character stream
+
+ \param c Pointer to memory to hold converted value
+ \param s Input value to be converted
+*/
+
+void xbFile::ePutInt16( char * c, xbInt16 s ){
+ const char *sp;
+ char *tp;
+ xbInt16 i;
+
+ tp = c;
+ sp = (const char *) &s;
+
+ if( iEndianType == 'L' )
+ for( i = 0; i < 2; i++ ) *tp++ = *sp++;
+ else{ /* big endian */
+ sp++;
+ for( i = 0; i < 2; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+/*************************************************************************/
+//! @brief Write a portable unsigned short value to memory location.
+/*!
+ Converts an unsigned short (16 bit integer) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ This routine puts an unsigned short value to a 2 byte character stream
+
+ \param c Pointer to memory to hold converted value
+ \param s Input value to be converted
+*/
+
+void xbFile::ePutUInt16( char * c, xbUInt16 s ){
+ const char *sp;
+ char *tp;
+ xbInt16 i;
+
+ tp = c;
+ sp = (const char *) &s;
+
+ if( iEndianType == 'L' )
+ for( i = 0; i < 2; i++ ) *tp++ = *sp++;
+ else{ /* big endian */
+ sp++;
+ for( i = 0; i < 2; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+
+/*************************************************************************/
+//! @brief Write a portable long value to memory location.
+/*!
+ Converts a long (32 bit integer) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ This routine puts a long value to a 4 byte character stream.
+
+ \param c Pointer to memory to hold converted value
+ \param l Input value to be converted
+*/
+
+void xbFile::ePutInt32( char * c, xbInt32 l )
+{
+ const char *sp;
+ char *tp;
+ xbInt16 i;
+
+ tp = c;
+ sp = (const char *) &l;
+ if( iEndianType == 'L' )
+ for( i = 0; i < 4; i++ ) *tp++ = *sp++;
+ else {
+ sp+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+
+
+/*************************************************************************/
+//! @brief Write a portable unsigned long value to memory location.
+/*!
+ Converts an unsigned long (32 bit integer) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ This routine puts an unsigned long value to a 4 byte character stream.
+
+ \param c Pointer to memory to hold converted value
+ \param ul Input value to be converted
+*/
+
+void xbFile::ePutUInt32( char * c, xbUInt32 ul )
+{
+ const char *sp;
+ char *tp;
+ xbInt16 i;
+
+ tp = c;
+ sp = (const char *) &ul;
+ if( iEndianType == 'L' )
+ for( i = 0; i < 4; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+
+/************************************************************************/
+//! @brief Determines if a file exists.
+/*!
+ \returns xbTrue if file exists<br>
+ xbFalse if file does not exist
+
+*/
+xbBool xbFile::FileExists() const {
+ return FileExists( sFqFileName, 0 );
+}
+/************************************************************************/
+//! @brief Determines if a file exists.
+/*!
+ \param iOption if 1, assume this is a request for a dbf file and
+ check for the a dbt memo file also, returns true if both files are found
+
+ \returns xbTrue if both files exist<br>
+ xbFalse if file does not exist
+
+*/
+xbBool xbFile::FileExists( xbInt16 iOption ) const {
+ return FileExists( sFqFileName, iOption );
+}
+
+/************************************************************************/
+//! @brief Determines if a file exists.
+/*!
+ \param sFileName - file name to check for
+
+ \returns xbTrue if file exists<br>
+ xbFalse if file does not exist
+*/
+
+xbBool xbFile::FileExists(const xbString &sFileName ) const {
+ return FileExists( sFileName, 0 );
+}
+
+/************************************************************************/
+//! @brief Determines if a file exists.
+/*!
+ \param sFileName - file name to check for
+ \param iOption if 1, assume this is a request for a dbf file and
+ check for the a dbt memo file also, returns true if both files are found
+
+ \returns xbTrue if both dbf and dbt files exist<br>
+ xbFalse if file does not exist
+*/
+
+xbBool xbFile::FileExists( const xbString & sFileName, xbInt16 iOption ) const {
+
+ struct stat buffer;
+ if(( stat( sFileName.Str(), &buffer ) != 0 )){
+ return xbFalse;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ if( iOption == 1 ){
+ xbString sFileName2 = sFileName;
+
+ if( sFileName2[sFileName2.Len()] == 'F' )
+ sFileName2.PutAt( sFileName2.Len(), 'T' );
+ else
+ sFileName2.PutAt( sFileName2.Len(), 't' );
+
+ if(( stat( sFileName2.Str(), &buffer) != 0 ))
+ return xbFalse;
+ }
+ #endif
+ return xbTrue;
+}
+
+/************************************************************************/
+//! @brief Determines if file is open.
+/*!
+
+ \returns xbTrue if file is open<br>
+ xbFalse if file is not open
+*/
+
+xbBool xbFile::FileIsOpen() const {
+ return bFileOpen;
+}
+/************************************************************************/
+//! @brief Get the block size.
+/*!
+ \returns Block Size
+*/
+
+xbUInt32 xbFile::GetBlockSize() const {
+ return ulBlockSize;
+}
+
+/************************************************************************/
+//! @brief Get the directory name.
+/*!
+ \returns the directory name of the file
+*/
+
+const xbString & xbFile::GetDirectory() const {
+ return sDirectory;
+}
+
+/************************************************************************/
+//! @brief Get the directory part of the file name.
+/*!
+ \param sFileDirPartOut - the returned directory name
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::GetFileDirPart( xbString & sFileDirPartOut ) const {
+ return GetFileDirPart( sFqFileName, sFileDirPartOut );
+}
+
+/************************************************************************/
+//! @brief Get the directory part of the file name.
+/*!
+ \param sCompleteFileNameIn - a fully qualfied input file name
+ \param sFileDirPartOut - the returned directory name part out of sCompleteFileNameIn
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbFile::GetFileDirPart( const xbString & sCompleteFileNameIn, xbString & sFileDirPartOut ) const {
+
+ sFileDirPartOut = sCompleteFileNameIn;
+ sFileDirPartOut.SwapChars( '\\', '/' );
+ xbUInt32 iPos = sFileDirPartOut.GetLastPos( '/' );
+
+ if( iPos > 0 ){
+ xbString sTemp = sFileDirPartOut;
+ sFileDirPartOut.Assign( sTemp, 1, iPos );
+ return XB_NO_ERROR;
+ }
+ return XB_INVALID_DATA;
+}
+
+/************************************************************************/
+//! @brief Get the extension part of the file name.
+/*!
+ \param sFileNameExtOut - the returned extension part out of sCompleteFileNameIn
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::GetFileExtPart( xbString & sFileNameExtOut ) const {
+ return GetFileExtPart( sFqFileName, sFileNameExtOut );
+}
+
+/************************************************************************/
+//! @brief Get the extension part of the file name.
+/*!
+ \param sCompleteFileNameIn - a fully qualfied input file name
+
+ \param sFileExtPartOut - the returned directory name part out of sCompleteFileNameIn
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbFile::GetFileExtPart( const xbString & sCompleteFileNameIn, xbString & sFileExtPartOut ) const {
+
+
+ sFileExtPartOut = sCompleteFileNameIn;
+ xbUInt32 iPos = sFileExtPartOut.GetLastPos( '.' );
+ if( iPos > 0 ){ /* get rid of the directory part of the name */
+ sFileExtPartOut.Ltrunc( iPos );
+ return XB_NO_ERROR;
+ }
+ return XB_INVALID_DATA;
+}
+/************************************************************************/
+//! @brief Get the time of last file modification timestamp as reported by the OS.
+/*!
+ \param mtime - returned time of last file modification
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::GetFileMtime( time_t &mtime ){
+
+ struct stat buffer;
+ if( stat( sFqFileName.Str(), &buffer ))
+ return XB_FILE_NOT_FOUND;
+ else{
+ mtime = buffer.st_mtime;
+ return XB_NO_ERROR;
+ }
+}
+/************************************************************************/
+//! @brief Get the file name.
+/*!
+ \returns the file name portion of the file
+*/
+
+const xbString & xbFile::GetFileName() const {
+ return sFileName;
+}
+
+
+/************************************************************************/
+//! @brief Get the name part of the file name.
+/*!
+ \param sFileNamePartOut - the returned file name part out of sCompleteFileNameIn
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbFile::GetFileNamePart( xbString & sFileNamePartOut ) const {
+ return GetFileNamePart( sFqFileName, sFileNamePartOut );
+}
+
+/************************************************************************/
+//! @brief Get the name part of the file name.
+/*!
+ \param sCompleteFileNameIn - a fully qualified input file name
+ \param sFileNamePartOut - the returned file name part out of sCompleteFileNameIn
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+//*********** fixme should this be static?????
+
+xbInt16 xbFile::GetFileNamePart( const xbString & sCompleteFileNameIn, xbString & sFileNamePartOut ) const {
+ /* extract the file name part out of the string */
+
+ sFileNamePartOut = sCompleteFileNameIn;
+ sFileNamePartOut.SwapChars( '\\', '/' );
+ xbUInt32 iPos = sFileNamePartOut.GetLastPos( '/' );
+ if( iPos > 0 ) /* get rid of the directory part of the name */
+ sFileNamePartOut.Ltrunc( iPos );
+ iPos = sFileNamePartOut.Pos( '.' );
+ if( iPos > 0 ){ /* get rid of the extension part of the name */
+ xbString sTemp = sFileNamePartOut;
+ sFileNamePartOut.Assign( sTemp, 1, iPos-1 );
+ }
+ return XB_NO_ERROR;
+}
+
+
+/************************************************************************/
+//! @brief Get the size of the file as reported by the OS.
+/*!
+ \param ullFileSize - unsigned long long field as output
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::GetFileSize( xbUInt64 &ullFileSize ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if(( iRc = xbFseek( 0, SEEK_END )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ullFileSize = xbFtell();
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::GetFileSize() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Get the file type aka Capitalized file extension
+/*!
+ \param sFileTypeOut - the returned extension part out of sCompleteFileNameIn
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::GetFileType( xbString & sFileTypeOut ) const {
+
+ xbInt16 iRc = GetFileExtPart( sFqFileName, sFileTypeOut );
+ sFileTypeOut.ToUpperCase();
+ return iRc;
+}
+/************************************************************************/
+//! @brief Get the fully qualified file name.
+/*!
+ \returns the fully qualfied name of the file
+*/
+
+const xbString & xbFile::GetFqFileName() const {
+ return sFqFileName;
+}
+
+/************************************************************************/
+//! @brief Get the open mode of the file.
+/*!
+ \returns XB_READ<br>
+ XB_READ_WRITE<br>
+ XB_WRITE<BR>
+*/
+
+xbInt16 xbFile::GetOpenMode() const {
+ return iOpenMode;
+}
+
+/************************************************************************/
+//! @brief Get the share mode of the file.
+/*!
+ \returns XB_SINGLE_USER - (file buffering on><br>
+ XB_MULTI_USER - (file buffering off)<br>
+*/
+
+xbInt16 xbFile::GetShareMode() const {
+ return iShareMode;
+}
+
+/************************************************************************/
+//! @brief Get the file type byte and version of the dbf file.
+/*!
+
+ Pull the first bye off the DBF file for further inspection.
+ First byte has various bits set to determine what the file format is.
+
+ \param sFileName - Name of file to examine
+ \param iVersion - Returned file version
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, xbInt16 &iVersion )
+{
+ unsigned char cFileTypeByte;
+ return GetXbaseFileTypeByte( sFileName, cFileTypeByte, iVersion );
+}
+
+/************************************************************************/
+//! @brief Get the file type byte and version of the dbf file.
+/*!
+ Pull the first bye off the DBF file for further inspection.
+ First byte has various bits set to determine what the file format is.
+
+ \param sFileName - Name of file to examine
+ \param cFileTypeByte - Retruned first byte of dbf file
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte )
+{
+ xbInt16 iVersion;
+ return GetXbaseFileTypeByte( sFileName, cFileTypeByte, iVersion );
+}
+
+/************************************************************************/
+//! @brief Get the file type byte and version of the dbf file.
+/*!
+ Pull the first bye off the DBF file for further inspection.
+ First byte has various bits set to determine what the file format is.
+
+
+ \param sFileName - Name of file to examine
+ \param cFileTypeByte - Returned first byte of dbf file
+ \param iVersion - Returned file version
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::GetXbaseFileTypeByte( const xbString &sFileName, unsigned char &cFileTypeByte, xbInt16 &iVersion ){
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ size_t stRc;
+ FILE *tfp;
+
+ try{
+
+ iVersion = 0;
+ cFileTypeByte = 0x00;
+ #ifdef HAVE__FSOPEN_F
+ // 0x40 is SH_DENYNO or _SH_DENYNO
+ if(( tfp = _fsopen( sFileName.Str(), "r", 0x40 )) == NULL ){
+ iErrorStop = 100;
+ iRc = XB_OPEN_ERROR;
+ throw iRc;
+ }
+ #else
+ if(( tfp = fopen( sFileName.Str(), "r" )) == NULL ){
+ iErrorStop = 110;
+ iRc = XB_OPEN_ERROR;
+ throw iRc;
+ }
+ #endif
+
+ #ifdef HAVE_FSEEKO_F
+ iRc = fseeko( tfp, 0, SEEK_SET );
+ #else
+ iRc = fseek( tfp, 0, SEEK_SET );
+ #endif
+
+ if( iRc != 0 ){
+ iErrorStop = 120;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+ stRc = fread( &cFileTypeByte, (size_t) 1, (size_t) 1, tfp );
+ if( stRc != (size_t) 1 ){
+ iErrorStop = 130;
+ iRc = XB_READ_ERROR;
+ throw iRc;
+ }
+ iRc = XB_NO_ERROR;
+ fclose( tfp );
+ iVersion = DetermineXbaseTableVersion( cFileTypeByte );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::GetXbaseFileType() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Determines status of file extension.
+/*!
+
+ \param sFileName - Name of file to examine
+ \param iOption - Inspection type<br>
+ 1 check for DBF<br>
+ 2 check for NDX<br>
+ 3 check for MDX<br>
+ 4 check for NTX<br>
+
+ \returns 0 if suffix found<br>
+ 1 if suffix not found, lower case<br>
+ 2 is suffix not found, upper case<br>
+
+*/
+
+xbInt16 xbFile::NameSuffixMissing( const xbString & sFileName, xbInt16 iOption ) const {
+
+ xbUInt32 ulLen = sFileName.Len();
+ if( ulLen <= 4 ){
+ if( sFileName[ulLen] >= 'A' && sFileName[ulLen] <= 'Z' )
+ return 2;
+ else
+ return 1;
+ }
+ if( iOption == 1 && sFileName[ulLen-3] == '.' &&
+ ( sFileName[ulLen-2] == 'd' || sFileName[ulLen-2] == 'D' ) &&
+ ( sFileName[ulLen-1] == 'b' || sFileName[ulLen-1] == 'B' ) &&
+ ( sFileName[ulLen] == 'f' || sFileName[ulLen] == 'F' )
+ )
+ return 0;
+ if( iOption == 2 && sFileName[ulLen-3] == '.' &&
+ ( sFileName[ulLen-2] == 'n' || sFileName[ulLen-2] == 'N' ) &&
+ ( sFileName[ulLen-1] == 'd' || sFileName[ulLen-1] == 'D' ) &&
+ ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' )
+ )
+ return 0;
+ if( iOption == 3 && sFileName[ulLen-3] == '.' &&
+ ( sFileName[ulLen-2] == 'm' || sFileName[ulLen-2] == 'M' ) &&
+ ( sFileName[ulLen-1] == 'd' || sFileName[ulLen-1] == 'D' ) &&
+ ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' )
+ )
+ return 0;
+ if( iOption == 4 && sFileName[ulLen-3] == '.' &&
+ ( sFileName[ulLen-2] == 'n' || sFileName[ulLen-2] == 'N' ) &&
+ ( sFileName[ulLen-1] == 't' || sFileName[ulLen-1] == 'T' ) &&
+ ( sFileName[ulLen] == 'x' || sFileName[ulLen] == 'X' )
+ )
+ return 0;
+ // next line might be problematic if file naem has mixed case and extension is missing
+ if( sFileName[ulLen-4] >= 'A' && sFileName[ulLen-4] <= 'Z' )
+ return 2;
+ else
+ return 1;
+}
+
+/***********************************************************************/
+//! @brief Read a block of data from file.
+/*!
+
+ \param ulBlockNo - block number to read
+ \param lReadSize - size of data to read at block location, set to 0 to read blocksize
+ \param *buf - pointer to buffer to write output data, assumed to be previosuly allocated
+ and large enough to contain data
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbFile::ReadBlock( xbUInt32 ulBlockNo, size_t lReadSize, void * buf ){
+ return ReadBlock( ulBlockNo, ulBlockSize, lReadSize, buf );
+}
+
+/***********************************************************************/
+//! @brief Read a block of data from file.
+/*!
+
+ \param ulBlockNo - block number to read
+ \param ulBlockSize - block size
+ \param lReadSize - size of data to read at block location, set to 0 to read blocksize
+ \param buf - pointer to buffer to write output data, assumed to be previosuly allocated
+ and large enough to contain data
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbFile::ReadBlock( xbUInt32 ulBlockNo, xbUInt32 ulBlockSize, size_t lReadSize, void * buf ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+
+ try{
+ if( ulBlockSize <= 0 ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_BLOCK_SIZE;
+ throw iRc;
+ }
+
+ if(( iRc = xbFseek(((xbInt64) ulBlockNo*ulBlockSize ), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+
+ if( lReadSize <= 0 )
+ lReadSize = ulBlockSize;
+
+ if(( iRc = xbFread( buf, lReadSize, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ iRc = XB_READ_ERROR;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::ReadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] BlkNo=[%ld] BlkSize=[%ld] ReadSize=[%ld]", iErrorStop, iRc, ulBlockNo, ulBlockSize, lReadSize );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+//! @brief Set the block size.
+/*!
+
+ \param ulBlockSize - unsigned long block size, divisible by 512
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::SetBlockSize( xbUInt32 ulBlockSize ){
+ if( ulBlockSize %512 != 0 )
+ return XB_INVALID_BLOCK_SIZE;
+
+ this->ulBlockSize = ulBlockSize;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! @brief Set the directory.
+/*!
+ \param sDirectory - Valid directory name
+*/
+
+void xbFile::SetDirectory( const xbString & sDirectory ){
+
+ this->sDirectory = sDirectory;
+ char cLastChar = sDirectory[sDirectory.Len()];
+ if( cLastChar != '/' && cLastChar != '\\' )
+ sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str());
+ else
+ sFqFileName.Sprintf( "%s%s", sDirectory.Str(), sFileName.Str());
+
+ #ifdef WIN32
+ sFqFileName.SwapChars( '/', '\\' );
+ #else
+ sFqFileName.SwapChars( '\\', '/' );
+ #endif
+}
+
+/************************************************************************/
+//! @brief Set the filename.
+/*!
+ This routine builds out two internal variables from the input file name<br>
+ sFileName - the file name part<br>
+ sFqFileName - the fully qualified file name<br>
+
+
+ \param sFileName - Input file name
+*/
+void xbFile::SetFileName( const xbString & sFileName ){
+
+ if( sFileName == "" ){
+ sFqFileName = "";
+ return;
+ }
+
+ char cPathSep = sFileName.GetPathSeparator();
+
+ if( cPathSep ){
+
+ xbString sName;
+ xbString sExt;
+ // GetFileDirPart( this->sDirectory );
+ GetFileNamePart( sFileName, sName );
+ GetFileExtPart( sFileName, sExt );
+ this->sFileName.Sprintf( "%s.%s", sName.Str(), sExt.Str());
+ sFqFileName = sFileName;
+
+ } else {
+
+ this->sFileName = sFileName;
+ if( sDirectory.Len() == 0 ){
+ sDirectory = GetDataDirectory();
+ char cLastChar = sDirectory[sDirectory.Len()];
+ if( cLastChar != '/' && cLastChar != '\\' )
+ sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str() );
+ else
+ sFqFileName = sDirectory + sFileName;
+ }
+ else{
+ char cLastChar = sDirectory[sDirectory.Len()];
+ if( cLastChar != '/' && cLastChar != '\\' )
+ sFqFileName.Sprintf( "%s/%s", sDirectory.Str(), sFileName.Str() );
+ else
+ sFqFileName = sDirectory + sFileName;
+ }
+ }
+
+ #ifdef WIN32
+ sFqFileName.SwapChars( '/', '\\' );
+ #else
+ sFqFileName.SwapChars( '\\', '/' );
+ #endif
+}
+
+/************************************************************************/
+//! @brief Set the fully qualifed filename.
+/*!
+ \param sFqFileName - Fully qualifed input file name
+*/
+
+void xbFile::SetFqFileName( const xbString & sFqFileName ){
+ this->sFqFileName = sFqFileName;
+
+ xbString sDir;
+ xbString sName;
+ xbString sExt;
+
+ GetFileDirPart ( sFqFileName, sDir );
+ GetFileNamePart( sFqFileName, sName );
+ GetFileExtPart ( sFqFileName, sExt );
+
+ sDirectory = sDir;
+ sFileName.Sprintf( "%s.%s", sName.Str(), sExt.Str() );
+
+ #ifdef WIN32
+ this->sDirectory.SwapChars ( '/', '\\' );
+ this->sFqFileName.SwapChars( '/', '\\' );
+ #else
+ this->sDirectory.SwapChars ( '\\', '/' );
+ this->sFqFileName.SwapChars( '\\', '/' );
+ #endif
+}
+
+/************************************************************************/
+//! @brief Set Home Folders.
+/*!
+ Create xbase64 log, data and temp folders in the home directory for current usre.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::SetHomeFolders(){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 iRc = XB_NO_ERROR;
+ xbString sHomeDir;
+ char cPathSeparator;
+ xbString sDflt;
+
+ try{
+
+ GetHomeDir( sHomeDir );
+ //std::cout << "CreateHomeFolders() home dir = [" << sHomeDir.Str() << "]\n";
+
+ if( FileExists( sHomeDir ) == xbFalse ){
+ iErrorStop = 100;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+
+ #ifdef WIN32
+ cPathSeparator = '\\';
+ #else
+ cPathSeparator = '/';
+ #endif
+ sDflt.Sprintf( ".%c", cPathSeparator );
+ // set the default folders just in case later steps fail
+ xbase->SetDataDirectory( sDflt );
+ #ifdef XB_LOGGING_SUPPORT
+ xbase->SetLogDirectory( sDflt );
+ #endif
+
+ if( sHomeDir[sHomeDir.Len()] != cPathSeparator )
+ sHomeDir += cPathSeparator;
+
+ xbString sWork( sHomeDir );
+ sWork += "xbase64";
+
+ if( FileExists( sWork ) == xbFalse ){
+ #ifdef WIN32
+ if( CreateDirectory( sWork.Str(), NULL ) == 0 ){
+ iErrorStop = 130;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+ #else
+ // 0777 mode is correct, the mode will be modified by the user's umask
+ if( mkdir( sWork.Str(), 0777 ) == -1 ){
+ iErrorStop = 140;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+ #endif
+ }
+
+ #ifdef XB_LOGGING_SUPPORT
+ sWork.Sprintf( "%sxbase64%clogs", sHomeDir.Str(), cPathSeparator );
+ // std::cout << "logdir = " << sWork.Str() << "\n";
+
+ if( FileExists( sWork ) == xbFalse ){
+ #ifdef WIN32
+ if( CreateDirectory( sWork.Str(), NULL ) == 0 ){
+ iErrorStop = 110;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+ #else
+ if( mkdir( sWork.Str(), 0777 ) == -1 ){
+ iErrorStop = 120;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+ #endif
+ }
+ xbase->SetLogDirectory( sWork );
+ #endif // XB_LOGGING_SUPPORT
+
+ sWork.Sprintf( "%sxbase64%cdata", sHomeDir.Str(), cPathSeparator );
+ // std::cout << "datadir = " << sWork.Str() << "\n";
+ if( FileExists( sWork ) == xbFalse ){
+ #ifdef WIN32
+ if( CreateDirectory( sWork.Str(), NULL ) == 0 ){
+ iErrorStop = 130;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+ #else
+ if( mkdir( sWork.Str(), 0777 ) == -1 ){
+ iErrorStop = 140;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+ #endif
+ }
+
+ sWork.Sprintf( "%sxbase64%ctemp%c", sHomeDir.Str(), cPathSeparator, cPathSeparator );
+ // std::cout << "tempdir = " << sWork.Str() << "\n";
+ if( FileExists( sWork ) == xbFalse ){
+ #ifdef WIN32
+ if( CreateDirectory( sWork.Str(), NULL ) == 0 ){
+ iErrorStop = 150;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+ #else
+ if( mkdir( sWork.Str(), 0777 ) == -1 ){
+ iErrorStop = 160;
+ iRc = XB_DIRECTORY_ERROR;
+ throw iRc;
+ }
+ #endif
+ }
+ xbase->SetTempDirectory( sWork );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::CreateHomeFolders() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Write a block of data to file.
+/*!
+
+ \param ulBlockNo - block number to write
+ \param lWriteSize - size of data to write, set to 0 to write blocksize
+ \param *buf - pointer to buffer of data to be written
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbFile::WriteBlock( xbUInt32 ulBlockNo, size_t lWriteSize, void * buf ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if( ulBlockSize == 0 ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_BLOCK_SIZE;
+ throw iRc;
+ }
+ if( lWriteSize <= 0 )
+ lWriteSize = ulBlockSize;
+ if(( iRc = xbFseek(( (xbInt64) ulBlockNo*ulBlockSize), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( buf, lWriteSize, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::WriteBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fclose.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFclose(){
+
+ int iRc = 0;
+ if( bFileOpen ){
+ iRc = fclose( fp );
+ if( iRc != 0 ){
+ return XB_CLOSE_ERROR;
+ }
+ else{
+ bFileOpen = xbFalse;
+ }
+ iFileNo = 0;
+ }
+ return XB_NO_ERROR;
+}
+
+
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary feof.
+/*!
+ \returns non zero if end-of-file is set for the stream.
+*/
+xbInt16 xbFile::xbFeof(){
+ return feof( fp );
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fflush.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFflush() {
+
+ if( fflush( fp ) )
+ return XB_WRITE_ERROR;
+ else
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fgetc.
+/*!
+ \param c - output integer returned by fgetc
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFgetc( xbInt32 &c ) {
+
+ int i;
+
+ i = fgetc( fp );
+ if( i == EOF )
+ return XB_EOF;
+
+ c = i;
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fgetc.
+/*!
+ \param c - output character returned by fgetc
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFgetc( char &c ) {
+
+ int i;
+ i = fgetc( fp );
+ if( i == EOF )
+ return XB_EOF;
+
+ c = (char) i;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fgets.
+/*!
+ \param lSize - reads in at most, one character less than lSize
+ \param s - an xbString containing data returned by fseek
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFgets( size_t lSize, xbString &s ) {
+
+ s = "";
+ if( feof( fp ))
+ return XB_EOF;
+
+ char *sBuf = (char *) malloc( lSize + 1 );
+
+ if( fgets( sBuf, (xbInt32) lSize, fp ) == NULL ){
+ free( sBuf );
+ return XB_EOF;
+ }
+ s.Set( sBuf );
+ free( sBuf );
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fopen.
+/*!
+
+ This routine supports all the standard C library open modes. The Xbase routines only
+ use "r" and "r+b".
+
+ \param sOpenMode
+ <table>
+ <tr><th>OpenMode<th>Description
+ <tr><td>r<td>Reading
+ <tr><td>r+<td>Reading and Writing
+ <tr><td>w<td>Open for writing. Truncate to zero bytes if it exists
+ <tr><td>w+<td>Open for reading and writing, truncate to zero bytes if it exists
+ <tr><td>a<td>Open for append
+ <tr><td>a+<td>Open for reading and writing (at end).
+ </table>
+ The mode can also include the letter "b" for binary ie; "r+b". The "b" is ignored on
+ POSIX compliant systems, but is included for cross platform compatibility.
+ \param sFileName File name to open
+ \param iShareMode
+ XB_SINGLE_USER<br>
+ XB_MULTI_USER<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFopen( const xbString &sOpenMode, const xbString &sFileName, xbInt16 iShareMode ) {
+ if( sFileName == "" || sFqFileName == "" )
+ SetFileName( sFileName );
+ return xbFopen( sOpenMode, iShareMode );
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fopen.
+/*!
+
+ This routine supports all the standard C library open modes. The Xbase routines only
+ use "r" and "r+".
+
+
+ \param sOpenMode
+ <table>
+ <tr><th>OpenMode<th>Description
+ <tr><td>r<td>Reading
+ <tr><td>r+<td>Reading and Writing
+ <tr><td>w<td>Open for writing. Truncate to zero bytes if it exists
+ <tr><td>w+<td>Open for reading and writing, truncate to zero bytes if it exists
+ <tr><td>a<td>Open for append
+ <tr><td>a+<td>Open for reading and writing (at end).
+ </table>
+ The mode can also include the letter "b" for binary ie; "r+b". The "b" is ignored on
+ POSIX compliant systems, but is included for cross platform compatibility.
+ \param iShareMode
+ XB_SINGLE_USER<br>
+ XB_MULTI_USER<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFopen( const xbString & sOpenMode, xbInt16 iShareMode ) {
+
+ #ifdef HAVE__FSOPEN_F
+ if(( fp = _fsopen( sFqFileName.Str(), sOpenMode.Str(), 0x40 )) != NULL ){
+ #else
+ if(( fp = fopen( sFqFileName.Str(), sOpenMode.Str())) != NULL ){
+ #endif
+
+ if( sOpenMode == "r" )
+ iOpenMode = XB_READ;
+ else if( sOpenMode == "w" )
+ iOpenMode = XB_WRITE;
+ else
+ iOpenMode = XB_READ_WRITE;
+
+ bFileOpen = xbTrue;
+ this->iShareMode = iShareMode;
+
+ #ifdef HAVE__FILENO_F
+ iFileNo = _fileno( fp );
+ #else
+ iFileNo = fileno( fp );
+ #endif
+
+ #ifdef HAVE_SETENDOFFILE_F
+ //used by visual studio, 32 bit
+ fHandle = (HANDLE) _get_osfhandle( iFileNo );
+ #endif
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( iShareMode )
+ xbFTurnOffFileBuffering();
+ #endif
+
+ return XB_NO_ERROR;
+ }
+ else
+ return XB_OPEN_ERROR;
+}
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fopen.
+/*!
+ \param iOpenMode
+ XB_READ<br>
+ XB_READ_WRITE<br>
+ \param iShareMode
+ XB_SINGLE_USER<br>
+ XB_MULTI_USER<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFopen( xbInt16 iOpenMode, xbInt16 iShareMode ) {
+ this->iOpenMode = iOpenMode;
+ if( iOpenMode == XB_READ_WRITE )
+ return xbFopen( "r+b", iShareMode );
+ else if( iOpenMode == XB_READ )
+ return xbFopen( "r", iShareMode );
+ else
+ return XB_INVALID_OPTION;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fputc.
+/*!
+ \param c Character to write
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbFile::xbFputc( xbInt32 c ) {
+ return xbFputc( c, 1 );
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fputc.
+/*!
+ \param c Character to write
+ \param iNoOfTimes Number of times to write the character
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbFile::xbFputc( xbInt32 c, xbInt32 iNoOfTimes ) {
+
+ for( xbInt32 l = 0; l < iNoOfTimes; l++ )
+ if( fputc( c, fp ) != (int) c )
+ return XB_WRITE_ERROR;
+
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fputs.
+/*!
+ \param s xbString to write to file
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFputs( const xbString & s ){
+ if( fputs( s.Str(), fp ) < 0 )
+ return XB_WRITE_ERROR;
+ else
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fread.
+/*!
+ \param p Pointer to data to write
+ \param size size of write
+ \param nmemb Number of times to read it
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFread( void *p, size_t size, size_t nmemb ) {
+
+ size_t iRc;
+ iRc = fread( p, size, nmemb, fp );
+ if( iRc == nmemb )
+ return XB_NO_ERROR;
+ else
+ return XB_READ_ERROR;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fseek.
+/*!
+ \param lOffset Position in file to seek to
+ \param iWhence SEEK_SET - from beginning of file<br>
+ SEEK_CUR - from current position<br>
+ SEEK_END - from end of file<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbFseek( xbInt64 lOffset, xbInt32 iWhence ) {
+
+ xbInt32 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try {
+
+ #if defined(HAVE_FSEEKO_F)
+ iRc = fseeko( fp, lOffset, iWhence );
+ if( iRc != 0 ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ #elif defined(HAVE__FSEEKI64_F)
+ iRc = _fseeki64( fp, lOffset, iWhence );
+ if( iRc != 0 ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ #else
+ #ifdef XB_PLATFORM_32
+ /* if request is larger than 2 gig,this is a part of a locking request,
+ assuming offset is less than 4 gig, split the request into 2 fseek calls */
+ if( lOffset > 2147483647 && iWhence == SEEK_SET ){
+ /* move forward max amt - 2G */
+ if(( iRc = fseek( fp, 2147483647, SEEK_SET )) != 0 ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ lOffset -= 2147483647;
+ iWhence = SEEK_CUR;
+ }
+ #endif
+ iRc = fseek( fp, (long) lOffset, iWhence );
+ if( iRc != 0 ){
+ iErrorStop = 310;
+ throw iRc;
+ }
+ #endif
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::xbFseek() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ iRc = XB_SEEK_ERROR;
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary ftell.
+/*!
+ Returns the current file position.
+ \returns Current file position.
+*/
+
+size_t xbFile::xbFtell() {
+ return (size_t) ftell( fp );
+}
+
+/************************************************************************/
+//! @brief Turn off file buffering.
+/*!
+ Turns off file buffering. File buffering can't be used while in multi user mode.
+
+*/
+
+void xbFile::xbFTurnOffFileBuffering() {
+ setvbuf( fp, NULL, _IONBF, 0 );
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for standard libary fwrite.
+/*!
+ \param p Pointer to data buffer to write
+ \param size Size of data to write
+ \param nmemb Number of times to write data buffer
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::xbFwrite( const void *p, size_t size, size_t nmemb ) {
+
+ size_t iRc;
+ iRc = fwrite( p, size, nmemb, fp );
+ if( iRc == nmemb )
+ return XB_NO_ERROR;
+ else
+ return XB_READ_ERROR;
+}
+
+/************************************************************************/
+//! @brief Read file until a particular character is encountered on input stream.
+/*!
+ This routine will read until cDelim is encountered or eof, which ever occurs first.
+
+ \param cDelim Delimiter to stop writing at.
+ \param sOut Output xbString containing data read
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbReadUntil( const char cDelim, xbString &sOut ){
+
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char c;
+
+ try{
+ sOut = "";
+ if(( iRc = xbFgetc( c )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ sOut = c;
+ while( iRc == XB_NO_ERROR && c != cDelim ){
+ if(( iRc = xbFgetc( c )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ sOut += c;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::xbReadUntil() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Delete file.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::xbRemove() {
+ return xbRemove( sFqFileName.Str(), 0 );
+}
+
+/************************************************************************/
+//! @brief Delete file.
+/*!
+ \param sFileNameIn Name of file to delete
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbRemove( const xbString & sFileNameIn ) {
+ return xbRemove( sFileNameIn, 0 );
+}
+
+
+/************************************************************************/
+//! @brief Delete file.
+/*!
+ \param sFileNameIn Name of file to delete
+ \param iOption If Set to 1, assume this is a delete request for a dbf file, and should rename the dbt file also
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::xbRemove( const xbString & sFileNameIn, xbInt16 iOption ) {
+
+ xbInt32 iRc = remove( sFileNameIn.Str());
+
+ if( iRc != 0 )
+ return XB_DELETE_FAILED;
+
+ if( iOption == 1 ){
+ xbString sFileName2 = sFileNameIn;
+
+ if( sFileName2[sFileName2.Len()] == 'F' )
+ sFileName2.PutAt( sFileName2.Len(), 'T' );
+ else
+ sFileName2.PutAt( sFileName2.Len(), 't' );
+
+ iRc = remove( sFileName2.Str());
+ if( iRc != 0 )
+ return XB_DELETE_FAILED;
+ }
+
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Rename file.
+/*!
+ \param sOldName Original file name
+ \param sNewName New file name
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbFile::xbRename( const xbString & sOldName, const xbString & sNewName ){
+ if( rename( sOldName.Str(), sNewName.Str()))
+ return XB_RENAME_ERROR;
+ else
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for rewind.
+/*!
+ Set file pointer at beginning of file.
+*/
+
+void xbFile::xbRewind() {
+ rewind( fp );
+}
+
+/************************************************************************/
+//! @brief Xbase wrapper for ftruncate.
+/*!
+ Set file size to llSize
+ \param llSize New file size.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::xbTruncate( xbInt64 llSize ) {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+ #ifdef HAVE_FTRUNCATE_F
+ if(( iRc = ftruncate( iFileNo, llSize )) != 0 ){
+ iErrorStop = 100;
+ iRc = XB_WRITE_ERROR;
+ throw iRc;
+ }
+ #elif defined(HAVE_SETENDOFFILE_F)
+ if(( iRc = xbFseek( llSize, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ if(( iRc = SetEndOfFile( fHandle )) == 0 ){
+ iErrorStop = 120;
+ iRc = XB_WRITE_ERROR;
+ throw iRc;
+ } else {
+ iRc = XB_NO_ERROR;
+ }
+ #else
+
+ // check that cmake can find function SetEndOfFile -
+ // cmake could not find for Borland 5.5
+ FATAL_COMPILE_ERROR
+ CANT_LOCATE_FUNCTION_ftruncate_or_SetEndOfFile
+
+ #endif
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::xbTruncate() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+
+#ifdef XB_LOCKING_SUPPORT
+
+//! @brief Lock / unlock file.
+/*!
+ \param iFunction XB_LOCK<br>
+ XB_UNLOCK<br>
+ \param lOffset Position in file to lock
+ \param stLen Length to lock
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::xbLock( xbInt16 iFunction, xbInt64 lOffset, size_t stLen ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iTries = 0;
+
+ try{
+ #ifdef HAVE_FCNTL_F
+ /* Unix lock function */
+
+ if(( iRc = xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ struct flock fl;
+ switch( iFunction ){
+ case( XB_LOCK ):
+ fl.l_type = F_WRLCK;
+ break;
+ case( XB_UNLOCK ):
+ fl.l_type = F_UNLCK;
+ break;
+ default:
+ iErrorStop = 110;
+ iRc = XB_INVALID_LOCK_OPTION;
+ throw iRc;
+ break;
+ }
+ fl.l_whence = SEEK_CUR;
+ fl.l_start = 0;
+ fl.l_len = (xbInt32) stLen;
+ do{
+ iRc = fcntl( iFileNo, F_SETLK, &fl );
+ if( iRc && (errno == EACCES || errno == EAGAIN )){
+ iTries++;
+ xbase->xbSleep( GetDefaultLockWait() );
+ } else if( iRc ){
+ iErrorStop = 120;
+ iRc = XB_LOCK_FAILED;
+ throw iRc;
+ }
+ } while( iRc && iTries < GetLockRetryCount());
+ if( iRc )
+ iRc = XB_LOCK_FAILED; // lock failed, don't log an exception
+
+ #elif defined(HAVE_LOCKFILE_F)
+ /* Windows 64 byte lock functions */
+ /* split a quad word into two double words */
+ typedef union{
+ size_t Qword;
+ xbUInt32 Dword[2];
+ } Qsplit;
+
+ Qsplit lPos;
+ Qsplit lLen;
+ lPos.Qword = (size_t) lOffset;
+ lLen.Qword = stLen;
+
+ do{
+ if( iFunction == XB_LOCK ){
+ if(( iRc = LockFile( fHandle, lPos.Dword[0], lPos.Dword[1], lLen.Dword[0], lLen.Dword[1] )) == 0 ){
+ iTries++;
+ xbase->xbSleep( GetDefaultLockWait() );
+ }
+ }
+ else if( iFunction == XB_UNLOCK ){
+ if(( iRc = UnlockFile( fHandle, lPos.Dword[0], lPos.Dword[1], lLen.Dword[0], lLen.Dword[1] )) == 0 ){
+ iTries++;
+ xbase->xbSleep( GetDefaultLockWait() );
+ }
+ }
+ else
+ {
+ iErrorStop = 130;
+ iRc = XB_INVALID_LOCK_OPTION;
+ throw iRc;
+ }
+ } while( iRc == 0 && iTries < GetLockRetryCount());
+ if( iRc == 0 )
+ iRc = XB_LOCK_FAILED; // lock failed, don't log an exception
+ else
+ iRc = XB_NO_ERROR;
+
+ #elif defined(HAVE_LOCKING_F) || defined(HAVE__LOCKING_F)
+
+ /* older 32 bit locking functions */
+ xbInt32 iLockType;
+ if( iFunction == XB_LOCK ){
+ iLockType = 2;
+ } else if( iFunction == XB_UNLOCK ){
+ iLockType = 0;
+ } else {
+ iErrorStop = 140;
+ iRc = XB_INVALID_LOCK_OPTION;
+ throw iRc;
+ }
+
+ if(( iRc = xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+
+ do{
+ #ifdef HAVE__LOCKING_F
+ if(( iRc = _locking( iFileNo, iLockType, stLen )) != 0 ){
+ #else
+ if(( iRc = locking( iFileNo, iLockType, stLen )) != 0 ){
+ #endif
+ iTries++;
+ xbase->xbSleep( GetDefaultLockWait() );
+ }
+ } while( iRc != 0 && iTries < GetLockRetryCount());
+
+ if( iRc != 0 )
+ iRc = XB_LOCK_FAILED; // lock failed, don't log an exception
+ else
+ iRc = XB_NO_ERROR;
+
+ #else
+
+ FATAL ERROR - CANT BUILD LIBRARY IN CURRENT CONFIG - MISSING - no file locking function defined in xbfile.cpp
+
+ #endif
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::xbLock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+
+
+}
+
+//! @brief Return the locking retry setting.
+/*!
+
+ \returns The lock retry setting for this file or ths system default setting if the lock retry for the file
+ has not been set.
+*/
+
+xbInt16 xbFile::GetLockRetryCount() const {
+
+ if( iLockRetries == -1 )
+ return xbase->GetDefaultLockRetries();
+ else
+ return iLockRetries;
+}
+
+//! @brief Set the lock retry countr for this specific file.
+/*!
+ \param iLockRetries The number of retries to attempt before returning failure for this file
+*/
+
+void xbFile::SetLockRetryCount( xbInt16 iLockRetries ) {
+ this->iLockRetries = iLockRetries;
+}
+
+#endif
+
+
+/***********************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! @brief Debugging routine - dump mem to the log file.
+/*!
+ This routine dumps data from meemory to the log file. This is
+ primarily used for debugging and analysis purposes.
+
+ \param p Pointer to data to write
+ \param lBlxkSize Size of block
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::DumpMemToDisk( char *p, size_t lSize ){
+
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbString sDir;
+ xbString sFn;
+
+ FILE *fpd = NULL;
+
+ try{
+
+ // sDir = GetLogDirectory();
+ sDir = xbase->GetLogDirectory();
+
+ char cLastChar = sDir[sDir.Len()];
+
+ // build logfile name
+ if( cLastChar != '/' && cLastChar != '\\' )
+ sFn.Sprintf( "%s/MemDump.txt", sDir.Str());
+ else
+ sFn.Sprintf( "%sMemDump.txt", sDir.Str());
+
+ // open the dump file for append
+ #ifdef HAVE__FSOPEN_F
+ if(( fpd = _fsopen( sFn.Str(), "w+b", 0x40 )) == NULL ){
+ #else
+ if(( fpd = fopen( sFn.Str(), "w+b")) == NULL ){
+ #endif
+ iErrorStop = 100;
+ iRc = XB_OPEN_ERROR;
+ throw iRc;
+ }
+
+ int i;
+ // dump the block to the file
+ for( size_t l = 0; l < lSize; l++ ){
+ i = *p;
+ if( fputc( i, fpd ) == EOF ){
+ iErrorStop = 110;
+ iRc = XB_WRITE_ERROR;
+ throw iRc;
+ }
+ p++;
+ }
+ // close the dump file
+ fclose( fpd );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::DumpBlockToDisk() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( fpd )
+ fclose( fpd );
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Debugging routine - dump a block to the log file.
+/*!
+ This routine dumps a block to the log file. This is
+ primarily used for debugging and analysis purposes.
+
+ \param ulBlockNo Block number to write
+ \param lBlxkSize Size of block
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbFile::DumpBlockToDisk( xbUInt32 ulBlockNo, size_t lBlkSize ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ xbUInt32 ulStartBlock;
+ xbUInt32 ulEndBlock;
+
+ char *p = 0x00;
+
+ xbString sDir;
+ xbString sFn;
+ char *buf = NULL;
+ FILE *fpd = NULL;
+ try{
+
+ if( ulBlockNo == 0 ){
+ ulStartBlock = 0;
+ xbUInt64 ullFileSizeulBlockNo;
+ if(( iRc = GetFileSize( ullFileSizeulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ulEndBlock = (xbUInt32) (ullFileSizeulBlockNo / lBlkSize);
+ } else {
+ ulStartBlock = ulBlockNo;
+ ulEndBlock = ulBlockNo;
+ }
+
+ if(( buf = (char *) malloc( lBlkSize )) == NULL ){
+ iErrorStop = 110;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+// sDir = GetLogDirectory();
+ sDir = xbase->GetLogDirectory();
+ char cLastChar = sDir[sDir.Len()];
+
+ for( xbUInt32 l = ulStartBlock; l < ulEndBlock; l++ ){
+
+ if(( iRc = ReadBlock( l, lBlkSize, buf )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ // build logfile name
+ if( cLastChar != '/' && cLastChar != '\\' )
+ sFn.Sprintf( "%s/BlockDump.B%ld", sDir.Str(), l);
+ else
+ sFn.Sprintf( "%sBlockDump.%ld", sDir.Str(), l);
+
+ // open the dump file for append
+ #ifdef HAVE__FSOPEN_F
+ if(( fpd = _fsopen( sFn.Str(), "w+b", 0x40 )) == NULL ){
+ #else
+ if(( fpd = fopen( sFn.Str(), "w+b")) == NULL ){
+ #endif
+ iErrorStop = 130;
+ iRc = XB_OPEN_ERROR;
+ throw iRc;
+ }
+
+ // dump the block to the file
+ p = buf;
+ for( size_t l = 0; l < lBlkSize; l++ ){
+ //if( fputc( *p, fpd ) != *p ){
+ if( fputc( *p, fpd ) == EOF ){
+ iErrorStop = 140;
+ iRc = XB_WRITE_ERROR;
+ throw iRc;
+ }
+ p++;
+ }
+ // close the dump file
+ fclose( fpd );
+ }
+
+ // free the buffer
+ if( buf )
+ free( buf );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFile::DumpBlockToDisk() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( buf )
+ free( buf );
+ if( fpd )
+ fclose( fpd );
+ }
+ return iRc;
+}
+#endif
+/***********************************************************************/
+} /* namespace xb */
+
+
+
diff --git a/src/core/xbfilter.cpp b/src/core/xbfilter.cpp
new file mode 100755
index 0000000..5c5f276
--- /dev/null
+++ b/src/core/xbfilter.cpp
@@ -0,0 +1,544 @@
+/* xbfilter.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+
+This module handles uda (user data area) methods
+
+*/
+
+#include "xbase.h"
+
+
+// might need to change thisto XB_EXPRESSION_SUPPORT
+#ifdef XB_FILTER_SUPPORT
+
+
+namespace xb{
+
+/************************************************************************/
+xbFilter::xbFilter( xbDbf *dbf ) {
+ this->dbf = dbf;
+ this->exp = NULL;
+ lLimit = 0; // max number of responses
+ lCurQryCnt = 0; // current number, this query + = moving fwd
+ // - = moving backwards
+
+ #ifdef XB_INDEX_SUPPORT
+ pIx = NULL; // if index is set, the class uses the index tag, otherwise table
+ vpTag = NULL;
+ #endif // XB_INDEX_SUPPORT
+
+}
+/************************************************************************/
+xbFilter::~xbFilter() {
+ if( exp )
+ delete exp;
+}
+/************************************************************************/
+xbInt32 xbFilter::GetLimit() const {
+ return lLimit;
+}
+/************************************************************************/
+xbInt32 xbFilter::GetQryCnt() const {
+ return lCurQryCnt;
+}
+/************************************************************************/
+void xbFilter::SetLimit( xbInt32 lLimit ){
+ this->lLimit = lLimit;
+}
+/************************************************************************/
+void xbFilter::ResetQryCnt(){
+ this->lCurQryCnt = 0;
+}
+
+/************************************************************************/
+xbInt16 xbFilter::Set( const char *sFilter ) {
+ xbString sFilt( sFilter );
+ return Set( sFilt );
+}
+
+/************************************************************************/
+xbInt16 xbFilter::Set( xbString &sFilter ) {
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( exp )
+ delete exp;
+
+ exp = new xbExp( dbf->GetXbasePtr(), dbf );
+ if(( iRc = exp->ParseExpression( sFilter.Str() )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if( exp->GetReturnType() != XB_EXP_LOGICAL ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_EXPRESSION;
+ delete exp;
+ exp = NULL;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::SetExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetFirstRecord( xbInt16 iOption ) {
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ lCurQryCnt = 0;
+ if(( iRc = dbf->GetFirstRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EMPTY || iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetNextRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF ){
+ return iRc;
+ } else {
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt++;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetFirstRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetNextRecord( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if( lLimit != 0 && abs( lCurQryCnt ) >= lLimit )
+ return XB_LIMIT_REACHED;
+
+ if(( iRc = dbf->GetNextRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetNextRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF ){
+ return iRc;
+ } else {
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt++;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetNextRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetPrevRecord( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if( lLimit != 0 && abs( lCurQryCnt ) >= lLimit )
+ return XB_LIMIT_REACHED;
+
+ if(( iRc = dbf->GetPrevRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetPrevRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF ){
+ return iRc;
+ } else {
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt--;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetPrevRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetLastRecord( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ lCurQryCnt = 0;
+ if(( iRc = dbf->GetLastRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetPrevRecord( iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF ){
+ return iRc;
+ } else {
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt--;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetLastRecord() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+
+#ifdef XB_INDEX_SUPPORT
+
+/************************************************************************/
+xbInt16 xbFilter::GetFirstRecordIx( xbInt16 iOption ) {
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ lCurQryCnt = 0;
+ if(( iRc = dbf->GetCurIx()->GetFirstKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EMPTY || iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( !bFound ){
+ // if(( iRc = pIx->GetNextKey( vpTag, iOption )) != XB_NO_ERROR ){
+ if(( iRc = dbf->GetCurIx()->GetNextKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF ){
+ return iRc;
+ } else {
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt++;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetFirstRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetNextRecordIx( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if( lLimit != 0 && abs( lCurQryCnt ) >= lLimit )
+ return XB_LIMIT_REACHED;
+
+ if(( iRc = dbf->GetCurIx()->GetNextKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( !bFound ){
+ // if(( iRc = pIx->GetNextKey( vpTag, iOption )) != XB_NO_ERROR ){
+ if(( iRc = dbf->GetCurIx()->GetNextKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF ){
+ return iRc;
+ } else {
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt++;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetNextRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetPrevRecordIx( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if( lLimit != 0 && abs( lCurQryCnt ) >= lLimit )
+ return XB_LIMIT_REACHED;
+
+ if(( iRc = dbf->GetCurIx()->GetPrevKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( !bFound ){
+ //if(( iRc = pIx->GetPrevKey( vpTag, iOption )) != XB_NO_ERROR ){
+ if(( iRc = dbf->GetCurIx()->GetPrevKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF ){
+ return iRc;
+ } else {
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt--;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetPrevRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/************************************************************************/
+xbInt16 xbFilter::GetLastRecordIx( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ if( !exp ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ lCurQryCnt = 0;
+ if(( iRc = dbf->GetCurIx()->GetLastKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_EOF )
+ return iRc;
+ else{
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ xbBool bFound = xbFalse;
+ while( !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = exp->ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = exp->GetBoolResult( bFound )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( !bFound ){
+ if(( iRc = dbf->GetCurIx()->GetPrevKey( dbf->GetCurTag(), iOption )) != XB_NO_ERROR ){
+ if( iRc == XB_BOF ){
+ return iRc;
+ } else {
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ lCurQryCnt--;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbFilter::GetLastRecordIx() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ dbf->GetXbasePtr()->WriteLogMessage( sMsg.Str() );
+ dbf->GetXbasePtr()->WriteLogMessage( dbf->GetXbasePtr()->GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+
+
+/************************************************************************/
+} /* namespace */
+#endif /* XB_FILTER_SUPPORT */ \ No newline at end of file
diff --git a/src/core/xbfuncs.cpp b/src/core/xbfuncs.cpp
new file mode 100755
index 0000000..f127211
--- /dev/null
+++ b/src/core/xbfuncs.cpp
@@ -0,0 +1,851 @@
+/* xbfuncs.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2017,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_FUNCTION_SUPPORT
+
+namespace xb{
+
+
+// All funtions have a similar structure, return an xbInt16 return code
+// Have a variable number of input operands and one output operand
+
+/************************************************************************/
+//! @brief Calculate absolute value of a numeric expression.
+/*!
+ Expression function ABS().
+ \param dIn Input - Numeric expression.
+ \param dOut Output - Absolute value.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbXBase::ABS( xbDouble dIn, xbDouble &dOut ){
+ if( dIn < 0 )
+ dOut = dIn * -1;
+ else
+ dOut = dIn;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Trim leading and trailing white space from a string.
+/*!
+ Expression function ALLTRIM().
+ \param sIn Input - Input string to trim.
+ \param sOut Output - Trimmed string.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbXBase::ALLTRIM( const xbString &sIn, xbString &sOut ){
+ sOut = sIn;
+ sOut.Trim();
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return ASCII code for the first character in a string.
+/*!
+ Expression function ASC().
+ \param sIn Input - Input character string.
+ \param dAscOut Output - Ascii code of first character in string.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::ASC( const xbString &sIn, xbDouble &dAscOut ){
+ if( sIn == "" )
+ return XB_PARSE_ERROR;
+
+ dAscOut = sIn[1];
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Return number indicating starting position of string within a string.
+/*!
+ Expression function AT().
+ \param s1 Input - Input string to search for.
+ \param s2 Input - Input string to search.
+ \param dPos Output - Position of string s1 within s2.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::AT( const xbString &s1, const xbString &s2, xbDouble &dPos ){
+ /* looks for s1 in s2 */
+ xbInt32 lCnt = 0;
+ const char *p;
+ const char *p2 = s2;
+ if( strlen( s1 ) > strlen( s2 )) return 0;
+ if(( p = strstr( s2, s1 )) == NULL )
+ return XB_NO_ERROR;
+ while( p2++ != p ) lCnt++;
+ dPos = lCnt + 1;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return character weekday name for date.
+/*!
+ Expression function CDOW().
+ \param dInDate Input - Input date.
+ \param sOutDow Output - Character day of week.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::CDOW( xbDate &dInDate, xbString &sOutDow ){
+ return dInDate.CharDayOf( sOutDow );
+}
+
+/*************************************************************************/
+//! @brief Convert numeric expression to a character.
+/*!
+ Expression function CHR().
+ \param dAsciCd Input - Numeric expression.
+ \param sOut Output - Character result.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::CHR( xbDouble dAsciCd, xbString &sOut ){
+ static char buf[2];
+ buf[0] = (char) dAsciCd;
+ buf[1] = 0x00;
+ sOut = buf;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return character month name for date.
+/*!
+ Expression function CMONTH().
+ \param dInDate Input - Input date.
+ \param sOutMonth Output - Character month.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::CMONTH( xbDate &dInDate, xbString &sOutMonth ){
+ return dInDate.CharMonthOf( sOutMonth );
+}
+/*************************************************************************/
+//! @brief Return date from character input date.
+/*!
+ Expression function CTOD().
+ \param sInDate Input - Input date in MM/DD/YY format.
+ \param dOutDate Output - Output date.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::CTOD( const xbString &sInDate, xbDate &dOutDate ){
+ return dOutDate.CTOD( sInDate );
+}
+/*************************************************************************/
+//! @brief Return system date.
+/*!
+ Expression function DATE().
+ \param dOutDate Output - Output date.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::DATE( xbDate &dOutDate ){
+ return dOutDate.Sysdate();
+}
+/*************************************************************************/
+//! @brief Return the day of the month from a date.
+/*!
+ Expression function DAY().
+ \param dInDate Input - Input date.
+ \param dOutDay Output - Output day of month.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::DAY( const xbDate &dInDate, xbDouble &dOutDay ){
+ xbInt16 iOutDay;;
+ iOutDay = dInDate.DayOf( XB_FMT_MONTH );
+ if( iOutDay < 0 ){
+ return iOutDay;
+ }else{
+ dOutDay = iOutDay;
+ return XB_NO_ERROR;
+ }
+}
+/*************************************************************************/
+//! @brief Return record deletion status for current record.
+/*!
+ Expression function DEL().
+ \param dbf Input - Table to check record deletion status.
+ \param iRecBufSw Input - Which buffer. 0 = Current record buffer, 1 = Original record buffer
+ \param sOut Output - "*" if record is deleted, otherise space.
+ \returns XB_NO_ERROR<br>XB_PARSE_ERROR.
+
+*/
+xbInt16 xbXBase::DEL( xbDbf *dbf , xbString &sOut, xbInt16 iRecBufSw ) {
+
+ if( dbf ){
+ if( dbf->RecordDeleted( iRecBufSw ))
+ sOut = "*";
+ else
+ sOut = " ";
+ return XB_NO_ERROR;
+ } else {
+ return XB_PARSE_ERROR;
+ }
+}
+
+/*************************************************************************/
+//! @brief Return record deletion status for current record.
+/*!
+ Expression function DELETED().
+ \param dbf Input - Table to check record deletion status for.
+ \param iRecBufSw Input - Which buffer. 0 = Current record buffer, 1 = Original record buffer
+ \param bOut Output - xbTrue if record is deleted.<br>xbFalse if record is not deleted.
+ \returns XB_NO_ERROR<br>XB_PARSE_ERROR.
+*/
+xbInt16 xbXBase::DELETED( xbDbf *dbf , xbBool &bOut, xbInt16 iRecBufSw ) {
+
+ if( dbf ){
+ bOut = dbf->RecordDeleted( iRecBufSw );
+ return XB_NO_ERROR;
+ } else {
+ return XB_PARSE_ERROR;
+ }
+}
+
+/*************************************************************************/
+//! @brief Clipper DESCEND function.
+/*!
+ Expression function DESCEND().
+ \param dtInDate Input - Input date.
+ \param dtOutDate Output - Output date.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbXBase::DESCEND( const xbDate &dtInDate, xbDate &dtOutDate ){
+ xbDate d( "29991231" );
+ dtOutDate.JulToDate8( 2415021 + d.JulianDays() - dtInDate.JulianDays());
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Clipper DESCEND function.
+/*!
+ Expression function DESEND().
+ \param dIn Input - Input number.
+ \param dOut Output - Output number.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbXBase::DESCEND( xbDouble dIn, xbDouble &dOut ){
+ dOut = dIn * -1;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Clipper DESCEND function.
+/*!
+ Expression function DESEND().
+ \param sIn Input - Input string.
+ \param sOut Output - Output string.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbXBase::DESCEND( const xbString &sIn, xbString &sOut ){
+
+ sOut = sIn;
+ for( xbUInt32 l = 0; l < sIn.Len(); l++ )
+ sOut.PutAt( l+1, (char) (255 - sOut[l+1]));
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return number of day of week.
+/*!
+ Expression function DOW().
+ \param dtInDate Input - Input date.
+ \param dDowOut Output - Output day of week.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::DOW( const xbDate &dtInDate, xbDouble &dDowOut ){
+ xbInt16 iDow;
+ iDow = dtInDate.DayOf( XB_FMT_WEEK );
+ if( iDow < 0 ){
+ return XB_PARSE_ERROR;
+ }else{
+ dDowOut = iDow;
+ return XB_NO_ERROR;
+ }
+}
+
+/*************************************************************************/
+//! @brief Return character date from input date.
+/*!
+ Expression function DTOC().
+ \param dInDate Input - Input date.
+ \param sOutFmtDate Output - Output date.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::DTOC( xbDate &dInDate, xbString &sOutFmtDate ){
+ return dInDate.FormatDate( "MM/DD/YY", sOutFmtDate );
+}
+/*************************************************************************/
+//! @brief Return char CCYYMMDD date from input date.
+/*!
+ Expression function DTOS().
+ \param dtInDate Input - Input date.
+ \param sOutFmtDate Output - Output date.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::DTOS( xbDate &dtInDate, xbString &sOutFmtDate ){
+ return dtInDate.FormatDate( "YYYYMMDD", sOutFmtDate );
+}
+/*************************************************************************/
+//! @brief Return exponent value.
+/*!
+ Expression function EXP().
+ This function returns e**x where e is approximately 2.71828 and x is dIn.
+
+ \param dIn Input - exp value.
+ \param dOut Output - value.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::EXP( xbDouble dIn, xbDouble &dOut )
+{
+ dOut = exp( dIn );
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Immediate if.
+/*!
+ Expression function IIF().
+ \param bResult Input - boolean expression.
+ \param sTrueResult Input - value if boolean expression is true.
+ \param sFalseResult Input - value if boolean expression is false.
+ \param sResult Output - sTrueResult or sFalseResult depending on bResultvalue.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::IIF( xbBool bResult, const xbString &sTrueResult, const xbString &sFalseResult, xbString &sResult )
+{
+ if( sFalseResult.Len() != sTrueResult.Len())
+ return XB_INCONSISTENT_PARM_LENS;
+ if( bResult )
+ sResult = sTrueResult;
+ else
+ sResult = sFalseResult;
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Convert number to integer, truncate any decimals.
+/*!
+ Expression function INT().
+ \param dIn Input - Input number.
+ \param dOut Output - integer.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbXBase::INT( xbDouble dIn, xbDouble &dOut )
+{
+ xbInt64 ll = (xbInt64) dIn;
+ dOut = (xbDouble) ll;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Check if string begins with alpha character.
+/*!
+ Expression function ISALPHA().
+ \param sIn Input - Input string.
+ \param bResult Output - xbTrue if string begins with alpha character.<br>xbFalse if string does not begin with alpha character.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::ISALPHA( const xbString &sIn, xbBool &bResult ){
+ if( isalpha(sIn[1]))
+ bResult = 1;
+ else
+ bResult = 0;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Check if string begins with lower case alpha character.
+/*!
+ Expression function ISLOWER().
+ \param sIn Input - Input string.
+ \param bResult Output - xbTrue if string begins with lower case alpha character.<br>
+ xbFalse if string does not begin with lower case alpha character.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbXBase::ISLOWER( const xbString &sIn, xbBool &bResult ){
+ if( islower(sIn[1]))
+ bResult = 1;
+ else
+ bResult = 0;
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Check if string begins with upper case alpha character.
+/*!
+ Expression function ISUPPER().
+ \param sIn Input - Input string.
+ \param bResult Output - xbTrue if string begins with upper case alpha character.<br>
+ xbFalse if string does not begin with upper case alpha character.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbXBase::ISUPPER( const xbString &sIn, xbBool &bResult ){
+ if( isupper(sIn[1]))
+ bResult = 1;
+ else
+ bResult = 0;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return left characters from string.
+/*!
+ Expression function LEFT().
+ \param sIn Input - string.
+ \param ulCharCnt Input - number of characters to extract from string.
+ \param sOut Output - resultant string.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::LEFT( const xbString &sIn, xbUInt32 ulCharCnt, xbString &sOut ){
+ sOut.Assign( sIn, 1, ulCharCnt );
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return length of string.
+/*!
+ Expression function LEN().
+ \param sIn Input - Input string.
+ \param dOut Output - string length.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbXBase::LEN( const xbString &sIn, xbDouble &dOut ){
+ dOut = sIn.Len();
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Calculate logarithm.
+/*!
+ Expression function LOG().
+ \param dIn Input - numeric expression.
+ \param dOut Output - numeric log value.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::LOG( xbDouble dIn, xbDouble &dOut ){
+ dOut = log( dIn );
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Trim left side of string.
+/*!
+ Expression function LTRIM().
+ \param sIn Input - string.
+ \param sOut Output - string result.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::LTRIM( const xbString &sIn, xbString &sOut ){
+ sOut = sIn;
+ sOut.Ltrim();
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Convert upper case to lower case.
+/*!
+ Expression function LOWER().
+ \param sIn Input - string.
+ \param sOut Output - string result.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbXBase::LOWER( const xbString &sIn, xbString &sOut ){
+ sOut = sIn;
+ sOut.ToLowerCase();
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return higher of two values.
+/*!
+ Expression function MAX().
+ \param d1 Input - Numeric value 1.
+ \param d2 Input - Numeric value 2.
+ \param dOut Output - Higher of d1 or d2.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::MAX( xbDouble d1, xbDouble d2, xbDouble &dOut )
+{
+ if( d1 > d2 )
+ dOut = d1;
+ else
+ dOut = d2;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return lessor of two values.
+/*!
+ Expression function MIN().
+ \param d1 Input - Numeric value 1.
+ \param d2 Input - Numeric value 2.
+ \param dOut Output - Lessor of d1 or d2.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::MIN( xbDouble d1, xbDouble d2, xbDouble &dOut )
+{
+ if( d1 < d2 )
+ dOut = d1;
+ else
+ dOut = d2;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return number of month for a given date.
+/*!
+ Expression function MONTH().
+ \param dInDate Input date.
+ \param dOutMonth - Month number.
+ \returns XB_NO_ERROR.<br>XB_PARSE_ERROR.
+*/
+xbInt16 xbXBase::MONTH( xbDate &dInDate, xbDouble &dOutMonth ){
+
+ xbInt16 iRc = dInDate.MonthOf();
+ if( iRc < 0 )
+ return XB_PARSE_ERROR;
+ else{
+ dOutMonth = iRc;
+ return XB_NO_ERROR;
+ }
+}
+
+/*************************************************************************/
+//! @brief Return number of records in a given table.
+/*!
+ Expression function RECCOUNT().
+ \param dbf - Table.
+ \param dOut - Number of records.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::RECCOUNT( xbDbf *dbf , xbDouble &dOut ) {
+
+ xbUInt32 ulRecCnt;
+ xbInt16 iRc = dbf->GetRecordCnt( ulRecCnt );
+ dOut = (xbDouble) ulRecCnt;
+ return iRc;
+
+}
+
+/*************************************************************************/
+//! @brief Return current record number for a given table.
+/*!
+ Expression function RECNO().
+ \param dbf - Table.
+ \param dOut - Record number.
+ \returns XB_NO_ERROR.<br>XB_PARSE_ERROR.
+*/
+
+xbInt16 xbXBase::RECNO( xbDbf *dbf , xbDouble &dOut ) {
+ if( dbf ){
+ dOut = (xbDouble) dbf->GetCurRecNo();
+ return XB_NO_ERROR;
+ } else {
+ dOut = -1;
+ return XB_PARSE_ERROR;
+ }
+}
+
+/*************************************************************************/
+//! @brief Repeat character expression N times.
+/*!
+ Expression function REPLICATE().
+ \param sIn Inout - String to replicate.
+ \param ulRepCnt Input - Number of times to repeat.
+ \param sOut Output - String result.
+ \returns XB_NO_ERROR.<br>XB_PARSE_ERROR.
+*/
+xbInt16 xbXBase::REPLICATE( const xbString &sIn, xbUInt32 ulRepCnt, xbString &sOut ){
+ sOut = "";
+ for( xbUInt32 i = 0; i < ulRepCnt; i++ )
+ sOut += sIn;
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Return right characters from string.
+/*!
+ Expression function RIGHT().
+ \param sIn Input - string.
+ \param ulCharCnt Input - number of characters to extract from string.
+ \param sOut Output - resultant string.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbXBase::RIGHT( const xbString &sIn, xbUInt32 ulCharCnt, xbString &sOut ){
+ if( sIn.Len() < ulCharCnt )
+ sOut = sIn;
+ else
+ sOut.Assign( sIn, sIn.Len() - ulCharCnt + 1, ulCharCnt );
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Trim right side of string.
+/*!
+ Expression function RTRIM().
+ \param sIn Input - string.
+ \param sOut Output - string result.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbXBase::RTRIM( const xbString &sIn, xbString &sOut ){
+ sOut = sIn;
+ sOut.Rtrim();
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Generate a string of N spaces.
+/*!
+ Expression function SPACE().
+ \param lCnt Input - Number of spaces.
+ \param sOut Output - Output string consisting of specified number of spaces.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbXBase::SPACE( xbInt32 lCnt, xbString &sOut ){
+ sOut = "";
+ for( xbInt32 i = 0; i < lCnt; i++ )
+ sOut += ' ';
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Calculate a square root.
+/*!
+ Expression function SQRT().
+ \param dBase Input - Base number.
+ \param dSqrRoot Output - Square root.
+ \returns XB_NO_ERROR.
+*/
+
+xbInt16 xbXBase::SQRT( xbDouble dBase, xbDouble &dSqrRoot )
+{
+ dSqrRoot = sqrt( dBase );
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Converts a valid 8 byte (CCYYMMDD) input date into a date class.
+/*!
+ Expression function STOD().
+ \param sInDate Input - Input date.
+ \param dtOutDate Output - Output date.
+ \returns XB_NO_ERROR.<br>XB_INVALID_DATE.
+*/
+
+xbInt16 xbXBase::STOD( const xbString &sInDate, xbDate &dtOutDate ){
+
+ if( dtOutDate.DateIsValid( sInDate )){
+ dtOutDate = sInDate;
+ return XB_NO_ERROR;
+ } else {
+ return XB_INVALID_DATE;
+ }
+}
+
+/*************************************************************************/
+//! @brief Convert number to a character string.
+/*!
+ Expression function STR().
+ \param dIn Input - Number.
+ \param sOut Output - String.
+ \returns XB_NO_ERROR.
+*/
+
+xbInt16 xbXBase::STR( xbDouble dIn, xbString &sOut) {
+ xbString sPadChar = " ";
+ return STR( dIn, 10, 0, sPadChar, sOut );
+}
+
+/*************************************************************************/
+//! @brief Convert number to a character string.
+/*!
+ Expression function STR().
+ \param dIn Input - Number.
+ \param ulLen Input - Length.
+ \param sOut Output - String.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbString &sOut) {
+ xbString sPadChar = " ";
+ return STR( dIn, ulLen, 0, sPadChar, sOut );
+}
+
+/*************************************************************************/
+//! @brief Convert number to a character string.
+/*!
+ Expression function STR().
+ \param dIn Input - Number.
+ \param ulLen Input - Length.
+ \param ulDec Input - Number of decimals.
+ \param sOut Output - String.
+ \returns XB_NO_ERROR.
+*/
+
+xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sOut) {
+ xbString sPadChar = " ";
+ return STR( dIn, ulLen, ulDec, sPadChar, sOut );
+}
+
+/*************************************************************************/
+//! @brief Convert number to a character string.
+/*!
+ Expression function STR().
+ \param dIn Input - Number.
+ \param ulLen Input - Length.
+ \param ulDec Input - Number of decimals.
+ \param sPadChar Input - Left pad character, typically zero or space.
+ \param sOut Output - String.
+ \returns XB_NO_ERROR.
+*/
+
+xbInt16 xbXBase::STR( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sPadChar, xbString &sOut) {
+
+ xbString sFmt;
+ sFmt.Sprintf( "%c%d.%df", '%', ulLen, ulDec );
+ sOut.Sprintf( sFmt.Str(), dIn, 0 );
+
+ // convert to all "*" if result is too long
+ if( sOut.Len() > ulLen ){
+ sOut = "*";
+ do{
+ sOut += "*";
+ } while( ulLen > sOut.Len());
+ } else if( sPadChar.Len() > 0 && sPadChar != " " ){
+ // this logic doesn't make sense when processing negative numbers,
+ // but it does behave like the original dbase
+ // you could end up with something like 0000-12.17 when you really want -000012.17
+ // that is probably why the STRZERO function came into being
+ xbUInt32 l = 1;
+ while( sOut[l] == ' ' ){
+ sOut.PutAt( l, sPadChar[1]);
+ l++;
+ }
+ }
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Convert number to a character string with leading zeroes.
+/*!
+ Expression function STRZERO().
+ \param dIn Input - Number.
+ \param ulLen Input - Length.
+ \param ulDec Input - Number of decimals.
+ \param sOut Output - String.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbXBase::STRZERO( xbDouble dIn, xbUInt32 ulLen, xbUInt32 ulDec, xbString &sOut){
+
+ xbString sFmt;
+ if( dIn < 0 )
+ sFmt.Sprintf( "%c+0%d.%df", '%', ulLen, ulDec );
+ else
+ sFmt.Sprintf( "%c0%d.%df", '%', ulLen, ulDec );
+ sOut.Sprintf( sFmt.Str(), dIn );
+
+ // convert to all "*" if result is too long
+ if( sOut.Len() > ulLen ){
+ sOut = "*";
+ do{
+ sOut += "*";
+ } while( ulLen > sOut.Len());
+ }
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Extract a portion of a string from another string.
+/*!
+ Expression function SUBSTR().
+ \param sIn Input - Source string.
+ \param ulStartPos Input - Starting position for string extraction.
+ \param ulLen Input - Number of characters to extract.
+ \param sOut Output - String.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbXBase::SUBSTR( const xbString &sIn, xbUInt32 ulStartPos, xbUInt32 ulLen, xbString &sOut ){
+ sOut = sIn;
+ sOut.Mid( ulStartPos, ulLen );
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Trim left and right sides of string.
+/*!
+ Expression function TRIM().
+ \param sIn Input - string.
+ \param sOut Output string result.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::TRIM( const xbString &sIn, xbString &sOut ){
+ return RTRIM( sIn, sOut );
+}
+
+/*************************************************************************/
+//! @brief Convert lower case to upper case.
+/*!
+ Expression function UPPER().
+ \param sIn Input - string.
+ \param sOut Output - string result.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbXBase::UPPER( const xbString &sIn, xbString &sOut ){
+ sOut = sIn;
+ sOut.ToUpperCase();
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Convert numeric characters to number.
+/*!
+ Expression function VAL().
+ \param sIn Input - string.
+ \param dOut Output - numeric result.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbXBase::VAL( const xbString &sIn, xbDouble &dOut )
+{
+ if( sIn )
+ // strtod(nptr,NULL);
+ dOut = atof( sIn );
+ else
+ dOut = 0;
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! @brief Return year for a given date.
+/*!
+ Expression function YEAR().
+ \param dInDate Input date.
+ \param dOutYear - Year.
+ \returns XB_NO_ERROR.<br>XB_PARSE_ERROR.
+*/
+
+xbInt16 xbXBase::YEAR( xbDate &dInDate, xbDouble &dOutYear ){
+
+ xbInt16 iRc = dInDate.YearOf();
+ if( iRc < 0 )
+ return XB_PARSE_ERROR;
+ else{
+ dOutYear = iRc;
+ return XB_NO_ERROR;
+ }
+}
+/*************************************************************************/
+}; // namespace
+#endif // XB_FUNCTION_SUPPORT
+/*************************************************************************/
diff --git a/src/core/xbixbase.cpp b/src/core/xbixbase.cpp
new file mode 100755
index 0000000..e2f929c
--- /dev/null
+++ b/src/core/xbixbase.cpp
@@ -0,0 +1,789 @@
+/* xbixbase.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+ Base index class
+
+*/
+
+#include "xbase.h"
+#ifdef XB_INDEX_SUPPORT
+
+namespace xb{
+
+/***********************************************************************/
+//! @brief Class constructor.
+/*!
+ /param dbf Pointer to dbf instance.
+*/
+xbIx::xbIx( xbDbf *dbf ) : xbFile( dbf->GetXbasePtr()) {
+ this->dbf = dbf;
+ vpCurTag = NULL;
+ cNodeBuf = NULL;
+ bLocked = xbFalse;
+}
+/***********************************************************************/
+//! @brief Class Destructor.
+xbIx::~xbIx(){}
+
+
+/***********************************************************************/
+//! @brief Add Keys for record number
+/*!
+ For a given a record number, add keys to each tag in the index file
+ if it was updated
+
+ \param ulRecNo Record number to add keys for
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIx::AddKeys( xbUInt32 ulRecNo ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 i = 0;
+ xbInt16 iKeySts;
+
+ try{
+ void * vpTag;
+ xbInt16 iTagCount = GetTagCount();
+
+ for( i = 0; i < iTagCount; i++ ){
+ vpTag = GetTag( i );
+ iKeySts = GetKeySts( vpTag );
+
+ if( iKeySts == 1 || iKeySts == 2 ){
+ if(( iRc = UpdateTagKey( 'A', vpTag, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIx::AddKeys() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Allocate memory for index node.
+/*!
+ Allocate an index node.
+
+ \param ulBufSize Size of buffer to allocate
+ \returns null on error<br>Pointer to newly allocated xbIxNode on success
+*/
+xbIxNode * xbIx::AllocateIxNode( xbUInt32 ulBufSize, xbInt16 )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbIxNode * pNode = (xbIxNode *) calloc( 1, sizeof( xbIxNode ));
+ if( pNode == NULL ){
+ iErrorStop = 100;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if( ulBufSize == 0 )
+ ulBufSize = GetBlockSize();
+
+ pNode->ulBufSize = ulBufSize;
+ pNode->cpBlockData = (char *) calloc( 1, ulBufSize );
+ if( pNode->cpBlockData == NULL ){
+ free( pNode );
+ iErrorStop = 110;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ return pNode;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIx::AllocateIxNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return NULL;
+}
+
+
+/***********************************************************************/
+//! @brief Binary search for given value on an index node.
+/*!
+
+ Binary search for key lookups
+ \param cKeyType Key type
+ \param npNode Pointer to index node for search
+ \param lKeyItemLen Lenth of key plus pointer values
+ \param vpKey Pointer to key value
+ \param lSearchKeyLen length of key to search
+ \param iCompRc output return code from the CompareKey routine. CompareKey returns an
+ integer value less than, equal to or greater than zero in when comparing values
+
+ \param bDescending xbTrue for descending index key lookup.<br>
+ xbFalse for ascending index key lookup.
+ \return The position in the node the key was found, if multiples it returns the first occurrence.
+ If the key is not found, it returns the slot it should be in.
+*/
+
+xbInt16 xbIx::BSearchBlock( char cKeyType, xbIxNode *npNode, xbInt32 lKeyItemLen, const void *vpKey,
+ xbInt32 lSearchKeyLen, xbInt16 &iCompRc, xbBool bDescending ) const {
+ xbInt32 lLo = 0;
+ xbInt32 lHi = 0;
+ xbInt32 lMid = 0;
+ xbInt32 lKeyCnt = GetKeyCount( npNode );
+
+
+ if( !bDescending ){
+ lHi = lKeyCnt - 1;
+
+ while( lLo <= lHi ){
+ lMid = (lLo + lHi) / 2;
+ iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen );
+ if( iCompRc > 0 )
+ lHi = lMid - 1;
+ else if( iCompRc < 0 )
+ lLo = lMid + 1;
+ else{ // found match, look for leftmost occurrence
+
+ xbInt32 lFoundPos = lMid;
+ lMid--;
+ while( lMid >= 0 && CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ) == 0 ){
+ lFoundPos = lMid;
+ lMid--;
+ }
+ iCompRc = 0;
+ lLo = lFoundPos;
+ lHi = -1;
+ }
+ }
+ // update the compare key results
+ if( lMid != lLo ){
+ if( lLo >= lKeyCnt )
+ iCompRc = 1;
+ else
+ iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lLo, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen );
+ }
+ return (xbInt16) lLo;
+
+ } else { // descending key
+
+ lLo = lKeyCnt - 1;
+ while( lLo >= lHi && lHi != -1 ){
+ lMid = (lLo + lHi) / 2;
+ iCompRc = CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen );
+
+ if( iCompRc > 0 ) {
+ lHi = lMid + 1;
+ }
+ else if( iCompRc < 0) {
+ lLo = lMid - 1;
+ }
+ else{ // found match, look for leftmost occurrence
+
+ xbInt32 lFoundPos = lMid;
+ lMid--;
+ while( lMid >= 0 && CompareKey( cKeyType, GetKeyData( npNode, lMid, lKeyItemLen ), vpKey, (size_t) lSearchKeyLen ) == 0 ){
+ lFoundPos = lMid;
+ lMid--;
+ }
+ iCompRc = 0;
+ lHi = lFoundPos;
+ lLo = -1;
+ }
+ }
+
+ // std::cout << "BSB1 lo = " << lLo << " mid = " << lMid << " hi = " << lHi << " keycnt = " << lKeyCnt << " iCompRc = " << iCompRc << "\n"; // key=" << (char *) vpKey << "\n";
+ if( lLo < 0 && iCompRc < 0 )
+ iCompRc = 1;
+ else if( iCompRc != 0 ) {
+ iCompRc = CompareKey( cKeyType, GetKeyData( npNode, (lLo < 0 ? 0 : lLo), lKeyItemLen ), vpKey, (size_t) lSearchKeyLen );
+ }
+ // std::cout << "BSB2 lo = " << lLo << " mid = " << lMid << " hi = " << lHi << " keycnt = " << lKeyCnt << " iCompRc = " << iCompRc << "\n"; // key=" << (char *) vpKey << "\n";
+ return (xbInt16) lHi;
+ }
+
+ // should never get here
+ // return (xbInt16) 0;
+}
+
+/***********************************************************************/
+//! @brief Check for duplicate keys.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::CheckForDupKeys(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 i = 0;
+
+ try{
+ void * vpTag;
+ xbInt16 iTagCount = GetTagCount();
+ for( i = 0; i < iTagCount; i++ ){
+ vpTag = GetTag( i );
+ if(( iRc = CheckForDupKey( vpTag )) < XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ if( iRc != XB_KEY_NOT_UNIQUE ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxBase::CheckForDupKeys() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Close index file.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIx::Close(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if(( iRc = xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIx::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Compare keys.
+/*!
+ \param cKeyType C - Character compare.<br>
+ N - Numeric BCD compare.<br>
+ D - Numeric compare.<br>
+ F - Numeric compare.<br>
+ \param v1 Left compare.<br>v2 - Right Compare.
+ \param iSearchKeyLen Length of key compare.
+ \returns 1 - Left operand is greater then right operand.<br>
+ 0 - Left operand is equal to right operand.<br>
+ -1 - Left operand is less than right operand.
+*/
+inline xbInt16 xbIx::CompareKey( char cKeyType, const void *v1, const void *v2, size_t iSearchKeyLen ) const{
+ if( cKeyType == 'C' ){ // character compare
+ return memcmp( v1, v2, iSearchKeyLen );
+ } else if( cKeyType == 'N' ){ // numeric bcd compare, mdx bcd numeric indices
+ xbBcd bcdk1( v1 );
+ return bcdk1.Compare( v2 );
+ } else if( cKeyType == 'D' || cKeyType == 'F' ){ // julian date compare, ndx float numeric indices
+ xbDouble *d1 = (xbDouble *) v1;
+ xbDouble *d2 = (xbDouble *) v2;
+ if( *d1 < *d2 )
+ return -1;
+ else if( *d1 > *d2 )
+ return 1;
+ else
+ return 0;
+ } else {
+// std::cout << "Unhandled key type [" << cKeyType << "]\n";
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Create Keys for record number
+/*!
+ \param iOpt 0 Build a key for FindKey usage, only rec buf 0.<br>
+ 1 Append Mode, Create key for an append, only use rec buf 0, set updated switch.<br>
+ 2 Update Mode, Create old version and new version keys, check if different, set update switch appropriately.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::CreateKeys( xbInt16 iOpt ) {
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 i = 0;
+
+ try{
+ void * vpTag;
+ xbInt16 iTagCount = GetTagCount();
+
+ for( i = 0; i < iTagCount; i++ ){
+ vpTag = GetTag( i );
+ if(( iRc = CreateKey( vpTag, iOpt )) < XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIx::CreateKeys() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Delete keys for record number
+/*!
+ Delete keys to each tag in the index file if it was updated as determined
+ by CreateKeys function
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+//xbInt16 xbIx::DeleteKeys( xbUInt32 ulRecNo ){
+
+xbInt16 xbIx::DeleteKeys(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 i = 0;
+
+ try{
+ void * vpTag;
+ xbInt16 iTagCount = GetTagCount();
+
+ for( i = 0; i < iTagCount; i++ ){
+ vpTag = GetTag( i );
+ if( GetKeySts( vpTag ) > 1 ){ // 0 = no update 1 = add 2 = update, 3 = delete
+ if(( iRc = UpdateTagKey( 'D', vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIx::DeleteKeys() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! @brief Dump anode for debug purposes.
+/*!
+ \param pNode Pointer to node to dump.
+ \param iOption 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIx::DumpNode( void *, xbIxNode *pNode, xbInt16 iOption ) const
+{
+ xbString s;
+ s.Sprintf( "Dump Node Block=[%d] CurKey=[%d]", pNode->ulBlockNo, pNode->iCurKeyNo );
+ xbase->WriteLogMessage( s, iOption );
+
+ return XB_NO_ERROR;
+}
+#endif // XB_DEBUG_SUPPORT
+
+/***********************************************************************/
+//! @brief Find double key
+/*!
+ \param vpTag Pointer to tag to search.
+ \param dKey Double value to search for.
+ \param iRetrieveSw xbTrue - Retrieve the record if key found.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ){
+ return FindKey( vpTag, &dKey, 8, iRetrieveSw );
+}
+/***********************************************************************/
+//! @brief Find string key
+/*!
+ \param vpTag Pointer to tag to search.
+ \param sKey String data to search for.
+ \param iRetrieveSw xbTrue - Retrieve the record if key found.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::FindKey( void *vpTag, const xbString &sKey, xbInt16 iRetrieveSw ){
+ return FindKey( vpTag, sKey.Str(), (xbInt32) sKey.Len(), iRetrieveSw );
+}
+/***********************************************************************/
+//! @brief Find character key
+/*!
+ \param vpTag Pointer to tag to search.
+ \param cKey String data to search for.
+ \param lKeyLen Length of key to search for.
+ \param iRetrieveSw xbTrue - Retrieve the record if key found.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::FindKey( void *vpTag, const char *cKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ){
+ return FindKey( vpTag, (void *) cKey, lKeyLen, iRetrieveSw );
+}
+/***********************************************************************/
+//! @brief Find bcd key
+/*!
+ \param vpTag Pointer to tag to search.
+ \param bcd BCD data to search for.
+ \param iRetrieveSw xbTrue - Retrieve the record if key found.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::FindKey( void *vpTag, const xbBcd &bcd, xbInt16 iRetrieveSw ){
+ return FindKey( vpTag, bcd.GetBcd(), 12, iRetrieveSw );
+}
+/***********************************************************************/
+//! @brief Find date key
+/*!
+ \param vpTag Pointer to tag to search.
+ \param dtKey Date data to search for.
+ \param iRetrieveSw xbTrue - Retrieve the record if key found.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::FindKey( void *vpTag, const xbDate &dtKey, xbInt16 iRetrieveSw ){
+ xbDouble d = (xbDouble) dtKey.JulianDays();
+ return FindKey( vpTag, &d, 8, iRetrieveSw );
+}
+/***********************************************************************/
+//! @brief Free all nodes in a linked list.
+/*!
+ \param np Pointer to first node in linked list to free.
+ \returns NULL.
+*/
+xbIxNode *xbIx::FreeNodeChain( xbIxNode *np ){
+
+ // routine returns NULL
+ if( np ){
+ // free memory for a given chain of nodes
+ xbIxNode * np2;
+
+ // Clear the previous node's next pointer
+ if( np->npPrev )
+ np->npPrev->npNext = NULL;
+
+ // Clear out the tree
+ while( np ){
+ np2 = np->npNext;
+ NodeFree( np );
+ np = NULL;
+ np = np2;
+ }
+ }
+ return NULL;
+}
+/***********************************************************************/
+//! @brief Read block for block number.
+/*!
+ Routine to read a node/block out of an index file and store in xbIxNode structure
+ \param vpTag Pointer to tag.
+ \param ulBlockNo Block number to read off disk.
+ \param iOpt
+ 0 = Node is read into block buffer, not added to the node chain<br>
+ 1 = Node is read into new xbIxNode, then added to the node chain, and sets CurNode with new node<br>
+ 2 = Node is read into new xbIxNode, not added to the node chain<br>
+ CurNode points to new node<br>
+ \param ulAddlBuf Additional buffer size added to memory
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbIx::GetBlock( void *vpTag, xbUInt32 ulBlockNo, xbInt16 iOpt, xbUInt32 ulAddlBuf ){
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbIxNode *np = NULL;
+ try{
+
+ if( !vpTag && iOpt == 1 ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // set target location of block read to read
+ char *cp;
+ if( iOpt == 0 )
+ cp = cNodeBuf;
+ else{
+ if(( np = AllocateIxNode(GetBlockSize() + ulAddlBuf )) == NULL ){
+ iErrorStop = 110;
+ iRc = XB_NO_MEMORY;
+ throw( iRc );
+ }
+ cp = np->cpBlockData;
+ }
+ if(( iRc = ReadBlock( ulBlockNo, GetBlockSize(), cp )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if( iOpt == 0 )
+ return iRc;
+ np->ulBlockNo = ulBlockNo;
+ np->iCurKeyNo = 0;
+ if( iOpt == 1 )
+ AppendNodeChain( vpTag, np );
+ else if( iOpt == 2 ){
+ std::cout << "Future use stub. xbIxbase::GetBlock() option 2 not coded.\n";
+ iErrorStop = 130;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ // SetCurNode( vpTag, np );
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIx::GetBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( np ) NodeFree( np );
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Get pointer to current tag.
+/*!
+ \returns Pointer to current tag.
+*/
+
+void *xbIx::GetCurTag() const {
+ return vpCurTag;
+}
+/***********************************************************************/
+//! @brief Get pointer to dbf.
+/*!
+ \returns Pointer to dbf.
+*/
+xbDbf *xbIx::GetDbf() const {
+ return this->dbf;
+}
+/***********************************************************************/
+//! @brief Get the first key for the current tag.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetFirstKey(){
+ return GetFirstKey( vpCurTag, 0 );
+}
+
+/***********************************************************************/
+//! @brief Get the first key for a given tag.
+/*!
+ \param vpTag Tag for get first key operation.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetFirstKey( void *vpTag ){
+ return GetFirstKey( vpTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the last key for the current tag.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetLastKey(){
+ return GetLastKey( 0, vpCurTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the last key for a given tag.
+/*!
+ \param vpTag Tag for get last key operation.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetLastKey( void *vpTag ){
+ return GetLastKey( 0, vpTag, 0 );
+}
+
+/***********************************************************************/
+//! @brief Get the file lock status.
+/*!
+ \returns xbTrue - Index file is locked.<br>xbFalse - Index file is not locked.
+*/
+xbBool xbIx::GetLocked() const {
+ return bLocked;
+}
+
+/***********************************************************************/
+//! @brief Get the key count for number of keys on a node.
+/*!
+ \param np Given node for key count.
+ \returns Number of keys on the node.
+*/
+xbInt32 xbIx::GetKeyCount( xbIxNode *np ) const {
+ // assumes the first four bytes of the block is a four byte number
+ // representing the number of keys contained on the block
+ return eGetInt32( np->cpBlockData );
+}
+/***********************************************************************/
+//! @brief Get key data for a given key number.
+/*!
+ \param np Given node for key retrieval.
+ \param iKeyNo Which key to pull.
+ \param iKeyItemLen Length of key plus pointers.
+ \returns Pointer to a given key.
+*/
+char * xbIx::GetKeyData( xbIxNode *np, xbInt16 iKeyNo, xbInt16 iKeyItemLen ) const {
+ if( !np ) return NULL;
+ char *p = np->cpBlockData;
+ xbUInt32 ulKeyCnt = eGetUInt32( p );
+ if( iKeyNo < 0 || iKeyNo > (xbInt16) ulKeyCnt ) return NULL;
+ xbInt16 iOffset = 12 + (iKeyNo * iKeyItemLen);
+ p+=iOffset;
+ return p;
+}
+/***********************************************************************/
+//! @brief Get the next key for the current tag.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetNextKey(){
+ return GetNextKey( vpCurTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the next key for the given tag.
+/*!
+ \param vpTag Tag for next key operation.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetNextKey( void *vpTag ){
+ return GetNextKey( vpTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the prev key for the current tag.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetPrevKey(){
+ return GetPrevKey( vpCurTag, 0 );
+}
+/***********************************************************************/
+//! @brief Get the previous key for the given tag.
+/*!
+ \param vpTag Tag for previous key operation.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::GetPrevKey( void *vpTag ){
+ return GetPrevKey( vpTag, 0 );
+}
+/***********************************************************************/
+//! @brief Free an index node
+/*!
+ \param ixNode Pointer to index node to free.
+ \returns void
+*/
+void xbIx::NodeFree( xbIxNode *ixNode ){
+ if( ixNode ){
+ if( ixNode->cpBlockData ){
+ free( ixNode->cpBlockData );
+ ixNode->cpBlockData = NULL;
+ }
+ free( ixNode );
+ ixNode = NULL;
+ }
+}
+/***********************************************************************/
+//! @brief Open an index file.
+/*!
+ MDX files are opened automatically and don't need opened.
+ NDX files that are associated with the DBF file are opened automatically.
+
+ Non production indexes that haven't been opened will need to be opened to be used.
+ \param sFileName Index file name to open.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIx::Open( const xbString & sFileName ){
+
+ // There are no locking requirements when opening an NDX index
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ /* copy the file name to the class variable */
+ this->SetFileName( sFileName );
+
+ if( !FileExists()){
+ iErrorStop = 100;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ /* open the file */
+ if(( iRc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ if(( iRc = ReadHeadBlock()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ SetCurTag( (xbInt16) 0 ); // default the first tag as the current tag
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIx::Open( %s ) Exception Caught. Error Stop = [%d] iRc = [%d]", sFileName.Str(), iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Set the current tag.
+/*!
+ \param vpCurTag Pointer to tag to set as current.
+ \returns void
+*/
+void xbIx::SetCurTag( void *vpCurTag ){
+ this->vpCurTag = vpCurTag;
+}
+/***********************************************************************/
+//! @brief Set the dbf pointer.
+/*!
+ \param dbf Dbf pointer to set.
+ \returns void
+*/
+void xbIx::SetDbf( xbDbf *dbf ){
+ this->dbf = dbf;
+}
+/***********************************************************************/
+//! @brief Set the file lock status.
+/*!
+ \param bLocked xbTrue - Set to locked.<br>xbFalse - Set to unlocked.
+ \returns void
+*/
+void xbIx::SetLocked( xbBool bLocked ){
+ this->bLocked = bLocked;
+}
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_INDEX_SUPPORT */
diff --git a/src/core/xbixmdx.cpp b/src/core/xbixmdx.cpp
new file mode 100755
index 0000000..0eef64b
--- /dev/null
+++ b/src/core/xbixmdx.cpp
@@ -0,0 +1,4844 @@
+/* xbixmdx.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+
+ MDX indices are comprised of blocks and pages.
+ A page is 512 bytes.
+ A Block is one or more pages.
+ The default block size is two 512 byte pages per block.
+ Nodes are used for storing block images in memory
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_MDX_SUPPORT
+
+namespace xb{
+
+/***********************************************************************/
+xbIxMdx::xbIxMdx( xbDbf *dbf ) : xbIx( dbf ){
+ Init();
+}
+/***********************************************************************/
+//void xbIxMdx::Init( xbInt16 iOpt ){
+void xbIxMdx::Init( xbInt16 ){
+
+ cVersion = 0;
+ cCreateYY = 0;
+ cCreateMM = 0;
+ cCreateDD = 0;
+ sFileName = "";
+ iBlockFactor = 0;
+ cProdIxFlag = 0;
+ cTagEntryCnt = 0;
+ iTagLen = 0;
+ iTagUseCnt = 0;
+ cNextTag = 0;
+ c1B = 0x0b;
+ ulPageCnt = 0;
+ ulFirstFreePage = 0;
+ ulNoOfBlockAvail = 0;
+ cUpdateYY = 0;
+ cUpdateMM = 0;
+ cUpdateDD = 0;
+ mdxTagTbl = NULL;
+ cNodeBuf = NULL;
+ bReuseEmptyNodes = xbTrue;
+}
+/***********************************************************************/
+xbIxMdx::~xbIxMdx(){
+ if( cNodeBuf )
+ free( cNodeBuf );
+
+ if( FileIsOpen())
+ Close();
+}
+/***********************************************************************/
+//! @brief Add key.
+/*!
+ Add key. If this is a unique index, this logic assumes the duplicate
+ check logic was already done.
+
+ \param vpTag Tag to update.
+ \param ulRecNo Record number to add key for.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){
+
+ xbMdxTag * npTag = (xbMdxTag *) vpTag;
+ if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && npTag->bFoundSts )
+ return XB_NO_ERROR;
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iHeadNodeUpdateOpt = 2;
+ xbIxNode * npRightNode = NULL;
+ xbUInt32 ulNewRightChild = 0;
+
+ try{
+ if(( iRc = xbIxMdx::KeySetPosAdd( npTag, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ xbInt32 lKeyCnt = GetKeyCount( npTag->npCurNode );
+ if( lKeyCnt < npTag->iKeysPerBlock ){
+ // Section A - add key to appropriate position if space available
+ // std::cout << "AddKey Section A begin\n";
+ if(( iRc = InsertNodeL( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ } else {
+ // land here with a full leaf node
+ iHeadNodeUpdateOpt = 1;
+ npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor );
+ if( !npRightNode ){
+ iErrorStop = 120;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRightChild ){
+ ulNewRightChild = npRightNode->ulBlockNo * (xbUInt32) iBlockFactor;
+ }
+
+ if(( iRc = xbIxMdx::SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ xbUInt32 ulTempBlockNo = npRightNode->ulBlockNo;
+
+ // section C - go up the tree, splitting nodes as necessary
+ xbIxNode * npParent = npTag->npCurNode->npPrev;
+ while( npParent && GetKeyCount( npParent ) >= npTag->iKeysPerBlock ){
+ //std::cout << "Section C begin interior node is full\n";
+ npRightNode = FreeNodeChain( npRightNode );
+ npRightNode = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npParent->ulBlockNo * (xbUInt32) iBlockFactor );
+ //std::cout << "Section C - B new right node block number for interior node split= " << npRightNode->ulBlockNo << "\n";
+
+ if( !npRightNode ){
+ iErrorStop = 140;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ //std::cout << "Section C - going to split interior node C\n";
+
+ if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, BlockToPage( ulTempBlockNo ))) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ // std::cout << "Section C - interior node split \n";
+ ulTempBlockNo = npRightNode->ulBlockNo;
+ npTag->npCurNode = npParent;
+ npParent = npParent->npPrev;
+ }
+
+ // section D - if cur node is split root, create new root
+ if(( npTag->npCurNode->ulBlockNo * (xbUInt32) iBlockFactor ) == npTag->ulRootPage ){
+ if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if( npRightNode )
+ npRightNode = FreeNodeChain( npRightNode );
+
+ } else {
+
+ // std::cout << "Section E, put key in parent\n";
+ if(( iRc = InsertNodeI( (void *) vpTag, (xbIxNode *) npParent, (xbInt16) npParent->iCurKeyNo, BlockToPage( npRightNode->ulBlockNo ))) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ }
+
+ // update the header
+ if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ // if adding the first key, set the cHasKeys field
+ if( !npTag->cHasKeys ){
+ npTag->cHasKeys = 0x01;
+ if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 246), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( &npTag->cHasKeys, 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ }
+
+ if( ulNewRightChild > 0 ){
+
+ char cBuf[4];
+ ePutUInt32( cBuf, ulNewRightChild );
+ if(( iRc = xbFseek( ((npTag->ulTagHdrPageNo * 512) + 252), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( &cBuf, 4, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ npTag->ulRightChild = ulNewRightChild;
+ }
+
+ if( npRightNode )
+ npRightNode = FreeNodeChain( npRightNode );
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::AddKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+void xbIxMdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){
+ xbMdxTag * mdxTag = (xbMdxTag *) vpTag;
+ if( mdxTag->npNodeChain == NULL ){
+ mdxTag->npNodeChain = npNode;
+ mdxTag->npCurNode = npNode;
+ } else {
+ npNode->npPrev = mdxTag->npCurNode;
+ mdxTag->npCurNode->npNext = npNode;
+ mdxTag->npCurNode = npNode;
+ }
+ // time stamp the node chain
+ GetFileMtime( mdxTag->tNodeChainTs );
+}
+
+/***********************************************************************/
+//! @brief Add new root node.
+/*!
+ \param mpTag Tag to update.
+ \param npLeft Left node.
+ \param npRight Right node.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxMdx::AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char *pLastKey = NULL;
+
+ try{
+ xbIxNode *npRoot = AllocateIxNode( npTag, GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, npRight->ulBlockNo * (xbUInt32) iBlockFactor );
+ if( !npRoot ){
+ iErrorStop = 100;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ npTag->ulRootPage = npRoot->ulBlockNo;
+
+ pLastKey = (char *) malloc( (size_t) npTag->iKeyLen );
+ if(( iRc = GetLastKeyForBlockNo( npTag, npLeft->ulBlockNo, pLastKey )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ char * pTrg = npRoot->cpBlockData;
+
+ // set no of keys to 1
+ ePutUInt32( pTrg, 1 );
+
+ // set the left node number
+ pTrg += 8;
+ ePutUInt32( pTrg, npLeft->ulBlockNo * (xbUInt32) iBlockFactor );
+
+ // set the key
+ pTrg+= 4;
+ memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen );
+ pTrg+= npTag->iKeyItemLen - 4;
+ ePutUInt32( pTrg, npRight->ulBlockNo * (xbUInt32) iBlockFactor );
+
+ // write out the new block
+ if(( iRc = WriteBlock( npRoot->ulBlockNo, GetBlockSize(), npRoot->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ // write out the new root node number in the tag header
+ // position the file
+ xbUInt32 ulPagePos = npTag->ulTagHdrPageNo * 512;
+
+ // save the number to a buffer
+ char cBuf[4];
+ ePutUInt32( cBuf, npRoot->ulBlockNo * ((xbUInt32) iBlockFactor ));
+
+ if(( iRc = xbFseek( ulPagePos, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( &cBuf, 4, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if( pLastKey )
+ free( pLastKey );
+
+ npRoot = FreeNodeChain( npRoot );
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::AddKeyNewRoot() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( pLastKey )
+ free( pLastKey );
+ }
+ return iRc;
+}
+
+
+
+/***********************************************************************/
+//! @brief Allocate a node.
+/*!
+ \param mpTag Pointer to mdx tag
+ \param ulBufSize Buffer size.
+ \param ulBlock2 Value to load in ulBlock2 field, bytes 4-7 in the first page of the block
+ \returns Pointer to new node.
+*/
+xbIxNode * xbIxMdx::AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt32 ulBlock2 ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbIxNode *n = NULL;
+
+ try{
+ if(( n = xbIx::AllocateIxNode( ulBufSize )) == NULL ){
+ iRc = XB_NO_MEMORY;
+ iErrorStop = 100;
+ throw iRc;
+ }
+ char *p = n->cpBlockData;
+ p += 4;
+
+ if( ulFirstFreePage > 0 && bReuseEmptyNodes ){
+ // have an empty node we can reuse
+ n->ulBlockNo = PageToBlock( ulFirstFreePage );
+ if(( iRc = ReadBlock( n->ulBlockNo, GetBlockSize(), n->cpBlockData )) != XB_NO_ERROR ){
+ iRc = 110;
+ throw iRc;
+ }
+ // update ulFirstFreePage
+ ulFirstFreePage = eGetUInt32( p );
+ if(( iRc = xbFseek( 36, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( p, 4, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ // memset cpBlockData to zeroes
+ memset( n->cpBlockData, 0x00, GetBlockSize());
+
+ } else {
+ n->ulBlockNo = ulPageCnt / (xbUInt32) iBlockFactor;
+ ulPageCnt += (xbUInt32) iBlockFactor;
+ }
+
+ mpTag->ulTagSize += (xbUInt32) iBlockFactor;
+ if( ulBlock2 > 0 ){
+ ePutUInt32( p, ulBlock2 );
+ }
+ }
+ catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::AllocateIxNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( n )
+ n = FreeNodeChain( n );
+ }
+ return n;
+}
+/***********************************************************************/
+//! @brief Calculate B-tree pointers.
+/*!
+ Set binary tree pointer value. The MDX tags are stored with binary
+ tree positions. This routine calculates the value in memory.
+ \returns void
+*/
+
+void xbIxMdx::CalcBtreePointers(){
+
+ xbInt16 iaLeftChild[48];
+ xbInt16 iaRightChild[48];
+ xbInt16 iaParent[48];
+
+ for( xbInt16 i = 0; i < 48; i++ ){
+ iaLeftChild[i] = 0;
+ iaRightChild[i] = 0;
+ iaParent[i] = 0;
+ }
+
+ // anything to do?
+ if( iTagUseCnt > 1 ){
+ xbString sBaseTag;
+ xbString sThisTag;
+ xbString sWorkTag;
+ xbInt16 iWorkTagNo;
+ xbBool bDone;
+ sBaseTag = GetTagName( GetTag( 0 ));
+
+ for( xbInt16 iThisTagNo = 1; iThisTagNo < iTagUseCnt; iThisTagNo++ ){
+ iWorkTagNo = 0;
+ sWorkTag.Set( sBaseTag );
+ sThisTag = GetTagName( GetTag( iThisTagNo ));
+ bDone = xbFalse;
+ while( !bDone ){
+ if( sThisTag < sWorkTag ){
+ if( iaLeftChild[iWorkTagNo] == 0 ) {
+ iaLeftChild[iWorkTagNo] = iThisTagNo + 1;
+ iaParent[iThisTagNo] = iWorkTagNo + 1;
+ bDone = xbTrue;
+ } else {
+ iWorkTagNo = iaLeftChild[iWorkTagNo]-1;
+ sWorkTag = GetTagName( GetTag( iWorkTagNo));
+ }
+ } else {
+ if( iaRightChild[iWorkTagNo] == 0 ) {
+ iaRightChild[iWorkTagNo] = iThisTagNo + 1;
+ iaParent[iThisTagNo] = iWorkTagNo + 1;
+ bDone = xbTrue;
+ } else {
+ iWorkTagNo = iaRightChild[iWorkTagNo]-1;
+ sWorkTag = GetTagName( GetTag( iWorkTagNo ));
+ }
+ }
+ }
+ }
+ }
+
+ xbString s;
+ xbMdxTag *mpTag = mdxTagTbl;
+ for( xbInt16 i = 0; i < iTagUseCnt; i++ ){
+ mpTag->cLeftChild = (char ) iaLeftChild[i];
+ mpTag->cRightChild = (char ) iaRightChild[i];
+ mpTag->cParent = (char ) iaParent[i];
+ mpTag = mpTag->next;
+ }
+}
+
+/**************************************************************************************************************/
+//! @brief Calculate the page number for a given block
+/*!
+ This routine is called by any function needing to calculate the page number for a given block.
+ Page numbers are stored internally in the physical file, and the library reads and writes in
+ blocks of one or more pages.
+
+ Assumes valid data input
+
+ \param ulBlockNo Block Number
+ \returns Calculated page number.
+*/
+
+inline xbUInt32 xbIxMdx::BlockToPage( xbUInt32 ulBlockNo ){
+ return ulBlockNo * (xbUInt32) iBlockFactor;
+}
+/***********************************************************************/
+char xbIxMdx::CalcTagKeyFmt( xbExp &exp ){
+
+ xbExpNode *n = exp.GetTreeHandle();
+ if( n->GetChildCnt() == 0 && n->GetNodeType() == XB_EXP_FIELD )
+ return 0x01;
+ else
+ return 0;
+}
+/***********************************************************************/
+//! @brief Check for duplicate key.
+/*!
+ \param vpTag Tag to check.
+ \returns XB_KEY_NOT_UNIQUE<br>XB_NO_ERROR
+*/
+
+xbInt16 xbIxMdx::CheckForDupKey( void *vpTag )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ mpTag->bFoundSts = xbFalse;
+ try{
+ if( GetUnique( mpTag )){
+ if( mpTag->iKeySts == XB_ADD_KEY || mpTag->iKeySts == XB_UPD_KEY )
+ if( KeyExists( mpTag )){
+ if( GetUniqueKeyOpt() == XB_EMULATE_DBASE ){
+ mpTag->bFoundSts = xbTrue;
+ return 0;
+ } else {
+ return XB_KEY_NOT_UNIQUE;
+ }
+ }
+ }
+ return 0;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::CheckForDupKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Check tag integrity.
+/*!
+ Check a tag for accuracy.
+
+ \param vpTag Tag to create key for.
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxMdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iRc2;
+ xbInt16 iRc3;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulIxCnt = 0;
+ xbUInt32 ulThisRecNo = 0;
+ xbUInt32 ulPrevRecNo = 0;
+ xbBool bDone = false;
+ xbString sMsg;
+ char cKeyType;
+ char *pPrevKeyBuf = NULL;
+ xbMdxTag *npTag = (xbMdxTag *) vpTag;
+ xbBool bDescending = npTag->cKeyFmt2 & 0x08;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ bLocked = xbTrue;
+ }
+ #endif
+
+ memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyLen );
+ cKeyType = GetKeyType( vpTag );
+
+ pPrevKeyBuf = (char *) calloc( 1, (size_t) npTag->iKeyLen );
+ iRc = GetFirstKey( vpTag, 0 );
+
+ memcpy( pPrevKeyBuf, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen );
+
+ // for each key in the index, make sure it is trending in the right direction
+ while( iRc == XB_NO_ERROR && !bDone ){
+ ulIxCnt++;
+
+ iRc = GetNextKey( vpTag, 0 );
+ if( iRc == XB_NO_ERROR ){
+
+ // compare this key to prev key
+ iRc2 = CompareKey( cKeyType, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ),
+ pPrevKeyBuf, (size_t) npTag->iKeyLen );
+
+ if(( iRc2 < 0 && !bDescending ) || ( iRc2 > 0 && bDescending )){
+ sMsg.Sprintf( "Key sequence error at key number [%ld].", ulIxCnt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ iErrorStop = 110;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ ulThisRecNo = 0;
+ if(( iRc3 = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulThisRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc3;
+ }
+
+ if( iRc2 == 0 && (ulThisRecNo <= ulPrevRecNo )){
+ sMsg.Sprintf( "Dbf record number sequence error at key number [%ld].", iOpt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ iErrorStop = 130;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+ // save this key info to prev key
+ memcpy( pPrevKeyBuf, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen );
+ ulPrevRecNo = ulThisRecNo;
+ }
+ }
+
+ xbUInt32 ulDbfCnt = 0;
+ if(( iRc = dbf->GetRecordCnt( ulDbfCnt )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ xbUInt32 ulFiltCnt = 0;
+ xbBool bFiltered = xbTrue;
+ // verify each record in the dbf file has a corresponding index entry
+ xbUInt32 j = 0;
+ while( j < ulDbfCnt ){
+
+ if(( iRc = dbf->GetRecord( ++j )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if( npTag->cHasFilter ){
+ if(( iRc = npTag->filter->ProcessExpression( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = npTag->filter->GetBoolResult( bFiltered )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+
+ if( bFiltered ){
+ if(( iRc = FindKeyForCurRec( vpTag )) != XB_NO_ERROR ){
+ ulThisRecNo = j;
+ iErrorStop = 180;
+ throw iRc;
+ }
+ ulFiltCnt++;
+ }
+ }
+
+ if((GetUniqueKeyOpt() == XB_EMULATE_DBASE) && (GetUnique( vpTag ))){
+ // can't compare counts if using XB_EMULATE_DBASE and it's a unique index
+ } else {
+ if( ulIxCnt != ulFiltCnt && GetUniqueKeyOpt() == XB_HALT_ON_DUPKEY ){
+ if( npTag->cHasFilter )
+ sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Filtered index entry count [%ld] does not match dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName );
+ else
+ sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Index entry count [%ld] does not match dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName );
+
+ xbase->WriteLogMessage( sMsg, iOpt );
+ iErrorStop = 190;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+ if( npTag->cHasFilter )
+ sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Filtered index entry count [%ld] matches dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName );
+ else
+ sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Index entry count [%ld] matches dbf record count [%ld] for tag [%s]", ulIxCnt, ulFiltCnt, npTag->cTagName );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ }
+
+ if( pPrevKeyBuf )
+ free( pPrevKeyBuf );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d] Tag = [%s]", iErrorStop, iRc, npTag->cTagName );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ), iOpt );
+ if( pPrevKeyBuf )
+ free( pPrevKeyBuf );
+
+ if( iErrorStop == 160 ){
+ sMsg.Sprintf( "xbIxMdx::CheckTagIntegrity() Missing index entry for record [%d]", ulThisRecNo );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ }
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked ){
+ dbf->LockTable( XB_UNLOCK );
+ }
+ #endif
+
+ return iRc;
+}
+
+/***********************************************************************/
+xbMdxTag *xbIxMdx::ClearTagTable(){
+
+ // clear the list of tags
+ xbMdxTag *tt = mdxTagTbl;
+ xbMdxTag *tt2;
+ while( tt ){
+ tt2 = tt;
+ tt = tt->next;
+ tt2->npNodeChain = FreeNodeChain( tt2->npNodeChain );
+ tt2->npCurNode = NULL;
+ if( tt2->cpKeyBuf )
+ free( tt2->cpKeyBuf );
+ if( tt2->cpKeyBuf2 )
+ free( tt2->cpKeyBuf2 );
+ if( tt2->exp )
+ delete tt2->exp;
+ if( tt2->filter )
+ delete tt2->filter;
+ if( tt2->sKeyExp )
+ delete tt2->sKeyExp;
+ if( tt2->sTagName )
+ delete tt2->sTagName;
+ if( tt2->sFiltExp )
+ delete tt2->sFiltExp;
+ free( tt2 );
+ }
+ return NULL;
+}
+/***********************************************************************/
+xbInt16 xbIxMdx::Close(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ mdxTagTbl = ClearTagTable();
+ if(( iRc = xbIx::Close()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Create key.
+/*!
+
+ \param vpTag Tag
+ \param iOpt 1 = Append, 2 = Update
+ \returns XB_KEY_NOT_UNIQUE<br>XB_NO_ERROR
+
+ iKeySts 0 - No Updates
+ 1 - Add Key XB_ADD_KEY
+ 2 - Update Key XB_UPD_KEY
+ 3 - Delete Key XB_DEL_KEY
+
+ bKeyFiltered xbFalse - Key filtered out
+ xbTrue - Key filtered in
+
+ cpKeyBuf - Key buffer for add
+ cpKeyBuf2 - Key buffer for delete
+
+*/
+
+xbInt16 xbIxMdx::CreateKey( void *vpTag, xbInt16 iOpt ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbBool bFilter0 = xbFalse; // filter against RecBuf, updated record buffer
+ xbBool bFilter1 = xbFalse; // filter against recBuf2, original record buffer
+
+ try{
+
+ xbMdxTag *npTag = (xbMdxTag *) vpTag;
+ npTag->iKeySts = 0;
+
+ // do tag filter logic
+ if( npTag->cHasFilter ){
+ if(( iRc = npTag->filter->ProcessExpression( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc = npTag->filter->GetBoolResult( bFilter0 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ } else {
+ bFilter0 = xbTrue;
+ }
+
+ // if add request and filtered out, we're done
+ if( iOpt == 1 && !bFilter0 )
+ return XB_NO_ERROR;
+
+ if(( iRc = npTag->exp->ProcessExpression( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if( npTag->exp->GetReturnType() == XB_EXP_CHAR )
+ npTag->exp->GetStringResult( npTag->cpKeyBuf, (xbUInt32) npTag->iKeyLen );
+ else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){
+ xbDouble d;
+ npTag->exp->GetNumericResult( d );
+ xbBcd bcd( d );
+ bcd.ToChar( npTag->cpKeyBuf );
+ }
+ else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d;
+ npTag->exp->GetNumericResult( d );
+ memcpy( npTag->cpKeyBuf, &d, 8 );
+ }
+
+ if( iOpt == 1 ) // Append
+ npTag->iKeySts = XB_ADD_KEY;
+
+ else if( iOpt == 2 ){ // Update
+ if( npTag->cHasFilter ){
+ if(( iRc = npTag->filter->ProcessExpression( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if(( iRc = npTag->filter->GetBoolResult( bFilter1 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ } else {
+ bFilter1 = xbTrue;
+ }
+
+ if(( iRc = npTag->exp->ProcessExpression( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ if( npTag->exp->GetReturnType() == XB_EXP_CHAR ){
+ npTag->exp->GetStringResult( npTag->cpKeyBuf2, (xbUInt32) npTag->iKeyLen );
+
+ } else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){
+ xbDouble d;
+ npTag->exp->GetNumericResult( d );
+ xbBcd bcd( d );
+ bcd.ToChar( npTag->cpKeyBuf2 );
+
+ } else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d;
+ npTag->exp->GetNumericResult( d );
+ memcpy( npTag->cpKeyBuf2, &d, 8 );
+
+ }
+
+ if( bFilter1 ){ // original key was indexed
+ if( bFilter0 ){ // new key s/b indexed, update it if changed
+ if( memcmp( npTag->cpKeyBuf, npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){
+ npTag->iKeySts = XB_UPD_KEY;
+ }
+ } else { // original key indexed, new key not indexed, delete it
+ npTag->iKeySts = XB_DEL_KEY;
+ }
+ } else { // original key not indexed
+ if( bFilter0 )
+ npTag->iKeySts = XB_ADD_KEY;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::CreateKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Create new tag.
+/*!
+ This routine creates a new tag. When complete, sets the cur tag pointer to
+ the newly created tag.
+
+
+ \param sName Tag Name, including .MDX suffix
+ \param sKey Key Expression
+ \param sFilter Filter expression.
+ \param iDescending
+ \param iUnique xbtrue - Unique.<br>xbFalse - Not unique.
+ \param iOverLay xbTrue - Overlay if file already exists.<br>xbFalse - Don't overlay.
+ \param vpTag Output from method Pointer to vptag pointer.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbIxMdx::CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag *tte = NULL;
+
+ try{
+ // verify room for new tag
+ if( !( iTagUseCnt < 47 )){
+ iErrorStop = 100;
+ iRc = XB_LIMIT_REACHED;
+ throw iRc;
+ }
+
+ // verify valid tag name
+ xbString sWorker = sName;
+ sWorker.Trim();
+ if( sWorker.Len() > 10 ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_TAG;
+ throw iRc;
+ }
+
+ // verify tag not already defined
+ if( iTagUseCnt > 0 ){
+ if( GetTag( sWorker )){
+ iErrorStop = 120;
+ iRc = XB_INVALID_TAG;
+ throw iRc;
+ }
+ }
+
+ // allocate a tag structure here
+ if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){
+ iErrorStop = 130;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ *vpTag = tte;
+ tte->sTagName = new xbString( sWorker );
+
+ //set up the key expression
+ sWorker = sFilter;
+ sWorker.Trim();
+ if( sWorker.Len() > 0 ){
+ if( sWorker.Len() == 0 || sWorker.Len() > 220 ){
+ iRc = XB_INVALID_TAG;
+ iErrorStop = 140;
+ throw iRc;
+ }
+ tte->sFiltExp = new xbString( sWorker );
+ tte->filter = new xbExp( dbf->GetXbasePtr());
+ if(( iRc = tte->filter->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ if((tte->filter->GetReturnType()) != 'L' ){
+ iRc = XB_INVALID_TAG;
+ iErrorStop = 160;
+ throw iRc;
+ }
+ tte->cHasFilter = 0x01;
+ }
+
+ //set up the key expression
+ sWorker = sKey;
+ sWorker.Trim();
+ if( sWorker.Len() == 0 || sWorker.Len() > 100 ){
+ iRc = XB_INVALID_TAG;
+ iErrorStop = 170;
+ throw iRc;
+ }
+ tte->sKeyExp = new xbString( sWorker );
+ tte->exp = new xbExp( dbf->GetXbasePtr());
+ if(( iRc = tte->exp->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ xbDate d;
+ d.Sysdate();
+ if( iTagUseCnt == 0 ){
+ // first tag, new mdx file
+ // create the file name
+ xbString sIxFileName = dbf->GetFqFileName();
+ sIxFileName.Trim();
+ xbUInt32 lLen = sIxFileName.Len();
+ sIxFileName.PutAt( lLen-2, 'M' );
+ sIxFileName.PutAt( lLen-1, 'D' );
+ sIxFileName.PutAt( lLen, 'X' );
+
+ // copy the file name to the class variable
+ this->SetFileName( sIxFileName );
+ if( FileExists() && !iOverlay ){
+ iErrorStop = 190;
+ iRc = XB_FILE_EXISTS;
+ throw iRc;
+ }
+
+ // first tag, need to create the file
+ if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+ cVersion = 2;
+ cCreateYY = (char) d.YearOf() - 1900;
+ cCreateMM = (char) d.MonthOf();
+ cCreateDD = (char) d.DayOf( XB_FMT_MONTH );
+
+ GetFileNamePart( sFileName );
+ sFileName.ToUpperCase();
+
+ SetBlockSize( (xbUInt32) dbf->GetCreateMdxBlockSize());
+ iBlockFactor = GetBlockSize() / 512;
+
+ cProdIxFlag = 1;
+ cTagEntryCnt = 48;
+ iTagLen = 32;
+ ulPageCnt = 4;
+ ulFirstFreePage = 0;
+ ulNoOfBlockAvail = 0;
+ cNextTag = 1;
+ c1B = 0x1B;
+ cUpdateYY = cCreateYY;
+ cUpdateMM = cCreateMM;
+ cUpdateDD = cCreateDD;
+
+ if(( iRc = WriteHeadBlock( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ }
+
+ // populate the tag table entry structure
+ tte->ulTagHdrPageNo = ulPageCnt;
+ ulPageCnt += (xbUInt32) iBlockFactor;
+ tte->sTagName->strncpy( tte->cTagName, 10 );
+
+ // cKeyFmt is always 0x10;
+ // tested 2+ZIPCD CITY+STATE or just standalone field - always 0x10
+ tte->cKeyFmt = 0x10; // = CalcTagKeyFmt( *tte->exp );
+
+ switch( tte->exp->GetReturnType()){
+ case XB_EXP_CHAR:
+ tte->cKeyType = 'C';
+ tte->iKeyLen = tte->exp->GetResultLen();
+ tte->iSecKeyType = 0;
+ break;
+
+ case XB_EXP_NUMERIC:
+ tte->cKeyType = 'N';
+ tte->iKeyLen = 12;
+ tte->iSecKeyType = 0;
+ break;
+
+ case XB_EXP_DATE:
+ tte->cKeyType = 'D';
+ tte->iKeyLen = 8;
+ tte->iSecKeyType = 1;
+ break;
+
+ default:
+ iErrorStop = 200;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 );
+ tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 );
+
+ // write the new tte entry here
+ char tteBuf[21];
+ memset( tteBuf, 0x00, 21 );
+
+ ePutUInt32( &tteBuf[0], tte->ulTagHdrPageNo );
+ for( xbUInt32 l = 0; l < tte->sTagName->Len() && l < 10; l++ ){
+ tteBuf[l+4] = tte->sTagName->GetCharacter(l+1);
+ }
+ tteBuf[15] = tte->cKeyFmt;
+ tteBuf[19] = 0x02; // appears to always be a 0x02
+ tteBuf[20] = tte->cKeyType;
+
+ if(( iRc = xbFseek( (iTagUseCnt * 32) + 544, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( tteBuf, 21, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+
+ // Begin Tag Header
+ tte->ulRootPage = ulPageCnt;
+ tte->ulTagSize = (xbUInt32) iBlockFactor;
+ ulPageCnt += 2;
+ tte->cKeyFmt2 = 0x10;
+ if( iDescending )
+ tte->cKeyFmt2 += 0x08;
+ if( iUnique ){
+ tte->cKeyFmt2 += 0x40;
+ tte->cUnique = 0x01;
+ }
+
+ tte->cTag11 = 0x1B; // always 0x1b ?
+ tte->cSerialNo = 0x01; // version incremented with each tag update
+ tte->ulLeftChild = tte->ulRootPage;
+ tte->ulRightChild = tte->ulRootPage;
+
+ tte->cTagYY = (char) d.YearOf() - 1900;
+ tte->cTagMM = (char) d.MonthOf();
+ tte->cTagDD = (char) d.DayOf( XB_FMT_MONTH );
+
+ tte->cKeyType2 = tte->cKeyType;
+ tte->iKeyItemLen = tte->iKeyLen + 4;
+ while(( tte->iKeyItemLen % 4 ) != 0 ) tte->iKeyItemLen++;
+
+ tte->iKeysPerBlock = (xbInt16) (GetBlockSize() - 12) / tte->iKeyItemLen;
+ tte->cKeyFmt3 = CalcTagKeyFmt( *tte->exp );
+
+ char *pBuf;
+ if(( pBuf = (char *) calloc( 1, (size_t) GetBlockSize())) == NULL ){
+ iErrorStop = 230;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ char *wPtr;
+ wPtr = pBuf;
+ ePutUInt32( wPtr, tte->ulRootPage );
+
+ wPtr += 4;
+ ePutUInt32( wPtr, tte->ulTagSize );
+
+ wPtr += 4;
+ *wPtr = tte->cKeyFmt2;
+
+ wPtr++;
+ *wPtr = tte->cKeyType2;
+
+ wPtr += 2;
+ *wPtr = tte->cTag11;
+
+ wPtr += 1;
+ ePutInt16( wPtr, tte->iKeyLen );
+
+ wPtr += 2;
+ ePutInt16( wPtr, tte->iKeysPerBlock );
+
+ wPtr += 2;
+ ePutInt16( wPtr, tte->iSecKeyType );
+
+ wPtr += 2;
+ ePutInt16( wPtr, tte->iKeyItemLen );
+
+ wPtr += 2;
+ *wPtr = tte->cSerialNo;
+
+ wPtr += 3;
+ *wPtr = tte->cUnique;
+
+ wPtr++;
+ for( xbUInt32 l = 0; l < tte->sKeyExp->Len(); l++ )
+ *wPtr++ = tte->sKeyExp->GetCharacter(l+1);
+
+ wPtr = pBuf;
+
+ tte->cHasKeys = 0x00;
+ pBuf[246] = tte->cHasKeys;
+
+ wPtr += 248;
+ ePutUInt32( wPtr, tte->ulLeftChild );
+ wPtr += 4;
+ ePutUInt32( wPtr, tte->ulRightChild );
+
+ pBuf[257] = tte->cTagYY;
+ pBuf[258] = tte->cTagMM;
+ pBuf[259] = tte->cTagDD;
+ pBuf[480] = tte->cKeyFmt3;
+
+ if( sFilter.Len() > 0 ){
+ pBuf[245] = tte->cHasFilter;
+ wPtr = pBuf;
+ wPtr += 762;
+ for( xbUInt32 l = 0; l < sFilter.Len(); l++ )
+ *wPtr++ = sFilter.GetCharacter(l+1);
+ }
+
+ if(( iRc = xbFseek( tte->ulTagHdrPageNo * 512, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+
+ if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw iRc;
+ }
+
+ memset( pBuf, 0x00, GetBlockSize() );
+ if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+
+ iTagUseCnt++;
+ cNextTag++;
+
+ if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 270;
+ throw iRc;
+ }
+
+ // update the dbf file if needed - discreet field, has no filter
+ // 10/15/22 - dbase 7 does not update this field on index creation
+ if( tte->cKeyFmt3 == 0x01 && !tte->cHasFilter ){
+ // printf( "cKeyFmt3 = [%x]\n", tte->cKeyFmt3 );
+ xbInt16 iFldNo;
+ if(( iRc = dbf->GetFieldNo( sKey, iFldNo )) != XB_NO_ERROR ){
+ iErrorStop = 280;
+ throw iRc;
+ }
+ xbInt64 lOffset = 31 + ((iFldNo + 1) * 32 );
+
+ if(( iRc = dbf->xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 290;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+ char cBuf[2];
+ cBuf[0] = 0x01;
+ cBuf[1] = 0x00;
+
+ if(( iRc = dbf->xbFwrite( cBuf, 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+ dbf->UpdateSchemaIxFlag( iFldNo, 0x01 );
+ }
+
+ // add the new entry to the end of the list of tags
+ if( mdxTagTbl == NULL ){
+ mdxTagTbl = tte;
+ } else {
+ xbMdxTag *tteL = mdxTagTbl;
+ while( tteL->next )
+ tteL = tteL->next;
+ tteL->next = tte;
+ }
+
+ /* update the btree pointers */
+ CalcBtreePointers();
+ char bBuf[3];
+ xbMdxTag *tteWork = mdxTagTbl;
+
+ if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 310;
+ throw iRc;
+ }
+ while( tteWork ){
+ bBuf[0] = tteWork->cLeftChild;
+ bBuf[1] = tteWork->cRightChild;
+ bBuf[2] = tteWork->cParent;
+
+ if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 320;
+ throw iRc;
+ }
+ if( tteWork->next ){
+ if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){
+ iErrorStop = 330;
+ throw iRc;
+ }
+ }
+ tteWork = tteWork->next;
+ }
+ free( pBuf );
+ }
+
+ catch (xbInt16 iRc ){
+ if( tte ){
+ if( tte->cpKeyBuf )
+ free( tte->cpKeyBuf );
+ if( tte->cpKeyBuf2 )
+ free( tte->cpKeyBuf2 );
+ if( tte->exp )
+ delete tte->exp;
+ if( tte->filter )
+ delete tte->filter;
+ if( tte->sKeyExp )
+ delete tte->sKeyExp;
+ if( tte->sFiltExp )
+ delete tte->sFiltExp;
+ if( tte->sTagName )
+ delete tte->sTagName;
+ free( tte );
+ }
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+};
+
+/***********************************************************************/
+//! @brief Delete a key from a node.
+/*!
+ This routine deletes a key from a supplied node.
+ \param vpTag Tag to delete key on.
+ \param npNode Node to delete key on.
+ \param iSlotNo Slot number of key to delete.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxMdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag * npTag = (xbMdxTag *) vpTag;
+
+ try{
+ xbInt32 lKeyCnt = GetKeyCount( npNode );
+ xbInt16 iLen = (lKeyCnt - iSlotNo - 1) * npTag->iKeyItemLen;
+ xbBool bLeaf = IsLeaf( vpTag, npNode );
+ if( !bLeaf )
+ iLen += 4;
+
+ char *pTrg = npNode->cpBlockData;
+ if( iLen > 0 ){
+ pTrg += (8 + (npTag->iKeyItemLen * (iSlotNo)) ); //lTrgPos;
+ // std::cout << "TrgSpot = " << (8 + (npTag->iKeyItemLen * (iSlotNo)) ) << "\n";
+ char *pSrc = pTrg;
+ pSrc += npTag->iKeyItemLen;
+ memmove( pTrg, pSrc, (size_t) iLen );
+ }
+
+ // zap out the right most key
+ pTrg = npNode->cpBlockData;
+ if( bLeaf ){
+ pTrg += (8 + (npTag->iKeyItemLen * ( lKeyCnt-1 )));
+
+ } else {
+ pTrg += (12 + (npTag->iKeyItemLen * ( lKeyCnt-1 )));
+
+ }
+
+ for( xbInt16 i = 0; i < npTag->iKeyItemLen; i++ )
+ *pTrg++ = 0x00;
+
+ // set the new number of keys
+ ePutUInt32( npNode->cpBlockData, (xbUInt32) lKeyCnt - 1 );
+
+ // if node empty, add it to the free node chain
+ if( lKeyCnt < 2 ){
+ if( bReuseEmptyNodes ){
+ if( bLeaf && lKeyCnt == 1 ){
+ if(( iRc = HarvestEmptyNode( npTag, npNode, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ // write out the block
+ if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::DeleteFromNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return XB_NO_ERROR;
+}
+
+/***********************************************************************/
+//! @brief Delete a key.
+/*!
+ This routine deletes a key. It assumes the key to delete
+ is the current key in the node chain.
+
+ \param vpTag Tag to delete key on.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::DeleteKey( void *vpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag * npTag = (xbMdxTag *) vpTag;
+
+ // save copy of node chain to reset to after delete completed
+ xbIxNode *npSaveNodeChain = npTag->npNodeChain;
+ npTag->npNodeChain = NULL;
+ xbIxNode * npSaveCurNode = npTag->npCurNode;
+
+ try{
+ xbString sMsg;
+
+ if(( iRc = xbIxMdx::KeySetPosDel( npTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // Delete key needs to handle two scenarios
+ // 1 - if delete is on the only key of leaf,
+ // traverse up tree, trim as needed
+ // 2 - if last key on node is deleted, and key value is not the same
+ // as prev key, ascend tree looking for an interior node needing
+ // updated key value
+
+ xbInt32 lOrigKeyCnt = GetKeyCount( npTag->npCurNode );
+ if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if( lOrigKeyCnt == 1 ){
+
+ // scenario 1
+ xbBool bDone = xbFalse;
+ xbBool bIsLeaf = xbFalse;
+ xbInt32 lKeyCnt;
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+
+ while( npTag->npCurNode && !bDone ){
+ lKeyCnt = GetKeyCount( npTag->npCurNode );
+ bIsLeaf = IsLeaf( npTag, npTag->npCurNode );
+ if( lKeyCnt > 0 ){
+ if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ } else if(( iRc = HarvestEmptyNode( npTag, npTag->npCurNode, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ if( (bIsLeaf && lKeyCnt > 1) || (!bIsLeaf && lKeyCnt > 0) )
+ bDone = xbTrue;
+ else
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+ }
+
+ } else if( npTag->npCurNode->iCurKeyNo == (xbUInt32) lOrigKeyCnt - 1 ){
+
+ // scenario 2
+ // if last two keys identical, then nothing to do, else go up looking for a key to change
+ if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ),
+ GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ),
+ (size_t) npTag->iKeyLen )){
+
+ xbIxNode *pNode = npTag->npCurNode->npPrev;
+ char *pSrc = GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen );
+
+ while( pNode && pNode->ulBlockNo != npTag->ulRootPage && pNode->iCurKeyNo == (xbUInt32) GetKeyCount( pNode ) )
+ pNode = pNode->npPrev;
+
+ if( pNode ){
+ if( pNode->iCurKeyNo < (xbUInt32) GetKeyCount( pNode )){
+ char *pTrg = pNode->cpBlockData;
+ pTrg += 12 + (pNode->iCurKeyNo * (xbUInt32) npTag->iKeyItemLen);
+ for( xbInt16 i = 0; i < npTag->iKeyLen; i++ )
+ *pTrg++ = *pSrc++;
+ // write out the block
+ if(( iRc = WriteBlock( pNode->ulBlockNo, GetBlockSize(), pNode->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ }
+ }
+ }
+ // restore node chain to pre delete status (which should be post add status)
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npNodeChain = npSaveNodeChain;
+ npTag->npCurNode = npSaveCurNode;
+
+ // update the serial number
+ if(( iRc = TagSerialNo( 3, npTag )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::DeleteKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( npSaveNodeChain ){
+ npTag->npNodeChain = npSaveNodeChain;
+ npSaveNodeChain = FreeNodeChain( npSaveNodeChain );
+ npTag->npCurNode = npSaveCurNode;
+ }
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Delete a given tag
+/*!
+ \param vpTag Input tag ptr for tag to be deleted<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a><br>
+ 1 = Deleted entire MDX file, only had one tag
+
+*/
+
+xbInt16 xbIxMdx::DeleteTag( void *vpTag ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ xbIxNode *n = NULL;
+ xbBool bLoneTag = xbFalse;
+
+ try{
+
+ if( !vpTag ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_TAG;
+ throw iRc;
+ }
+
+ char cSaveHasFilter = mpTag->cHasFilter;
+ char cSaveKeyFmt3 = mpTag->cKeyFmt3;
+ xbString sSaveKey = mpTag->sKeyExp->Str();
+
+ if( iTagUseCnt == 1 ){
+ // std::cout << "xbIxMdx::DeleteTag - one tag found, delete the mdx file\n";
+
+ // close the mdx file
+ if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ // delete the file
+ xbRemove();
+
+ // init variables - needed?
+ // Init();
+ // iRc > 0 defines this as the only tag in an MDX file, MDX file deleted.
+ // signals to the calling process to drop the MDX file from the
+ // list of updateable indices.
+ bLoneTag = xbTrue;
+
+ } else {
+
+ // harvest tag nodes
+
+ if(( iRc = HarvestTagNodes( mpTag, xbTrue )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ // remove an entry from tag table
+ // which tag is this?
+ xbInt16 iTagNo = 0;
+ xbMdxTag *mp = mdxTagTbl;
+ xbMdxTag *mpPrev = NULL;
+ while( mp && mp->ulTagHdrPageNo != mpTag->ulTagHdrPageNo ){
+ iTagNo++;
+ mpPrev = mp;
+ mp = mp->next;
+ }
+
+ // remove it from the linked list of tags
+ if( !mpPrev ){
+ mdxTagTbl = mp->next;
+ } else {
+ mpPrev->next = mp->next;
+ }
+ if( mp ){
+ if( mp->cpKeyBuf ) free( mp->cpKeyBuf );
+ if( mp->cpKeyBuf2 ) free( mp->cpKeyBuf2 );
+ if( mp->exp ) delete mp->exp;
+ if( mp->filter ) delete mp->filter;
+ if( mp->sKeyExp ) delete mp->sKeyExp;
+ if( mp->sFiltExp ) delete mp->sFiltExp;
+ if( mp->sTagName ) delete mp->sTagName;
+ free( mp );
+ }
+ xbInt32 iTarg = iTagNo * 32;
+ xbInt32 iSrc = iTarg + 32;
+ xbInt32 iLen = (iTagUseCnt - iTagNo) * 32;
+
+ if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ char Buf[1536]; // 47 tags + 1 in case tag #47 is deleted
+ memset( Buf, 0x00, 1536 );
+ if(( iRc = xbFread( Buf, 1504, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ char *pTrg = Buf;
+ pTrg += iTarg;
+ char *pSrc = Buf;
+ pSrc += iSrc;
+ for( xbInt32 i = 0; i < iLen; i++ )
+ *pTrg++ = *pSrc++;
+
+ if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( Buf, 1504, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ iTagUseCnt--;
+ if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+
+ /* update the btree pointers */
+ CalcBtreePointers();
+ char bBuf[3];
+ xbMdxTag *tteWork = mdxTagTbl;
+
+ if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ while( tteWork ){
+ bBuf[0] = tteWork->cLeftChild;
+ bBuf[1] = tteWork->cRightChild;
+ bBuf[2] = tteWork->cParent;
+
+ if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 320;
+ throw iRc;
+ }
+ if( tteWork->next ){
+ if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){
+ iErrorStop = 330;
+ throw iRc;
+ }
+ }
+ tteWork = tteWork->next;
+ }
+ }
+
+ // update the dbf file if needed, if discreet field with no filter
+ // printf( "cSaveKeyFmt3 = [%x] cSaveHasFilter=[%x] SaveKey = [%s]\n", cSaveKeyFmt3, cSaveHasFilter, sSaveKey.Str());
+
+ if( cSaveKeyFmt3 == 0x01 && !cSaveHasFilter ){
+ xbInt16 iFldNo;
+ if(( iRc = dbf->GetFieldNo( sSaveKey, iFldNo )) != XB_NO_ERROR ){
+ iErrorStop = 340;
+ throw iRc;
+ }
+ xbInt64 lOffset = 31 + ((iFldNo + 1) * 32 );
+
+ if(( iRc = dbf->xbFseek( lOffset, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 350;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+ char cBuf[2];
+ cBuf[0] = 0x00;
+ cBuf[1] = 0x00;
+ if(( iRc = dbf->xbFwrite( cBuf, 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 360;
+ throw iRc;
+ }
+ dbf->UpdateSchemaIxFlag( iFldNo, 0x00 );
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( n )
+ free( n );
+ }
+ if( bLoneTag && !iRc )
+ return 1;
+ else
+ return iRc;
+}
+
+/***********************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+
+//! @brief Dump a given block for a tag
+/*!
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param ulBlockNo Block number to dump
+ \param mpTag Index tag pointer
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbIxMdx::DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag *mpTag ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbString s, s2;
+ xbBool bLeaf;
+ char *p;
+
+ try{
+ if(( iRc = GetBlock( mpTag, ulBlockNo, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 10;
+ throw iRc;
+ }
+ p = cNodeBuf;
+ xbInt32 lNoOfKeys = eGetInt32( p );
+ p+=4;
+ xbUInt32 ulNode2 = eGetUInt32( p );
+
+ if( !mpTag ){
+ // if no tag info, print what is available without tag info and exit
+ s.Sprintf( "--- BlkNo = %ld Page = %ld NoOfKeys = %ld Node2 (opt NextFreePage) = %ld", ulBlockNo, BlockToPage( ulBlockNo ), lNoOfKeys, ulNode2 );
+ xbase->WriteLogMessage( s, iOpt );
+ return XB_NO_ERROR;
+ }
+
+ p+=4;
+ p+= mpTag->iKeyItemLen * lNoOfKeys;
+ if( eGetUInt32( p ) == 0 ){
+ bLeaf = xbTrue;
+ // std::cout << "No of keys = " << lNoOfKeys << "\n";
+ s.Sprintf( "--- Leaf Node KeyCnt %d\t Page %d\t Block %d", lNoOfKeys, BlockToPage( ulBlockNo ), ulBlockNo );
+ } else {
+ bLeaf = xbFalse;
+ s.Sprintf( "--- Interior Node KeyCnt %d\t Page %d\t Block %d", lNoOfKeys, BlockToPage( ulBlockNo ), ulBlockNo );
+ }
+ if( ulNode2 > 0 )
+ s.Sprintf( "%s Node2 (opt NextFreePage) = %d", s.Str(), ulNode2 );
+
+ xbase->WriteLogMessage( s, iOpt );
+
+ xbInt32 l;
+ for( l = 0; l < lNoOfKeys; l++ ){
+ p = cNodeBuf + (8 + (l * mpTag->iKeyItemLen ));
+ s.Sprintf( "%08ld\t", eGetUInt32( p ));
+ p+=4;
+ if( mpTag->cKeyType2 == 'C' ){ //CHAR
+ for( xbInt32 l = 0; l < (mpTag->iKeyItemLen-4); l++ )
+ s += *p++;
+ s.Trim();
+ } else if( mpTag->cKeyType2 == 'N' ){ // NUMERIC
+ xbBcd bcd( p );
+ bcd.ToString( s2 );
+ s += s2;
+ } else if( mpTag->cKeyType2 == 'D' ){ // DATE
+ xbInt32 lDate = (xbInt32) eGetDouble( p );
+ xbDate d( lDate );
+ s.Sprintf( "%s\t%ld\t(%s)", s.Str(), lDate, d.Str());
+ } else {
+ s.Sprintf( "Unknown key type [%c]", mpTag->cKeyType2 );
+ }
+ xbase->WriteLogMessage( s, iOpt );
+ }
+ if( !bLeaf ){
+ // interior node has one extra key at the right most position
+ p = cNodeBuf + (8 + (l * mpTag->iKeyItemLen ));
+ s.Sprintf( "\t%08ld", eGetUInt32( p ));
+ xbase->WriteLogMessage( s, iOpt );
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::DumpBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/**************************************************************************************************************/
+//! @brief Dump free blocks.
+/*!
+ Dump free blocks for index debugging purposes.
+
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+xbInt16 xbIxMdx::DumpFreeBlocks( xbInt16 iOpt ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbString s;
+ char *pBuf = NULL;
+ char *pNextPage;
+ xbUInt32 ulNextPage;
+
+ try{
+
+ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ xbUInt32 ulLastBlock = PageToBlock( ulPageCnt );
+
+ pBuf = (char *) malloc( (size_t) GetBlockSize());
+ if( !pBuf ){
+ iErrorStop = 110;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if( ulFirstFreePage > 0 ){
+ xbUInt32 ulThisFreePage = ulFirstFreePage;
+ xbUInt32 ulNextFreePage = 0;
+ xbUInt32 ulCnt = 0;
+ xbase->WriteLogMessage( "*** Free Blocks ***", iOpt );
+ s.Sprintf( "File Header - FirstFreePage = %ld Block = %ld", ulFirstFreePage, PageToBlock( ulFirstFreePage ));
+ xbase->WriteLogMessage( s, iOpt );
+ while( ulThisFreePage > 0 ){
+ if(( iRc = ReadBlock( PageToBlock( ulThisFreePage ), GetBlockSize(), pBuf )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ pNextPage = pBuf;
+ pNextPage+=4;
+ ulNextFreePage = eGetUInt32( pNextPage );
+ s.Sprintf( "Free Page# = %ld\t(Block# = %ld)\tNext Free Page = %ld\t(Block = %ld)", ulThisFreePage, PageToBlock( ulThisFreePage ), ulNextFreePage, PageToBlock( ulNextFreePage ));
+ xbase->WriteLogMessage( s, iOpt );
+ ulThisFreePage = ulNextFreePage;
+ ulCnt++;
+ }
+ s.Sprintf( "%ld free blocks (%ld pages)", ulCnt, BlockToPage( ulCnt ));
+ xbase->WriteLogMessage( s, iOpt );
+ xbase->WriteLogMessage( "*** End Of Free Blocks ***", iOpt );
+ }
+
+ pNextPage = pBuf;
+ pNextPage+=4;
+
+ s = "*** Beginning of Block2 Info ***";
+ xbase->WriteLogMessage( s, iOpt );
+ s = "ulBlock2 info. ulBlock2 is either one of a linked list of free nodes, or the id of the original node that this node was split from.";
+ xbase->WriteLogMessage( s, iOpt );
+ s = "Stored in physical file as pages, processed in blocks";
+ xbase->WriteLogMessage( s, iOpt );
+
+ xbUInt32 ulFirstBlock = 3;
+
+ for( xbUInt32 ul = ulFirstBlock; ul < ulLastBlock; ul++ ){
+ if(( iRc = ReadBlock( ul, GetBlockSize(), pBuf )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ ulNextPage = eGetUInt32( pNextPage );
+ if( ulNextPage > 0 ){
+ s.Sprintf( " Block# = %ld\tPage# = %ld\tulBlock2 = %ld\tulBlock2(Page) = %ld", ul, BlockToPage( ul ), PageToBlock( ulNextPage ), ulNextPage );
+ xbase->WriteLogMessage( s, iOpt );
+ }
+ }
+ s = "*** End of Block2 Info ***";
+ xbase->WriteLogMessage( s, iOpt );
+
+ if( pBuf ) free( pBuf );
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::DumpFreeBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( pBuf ) free( pBuf );
+ }
+ return iRc;
+}
+
+/**************************************************************************************************************/
+//! @brief Dump interior and leaf blocks for a given tag.
+/*!
+ Dump blocks for given tag for index debugging purposes.
+
+ A page is 512 bytes<br>
+ A block is one or more pages<br>
+ The default mdx block size is 2 pages, or 1024 bytes<br>
+ The first four pages or header pages<br>
+
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param vpTag Index tag pointer, defaults to all tags if null.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbIxMdx::DumpTagBlocks( xbInt16 iOpt, void * vpTag ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iCurTag = 0;
+ xbString s;
+ xbInt16 iBlockCtr = 0;
+
+ try{
+
+ xbMdxTag * mpTag;
+ if( vpTag == NULL )
+ mpTag = (xbMdxTag *) GetTag( iCurTag++ );
+ else
+ mpTag = (xbMdxTag *) vpTag;
+
+ if( mpTag == NULL ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_TAG;
+ throw iRc;
+ }
+
+ xbIxNode *n;
+ xbString s;
+ xbString s2;
+ xbBool bDone = xbFalse;
+
+ xbUInt32 ulBlkNo;
+ xbLinkListOrd<xbUInt32> ll;
+ xbLinkListNode<xbUInt32> * llN;
+
+ ll.SetDupKeys( xbFalse );
+
+ s.Sprintf( "%s Root Page %ld (Block %ld)", mpTag->cTagName, mpTag->ulRootPage, PageToBlock( mpTag->ulRootPage ) );
+ xbase->WriteLogMessage( s, iOpt );
+
+ // for each tag
+ while( !bDone ){
+
+ // clear out any history
+ ll.Clear();
+ if( mpTag->npNodeChain ){
+ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain );
+ mpTag->npCurNode = NULL;
+ }
+
+ while( GetNextKey( mpTag, 0 ) == 0 ){
+ n = mpTag->npNodeChain;
+
+ while(n){
+ ll.InsertKey( n->ulBlockNo, (xbUInt32) n->iCurKeyNo );
+ n = n->npNext;
+ }
+ }
+ llN = ll.GetHeadNode();
+
+ while( llN ){
+
+ ulBlkNo = llN->GetKey();
+ xbIxMdx::DumpBlock( iOpt, ulBlkNo, mpTag );
+ llN = llN->GetNextNode();
+ iBlockCtr++;
+ }
+
+ if( vpTag || iCurTag >= GetTagCount())
+ bDone = xbTrue;
+ else
+ mpTag = (xbMdxTag *) GetTag( iCurTag++ );
+ }
+
+ s.Sprintf( "\nTotal Blocks: %d", iBlockCtr );
+ xbase->WriteLogMessage( s, iOpt );
+
+ if( mpTag->npNodeChain ){
+ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain );
+ mpTag->npCurNode = NULL;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::DumpTagBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Dump index file header.
+/*!
+ Dump a index file header for debugging purposes.
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param iFmtOpt Output Format<br>
+ 0, 1 = Header info only<br>
+ 2 = Tag info<br>
+ 3 = Header && Tag info<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxMdx::DumpHeader( xbInt16 iOpt, xbInt16 iFmtOpt )
+{
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbString s;
+ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR )
+ return iRc;
+
+ char c, tfv, cDisplayMask = 1;
+ cDisplayMask = cDisplayMask << 7;
+ if( iFmtOpt != 2 && iFmtOpt != 4 ){
+ s = "*** MDX Index Header ***";
+ xbase->WriteLogMessage( s, iOpt );
+ s = "Version = ";
+ tfv = cVersion;
+ for( c = 1; c<= 8; c++ ){
+ //std::cout << (tfv & cDisplayMask ? '1' : '0');
+ s+= (tfv & cDisplayMask ? '1' : '0');
+ tfv <<= 1;
+ }
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Create Date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "File Name = %s", sFileName.Str() );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Block Factor = %d", iBlockFactor );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Block Size = %d", GetBlockSize() );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Prod Ix Flag = %d", (xbInt16) cProdIxFlag );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Tag Entry Cnt = %d", (xbInt16) cTagEntryCnt );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Tag Len = %d", iTagLen );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Next Tag = %d", (xbInt16) cNextTag );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Tag Use Cnt = %d", iTagUseCnt );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Page Cnt = %d", ulPageCnt );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "First Free Page = %d", ulFirstFreePage );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "No Of Block Avail = %d\n", ulNoOfBlockAvail );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Last update date = %d/%d/%d", (int) cCreateMM, (int) cCreateDD, (int) cCreateYY % 100 );
+ xbase->WriteLogMessage( s, iOpt );
+
+ if( ulFirstFreePage > 0 ){
+ xbString s;
+ xbUInt32 ulNfp = ulFirstFreePage; // next free page
+ xbInt16 lc = 0;
+ while( ulNfp && lc++ < 5 ){
+ if( s.Len() > 0 )
+ s += ",";
+ s.Sprintf( "%s%ld", s.Str(), ulNfp );
+ if(( iRc = GetBlock( NULL, (xbUInt32) (ulNfp / (xbUInt32) iBlockFactor), 0 )) != 0 )
+ return iRc;
+ ulNfp = eGetUInt32( cNodeBuf+4 );
+ }
+ xbase->WriteLogMessage( s, iOpt );
+ }
+ }
+ if( iFmtOpt > 1 ){
+ xbMdxTag *tt = mdxTagTbl;
+ xbString s;
+ xbInt16 i = 0;
+
+ if( tt ){
+ while( tt ){
+ i++;
+ if(( iRc = LoadTagDetail( 2, tt )) != XB_NO_ERROR )
+ return iRc;
+
+ s.Sprintf( "TTE (%d)\tName HdrPage\tFormat\tLeftChild\tRightChild\tParent\tKeyType", i );
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "TTE (%d)\t%-12s %d\t\t%d\t%d\t\t%d\t\t%d\t%c\n", i, tt->cTagName, tt->ulTagHdrPageNo, tt->cKeyFmt, tt->cLeftChild, tt->cRightChild, tt->cParent, tt->cKeyType );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "TTH (%d)\tRoot\tTagSize\tKeyFmt2\tType2\tKeyLen\tKeysPerBlock\tSecType\tKeyItemLen\tSerial#\tHasKeys\tFilter\tDesc\tUnique\tLchild\tRchild\tKeyFmt3\tTagDate", i );
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "TTH (%d)\t%d\t%d\t%d\t%c\t%d\t%d\t\t%d\t%d\t\t%x\t%x\t%d\t%d\t%d\t%d\t%d\t%d\t%d/%d/%d",
+ i, tt->ulRootPage, tt->ulTagSize, tt->cKeyFmt2, tt->cKeyType2, tt->iKeyLen, tt->iKeysPerBlock, tt->iSecKeyType, tt->iKeyItemLen, tt->cSerialNo, tt->cHasKeys, tt->cHasFilter,
+ (((tt->cKeyFmt2 & 0x08) > 0) ? 1 : 0), // descending?
+ tt->cUnique, tt->ulLeftChild, tt->ulRightChild, tt->cKeyFmt3, (int) tt->cTagMM, (int) tt->cTagDD, (int) tt->cTagYY % 100 );
+
+ xbase->WriteLogMessage( s, iOpt );
+
+ s.Sprintf( "Key (%d) %s", i, tt->sKeyExp->Str());
+ xbase->WriteLogMessage( s, iOpt );
+
+ if( tt->cHasFilter ){
+ s.Sprintf( "Flt (%d) %s", i, tt->sFiltExp->Str());
+ xbase->WriteLogMessage( s, iOpt );
+ }
+ xbase->WriteLogMessage( "", iOpt );
+ tt = tt->next;
+
+ }
+ }
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+xbInt16 xbIxMdx::DumpIxForTag( void *vpTag, xbInt16 iOutputOpt )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iDepth = 0;
+ xbUInt32 lKeyCtr = 0;
+ xbInt32 iMinDepth = 999999;
+ xbInt32 iMaxDepth = 0;
+
+ try{
+ /*
+ get first node
+ while interior node
+ print the left key
+ level++
+ go down one on the left
+ */
+
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ if( mpTag->npNodeChain ){
+ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain );
+ mpTag->npCurNode = NULL;
+ }
+
+ // Get the root
+ if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // lRootBlock is now available
+ if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // if no keys on this node, then the index is empty
+ xbUInt32 ulKeyPtr = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ if( ulKeyPtr == 0 ){
+ iErrorStop = 120;
+ iRc = XB_EMPTY;
+ throw iRc;
+ }
+ while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node
+ PrintKey( vpTag, mpTag->npCurNode , 0, iDepth++, 'I', iOutputOpt );
+ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ // loop through the leaf entries of left most leaf
+ if( iDepth < iMinDepth ) iMinDepth = iDepth;
+ if( iDepth > iMaxDepth ) iMaxDepth = iDepth;
+ xbUInt32 ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ for( xbUInt32 ul = 0; ul < ulNoOfKeys; ul++ ){
+ PrintKey( vpTag, mpTag->npCurNode , ul, iDepth, 'L', iOutputOpt );
+ lKeyCtr++;
+ }
+
+ // if head node = start node, return
+ if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor ))
+ return XB_NO_ERROR;
+
+ xbBool bEof = false;
+ while( !bEof ){
+
+ // go up the chain, looking for an interior node with more keys on it
+ xbIxNode * TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ mpTag->npCurNode->npNext = NULL;
+ TempIxNode->npPrev = NULL;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ iDepth--;
+
+ while( mpTag->npCurNode->iCurKeyNo >= eGetUInt32( mpTag->npCurNode->cpBlockData ) &&
+ mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){
+ TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ mpTag->npCurNode->npNext = NULL;
+ TempIxNode->npPrev = NULL;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ iDepth--;
+ }
+ // if head node && right most key, return
+ if( mpTag->npCurNode->ulBlockNo == (mpTag->ulRootPage / (xbUInt32) iBlockFactor) &&
+ mpTag->npCurNode->iCurKeyNo == eGetUInt32( mpTag->npCurNode->cpBlockData ))
+ bEof = true;
+
+ if( !bEof ){
+ mpTag->npCurNode->iCurKeyNo++;
+ if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ PrintKey( vpTag, mpTag->npCurNode , mpTag->npCurNode->iCurKeyNo, iDepth++, 'I', iOutputOpt );
+
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+
+ // traverse down the left side of the tree
+ while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node
+ {
+ PrintKey( vpTag, mpTag->npCurNode , 0, iDepth++, 'I', iOutputOpt );
+ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ }
+ if( iDepth < iMinDepth ) iMinDepth = iDepth;
+ if( iDepth > iMaxDepth ) iMaxDepth = iDepth;
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ for( xbUInt32 ul = 0; ul < ulNoOfKeys; ul++ ){
+ PrintKey( vpTag, mpTag->npCurNode , ul, iDepth, 'L', iOutputOpt );
+ lKeyCtr++;
+ }
+ }
+ }
+ xbString s;
+ s.Sprintf( "Total keys = [%ld] Min Depth = [%d] Max Depth = [%d]", lKeyCtr, iMinDepth, iMaxDepth );
+ xbase->WriteLogMessage( s.Str(), 2 );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::DumpIxForTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/**************************************************************************************************************/
+void xbIxMdx::DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const
+{
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ xbString s( "Dump Node Chain" );
+ xbase->WriteLogMessage( s, 2 );
+
+ if( mpTag->npNodeChain ){
+ xbIxNode *n = mpTag->npNodeChain;
+ xbInt16 iCtr = 0;
+ char cLeaf;
+ s.Sprintf( "Cnt\tThis Prev Next CurKeyNo BlockNo Page NoOfKeys Type" );
+ xbase->WriteLogMessage( s, iOutputOpt );
+ while( n ){
+ IsLeaf( vpTag, n ) ? cLeaf = 'L' : cLeaf = 'I';
+ s.Sprintf( "%d\t%08x %08x %08x %08d %08d %08d %08ld %c",
+ iCtr++, n, n->npPrev, n->npNext, n->iCurKeyNo,
+ n->ulBlockNo, n->ulBlockNo * (xbUInt32) iBlockFactor,
+ eGetUInt32( n->cpBlockData ), cLeaf );
+ xbase->WriteLogMessage( s, 2 );
+ n = n->npNext;
+ }
+ } else {
+ s = "Empty Node Chain";
+ xbase->WriteLogMessage( s, 2 );
+ }
+}
+#endif
+
+/***********************************************************************************************/
+xbInt16 xbIxMdx::FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw ){
+
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ if( mpTag->cKeyType2 == 'N' ){ // mdx indices store numeric keys as bcd values
+ xbBcd bcd( dKey );
+ return xbIx::FindKey( vpTag, bcd, iRetrieveSw );
+ } else // this would be a julian date inquiry
+ return FindKey( vpTag, &dKey, 8, iRetrieveSw );
+}
+
+/***********************************************************************************************/
+// iRetrieveSw = 1 - position db file to index position
+// 0 - do not position dbf file
+
+xbInt16 xbIxMdx::FindKey( void *vpTag, const void * vpKey,
+ xbInt32 lSearchKeyLen, xbInt16 iRetrieveSw ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ // clean up any previous table updates before moving on
+ if( iRetrieveSw ){
+ if( dbf->GetDbfStatus() == XB_UPDATED ){
+ if( dbf->GetAutoCommit() == 1 ){
+ if(( iRc = dbf->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = dbf->Abort()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ xbUInt32 ulNoOfKeys;
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ char cKeyType = GetKeyType( vpTag );
+ xbBool bDescending = mpTag->cKeyFmt2 & 0x08;
+
+ if( mpTag->npNodeChain ){
+
+ // determine if the index has been updated since the last time it was used
+ time_t tFileTs;
+ if(( iRc = GetFileMtime( tFileTs )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if( mpTag->tNodeChainTs < tFileTs ){
+ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain );
+ mpTag->npCurNode = NULL;
+
+ } else {
+ // pop up the chain looking for appropriate starting point
+ xbBool bDone = false;
+ xbIxNode * TempIxNode;
+ while( mpTag->npCurNode && !bDone &&
+ (mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor ))){ // not root node
+ //if no keys on the node, pop up one
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ if( ulNoOfKeys == 0 ){
+ TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+
+ } else {
+
+ iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, 0, mpTag->iKeyItemLen ), (size_t) lSearchKeyLen );
+ if( (!bDescending && iRc <= 0) || (bDescending && iRc >= 0 )){
+ TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ } else {
+ // get the number of keys on the block and compare the key to the rightmost key
+ xbUInt32 ulKeyCtr = eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1; // IsLeaf( vpTag, mpTag->npCurNode );
+ iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, ulKeyCtr, mpTag->iKeyItemLen), (size_t) lSearchKeyLen );
+
+ if( (!bDescending && iRc > 0) || (bDescending && iRc < 0 )){
+ TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ } else {
+ bDone = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // either started empty, or cleared due to file time diff
+ if( !mpTag->npNodeChain ){
+ // Get the root
+ if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ // lRootBlock is now available
+ if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ // if this is a leaf node and no keys on this node, then the index is empty
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ if( ulNoOfKeys == 0 && IsLeaf( vpTag, mpTag->npCurNode )){
+// iRc = XB_EMPTY;
+ iRc = XB_NOT_FOUND;
+ return iRc;
+ }
+ }
+
+ // should be in the appropriate position in the node chain to continue the search from here
+ // run down through the interior nodes
+ xbInt16 iSearchRc = 0;
+ xbUInt32 ulKeyPtr = 0;
+
+ while( vpTag && !IsLeaf( vpTag, mpTag->npCurNode ) ){
+ // get the number of keys on the block and compare the key to the rightmost key
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+
+ if( ulNoOfKeys == 0 ){
+ mpTag->npCurNode->iCurKeyNo = 0;
+
+ } else {
+
+ iRc = CompareKey( cKeyType, vpKey, GetKeyData( mpTag->npCurNode, ulNoOfKeys - 1, mpTag->iKeyItemLen), (size_t) lSearchKeyLen );
+ if( (!bDescending && iRc > 0) || (bDescending && iRc < 0)){
+ mpTag->npCurNode->iCurKeyNo = ulNoOfKeys;
+ }
+ else
+ {
+ mpTag->npCurNode->iCurKeyNo = (xbUInt32) BSearchBlock( cKeyType, mpTag->npCurNode,
+ (xbInt32) mpTag->iKeyItemLen, vpKey, (xbInt32) lSearchKeyLen, iSearchRc, bDescending );
+ }
+ }
+
+ if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32)iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ }
+
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ xbInt16 iCompRc = 0;
+
+ if( ulNoOfKeys == 0 ){
+ // iCompRc = -1;
+ // iRc = XB_EMPTY;
+ iRc = XB_NOT_FOUND;
+ return iRc;
+
+ } else {
+
+ iRc = BSearchBlock( cKeyType, mpTag->npCurNode, mpTag->iKeyItemLen, vpKey, lSearchKeyLen, iCompRc, bDescending );
+ // iCompRc
+ // 0 found
+ // < 0 eof encountered, search key > last key in file
+ // > 0 not found, positioned to next key
+
+ if( iCompRc >= 0 ){
+ mpTag->npCurNode->iCurKeyNo = (xbUInt32) iRc;
+
+
+ if( iRetrieveSw ){
+
+ xbUInt32 ulKey = mpTag->npCurNode->iCurKeyNo;
+ if( ulKey >= ulNoOfKeys ) // if position past end of keys, need to go back one and position to last key
+ ulKey--;
+
+ // if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+
+ if(( iRc = GetKeyPtr( vpTag, ulKey, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ if( iCompRc == 0 )
+ return XB_NO_ERROR;
+ else if( iCompRc > 0 )
+ return XB_NOT_FOUND;
+ else
+ return XB_EOF;
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::FindKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+xbInt16 xbIxMdx::FindKeyForCurRec( void * vpTag )
+{
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 i = 0;
+
+ try{
+ if(( iRc = CreateKey( vpTag, 0 )) < XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::FindKeyForCurRec() Exception Caught. Error Stop = [%d] iRc = [%d] Tag=[%d]", iErrorStop, iRc, i );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return XB_NO_ERROR;
+}
+
+/***********************************************************************/
+//! @brief Get dbf record number for given key number.
+/*!
+ \param vpTag Tag to retrieve dbf rec number on.
+ \param iKeyNo Key number for retrieval
+ \param np Pointer to node
+ \param ulDbfPtr- Output dbf record number
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxMdx::GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulDbfPtr ) const {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ #ifdef XB_DEBUG_SUPPORT
+ // turn this off in production mode for better performance
+ xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData );
+ if( iKeyNo < 0 || iKeyNo > (xbInt16) --ulNoOfKeys ){
+ iErrorStop = 100;
+ throw XB_INVALID_KEYNO;
+ }
+ #endif
+
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ char *p = ( np->cpBlockData);
+ p += (8 + (iKeyNo * mpTag->iKeyItemLen));
+ ulDbfPtr = eGetUInt32 ( p );
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::GetDbfPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+/*!
+ \param vpTag Tag to retrieve first key on.
+ \param iRetrieveSw xbTrue - Retrieve the record on success.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw = 0 ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ // convert the tag pointer to mdx tag pointer
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+
+
+ try{
+ // clear out any history
+ if( mpTag->npNodeChain ){
+ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain );
+ mpTag->npCurNode = NULL;
+ }
+ // Get the root
+ if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // lRootPage is available
+ if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ // if no keys on this node, then the index is empty
+ // this is not true
+
+ xbUInt32 ulKeyPtr = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ if( ulKeyPtr == 0 && IsLeaf( vpTag, mpTag->npCurNode )){
+ iRc = XB_EMPTY;
+ return iRc;
+ }
+
+ while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node
+ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ // retrieve record to data buf
+ if( iRetrieveSw ){
+ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::GetFirstKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//xbBool xbIxMdx::GetIndexUpdated() const {
+// std::cout << "xbIxMdx::GetIndexUpdate() FIX ME \n";
+// return xbFalse;
+//}
+
+/***********************************************************************/
+//! @brief Get the key expression for the given tag.
+/*!
+ \param vpTag Tag to retrieve key expression from tag.
+ \returns Key expression.
+*/
+
+xbString &xbIxMdx::GetKeyExpression( const void * vpTag ) const{
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ return *mpTag->sKeyExp;
+}
+
+/***********************************************************************/
+//! @brief Get the key expression for the given tag.
+/*!
+ \param vpTag Tag to retrieve filter expression from tag (if it exists).
+ \returns Key filter.
+*/
+
+xbString &xbIxMdx::GetKeyFilter( const void * vpTag ) const{
+
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+
+ if( mpTag->sFiltExp )
+ return *mpTag->sFiltExp;
+ else
+ return sNullString;
+
+}
+/**************************************************************************************************/
+xbInt16 xbIxMdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) const {
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ xbMdxTag *mdxTag = (xbMdxTag *) vpTag;
+ char *p = np->cpBlockData;
+ xbUInt32 ulKeyCnt = eGetUInt32( p );
+ if( iKeyNo < 0 || iKeyNo > (xbInt16) ulKeyCnt ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_KEYNO;
+ throw iRc;
+ }
+ p+=8; // skip past first two four byte numeric fields
+ p+= (iKeyNo * mdxTag->iKeyItemLen);
+ ulKeyPtr = eGetUInt32( p );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::GetKeyPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Returns key update status.
+/*!
+ \param vpTag Tag to check status on.
+ \returns XB_UPD_KEY Key updated.<br>
+ XB_DEL_KEY Key deleted.<br>
+ XB_ADD_KEY Key added.<br>
+ 0 No key updates
+
+*/
+inline xbInt16 xbIxMdx::GetKeySts( void *vpTag ) const{
+
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ return mpTag->iKeySts;
+}
+
+/***********************************************************************/
+char xbIxMdx::GetKeyType( const void *vpTag ) const {
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ return mpTag->cKeyType;
+}
+
+/***********************************************************************/
+//! @brief Get the last key for the given tag.
+/*!
+ \param vpTag Tag to retrieve last key on.
+ \param iRetrieveSw xbTrue - Retrieve the record on success.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::GetLastKey( void *vpTag, xbInt16 iRetrieveSw ){
+ return GetLastKey( 0, vpTag, iRetrieveSw );
+}
+
+/***********************************************************************/
+//! @brief Get the last key for the given tag and starting node.
+/*!
+ \param ulBlockNo Starting node
+ \param vpTag Tag to retrieve last key on.
+ \param iRetrieveSw xbTrue - Retrieve the record if key found.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 iRetrieveSw ){
+
+ // if UlNodeNo is zero, start at head node, otherwise start at ulNodeNo
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ // convert the tag pointer to mdx tag pointer
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+
+ try{
+ xbUInt32 ulNoOfKeys = 0;
+ // clear out any history
+ if( mpTag->npNodeChain ){
+ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain );
+ mpTag->npCurNode = NULL;
+ }
+ // Get the root
+ if( ulBlockNo == 0 ){
+ if(( iRc = LoadTagDetail( 2, mpTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ //if(( iRc = GetBlock( vpTag, (mpTag->ulRootPage / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ if(( iRc = GetBlock( vpTag, PageToBlock( mpTag->ulRootPage ), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ } else {
+ if(( iRc = GetBlock( vpTag, ulBlockNo, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+
+ if( ulNoOfKeys == 0 && IsLeaf( vpTag, mpTag->npCurNode )){
+ iRc = XB_EMPTY;
+ return iRc;
+ }
+
+ mpTag->npCurNode->iCurKeyNo = ulNoOfKeys;
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+
+ xbUInt32 ulKeyPtr = 0;
+ while( !IsLeaf( vpTag, mpTag->npCurNode )){ // go down the chain looking for a leaf node
+ // std::cout << "Considered an interior node\n";
+ if(( iRc = GetKeyPtr( vpTag, ulNoOfKeys, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ mpTag->npCurNode->iCurKeyNo = ulNoOfKeys;
+ }
+ // leaf node has one fewer keys than the interior node
+ mpTag->npCurNode->iCurKeyNo--;
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+
+ // retrieve record to data buf
+ if( iRetrieveSw ){
+ if(( iRc = GetKeyPtr( vpTag, (ulNoOfKeys-1), mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::GetLastKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Get the last key for a block number.
+/*!
+ \param vpTag Tag to retrieve first key on.
+ \param ulBlockNo Block number for key retrieval.
+ \param cpBuf output buffer for key placement
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpBuf ){
+
+ // returns the last key for a given block number
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbMdxTag * npTag = (xbMdxTag *) vpTag;
+
+ try{
+ xbIxNode * npSaveNodeChain = npTag->npNodeChain;
+ xbIxNode * npSaveCurNode = npTag->npCurNode;
+ npTag->npNodeChain = NULL;
+
+ if(( iRc = GetLastKey( ulBlockNo, npTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ memcpy( cpBuf, GetKeyData( npTag->npCurNode, GetKeyCount( npTag->npCurNode ) - 1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen );
+
+ // free memory
+ FreeNodeChain( npTag->npNodeChain );
+ npTag->npNodeChain = npSaveNodeChain;
+ npTag->npCurNode = npSaveCurNode;
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::GetLastKeyForBlockNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ) );
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Get the next key for the given tag.
+/*!
+ \param vpTag Tag to retrieve next key on.
+ \param iRetrieveSw xbTrue - Retrieve the record on success.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::GetNextKey( void * vpTag, xbInt16 iRetrieveSw ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ // convert the tag pointer to mdx tag pointer
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+
+ try{
+ if( !mpTag->npCurNode )
+ return GetFirstKey( vpTag, iRetrieveSw );
+
+ // more keys on this node?
+ xbUInt32 ulKeyPtr;
+ if( (eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1) > mpTag->npCurNode->iCurKeyNo ){
+ mpTag->npCurNode->iCurKeyNo++;
+
+ if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if( iRetrieveSw ){
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ } else {
+ return iRc;
+ }
+ } else {
+ return iRc;
+ }
+ }
+
+ // if at end head node, then at eof
+ if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor ))
+ return XB_EOF;
+
+ // This logic assumes that interior nodes have n+1 left node no's where n is the number of keys in the node
+ xbIxNode * TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+
+ // While no more right keys && not head node, pop up one node
+ while( mpTag->npCurNode->iCurKeyNo >= eGetUInt32( mpTag->npCurNode->cpBlockData ) &&
+ mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){
+
+ TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ }
+
+ // if head node && right most key, return eof
+ if( mpTag->npCurNode->ulBlockNo == (mpTag->ulRootPage / (xbUInt32) iBlockFactor) &&
+ mpTag->npCurNode->iCurKeyNo == eGetUInt32( mpTag->npCurNode->cpBlockData ))
+ return XB_EOF;
+
+ // move one to the right
+ mpTag->npCurNode->iCurKeyNo++;
+ if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ // traverse down the left side of the tree
+ while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node
+ {
+ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ // retrieve record to data buf
+ if( iRetrieveSw ){
+ if(( iRc = GetKeyPtr( vpTag, 0, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ } else {
+ return iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::GetNextKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Get the previous key for the given tag.
+/*!
+ \param vpTag Tag to retrieve previous key on.
+ \param iRetrieveSw xbTrue - Retrieve the record on success.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::GetPrevKey( void * vpTag, xbInt16 iRetrieveSw ){
+
+ xbString s;
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ // convert the tag pointer to mdx tag pointer
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+
+ try{
+ if( !mpTag->npCurNode ){
+ return GetLastKey( 0, vpTag, iRetrieveSw );
+ }
+
+ xbUInt32 ulKeyPtr = 0;
+ // more keys on this assumed-leaf node?
+
+ if( mpTag->npCurNode->iCurKeyNo > 0 ){
+ mpTag->npCurNode->iCurKeyNo--;
+
+ if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if( iRetrieveSw ){
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ } else {
+ return iRc;
+ }
+ } else {
+ return iRc;
+ }
+ }
+
+ //if head node = start node, at eof
+ if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor ))
+ return XB_BOF;
+
+ // This logic assumes that interior nodes have n+1 left node no's where n is the number of keys in the node
+ xbIxNode * TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+
+ // While no more left keys && not head node, pop up one node
+ while( mpTag->npCurNode->iCurKeyNo == 0 &&
+ mpTag->npCurNode->ulBlockNo != ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) ){
+ TempIxNode = mpTag->npCurNode;
+ mpTag->npCurNode = mpTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ }
+
+ //if head node && left most key, return bof
+ if( mpTag->npCurNode->ulBlockNo == ( mpTag->ulRootPage / (xbUInt32) iBlockFactor) && mpTag->npCurNode->iCurKeyNo == 0 )
+ return XB_BOF;
+
+ // move one to the left
+ mpTag->npCurNode->iCurKeyNo--;
+ if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ // traverse down the right side of the tree
+ xbUInt32 ulNoOfKeys;
+ while( !IsLeaf( vpTag, mpTag->npCurNode )) // go down the chain looking for a leaf node
+ {
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ mpTag->npCurNode->iCurKeyNo = ulNoOfKeys;
+
+ if(( iRc = GetKeyPtr( vpTag, ulNoOfKeys, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( vpTag, (ulKeyPtr / (xbUInt32) iBlockFactor), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+
+ // ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ mpTag->npCurNode->iCurKeyNo = eGetUInt32( mpTag->npCurNode->cpBlockData ) - 1;
+
+ // retrieve record to data buf
+ if( iRetrieveSw ){
+ if(( iRc = GetKeyPtr( vpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ } else {
+ return iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::GetPrevKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief GetReuseEmptyNode swuitch setting.
+/*!
+ \returns xbFalse - Do not reuse empty MDX index nodes (Dbase 6. behavior).
+ xbTrue - Reuse empty MDX index nodes.
+*/
+
+xbBool xbIxMdx::GetReuseEmptyNodesSw() const {
+ return bReuseEmptyNodes;
+}
+/***********************************************************************/
+xbBool xbIxMdx::GetSortOrder( void *vpTag ) const {
+
+ // return true if descending
+ xbMdxTag *mTag = (xbMdxTag *) vpTag;
+ if( mTag->cKeyFmt2 & 0x08 )
+ return 0x01;
+ else
+ return 0x00;
+}
+
+/***********************************************************************/
+//! @brief Get tag for tag number.
+/*!
+ \param iTagNo - Zero based, which tag to retrieve.
+ \returns Pointer to mdx tag for a given tag number.
+*/
+
+void * xbIxMdx::GetTag( xbInt16 iTagNo ) const {
+
+ xbMdxTag *tt = mdxTagTbl;
+ xbInt16 i = 0;
+
+ while( i < iTagNo && tt->next ){
+ tt = tt->next;
+ i++;
+ }
+ if( i == iTagNo )
+ return (void *) tt;
+ else
+ return NULL;
+}
+
+/***********************************************************************/
+//! @brief Get tag for tag name.
+/*!
+ \sTagName - Tag name to retrieve.
+ \returns Pointer to mdx tag for a given tag number.
+*/
+
+void * xbIxMdx::GetTag( xbString &sTagName ) const {
+
+ xbMdxTag *tt = mdxTagTbl;
+
+ while( sTagName != tt->cTagName && tt->next ){
+ tt = tt->next;
+ }
+
+ if( sTagName == tt->cTagName )
+ return (void *) tt;
+ else
+ return NULL;
+}
+/***********************************************************************/
+xbInt16 xbIxMdx::GetTagCount() const {
+ return iTagUseCnt;
+}
+
+/***********************************************************************/
+void xbIxMdx::GetTagName( void *vpTag, xbString &sTagName ){
+
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ sTagName = mpTag->sTagName->Str();
+}
+
+/***********************************************************************/
+//const char *xbIxMdx::GetTagName( void *vpTag, xbInt16 iOpt ) const {
+
+const char *xbIxMdx::GetTagName( void *vpTag, xbInt16 ) const {
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ return mpTag->cTagName;
+}
+
+/***********************************************************************/
+xbString &xbIxMdx::GetTagName( void *vpTag ) const {
+
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ return *mpTag->sTagName;
+}
+
+/***********************************************************************/
+void *xbIxMdx::GetTagTblPtr() const {
+ return (void *) mdxTagTbl;
+}
+
+/***********************************************************************/
+xbBool xbIxMdx::GetUnique( void *vpTag ) const {
+//! @brief Determine unique setting for given tag.
+/*!
+ \param vpTag Tag to retrieve expression from.
+ \returns xbTrue if unique key.
+*/
+ xbMdxTag *mTag = (xbMdxTag *) vpTag;
+ return mTag->cUnique;
+}
+
+/***********************************************************************/
+//! @brief Harvest Empty Node.
+/*!
+ Harvest empty MDX node and add it to the chain of link nodes
+
+ \param mpTag Tag to harvest.
+ \param iOpt - 0 Don't write the node info to disk, handled elsewhere (don't write it twice)
+ 1 Write the update into to disk
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxMdx::HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbBool bRootPage = xbFalse;
+ xbInt32 iOffset = 0;
+
+ try{
+
+ if( mpTag->ulRootPage == BlockToPage( npNode->ulBlockNo ) && !bHarvestRoot ){
+ bRootPage = xbTrue;
+ }
+ memset( npNode->cpBlockData, 0x00, GetBlockSize());
+
+ char *pTrg = npNode->cpBlockData;
+ if( !bRootPage ){
+ pTrg += 4;
+ ePutUInt32( pTrg, ulFirstFreePage );
+ }
+
+ if( bRootPage ){
+ if( mpTag->cHasKeys ){
+
+ // std::cout << "setting has keys\n";
+
+ mpTag->cHasKeys = 0x00;
+ if(( iRc = xbFseek( ((mpTag->ulTagHdrPageNo * 512) + 246), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( &mpTag->cHasKeys, 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // might need to update left sibling and right sibling here.
+ // Fields don't seem to be updated consistently by other xbase tools,
+ // for now, not updating
+ }
+
+ } else {
+
+ // update header
+ // seek to position byte 13
+ ulFirstFreePage = BlockToPage( npNode->ulBlockNo );
+ if(( iRc = xbFseek( 36, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ // write it
+ char c4[4];
+ ePutUInt32( c4, ulFirstFreePage );
+ if(( iRc = xbFwrite( c4, 4, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ if( iOpt == 1 ){
+ if(( iRc = xbFseek( (xbInt64) ((npNode->ulBlockNo * GetBlockSize() )) + iOffset, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ // write out the block
+ if(( iRc = xbFwrite( npNode->cpBlockData, GetBlockSize(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::HarvestEmptyNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Harvest Tag Nodes.
+/*!
+ Save all nodes for a given tag into the free node chain.
+ Used for reindexing or deleting a given tag.
+
+ \param mpTag Tag for harvesting nodes
+ \param bHarvestRoot Set to True when deleting tag
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::HarvestTagNodes( xbMdxTag *mpTag, xbBool bHarvestRoot ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbUInt32 ulBlkNo;
+ xbLinkListOrd<xbUInt32> ll;
+ xbLinkListNode<xbUInt32> * llN;
+ xbIxNode * n;
+
+ try{
+
+ ll.SetDupKeys( xbFalse );
+
+ // clear out any history
+ if( mpTag->npNodeChain ){
+ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain );
+ mpTag->npCurNode = NULL;
+ }
+
+ while( GetNextKey( mpTag, 0 ) == 0 ){
+ n = mpTag->npNodeChain;
+ while(n){
+ ll.InsertKey( n->ulBlockNo, (xbUInt32) n->iCurKeyNo );
+ n = n->npNext;
+ }
+ }
+
+ if( bHarvestRoot )
+ ll.InsertKey( PageToBlock( mpTag->ulTagHdrPageNo ), 0 );
+
+ llN = ll.GetHeadNode();
+ if(( n = xbIx::AllocateIxNode( GetBlockSize())) == NULL ){
+ iErrorStop = 100;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ while( llN ){
+ ulBlkNo = llN->GetKey();
+
+ // read in a block for the block number
+ if(( iRc = ReadBlock( ulBlkNo, GetBlockSize(), n->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // harvest it
+ n->ulBlockNo = ulBlkNo;
+ if(( iRc = HarvestEmptyNode( mpTag, n, 1, bHarvestRoot )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ }
+ n = FreeNodeChain( n );
+ mpTag->npNodeChain = FreeNodeChain( mpTag->npNodeChain );
+ mpTag->npCurNode = NULL;
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::HarvestTagNodes() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Insert key into interior node.
+/*!
+ Insert key into non-full interior node.<br>
+ Assumes valid inputs
+
+ \param vpTag Tag in play.
+ \param npNode Node for insertion.
+ \param iSlotNo Slot number to insert key.
+ \param ulPtr Page number to insert.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 ulPtr ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char *pNewKeyPos;
+ char *pTrg;
+ char *pLastKey = NULL;
+ xbMdxTag * npTag;
+ npTag = (xbMdxTag *) vpTag;
+ xbInt16 iCopyLen;
+ xbInt16 iNewKeyPos = 8;
+
+ try{
+ xbInt32 lKeyCnt = GetKeyCount( npNode );
+ iNewKeyPos += (iSlotNo * npTag->iKeyItemLen);
+ char *pSrc = npNode->cpBlockData;
+
+ if( iSlotNo < lKeyCnt )
+ iCopyLen = ((lKeyCnt - iSlotNo) * npTag->iKeyItemLen) + 4;
+ else
+ iCopyLen = 0;
+
+ xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 12;
+ if( ulRqdBufSize > npNode->ulBufSize ){
+ npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen;
+ npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize );
+ if( !npNode->cpBlockData ){
+ iErrorStop = 100;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ }
+
+ // if not appending to the end of the node, make some room, move things to the right
+ pNewKeyPos = npNode->cpBlockData;
+ pNewKeyPos += iNewKeyPos;
+
+ if( iSlotNo < lKeyCnt ){
+ pTrg = pNewKeyPos;
+ pTrg += npTag->iKeyItemLen;
+ memmove( pTrg, pNewKeyPos, (size_t) iCopyLen );
+ }
+
+ // get the right most key for the left part of the split node
+ xbUInt32 ulKeyPtr2;
+ if(( iRc = GetKeyPtr( vpTag, npNode->iCurKeyNo, npNode, ulKeyPtr2 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ // get the new right key value for the freshly split node
+ pLastKey = (char *) malloc((size_t) npTag->iKeyLen);
+ if(( iRc = GetLastKeyForBlockNo( vpTag, PageToBlock( ulKeyPtr2 ), pLastKey )) != XB_NO_ERROR ){
+ iRc = 120;
+ throw iRc;
+ }
+
+ // write the key values
+ pTrg = pNewKeyPos;
+ pTrg += 4;
+ pSrc = pLastKey;
+ for( xbInt16 i = 0; i < npTag->iKeyLen; i++ )
+ *pTrg++ = *pSrc++;
+
+ pTrg = pNewKeyPos;
+ //pTrg+= npTag->iKeyItemLen - 4;
+ pTrg+= npTag->iKeyItemLen;
+
+ ePutUInt32( pTrg, ulPtr);
+ ePutInt32( npNode->cpBlockData, ++lKeyCnt );
+
+ // write out the updated block to disk
+ if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( pLastKey )
+ free( pLastKey );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::InsertNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( pLastKey )
+ free( pLastKey );
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Insert key into leaf node.
+/*!
+ Insert key into non-full leaf node.<br>
+ Assumes valid inputs
+
+ \param vpTag Tag in play.
+ \param npNode Node for insertion.
+ \param iSlotNo Slot number to insert key.
+ \param ulPtr Pointer number to insert.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxMdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){
+
+ // format of block data is
+ // 4 bytes number of keys on block
+ // 4 bytes - next free block or split block num
+ // repeating
+ // 4 bytes record number
+ // x bytes key data
+
+ // Special processing note: when splitting node, new key is first inserted into full left node before
+ // the node is split. This routine will make additional room in the buffer for that scenario
+
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char *pNewKeyPos; // pointer to position in record for new key composite
+ char *pTrg;
+
+ xbInt16 iNewKeyPos = 8; // position in data block where new key begins.
+ // is the position of the record number, where the fmt is
+ // [four byte rec number][actual key data] repeats
+
+ xbMdxTag * npTag = (xbMdxTag *) vpTag;
+ xbInt16 iCopyLen;
+
+ try{
+ xbInt32 lKeyCnt = GetKeyCount( npNode );
+ iNewKeyPos += (iSlotNo * npTag->iKeyItemLen);
+
+ // length of number of keys that need to be moved to the right
+ if( iSlotNo < lKeyCnt )
+ iCopyLen = (lKeyCnt - iSlotNo) * npTag->iKeyItemLen;
+ else
+ iCopyLen = 0;
+
+ // +8 is to include the first two 4 byte fields in the block
+ xbUInt32 ulRqdBufSize = (xbUInt32) ((lKeyCnt + 1) * npTag->iKeyItemLen) + 8;
+
+ if( ulRqdBufSize > npNode->ulBufSize ){
+
+ npNode->ulBufSize += (xbUInt32) npTag->iKeyItemLen;
+ npNode->cpBlockData = (char *) realloc( npNode->cpBlockData, (size_t) npNode->ulBufSize );
+
+ // init the newly acquired buffer space
+ char *p = npNode->cpBlockData;
+ p += (npNode->ulBufSize - (xbUInt32) npTag->iKeyItemLen);
+ memset( p, 0x00, (size_t) npTag->iKeyItemLen );
+
+ if( !npNode->cpBlockData ){
+ iErrorStop = 100;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ }
+
+ // if not appending to end, move things right
+
+ pNewKeyPos = npNode->cpBlockData;
+ pNewKeyPos += iNewKeyPos;
+
+ if( iSlotNo < lKeyCnt ) {
+ pTrg = pNewKeyPos;
+ pTrg += npTag->iKeyItemLen;
+ memmove( pTrg, pNewKeyPos, (size_t) iCopyLen );
+
+ }
+
+ // write rec number
+ ePutUInt32( pNewKeyPos, ulPtr );
+
+ // write the key value
+ pTrg = pNewKeyPos;
+ pTrg += 4;
+ char * pSrc = cpKeyBuf;
+ for( xbInt16 i = 0; i < npTag->iKeyLen; i++ )
+ *pTrg++ = *pSrc++;
+
+ // update number of keys on the node
+ ePutInt32( npNode->cpBlockData, ++lKeyCnt );
+
+ // determine length of node, zap everything to the right of it
+ xbUInt32 iStartPos = 8 + ((xbUInt32) lKeyCnt * (xbUInt32) npTag->iKeyItemLen );
+ xbUInt32 iClearLen = npNode->ulBufSize - iStartPos;
+
+ char *p = npNode->cpBlockData;
+ p += iStartPos;
+ memset( p, 0x00, iClearLen );
+
+ // write out the updated block to disk
+ if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::InsertNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+inline xbBool xbIxMdx::IsLeaf( void *vpTag, xbIxNode *npNode ) const{
+
+ // for performance reasons, does no data checking
+ // will result in potentially hard to find segfaults if passing invalid npNode
+
+ xbMdxTag *mTag = (xbMdxTag *) vpTag;
+ char *p = npNode->cpBlockData;
+
+ xbInt32 lNoOfKeys = eGetInt32( p );
+ // mdx interior nodes have a sibling number to the right of the right most key in the node
+ p+=8;
+ p+= mTag->iKeyItemLen * lNoOfKeys;
+
+ if( eGetUInt32( p ) == 0 ){
+ // std::cout << "leaf node\n";
+ return true;
+ } else {
+ // std::cout << "interior node\n";
+ return false;
+ }
+}
+
+/***********************************************************************/
+xbInt16 xbIxMdx::KeyExists( void * vpTag )
+{
+ // this method assumes the key has already been built
+
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ xbInt16 iRc = FindKey( vpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 );
+
+ if( iRc == 0 )
+ return 1;
+ else
+ return 0;
+
+}
+
+/***********************************************************************/
+//! @brief Set position for key add.
+/*!
+ This routine is called by the AddKey() method and is used to position
+ the node chain to the position the new key should be added to the index.
+
+ \param npTag Pointer to npTag.
+ \param ulAddRecNo Record number to add.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxMdx::KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddRecNo ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ iRc = FindKey( mpTag, mpTag->cpKeyBuf, mpTag->iKeyLen, 0 );
+ if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY )
+ return XB_NO_ERROR; // good position
+ else if( iRc != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // get here if key was found, get the right most instance of any non unique key for append, find correct spot for update
+ if( GetUnique( mpTag ) == 0 ){
+
+ xbUInt32 ulCurRecNo;
+ if(( iRc = GetDbfPtr( mpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ xbBool bKeysMatch = xbTrue;
+
+ while( bKeysMatch && ulAddRecNo > ulCurRecNo && iRc == XB_NO_ERROR ){
+ if(( iRc = GetNextKey( mpTag, 0 )) == XB_NO_ERROR ){
+ if( memcmp( GetKeyData( mpTag->npCurNode, mpTag->npCurNode->iCurKeyNo, mpTag->iKeyItemLen ), mpTag->cpKeyBuf, (size_t) mpTag->iKeyLen ))
+ bKeysMatch = xbFalse;
+ else{
+ if(( iRc = GetDbfPtr( mpTag, mpTag->npCurNode->iCurKeyNo, mpTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ }
+ }
+ }
+ if( iRc == XB_EOF ){ // eof condition
+ if(( iRc = GetLastKey( 0, mpTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ mpTag->npCurNode->iCurKeyNo++;
+ return XB_NO_ERROR;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::KeySetPosAdd() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Set position for key delete.
+/*!
+ This routine is called by the DeleteKey() method and is used to position
+ the node chain to the position the old key should be deleted from the index.
+
+ \param npTag Pointer to npTag.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::KeySetPosDel( xbMdxTag *npTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbString sMsg;
+
+ try{
+
+ iRc = FindKey( npTag, npTag->cpKeyBuf2, npTag->iKeyLen, 0 );
+ if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY )
+ return XB_NO_ERROR; // good pos ition
+ else if( iRc != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ xbUInt32 ulIxRecNo;
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if( ulIxRecNo == dbf->GetCurRecNo())
+ return XB_NO_ERROR;
+
+ if( GetUnique( npTag ) == 1 ){
+ iErrorStop = 120;
+ iRc = XB_NOT_FOUND;
+ throw iRc;
+ }
+
+ xbBool bFound = xbFalse;
+ xbBool bKeysMatch = xbTrue;
+ while( bKeysMatch && !bFound && iRc == XB_NO_ERROR ){
+
+ if(( iRc = GetNextKey( npTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){
+ bKeysMatch = xbFalse;
+ } else {
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if( ulIxRecNo == dbf->GetCurRecNo())
+ bFound = xbTrue;
+ }
+ }
+
+ if( bFound )
+ return XB_NO_ERROR;
+ else
+ return XB_NOT_FOUND;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::KeySetPosDel() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Returns key update status.
+/*!
+ \param vpTag Tag to check status on.
+ \returns xbtrue - Key was updated.<br>xbFalse - Key not updated.
+*/
+/*
+inline xbBool xbIxMdx::KeyFiltered( void *vpTag ) const{
+
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ return mpTag->bKeyFiltered;
+}
+*/
+/***********************************************************************/
+xbInt16 xbIxMdx::LoadTagDetail( xbInt16 iOption, xbMdxTag *tte ){
+
+ // option 1 - Load the entire tag detail
+ // option 2 - Load the dynamic variables only
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ size_t iReadSize;
+ char *buf = NULL;
+ char *p;
+
+ try{
+ // set the read length based on the option
+ if( iOption == 1 )
+ iReadSize = 1024;
+
+ else if( iOption == 2 )
+ // iReadSize = 4;
+ iReadSize = 260;
+ else{
+ iRc = XB_INVALID_OPTION;
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( buf = (char *) calloc( 1, (size_t) iReadSize )) == NULL ){
+ iErrorStop = 110;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if(( iRc = ReadBlock( tte->ulTagHdrPageNo,(xbUInt32) (GetBlockSize() / (xbUInt16) iBlockFactor),
+ iReadSize, buf )) != XB_NO_ERROR ){
+ free( buf );
+ iErrorStop = 30;
+ throw iRc;
+ }
+
+ p = buf;
+ tte->ulRootPage = eGetUInt32( p );
+
+ if( iOption == 1 ){
+ p+=4;
+ tte->ulTagSize = eGetUInt32( p );
+ p+=4;
+ tte->cKeyFmt2 = *p;
+ p++;
+ tte->cKeyType2 = *p;
+ p+=3;
+ tte->iKeyLen = eGetInt16( p );
+ p+=2;
+ tte->iKeysPerBlock = *p;
+ p+=2;
+ tte->iSecKeyType = eGetInt16( p );
+ p+=2;
+ tte->iKeyItemLen = eGetInt16( p );
+ p+=2;
+ tte->cSerialNo = *p;
+ p+=3;
+ tte->cUnique = *p;
+ p++;
+
+ // next line assumes expression is a null terminated string in the block
+ tte->sKeyExp = new xbString();
+ tte->sKeyExp->Sprintf( "%s", p );
+
+ p+=221;
+ tte->cHasFilter = *p;
+ p+=1;
+ tte->cHasKeys = *p;
+ p+=2;
+ tte->ulLeftChild = eGetUInt32( p );
+ p+=4;
+ tte->ulRightChild = eGetUInt32( p );
+ p+=5;
+ tte->cTagYY = *p;
+ p++;
+ tte->cTagMM = *p;
+ p++;
+ tte->cTagDD = *p;
+ // p+=223;
+
+ p+=221;
+ tte->cKeyFmt3 = *p;
+
+ if( tte->cHasFilter ){
+ p+=282;
+ tte->sFiltExp = new xbString();
+ tte->sFiltExp->Sprintf( "%s", p );
+ tte->filter = new xbExp( xbase, dbf );
+ if(( iRc = tte->filter->ParseExpression( tte->sFiltExp->Str())) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ tte->npNodeChain = NULL;
+ tte->npCurNode = NULL;
+ tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 );
+ tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 );
+ tte->exp = new xbExp( xbase, dbf );
+ if(( iRc = tte->exp->ParseExpression( tte->sKeyExp->Str() )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ } else if( iOption == 2 ){
+ // refresh the dynamic tag variables
+ p+=4;
+ tte->ulTagSize = eGetUInt32( p );
+ p+= 16;
+ tte->cSerialNo = *p;
+ p+= 226;
+ tte->cHasKeys = *p;
+ p+=2;
+ tte->ulLeftChild = eGetUInt32( p );
+ p+=4;
+ tte->ulRightChild = eGetUInt32( p );
+ p+=5;
+ tte->cTagYY = *p;
+ p++;
+ tte->cTagMM = *p;
+ p++;
+ tte->cTagDD = *p;
+ }
+ if( buf )
+ free( buf );
+
+ }
+ catch (xbInt16 iRc ){
+ if( buf )
+ free( buf );
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::LoadTagDetail() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+xbInt16 xbIxMdx::LoadTagTable()
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char * buf = NULL;
+
+ //std::cout << "xbIxMdx::LoadTagTable() tag use cnt = " << iTagUseCnt << "\n";
+
+ try{
+
+ if( iTagUseCnt > 46 ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ xbInt16 iBufSize = (xbInt16) iTagLen * iTagUseCnt;
+
+ if(( buf = (char *) malloc( (size_t) iBufSize )) == NULL ){
+ iErrorStop = 110;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = xbFread( buf, (size_t) iBufSize, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ xbInt16 iPos;
+ char *p;
+ xbMdxTag *tte;
+ xbMdxTag *ttel = NULL;
+
+ for( xbInt16 i = 0; i < iTagUseCnt; i++ ){
+ iPos = i * iTagLen;
+ p = buf + iPos;
+
+ if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){
+ iErrorStop = 140;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+
+ // set the current tag to the first tag in the table
+ if( !vpCurTag )
+ xbIx::SetCurTag( (void *) tte );
+
+ if( mdxTagTbl )
+ ttel->next = tte;
+ else
+ mdxTagTbl = tte;
+
+ ttel = tte;
+ tte->next = NULL;
+ tte->ulTagHdrPageNo = eGetUInt32( p );
+
+ p += 4;
+ for( xbUInt32 i = 0; i < 11; i ++ )
+ tte->cTagName[i] = *p++;
+
+ tte->cTagName[11] = 0x00;
+ tte->cKeyFmt = *p++;
+ tte->cLeftChild = *p++;
+ tte->cRightChild = *p++;
+ tte->cParent = *p++;
+ tte->c2 = *p++;
+ tte->cKeyType = *p;
+ tte->sTagName = new xbString();
+ tte->sTagName->Set( tte->cTagName );
+ tte->sTagName->Trim();
+
+ if(( iRc = LoadTagDetail( 1, tte )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ if( buf )
+ free( buf );
+ }
+ catch (xbInt16 iRc ){
+ if( buf )
+ free( buf );
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::LoadTagTable() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ if( iErrorStop == 100 ){
+ sMsg.Sprintf( "xbIxMdx::LoadTagTable() Invalid Tag Count: %d", iTagUseCnt );
+ xbase->WriteLogMessage( sMsg.Str());
+ }
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/**************************************************************************************************************/
+//! @brief Calculate the block number for a given page.
+/*!
+ This routine is called by any function needing to calculate the block number for a given page.
+ Page numbers are stored internally in the physical file, and the library reads and writes in
+ blocks of one or more pages.
+
+ Assumes valid data input
+
+ \param ulPageNo Page Number
+ \returns Calculated block number.
+*/
+
+inline xbUInt32 xbIxMdx::PageToBlock( xbUInt32 ulPageNo ){
+ return ulPageNo / (xbUInt32) iBlockFactor;
+}
+
+
+
+/**************************************************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+xbInt16 xbIxMdx::PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt ){
+
+ xbString sPre;
+ sPre.Sprintf( "%c ", cType );
+ for( xbInt16 i = 0; i < iDepth; i++ )
+ sPre += "|";
+
+ xbString sPost;
+ sPost.Sprintf( "\tThisBlock=[%ld] KeyNo=[%d] Depth=[%d]", npNode->ulBlockNo, iKeyNo, iDepth );
+
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ char *p = npNode->cpBlockData + (8 + (iKeyNo * mpTag->iKeyItemLen ));
+
+ xbString sKeyPtr;
+ xbUInt32 ulNoOfKeys = 0;
+ if( cType == 'I' ) { // interior
+ sKeyPtr.Sprintf( " ptr=[%ld]", eGetUInt32( p ));
+ ulNoOfKeys = eGetUInt32( mpTag->npCurNode->cpBlockData );
+ }
+ else if( cType == 'L' ) // leaf
+ sKeyPtr.Sprintf( " rec=[%ld]", eGetUInt32( p ));
+ p += 4;
+
+ xbString s;
+ if(( cType == 'I' && iKeyNo < (xbInt16) ulNoOfKeys) || cType == 'L' ){
+ if( mpTag->cKeyType2 == 'C' ){ //CHAR
+ for( xbInt32 l = 0; l < (mpTag->iKeyItemLen-4); l++ )
+ s += *p++;
+
+ } else if( mpTag->cKeyType2 == 'N' ){ // NUMERIC
+ xbBcd bcd( p );
+ xbString s2;
+ bcd.ToString( s2 );
+ s += s2;
+
+ } else if( mpTag->cKeyType2 == 'D' ){ // DATE
+ xbInt32 lDate = (xbInt32) eGetDouble( p );
+ xbDate d( lDate );
+ //xbString s2;
+ //d.JulToDate8( lDate, s2 );
+ s.Sprintf( "%s%s", s.Str(), d.Str());
+ }
+ } else {
+ s = "Rightmost InteriorNode Pointer";
+ }
+
+ xbString sOut( sPre );
+ sOut += s;
+ sOut += sPost;
+ sOut += sKeyPtr;
+
+ xbase->WriteLogMessage( sOut, iOutputOpt );
+ return XB_NO_ERROR;
+}
+#endif
+
+/***********************************************************************/
+//! @brief ReadHeadBlock.
+/*!
+ Read values off head block in MDX file
+ \param iOpt 0 - Read entire block, initialize as needed.<br>
+ 1 - Read in only dynamic section of block<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+*/
+
+xbInt16 xbIxMdx::ReadHeadBlock( xbInt16 iOpt )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( !FileIsOpen()){
+ iRc = XB_NOT_OPEN;
+ iErrorStop = 100;
+ throw iRc;
+ }
+ char sBuf[48];
+ memset( sBuf, 0x00, 48 );
+
+ if( iOpt == 0 ){
+ if(( iRc = xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if(( iRc = xbFread( sBuf, 47, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ } else {
+
+ if(( iRc = xbFseek( 28, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ if(( iRc = xbFread( sBuf, 19, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ }
+
+ char *p = sBuf;
+ if( iOpt == 0 ){
+ cVersion = *p++;
+ cCreateYY = *p++;
+ cCreateMM = *p++;
+ cCreateDD = *p++;
+ sFileName.Assign( p, 1, 16 );
+ p+=16;
+ iBlockFactor = eGetInt16( p );
+ p+=2;
+ SetBlockSize( (xbUInt32) eGetInt16( p ));
+ p+=2;
+ cProdIxFlag = *p++;
+ cTagEntryCnt = *p++;
+ iTagLen = *p;
+ p+=2;
+
+ iTagUseCnt = eGetInt16( p );
+ //lTagUseCnt = eGetInt32( p );
+ //p+=4;
+ p+=2;
+ cNextTag = *p++;
+ c1B = *p++;
+
+ ulPageCnt = eGetUInt32( p );
+ p+=4;
+ ulFirstFreePage = eGetUInt32( p );
+ p+=4;
+ ulNoOfBlockAvail = eGetUInt32( p );
+ p+=4;
+ cUpdateYY = *p++;
+ cUpdateMM = *p++;
+ cUpdateDD = *p;
+
+ if( cNodeBuf )
+ free( cNodeBuf );
+
+ if(( cNodeBuf = (char *) malloc( (size_t) GetBlockSize())) == NULL ){
+ iErrorStop = 150;
+ throw XB_NO_MEMORY;
+ }
+
+ if(( iRc = xbIxMdx::LoadTagTable()) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+ } else {
+ iTagUseCnt = eGetInt16( p );
+ p+=4;
+ ulPageCnt = eGetUInt32( p );
+ p+=4;
+ ulFirstFreePage = eGetUInt32( p );
+ p+=4;
+ ulNoOfBlockAvail = eGetUInt32( p );
+ p+=4;
+ cUpdateYY = *p++;
+ cUpdateMM = *p++;
+ cUpdateDD = *p;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::ReadHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( cNodeBuf )
+ free( cNodeBuf );
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Reindex
+/*!
+ Reindex specifed tag or all tags
+ \param **vpTag &tag - Tag to reindex.<br>
+ NULL - Reindex all tags<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+
+ If this method fails, the index is left in an undefined state
+
+*/
+
+xbInt16 xbIxMdx::Reindex( void **vpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag * mpTag;
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif
+
+ if( vpTag )
+ mpTag = (xbMdxTag *) *vpTag;
+ else
+ mpTag = NULL;
+
+ struct tagInfo{
+ xbBool bUnique;
+ xbBool bDesc;
+ char sTagName[11];
+ xbString *sKeyExp;
+ xbString *sFiltExp;
+ tagInfo *next;
+ };
+ tagInfo *ti = NULL;
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ bLocked = xbTrue;
+ }
+ #endif
+
+ if( mpTag == NULL ){
+ // do all tags
+ xbMdxTag *tt = mdxTagTbl;
+ tagInfo *pHead = NULL;
+ tagInfo *pEnd = NULL;
+
+ if( tt ){
+ while( tt ){
+ ti = (tagInfo *) calloc( 1, sizeof( tagInfo ));
+ ti->bUnique = tt->cUnique ? 1 : 0;
+ ti->bDesc = (((tt->cKeyFmt2 & 0x08) > 0) ? 1 : 0);
+ memcpy( ti->sTagName, tt->cTagName, 11 );
+ ti->sKeyExp = new xbString( tt->sKeyExp->Str());
+ if( tt->cHasFilter )
+ ti->sFiltExp = new xbString( tt->sFiltExp->Str());
+ else
+ ti->sFiltExp = new xbString( "" );
+ if( !pHead )
+ pHead = ti;
+ else
+ pEnd->next = ti;
+ pEnd = ti;
+ tt = tt->next;
+ }
+ }
+
+ // get the file name and save it
+ xbString sMdxFileName = GetFqFileName();
+
+ // close the mdx file
+ if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ // delete the file
+ xbRemove();
+
+ // init variables
+ Init();
+
+ tagInfo *p = pHead;
+ tagInfo *pDel;
+
+ // create new file & add the tags
+ while( p ){
+
+ if(( iRc = CreateTag( p->sTagName, p->sKeyExp->Str(), p->sFiltExp->Str(), p->bDesc, p->bUnique, xbTrue, vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ delete p->sKeyExp;
+ delete p->sFiltExp;
+ pDel = p;
+ p = p->next;
+ free( pDel );
+ }
+ } else {
+ if(( iRc = HarvestTagNodes( mpTag )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+ xbUInt32 ulRecCnt = 0;
+ if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ xbInt16 iCurTag = 0;
+ xbBool bDone = xbFalse;
+
+ for( xbUInt32 ulRec = 1; ulRec <= ulRecCnt; ulRec++ ){
+ if(( iRc = dbf->GetRecord( ulRec )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ bDone = xbFalse;
+ iCurTag = 0;
+ if( !vpTag )
+ mpTag = (xbMdxTag *) GetTag( iCurTag++ );
+
+ while( !bDone ){
+ // do the tag things
+ // CreateKey
+ if(( iRc = CreateKey( mpTag, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if( mpTag->iKeySts == XB_ADD_KEY ){
+ if( mpTag->cUnique ){
+ if(( iRc = CheckForDupKey( mpTag )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ if(( iRc = AddKey( mpTag, ulRec )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ }
+ if( vpTag || iCurTag >= GetTagCount())
+ bDone = xbTrue;
+ else
+ mpTag = (xbMdxTag *) GetTag( iCurTag++ );
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::ReIndex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ this->DeleteTag( mpTag );
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked ){
+ dbf->LockTable( XB_UNLOCK );
+ }
+ #endif
+ return iRc;
+}
+
+/***********************************************************************/
+xbInt16 xbIxMdx::SetCurTag( xbString &sTagName ) {
+
+ xbMdxTag *tt = (xbMdxTag *) GetTag( sTagName );
+ if( tt ){
+ xbIx::SetCurTag((void *) tt );
+ return XB_NO_ERROR;
+ } else
+ return XB_INVALID_TAG;
+}
+
+/***********************************************************************/
+xbInt16 xbIxMdx::SetCurTag( xbInt16 iTagNo ) {
+
+ xbMdxTag *tt = (xbMdxTag *) GetTag( iTagNo );
+ if( tt ){
+ xbIx::SetCurTag((void *) tt );
+ return XB_NO_ERROR;
+ } else
+ return XB_INVALID_TAG;
+}
+
+/***********************************************************************/
+//! @brief SetReuseEmptyNode switch setting.
+/*!
+ \param bEmptyNodeSw xbFalse - Do not reuse empty MDX index nodes (Dbase 6. behavior).
+ xbTrue - Reuse empty MDX index nodes.
+*/
+
+void xbIxMdx::SetReuseEmptyNodesSw( xbBool bEmptyNodesSw ) {
+ bReuseEmptyNodes = bEmptyNodesSw;
+}
+
+/***********************************************************************/
+//! @brief Split an interior node
+/*!
+
+ This routine splits an interior node into two nodes, divided by dSplitFactor.<br>
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.<br>
+ For V7, if adding a key to the end of a node, it will create a right node
+ with only one key, and the left node is still full.<br><br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ This routine first inserts the key into the left node in the appropriate location
+ then splits the node based on the split factor setting.
+
+ \param vpTag Tag in play.
+ \param npLeft Left node to split.
+ \param npRight Right node to split.
+ \param iSlotNo Slot number for split.
+ \param ulPtr Pointer number to insert.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 ulPtr ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag * npTag = (xbMdxTag *) vpTag;
+ xbDouble dSplitFactor = .5; // split the nodes 50/50
+ xbInt16 iLen;
+ char *pSrc;
+ char *pTrg;
+
+ try{
+ xbInt32 lKeyCnt = GetKeyCount( npLeft );
+ xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor);
+ xbInt32 lNewRightKeyCnt = lKeyCnt - lNewLeftKeyCnt;
+ if(( iRc = InsertNodeI( vpTag, npLeft, iSlotNo, ulPtr )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // move the right half of the left node to the right node
+ pSrc = npLeft->cpBlockData;
+ pSrc += 8 + ((lNewLeftKeyCnt+1) * npTag->iKeyItemLen);
+ pTrg = npRight->cpBlockData;
+ pTrg += 8;
+ iLen = (lNewRightKeyCnt * npTag->iKeyItemLen) + 4;
+ memmove( pTrg, pSrc, (size_t) iLen );
+
+ // eliminate chattle on the right
+ iLen = 12 + (lNewLeftKeyCnt * npTag->iKeyItemLen);
+ pSrc = npLeft->cpBlockData;
+ pSrc += iLen;
+ memset( pSrc, 0x00, npLeft->ulBufSize - (xbUInt32) iLen );
+
+ // write the new key counts into the nodes
+ pTrg = npLeft->cpBlockData;
+ ePutInt32( pTrg, lNewLeftKeyCnt );
+ pTrg = npRight->cpBlockData;
+ ePutInt32( pTrg, lNewRightKeyCnt );
+
+ // write out the block
+ if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // write out the block
+ if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::SplitNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Split a leaf node.
+/*!
+ This routine splits an index leaf into two nodes, divided by dSplitFactor.<br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ This routine first inserts the key into the left node in the appropriate location
+ then splits the node based on the split factor setting.
+
+ \param vpTag Tag in play.
+ \param npLeft Left node to split.
+ \param npRight Right node to split.
+ \param iSlotNo Slot number for split.
+ \param ulPtr Pointer number to insert.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight,
+ xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbDouble dSplitFactor = .5; // can adjust performance with this number
+ xbMdxTag *mpTag = (xbMdxTag *) vpTag;
+ xbString sMsg;
+
+ xbInt16 iLen;
+ char *pSrc;
+ char *pTrg;
+
+ // std::cout << "In xbIxMdx::SplitNodeL()\n";
+ try{
+ xbInt32 lKeyCnt = GetKeyCount( npLeft );
+ xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1;
+ xbInt32 lNewRightKeyCnt = lKeyCnt + 1 - lNewLeftKeyCnt;
+
+ if(( iRc = InsertNodeL( vpTag, npLeft, iSlotNo, cpKeyBuf, ulPtr )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // move right half off of left node to the right node
+ pSrc = npLeft->cpBlockData;
+ pSrc += 8 + (lNewLeftKeyCnt * mpTag->iKeyItemLen);
+ pTrg = npRight->cpBlockData;
+ pTrg += 8;
+ iLen = lNewRightKeyCnt * mpTag->iKeyItemLen;
+ memmove( pTrg, pSrc, (size_t) iLen );
+
+ // write the new key counts into the nodes
+ pTrg = npLeft->cpBlockData;
+ ePutInt32( pTrg, lNewLeftKeyCnt );
+ pTrg = npRight->cpBlockData;
+ ePutInt32( pTrg, lNewRightKeyCnt );
+
+ // zero out the next key number so this node is not confused with interior node
+ iLen = 8 + (lNewLeftKeyCnt * mpTag->iKeyItemLen);
+ pSrc = npLeft->cpBlockData;
+ pSrc += iLen;
+ memset( pSrc, 0x00, npLeft->ulBufSize - (xbUInt32) iLen );
+
+ // write out the left block
+ if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // write out the right block
+ if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::SplitNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/**************************************************************************************************************/
+//! @brief TagSerialNo.
+/*!
+ This routine is used internally for reading or updating the serial number on a given tag when the tag.
+
+ \param iOption 1 - Read tag serial number off disk, save in structure<br>
+ 2 - Write serial number from memory to disk<br>
+ 3 - Read serial number off disk, increment, write updated number to disk<br>
+ mpTag - Pointer to tag for serial number update
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::TagSerialNo( xbInt16 iOption, xbMdxTag * mpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbInt64 lPos = (mpTag->ulTagHdrPageNo * 512) + 20;
+
+ if( iOption != 2 ){
+ if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc = xbFgetc( mpTag->cSerialNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ if( iOption == 3 )
+ mpTag->cSerialNo++;
+
+ if( iOption != 1 ){
+ if(( iRc = xbFseek( lPos, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( &mpTag->cSerialNo, 1, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::UpdateSerialNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief UpdateTagKey
+/*!
+ This routine updates a key or a given tag.
+ The file header is considered to be the first 2048 bytes in the file.
+
+ \param cAction A - Add a key.<br>
+ D - Delete a key.<br>
+ R - Revise a key.<br>
+ \param vpTg - Pointer to tag.<br>
+ \param ulRecNo - Record number association with the action.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxMdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){
+
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag *npTag = (xbMdxTag *) vpTag;
+
+ try{
+ // save off any needed fields for updating
+ xbUInt32 ulTagSizeSave = npTag->ulTagSize;
+
+ if( cAction == 'D' || cAction == 'R' ){
+ // std::cout << "UpdateTagKey-delete going to DeleteKey \n";
+ if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+
+ if( cAction == 'A' || cAction == 'R' ){
+ if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ if( ulTagSizeSave != npTag->ulTagSize ){
+ if(( iRc = UpdateTagSize( npTag, npTag->ulTagSize )) != XB_NO_ERROR) {
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ // update the serial number
+ if(( iRc = TagSerialNo( 3, npTag )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::UpdateTagKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/**************************************************************************************************************/
+//! @brief Write head block.
+/*!
+ This routine updates the MDX file header and commits changes to disk.
+ The file header is considered to be the first 2048 bytes in the file.
+
+ \param iOption 0 - Entire 2048 byte header, used for creating a new mdx file.<br>
+ 1 - Bytes 28 through 46, used when adding or deleting a tag.<br>
+ 2 - Bytes 32 through 46, used after updating keys in the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbIxMdx::WriteHeadBlock( xbInt16 iOption ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbDate d;
+ d.Sysdate(); // set to system date, today
+ cUpdateYY = (char) d.YearOf() - 1900;
+ cUpdateMM = (char) d.MonthOf();
+ cUpdateDD = (char) d.DayOf( XB_FMT_MONTH );
+
+ if( iOption > 0 ){
+ char buf[48];
+ memset( buf, 0x00, 48 );
+ xbUInt32 ulStartPos = 0;
+ xbUInt32 ulLen = 0;
+
+ if( iOption == 1 ){
+ ePutInt16( &buf[28], iTagUseCnt );
+ buf[30] = cNextTag;
+ buf[31] = 0x1b;
+ ulStartPos = 28;
+ ulLen = 19;
+ } else {
+ ulStartPos = 32;
+ ulLen = 16;
+ }
+
+ ePutUInt32( &buf[32], ulPageCnt );
+ ePutUInt32( &buf[36], ulFirstFreePage );
+ ePutUInt32( &buf[40], ulNoOfBlockAvail );
+ buf[44] = cUpdateYY;
+ buf[45] = cUpdateMM;
+ buf[46] = cUpdateDD;
+
+ if(( iRc = xbFseek( ulStartPos, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if(( iRc = xbFwrite( &buf[ulStartPos], ulLen, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ } else if( iOption == 0 ){
+ char buf[2048];
+ memset( buf, 0x00, 2048 );
+
+ buf[0] = cVersion;
+ cCreateYY = cUpdateYY;
+ cCreateMM = cUpdateMM;
+ cCreateDD = cUpdateDD;
+ buf[1] = cCreateYY;
+ buf[2] = cCreateMM;
+ buf[3] = cCreateDD;
+
+
+ for( xbUInt32 l = 0; l < sFileName.Len() && l < 10; l++ ){
+ buf[l+4] = sFileName[l+1];
+ }
+
+ ePutInt16( &buf[20], iBlockFactor );
+ ePutInt16( &buf[22], (xbInt16) GetBlockSize() );
+
+ buf[24] = cProdIxFlag;
+ buf[25] = cTagEntryCnt;
+ ePutInt16 ( &buf[26], iTagLen );
+ ePutInt16 ( &buf[28], iTagUseCnt );
+ buf[30] = cNextTag;
+ buf[31] = c1B;
+ ePutUInt32( &buf[32], ulPageCnt );
+ ePutUInt32( &buf[36], ulFirstFreePage );
+ ePutUInt32( &buf[40], ulNoOfBlockAvail );
+ buf[44] = cUpdateYY;
+ buf[45] = cUpdateMM;
+ buf[46] = cUpdateDD;
+
+ // not sure what the following "1" is for in a sea of zeroes....
+ // maybe it's current tag or default tag or something along those lines?
+ buf[529] = 0x01;
+
+ xbRewind();
+ if(( iRc = xbFwrite( buf, 2048, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ } else {
+ iErrorStop = 130;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::WriteHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] option = [%d]", iErrorStop, iRc, iOption );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+xbInt16 xbIxMdx::UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char buf[4];
+ try{
+ ePutUInt32( &buf[0], ulTagSz );
+ if(( iRc = xbFseek( (xbInt64) ((mpTag->ulTagHdrPageNo *512 )+ 4), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( &buf[0], 4, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxMdx::UpdateTagSize() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+
+/***********************************************************************/
+//void xbIxMdx::TestStub( char *s, void *vpTag ){
+void xbIxMdx::TestStub( char *, void * ){
+}
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_MDX_SUPPORT */
+
diff --git a/src/core/xbixndx.cpp b/src/core/xbixndx.cpp
new file mode 100755
index 0000000..b28dd9d
--- /dev/null
+++ b/src/core/xbixndx.cpp
@@ -0,0 +1,2834 @@
+/* xbixndx.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_NDX_SUPPORT
+
+namespace xb{
+
+
+/***********************************************************************/
+//! @brief Class constructor.
+/*!
+ \param dbf Pointer to dbf instance.
+*/
+
+xbIxNdx::xbIxNdx( xbDbf *dbf ) : xbIx( dbf ){
+ ndxTag = (xbNdxTag *) calloc( 1, sizeof( xbNdxTag ));
+ SetBlockSize( XB_NDX_BLOCK_SIZE );
+ cNodeBuf = (char *) malloc( XB_NDX_BLOCK_SIZE );
+}
+/***********************************************************************/
+//! @brief Class Destructor.
+xbIxNdx::~xbIxNdx(){
+ if( ndxTag ){
+ ndxTag->npNodeChain = FreeNodeChain( ndxTag->npNodeChain );
+ if( ndxTag->cpKeyBuf )
+ free( ndxTag->cpKeyBuf );
+ if( ndxTag->cpKeyBuf2 )
+ free( ndxTag->cpKeyBuf2 );
+ if( ndxTag->exp ){
+ delete ndxTag->exp;
+ ndxTag->exp = NULL;
+ }
+ ndxTag->sKeyExpression.Set( NULL );
+ ndxTag->sTagName.Set( NULL );
+ free( ndxTag );
+ ndxTag = NULL;
+ }
+ if( cNodeBuf )
+ free( cNodeBuf );
+}
+/***********************************************************************/
+//! @brief Add key.
+/*!
+ Add key. If this is a unique index, this logic assumes the duplicate
+ check logic was already done.
+
+ \param vpTag Tag to update.
+ \param ulRecNo Record number to add key for.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::AddKey( void * vpTag, xbUInt32 ulRecNo ){
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && npTag->bFoundSts )
+ return XB_NO_ERROR;
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iHeadNodeUpdateOpt = 2;
+
+
+ try{
+
+ if(( iRc = xbIxNdx::KeySetPosAdd( npTag, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ xbInt32 lKeyCnt = GetKeyCount( npTag->npCurNode );
+ if( lKeyCnt < npTag->iKeysPerBlock ){
+ // Section A - add key to appropriate position if space available
+ if(( iRc = InsertNodeL( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ } else {
+ // land here with a full leaf node
+ iHeadNodeUpdateOpt = 1;
+ // section B - split the leaf node
+ xbIxNode * npRightNode = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 );
+ if( !npRightNode ){
+ iErrorStop = 120;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if(( iRc = SplitNodeL( npTag, npTag->npCurNode, npRightNode, npTag->npCurNode->iCurKeyNo, npTag->cpKeyBuf, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ xbUInt32 ulTempBlockNo = npRightNode->ulBlockNo;
+
+ // section C - go up the tree, splitting nodes as necessary
+ xbIxNode * npParent = npTag->npCurNode->npPrev;
+ while( npParent && GetKeyCount( npParent ) >= npTag->iKeysPerBlock ){
+ npRightNode = FreeNodeChain( npRightNode );
+ npRightNode = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 );
+ if( !npRightNode ){
+ iErrorStop = 140;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ if(( iRc = SplitNodeI( npTag, npParent, npRightNode, npParent->iCurKeyNo, ulTempBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ ulTempBlockNo = npRightNode->ulBlockNo;
+ npTag->npCurNode = npParent;
+ npParent = npParent->npPrev;
+ }
+
+ // section D - if cur node is split root, create new root
+ if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock ){
+ // xbase->WriteLogMessage( "Section d" );
+ if(( iRc = AddKeyNewRoot( npTag, npTag->npCurNode, npRightNode )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ npRightNode = FreeNodeChain( npRightNode );
+
+ } else {
+ // else section E, put key in parent
+ if(( iRc = InsertNodeI( vpTag, npParent, npParent->iCurKeyNo, npRightNode->ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ npRightNode = FreeNodeChain( npRightNode );
+ }
+ }
+
+ // update the header
+ if(( iRc = WriteHeadBlock( iHeadNodeUpdateOpt )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ // ---- free whatever is left of the node chain here, this might not be right, might need to restore it to
+ // the point right after SetKeyPosAdd
+ npTag->npNodeChain = FreeNodeChain( ndxTag->npNodeChain );
+ npTag->npCurNode = NULL;
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::AddKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Add new root node.
+/*!
+ \param npTag Tag to update.
+ \param npLeft Left node.
+ \param npRight Right node.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::AddKeyNewRoot( xbNdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbString sMsg;
+ char *pLastKey = NULL;
+
+ try{
+ xbIxNode *npRoot = AllocateIxNode( GetBlockSize() + (xbUInt32) npTag->iKeyItemLen, 1 );
+ if( !npRoot ){
+ iErrorStop = 100;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ npTag->ulRootBlock = npRoot->ulBlockNo;
+ pLastKey = (char *) malloc( (size_t) ndxTag->iKeyLen );
+ if(( iRc = GetLastKeyForBlockNo( npTag, npLeft->ulBlockNo, pLastKey )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ char * pTrg = npRoot->cpBlockData;
+
+ // set no of keys to 1
+ ePutUInt32( pTrg, 1 );
+
+ // set the left node number
+ pTrg += 4;
+ ePutUInt32( pTrg, npLeft->ulBlockNo );
+
+ // set the key
+ pTrg+= 8;
+ memcpy( pTrg, pLastKey, (size_t) npTag->iKeyLen );
+
+ // set the right node number
+ pTrg+= (npTag->iKeyItemLen - 8);
+ ePutUInt32( pTrg, npRight->ulBlockNo );
+
+ // write out the block
+ if(( iRc = WriteBlock( npRoot->ulBlockNo, GetBlockSize(), npRoot->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if( pLastKey )
+ free( pLastKey );
+ NodeFree( npRoot );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::AddKeyNewRoot() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( pLastKey )
+ free( pLastKey );
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Append node to node chain.
+/*!
+ Append a node to the current node chain for a given tag.
+
+ \param vpTag Tag to update.
+ \param npNode Node to add to node chain.
+ \returns void
+*/
+void xbIxNdx::AppendNodeChain( void *vpTag, xbIxNode * npNode ){
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ if( npTag->npNodeChain == NULL ){
+ npTag->npNodeChain = npNode;
+ npTag->npCurNode = npNode;
+ } else {
+ npNode->npPrev = npTag->npCurNode;
+ npTag->npCurNode->npNext = npNode;
+ npTag->npCurNode = npNode;
+ }
+ // time stamp the node chain
+ GetFileMtime( npTag->tNodeChainTs );
+}
+
+/***********************************************************************/
+//! @brief Allocate a node.
+/*!
+ \param ulBufSize Buffer size.
+ \param iOpt 0 - Don't update the node block number on the node.
+ 1 - Set node block number to the next available block number.
+ \returns Pointer to new node.
+*/
+
+xbIxNode * xbIxNdx::AllocateIxNode( xbUInt32 ulBufSize, xbInt16 iOpt ){
+ xbIxNode *n = xbIx::AllocateIxNode( ulBufSize );
+ if( n && iOpt == 1 ) n->ulBlockNo = ndxTag->ulTotalBlocks++;
+ return n;
+}
+/***********************************************************************/
+//! @brief Check for duplicate key.
+/*!
+ \param vpTag Tag to check.
+ \returns XB_KEY_NOT_UNIQUE<br>XB_NO_ERROR
+*/
+xbInt16 xbIxNdx::CheckForDupKey( void *vpTag )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag *npTag = (xbNdxTag *) vpTag;
+ npTag->bFoundSts = xbFalse;
+ try{
+ if( GetUnique()){
+ if( npTag->iKeySts == XB_ADD_KEY || npTag->iKeySts == XB_UPD_KEY )
+ if( KeyExists( vpTag )){
+ if( GetUniqueKeyOpt() == XB_EMULATE_DBASE ){
+ npTag->bFoundSts = xbTrue;
+ return 0;
+ } else {
+ return XB_KEY_NOT_UNIQUE;
+ }
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::CheckForDupKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Check tag integrity.
+/*!
+ Check a tag for accuracy.
+
+ \param vpTag Tag to create key for.
+ \param iOpt Output message destination<br>
+ 0 = Syslog<br>
+ 1 = Stdout<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::CheckTagIntegrity( void *vpTag, xbInt16 iOpt ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iRc2;
+ xbInt16 iRc3;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulIxCnt = 0;
+ xbUInt32 ulThisRecNo = 0;
+ xbUInt32 ulPrevRecNo = 0;
+ xbBool bDone = false;
+ xbString sMsg;
+ char cKeyType;
+ char *pPrevKeyBuf = NULL;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bLocked = xbFalse;
+ #endif
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ bLocked = xbTrue;
+ }
+ #endif
+
+ memset( npTag->cpKeyBuf2, 0x00, (size_t) npTag->iKeyLen );
+ cKeyType = GetKeyType( vpTag );
+
+ sMsg.Sprintf( "Checking index type [%c]", cKeyType );
+ xbase->WriteLogMessage( sMsg, iOpt );
+
+ pPrevKeyBuf = (char *) calloc( 1, (size_t) ndxTag->iKeyLen );
+
+ // for each key in the index, make sure it is trending in the right direction
+ iRc = GetFirstKey( vpTag, 0 );
+ while( iRc == XB_NO_ERROR && !bDone ){
+ ulIxCnt++;
+ iRc = GetNextKey( vpTag, 0 );
+ if( iRc == XB_NO_ERROR ){
+ // compare this key to prev key
+ iRc2 = CompareKey( cKeyType, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ),
+ pPrevKeyBuf, (size_t) npTag->iKeyLen );
+
+ if( iRc2 < 0 ){
+ sMsg.Sprintf( "Key sequence error at key number [%ld].", ulIxCnt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ iErrorStop = 110;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ ulThisRecNo = 0;
+ if(( iRc3 = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulThisRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc3;
+ }
+
+ if( iRc2 == 0 && (ulThisRecNo <= ulPrevRecNo )){
+ sMsg.Sprintf( "Dbf record number sequence error at key number [%ld].", iOpt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ iErrorStop = 130;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+ // save this key info to prev key
+ memcpy( pPrevKeyBuf, GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen );
+ ulPrevRecNo = ulThisRecNo;
+ }
+ }
+
+ // verify the index count matches the tag count
+ xbUInt32 ulDbfCnt = 0;
+ if(( iRc = dbf->GetRecordCnt( ulDbfCnt )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if( GetUniqueKeyOpt() == XB_EMULATE_DBASE && GetUnique( vpTag )){
+ // Can't compare counts if using XB_EMULATE_DBASE and it's a unique index
+ } else {
+ if( ulDbfCnt != ulIxCnt ){
+ sMsg.Sprintf( "CheckTagIntegrity() Index entry count [%ld] does not match dbf record count [%ld]", ulIxCnt, ulDbfCnt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ iErrorStop = 150;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ // verify each record in the dbf file has a corresponding index entry
+ xbUInt32 j = 0;
+ while( j < ulDbfCnt ){
+ if(( iRc = dbf->GetRecord( ++j )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = FindKeyForCurRec( vpTag )) != XB_NO_ERROR ){
+ ulThisRecNo = j;
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ sMsg.Sprintf( "CheckTagIntegrity() Index entry count [%ld] matches dbf record count [%ld]", ulIxCnt, ulDbfCnt );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ }
+ if( pPrevKeyBuf )
+ free( pPrevKeyBuf );
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::CheckTagIntegrity() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ), iOpt );
+ if( pPrevKeyBuf )
+ free( pPrevKeyBuf );
+
+ if( iErrorStop == 170 ){
+ sMsg.Sprintf( "xbIxNdx::CheckTagIntegrity() Missing index entry for record [%d]", ulThisRecNo );
+ xbase->WriteLogMessage( sMsg, iOpt );
+ }
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bLocked ){
+ dbf->LockTable( XB_UNLOCK );
+ }
+ #endif
+ return iRc;
+
+}
+/***********************************************************************/
+//! @brief Create key for tag.
+/*!
+ Append a node to the current node chain for a given tag.
+
+ \param vpTag Tag to create key for.
+ \param iOpt 0 = Build a key for FindKey usage, only rec buf 0.<br>
+ 1 = Append Mode, Create key for an append, only use rec buf 0, set updated switch.<br>
+ 2 = Update Mode, Create old version and new version keys, check if different, set update switch appropriately.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::CreateKey( void * vpTag, xbInt16 iOpt ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ if(( iRc = npTag->exp->ProcessExpression( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if( npTag->exp->GetReturnType() == XB_EXP_CHAR ){
+ npTag->exp->GetStringResult( npTag->cpKeyBuf, (xbUInt32) npTag->iKeyLen );
+ }
+ else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC ){
+ xbDouble d;
+ npTag->exp->GetNumericResult( d );
+ memcpy( npTag->cpKeyBuf, &d, 8 );
+ }
+ else if( npTag->exp->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d;
+ npTag->exp->GetNumericResult( d );
+ memcpy( npTag->cpKeyBuf, &d, 8 );
+ }
+
+ npTag->iKeySts = 0;
+ if( iOpt == 1 )
+ npTag->iKeySts = XB_ADD_KEY;
+
+ else if( iOpt == 2 ){
+ if(( iRc = npTag->exp->ProcessExpression( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ if( npTag->exp->GetReturnType() == XB_EXP_CHAR ){
+ npTag->exp->GetStringResult( npTag->cpKeyBuf2, (xbUInt32) npTag->iKeyLen );
+ } else if( npTag->exp->GetReturnType() == XB_EXP_NUMERIC || npTag->exp->GetReturnType() == XB_EXP_DATE ){
+ xbDouble d;
+ npTag->exp->GetNumericResult( d );
+ memcpy( npTag->cpKeyBuf2, &d, 8 );
+ }
+ if( memcmp( npTag->cpKeyBuf, npTag->cpKeyBuf2, (size_t) npTag->iKeyLen ))
+ npTag->iKeySts = XB_UPD_KEY;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::CreateKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Create new tag.
+/*!
+ This routine creates a new tag. Since NDX files have only one tag,
+ this creates a new NDX file.
+
+ \param sName Tag Name, including .NDX suffix
+ \param sKey Key Expression
+ \param sFilter Filter expression. Not supported by NDX indices.
+ \param iDescending Not supported by NDX indices.
+ \param iUnique xbtrue - Unique.<br>xbFalse - Not unique.
+ \param iOverLay xbTrue - Overlay if file already exists.<br>xbFalse - Don't overlay.
+ \param vpTag Output from method Pointer to vptag pointer.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::CreateTag( const xbString &sName, const xbString &sKey,
+ const xbString &, xbInt16, xbInt16 iUnique, xbInt16 iOverLay, void **vpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag *npTag = ndxTag;
+ *vpTag = ndxTag;
+
+ try{
+ //xbString sMsg;
+ SetFileName( sName );
+
+ if( FileExists() && !iOverLay )
+ return XB_FILE_EXISTS;
+
+ if( FileIsOpen()){
+ if(( iRc = xbTruncate(0)) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc = xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+ npTag->sKeyExpression.Set( "" );
+
+ if( npTag->cpKeyBuf ){
+ free( npTag->cpKeyBuf );
+ npTag->cpKeyBuf = NULL;
+ }
+ if( npTag->cpKeyBuf2 ){
+ free( npTag->cpKeyBuf2 );
+ npTag->cpKeyBuf2 = NULL;
+ }
+ }
+ if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ //set up the key expression
+ npTag->exp = new xbExp( dbf->GetXbasePtr());
+ if(( iRc = npTag->exp->ParseExpression( dbf, sKey )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ switch( npTag->exp->GetReturnType()){
+ case XB_EXP_CHAR:
+ npTag->cKeyType = 'C';
+ npTag->iKeyType = 0;
+ npTag->iKeyLen = npTag->exp->GetResultLen();
+ break;
+
+ case XB_EXP_NUMERIC:
+ npTag->cKeyType = 'F';
+ npTag->iKeyType = 1;
+ npTag->iKeyLen = 8;
+ break;
+
+ case XB_EXP_DATE:
+ npTag->cKeyType = 'D';
+ npTag->iKeyType = 1;
+ npTag->iKeyLen = 8;
+ break;
+
+ default:
+ iErrorStop = 140;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ npTag->iUnique = iUnique;
+ npTag->ulRootBlock = 1L;
+ //npTag->ulTotalBlocks = 2l;
+ npTag->ulTotalBlocks = 2L;
+ npTag->sKeyExpression = sKey;
+
+ GetFileNamePart( npTag->sTagName );
+
+ if( npTag->iKeyLen > 100 ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ npTag->iKeyItemLen = npTag->iKeyLen + 8;
+ while(( npTag->iKeyItemLen % 4 )!= 0 ) npTag->iKeyItemLen++;
+
+ npTag->iKeysPerBlock = (xbInt16) (GetBlockSize() - 8 ) / npTag->iKeyItemLen;
+ ndxTag->cpKeyBuf = (char *) malloc( (size_t) ndxTag->iKeyLen );
+ ndxTag->cpKeyBuf2 = (char *) malloc( (size_t) ndxTag->iKeyLen );
+
+ if(( iRc = WriteHeadBlock(0)) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+ //write out block binary zeroes
+ char buf[512];
+ memset( buf, 0x00, 512 );
+ if(( iRc = xbFwrite( buf, 1, 512 )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Delete a key.
+/*!
+ This routine deletes a key from a supplied node.
+ \param vpTag Tag to delete key on.
+ \param npNode Node to delete key on.
+ \param iSlotNo Slot number of key to delete.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo )
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+
+ xbInt32 lKeyCnt = GetKeyCount( npNode );
+ xbInt16 iLen = (lKeyCnt - iSlotNo - 1) * npTag->iKeyItemLen;
+ if( !IsLeaf( vpTag, npNode ))
+ iLen += 4;
+
+ if( iLen > 0 ){
+ char *pTrg = npNode->cpBlockData;
+ pTrg += (4 + (npTag->iKeyItemLen * (iSlotNo)) ); //lTrgPos;
+ char *pSrc = pTrg;
+ pSrc += npTag->iKeyItemLen;
+ memmove( pTrg, pSrc, (size_t) iLen );
+ }
+
+ // set the new number of keys
+ ePutUInt32( npNode->cpBlockData, (xbUInt32) lKeyCnt - 1 );
+
+ // write out the block
+ if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::DeleteFromNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return XB_NO_ERROR;
+}
+
+/***********************************************************************/
+//! @brief Delete a key.
+/*!
+ This routine deletes a key. It assumes the key to delete
+ is the current key in the node chain.
+
+ \param vpTag Tag to delete key on.
+
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::DeleteKey( void *vpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ // save copy of node chain to reset to after delete completed
+ xbIxNode *npSaveNodeChain = npTag->npNodeChain;
+ npTag->npNodeChain = NULL;
+ xbIxNode * npSaveCurNode = npTag->npCurNode;
+
+ try{
+
+ xbString sMsg;
+
+ if(( iRc = xbIxNdx::KeySetPosDel( npTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // Delete key needs to handle two scenarios
+ // 1 - if the delete is on the only key of a leaf node, then traverse up the tree, trimming as needed
+ // 2 - if the last key on a node is deleted, and the key value is not the same as the prev key value
+ // go up the tree looking for an interior node needing updated key value
+
+ xbInt32 lOrigKeyCnt = GetKeyCount( npTag->npCurNode );
+ if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ if( lOrigKeyCnt == 1 ){
+ // scenario 1
+ xbBool bDone = xbFalse;
+ xbBool bIsLeaf = xbFalse;
+ xbInt32 lKeyCnt;
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+
+ while( npTag->npCurNode && !bDone ){
+ lKeyCnt = GetKeyCount( npTag->npCurNode );
+ bIsLeaf = IsLeaf( npTag, npTag->npCurNode );
+ if( lKeyCnt > 0 ){
+ if(( iRc = DeleteFromNode( npTag, npTag->npCurNode, npTag->npCurNode->iCurKeyNo )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ if( (bIsLeaf && lKeyCnt > 1) || (!bIsLeaf && lKeyCnt > 0) )
+ bDone = xbTrue;
+ else
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+ }
+ } else if( npTag->npCurNode->iCurKeyNo == (xbUInt32) lOrigKeyCnt - 1 ){
+
+ // scenario 2
+ // if last two keys identical, then nothing to do, else go up looking for a key to change
+ if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ),
+ GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen ),
+ (size_t) npTag->iKeyLen )){
+
+ xbIxNode *pNode = npTag->npCurNode->npPrev;
+ char *pSrc = GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo-1, npTag->iKeyItemLen );
+
+ while( pNode && pNode->ulBlockNo != npTag->ulRootBlock && pNode->iCurKeyNo == (xbUInt32) GetKeyCount( pNode ) )
+ pNode = pNode->npPrev;
+ if( pNode ){
+ if( pNode->iCurKeyNo < (xbUInt32) GetKeyCount( pNode )){
+ char *pTrg = pNode->cpBlockData;
+ pTrg += 12 + (pNode->iCurKeyNo * (xbUInt32) npTag->iKeyItemLen);
+ for( xbInt16 i = 0; i < npTag->iKeyLen; i++ )
+ *pTrg++ = *pSrc++;
+
+ // write out the block
+ if(( iRc = WriteBlock( pNode->ulBlockNo, GetBlockSize(), pNode->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ }
+ }
+ }
+
+ // restore node chain to pre delete status (which should be post add status)
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npNodeChain = npSaveNodeChain;
+ npTag->npCurNode = npSaveCurNode;
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::DeleteKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( npSaveNodeChain ){
+ npTag->npNodeChain = npSaveNodeChain;
+ npSaveNodeChain = FreeNodeChain( npSaveNodeChain );
+ npTag->npCurNode = npSaveCurNode;
+ }
+ }
+ return iRc;
+}
+
+
+/***********************************************************************/
+//! @brief Delete tag.
+/*!
+ In the case of an ndx tag, it deletes the ndx file as it contains
+ only one tag.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::DeleteTag( void * ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ //xbNdxTag * npTag;
+ //vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+
+ // if open, close it
+ if( FileIsOpen()){
+ if(( iRc = Close()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ // delete file
+ if(( iRc = xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+
+//! @brief Dump a block for a given tag.
+/*!
+ Dump blocks for given tag for debugging purposes.
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \param vpTag - Not required for single tag NDX files.
+ \returns void
+*/
+
+xbInt16 xbIxNdx::DumpTagBlocks( xbInt16 iOpt, void * ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 lNoOfKeys;
+ char *p;
+ xbString s;
+ xbBool bIsLeaf = false;
+
+ try{
+ if( !FileIsOpen()){
+ iRc = XB_NOT_OPEN;
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ xbUInt32 ulStartBlock;
+ xbUInt32 ulEndBlock;
+ ulStartBlock = 1;
+ ulEndBlock = ndxTag->ulTotalBlocks;
+
+ for( xbUInt32 lBlk = ulStartBlock; lBlk < ulEndBlock; lBlk++ ){
+
+ memset( cNodeBuf, 0x00, XB_NDX_BLOCK_SIZE );
+ if(( iRc = ReadBlock( lBlk, XB_NDX_BLOCK_SIZE, cNodeBuf )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ p = cNodeBuf;
+ lNoOfKeys = eGetUInt32( p );
+
+ if( eGetUInt32( p + 4 ) > 0 ){
+ bIsLeaf = false;
+ s.Sprintf( "Node # %ld - Interior Node - Key Type [%c] Key Count [%ld]", lBlk, ndxTag->cKeyType, lNoOfKeys );
+ } else {
+ bIsLeaf = true;
+ s.Sprintf( "Node # %ld - Leaf Node - Key Type [%c] Key count [%ld]", lBlk, ndxTag->cKeyType, lNoOfKeys );
+ }
+ xbase->WriteLogMessage( s, iOpt );
+ xbase->WriteLogMessage( "Key Child Dbf Rec Key", iOpt );
+ p += 4;
+ xbUInt32 ulLeftBranch;
+ xbUInt32 ulRecNo;
+ xbString sKey;
+ xbDouble d;
+
+ xbUInt32 l;
+ for( l = 0; l < lNoOfKeys; l++ ){
+ ulLeftBranch = eGetUInt32( p );
+ p+= 4;
+ ulRecNo = eGetUInt32( p );
+ p+= 4;
+ if( ndxTag->cKeyType == 'C' ){
+ sKey.Assign( p, 1, (xbUInt32) ndxTag->iKeyLen );
+ } else if( ndxTag->cKeyType == 'D' ){
+ xbInt32 lDate = (xbInt32) eGetDouble( p );
+ xbDate dt( lDate );
+ //xbString s2;
+ //dt.JulToDate8( lDate, s2 );
+ sKey.Sprintf( "%ld - %s", lDate, dt.Str());
+ } else {
+ d = eGetDouble( p );
+ sKey.Sprintf( "%f", d );
+ }
+ p+= (ndxTag->iKeyItemLen-8);
+
+ s.Sprintf( "%3d %9d %9d %s", l+1, ulLeftBranch, ulRecNo, sKey.Str() );
+ xbase->WriteLogMessage( s, iOpt );
+ }
+ if( !bIsLeaf ){
+ ulLeftBranch = eGetUInt32( p );
+ s.Sprintf( "%3d %9d", l+1, ulLeftBranch );
+ xbase->WriteLogMessage( s, iOpt );
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::DumpTagBlocks() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Dump index file header.
+/*!
+ Dump a index file header for debugging purposes.
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::DumpHeader( xbInt16 iOpt, xbInt16 ){
+ xbString s;
+ xbInt16 iRc;
+
+ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR )
+ return iRc;
+
+ s.Sprintf( "Index Header Node for %s", GetFileName().Str());
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "--------------------------------" );
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "Root block = %ld", ndxTag->ulRootBlock );
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "Total blocks = %ld", ndxTag->ulTotalBlocks );
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "Key types = %c,%d", ndxTag->cKeyType, ndxTag->iKeyType );
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "Key Length = %d", ndxTag->iKeyLen );
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "Keys Per Block = %d", ndxTag->iKeysPerBlock );
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "Key Item Len = %ld", ndxTag->iKeyItemLen );
+ xbase->WriteLogMessage( s, iOpt);
+ s.Sprintf( "Serial No = %d", ndxTag->cSerNo );
+ xbase->WriteLogMessage( s, iOpt);
+ s.Sprintf( "Unique = %d", ndxTag->iUnique );
+ xbase->WriteLogMessage( s, iOpt );
+ s.Sprintf( "KeyExpression = %s", ndxTag->sKeyExpression.Str() );
+ xbase->WriteLogMessage( s, iOpt );
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Dump the index for a tag.
+/*!
+ Stub.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbIxNdx::DumpIxForTag( void *, xbInt16 )
+{
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Dump the index node chain.
+/*!
+ Dump the index node chain for debugging purposes.
+ \param vpTag Tag of node chain to dump.
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns void
+*/
+void xbIxNdx::DumpIxNodeChain( void *vpTag, xbInt16 iOpt ) const
+{
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ xbString s( "Dump Node Chain" );
+ xbase->WriteLogMessage( s, iOpt );
+
+ if( npTag->npNodeChain ){
+ xbIxNode *n = npTag->npNodeChain;
+ xbInt16 iCtr = 0;
+ char cLeaf;
+ s.Sprintf( "Cnt\tThis Prev Next CurKeyNo BlockNo NoOfKeys Type" );
+ xbase->WriteLogMessage( s, iOpt );
+ while( n ){
+ IsLeaf( vpTag, n ) ? cLeaf = 'L' : cLeaf = 'I';
+ s.Sprintf( "%d\t%08x %08x %08x %08d %08d %08d %c",
+ iCtr++, n, n->npPrev, n->npNext, n->iCurKeyNo,
+ n->ulBlockNo, eGetUInt32( n->cpBlockData ), cLeaf );
+ xbase->WriteLogMessage( s, iOpt );
+ n = n->npNext;
+ }
+ } else {
+ s = "Empty Node Chain";
+ xbase->WriteLogMessage( s, iOpt );
+ }
+}
+/***********************************************************************/
+//! @brief Dump node.
+/*!
+ Dump a node for debugging purposes.
+ \param vpTag Tag of node chain to dump.
+ \param pNode Node to dump.
+ \param iOpt Output message destination<br>
+ 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns XB_INVALID_OBJECT<br>XB_NO_ERROR
+*/
+
+xbInt16 xbIxNdx::DumpNode( void *vpTag, xbIxNode *pNode, xbInt16 iOpt ) const
+{
+ xbString s;
+ xbString sKey;
+ xbUInt32 lLeftBranch;
+ xbUInt32 lRecNo;
+ xbDouble d;
+
+ if( !pNode )
+ return XB_INVALID_OBJECT;
+
+ xbIx::DumpNode( vpTag, pNode, iOpt );
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ xbUInt32 lNoOfKeys = eGetUInt32( pNode->cpBlockData );
+ xbBool bIsLeaf = IsLeaf( vpTag, pNode );
+
+ if( bIsLeaf )
+ xbase->WriteLogMessage( "Leaf node", iOpt );
+ else
+ xbase->WriteLogMessage( "Interior node", iOpt );
+
+ s.Sprintf( "Key type = [%c] No Of Keys =[%d] Prev =[%x] Next =[%x]", npTag->cKeyType, lNoOfKeys, pNode->npPrev, pNode->npNext );
+ xbase->WriteLogMessage( s, iOpt );
+
+ char *p = pNode->cpBlockData;
+ p += 4;
+
+ xbUInt32 l;
+ for( l = 0; l < lNoOfKeys; l++ ){
+
+ lLeftBranch = eGetUInt32( p );
+ p+= 4;
+ lRecNo = eGetUInt32( p );
+ p+= 4;
+
+ if( npTag->cKeyType == 'C' ){
+ sKey.Assign( p, 1, (xbUInt32) npTag->iKeyLen );
+ } else if( npTag->cKeyType == 'D' ){
+ xbInt32 lDate = (xbInt32) eGetDouble( p );
+ xbDate dt( lDate );
+ sKey.Sprintf( "%ld - %s", lDate, dt.Str());
+ } else {
+ d = eGetDouble( p );
+ sKey.Sprintf( "%f", d );
+ }
+ p+= (npTag->iKeyItemLen-8);
+ s.Sprintf( "%3d %9d %9d %s", l+1, lLeftBranch, lRecNo, sKey.Str() );
+ xbase->WriteLogMessage( s, iOpt );
+ }
+ if( !bIsLeaf ){
+ lLeftBranch = eGetUInt32( p );
+ s.Sprintf( "%3d %9d", l+1, lLeftBranch );
+ xbase->WriteLogMessage( s.Str(), iOpt );
+ }
+ return XB_NO_ERROR;
+}
+#endif
+/***********************************************************************/
+//! @brief Find key
+/*!
+ \param vpTag Pointer to tag to search.
+ \param vpKey Void pointer to key data to search on.
+ \param lSearchKeyLen Length of key to search for.
+ \param iRetrieveSw xbTrue - Retrieve the record if key found.<br>
+ xbFalse - Don't retrieve record, check for key existence only.
+ \returns XB_NO_ERROR - Key found.<br>
+ XB_NOT_FOUND - Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::FindKey( void *vpTag, const void *vpKey, xbInt32 lSearchKeyLen,
+ xbInt16 iRetrieveSw ){
+
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbString sMsg;
+ // xbInt16 iFindSts;
+ try{
+ // clean up any previous table updates before moving on
+ if( iRetrieveSw ){
+ if( dbf->GetDbfStatus() == XB_UPDATED ){
+ if( dbf->GetAutoCommit() == 1 ){
+ if(( iRc = dbf->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = dbf->Abort()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ xbUInt32 ulNoOfKeys;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ char cKeyType = npTag->cKeyType;
+
+ if( npTag->npNodeChain ){
+
+ // determine if the index has been updated since the last time it was used
+ time_t tFileTs;
+ if(( iRc = GetFileMtime( tFileTs )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if( npTag->tNodeChainTs < tFileTs ){
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, (npTag->ulRootBlock ), 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ } else {
+ // pop up the chain looking for appropriate starting point
+ xbBool bDone = false;
+ xbIxNode * TempIxNode;
+ while( npTag->npCurNode && !bDone && npTag->npCurNode->ulBlockNo != npTag->ulRootBlock ){ // not root node
+ iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, 0, npTag->iKeyItemLen ), (size_t) lSearchKeyLen );
+ if( iRc <= 0 ){
+ TempIxNode = npTag->npCurNode;
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ } else {
+ // get the number of keys on the block and compare the key to the rightmost key
+ xbUInt32 ulKeyCtr = eGetUInt32( npTag->npCurNode->cpBlockData ) - 1;
+ iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, ulKeyCtr, npTag->iKeyItemLen), (size_t) lSearchKeyLen );
+
+ if( iRc > 0 ){
+ TempIxNode = npTag->npCurNode;
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ } else {
+ bDone = true;
+ }
+ }
+ }
+ }
+ } else {
+ if(( iRc = GetBlock( npTag, (npTag->ulRootBlock ), 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+
+
+ // if cur node is the base node and no keys on this node, then the index is empty
+ if( npTag->ulRootBlock == npTag->npCurNode->ulBlockNo ){
+ ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData );
+ if( ulNoOfKeys == 0 && IsLeaf( npTag, npTag->npCurNode )){
+ // iRc = XB_EMPTY;
+
+ iRc = XB_NOT_FOUND;
+ return iRc;
+ }
+ }
+
+ // should be in the appropriate position in the node chain to continue the search from here
+ // run down through the interior nodes
+ xbInt16 iSearchRc = 0;
+ xbUInt32 ulKeyPtr = 0;
+
+ while( !IsLeaf( npTag, npTag->npCurNode ) ){
+
+ // get the number of keys on the block and compare the key to the rightmost key
+ ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData );
+ if( ulNoOfKeys == 0 ) // interior nodes can have zero keys, just a link to the next lower node
+ npTag->npCurNode->iCurKeyNo = 0;
+ else
+ {
+ iRc = CompareKey( cKeyType, vpKey, GetKeyData( npTag->npCurNode, ulNoOfKeys - 1, npTag->iKeyItemLen), (size_t) lSearchKeyLen );
+ if( iRc > 0 ){
+ npTag->npCurNode->iCurKeyNo = ulNoOfKeys;
+ } else {
+ npTag->npCurNode->iCurKeyNo = (xbUInt32) BSearchBlock( cKeyType, npTag->npCurNode,
+ (xbInt32) npTag->iKeyItemLen, vpKey, (xbInt32) lSearchKeyLen, iSearchRc );
+ }
+ }
+
+ if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+
+ // should be on a the correct leaf node, it may or may not contain the actual key
+ ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData );
+ xbInt16 iCompRc = 0;
+
+ if( ulNoOfKeys == 0 ){
+ iRc = XB_NOT_FOUND;
+ return iRc;
+ } else {
+
+ iRc = BSearchBlock( cKeyType, npTag->npCurNode, npTag->iKeyItemLen, vpKey, lSearchKeyLen, iCompRc );
+
+ // iCompRc
+ // 0 found
+ // < 0 eof encountered, search key > last key in file
+ // > 0 not found, positioned to next key
+
+
+ // std::cout << "xbIxNdx::FindKey -Rc = " << iRc << " CompRc = " << iCompRc << " NoOfKeys = " << ulNoOfKeys << " blk no = " << npTag->npCurNode->ulBlockNo << "\n";
+
+ if( iCompRc >= 0 ){
+ npTag->npCurNode->iCurKeyNo = (xbUInt32) iRc;
+ if( iRetrieveSw ){
+ xbUInt32 ulKey = npTag->npCurNode->iCurKeyNo;
+ if( ulKey >= ulNoOfKeys ) // if position past end of keys, need to go back one and position to last key
+ ulKey--;
+
+ if(( iRc = GetDbfPtr( vpTag, ulKey, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ if( iCompRc == 0 )
+ return XB_NO_ERROR;
+ else if( iCompRc > 0 )
+ return XB_NOT_FOUND;
+ else
+ return XB_EOF;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::FindKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Find key for current record
+/*!
+ This routine is called when updating a key.
+
+ \param vpTag Pointer to tag to search.
+ XB_NOT_FOUND Key not found.<br>
+ <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::FindKeyForCurRec( void * vpTag )
+{
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+ if(( iRc = CreateKey( vpTag, 0 )) < XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // find key
+ iRc = FindKey( vpTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 );
+ if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY || iRc == XB_EOF )
+ return iRc;
+
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ // if keys are unique, and the recrd number matches, then we are good
+ if( GetUnique() )
+ return XB_NO_ERROR;
+
+ // get here if key found and not unique, need to move forward looking for correct rec no
+ xbUInt32 ulDbfRecNo = dbf->GetCurRecNo();
+ xbBool bKeysMatch = true; // keys match?
+ xbBool bCurRecsMatch = false; // cur recod number matches?
+ xbUInt32 ulIxRecNo = 0;
+ char cKeyType = GetKeyType( vpTag );
+
+ if(( iRc = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if( ulIxRecNo == ulDbfRecNo )
+ bCurRecsMatch = true;
+
+ xbInt16 iCompRc;
+ while( !bCurRecsMatch && bKeysMatch ){
+
+ if(( iRc = GetNextKey( vpTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ // do compare key here
+ iCompRc = CompareKey( cKeyType, npTag->cpKeyBuf, GetKeyData( npTag->npCurNode, 0, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen );
+ if( iCompRc != 0 )
+ bKeysMatch = false;
+ else{
+ if(( iRc = GetDbfPtr( vpTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if( ulIxRecNo == ulDbfRecNo )
+ bCurRecsMatch = true;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::FindKeyForCurRec() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return XB_NO_ERROR;
+}
+
+/***********************************************************************/
+//! @brief Get dbf record number for given key number.
+/*!
+ \param vpTag Tag to retrieve dbf rec number on.
+ \param iKeyNo Key number for retrieval
+ \param np Pointer to node
+ \param ulDbfPtr- Output dbf record number
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulDbfPtr ) const {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ #ifdef XB_DEBUG_SUPPORT
+ // turn this off in production mode for better performance
+ xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData );
+ if( iKeyNo < 0 || iKeyNo > (xbInt16) --ulNoOfKeys ){
+ iErrorStop = 100;
+ throw XB_INVALID_KEYNO;
+ }
+ #endif
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ char *p = ( np->cpBlockData);
+ p += (8 + (iKeyNo * npTag->iKeyItemLen));
+ ulDbfPtr = eGetUInt32 ( p );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::GetDbfPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Get the first key for the given tag.
+/*!
+ \param vpTag Tag to retrieve first key on.
+ \param iRetrieveSw xbTrue - Retrieve the record on success.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+ // clear out any history
+ if( npTag->npNodeChain ){
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+ }
+ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // lRootBlock is now available
+ if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // if no keys on this node, and it's a leaf node then the index is empty
+ xbUInt32 ulKeyPtr = eGetUInt32( npTag->npCurNode->cpBlockData );
+ if( ulKeyPtr == 0 && IsLeaf( npTag, npTag->npCurNode )){
+ iRc = XB_EMPTY;
+ return iRc;
+ }
+ while( !IsLeaf( npTag, npTag->npCurNode )) // go down the chain looking for a leaf node
+ {
+ if(( iRc = GetKeyPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+ if( iRetrieveSw ){
+ if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ }
+ catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::GetFirstKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Get the key expression for the given tag.
+/*!
+ \param vpTag Tag to retrieve expression from.
+ \returns Key expression.
+*/
+
+xbString &xbIxNdx::GetKeyExpression( const void * vpTag ) const{
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ return npTag->sKeyExpression;
+}
+
+
+/***********************************************************************/
+//! @brief Get the key filter for the given tag.
+/*!
+ NDX index files do not support filters. This returns NULL.
+ \returns NULL.
+*/
+
+xbString &xbIxNdx::GetKeyFilter( const void * ) const{
+ return sNullString;
+}
+/***********************************************************************/
+//! @brief Get the key length for the given tag.
+/*!
+ \param vpTag Tag to retrieve key length for.
+ \returns Length of key.
+*/
+xbInt32 xbIxNdx::GetKeyLen( const void * vpTag ) const{
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ return npTag->iKeyLen;
+}
+/***********************************************************************/
+//! @brief Get child node number for given key number.
+/*!
+ \param vpTag Tag to retrieve dbf rec number on.
+ \param iKeyNo Key number for retrieval
+ \param np Pointer to node
+ \param ulKeyPtr Output node number
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *np, xbUInt32 &ulKeyPtr ) const {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+ #ifdef XB_DEBUG_SUPPORT
+ // turn this off in production mode for better performance
+ xbUInt32 ulNoOfKeys = eGetUInt32 ( np->cpBlockData );
+ if( iKeyNo < 0 || iKeyNo > (xbInt16) ulNoOfKeys ){
+ iErrorStop = 100;
+ throw XB_INVALID_KEYNO;
+ }
+ #endif
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ char *p = ( np->cpBlockData);
+ p += (4 + (iKeyNo * npTag->iKeyItemLen));
+ ulKeyPtr = eGetUInt32 ( p );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::GetKeyPtr() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Returns key update status.
+/*!
+ \param vpTag Tag to check status on.
+ \returns XB_UPD_KEY Key updated.<br>
+ XB_DEL_KEY Key deleted.<br>
+ XB_ADD_KEY Key added.<br>
+ 0 No key updates
+
+*/
+xbInt16 xbIxNdx::GetKeySts( void *vpTag ) const{
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ return npTag->iKeySts;
+}
+/***********************************************************************/
+//! @brief Get character key type for given tag.
+/*!
+ \param vpTag Tag to retrieve key type for.
+ \returns Char key type.
+*/
+
+char xbIxNdx::GetKeyType( const void * vpTag ) const{
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ return npTag->cKeyType;
+}
+
+/***********************************************************************/
+//! @brief Get numeric key type for given tag.
+/*!
+ \param vpTag Tag to retrieve first key for.
+ \returns Numeric key type.
+*/
+xbInt16 xbIxNdx::GetKeyTypeN( const void * vpTag ) const{
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ return npTag->iKeyType;
+}
+/***********************************************************************/
+//! @brief Get the last key for the given tag.
+/*!
+ \param vpTag Tag to retrieve last key on.
+ \param iRetrieveSw xbTrue - Retrieve the record on success.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::GetLastKey( void *vpTag, xbInt16 iRetrieveSw ){
+ return GetLastKey( 0, vpTag, iRetrieveSw );
+// return GetLastKey( 0, vpTag, 1 );
+
+}
+/***********************************************************************/
+//! @brief Get the last key for the given tag and starting node.
+/*!
+ \param ulNodeNo Starting node
+ \param vpTag Tag to retrieve last key on.
+ \param iRetrieveSw xbTrue - Retrieve the record if key found.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 iRetrieveSw ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulKeyPtr = 0;
+ xbUInt32 ulNoOfKeys = 0;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+ // clear out any history
+ if( npTag->npNodeChain ){
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+ }
+ if( ulNodeNo == 0 ){
+ if(( iRc = ReadHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // lRootBlock is now available
+ if(( iRc = GetBlock( npTag, npTag->ulRootBlock, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = GetBlock( npTag, ulNodeNo, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ // if no keys on this node, then the index is empty
+ ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData );
+ if( ulNoOfKeys == 0 && IsLeaf( npTag, npTag->npCurNode )){
+ iRc = XB_EMPTY;
+ return iRc;
+ }
+ npTag->npCurNode->iCurKeyNo = ulNoOfKeys;
+ while( !IsLeaf( npTag, npTag->npCurNode ) ){ // go down the chain looking for a leaf node
+ npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData );
+ if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData );
+ npTag->npCurNode->iCurKeyNo = ulNoOfKeys;
+ }
+ // get here on a leaf node, it has one fewer iCurKeyNo
+ npTag->npCurNode->iCurKeyNo--;
+ if( iRetrieveSw ){
+ ulNoOfKeys = eGetUInt32( npTag->npCurNode->cpBlockData );
+ if(( iRc = GetDbfPtr( npTag, ulNoOfKeys - 1, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::GetLastKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Get the last key for a block number.
+/*!
+ \param vpTag Tag to retrieve first key on.
+ \param ulBlockNo Block number for key retrieval.
+ \param cpBuf output buffer for key placement
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpBuf ){
+
+ // returns the last key for a given block number
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+ xbIxNode * npSaveNodeChain = npTag->npNodeChain;
+ xbIxNode * npSaveCurNode = npTag->npCurNode;
+ npTag->npNodeChain = NULL;
+
+ if(( iRc = GetLastKey( ulBlockNo, npTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // set the key
+ memcpy( cpBuf, GetKeyData( npTag->npCurNode, GetKeyCount( npTag->npCurNode ) - 1, npTag->iKeyItemLen ), (size_t) npTag->iKeyLen );
+
+ // free memory
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npNodeChain = npSaveNodeChain;
+ npTag->npCurNode = npSaveCurNode;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::GetLastKeyForBlockNo() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ) );
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Get the next key for the given tag.
+/*!
+ \param vpTag Tag to retrieve next key on.
+ \param iRetrieveSw xbTrue - Retrieve the record on success.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::GetNextKey( void * vpTag, xbInt16 iRetrieveSw ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+ if( !npTag->npNodeChain )
+ return GetFirstKey( vpTag, iRetrieveSw );
+
+ // more keys on this node? if yes, get the next one to the right
+ xbUInt32 ulKeyPtr;
+ if((eGetUInt32( npTag->npCurNode->cpBlockData ) -1) > npTag->npCurNode->iCurKeyNo ){
+ npTag->npCurNode->iCurKeyNo++;
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if( iRetrieveSw ){
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ } else {
+ return iRc;
+ }
+ } else {
+ return iRc;
+ }
+ }
+ // if at end of head node, then eof
+ if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock )
+ return XB_EOF;
+
+ // This logic assumes that interior nodes have n+1 left node no's where n is he the number of keys in the node
+ xbIxNode * TempIxNode = npTag->npCurNode;
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+
+ while( npTag->npCurNode->iCurKeyNo >= eGetUInt32( npTag->npCurNode->cpBlockData ) &&
+ (npTag->npCurNode->ulBlockNo != npTag->ulRootBlock )){
+ TempIxNode = npTag->npCurNode;
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ }
+
+ // head node and at end of head node, then eof
+ if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock &&
+ npTag->npCurNode->iCurKeyNo == eGetUInt32( npTag->npCurNode->cpBlockData ))
+ return XB_EOF;
+
+ // move one to the right
+ npTag->npCurNode->iCurKeyNo++;
+
+ if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ while( !IsLeaf( npTag, npTag->npCurNode )) // go down the chain looking for a leaf node
+ {
+ if(( iRc = GetKeyPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ if( iRetrieveSw ){
+ if(( iRc = GetDbfPtr( npTag, 0, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ }
+ catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::GetNextKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Get the previous key for the given tag.
+/*!
+ \param vpTag Tag to retrieve previous key on.
+ \param iRetrieveSw xbTrue - Retrieve the record on success.<br>
+ xbFalse - Don't retrieve record.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::GetPrevKey( void * vpTag, xbInt16 iRetrieveSw ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ // This method assumes last index call landed on a valid key.
+ // If last call resulted in an error, this method will returns XB_BOF
+
+ try{
+ if( !npTag->npNodeChain )
+ return GetLastKey( 0, vpTag, iRetrieveSw );
+
+ xbUInt32 ulKeyPtr;
+ if( npTag->npCurNode->iCurKeyNo > 0 ){
+ npTag->npCurNode->iCurKeyNo--;
+
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if( iRetrieveSw ){
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ } else {
+ return iRc;
+ }
+ }
+ }
+
+ // next two lines might have been an issue
+ if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock && GetKeyCount( npTag->npCurNode ) == 0 && IsLeaf( npTag, npTag->npCurNode ))
+ return XB_EMPTY;
+
+ if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock )
+ return XB_BOF;
+
+ // This logic assumes that interior nodes have n+1 left node no's where n is he the number of keys in the node
+ xbIxNode * TempIxNode = npTag->npCurNode;
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+
+ while( npTag->npCurNode->iCurKeyNo == 0 &&
+ (npTag->npCurNode->ulBlockNo != npTag->ulRootBlock )){
+ TempIxNode = npTag->npCurNode;
+ npTag->npCurNode = npTag->npCurNode->npPrev;
+ TempIxNode = FreeNodeChain( TempIxNode );
+ }
+
+ // head node and at end of head node, then bof
+ if( npTag->npCurNode->ulBlockNo == npTag->ulRootBlock &&
+ npTag->npCurNode->iCurKeyNo == 0 )
+ return XB_BOF;
+
+ // move one to the left
+ npTag->npCurNode->iCurKeyNo--;
+
+ if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ while( !IsLeaf( npTag, npTag->npCurNode )){ // go down the chain looking for a leaf node
+ npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData );
+ if(( iRc = GetKeyPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if(( iRc = GetBlock( npTag, ulKeyPtr, 1, (xbUInt32) npTag->iKeyItemLen )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+
+ npTag->npCurNode->iCurKeyNo = eGetUInt32( npTag->npCurNode->cpBlockData ) - 1;
+ if( iRetrieveSw ){
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetRecord( ulKeyPtr )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+ }
+
+ catch( xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::GetPrevKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Get the sort order for given tag.
+/*!
+ Ndx indices only support ascending keys.
+ \returns 0
+*/
+xbBool xbIxNdx::GetSortOrder( void * ) const{
+ return 0;
+}
+/***********************************************************************/
+//! @brief Get tag for tag number.
+/*!
+ \returns Pointer to ndx tag.
+*/
+void * xbIxNdx::GetTag( xbInt16 ) const{
+ return ndxTag;
+}
+/***********************************************************************/
+//! @brief Get tag for tag name.
+/*!
+ \returns Pointer to ndx tag.
+*/
+void * xbIxNdx::GetTag( xbString & ) const{
+ return ndxTag;
+}
+
+/***********************************************************************/
+//! @brief Get tag count.
+/*!
+ NDX index files contain one tag.
+ \returns 1
+*/
+
+xbInt16 xbIxNdx::GetTagCount() const{
+ return 1;
+}
+/***********************************************************************/
+//! @brief Get tag name.
+/*!
+ \returns Tag name.
+*/
+xbString &xbIxNdx::GetTagName( void * ) const {
+// char * xbIxNdx::GetTagName( void * ) const {
+
+ return ndxTag->sTagName;
+
+}
+/***********************************************************************/
+//! @brief Get tag name.
+/*!
+ \returns Tag name.
+*/
+const char * xbIxNdx::GetTagName( void *, xbInt16 ) const {
+ return ndxTag->sTagName;
+}
+
+/***********************************************************************/
+//! @brief Get the unique setting for given tag.
+/*!
+ \param vpTag Tag to unique setting on.
+ \returns xbTrue - Unique index.<br> xbFalse - Not unique index.
+*/
+xbBool xbIxNdx::GetUnique( void * vpTag ) const{
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ return npTag->iUnique;
+}
+
+/***********************************************************************/
+//! @brief Insert key into interior node.
+/*!
+ Insert key into non-full interior node.<br>
+ Assumes valid inputs
+
+ \param vpTag Tag in play.
+ \param npNode Node for insertion.
+ \param iSlotNo Slot number to insert key.
+ \param ulPtr Pointer number to insert.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 ulPtr ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char *pTrg;
+ xbInt16 iSrcPos;
+ char *pLastKey = NULL;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+ // update number of keys on the node
+ xbInt32 lKeyCnt = GetKeyCount( npNode );
+ iSrcPos = 12 + (iSlotNo * npTag->iKeyItemLen);
+
+ char *pSrc = npNode->cpBlockData;
+ pSrc += iSrcPos;
+
+ // if not appending to the end of the node, make some room, move things to the right
+ if( iSlotNo < lKeyCnt ) {
+ xbInt16 iCopyLen = ((lKeyCnt - iSlotNo) * npTag->iKeyItemLen) - 4;
+ pTrg = pSrc;
+ pTrg += npTag->iKeyItemLen;
+ memmove( pTrg, pSrc, (size_t) iCopyLen );
+ }
+
+ // get the right most key for the left part of the split node
+ xbUInt32 ulKeyPtr2;
+ if(( iRc = GetKeyPtr( vpTag, npNode->iCurKeyNo, npNode, ulKeyPtr2 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // get the new right key value for the freshly split node
+ pLastKey = (char *) malloc((size_t) ndxTag->iKeyLen);
+ if(( iRc = GetLastKeyForBlockNo( vpTag, ulKeyPtr2, pLastKey )) != XB_NO_ERROR ){
+ iRc = 110;
+ throw iRc;
+ }
+ // write the key value
+ pTrg = pSrc;
+ char *pTrg2 = pSrc;
+ pSrc = pLastKey;
+ for( xbInt16 i = 0; i < npTag->iKeyLen; i++ )
+ *pTrg++ = *pSrc++;
+
+ pTrg2 += (npTag->iKeyItemLen - 8);
+ ePutUInt32( pTrg2, ulPtr );
+ ePutInt32( npNode->cpBlockData, ++lKeyCnt );
+
+ // write out the updated block to disk
+ if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ if( pLastKey )
+ free( pLastKey );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::InsertNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( pLastKey )
+ free( pLastKey );
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Insert key into leaf node.
+/*!
+ Insert key into non-full leaf node.<br>
+ Assumes valid inputs
+
+ \param vpTag Tag in play.
+ \param npNode Node for insertion.
+ \param iSlotNo Slot number to insert key.
+ \param ulPtr Pointer number to insert.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo,
+ char * cpKeyBuf, xbUInt32 ulPtr ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char *pSrc;
+ char *pTrg;
+ char *pKeyPos;
+ xbString sMsg;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ try{
+ xbInt32 lKeyCnt = GetKeyCount( npNode );
+ xbInt16 iKeyPos = 4 + iSlotNo * npTag->iKeyItemLen;
+ pKeyPos = npNode->cpBlockData;
+ pKeyPos += iKeyPos;
+
+ // if not appending to end, make space, move things right
+ if( iSlotNo < lKeyCnt ) {
+ xbInt16 iCopyLen = (lKeyCnt - iSlotNo) * npTag->iKeyItemLen;
+ pTrg = pKeyPos;
+ pTrg += npTag->iKeyItemLen;
+ memmove( pTrg, pKeyPos, (size_t) iCopyLen );
+ }
+ // if leaf, write rec number
+ pTrg = pKeyPos;
+ memset( pTrg, 0x00, 4 );
+ pTrg += 4;
+ ePutUInt32( pTrg, ulPtr );
+ pTrg += 4;
+
+ // write the key value
+ pSrc = cpKeyBuf;
+ for( xbInt16 i = 0; i < npTag->iKeyLen; i++ )
+ *pTrg++ = *pSrc++;
+
+ // update number of keys on the node
+ ePutInt32( npNode->cpBlockData, ++lKeyCnt );
+
+ // write out the updated block to disk
+ if(( iRc = WriteBlock( npNode->ulBlockNo, GetBlockSize(), npNode->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::InsertNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Determine node leaf status
+/*!
+ \param npNode Node to examine.
+ \returns xbTrue - Leaf node.<br> xbFalse - Interior node.
+*/
+xbBool xbIxNdx::IsLeaf( void *, xbIxNode *npNode ) const {
+ xbUInt32 ulBlock = eGetUInt32 ( npNode->cpBlockData+4 );
+ if( ulBlock > 0 ) // if the second four bytes are a number, it's an interior node
+ return false;
+ else
+ return true;
+}
+/***********************************************************************/
+//! @brief Determine if key exists.
+/*!
+ This method assumes the key has already been built and is in either
+ cpKeyBuf or dKey.
+
+ \param vpTag - Pointer to tag.
+ \returns xbTrue - Key exists.<br> xbFalse - Key does not exist.
+*/
+xbInt16 xbIxNdx::KeyExists( void * vpTag ){
+
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ xbInt16 iRc = FindKey( vpTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 );
+ if( iRc == 0 )
+ return 1;
+ else
+ return 0;
+}
+
+/***********************************************************************/
+//! @brief Set position for key add.
+/*!
+ This routine is called by the AddKey() method and is used to position
+ the node chain to the position the new key should be added to the index.
+
+ \param npTag Pointer to npTag.
+ \param ulAddRecNo Record number to add.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::KeySetPosAdd( xbNdxTag *npTag, xbUInt32 ulAddRecNo ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+
+ iRc = FindKey( npTag, npTag->cpKeyBuf, npTag->iKeyLen, 0 );
+ if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY )
+ return XB_NO_ERROR; // good position
+
+ else if( iRc != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ // get here if key was found, get the right most instance of any non unique key for append, find correct spot for update
+ if( GetUnique() == 0 ){
+ xbUInt32 ulCurRecNo;
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ xbBool bKeysMatch = xbTrue;
+ while( bKeysMatch && ulAddRecNo > ulCurRecNo && iRc == XB_NO_ERROR ){
+ if(( iRc = GetNextKey( npTag, 0 )) == XB_NO_ERROR ){
+ if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf, (size_t) npTag->iKeyLen ))
+ bKeysMatch = xbFalse;
+ else{
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulCurRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ }
+ }
+ }
+ if( iRc == XB_EOF ){ // eof condition
+ if(( iRc = GetLastKey( 0, npTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ npTag->npCurNode->iCurKeyNo++;
+ return XB_NO_ERROR;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::KeySetPos() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Set position for key add.
+/*!
+ This routine is called by the DeleteKey() method and is used to position
+ the node chain to the position the old key should be deleted from the index.
+
+ \param npTag Pointer to npTag.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::KeySetPosDel( xbNdxTag *npTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbString sMsg;
+
+ try{
+ iRc = FindKey( NULL, npTag->cpKeyBuf2, npTag->iKeyLen, 0 );
+ if( iRc == XB_NOT_FOUND || iRc == XB_EMPTY )
+ return XB_NO_ERROR; // good position
+ else if( iRc != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ xbUInt32 ulIxRecNo;
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ if( ulIxRecNo == dbf->GetCurRecNo())
+ return XB_NO_ERROR;
+ if( GetUnique() == 1 ){
+ iErrorStop = 120;
+ iRc = XB_NOT_FOUND;
+ throw iRc;
+ }
+ xbBool bFound = xbFalse;
+ xbBool bKeysMatch = xbTrue;
+ while( bKeysMatch && !bFound && iRc == XB_NO_ERROR ){
+ if(( iRc = GetNextKey( npTag, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ if( memcmp( GetKeyData( npTag->npCurNode, npTag->npCurNode->iCurKeyNo, npTag->iKeyItemLen ), npTag->cpKeyBuf2, (size_t) npTag->iKeyLen )){
+ bKeysMatch = xbFalse;
+ } else {
+ if(( iRc = GetDbfPtr( npTag, npTag->npCurNode->iCurKeyNo, npTag->npCurNode, ulIxRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ if( ulIxRecNo == dbf->GetCurRecNo())
+ bFound = xbTrue;
+ }
+ }
+ if( bFound )
+ return XB_NO_ERROR;
+ else
+ return XB_NOT_FOUND;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::KeySetPosDel() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Returns key filter status.
+/*!
+ \param vpTag Tag to check status on.
+ \returns xbtrue - Key was updated.<br>xbFalse - Key not updated.
+
+ Always true for NDX style indices.
+*/
+//inline xbBool xbIxNdx::KeyFiltered( void *vpTag ) const{
+// return xbTrue;
+//}
+
+/***********************************************************************/
+//! @brief Read head block of index file.
+/*!
+ \param iOpt 0 - Read in entire block
+ 1 - Read in only dynamic section of block
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbIxNdx::ReadHeadBlock( xbInt16 iOpt = 0 ) {
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ try{
+ if( !FileIsOpen()){
+ iRc = XB_NOT_OPEN;
+ iErrorStop = 100;
+ throw iRc;
+ }
+ xbInt16 iLen;
+ iOpt == 0 ? iLen = 512 : iLen = 21;
+
+ if(( iRc = ReadBlock( (xbUInt32) 0, (size_t) iLen, cNodeBuf )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ char *p = cNodeBuf;
+ ndxTag->ulRootBlock = eGetUInt32( p ); p+=4;
+ ndxTag->ulTotalBlocks = eGetUInt32( p ); p+=5;
+ if( iOpt == 0 ){
+ ndxTag->cKeyType = *p; p+=3;
+ ndxTag->iKeyLen = eGetInt16( p ); p+=2;
+ ndxTag->iKeysPerBlock = eGetInt16( p ); p+=2;
+ ndxTag->iKeyType = eGetInt16( p ); p+=2;
+ ndxTag->iKeyItemLen = eGetInt16( p ); p+=2;
+ ndxTag->cSerNo = *p; p+=3;
+ ndxTag->iUnique = *p; p++;
+ ndxTag->sKeyExpression.Set( p );
+
+ if( ndxTag->exp )
+ delete ndxTag->exp;
+
+ ndxTag->exp = new xbExp( xbase, dbf );
+ if(( iRc = ndxTag->exp->ParseExpression( ndxTag->sKeyExpression.Str() )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if( ndxTag->cpKeyBuf )
+ free( ndxTag->cpKeyBuf );
+ if( ndxTag->cpKeyBuf2 )
+ free( ndxTag->cpKeyBuf2 );
+
+ ndxTag->cpKeyBuf = (char *) malloc( (size_t) ndxTag->iKeyLen );
+ ndxTag->cpKeyBuf2 = (char *) malloc( (size_t) ndxTag->iKeyLen );
+
+ if( ndxTag->sTagName == "" )
+ GetFileNamePart( ndxTag->sTagName );
+
+ } else {
+ p+= 11;
+ ndxTag->cSerNo = *p;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::ReadHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Reindex a tag.
+/*!
+ \param vpTag Pointer to tag pointer.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::Reindex( void **vpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag *npTag = ndxTag;
+
+ try{
+ xbInt16 iUnique = GetUnique( *vpTag );
+
+ npTag->npNodeChain = FreeNodeChain( npTag->npNodeChain );
+ npTag->npCurNode = NULL;
+ npTag->ulRootBlock = 1L;
+ npTag->ulTotalBlocks = 2L;
+
+ if(( iRc = xbTruncate( 1024 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ char buf[512];
+ memset( buf, 0x00, 512 );
+
+ if(( iRc = WriteBlock( 1, 0, buf )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ xbUInt32 ulRecCnt = 0;
+ if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ for( xbUInt32 l = 1; l <= ulRecCnt; l++ ){
+ if(( iRc = dbf->GetRecord( l )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if(( iRc = CreateKey( npTag, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ if( iUnique ){
+ // iRc = CheckForDupKey( vpTag2 );
+ iRc = CheckForDupKey( npTag );
+ if( iRc != 0 ){
+ if( iRc < 0 ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ return XB_KEY_NOT_UNIQUE;
+ }
+ }
+
+ if(( iRc = AddKey( npTag, l )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ }
+ *vpTag = npTag;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::Reindex() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ this->DeleteTag( NULL ); // Don't leave the index in an incomplete state
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Set current tag.
+/*!
+ For ndx indices, there is only one tag.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbIxNdx::SetCurTag( xbInt16 ) {
+ xbIx::SetCurTag( ndxTag );
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Set current tag.
+/*!
+ For ndx indices, there is only one tag.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbIxNdx::SetCurTag( xbString & ) {
+ xbIx::SetCurTag( ndxTag );
+ dbf->SetCurTag( "NDX", this, GetTag(0) );
+ return XB_NO_ERROR;
+}
+
+/***********************************************************************/
+//! @brief Split an interior node
+/*!
+
+ This routine splits an interior node into two nodes, divided by dSplitFactor.<br>
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.<br>
+ For V7, if adding a key to the end of a node, it will create a right node
+ with only one key, and the left node is still full.<br><br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ This routine first inserts the key into the left node in the appropriate location
+ then splits the node based on the split factor setting.
+
+ \param vpTag Tag in play.
+ \param npLeft Left node to split.
+ \param npRight Right node to split.
+ \param iSlotNo Slot number for split.
+ \param ulPtr Pointer number to insert.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 ulPtr ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbNdxTag * npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+ xbDouble dSplitFactor = .5; // split the nodes 50/50
+ xbString sMsg;
+
+ try{
+ xbInt32 lKeyCnt = GetKeyCount( npLeft );
+ xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1;
+ xbInt32 lNewRightKeyCnt = lKeyCnt - lNewLeftKeyCnt;
+ xbInt16 iSrcPos;
+ xbInt16 iCopyLen;
+ char *pSrc;
+ char *pTrg;
+
+ // insert the key into the left node
+ if(( iRc = InsertNodeI( vpTag, npLeft, iSlotNo, ulPtr )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // move the right half of the left node to the right node
+ iSrcPos = ((lNewLeftKeyCnt + 1) * npTag->iKeyItemLen) + 4;
+ iCopyLen = (lNewRightKeyCnt * npTag->iKeyItemLen) + 4;
+ pSrc = npLeft->cpBlockData;
+ pSrc += iSrcPos;
+ pTrg = npRight->cpBlockData;
+ pTrg += 4;
+ memmove( pTrg, pSrc, (size_t) iCopyLen );
+
+ // write the new key counts into the nodes
+ pTrg = npLeft->cpBlockData;
+ ePutInt32( pTrg, lNewLeftKeyCnt );
+ pTrg = npRight->cpBlockData;
+ ePutInt32( pTrg, lNewRightKeyCnt );
+
+ // write the new key counts into the nodes
+ pTrg = npLeft->cpBlockData;
+ ePutInt32( pTrg, lNewLeftKeyCnt );
+ pTrg = npRight->cpBlockData;
+ ePutInt32( pTrg, lNewRightKeyCnt );
+
+ // write out the block
+ if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // write out the block
+ if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::SplitNodeI() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Split a leaf node.
+/*!
+ This routine splits an index leaf into two nodes, divided by dSplitFactor.<br>
+ This behaves differently than V7 Dbase. V7 does not balance the nodes.<br>
+ For V7, if adding a key to the end of a node, it will create a right node
+ with only one key, and the left node is still full.<br><br>
+
+ Possible performance improvement options.<br>
+ Two modes when splitting:<br>
+ a) Split nodes in the middle - good for random access applications<br>
+ b) Split off right node with only one key - good for applications with
+ expectation of ascending keys added moving forward.<br>
+
+ This routine first inserts the key into the left node in the appropriate location
+ then splits the node based on the split factor setting.
+
+ \param vpTag Tag in play.
+ \param npLeft Left node to split.
+ \param npRight Right node to split.
+ \param iSlotNo Slot number for split.
+ \param ulPtr Pointer number to insert.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight,
+ xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 ulPtr ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbDouble dSplitFactor = .5;
+ xbNdxTag *npTag;
+ vpTag ? npTag = (xbNdxTag *) vpTag : npTag = ndxTag;
+
+ xbString sMsg;
+ try{
+ xbInt32 lKeyCnt = GetKeyCount( npLeft );
+ xbInt32 lNewLeftKeyCnt = (xbInt32) ((lKeyCnt + 1) * dSplitFactor) + 1;
+ xbInt32 lNewRightKeyCnt = lKeyCnt + 1 - lNewLeftKeyCnt;
+
+ // xbInt16 iSrcPos;
+ xbInt16 iLen;
+ char *pSrc = npLeft->cpBlockData;
+ char *pTrg;
+
+ if(( iRc = InsertNodeL( vpTag, npLeft, iSlotNo, cpKeyBuf, ulPtr )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ // move right half off of left node to the right node
+ pSrc = npLeft->cpBlockData;
+ pSrc += ((lNewLeftKeyCnt * npTag->iKeyItemLen)+4);
+ pTrg = npRight->cpBlockData;
+ pTrg += 4;
+ iLen = lNewRightKeyCnt * npTag->iKeyItemLen;
+ memmove( pTrg, pSrc, (size_t) iLen );
+
+ // write the new key counts into the nodes
+ pTrg = npLeft->cpBlockData;
+ ePutInt32( pTrg, lNewLeftKeyCnt );
+ pTrg = npRight->cpBlockData;
+ ePutInt32( pTrg, lNewRightKeyCnt );
+
+ // write out the left block
+ if(( iRc = WriteBlock( npLeft->ulBlockNo, GetBlockSize(), npLeft->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ // write out the right block
+ if(( iRc = WriteBlock( npRight->ulBlockNo, GetBlockSize(), npRight->cpBlockData )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::SplitNodeL() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief UpdateTagKey
+/*!
+ This routine updates a key or a given tag.
+ The file header is considered to be the first 2048 bytes in the file.
+
+ \param cAction A - Add a key.<br>
+ D - Delete a key.<br>
+ R - Revise a key.<br>
+ \param vpTg - Pointer to tag.<br>
+ \param ulRecNo - Record number association with the action.<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbIxNdx::UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo ){
+
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ // ..xbNdxTag *npTag = (xbMdxTag *) vpTag;
+
+ try{
+ // save off any needed fileds for updating
+ // xbUInt32 ulTagSizeSave = mpTag->ulTagSize;
+ //xbUInt32 ulLeftChildSave = mpTag->ulLeftChild;
+ //xbUInt32 ulRightChildSave = mpTag->ulRightChild;
+
+
+ if( cAction == 'D' || cAction == 'R' ){
+// std::cout << "UpdateTagKey delete\n";
+ if(( iRc = DeleteKey( vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+
+ if( cAction == 'A' || cAction == 'R' ){
+ if(( iRc = AddKey( vpTag, ulRecNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::UpdateTagKey() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Write head block.
+/*!
+ Commit the index head node to disk.
+ \param iOpt 0 - Entire header.<br>
+ 1 - Update root block, number of blocks and seq number.<br>
+ 2 - Update sequence number only<br>
+ \returns <a href="xbretcod_8h.html">
+*/
+
+xbInt16 xbIxNdx::WriteHeadBlock( xbInt16 iOpt ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iOpt == 2 ){
+
+ // increment the serial number
+ if( ndxTag->cSerNo >= 0 && ndxTag->cSerNo < 127 )
+ ndxTag->cSerNo++;
+ else
+ ndxTag->cSerNo = 0;
+
+ if(( iRc = xbFseek( 20, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc = xbFputc( ndxTag->cSerNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ } else if( iOpt == 1 ){
+ xbRewind();
+ char buf[8];
+ ePutUInt32( &buf[0], ndxTag->ulRootBlock );
+ ePutUInt32( &buf[4], ndxTag->ulTotalBlocks );
+ if(( iRc = xbFwrite( buf, 8, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ return WriteHeadBlock( 2 );
+
+ } else if ( iOpt == 0 ){
+
+ char buf[512];
+ memset( buf, 0x00, 512 );
+ ePutUInt32( &buf[0], ndxTag->ulRootBlock );
+ ePutUInt32( &buf[4], ndxTag->ulTotalBlocks );
+ buf[9] = ndxTag->cKeyType;
+ buf[11] = 0x1B;
+ ePutInt16( &buf[12], ndxTag->iKeyLen );
+ ePutInt16( &buf[14], ndxTag->iKeysPerBlock );
+ ePutInt16( &buf[16], ndxTag->iKeyType );
+ ePutInt16( &buf[18], ndxTag->iKeyItemLen );
+ if( ndxTag-> iUnique ) buf[23] = 0x01;
+
+ for( xbUInt32 i = 0; i < ndxTag->sKeyExpression.Len(); i++ )
+ buf[i+24] = ndxTag->sKeyExpression.GetCharacter(i+1);
+
+ xbRewind();
+ if(( iRc = xbFwrite( buf, 512, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ } else {
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxNdx::WriteHeadBlock() Exception Caught. Error Stop = [%d] iRc = [%d] option = [%d] ser=[%d]", iErrorStop, iRc, iOpt, ndxTag->cSerNo );
+ xbase->WriteLogMessage( sMsg );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_NDX_SUPPORT */
+
+
+
diff --git a/src/core/xbixtdx.cpp b/src/core/xbixtdx.cpp
new file mode 100755
index 0000000..4137725
--- /dev/null
+++ b/src/core/xbixtdx.cpp
@@ -0,0 +1,661 @@
+/* xbixtdx.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+This module handles temporary index logic
+
+*/
+
+#include "xbase.h"
+
+
+
+#ifdef XB_TDX_SUPPORT
+
+
+namespace xb{
+
+/************************************************************************/
+xbIxTdx::xbIxTdx( xbDbf *dbf ) : xbIxMdx( dbf ) {
+//xbIxMdx::xbIxMdx( xbDbf *dbf ) : xbIx( dbf ){
+
+// std::cout << "xbIxTdx::Constructor()\n";
+
+ // Init(); not needed, called in xbMdx
+
+
+}
+
+/************************************************************************/
+xbIxTdx::~xbIxTdx() {
+
+// std::cout << "xbIxTdx::Destructor()\n";
+
+}
+
+/***********************************************************************/
+xbInt16 xbIxTdx::Close(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+std::cout << "xbIxTdx::Close\n";
+
+ try{
+ if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc = xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxTdx::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Create new tag.
+/*!
+ This routine creates a new tag. When complete, sets the cur tag pointer to
+ the newly created tag.
+
+
+ \param sName Tag Name, including .MDX suffix
+ \param sKey Key Expression
+ \param sFilter Filter expression.
+ \param iDescending
+ \param iUnique xbtrue - Unique.<br>xbFalse - Not unique.
+ \param iOverLay xbTrue - Overlay if file already exists.<br>xbFalse - Don't overlay.
+ \param vpTag Output from method Pointer to vptag pointer.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbIxTdx::CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag *tte = NULL;
+
+ std::cout << "xbIxTdx::CreateTag()\n";
+
+
+ // std::cout << "CreateTag() name=[" << sName.Str() << "] key=[" << sKey.Str() << "] sFilter=[" << sFilter.Str() << "]\n";
+ // std::cout << "TagUseCnt = " << iTagUseCnt << std::endl;
+
+
+ try{
+ // verify room for new tag
+ if( !( iTagUseCnt < 47 )){
+ iErrorStop = 100;
+ iRc = XB_LIMIT_REACHED;
+ throw iRc;
+ }
+
+ // verify valid tag name
+ xbString sWorker = sName;
+ sWorker.Trim();
+ if( sWorker.Len() > 10 ){
+ iErrorStop = 110;
+ iRc = XB_INVALID_TAG;
+ throw iRc;
+ }
+
+ // verify tag not already defined
+ if( iTagUseCnt > 0 ){
+ if( GetTag( sWorker )){
+ iErrorStop = 120;
+ iRc = XB_INVALID_TAG;
+ throw iRc;
+ }
+ }
+
+ // allocate a tag structure here
+ if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){
+ iErrorStop = 130;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ *vpTag = tte;
+ tte->sTagName = new xbString( sWorker );
+
+ //set up the key expression
+ sWorker = sFilter;
+ sWorker.Trim();
+ if( sWorker.Len() > 0 ){
+ if( sWorker.Len() == 0 || sWorker.Len() > 220 ){
+ iRc = XB_INVALID_TAG;
+ iErrorStop = 140;
+ throw iRc;
+ }
+ tte->sFiltExp = new xbString( sWorker );
+ tte->filter = new xbExp( dbf->GetXbasePtr());
+ if(( iRc = tte->filter->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ // tte->filter->DumpTree( 1 );
+
+ if((tte->filter->GetReturnType()) != 'L' ){
+ iRc = XB_INVALID_TAG;
+ iErrorStop = 160;
+ throw iRc;
+ }
+ tte->cHasFilter = 0x01;
+ }
+
+ //set up the key expression
+ sWorker = sKey;
+ sWorker.Trim();
+ if( sWorker.Len() == 0 || sWorker.Len() > 100 ){
+ iRc = XB_INVALID_TAG;
+ iErrorStop = 170;
+ throw iRc;
+ }
+ tte->sKeyExp = new xbString( sWorker );
+ tte->exp = new xbExp( dbf->GetXbasePtr());
+ if(( iRc = tte->exp->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ xbDate d;
+ d.Sysdate();
+ if( iTagUseCnt == 0 ){
+ // first tag, new mdx file
+ // create the file name
+
+ // create temp file
+ xbString sIxFileName;
+ if(( iRc = CreateUniqueFileName( GetTempDirectory(), "TDX", sIxFileName )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ // copy the file name to the class variable
+ this->SetFileName( sIxFileName );
+ if( FileExists() && !iOverlay ){
+ iErrorStop = 200;
+ iRc = XB_FILE_EXISTS;
+ throw iRc;
+ }
+
+ // first tag, need to create the file
+ if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ cVersion = 2;
+ cCreateYY = (char) d.YearOf() - 1900;
+ cCreateMM = (char) d.MonthOf();
+ cCreateDD = (char) d.DayOf( XB_FMT_MONTH );
+
+ GetFileNamePart( sFileName );
+ sFileName.ToUpperCase();
+
+ SetBlockSize( (xbUInt32) dbf->GetCreateMdxBlockSize());
+ iBlockFactor = GetBlockSize() / 512;
+
+ cProdIxFlag = 0; // MDX is 1
+ cTagEntryCnt = 48;
+ iTagLen = 32;
+ ulPageCnt = 4;
+ ulFirstFreePage = 0;
+ ulNoOfBlockAvail = 0;
+ cNextTag = 1;
+ c1B = 0x1B;
+ cUpdateYY = cCreateYY;
+ cUpdateMM = cCreateMM;
+ cUpdateDD = cCreateDD;
+
+ if(( iRc = WriteHeadBlock( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+ }
+
+ // populate the tag table entry structure
+ tte->ulTagHdrPageNo = ulPageCnt;
+ ulPageCnt += (xbUInt32) iBlockFactor;
+ tte->sTagName->strncpy( tte->cTagName, 10 );
+
+ // cKeyFmt is always 0x10;
+ // tested 2+ZIPCD CITY+STATE or just standalone field - always 0x10
+ tte->cKeyFmt = 0x10; // = CalcTagKeyFmt( *tte->exp );
+
+ switch( tte->exp->GetReturnType()){
+ case XB_EXP_CHAR:
+ tte->cKeyType = 'C';
+ tte->iKeyLen = tte->exp->GetResultLen();
+ tte->iSecKeyType = 0;
+ break;
+
+ case XB_EXP_NUMERIC:
+ tte->cKeyType = 'N';
+ tte->iKeyLen = 12;
+ tte->iSecKeyType = 0;
+ break;
+
+ case XB_EXP_DATE:
+ tte->cKeyType = 'D';
+ tte->iKeyLen = 8;
+ tte->iSecKeyType = 1;
+ break;
+
+ default:
+ iErrorStop = 200;
+ iRc = XB_INVALID_INDEX;
+ throw iRc;
+ }
+
+ tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 );
+ tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 );
+
+// if( 0 ){
+// printf( "ulTagHdrPageNo=[%d] cTagName=[%-11s], cLeftChild=[%d] cRightChild=[%d] cParent=[%d] cKeyType=[%c]\n\n",
+// tte->ulTagHdrPageNo, tte->cTagName, tte->cLeftChild, tte->cRightChild, tte->cParent, tte->cKeyType );
+// }
+
+ // write the new tte entry here
+ char tteBuf[21];
+ memset( tteBuf, 0x00, 21 );
+
+ ePutUInt32( &tteBuf[0], tte->ulTagHdrPageNo );
+ for( xbUInt32 l = 0; l < tte->sTagName->Len() && l < 10; l++ ){
+ tteBuf[l+4] = tte->sTagName->GetCharacter(l+1);
+ }
+ tteBuf[15] = tte->cKeyFmt;
+ tteBuf[19] = 0x02; // appears to always be a 0x02
+ tteBuf[20] = tte->cKeyType;
+
+ if(( iRc = xbFseek( (iTagUseCnt * 32) + 544, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( tteBuf, 21, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+
+
+ // Begin Tag Header
+ tte->ulRootPage = ulPageCnt;
+ tte->ulTagSize = (xbUInt32) iBlockFactor;
+ ulPageCnt += 2;
+ tte->cKeyFmt2 = 0x10;
+ if( iDescending )
+ tte->cKeyFmt2 += 0x08;
+ if( iUnique ){
+ tte->cKeyFmt2 += 0x40;
+ tte->cUnique = 0x01;
+ }
+
+ tte->cTag11 = 0x1B; // always 0x1b ?
+ tte->cSerialNo = 0x01; // version incremented with each tag update
+ tte->ulLeftChild = tte->ulRootPage;
+ tte->ulRightChild = tte->ulRootPage;
+
+ tte->cTagYY = (char) d.YearOf() - 1900;
+ tte->cTagMM = (char) d.MonthOf();
+ tte->cTagDD = (char) d.DayOf( XB_FMT_MONTH );
+
+ tte->cKeyType2 = tte->cKeyType;
+ tte->iKeyItemLen = tte->iKeyLen + 4;
+ while(( tte->iKeyItemLen % 4 ) != 0 ) tte->iKeyItemLen++;
+
+ tte->iKeysPerBlock = (xbInt16) (GetBlockSize() - 12) / tte->iKeyItemLen;
+
+ //std::cout << "-------------- create tag info\n";
+ //std::cout << "keylen=" << tte->iKeyLen << " iKeyItemLen = " << tte->iKeyItemLen << " keys per block calc = " << tte->iKeysPerBlock << "\n";
+
+ tte->cKeyFmt3 = CalcTagKeyFmt( *tte->exp );
+
+// printf( "ulRootPage=[%d] cKeyFmt2=[%d] cKeyType2=[%d] iKeyLen=[%d]iKeysPerBlock=[%d]\n", tte->ulRootPage, tte->cKeyFmt2, tte->cKeyType2, tte->iKeyLen, tte->iKeysPerBlock );
+// printf( "iSecKeyType=[%d] iKeyItemLen=[%d] cUnique=[%d] \n", tte->iSecKeyType, tte->iKeyItemLen, tte->cUnique );
+
+ char *pBuf;
+ if(( pBuf = (char *) calloc( 1, (size_t) GetBlockSize())) == NULL ){
+ iErrorStop = 230;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ char *wPtr;
+ wPtr = pBuf;
+ ePutUInt32( wPtr, tte->ulRootPage );
+
+ wPtr += 4;
+ ePutUInt32( wPtr, tte->ulTagSize );
+
+ wPtr += 4;
+ *wPtr = tte->cKeyFmt2;
+
+ wPtr++;
+ *wPtr = tte->cKeyType2;
+
+ wPtr += 2;
+ *wPtr = tte->cTag11;
+
+ wPtr += 1;
+ ePutInt16( wPtr, tte->iKeyLen );
+
+ wPtr += 2;
+ ePutInt16( wPtr, tte->iKeysPerBlock );
+
+ wPtr += 2;
+ ePutInt16( wPtr, tte->iSecKeyType );
+
+ wPtr += 2;
+ ePutInt16( wPtr, tte->iKeyItemLen );
+
+ wPtr += 2;
+ *wPtr = tte->cSerialNo;
+
+ wPtr += 3;
+ *wPtr = tte->cUnique;
+
+ wPtr++;
+ for( xbUInt32 l = 0; l < tte->sKeyExp->Len(); l++ )
+ *wPtr++ = tte->sKeyExp->GetCharacter(l+1);
+
+ wPtr = pBuf;
+
+ tte->cHasKeys = 0x00;
+ pBuf[246] = tte->cHasKeys;
+
+ wPtr += 248;
+ ePutUInt32( wPtr, tte->ulLeftChild );
+ wPtr += 4;
+ ePutUInt32( wPtr, tte->ulRightChild );
+
+ pBuf[257] = tte->cTagYY;
+ pBuf[258] = tte->cTagMM;
+ pBuf[259] = tte->cTagDD;
+ pBuf[480] = tte->cKeyFmt3;
+
+ if( sFilter.Len() > 0 ){
+ pBuf[245] = tte->cHasFilter;
+ wPtr = pBuf;
+ wPtr += 762;
+ for( xbUInt32 l = 0; l < sFilter.Len(); l++ )
+ *wPtr++ = sFilter.GetCharacter(l+1);
+ }
+
+ if(( iRc = xbFseek( tte->ulTagHdrPageNo * 512, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw iRc;
+ }
+
+ if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+
+ memset( pBuf, 0x00, GetBlockSize() );
+ if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 270;
+ throw iRc;
+ }
+
+ iTagUseCnt++;
+ cNextTag++;
+
+
+ if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 280;
+ throw iRc;
+ }
+
+ // add the new entry to the end of the list of tags
+ if( mdxTagTbl == NULL ){
+ mdxTagTbl = tte;
+ } else {
+ xbMdxTag *tteL = mdxTagTbl;
+ while( tteL->next )
+ tteL = tteL->next;
+ tteL->next = tte;
+ }
+
+ /* update the btree pointers */
+ CalcBtreePointers();
+ char bBuf[3];
+ xbMdxTag *tteWork = mdxTagTbl;
+
+ if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 290;
+ throw iRc;
+ }
+ while( tteWork ){
+ bBuf[0] = tteWork->cLeftChild;
+ bBuf[1] = tteWork->cRightChild;
+ bBuf[2] = tteWork->cParent;
+
+ if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+ if( tteWork->next ){
+ if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){
+ iErrorStop = 310;
+ throw iRc;
+ }
+ }
+ tteWork = tteWork->next;
+ }
+ free( pBuf );
+
+ }
+
+ catch (xbInt16 iRc ){
+ if( tte ){
+ if( tte->cpKeyBuf )
+ free( tte->cpKeyBuf );
+ if( tte->cpKeyBuf2 )
+ free( tte->cpKeyBuf2 );
+ if( tte->exp )
+ delete tte->exp;
+ if( tte->filter )
+ delete tte->filter;
+ if( tte->sKeyExp )
+ delete tte->sKeyExp;
+ if( tte->sFiltExp )
+ delete tte->sFiltExp;
+ if( tte->sTagName )
+ delete tte->sTagName;
+ free( tte );
+ }
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxTdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+};
+
+/***********************************************************************/
+//! @brief Delete a given tag
+/*!
+ \param vpTag Input tag ptr for tag to be deleted<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a><br>
+ 1 = Deleted entire MDX file, only had one tag
+
+*/
+
+xbInt16 xbIxTdx::DeleteTag( void *vpTag ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbMdxTag * mpTag = (xbMdxTag *) vpTag;
+ xbIxNode *n = NULL;
+ xbBool bLoneTag = xbFalse;
+
+ try{
+
+ if( !vpTag ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_TAG;
+ throw iRc;
+ }
+
+ // char cSaveHasFilter = mpTag->cHasFilter;
+ // char cSaveKeyFmt3 = mpTag->cKeyFmt3;
+ // xbString sSaveKey = mpTag->sKeyExp->Str();
+
+ if( iTagUseCnt == 1 ){
+ // std::cout << "xbIxTdx::DeleteTag - one tag found, delete the mdx file\n";
+
+ // close the mdx file
+ if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ // delete the file
+ xbRemove();
+
+ // init variables - needed?
+ // Init();
+ // iRc > 0 defines this as the only tag in an MDX file, MDX file deleted.
+ // signals to the calling process to drop the MDX file from the
+ // list of updateable indices.
+ bLoneTag = xbTrue;
+
+ } else {
+
+ // harvest tag nodes
+
+ if(( iRc = HarvestTagNodes( mpTag, xbTrue )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ // remove an entry from tag table
+ // which tag is this?
+ xbInt16 iTagNo = 0;
+ xbMdxTag *mp = mdxTagTbl;
+ xbMdxTag *mpPrev = NULL;
+ while( mp && mp->ulTagHdrPageNo != mpTag->ulTagHdrPageNo ){
+ iTagNo++;
+ mpPrev = mp;
+ mp = mp->next;
+ }
+
+ // remove it from the linked list of tags
+ if( !mpPrev ){
+ mdxTagTbl = mp->next;
+ } else {
+ mpPrev->next = mp->next;
+ }
+ if( mp ){
+ if( mp->cpKeyBuf ) free( mp->cpKeyBuf );
+ if( mp->cpKeyBuf2 ) free( mp->cpKeyBuf2 );
+ if( mp->exp ) delete mp->exp;
+ if( mp->filter ) delete mp->filter;
+ if( mp->sKeyExp ) delete mp->sKeyExp;
+ if( mp->sFiltExp ) delete mp->sFiltExp;
+ if( mp->sTagName ) delete mp->sTagName;
+ free( mp );
+ }
+ xbInt32 iTarg = iTagNo * 32;
+ xbInt32 iSrc = iTarg + 32;
+ xbInt32 iLen = (iTagUseCnt - iTagNo) * 32;
+
+ if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ char Buf[1536]; // 47 tags + 1 in case tag #47 is deleted
+ memset( Buf, 0x00, 1536 );
+ if(( iRc = xbFread( Buf, 1504, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ char *pTrg = Buf;
+ pTrg += iTarg;
+ char *pSrc = Buf;
+ pSrc += iSrc;
+ for( xbInt32 i = 0; i < iLen; i++ )
+ *pTrg++ = *pSrc++;
+
+ if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( Buf, 1504, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ iTagUseCnt--;
+ if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+
+ // update the btree pointers
+ CalcBtreePointers();
+ char bBuf[3];
+ xbMdxTag *tteWork = mdxTagTbl;
+
+ if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ while( tteWork ){
+ bBuf[0] = tteWork->cLeftChild;
+ bBuf[1] = tteWork->cRightChild;
+ bBuf[2] = tteWork->cParent;
+
+ if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 320;
+ throw iRc;
+ }
+ if( tteWork->next ){
+ if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){
+ iErrorStop = 330;
+ throw iRc;
+ }
+ }
+ tteWork = tteWork->next;
+ }
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbIxTdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ if( n )
+ free( n );
+ }
+ if( bLoneTag && !iRc )
+ return 1;
+ else
+ return iRc;
+}
+
+/************************************************************************/
+
+
+
+/************************************************************************/
+} /* namespace */
+#endif /* XB_TDX_SUPPORT */ \ No newline at end of file
diff --git a/src/core/xblog.cpp b/src/core/xblog.cpp
new file mode 100755
index 0000000..9443006
--- /dev/null
+++ b/src/core/xblog.cpp
@@ -0,0 +1,227 @@
+/* xblog.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+
+#include "xbase.h"
+//#include <time.h>
+
+#ifdef XB_LOGGING_SUPPORT
+
+namespace xb{
+
+/******************************************************************************/
+//! @brief Constructor.
+xbLog::xbLog() : xbFile( NULL ){
+
+
+ // std::cout << "xbLog::xbLog(1) Directory = [" << GetLogDirectory() << "]\n";
+ // std::cout << "xbLog::xbLog(1) Name = [" << GetLogFileName() << "]\n";
+
+ SetDirectory( GetLogDirectory());
+ SetFileName ( GetLogFileName());
+
+ bLoggingStatus = xbFalse;
+ lLogSize = 100000;
+
+ #ifdef XB_LOCKING_SUPPORT
+ iShareMode = XB_MULTI_USER;
+ #else
+ iShareMode = XB_SINGLE_USER;
+ #endif
+}
+/******************************************************************************/
+//! @brief Constructor.
+/*!
+ \param sLogFileName - Log file name.
+*/
+xbLog::xbLog( const xbString & sLogFileName ) : xbFile( NULL ){
+ if( sLogFileName.GetPathSeparator())
+ SetFqFileName( sLogFileName ); // file name includes a path
+ else
+ SetFileName( sLogFileName ); // no file path
+
+ bLoggingStatus = xbFalse;
+ lLogSize = 100000;
+
+ #ifdef XB_LOCKING_SUPPORT
+ iShareMode = XB_MULTI_USER;
+ #else
+ iShareMode = XB_SINGLE_USER;
+ #endif
+
+}
+/******************************************************************************/
+//! @brief Deconstructor.
+xbLog::~xbLog(){
+ xbFclose();
+}
+/******************************************************************************/
+//! @brief Get the current log status
+/*!
+ \returns xbTrue - Logging turned on.<br>xbFalse - Logging turned off.
+*/
+xbBool xbLog::LogGetStatus(){
+ return bLoggingStatus;
+}
+/******************************************************************************/
+//! @brief Close the logfile.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbLog::LogClose(){
+ return xbFclose();
+}
+/******************************************************************************/
+//! @brief Set maximum log file size.
+/*!
+ \param lSize - New maximum log file size.
+ \returns void
+*/
+void xbLog::LogSetLogSize( size_t lSize ){
+ lLogSize = lSize;
+}
+/******************************************************************************/
+//! @brief Set log status.
+/*!
+ \param bStatus xbTrue - Turn logging on.<br>xbFalse - Turn logging off.
+ \returns void
+*/
+void xbLog::LogSetStatus( xbBool bStatus ){
+ if( bLoggingStatus && !bStatus )
+ LogClose();
+ bLoggingStatus = bStatus;
+}
+/******************************************************************************/
+//! @brief Open the logfile.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbLog::LogOpen(){
+ xbInt16 rc;
+
+// std::cout << "*****\nxbLog::LogOpen(1) GetLogDirectory = " << GetLogDirectory() << "\n";
+// std::cout << "xbLog::LogOpen(1) GetLogFileName = " << GetLogFileName() << "\n";
+// std::cout << "xbLog::GetFqFileName(1) = " << GetFqFileName() << "\n\n";
+
+ // 4.1.3 added next two lines for dynamic log file name changing
+ SetDirectory( GetLogDirectory());
+ SetFileName ( GetLogFileName());
+
+// std::cout << "*****\nxbLog::LogOpen(2) GetLogDirectory = " << GetLogDirectory() << "\n";
+// std::cout << "xbLog::LogOpen(2) GetLogFileName = " << GetLogFileName() << "\n";
+// std::cout << "xbLog::GetFqFileName(2) = " << GetFqFileName() << "\n\n";
+
+ if(( rc = xbFopen( "a", iShareMode )) != XB_NO_ERROR )
+ return rc;
+ xbFTurnOffFileBuffering();
+ return XB_NO_ERROR;
+}
+/******************************************************************************/
+//! @brief Write a logfile message.
+/*!
+ \param sLogEntryData - Message to write to the logfile.
+ \param iOutputOption 0 - Write to logfile.<br>
+ 1 - Write to stdout.<br>
+ 2 - Write to both logfile and stdout.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbLog::LogWrite( const xbString &sLogEntryData, xbInt16 iOutputOption ){
+
+ if( bLoggingStatus == xbFalse ){ // logging turned off
+ return XB_NO_ERROR;
+ }
+ xbInt16 rc = 0;
+ if( iOutputOption != 1 && !FileIsOpen() ){
+ if(( rc = LogOpen()) != XB_NO_ERROR ){
+ fprintf( stderr, "Error - cant write to logfile\n" );
+ return rc;
+ }
+ }
+ if( iOutputOption != 1 && lLogSize < xbFtell()){
+ xbFputs( "Swapping to next log file" );
+ xbFclose();
+ xbString sBackupName;
+ sBackupName.Sprintf( "%s.bak", GetFqFileName().Str());
+ if( FileExists( sBackupName ))
+ xbRemove( sBackupName );
+
+ xbRename( GetFqFileName(), sBackupName );
+ xbFopen( "a", iShareMode );
+ }
+ xbString sTimeStamp;
+ xbString sFled; // formatted log entry data
+
+ if( iOutputOption != 1 ){
+ #ifdef HAVE__LOCALTIME64_S_F
+ __time64_t timer;
+ struct tm tb;
+ _time64( &timer );
+ _localtime64_s( &tb, &timer );
+ tb.tm_year += 1900;
+ tb.tm_mon++;
+ sTimeStamp.Sprintf( "%4d-%02d-%02d %02d:%02d:%02d", tb.tm_year, tb.tm_mon, tb.tm_mday, tb.tm_hour, tb.tm_min, tb.tm_sec );
+ #else
+ time_t timer;
+ struct tm *tb;
+ timer = time( NULL );
+ tb = localtime( &timer );
+ tb->tm_year += 1900;
+ tb->tm_mon++;
+ sTimeStamp.Sprintf( "%4d-%02d-%02d %02d:%02d:%02d", tb->tm_year, tb->tm_mon, tb->tm_mday, tb->tm_hour, tb->tm_min, tb->tm_sec );
+ #endif
+ sFled.Sprintf( "%s - %s\n", sTimeStamp.Str(), sLogEntryData.Str() );
+ }
+
+ switch( iOutputOption ){
+ case 0:
+ xbFputs( sFled );
+ break;
+ case 1:
+ std::cout << sLogEntryData << std::endl;
+ break;
+ case 2:
+ xbFputs( sFled );
+ std::cout << sLogEntryData << std::endl;
+ break;
+ }
+ return XB_NO_ERROR;
+}
+/******************************************************************************/
+//! @brief Write bytes to logfile.
+/*!
+ \param ulByteCnt - Number of bytes to write to logfile.
+ \param p - Pointer to data to write to logfile.
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbLog::LogWriteBytes( xbUInt32 ulByteCnt, const char *p ){
+
+ if( bLoggingStatus == xbFalse ) // logging turned off
+ return XB_NO_ERROR;
+ const char *p2 = p;
+ xbFputc( '[' );
+ for( xbUInt32 l = 0; l < ulByteCnt; l++ )
+ xbFputc( *p2++ );
+ xbFputc( ']' );
+ return XB_NO_ERROR;
+}
+/******************************************************************************/
+} // namespace
+#endif // XB_LOGGING_ON
+
+
+
+
diff --git a/src/core/xbmemo.cpp b/src/core/xbmemo.cpp
new file mode 100755
index 0000000..406a77d
--- /dev/null
+++ b/src/core/xbmemo.cpp
@@ -0,0 +1,219 @@
+/* xbmemo.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+ Base memo class
+*/
+
+#include "xbase.h"
+
+#ifdef XB_MEMO_SUPPORT
+
+namespace xb{
+
+/***********************************************************************/
+//! @brief Class Constructor.
+/*!
+ \param dbf Pointer to dbf construct.
+ \param sFileName Memo file name.
+*/
+
+xbMemo::xbMemo( xbDbf * dbf, xbString const &sFileName ) : xbFile( dbf->GetXbasePtr() ) {
+ this->dbf = dbf; /* pointer to the associated dbf class instance */
+ // xbase = dbf->GetXbasePtr(); /* pointer to the engine */
+ SetDirectory( dbf->GetDirectory());
+ SetFileName( sFileName );
+ mbb = NULL;
+ #ifdef XB_LOCKING_SUPPORT
+ bFileLocked = xbFalse;
+ #endif
+}
+/***********************************************************************/
+//! @brief Class Destructor.
+xbMemo::~xbMemo(){
+ if( mbb )
+ free( mbb );
+}
+/***********************************************************************/
+//! @brief Calculate the last data block number.
+/*!
+ \param ulLastDataBlock Output - Last used block number in the file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemo::CalcLastDataBlock( xbUInt32 & ulLastDataBlock ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ if(( iRc = xbFseek( 0, SEEK_END )) != XB_NO_ERROR )
+ return iRc;
+
+ ulLastDataBlock = (xbUInt32) xbFtell() / (xbUInt32) GetBlockSize();
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Close the memo file.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemo::CloseMemoFile(){
+
+ if( mbb ){
+ free( mbb );
+ mbb = NULL;
+ }
+ return xbFclose();
+}
+
+
+/***********************************************************************/
+//! @brief Get memo file type.
+/*!
+ \returns 3 - Version 3 memo file.<br>
+ 4 - Version 4 memo file.
+*/
+xbInt16 xbMemo::GetMemoFileType(){
+ return iMemoFileType;
+}
+
+/***********************************************************************/
+//! @brief Get next block available from file header.
+/*!
+ \param ulBlockNo Output - Next block number for appending data to memo file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemo::GetHdrNextBlock( xbUInt32 & ulBlockNo ){
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+ if(( iRc = ReadDbtHeader( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ ulBlockNo = ulHdrNextBlock;
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbmemo::GetNextAvailableBlock() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+#ifdef XB_LOCKING_SUPPORT
+//! @brief Lock memo file
+/*!
+
+ \param iLockFunction XB_LOCK<br>XB_UNLOCK
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemo::LockMemo( xbInt16 iLockFunction ){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iLockFunction == XB_LOCK ){
+
+ if( bFileLocked ) // already locked
+ return XB_NO_ERROR;
+
+ if( dbf->GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_LOCK, LK4026531838, 1 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else {
+ iErrorStop = 100;
+ throw iRc;
+ }
+ } else {
+ bFileLocked = xbTrue;
+ }
+ }
+ } else if( iLockFunction == XB_UNLOCK ){
+
+ if( !bFileLocked ) // already unlocked
+ return XB_NO_ERROR;
+
+ if( dbf->GetLockFlavor() == LK_DBASE ){
+ iRc = xbLock( XB_UNLOCK, LK4026531838, 1 );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_LOCK_FAILED )
+ return iRc;
+ else {
+ iErrorStop = 110;
+ throw iRc;
+ }
+ } else {
+ bFileLocked = xbFalse;
+ }
+ }
+ } else {
+ iErrorStop = 120;
+ iRc = XB_INVALID_OPTION;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbmemo::LockMemoFile() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Get memo file lock status.
+/*!
+ \returns xbTrue - Memo file is locked.<br>
+ xbFalse - Memo file is not locked.
+*/
+xbBool xbMemo::GetMemoLocked() const {
+ return bFileLocked;
+}
+#endif
+
+/***********************************************************************/
+//! @brief Update Next Node number in file header
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemo::UpdateHeadNextNode(){
+
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ try{
+ char buf[4];
+ ePutUInt32( buf, ulHdrNextBlock );
+ if(( iRc = xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc = xbFwrite( &buf, 4, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbmemo::UpdateHeadeNextNode() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_MEMO_SUPPORT */
+
diff --git a/src/core/xbmemo3.cpp b/src/core/xbmemo3.cpp
new file mode 100755
index 0000000..767e9d2
--- /dev/null
+++ b/src/core/xbmemo3.cpp
@@ -0,0 +1,585 @@
+/* xbmemo3.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+ This class is used for support dBASE V3 memo files
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_MEMO_SUPPORT
+#ifdef XB_DBF3_SUPPORT
+
+namespace xb{
+
+/***********************************************************************/
+//! @brief Class Constructor.
+/*!
+ \param dbf Pointer to dbf instance.
+ \param sFileName Memo file name.
+*/
+xbMemoDbt3::xbMemoDbt3( xbDbf * dbf, xbString const & sFileName ) : xbMemo( dbf, sFileName ){
+ iMemoFileType = 3;
+ SetBlockSize( 512 );
+}
+
+/***********************************************************************/
+//! @brief Class Deconstructor.
+xbMemoDbt3::~xbMemoDbt3(){}
+
+/***********************************************************************/
+//! @brief Abort.
+/*!
+ Abort any pending updates to the memo file.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbMemoDbt3::Abort(){
+ return XB_NO_ERROR;
+}/***********************************************************************/
+//! @brief Commit changes to memo file.
+/*!
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbMemoDbt3::Commit(){
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Create memo file.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt3::CreateMemoFile(){
+
+ xbInt16 rc = XB_NO_ERROR;
+ char cBuf[4];
+ if(( rc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR )
+ return rc;
+ ulHdrNextBlock = 1L;
+ ePutUInt32( cBuf, ulHdrNextBlock );
+ if(( rc = xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){
+ xbFclose();
+ return rc;
+ }
+ for(int i = 0; i < 12; i++ )
+ xbFputc( 0x00 );
+ xbFputc( 0x03 );
+ for(int i = 0; i < 495; i++ )
+ xbFputc( 0x00 );
+ if(( mbb = (void *) malloc( 512 )) == NULL ){
+ xbFclose();
+ return XB_NO_MEMORY;
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! @brief Dump memo file header.
+/*!
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbMemoDbt3::DumpMemoFreeChain() {
+ std::cout << "Xbase version 3 file - no free block chain" << std::endl;
+ return XB_NO_ERROR;
+}
+#endif // XB_DEBUG_SUPPORT
+
+//! @brief Dump memo file header.
+/*!
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbMemoDbt3::DumpMemoHeader(){
+ xbInt16 rc = XB_NO_ERROR;
+ xbUInt64 stFileSize;
+ if(( rc = ReadDbtHeader( 1 )) != XB_NO_ERROR )
+ return rc;
+ GetFileSize( stFileSize );
+ std::cout << "Version 3 Memo Header Info" << std::endl;
+ std::cout << "Memo File Name = " << GetFqFileName() << std::endl;
+ std::cout << "Next Available Block = " << ulHdrNextBlock << std::endl;
+ std::cout << "Memo File Version = " << (xbInt16) cVersion << " (";
+ BitDump( cVersion );
+ std::cout << ")" << std::endl;
+ std::cout << "Block Size = " << GetBlockSize() << std::endl;
+ std::cout << "File Size = " << stFileSize << std::endl;
+ std::cout << "Block Count = " << stFileSize / GetBlockSize() << std::endl;
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Get a memo field for a given field number.
+/*!
+ \param iFieldNo Field number to retrieve data for.
+ \param sMemoData Output - string containing memo field data.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt3::GetMemoField( xbInt16 iFieldNo, xbString & sMemoData ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 rc = XB_NO_ERROR;
+ xbUInt32 ulScnt;
+ char *sp, *spp;
+ xbUInt32 ulBlockNo;
+ xbBool bDone = xbFalse;
+ sMemoData = "";
+ try{
+
+ if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+
+ if( ulBlockNo == 0L ){
+ sMemoData = "";
+ return XB_NO_ERROR;
+ }
+ spp = NULL;
+
+ while( !bDone ){
+ if(( rc = ReadBlock( ulBlockNo++, GetBlockSize(), mbb )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+ ulScnt = 0;
+ sp = (char *) mbb;
+ while( ulScnt < 512 && !bDone ){
+ if( *sp == 0x1a && *spp == 0x1a )
+ bDone = xbTrue;
+ else{
+ ulScnt++; spp = sp; sp++;
+ }
+ }
+ sMemoData.Append( (char *) mbb, ulScnt );
+ }
+ sMemoData.ZapTrailingChar( 0x1a );
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt3::GetMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Get a memo field length for a given field number.
+/*!
+ \param iFieldNo Field number to retrieve data for.
+ \param ulFieldLen Output - length of memo field data.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemoDbt3::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 & ulFieldLen ){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iScnt;
+ char *sp, *spp;
+ xbUInt32 ulBlockNo;
+ xbInt16 iNotDone;
+ try{
+ if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ if( ulBlockNo == 0 ){
+ ulFieldLen = 0;
+ return XB_NO_ERROR;
+ }
+ ulFieldLen = 0L;
+ spp = NULL;
+ iNotDone = 1;
+ while( iNotDone ){
+ if(( rc = ReadBlock( ulBlockNo++, GetBlockSize(), mbb )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ iScnt = 0;
+ sp = (char *) mbb;
+ while( iScnt < 512 && iNotDone ){
+ if( *sp == 0x1a && *spp == 0x1a )
+ iNotDone = 0;
+ else{
+ ulFieldLen++; iScnt++; spp = sp; sp++;
+ }
+ }
+ }
+ if( ulFieldLen > 0 ) ulFieldLen--;
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt3::GetMemoFieldLen() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Open memo file.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt3::OpenMemoFile() {
+ xbInt16 rc = XB_NO_ERROR;
+ if(( rc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR )
+ return rc;
+ if(( mbb = (void *) malloc( 512 )) == NULL ){
+ xbFclose();
+ return XB_NO_MEMORY;
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Pack memo file.
+/*!
+ This routine frees up any unused blocks in the file resulting from field updates.
+ Version 3 memo files do not reclaim unused space (Version 4 files do).
+ This routine cleans up the unused space.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUInt32 ulNumItems ))
+{
+ xbInt16 iRc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char * cBlock = NULL;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bTableLocked = xbFalse;
+ xbBool bMemoLocked = xbFalse;
+ #endif
+
+ try{
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ } else {
+ bTableLocked = xbTrue;
+ }
+ if(( iRc = LockMemo( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ } else {
+ bMemoLocked = xbTrue;
+ }
+ }
+ #endif
+
+ // create temp file
+ xbString sTempMemoName;
+ //if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){
+ if(( iRc = CreateUniqueFileName( GetTempDirectory(), "DBT", sTempMemoName )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ xbMemoDbt3 *pMemo = new xbMemoDbt3( dbf, sTempMemoName );
+ if(( iRc = pMemo->CreateMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ // for dbase III, block size is always 512, don't need to reset it
+ // for each record in dbf
+ xbUInt32 ulRecCnt;
+ if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ xbInt32 lFldCnt = dbf->GetFieldCnt();
+ char cFldType;
+ xbString sMemoFldData;
+
+ for( xbUInt32 ulI = 1; ulI <= ulRecCnt; ulI++ ){
+
+ if(( iRc = dbf->GetRecord( ulI )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ if( (void *) memoStatusFunc )
+ (*memoStatusFunc) ( ulI, ulRecCnt );
+
+ // for each memo field
+ for( xbInt32 lFc = 0; lFc < lFldCnt; lFc++ ){
+ if(( iRc = dbf->GetFieldType( lFc, cFldType )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+
+ if( cFldType == 'M' ){
+ // copy it to work field
+ if(( iRc = dbf->GetMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ // write it to new field
+ if(( iRc = pMemo->UpdateMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ //copy target back to source
+ xbUInt32 ulBlkSize = GetBlockSize();
+ xbUInt64 ullFileSize;
+ if(( iRc = pMemo->GetFileSize( ullFileSize )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ // file size should be evenly divisible by block size
+ xbUInt32 ulBlkCnt;
+
+ if( ullFileSize % ulBlkSize ){
+ iErrorStop = 200;
+ throw iRc;
+ } else {
+ ulBlkCnt = (xbUInt32) (ullFileSize / ulBlkSize);
+ }
+ if(( iRc = xbTruncate( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+
+ if(( cBlock = (char *) malloc( (size_t) ulBlkSize )) == NULL ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+
+ // can't rename files in a multiuser, cross platform environment, causes issues
+ // copy work table back to source table
+ for( xbUInt32 ulBc = 0; ulBc < ulBlkCnt; ulBc++ ){
+ if(( iRc = pMemo->ReadBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ if(( iRc = WriteBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+ }
+
+ //close and delete target
+ if(( iRc = pMemo->xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw iRc;
+ }
+
+ if(( iRc = pMemo->xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+ free( cBlock );
+ delete pMemo;
+ }
+ catch (xbInt16 iRc ){
+ free( cBlock );
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt3::PackMemo() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bTableLocked )
+ dbf->LockTable( XB_UNLOCK );
+ if( bMemoLocked )
+ LockMemo( XB_UNLOCK );
+ #endif
+ return iRc;
+}
+
+/***********************************************************************/
+//! @brief Read dbt header file.
+/*!
+ \param iOption 0 --> read only first four bytes<br>
+ 1 --> read the entire thing
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt3::ReadDbtHeader( xbInt16 iOption ){
+ char *p;
+ char MemoBlock[20];
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulReadSize;
+
+ try{
+ if( !FileIsOpen() ){
+ iErrorStop = 100;
+ rc = XB_NOT_OPEN;
+ throw rc;
+ }
+ if( iOption == 0 )
+ ulReadSize = 4;
+ else{
+ xbUInt64 stFileSize = 0;
+ if(( rc = GetFileSize( stFileSize )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ if( stFileSize < 4 ){
+ iErrorStop = 120;
+ rc = XB_INVALID_BLOCK_NO;
+ throw rc;
+ }
+ else if( stFileSize > 20 )
+ ulReadSize = 130;
+ else
+ ulReadSize = 4;
+ }
+ if( xbFseek( 0, SEEK_SET )){
+ iErrorStop = 140;
+ rc = XB_SEEK_ERROR;
+ throw rc;
+ }
+ if(( xbFread( &MemoBlock, ulReadSize, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ rc = XB_READ_ERROR;
+ throw rc;
+ }
+ p = MemoBlock;
+ ulHdrNextBlock = eGetUInt32( p );
+
+ if( iOption == 0)
+ return XB_NO_ERROR;
+
+ if( ulReadSize >= 20 ){
+ p+=16;
+ cVersion = *p;
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt3::ReadDbtHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Update header name.
+/*!
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbMemoDbt3::UpdateHeaderName(){
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! @brief Update a memo field for a given field number.
+/*!
+ \param iFieldNo Field number to update data for.
+ \param sMemoData Data to update memo field data with.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt3::UpdateMemoField( xbInt16 iFieldNo, const xbString & sMemoData ) {
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 rc = XB_NO_ERROR;
+ try{
+ if( sMemoData == "" ){
+ if(( rc = dbf->PutField( iFieldNo, "" )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ } else {
+ xbUInt32 ulDataLen = sMemoData.Len() + 2;
+ xbUInt32 ulBlocksNeeded = (ulDataLen / 512) + 1;
+ xbUInt32 ulLastDataBlock;
+ if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ if(( rc = xbFseek( ((xbInt64) ulLastDataBlock * 512), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+ if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw rc;
+ }
+ if(( rc = xbFputc( 0x1a, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw rc;
+ }
+ if(( rc = xbFputc( 0x00, (xbInt32) ( ulBlocksNeeded * 512 ) - (xbInt32) ulDataLen )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw rc;
+ }
+ if(( rc = dbf->PutULongField( iFieldNo, ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw rc;
+ }
+ ulHdrNextBlock = ulLastDataBlock + ulBlocksNeeded;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw rc;
+ }
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt3::UpdateMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+
+/***********************************************************************/
+//! @brief Empty the memo file.
+/*!
+ This routine clears everything out of the file. It does not address the
+ block pointers on the dbf file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemoDbt3::Zap(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ char cBuf[4];
+
+ try{
+ ulHdrNextBlock = 1L;
+ ePutUInt32( cBuf, ulHdrNextBlock );
+
+ if(( iRc != xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc != xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ if(( iRc != xbTruncate( 512 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt3::Zap() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_DBF3_SUPPORT */
+#endif /* XB_MEMO_SUPPORT */
diff --git a/src/core/xbmemo4.cpp b/src/core/xbmemo4.cpp
new file mode 100755
index 0000000..9770806
--- /dev/null
+++ b/src/core/xbmemo4.cpp
@@ -0,0 +1,1336 @@
+/* xbmemo4.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+ This class is used for support dBASE V4 memo files
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_MEMO_SUPPORT
+#ifdef XB_DBF4_SUPPORT
+
+namespace xb{
+
+/***********************************************************************/
+//! @brief Class Constructor.
+/*!
+ \param dbf Pointer to dbf instance.
+ \param sFileName Memo file name.
+*/
+xbMemoDbt4::xbMemoDbt4( xbDbf * dbf, xbString const & sFileName ) : xbMemo( dbf, sFileName ){
+ iMemoFileType = 4;
+ SetBlockSize( dbf->GetCreateMemoBlockSize() );
+}
+
+/***********************************************************************/
+//! @brief Class Deconstructor.
+xbMemoDbt4::~xbMemoDbt4(){}
+
+/***********************************************************************/
+//! @brief Abort.
+/*!
+ Abort any pending updates to the memo file.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbMemoDbt4::Abort(){
+
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulBlockNo;
+
+ try{
+ xbUInt32 ulNodeCnt = llNewBlocks.GetNodeCnt();
+ for( xbUInt32 l = 0; l < ulNodeCnt; l++ ){
+ if(( rc = llNewBlocks.RemoveFromFront( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ if(( rc = FreeMemoBlockChain( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ }
+ llOldBlocks.Clear();
+ }
+
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::Abort() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+
+/***********************************************************************/
+//! @brief Commit changes to memo file.
+/*!
+ Commit any pending updates to the memo file.
+ \returns XB_NO_ERROR.
+*/
+xbInt16 xbMemoDbt4::Commit(){
+
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulBlockNo;
+
+ try{
+ xbUInt32 ulNodeCnt = llOldBlocks.GetNodeCnt();
+ for( xbUInt32 l = 0; l < ulNodeCnt; l++ ){
+ if(( rc = llOldBlocks.RemoveFromFront( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ if(( rc = FreeMemoBlockChain( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ }
+ llNewBlocks.Clear();
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::Commit() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Create memo file.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::CreateMemoFile(){
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 rc = XB_NO_ERROR;
+ char cBuf[4];
+
+ try{
+ if(( rc = xbFopen( "w+b", dbf->GetShareMode() )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+
+ ulHdrNextBlock = 1L;
+ ePutUInt32( cBuf, ulHdrNextBlock );
+ if(( rc = xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){
+ iErrorStop = 110;
+ xbFclose();
+ throw rc;
+ }
+ for(int i = 0; i < 4; i++ )
+ xbFputc( 0x00 );
+ GetFileNamePart( sDbfFileNameWoExt );
+ sDbfFileNameWoExt.PadRight( ' ', 8 ); // need at least eight bytes of name
+ sDbfFileNameWoExt = sDbfFileNameWoExt.Mid( 1, 8 ); // need no more than eight bytes of name
+ for( int i = 1; i < 9; i++ )
+ xbFputc( sDbfFileNameWoExt[i] );
+
+ for(int i = 0; i < 4; i++ )
+ xbFputc( 0x00 );
+
+ ePutInt16( cBuf, GetBlockSize());
+ if(( rc = xbFwrite( cBuf, 2, 1 ))!= XB_NO_ERROR ){
+ iErrorStop = 120;
+ xbFclose();
+ throw rc;
+ }
+ for( xbUInt32 i = 0; i < (GetBlockSize() - 22); i++ )
+ xbFputc( 0x00 );
+ if(( mbb = (void *) malloc( GetBlockSize())) == NULL ){
+ rc = XB_NO_MEMORY;
+ iErrorStop = 130;
+ return XB_NO_MEMORY;
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::CreateMemoFile() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ xbFclose();
+ }
+ return rc;
+}
+/***********************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! @brief Dump memo file header.
+/*!
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbMemoDbt4::DumpMemoFreeChain()
+{
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulCurBlock, ulLastDataBlock;
+
+ try{
+ if(( rc = ReadDbtHeader(1)) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+
+ ulCurBlock = ulHdrNextBlock;
+ std::cout << "**********************************" << std::endl;
+ std::cout << "Head Node Next Block = " << ulCurBlock << std::endl;;
+ std::cout << "Total blocks in file = " << ulLastDataBlock << std::endl;
+
+ while( ulCurBlock < ulLastDataBlock ){
+ if(( rc = ReadBlockHeader( ulCurBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+ std::cout << "**********************************" << std::endl;
+ std::cout << "This Free Block = [" << ulCurBlock << "] contains [" << ulFreeBlockCnt << "] block(s)" << std::endl;
+ std::cout << "Next Free Block = [" << ulNextFreeBlock << "]" << std::endl;
+ ulCurBlock = ulNextFreeBlock;
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::UpdateMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return XB_NO_ERROR;
+}
+
+//! @brief Dump memo internals.
+/*!
+ \returns XB_NO_ERROR
+*/
+
+xbInt16 xbMemoDbt4::DumpMemoInternals() {
+
+ xbLinkListNode<xbUInt32> *llPtr;
+ xbInt16 iNodeCnt;
+
+ llPtr = llOldBlocks.GetHeadNode();
+ iNodeCnt = llOldBlocks.GetNodeCnt();
+
+ std::cout << "Link List Old Blocks - " << iNodeCnt << " nodes" << std::endl;
+ for( xbInt16 i = 0; i < iNodeCnt; i++ ){
+ std::cout << llPtr->GetKey() << ",";
+ llPtr = llPtr->GetNextNode();
+ }
+ std::cout << std::endl;
+
+ llPtr = llNewBlocks.GetHeadNode();
+ iNodeCnt = llNewBlocks.GetNodeCnt();
+ std::cout << "Link List New Blocks - " << iNodeCnt << " nodes" << std::endl;
+ for( xbInt16 i = 0; i < iNodeCnt; i++ ){
+ std::cout << llPtr->GetKey() << ",";
+ llPtr = llPtr->GetNextNode();
+ }
+ std::cout << std::endl;
+
+ return XB_NO_ERROR;
+}
+#endif // XB_DEBUG_SUPPORT
+
+/***********************************************************************/
+//! @brief Dump memo file header.
+/*!
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbMemoDbt4::DumpMemoHeader(){
+
+ xbInt16 rc = XB_NO_ERROR;
+ xbUInt32 ulLastDataBlock;
+ CalcLastDataBlock( ulLastDataBlock );
+
+ if(( rc = ReadDbtHeader( 1 )) != XB_NO_ERROR )
+ return rc;
+ std::cout << "Version 4 Memo Header Info" << std::endl;
+ std::cout << "Memo File Name = " << GetFqFileName() << std::endl;
+ std::cout << "Hdr Next Avail Block = " << ulHdrNextBlock << std::endl;
+ std::cout << "Block Size = " << GetBlockSize() << std::endl;
+ std::cout << "Dbf File Name wo Ext = " << sDbfFileNameWoExt.Str() << std::endl;
+ std::cout << "Last Data Block = " << ulLastDataBlock << std::endl;
+ return rc;
+}
+
+/************************************************************************/
+//! @brief Find an empty set of blocks in the free block chain
+/*!
+ This routine searches thruugh the free node chain in a dbase IV type
+ memo file searching for a place to grab some free blocks for reuse
+
+ \param ulBlocksNeeded The size to look in the chain for.
+ \param ulLastDataBlock is the last data block in the file, enter 0
+ for the routine to calculate it.
+ \param ulLocation The location it finds.
+ \param ulPreviousNode Block number of the node immediately previous to this node in the chain.<br>
+ 0 if header node
+ \param bFound Output xbFalse - Spot not found in chain.<br>
+ xbTrue - Spot found in chain.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemoDbt4::FindBlockSetInChain( xbUInt32 ulBlocksNeeded,
+ xbUInt32 &ulLastDataBlock, xbUInt32 &ulLocation, xbUInt32 &ulPrevNode, xbBool &bFound ){
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulCurNode;
+
+ try{
+ ulPrevNode = 0L;
+ if( ulLastDataBlock == 0 ){
+ /* Determine last good data block */
+ if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ }
+ if( ulHdrNextBlock < ulLastDataBlock ){
+ ulCurNode = ulHdrNextBlock;
+
+ if(( rc = ReadBlockHeader( ulHdrNextBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ while( ulBlocksNeeded > ulFreeBlockCnt && ulNextFreeBlock < ulLastDataBlock ){
+ ulPrevNode = ulCurNode;
+ ulCurNode = ulNextFreeBlock;
+ if(( rc = ReadBlockHeader( ulNextFreeBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+ }
+ if( ulBlocksNeeded <= ulFreeBlockCnt ){
+ ulLocation = ulCurNode;
+ // PreviousNode = lPrevNode;
+ bFound = xbTrue;
+ } else { // no data found and at end of chain
+ ulPrevNode = ulCurNode;
+ bFound = xbFalse;
+ }
+ } else {
+ bFound = xbFalse;
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::FindBlockSetInChain() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Free a block.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::FreeMemoBlockChain( xbUInt32 ulBlockNo ){
+ xbUInt32 ulLastDataBlock;
+ return FreeMemoBlockChain( ulBlockNo, ulLastDataBlock );
+}
+
+/***********************************************************************/
+//! @brief Free a block.
+/*!
+ \param ulBlockNo The block number being deleted.
+ \param ulLastDataBlock Output - Last free block number,prior to this block.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemoDbt4::FreeMemoBlockChain( xbUInt32 ulBlockNo, xbUInt32 &ulLastDataBlock )
+{
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulNoOfFreedBlocks;
+ xbUInt32 ulLastFreeBlock = 0;
+ xbUInt32 ulLastFreeBlockCnt = 0;
+ xbUInt32 ulSaveNextFreeBlock;
+
+ // iFieldNo - The field no m\bing deleted
+ // iBlockNo - The block number being deleted
+ // iNoOfFreeBlocks - The number of blocks being freed with this delete
+ // iLastDataBlock - The next block number to allocate if more blocks needed
+ // iHdrNextBlock - The head pointer in the main header block
+ // iNextFreeBlock - The block that is immediately following the current free block to be added
+ // iLastFreeBlock - Last free block number,prior to this block
+ // iLastFreeBlockCnt - Last free block number of blocks
+
+ try{
+
+ if( ulBlockNo <= 0 ){
+ iErrorStop = 100;
+ rc =XB_INVALID_BLOCK_NO;
+ throw rc;
+ }
+
+ /* Load the first block */
+ if(( rc = ReadBlockHeader( ulBlockNo, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+
+ if( (ulFieldLen) % GetBlockSize() )
+ ulNoOfFreedBlocks = ((ulFieldLen) / GetBlockSize()) + 1L;
+ else
+ ulNoOfFreedBlocks = (ulFieldLen) / GetBlockSize();
+
+ /* Determine last good data block */
+ if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+
+ if(( rc = ReadDbtHeader( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw rc;
+ }
+
+ // Not an empty node chain, position to correct location in chain
+ ulNextFreeBlock = ulHdrNextBlock;
+ while( ulBlockNo > ulNextFreeBlock && ulBlockNo < ulLastDataBlock ){
+ ulLastFreeBlock = ulNextFreeBlock;
+
+ if(( rc = ReadBlockHeader( ulNextFreeBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ return rc;
+ }
+ ulLastFreeBlockCnt = ulFreeBlockCnt;
+ }
+
+ // One of two outcomes at this point
+ // A) This block is combined with the next free block chain, and points to the free chain after the next free block
+ // B) This block is not combined with the next free block chain, and points to the next block
+ // (which could be the last block
+
+ // should next block should be concatonated onto the end of this set?
+ ulSaveNextFreeBlock = ulNextFreeBlock;
+ if(( ulBlockNo + ulNoOfFreedBlocks ) == ulNextFreeBlock && ulNextFreeBlock < ulLastDataBlock ){
+ if(( rc = ReadBlockHeader( ulNextFreeBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw rc;
+ }
+ ulNoOfFreedBlocks += ulFreeBlockCnt;
+ ulSaveNextFreeBlock = ulNextFreeBlock;
+ }
+
+ // if this is the first set of free blocks
+ if( ulLastFreeBlock == 0 ){
+ // 1 - write out the current block
+ // 2 - update header block
+ // 3 - write header block
+ // 4 - update data field
+
+ ePutUInt32( (char *) mbb, ulSaveNextFreeBlock );
+ ePutUInt32( (char *) mbb+4, ulNoOfFreedBlocks );
+ if(( rc = WriteBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw rc;
+ }
+
+ ulHdrNextBlock = ulBlockNo;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw rc;
+ }
+ return XB_NO_ERROR;
+ }
+
+ /* determine if this block set should be added to the previous set */
+ if(( ulLastFreeBlockCnt + ulLastFreeBlock ) == ulBlockNo ){
+ if(( rc = ReadBlockHeader( ulLastFreeBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw rc;
+ }
+ ulFreeBlockCnt += ulNoOfFreedBlocks;
+
+ ePutUInt32( (char *) mbb, ulSaveNextFreeBlock );
+ ePutUInt32( (char *) mbb+4, ulFreeBlockCnt );
+ if(( rc = WriteBlock( ulLastFreeBlock, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw rc;
+ }
+ return XB_NO_ERROR;
+ }
+
+ /* insert into the chain */
+ /* 1 - set the next bucket on the current node */
+ /* 2 - write this node */
+ /* 3 - go to the previous node */
+ /* 4 - insert this nodes id into the previous node set */
+ /* 5 - write previous node */
+
+ ePutUInt32( (char *) mbb, ulSaveNextFreeBlock );
+ ePutUInt32( (char *) mbb+4, ulNoOfFreedBlocks );
+ if(( rc = WriteBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw rc;
+ }
+
+ if(( rc = ReadBlockHeader( ulLastFreeBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw rc;
+ }
+
+ ePutUInt32( (char *) mbb, ulBlockNo );
+ if(( rc = WriteBlock( ulLastFreeBlock, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw rc;
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::DeleteMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/************************************************************************/
+//! @brief Get a set of blocks from the free block chain.
+/*!
+ This routine grabs a set of blocks out of the free block chain.
+
+ \param ulBlocksNeeded The number of blocks requested.
+ \param ulLocation
+ \param ulPrevNode
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+
+xbInt16 xbMemoDbt4::GetBlockSetFromChain( xbUInt32 ulBlocksNeeded,
+ xbUInt32 ulLocation, xbUInt32 ulPrevNode )
+{
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ xbUInt32 ulNextFreeBlock2;
+ xbUInt32 ulNewFreeBlocks;
+ xbUInt32 ulSaveNextFreeBlock;
+
+ try{
+ if(( rc = ReadBlockHeader( ulLocation, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+
+ if( ulBlocksNeeded == ulFreeBlockCnt ){ // grab this whole set of blocks
+ if( ulPrevNode == 0 ){ // first in the chain
+ ulHdrNextBlock = ulNextFreeBlock;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ }
+ else // remove out of the middle or end
+ {
+ ulNextFreeBlock2 = ulNextFreeBlock;
+ if(( rc = ReadBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+ ulNextFreeBlock = ulNextFreeBlock2;
+ if(( rc = WriteBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw rc;
+ }
+ }
+ } else { // only take a portion of this set
+ if( ulPrevNode == 0 ){ // first in the set
+ ulHdrNextBlock = ulLocation + ulBlocksNeeded;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw rc;
+ }
+ ulFreeBlockCnt -= ulBlocksNeeded;
+ if(( rc = WriteBlockHeader( ulHdrNextBlock, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw rc;
+ }
+ }
+ else { // remove out of the middle or end
+ ulNewFreeBlocks = ulFreeBlockCnt - ulBlocksNeeded;
+ ulSaveNextFreeBlock = ulNextFreeBlock;
+ ulNextFreeBlock2 = ulLocation + ulBlocksNeeded;
+
+ if(( rc = ReadBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw rc;
+ }
+ ulNextFreeBlock = ulNextFreeBlock2;
+ if(( rc = WriteBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw rc;
+ }
+ ulFreeBlockCnt = ulNewFreeBlocks;
+ ulNextFreeBlock = ulSaveNextFreeBlock;
+ if(( rc = WriteBlockHeader( ulNextFreeBlock2, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw rc;
+ }
+ }
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::GetBlockSetFromChain() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Get a memo field for a given field number.
+/*!
+ \param iFieldNo Field number to retrieve data for.
+ \param sMemoData Output - string containing memo field data.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::GetMemoField( xbInt16 iFieldNo, xbString & sMemoData ){
+
+ xbUInt32 ulBlockNo;
+ xbUInt32 ulMemoFieldLen;
+ xbUInt32 ulMemoFieldDataLen;
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char *p = NULL;
+
+ try{
+ if(( rc = GetMemoFieldLen( iFieldNo, ulMemoFieldLen, ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+
+ if( ulBlockNo == 0L || ulMemoFieldLen == 0L )
+ sMemoData = "";
+ else{
+ ulMemoFieldDataLen = ulMemoFieldLen - 8;
+
+ if(( p = (char *)calloc(1, ulMemoFieldDataLen+1)) == NULL ){
+ iErrorStop = 110;
+ rc = XB_NO_MEMORY;
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::GetMemoField() lBlockNo = %ld Data Len = [%ld]", ulBlockNo, ulMemoFieldDataLen + 1 );
+ xbase->WriteLogMessage( sMsg.Str() );
+ throw rc;
+ }
+
+ // go to the first block of the memo field, skip past the first 8 bytes
+ if(( xbFseek( ( ulBlockNo * GetBlockSize() + 8 ), SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ rc = XB_SEEK_ERROR;
+ throw rc;
+ }
+
+ // read the memo file data into buffer pointed to by "p"
+ if(( rc = xbFread( p, ulMemoFieldDataLen, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ rc = XB_READ_ERROR;
+ throw rc;
+ }
+ // null terminate the string
+ char *p2;
+ p2 = p + ulMemoFieldDataLen;
+ *p2 = 0x00;
+
+ // save it to the string
+ sMemoData.Set( p, ulMemoFieldDataLen + 1 );
+ free( p );
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::GetMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ if( p )
+ free( p );
+ }
+ return rc;
+}
+
+/***********************************************************************/
+//! @brief Get a memo field length for a given field number.
+/*!
+ \param iFieldNo Field number to retrieve data for.
+ \param ulMemoFieldLen Output - length of memo field data.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemoDbt4::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen ){
+ xbUInt32 ulBlockNo;
+ return GetMemoFieldLen( iFieldNo, ulMemoFieldLen, ulBlockNo );
+}
+
+/***********************************************************************/
+//! @brief Get a memo field length for a given field number.
+/*!
+ \param iFieldNo Field number to retrieve data for.
+ \param ulMemoFieldLen Output - length of memo field data.
+ \param ulBlockNo Output - Starting block number.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 &ulMemoFieldLen, xbUInt32 &ulBlockNo ){
+
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+ char cFieldType;
+
+ try{
+
+ if(( rc = dbf->GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ if( cFieldType != 'M' ){
+ iErrorStop = 110;
+ rc = XB_INVALID_MEMO_FIELD;
+ throw rc;
+ }
+ if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+ if( ulBlockNo < 1 ){
+ ulMemoFieldLen = 0;
+ return XB_NO_ERROR;
+ }
+ if(( rc = ReadBlockHeader( ulBlockNo, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw rc;
+ }
+ ulMemoFieldLen = ulFieldLen;
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::GetMemoFieldLen() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Open memo file.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::OpenMemoFile() {
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 rc = XB_NO_ERROR;
+
+ try{
+ if(( rc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+ if(( rc = ReadDbtHeader( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ if(( mbb = (void *) malloc( GetBlockSize())) == NULL ){
+ xbFclose();
+ iErrorStop = 120;
+ rc = XB_NO_MEMORY;
+ throw rc;
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::OpenMemoFile() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Pack memo file.
+/*!
+ This routine frees up any unused blocks in the file resulting from field updates.
+ Version 3 memo files do not reclaim unused space (Version 4 files do).
+ This routine cleans up the unused space.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUInt32 ulNumItems )){
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ char * cBlock = NULL;
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbBool bTableLocked = xbFalse;
+ xbBool bMemoLocked = xbFalse;
+ #endif
+
+ try{
+ #ifdef XB_LOCKING_SUPPORT
+ if( dbf->GetAutoLock() && !dbf->GetTableLocked() ){
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ } else {
+ bTableLocked = xbTrue;
+ }
+ if(( iRc = LockMemo( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ } else {
+ bMemoLocked = xbTrue;
+ }
+ }
+ #endif
+
+ // create temp file
+ xbString sTempMemoName;
+ //if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){
+ if(( iRc = CreateUniqueFileName( GetTempDirectory(), "DBT", sTempMemoName )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ xbMemoDbt4 *pMemo = new xbMemoDbt4( dbf, sTempMemoName );
+ if(( iRc = pMemo->CreateMemoFile()) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ // for dbase III, block size is always 512, don't need to reset it
+ // for each record in dbf
+ xbUInt32 ulRecCnt;
+ if(( iRc = dbf->GetRecordCnt( ulRecCnt)) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ xbInt32 lFldCnt = dbf->GetFieldCnt();
+ char cFldType;
+ xbString sMemoFldData;
+
+ for( xbUInt32 ulI = 1; ulI <= ulRecCnt; ulI++ ){
+ if(( iRc = dbf->GetRecord( ulI )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ if( (void *) memoStatusFunc)
+ (*memoStatusFunc)(ulI, ulRecCnt );
+
+ // for each memo field
+ for( xbInt32 lFc = 0; lFc < lFldCnt; lFc++ ){
+ if(( iRc = dbf->GetFieldType( lFc, cFldType )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if( cFldType == 'M' ){
+ // copy it to work field
+ if(( iRc = dbf->GetMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ // write it to new field
+ if(( iRc = pMemo->UpdateMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ }
+ }
+ }
+
+ //copy target back to source
+ xbUInt32 ulBlkSize = GetBlockSize();
+ xbUInt64 ullFileSize;
+ if(( iRc = pMemo->GetFileSize( ullFileSize )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ // file size should be evenly divisible by block size
+ xbUInt32 ulBlkCnt;
+
+ if( ullFileSize % ulBlkSize ){
+ iErrorStop = 200;
+ throw iRc;
+ } else {
+ ulBlkCnt = (xbUInt32) (ullFileSize / ulBlkSize);
+ }
+ if(( iRc = xbTruncate( 0 )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+
+ if(( cBlock = (char *) malloc( ulBlkSize )) == NULL ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+
+ // can't rename files in a multiuser, cross platform environment, causes issues
+ // copy work table back to source table
+
+ for( xbUInt32 ulBc = 0; ulBc < ulBlkCnt; ulBc++ ){
+ if(( iRc = pMemo->ReadBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ if(( iRc = WriteBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+ }
+
+ if(( xbFseek( 8, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ iRc = XB_SEEK_ERROR;
+ throw iRc;
+ }
+
+ for( int i = 1; i < 9; i++ )
+ xbFputc( sDbfFileNameWoExt[i] );
+
+ //close and delete target
+ if(( iRc = pMemo->xbFclose()) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+ if(( iRc = pMemo->xbRemove()) != XB_NO_ERROR ){
+ iErrorStop = 270;
+ throw iRc;
+ }
+ free( cBlock );
+ delete pMemo;
+
+ }
+ catch (xbInt16 iRc ){
+ free( cBlock );
+
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::PackMemo() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ #ifdef XB_LOCKING_SUPPORT
+ if( bTableLocked )
+ dbf->LockTable( XB_UNLOCK );
+ if( bMemoLocked )
+ LockMemo( XB_UNLOCK );
+ #endif
+ return iRc;
+}
+/***********************************************************************/
+//! @brief Read block header.
+/*!
+ \param ulBlockNo Block to read
+ \param iOption 1 - Read fields option 1
+ 2 - Read fields option 2
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::ReadBlockHeader( xbUInt32 ulBlockNo, xbInt16 iOption ) {
+
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if(( rc = ReadBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ rc = XB_READ_ERROR;
+ }
+ if( iOption == 1 ){
+ iField1 = eGetInt16((char *) mbb );
+ iStartPos = eGetInt16((char *) mbb+2);
+ ulFieldLen = eGetUInt32((char *) mbb+4);
+ }
+ else if( iOption == 2 ){
+ ulNextFreeBlock = eGetUInt32((char *) mbb );
+ ulFreeBlockCnt = eGetUInt32((char *) mbb+4 );
+ }
+ else{
+ iErrorStop = 110;
+ rc = XB_INVALID_OPTION;
+ throw rc;
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::ReadBlockHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Read dbt header file.
+/*!
+ \param iOption 0 --> read only first four bytes<br>
+ 1 --> read the entire thing
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::ReadDbtHeader( xbInt16 iOption ) {
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iReadLen = 0;
+ char *p;
+ char MemoBlock[22];
+
+ try{
+ if( !FileIsOpen() ){
+ iErrorStop = 100;
+ rc = XB_NOT_OPEN;
+ throw rc;
+ }
+ if( xbFseek( 0, SEEK_SET )){
+ iErrorStop = 110;
+ rc = XB_SEEK_ERROR;
+ throw rc;
+ }
+ if( iOption )
+ iReadLen = 22;
+ else
+ iReadLen = 4;
+
+ if(( xbFread( &MemoBlock, (size_t) iReadLen, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ rc = XB_READ_ERROR;
+ throw rc;
+ }
+
+ p = MemoBlock;
+ ulHdrNextBlock = eGetUInt32( p );
+ if( iOption == 0)
+ return XB_NO_ERROR;
+
+ p += 8;
+ sDbfFileNameWoExt = "";
+ for( int i = 0; i < 8; i++ )
+ sDbfFileNameWoExt += *p++;
+
+ p += 4;
+ SetBlockSize( (xbUInt32) eGetInt16( p ));
+
+ cVersion = MemoBlock[16];
+
+ }
+ catch (xbInt16 rc ){
+
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::ReadDbtHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+
+/***********************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+//! @brief Read free block information from header.
+/*!
+ This routing pulls any reusable block information for file header.
+ Not used with version 3 memo files - stub.
+
+ \param ulBlockNo
+ \param ulNextBlock
+ \param ulFreeBlockCnt
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbMemoDbt4::ReadFreeBlockHeader( xbUInt32 ulBlockNo, xbUInt32 &ulNextBlock, xbUInt32 &ulFreeBlockCount ){
+
+ xbInt16 rc = XB_NO_ERROR;
+ rc = ReadBlockHeader( ulBlockNo, 2 );
+ ulNextBlock = ulNextFreeBlock;
+ ulFreeBlockCount = ulFreeBlockCnt;
+ return rc;
+}
+#endif
+/***********************************************************************/
+//! @brief Update header name.
+/*!
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbMemoDbt4::UpdateHeaderName() {
+
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ GetFileNamePart( sDbfFileNameWoExt );
+ sDbfFileNameWoExt.PadRight( ' ', 8 ); // need at least eight bytes of name
+ sDbfFileNameWoExt = sDbfFileNameWoExt.Mid( 1, 8 ); // need no more than eight bytes of name
+
+ try{
+ if(( rc = xbFseek( 8, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+
+ for( int i = 1; i < 9; i++ ){
+ if(( rc = xbFputc( sDbfFileNameWoExt[i] )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::UpdateHeaderName() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Update a memo field length for a given field number.
+/*!
+ \param iFieldNo Field number to update data for.
+ \param sMemoData Data to update memo field data with.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::UpdateMemoField( xbInt16 iFieldNo, const xbString & sMemoData ) {
+
+ xbInt16 iErrorStop = 0;
+ xbInt16 rc = XB_NO_ERROR;
+ xbUInt32 ulBlockNo;
+
+ try{
+
+ if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw rc;
+ }
+
+ if( sMemoData == "" ){
+ if( ulBlockNo == 0 ){
+ /* if nothing to do, return */
+ return XB_NO_ERROR;
+ } else {
+
+ // if this is in the new blocks link list already, then this is not the first update for this memo field
+ // this would be second or third update on the field since the original change and not commited
+ // Since it won't be needed in either a Commmit() or Abort(), can be freed immediately
+ if( llNewBlocks.SearchFor( ulBlockNo ) > 0 ){
+ if(( rc = FreeMemoBlockChain( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw rc;
+ }
+ if(( llNewBlocks.RemoveByVal( ulBlockNo )) < XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw rc;
+ }
+ } else {
+ // first revision, save what it was in case of Abort() command
+ if(( llOldBlocks.InsertAtFront( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw rc;
+ }
+ }
+ if(( rc = dbf->PutField( iFieldNo, "" )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw rc;
+ }
+ }
+ } else {
+ // free up the old space
+ xbUInt32 ulLastDataBlock = 0L;
+
+ if( ulBlockNo > 0 ){
+ if( llNewBlocks.SearchFor( ulBlockNo ) > 0 ){
+
+ if(( rc = FreeMemoBlockChain( ulBlockNo, ulLastDataBlock )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw rc;
+ }
+ } else {
+ // first revision, save what it was in case of Abort() command
+ if(( rc = llOldBlocks.InsertAtFront( ulBlockNo )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw rc;
+ }
+ }
+ }
+ // should next line be unsigned 32 bit int?
+ xbUInt32 ulTotalLen = 8 + sMemoData.Len();
+ xbUInt32 ulBlocksNeeded;
+ if( ulTotalLen % GetBlockSize())
+ ulBlocksNeeded = ulTotalLen / GetBlockSize() + 1;
+ else
+ ulBlocksNeeded = ulTotalLen / GetBlockSize();
+
+ xbBool bUsedBlockFound;
+ xbUInt32 ulHeadBlock;
+ xbUInt32 ulPrevNode;
+ if(( rc = FindBlockSetInChain( ulBlocksNeeded, ulLastDataBlock, ulHeadBlock, ulPrevNode, bUsedBlockFound )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw rc;
+ }
+ iField1 = -1;
+ iStartPos = 8;
+ ulFieldLen = sMemoData.Len() + 8;
+
+ if( bUsedBlockFound ){
+
+ if(( rc = GetBlockSetFromChain( ulBlocksNeeded, ulHeadBlock, ulPrevNode )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw rc;
+ }
+
+ if(( rc = WriteBlockHeader( ulHeadBlock, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw rc;
+ }
+
+ if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw rc;
+ }
+ } else { // append to the end
+
+ if(( rc = WriteBlockHeader( ulLastDataBlock, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw rc;
+ }
+
+ if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw rc;
+ }
+
+ if(( rc = xbFputc( 0x00, (xbInt32)((ulBlocksNeeded * GetBlockSize()) - (sMemoData.Len() + 8)))) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw rc;
+ }
+
+ if( ulLastDataBlock == ulHdrNextBlock ){ // this is first node to be added to the node chain
+ ulHdrNextBlock += ulBlocksNeeded;
+ ulHeadBlock = ulLastDataBlock;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw rc;
+ }
+
+ } else { // adding memo data to the end of the file, but chain exists
+
+ ulNextFreeBlock = ulLastDataBlock + ulBlocksNeeded;
+ ulHeadBlock = ulLastDataBlock;
+ if(( rc = WriteBlockHeader( ulPrevNode, 2 )) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw rc;
+ }
+ }
+ }
+
+ if(( rc = llNewBlocks.InsertAtFront( ulHeadBlock )) != XB_NO_ERROR ){ // In case of Abort(), this block needs to be freed
+ iErrorStop = 270;
+ throw rc;
+ }
+ if(( rc = dbf->PutLongField( iFieldNo, (xbInt32) ulHeadBlock )) != XB_NO_ERROR ){
+ iErrorStop = 280;
+ throw rc;
+ }
+ }
+ }
+
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::UpdateMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Write block header.
+/*!
+ \param ulBlockNo Block to read
+ \param iOption 1 - Read fields option 1
+ 2 - Read fields option 2
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbMemoDbt4::WriteBlockHeader( xbUInt32 ulBlockNo, xbInt16 iOption ) {
+
+ xbInt16 rc = XB_NO_ERROR;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ if( iOption == 1 ){
+ ePutInt16 ((char *) mbb, iField1 );
+ ePutInt16 ((char *) mbb+2, iStartPos );
+ ePutUInt32((char *) mbb+4, ulFieldLen );
+ }
+ else if( iOption == 2 ){
+ ePutUInt32((char *) mbb, ulNextFreeBlock );
+ ePutUInt32((char *) mbb+4, ulFreeBlockCnt );
+ }
+ else{
+ iErrorStop = 100;
+ rc = XB_INVALID_OPTION;
+ throw rc;
+ }
+
+ if(( rc = WriteBlock( ulBlockNo, 8, mbb )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ rc = XB_READ_ERROR;
+ }
+ }
+ catch (xbInt16 rc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::WriteHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( rc ));
+ }
+ return rc;
+}
+/***********************************************************************/
+//! @brief Empty the memo file.
+/*!
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbMemoDbt4::Zap(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ char cBuf[4];
+ try{
+ ulHdrNextBlock = 1L;
+ ePutUInt32( cBuf, ulHdrNextBlock );
+
+ if(( iRc != xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if(( iRc != xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ if(( iRc != xbTruncate( GetBlockSize())) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbMemoDbt4::Zap() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}/***********************************************************************/
+} /* namespace */
+#endif /* XB_DBF4_SUPPORT */
+#endif /* XB_MEMO_SUPPORT */
+
diff --git a/src/core/xbssv.cpp b/src/core/xbssv.cpp
new file mode 100755
index 0000000..532f942
--- /dev/null
+++ b/src/core/xbssv.cpp
@@ -0,0 +1,658 @@
+/* xbssv.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+namespace xb{
+
+const xbErrorMessage xbErrorMessages[] = {
+ { XB_NO_ERROR, "No Error" },
+ { XB_NO_MEMORY, "No Memory" },
+ { XB_INVALID_OPTION, "Invalid Option" },
+ { XB_INVALID_PARAMETER, "Invalid Parameter" },
+ { XB_DUP_TABLE_OR_ALIAS, "Duplicate Alias/Table Name" },
+ { XB_INVALID_NODELINK, "Invalid Node Link" },
+ { XB_KEY_NOT_UNIQUE, "Key Not Unique" },
+ { XB_MEMCPY_ERROR, "Memory copy failure" },
+ { XB_FILE_EXISTS, "File Already Exists" },
+ { XB_ALREADY_OPEN, "Database already open" },
+ { XB_DBF_FILE_NOT_OPEN, "DBF File Not Open" },
+ { XB_FILE_NOT_FOUND, "File not found" },
+ { XB_FILE_TYPE_NOT_SUPPORTED, "Not an Xbase type database" },
+ { XB_RENAME_ERROR, "Unable to rename file" },
+ { XB_INVALID_OBJECT, "Invalid Object" },
+ { XB_NOT_OPEN, "Database not open" },
+ { XB_NOT_FOUND, "Not Found" },
+ { XB_OPEN_ERROR, "Open Error" },
+ { XB_CLOSE_ERROR, "Close Error" },
+ { XB_SEEK_ERROR, "Seek Error" },
+ { XB_READ_ERROR, "Read Error" },
+ { XB_WRITE_ERROR, "Error writing to disk drive" },
+ { XB_EOF, "End Of File" },
+ { XB_BOF, "Beginning Of File" },
+ { XB_INVALID_BLOCK_SIZE, "Invalid Block Size" },
+ { XB_INVALID_BLOCK_NO, "Invalid Block Number" },
+ { XB_INVALID_RECORD, "Invalid Record Number" },
+ { XB_DELETE_FAILED, "Delete Failed" },
+ { XB_INVALID_TABLE_NAME, "Invalid Table Name" },
+ { XB_EMPTY, "Empty Table or Index" },
+ { XB_LIMIT_REACHED, "Limit Reached" },
+ { XB_BLOCKREAD_NOT_ENABLED, "Block Read Mode is not enabled" },
+ { XB_DIRECTORY_ERROR, "Directory Read/Write error" },
+ { XB_INVALID_FIELD_TYPE, "Unknown Field Type" },
+ { XB_INVALID_FIELD_NO, "Invalid Field Number" },
+ { XB_INVALID_DATA, "Invalid Data" },
+ { XB_INVALID_FIELD_NAME, "Invalid Field Name" },
+ { XB_INVALID_MEMO_FIELD, "Not a Memo field" },
+ { XB_INVALID_FIELD, "Invalid Field" },
+ { XB_INVALID_FIELD_LEN, "Invalid Field Length" },
+ { XB_INVALID_DATE, "Invalid Date" },
+ { XB_INVALID_LOCK_OPTION, "Invalid Lock Option" },
+ { XB_LOCK_FAILED, "Lock Failed" },
+ { XB_TABLE_NOT_LOCKED, "Table Not Locked" },
+ { XB_PARSE_ERROR, "Parse Error" },
+ { XB_INVALID_FUNCTION, "Invalid or Undefined Function" },
+ { XB_INVALID_PARM, "Invalid Parm" },
+ { XB_INCONSISTENT_PARM_LENS, "Inconsistent parm lengths" },
+ { XB_INCOMPATIBLE_OPERANDS, "Incompatible operands" },
+ { XB_UNBALANCED_PARENS, "Unbalanced Parens" },
+ { XB_UNBALANCED_QUOTES, "Unbalanced Quotes" },
+ { XB_INVALID_EXPRESSION, "Invalid expression" },
+ { XB_INVALID_KEYNO, "Invalid Key Number" },
+ { XB_INVALID_INDEX, "Index File Error" },
+ { XB_INVALID_TAG, "Invalid index tag" },
+ { XB_SYNTAX_ERROR, "Invalid SQL Syntax" },
+ { XB_MAX_ERROR_NO, "End of Error List" }
+};
+// see also xbretcod.h
+
+xbInt16 xbSsv::iEndianType = 0;
+xbString xbSsv::sDefaultDateFormat = "MM/DD/YY";
+
+xbInt16 xbSsv::iDefaultFileVersion = 4;
+xbString xbSsv::sNullString = "";
+xbBool xbSsv::bDefaultAutoCommit = xbTrue;
+
+xbString xbSsv::sDataDirectory = PROJECT_DATA_DIR;
+xbString xbSsv::sTempDirectory = PROJECT_TEMP_DIR;
+
+#ifdef XB_LOGGING_SUPPORT
+xbString xbSsv::sLogDirectory = PROJECT_LOG_DIR;
+xbString xbSsv::sLogFileName = PROJECT_DFLT_LOGFILE;
+#endif // XB_LOGGING_SUPPORT
+
+#ifdef XB_LOCKING_SUPPORT
+xbInt32 xbSsv::lDefaultLockWait = 100;
+xbInt16 xbSsv::iDefaultLockRetries = 3;
+xbBool xbSsv::bDefaultAutoLock = xbTrue;
+xbInt16 xbSsv::iDefaultLockFlavor = 1;
+xbBool xbSsv::bMultiUser = xbTrue;
+#else
+xbBool xbSsv::bMultiUser = xbFalse;
+#endif // XB_LOCKING_SUPPORT
+
+#if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT)
+xbInt16 xbSsv::iUniqueKeyOpt = XB_HALT_ON_DUPKEY;
+ // is one of XB_HALT_ON_DUPKEY || XB_EMULATE_DBASE
+#endif // defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT)
+
+
+#ifdef XB_MDX_SUPPORT
+xbInt16 xbSsv::iCreateMdxBlockSize = 1024; // 1024 is DBase 7 default size for MDX index blocks
+#endif // XB_MDX_SUPPORT
+
+#ifdef XB_BLOCKREAD_SUPPORT
+xbUInt32 xbSsv::ulDefaultBlockReadSize = 32768; // 32K buffer for block DBF datafile reads
+#endif // XB_BLOCKREAD_SUPPORT
+
+/*************************************************************************/
+//! @brief Class Constructor.
+xbSsv::xbSsv(){}
+/*************************************************************************/
+void xbSsv::BitDump( unsigned char c ) const {
+ for( int i = 7; i >= 0; i-- )
+ std::cout << (BitSet( c, i ) ? 1 : 0);
+}
+void xbSsv::BitDump( char c ) const {
+ BitDump( (unsigned char) c );
+}
+/*************************************************************************/
+//! @brief Check a bit in a one byte field and see if it is set.
+/*!
+ \param c One byte char field to examine.
+ \param iBitNo which bit to examine.
+ \returns xbTrue Bit is set<br>
+ xbFalse Bit is not set
+*/
+xbBool xbSsv::BitSet( unsigned char c, xbInt16 iBitNo ) const {
+ return c & 1 << iBitNo;
+}
+/*************************************************************************/
+//! @brief Display error message on console for a given error number.
+/*!
+ \param iErrorCode Error number to reference
+*/
+void xbSsv::DisplayError( xbInt16 iErrorCode ) const {
+ std::cout << (const char *) GetErrorMessage( iErrorCode ) << std::endl;
+}
+/*************************************************************************/
+//! @brief Get the default auto commit setting.
+/*!
+
+ When auto commit is enabled, the library will automatically post any updates
+ when moving off an updated record or closing files.
+ If auto commit is disabled, the application program will need to explicitly
+ update the tables using using dbf->Put() and dbf->AppendRecord().
+
+ \returns xbTrue if auto commit is turned on<br>
+ xbFalse is auto commit is turned off
+*/
+
+xbBool xbSsv::GetDefaultAutoCommit() const {
+ return bDefaultAutoCommit;
+}
+/*************************************************************************/
+//! @brief Get the current data directory.
+/*!
+ \returns xbString containing the current data directory
+ where the database files are stored.
+*/
+
+xbString &xbSsv::GetDataDirectory() const {
+ return sDataDirectory;
+}
+/*************************************************************************/
+//! @brief Get the default date format.
+/*!
+ \returns xbString containing the default date format.
+*/
+
+xbString & xbSsv::GetDefaultDateFormat() const {
+ return sDefaultDateFormat;
+}
+/*************************************************************************/
+//! @brief Get the Endian type.
+/*!
+ \returns B - Big endian<br>
+ L - Little endian<br>
+*/
+xbInt16 xbSsv::GetEndianType() const {
+ return iEndianType;
+}
+/*************************************************************************/
+//! @brief Get an error message.
+/*!
+ \param iErrorCode ErrorCode is the error number of description to be returned.
+ \returns Returns a pointer to a string containing a text description for the error code.
+*/
+
+const char * xbSsv::GetErrorMessage( xbInt16 iErrorCode ) const{
+
+ if( iErrorCode > 0 || iErrorCode <= XB_MAX_ERROR_NO )
+ return "";
+
+ xbBool bFound = xbFalse;
+ xbInt16 iCtr = 0;
+ while( !bFound ){
+ if( xbErrorMessages[iCtr].iErrorNo == XB_MAX_ERROR_NO )
+ return "Unknown Error";
+ if( xbErrorMessages[iCtr].iErrorNo == iErrorCode )
+ return xbErrorMessages[iCtr].sErrorText;
+ iCtr++;
+ }
+ return "";
+}
+/*************************************************************************/
+//! @brief Get home directory.
+/*!
+ \param sHomeDirOut - Output home directory for current user.
+*/
+
+void xbSsv::GetHomeDir( xbString &sHomeDirOut ){
+
+ #ifdef HAVE_GETENV_S_F
+
+ char sPath[MAX_PATH];
+ size_t lSize;
+
+ sHomeDirOut = "";
+ memset( sPath, 0x00, MAX_PATH );
+
+ getenv_s( &lSize, NULL, 0, "HOMEDRIVE" );
+ if( lSize > 0 ){
+ getenv_s( &lSize, sPath, lSize, "HOMEDRIVE" );
+ sHomeDirOut = sPath;
+ memset( sPath, 0x00, MAX_PATH );
+ }
+
+ getenv_s( &lSize, NULL, 0, "HOMEPATH" );
+ if( lSize > 0 ){
+ getenv_s( &lSize, sPath, lSize, "HOMEPATH" );
+ sHomeDirOut += sPath;
+ }
+ if( sHomeDirOut == "" )
+ sHomeDirOut = "C:\xbase64";
+
+ #elif defined(WIN32)
+ sHomeDirOut.Sprintf( "%s%s", getenv( "HOMEDRIVE" ), getenv( "HOMEPATH" ));
+
+ #else
+ sHomeDirOut.Sprintf( "%s", getenv( "HOME" ));
+ sHomeDirOut.Trim();
+ if( sHomeDirOut == "" )
+ sHomeDirOut.Sprintf( "%s", getpwuid( getuid())->pw_dir );
+ #endif
+
+ sHomeDirOut.Trim();
+}
+
+
+/*************************************************************************/
+//! @brief Set the data directory.
+/*!
+ \param sDataDirectory Set the data directory.
+*/
+
+void xbSsv::SetDataDirectory( const xbString &sDataDirectory ){
+ this->sDataDirectory = sDataDirectory;
+
+ #ifdef WIN32
+ this->sDataDirectory.SwapChars( '/', '\\' );
+ #else
+ this->sDataDirectory.SwapChars( '\\', '/' );
+ #endif
+
+}
+
+/*************************************************************************/
+//! @brief Set the default date format.
+/*!
+ \param sDefaultDateFormat Set the default date format.
+*/
+
+void xbSsv::SetDefaultDateFormat( const xbString &sDefaultDateFormat ) {
+ this->sDefaultDateFormat = sDefaultDateFormat;
+}
+
+/*************************************************************************/
+//! @brief Set the default auto commit.
+/*!
+
+ Disabling auto commit requires the application execute explicit updates
+ using dbf->Put() and dbf->AppendRecord(). With auto commit on, the library
+ posts updates automatically when moving off the current record or closing
+ a file.
+
+ \param bDefaultAutoCommit xbTrue - Enable default auto commit.<br>
+ xbFalse - Disable default auto commit.<br>
+*/
+
+void xbSsv::SetDefaultAutoCommit( xbBool bDefaultAutoCommit ) {
+ this->bDefaultAutoCommit = bDefaultAutoCommit;
+}
+/*************************************************************************/
+//! @brief Set the endian type
+/*!
+ This routine determines the Endian-ness at run time instead of
+ compile time as some processers (ie; Sparc,ARM) can be switched either way.
+ This routine is called automatically by the library at startup and does not
+ need to be called in an application program.
+
+*/
+
+void xbSsv::SetEndianType() {
+ xbInt16 e = 1;
+ iEndianType = *(char *) &e;
+ if( iEndianType )
+ iEndianType = 'L';
+ else
+ iEndianType = 'B';
+ return;
+}
+
+/*************************************************************************/
+//! @brief Set the temp directory.
+/*!
+ \param sTempDirectory Set the data direcroty.
+*/
+
+void xbSsv::SetTempDirectory( const xbString &sTempDirectory ){
+ this->sTempDirectory = sTempDirectory;
+
+ #ifdef WIN32
+ this->sTempDirectory.SwapChars( '/', '\\' );
+ #else
+ this->sTempDirectory.SwapChars( '\\', '/' );
+ #endif
+
+}
+
+/*************************************************************************/
+//! @brief Get the OS dependent path separator.
+/*!
+ \returns Returns '\' for windows environment, otherwise returns '/'.
+*/
+
+char xbSsv::GetPathSeparator() const {
+ #ifdef WIN32
+ return '\\';
+ #else
+ return '/';
+ #endif
+}
+
+/*************************************************************************/
+//! @brief Get the current temp directory.
+/*!
+ \returns xbString containing the current data directory
+ where the database files are stored.
+*/
+
+xbString &xbSsv::GetTempDirectory() const {
+ return sTempDirectory;
+}
+
+/*************************************************************************/
+#ifdef XB_LOGGING_SUPPORT
+
+//! @brief Get the default log file name.
+/*!
+ \returns Returns the log file name.
+*/
+
+xbString & xbSsv::GetLogFileName() const {
+ return sLogFileName;
+}
+
+/*************************************************************************/
+//! @brief Get the default log directory.
+/*!
+ \returns Returns the log directory.
+*/
+
+
+xbString & xbSsv::GetLogDirectory() const {
+ return sLogDirectory;
+}
+/*************************************************************************/
+//! @brief Set the default log directory name.
+/*!
+ \param sLogDirectory Name of desired log directory.
+
+*/
+
+void xbSsv::SetLogDirectory( const xbString &sLogDirectoryIn ){
+
+ this->sLogDirectory = sLogDirectoryIn;
+ #ifdef WIN32
+ this->sLogDirectory.SwapChars( '/', '\\' );
+ #else
+ this->sLogDirectory.SwapChars( '\\', '/' );
+ #endif
+
+}
+
+//! @brief Set the log file name.
+/*!
+ \param sLogFileName - Log File Name.
+ \return void
+*/
+
+void xbSsv::SetLogFileName( const xbString & sLogFileName ){
+
+ this->sLogFileName = sLogFileName;
+}
+
+
+#else
+
+xbString & xbSsv::GetLogFileName() const {
+ return sNullString;
+}
+
+
+xbString & xbSsv::GetLogDirectory() const {
+
+std::cout << "xbSsv::GetLogDirectory() returning null\n";
+
+ return sNullString;
+}
+
+void xbSsv::SetLogDirectory( const xbString &sLogDirectory ){
+ return;
+}
+
+void xbSsv::SetLogFileName( const xbString & sLogFileName ){
+ return;
+}
+
+#endif
+
+/*************************************************************************/
+
+#ifdef XB_LOCKING_SUPPORT
+
+//! @brief Get the default lock retries.
+/*!
+ This is the number of lock attempts the libary will make before returning
+ failure if the file can not be locked.
+ \returns Default lock retry count.
+*/
+xbInt16 xbSsv::GetDefaultLockRetries() const {
+ return iDefaultLockRetries;
+}
+
+//! @brief Set the default lock retries.
+/*!
+ \param iDefaultLockRetries - Number of lock attempts before returning failure.
+*/
+void xbSsv::SetDefaultLockRetries( xbInt16 iDefaultLockRetries ) {
+ this->iDefaultLockRetries = iDefaultLockRetries;
+}
+
+//! @brief Get the default auto lock setting.
+/*!
+ When auto locking is turned on, the library automatically locks and unlocks
+ files and indices as needed in a multi user environment.
+ \returns Number of lock attempt settings.
+*/
+xbBool xbSsv::GetDefaultAutoLock() const {
+ return bDefaultAutoLock;
+}
+
+
+//! @brief Set the default auto lock setting.
+/*!
+ When auto locking is turned on, the library automatically locks and unlocks
+ files and indices as needed in a multi user environment. Locking is not required
+ in single a single user environment.
+
+ \param bDefaultAutoLock xbTrue - Turn autolocking on<br>
+ xbFalse - Turn autolocking off<br>
+*/
+void xbSsv::SetDefaultAutoLock( xbBool bDefaultAutoLock ) {
+ this->bDefaultAutoLock = bDefaultAutoLock;
+}
+
+//! @brief Enable default auto locking.
+/*!
+ When auto locking is turned on, the library automatically locks and unlocks
+ files and indices as needed in a multi user environment.
+*/
+void xbSsv::EnableDefaultAutoLock() {
+ this->bDefaultAutoLock = xbTrue;
+}
+
+//! @brief Disable defalt auto locking.
+/*!
+ When auto locking is turned off, the library does not automatically lock
+ and unlock files and indices as needed in a multi user environment.
+ Locking is not needed in a single user environment.
+
+*/
+void xbSsv::DisableDefaultAutoLock() {
+ this->bDefaultAutoLock = xbFalse;
+}
+/***************************************************************************/
+//! @brief Get default lock flavor
+/*!
+ Currently one flavor. This routine is part of the structure to support
+ future additional locking scenarios for Clipper and Foxpro.
+ \returns 1
+*/
+xbInt16 xbSsv::GetDefaultLockFlavor() const {
+ return iDefaultLockFlavor;
+}
+
+/***************************************************************************/
+//! @brief Set default lock flavor
+/*!
+ Currently one flavor. This routine is part of the structure to support
+ future additional locking scenarios for Clipper and Foxpro.
+*/
+void xbSsv::SetDefaultLockFlavor( xbInt16 iDefaultLockFlavor ) {
+ this->iDefaultLockFlavor = iDefaultLockFlavor;
+}
+
+/***************************************************************************/
+//! @brief Set default lock wait
+/*!
+ \param lLockWait Set default lock wait in milliseconds.
+*/
+void xbSsv::SetDefaultLockWait( xbInt32 lLockWait ) {
+ this->lDefaultLockWait = lLockWait;
+}
+
+/***************************************************************************/
+//! @brief Get default lock wait
+/*!
+ \returns Lock wait time in milliseconds.
+
+*/
+xbInt32 xbSsv::GetDefaultLockWait() const {
+ return lDefaultLockWait;
+}
+#endif
+
+/***************************************************************************/
+//! @brief Get the multi user setting.
+/*!
+ \returns xbTrue - Multi user mode turned on.<br>
+ xbFalse - Multi user mode turned off.<br>
+*/
+xbBool xbSsv::GetMultiUser() const {
+ return bMultiUser;
+}
+
+//! @brief Get the multi user setting.
+/*!
+ \param bMultiUser xbTrue - Turn on Multi user mode.<br>
+ xbFalse - Turn off Multi user mode.<br>
+*/
+void xbSsv::SetMultiUser( xbBool bMultiUser ) {
+ this->bMultiUser = bMultiUser;
+}
+
+
+
+/************************************************************************/
+#ifdef XB_MDX_SUPPORT
+//! @brief Get the mdx file block size used when creating a memo file.
+/*!
+ \returns system default setting for MDX block size.
+*/
+xbInt16 xbSsv::GetCreateMdxBlockSize() const {
+ return iCreateMdxBlockSize;
+}
+
+/************************************************************************/
+//! @brief Create mdx block size.
+/*!
+ This routine sets the mdx file block size at the system level. This value is
+ used when the mdx index file is initially created so if you want to change it,
+ this must be called before creating the table.
+
+ \param iBlockSize - Block size, must be evenly divisible by 512 and <= 16384
+ \returns XB_INVALID_BLOCK_SIZE<br>XB_NO_ERROR
+*/
+
+xbInt16 xbSsv::SetCreateMdxBlockSize( xbInt16 iBlockSize ){
+
+ if( iBlockSize < 512 || iBlockSize > 16384 || iBlockSize % 512 )
+ return XB_INVALID_BLOCK_SIZE;
+ else
+ iCreateMdxBlockSize = iBlockSize;
+
+ return XB_NO_ERROR;
+}
+#endif
+
+/************************************************************************/
+#if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT)
+//! @brief Get Unique Key Opt
+/*!
+ This routine returns the Unique Key Processing Option which is one of:
+ XB_HALT_ON_DUPKEY
+ XB_EMULATE_DBASE
+*/
+
+xbInt16 xbSsv::GetUniqueKeyOpt() const {
+ return iUniqueKeyOpt;
+}
+
+//! @brief Set Unique Key Opt
+/*! @brief Set Unique Key Opt
+ This routine Sets the Unique Key Processing Option which is one of:
+ XB_HALT_ON_DUPKEY
+ XB_EMULATE_DBASE
+*/
+xbInt16 xbSsv::SetUniqueKeyOpt( xbInt16 iOpt ){
+ if( iOpt == XB_HALT_ON_DUPKEY || iOpt == XB_EMULATE_DBASE ){
+ iUniqueKeyOpt = iOpt;
+ return XB_NO_ERROR;
+ } else {
+ return XB_INVALID_OPTION;
+ }
+}
+#endif
+/************************************************************************/
+#ifdef XB_BLOCKREAD_SUPPORT
+
+//! @brief Get Default Read Block Size
+/*!
+ This routine returns the default read block size used when allocating
+ buffer space for block reads of table data. Initial setting is 32768 bytes.
+*/
+xbUInt32 xbSsv::GetDefaultBlockReadSize() const {
+ return ulDefaultBlockReadSize;
+}
+
+
+//! @brief Set Default Read Block Size
+/*!
+ This routine sets the default read block size used when allocating
+ buffer space for block reads of table data. Initial setting is 32768 bytes.
+*/
+void xbSsv::SetDefaultBlockReadSize( xbUInt32 ulDfltBlockReadSize ){
+ ulDefaultBlockReadSize = ulDfltBlockReadSize;
+}
+
+#endif // XB_BLOCKREAD_SUPPORT
+/************************************************************************/
+
+
+
+
+} /* namespace */ \ No newline at end of file
diff --git a/src/core/xbstring.cpp b/src/core/xbstring.cpp
new file mode 100755
index 0000000..89cefb6
--- /dev/null
+++ b/src/core/xbstring.cpp
@@ -0,0 +1,2000 @@
+/* xbstring.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2021,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+//#ifdef __GNU LesserG__
+// #pragma implementation "xbstring.h"
+//#endif
+
+#include "xbase.h"
+
+
+namespace xb{
+
+XBDLLEXPORT const char * xbString::NullString = "";
+XBDLLEXPORT char xbString::cJunkBuf;
+
+/************************************************************************/
+//! @brief Destructor
+
+xbString::~xbString(){
+ if (data != NULL)
+ free(data);
+
+}
+
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param ulSize - Allocation size. The allocation size is normally handled internally
+ by the class, but it can be set in this constructor.
+*/
+xbString::xbString(xbUInt32 ulSize) {
+ data = (char *)calloc(1, ulSize);
+ this->size = ulSize;
+// memset( data, 0x00, ulSize ); - redundant, initialized by calloc
+}
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param c - Initialize string to c.
+*/
+xbString::xbString(char c) {
+ data = (char *)calloc(1, 2);
+ data[0] = c;
+ data[1] = 0;
+ size = 2;
+}
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param s - Initialize string to s.
+*/
+xbString::xbString( const char *s ) {
+
+ if( s == NULL ){
+ size = 0;
+ data = NULL;
+ } else {
+ size = (xbUInt32) (strlen(s) + 1 );
+ data = (char *) calloc( 1, size );
+ xb_strcpy( data, s );
+ }
+ // ctor(s);
+}
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param d - Initiailize string to d.
+*/
+xbString::xbString( xbDouble d ) {
+ data = NULL;
+ size = 0;
+ Sprintf("%f", d);
+}
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param s Initialize string to s.
+ \param ulMaxLen Maximum length of string. Truncate any characters greater than ulMaxLen.
+*/
+xbString::xbString( const char *s, xbUInt32 ulMaxLen ) {
+ xbUInt32 sSize = (xbUInt32) strlen( s );
+ if( sSize < ulMaxLen )
+ size = sSize;
+ else
+ size = ulMaxLen;
+ data = (char *)calloc(1, size+1);
+ for( xbUInt32 i = 0; i < size; i++ )
+ data[i] = s[i];
+ data[size] = '\0';
+ size++; // account for null trailing byte
+ return;
+}
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param s Initialize string to s.
+*/
+xbString::xbString( const xbString &s ) {
+ ctor(s.Str());
+}
+
+/************************************************************************/
+//! @brief Operator const char *
+/*!
+ \returns Pointer to string data.
+*/
+xbString::operator const char *() const {
+ return data ? data : NullString;
+}
+
+/************************************************************************/
+//! @brief Set operator =
+/*!
+ \param s - Set the string to the string on the right of the equal sign.
+*/
+xbString &xbString::operator=( const xbString &s ) {
+ return Set(s);
+}
+/************************************************************************/
+//! @brief Set operator =
+/*!
+ \param s - Set the string to the string on the right of the equal sign.
+*/
+xbString &xbString::operator=( const char *s ) {
+ return Set(s);
+}
+
+/************************************************************************/
+//! @brief Stream insertion operator <<
+/*!
+ std::cout << MyString << std::endl;
+
+ \param os Output stream
+ \param s String to send to output stream
+*/
+std::ostream& operator<< ( std::ostream& os, const xbString & s ) {
+ return os << s.Str();
+}
+/************************************************************************/
+//! @brief Append operator +=
+/*!
+ \param s - Append s to the string.
+*/
+xbString &xbString::operator+=( const xbString &s ) {
+
+ if (s.IsNull())
+ return (*this);
+
+ xbUInt32 Len = s.Len();
+ xbUInt32 oldLen = this->Len();
+ xbUInt32 newLen = Len + oldLen;
+
+ data = (char *)realloc(data, newLen+1);
+ if( !data )
+ return (*this);
+
+ if(oldLen == 0)
+ data[0] = 0;
+
+ char *t = data;
+ t+= oldLen;
+ for( xbUInt32 i = 0; i < Len; i++ )
+ *t++ = s.GetCharacter(i+1);
+
+ data[newLen] = '\0';
+ size == 0 ? size += (Len + 1) : size += Len;
+
+ return (*this);
+}
+/************************************************************************/
+//! @brief Append operator +=
+/*!
+ \param s - Append s to the string.
+*/
+xbString &xbString::operator+=( const char *s ) {
+
+ if (s == NULL)
+ return (*this);
+ xbUInt32 Len = (xbUInt32) strlen(s);
+ xbUInt32 oldLen = this->Len();
+ xbUInt32 newLen = Len + oldLen;
+ data = (char *)realloc(data, newLen+1);
+ if(oldLen == 0)
+ data[0] = 0;
+ for( xbUInt32 i = 0; i < Len; i++ )
+ data[i+oldLen] = s[i];
+ data[newLen] = '\0';
+ // size += Len;
+ size == 0 ? size+= (Len + 1) : size += Len;
+ return (*this);
+}
+/************************************************************************/
+//! @brief Append operator +=
+/*!
+ \param c - Append c to the string.
+*/
+xbString &xbString::operator+=( char c ) {
+ xbUInt32 Len = 1;
+ xbUInt32 oldLen = this->Len();
+ data = (char *)realloc(data, oldLen+Len+1);
+ data[oldLen] = c;
+ data[oldLen+1] = 0;
+ // size++;
+ size == 0 ? size += 2 : size++;
+ return (*this);
+}
+/************************************************************************/
+//! @brief Append operator -=
+/*!
+ Append s to the right of this string, right trimming both strings.
+ \param s - Append s to the right of the string value.
+*/
+xbString &xbString::operator-=( const xbString &s ) {
+
+ Rtrim();
+ if (s.IsNull())
+ return (*this);
+ xbUInt32 Len = s.Len();
+ xbUInt32 oldLen = this->Len();
+ xbUInt32 newLen = Len + oldLen;
+
+ data = (char *)realloc(data, newLen+1);
+ if(oldLen == 0)
+ data[0] = 0;
+
+ for( xbUInt32 i = 0; i < Len; i++ )
+ data[i+oldLen] = s.GetCharacter(i+1);
+
+ data[newLen] = '\0';
+ //size += Len;
+ size == 0 ? size += (Len+1) : size += Len;
+ Rtrim();
+ return (*this);
+}
+/************************************************************************/
+//! @brief Append operator -=
+/*!
+ Append s to the right of this string, right trimming both strings.
+ \param s - Append s to the right of the string value.
+*/
+xbString &xbString::operator-=(const char *s) {
+
+ Rtrim();
+ if (s == NULL)
+ return (*this);
+ xbUInt32 Len = (xbUInt32) strlen(s);
+ xbUInt32 oldLen = this->Len();
+ xbUInt32 newLen = Len + oldLen;
+
+ data = (char *)realloc(data, newLen+1);
+
+ if(oldLen == 0)
+ data[0] = 0;
+
+ for( xbUInt32 i = 0; i < Len; i++ )
+ data[i+oldLen] = s[i];
+ data[newLen] = '\0';
+
+ //size += Len;
+ size == 0 ? size += (Len+1) : size += Len;
+
+ Rtrim();
+ return (*this);
+}
+/************************************************************************/
+//! @brief Append operator -=
+/*!
+ Append c to the right of this string, trimming right space on this string first.
+ \param c - Append s to the right of the string value.
+*/
+xbString &xbString::operator-=(const char c) {
+ Rtrim();
+ xbUInt32 oldSize = size;
+
+ // size += 1;
+ size == 0 ? size += 2 : size += 1;
+
+ data = (char *)realloc( data, size );
+ if( oldSize == 0 ) data[0] = 0;
+ data[size-2] = c;
+ data[size-1] = 0;
+ Trim();
+ return (*this);
+}
+/************************************************************************/
+//! @brief Concatonate operator -
+/*!
+ Concatonate left string with right string returning reference to new string.
+ Both strings are trimmed.
+
+ \param s1 Right string operator.
+*/
+xbString xbString::operator-(const xbString &s1) {
+ xbString tmp( data );
+ tmp -= s1;
+ return tmp;
+}
+/************************************************************************/
+//! @brief Concatonate operator +
+/*!
+ Concatonate left string with right string returning reference to new string.
+
+ \param s1 Right string operator.
+*/
+xbString xbString::operator+( const char *s1) {
+ xbString tmp( data );
+ tmp += s1;
+ return tmp;
+}
+/************************************************************************/
+//! @brief Concatonate operator +
+/*!
+ Concatonate left string with right string returning reference to new string.
+
+ \param s1 Right string operator.
+*/
+xbString xbString::operator+( const xbString &s1) {
+ xbString tmp( data );
+ tmp += s1;
+ return tmp;
+}
+/************************************************************************/
+//! @brief Concatonate operator +
+/*!
+ Concatonate left string with right string returning reference to new string.
+
+ \param c Right string operator.
+*/
+
+xbString xbString::operator+( const char c) {
+ xbString tmp( data );
+ tmp += c;
+ return tmp;
+}
+/************************************************************************/
+//! @brief operator []
+/*!
+ \param n - Offset into the string of the byte to retrieve.
+ \returns c - The character to return from the offset within the [] brackets.
+*/
+char &xbString::operator[]( xbUInt32 n ) const {
+ if( n > 0 && n <= size )
+ return data[n-1];
+ else
+ return cJunkBuf;
+}
+/************************************************************************/
+//! @brief operator []
+/*!
+ \param n - Offset into the string of the byte to retrieve.
+ \returns c - The character to return from the offset within the [] brackets.
+*/
+char &xbString::operator[]( xbInt32 n ) const {
+ if( n > 0 && n <= (xbInt32) size )
+ return data[n-1];
+ else
+ return cJunkBuf;
+}
+/************************************************************************/
+//! @brief operator ==
+/*!
+ \param s String to compare
+ \returns xbTrue - Strings match.<br>
+ zbFalse - Strings don't match.<br>
+
+*/
+xbBool xbString::operator==( const xbString &s ) const {
+
+ if( data == NULL || data[0] == 0 ) {
+ if( s.data == NULL || s.data[0] == 0 )
+ return true;
+ return false;
+ } else {
+ if( s.data == NULL || s.data[0] == 0 )
+ return false;
+ return( strcmp(data,s.data) == 0 ? xbTrue : xbFalse );
+ }
+}
+/************************************************************************/
+//! @brief operator ==
+/*!
+ \param s String to compare
+ \returns xbTrue - Strings match.<br>
+ zbFalse - Strings don't match.<br>
+*/
+xbBool xbString::operator==( const char *s ) const {
+
+ if (s == NULL) {
+ if ( data == NULL)
+ return true;
+ return false;
+ }
+ if ((s[0] == 0) && data == NULL)
+ return true;
+ if ( data == NULL)
+ return false;
+ return( strcmp( data, s) == 0 ? xbTrue : xbFalse );
+}
+/************************************************************************/
+//! @brief operator !=
+/*!
+ \param s String to compare
+ \returns xbTrue - Strings don't match.<br>
+ xbFalse - Strings match.<br>
+*/
+xbBool xbString::operator!=( const xbString &s ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s.data == NULL || s.data[0] == 0 )
+ return xbFalse; // NULL != NULL
+ return xbTrue; // NULL != !NULL
+ } else {
+ if( s.data == NULL || s.data[0] == 0 )
+ return xbTrue; // !NULL != NULL
+ return( strcmp( data, s.data ) != 0 ? xbTrue : xbFalse ); // !NULL != !NULL
+ }
+}
+/************************************************************************/
+//! @brief operator !=
+/*!
+ \param s String to compare
+ \returns xbTrue - Strings don't match.<br>
+ zbFalse - Strings match.<br>
+*/
+xbBool xbString::operator!=( const char *s ) const {
+ if( s == NULL || s[0] == 0 ) {
+ if( data == NULL || data[0] == 0 )
+ return xbFalse; // NULL != NULL
+ return xbTrue; // NULL != !NULL
+ } else {
+ if( s == NULL || s[0] == 0 )
+ return xbTrue; // !NULL != NULL
+ return( strcmp( data, s ) != 0 ? xbTrue : xbFalse ); // !NULL != !NULL
+ }
+}
+/************************************************************************/
+//! @brief operator <
+/*!
+ \param s String to compare
+ \returns xbTrue - Left string is less than the right string.<br>
+ zbFalse - Left string is not less than the right string.<br>
+*/
+xbBool xbString::operator< ( const xbString &s ) const {
+
+ if( data == NULL || data[0] == 0 ) {
+ if( s.data == NULL || s.data[0] == 0 )
+ return false;
+ return true;
+ } else {
+ if( s.data == NULL || s.data[0] == 0 )
+ return false;
+ return ( strcmp(data, s.data) < 0 ? xbTrue : xbFalse );
+ }
+}
+/************************************************************************/
+//! @brief operator >
+/*!
+ \param s String to compare
+ \returns xbTrue - Left string is greater than the right string.<br>
+ zbFalse - Left string is not greater than the right string.<br>
+*/
+xbBool xbString::operator> ( const xbString &s ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s.data == NULL || s.data[0] == 0 )
+ return false;
+ return false;
+ } else {
+ if( s.data == NULL || s.data[0] == 0 )
+ return true;
+ return( strcmp(data,s.data) > 0 ? xbTrue : xbFalse );
+ }
+}
+/************************************************************************/
+//! @brief operator <=
+/*!
+ \param s String to compare
+ \returns xbTrue - Left string is less than or equal to the right string.<br>
+ zbFalse - Left string is not less than or equal to the right string.<br>
+*/
+xbBool xbString::operator<=( const xbString &s ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s.data == NULL || s.data[0] == 0 )
+ return true;
+ return true;
+ } else {
+ if( s.data == NULL || s.data[0] == 0 )
+ return false;
+ return( strcmp(data,s.data) <= 0 ? xbTrue : xbFalse );
+ }
+}
+/************************************************************************/
+//! @brief operator >=
+/*!
+ \param s String to compare
+ \returns xbTrue - Left string is greater than or equal to the right string.<br>
+ zbFalse - Left string is not greater than or equal to the right string.<br>
+*/
+xbBool xbString::operator>=( const xbString &s ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s.data == NULL || s.data[0] == 0 )
+ return true;
+ return false;
+ } else {
+ if( s.data == NULL || s.data[0] == 0 )
+ return true;
+ return( strcmp(data, s.data) >= 0 ? xbTrue : xbFalse );
+ }
+}
+
+/************************************************************************/
+//! @brief Add a prefixing back slash to specified characters in the string.
+/*!
+ \param c Character to prefix with a backslash.
+ \returns Reference to this string.
+*/
+xbString &xbString::AddBackSlash( char c ) {
+
+ xbUInt32 lCnt = CountChar( c );
+ if( lCnt == 0 )
+ return *this;
+ char *p;
+ if(( p = (char *)calloc( 1, size + lCnt )) == NULL )
+ return *this;
+
+ char *p2 = p;
+ for( xbUInt32 lS = 0; lS < size; lS++ ){
+ if( data[lS] == c )
+ *p2++ = '\\';
+ *p2++ = data[lS];
+ }
+ if( data )
+ free( data );
+ data = p;
+
+ // size += lCnt;
+ size == 0 ? size += (lCnt+1) : size += lCnt;
+
+ return *this;
+}
+/************************************************************************/
+//! @brief Append data to string.
+/*!
+ \param s String data to append.
+ \returns Reference to this string.
+*/
+xbString &xbString::Append( const xbString &s ) {
+ *this += s;
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Append data to string.
+/*!
+ \param s String data to append.
+ \returns Reference to this string.
+*/
+xbString &xbString::Append( const char *s ) {
+ *this += s;
+ return *this;
+}
+/************************************************************************/
+//! @brief Append data to string.
+/*!
+ \param c String data to append.
+ \returns Reference to this string.
+*/
+xbString &xbString::Append( char c ) {
+ *this += c;
+ return *this;
+}
+/************************************************************************/
+//! @brief Append data to string.
+/*!
+ \param s String data to append.
+ \param ulByteCount Maximum number of bytes to append.
+ \returns Reference to this string.
+*/
+xbString &xbString::Append( const char *s, xbUInt32 ulByteCount ) {
+
+ if ( s == NULL || !*s || ulByteCount == 0)
+ return (*this);
+
+ xbUInt32 ulOrigLen = this->Len();
+
+ // s might not be null byte at the end, can't use strlen
+ // xbUInt32 ulAddLen = strlen( s );
+ xbUInt32 ulAddLen = 0;
+ const char *p = s;
+
+ while( ulAddLen < ulByteCount && *p ){
+ p++;
+ ulAddLen++;
+ }
+
+ if( ulAddLen > ulByteCount )
+ ulAddLen = ulByteCount;
+
+ size = ulOrigLen + ulAddLen + 1;
+ data = (char *) realloc( data, size );
+
+ for( xbUInt32 i = 0; i < ulAddLen; i++ )
+ data[i+ulOrigLen] = s[i];
+
+ data[size-1] = 0x00;
+ return (*this);
+}
+
+/************************************************************************/
+//! @brief Assign portion of string.
+/*!
+ \param sStr - Source string for copy operation. sStr needs to be a Null terminated string.
+ \param ulStartPos - Starting position within source string.
+ \param ulCopyLen - Length of data to copy.
+ \returns Reference to this string.
+*/
+xbString &xbString::Assign(const char * sStr, xbUInt32 ulStartPos, xbUInt32 ulCopyLen){
+ if(data){
+ free(data);
+ data = 0;
+ size = 0;
+ }
+ xbUInt32 lLen = (xbUInt32) strlen( sStr );
+ if( ulStartPos > lLen ){
+ size = 0;
+ return( *this );
+ }
+ if((( ulCopyLen - 1) + ulStartPos ) > lLen )
+ ulCopyLen = lLen - ulStartPos + 1;
+ data = (char *)calloc(1, ulCopyLen + 1);
+
+ //size = ulCopyLen + 1;
+ size == 0 ? size += (ulCopyLen+1) : size += ulCopyLen;
+
+ for( xbUInt32 i = 0; i < ulCopyLen; i++ )
+ data[i] = sStr[i + ulStartPos - ((xbUInt32) 1)];
+ data[ulCopyLen] = '\0';
+ return (*this);
+}
+/************************************************************************/
+//! @brief Assign portion of string.
+/*!
+ \param sStr - Source string for copy operation. sStr needs to be a Null terminated string.
+ \param ulStartPos - Starting position within source string.
+ \returns Reference to this string.
+*/
+xbString &xbString::Assign(const char * sStr, xbUInt32 ulStartPos){
+ if(data){
+ free(data);
+ data = 0;
+ size = 0;
+ }
+ xbUInt32 ulSrcLen = (xbUInt32) strlen( sStr );
+ if( ulStartPos > ulSrcLen ){
+ size = 0;
+ return( *this );
+ }
+ xbUInt32 ulCopyLen;
+ ulCopyLen = ulSrcLen - ulStartPos + 1;
+ data = (char *)calloc(1, ulCopyLen + 1);
+
+ size = ulCopyLen + 1;
+
+ for( xbUInt32 i = 0; i < ulCopyLen; i++ )
+ data[i] = sStr[i + ulStartPos - ((xbUInt32) 1)];
+ data[ulCopyLen] = '\0';
+ return (*this);
+}
+
+/************************************************************************/
+//! @brief Assign portion of string.
+/*!
+ \param sStr - Source string for copy operation. sStr needs to be a Null terminated string.
+ \param ulStartPos - Starting position within source string.
+ \param ulCopyLen - Length of data to copy.
+ \returns Reference to this string.
+*/
+xbString &xbString::Assign(const xbString& sStr, xbUInt32 ulStartPos, xbUInt32 ulCopyLen){
+ if(data){
+ free(data);
+ data = 0;
+ size = 0;
+ }
+ xbUInt32 ulSrcLen = sStr.Len();
+ if( ulStartPos > ulSrcLen ){
+ size = 0;
+ return( *this );
+ }
+ if((( ulCopyLen - 1) + ulStartPos ) > ulSrcLen )
+ ulCopyLen = ulSrcLen - ulStartPos + 1;
+ data = (char *)calloc(1, ulCopyLen + 1);
+ size = ulCopyLen + 1;
+ for( xbUInt32 i = 0; i < ulCopyLen; i++ )
+ data[i] = sStr[i + ulStartPos];
+ data[ulCopyLen] = '\0';
+ return (*this);
+}
+/************************************************************************/
+//! @brief Assign portion of string.
+/*!
+ \param sStr - Source string for copy operation. sStr needs to be a Null terminated string.
+ \param ulStartPos - Starting position within source string.
+ \returns Reference to this string.
+*/
+xbString &xbString::Assign(const xbString& sStr, xbUInt32 ulStartPos){
+ if(data){
+ free(data);
+ data = 0;
+ size = 0;
+ }
+ xbUInt32 ulSrcLen = sStr.Len();
+ if( ulStartPos > ulSrcLen ){
+ size = 0;
+ return( *this );
+ }
+ xbUInt32 ulCopyLen;
+ ulCopyLen = ulSrcLen - ulStartPos + 1;
+ data = (char *)calloc(1, ulCopyLen + 1);
+ size = ulCopyLen;
+ for( xbUInt32 i = 0; i < ulCopyLen; i++ )
+ data[i] = sStr[i + ulStartPos];
+ data[ulCopyLen] = '\0';
+ size++;
+ return (*this);
+}
+/************************************************************************/
+//! @brief Copy a string
+/*!
+ \returns xbString.
+*/
+xbString xbString::Copy() const {
+ return( *this );
+}
+/************************************************************************/
+//! @brief Count the number of characters in the string.
+/*!
+ \param c Character to count.
+ \param iOpt 0 - Count the number of characters.<br>
+ 1 - Count the number of characters not between single or double quotes.
+ \returns The number of characters.
+*/
+xbUInt32 xbString::CountChar( char c, xbInt16 iOpt ) const {
+ if( iOpt == 0 )
+ return CountChar( c );
+ else{
+ xbBool bSingleQuote = xbFalse;
+ xbBool bDoubleQuote = xbFalse;
+ char cPrevChar = 0x00;
+ xbUInt32 i,j;
+ for( i = 0, j = 0; i < size; i++ ){
+ if( bSingleQuote && data[i] == '\'' && cPrevChar != '\\' ){
+ bSingleQuote = xbFalse;
+ }
+ else if( bDoubleQuote && data[i] == '"' && cPrevChar != '\\' ){
+ bDoubleQuote = xbFalse;
+ }
+ else if( data[i] == '\'' && cPrevChar != '\\' && !bDoubleQuote ){
+ bSingleQuote = xbTrue;
+ }
+ else if( data[i] == '"' && cPrevChar != '\\' && !bSingleQuote ){
+ bDoubleQuote = xbTrue;
+ }
+ else if( !bDoubleQuote && !bSingleQuote && data[i] == c ){
+ j++;
+ }
+ cPrevChar = data[i];
+ }
+ return j;
+ }
+}
+/************************************************************************/
+//! @brief Count the number of characters in the string.
+/*!
+ \param c Character to count.
+ \returns The number of characters.
+*/
+xbUInt32 xbString::CountChar( char c ) const {
+ xbUInt32 i,j;
+ for( i = 0,j = 0; i < size; i++ )
+ if( data[i] == c )
+ j++;
+ return j;
+}
+/************************************************************************/
+void xbString::ctor( const char *s ) {
+
+ // this routine assumes it was called by one of the constructors.
+
+ if (s == NULL) {
+ data = NULL;
+ size = 0;
+ return;
+ }
+
+ size = (xbUInt32) (strlen(s) + 1);
+ data = (char *) calloc( 1, size);
+
+ xb_strcpy(data, s);
+}
+/************************************************************************/
+//! @brief Convert hex character to string.
+/*!
+ This routine converts a four byte string in the format of 0x00 to a one byte char value.
+ The first four bytes of the string must be in the format 0x00.
+ Anything past the first four bytes is disregarded.
+
+ \param cOut Output character.
+ \returns XB_INVALID_PARM on error<br>
+ XB_NO_ERROR on success.
+*/
+xbInt16 xbString::CvtHexChar( char &cOut ){
+
+ int j, k;
+ char c;
+
+ if( Len() < 4 || data[0] != '0' || (data[1]!='X' && data[1]!='x' ))
+ return XB_INVALID_PARM;
+
+ c = (char) toupper( data[2] );
+ j = ( c > '9' ? c - 'A' + 10 : c - '0' );
+ c = (char)toupper( data[3] );
+ k = ( c > '9' ? c - 'A' + 10 : c - '0' );
+ j = ( j << 4 ) + k;
+
+ cOut = ( char ) j;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! @brief Convert string of hex characters to string.
+/*!
+
+ This routine converts a string of four byte format of 0x00 to a string of one byte chars.
+
+ \param sOut Output string of converted characters.
+ \returns XB_INVALID_PARM on error<br>
+ XB_NO_ERROR on success.
+*/
+xbInt16 xbString::CvtHexString( xbString &sOut ){
+ char c;
+ xbString ws;
+ ws = data;
+ sOut = "";
+ xbInt16 iRc;
+ while( ws.Len()){
+ if(( iRc= ws.CvtHexChar( c )) != XB_NO_ERROR )
+ return iRc;
+ sOut += c;
+ ws.Ltrunc( 4 );
+ }
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! @brief Convert string to xbUInt64 number
+/*!
+ \param ullOut - output unsigned long long.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbString::CvtULongLong( xbUInt64 &ullOut ){
+
+ // version 1 - fast, but no data checking
+ ullOut = 0;
+ char *s = data;
+ int i = 0;
+ while( *s ){
+ ullOut *= 10;
+ ullOut += (xbUInt64) *s - '0';
+ s++;
+ i++;
+ }
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! @brief Convert string to xbInt64 number
+/*!
+ \param llOut - output long long.
+ \returns XB_NO_ERROR
+*/
+xbInt16 xbString::CvtLongLong( xbInt64 &llOut ){
+
+ // version 1 - fast, but no data checking
+ llOut = 0;
+ char *s = data;
+ int i = 0;
+ while( *s ){
+ llOut *= 10;
+ llOut += (xbInt64) *s - '0';
+ s++;
+ i++;
+ }
+ return XB_NO_ERROR;
+}
+
+/************************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+void xbString::Dump( const char * title, xbInt16 iHexOption ) const {
+ fprintf(stdout, "%s StringSize[%d] DataLen=[%d] data=[%s]\n", title, size, Len(), data );
+ if( iHexOption ){
+ std::cout << "Hex values" << std::endl;
+ for( xbUInt32 i = 0; i < strlen( data ); i++ )
+ printf( " %x", data[i] );
+ std::cout << std::endl;
+ }
+}
+void xbString::Dump( const char * title ) const {
+ Dump( title, 0 );
+}
+
+void xbString::DumpHex( const char * title ) const {
+ Dump( title, 1 );
+}
+#endif
+
+/************************************************************************/
+//! @brief Extract an element out of a delimited string.
+/*!
+ \param sSrc Source string.
+ \param cDelim Delimiter.
+ \param lSkipCnt Number of delimiters to skip.
+ \param iOpt 0 - ignore single and double quotes.<br>
+ 1 - ignore delimiters between single or double quotes.
+ \returns Reference to string extracted from element.
+*/
+xbString &xbString::ExtractElement( xbString &sSrc, char cDelim, xbUInt32 lSkipCnt, xbInt16 iOpt )
+{
+ return ExtractElement( sSrc.Str(), cDelim, lSkipCnt, iOpt );
+}
+
+/************************************************************************/
+//! @brief Extract an element out of a delimited string.
+/*!
+ \param pSrc Source string.
+ \param cDelim Delimiter.
+ \param lSkipCnt Number of delimiters to skip.
+ \param iOpt 0 - ignore single and double quotes.<br>
+ 1 - ignore delimiters between single or double quotes.
+ \returns Reference to string extracted from element.
+*/
+xbString &xbString::ExtractElement( const char *pSrc, char cDelim, xbUInt32 lSkipCnt, xbInt16 iOpt )
+{
+ /* opt values
+ 0 - ignore single and double quotes
+ 1 - ignore delimiters between single or double quotes
+ */
+
+ xbUInt32 lLen;
+ xbUInt32 lCurCnt = 0;
+ xbBool bInSingleQuotes = xbFalse;
+ xbBool bInDoubleQuotes = xbFalse;
+ char cPrevChar = 0x00;
+ const char *s = pSrc;
+ const char *pAnchor;
+
+ /* skip past skipcnt delimiters */
+ while( *s && lCurCnt < (lSkipCnt-1) ){
+ if( iOpt == 0 ){
+ if( *s == cDelim )
+ lCurCnt++;
+ } else {
+ if( *s == cDelim && !bInSingleQuotes && !bInDoubleQuotes ){
+ lCurCnt++;
+ } else if( *s == '\'' && !bInDoubleQuotes && cPrevChar != '\\' ){
+ if( bInSingleQuotes == xbTrue )
+ bInSingleQuotes = xbFalse;
+ else
+ bInSingleQuotes = xbTrue;
+ } else if( *s == '"' && !bInSingleQuotes && cPrevChar != '\\' ){
+ if( bInDoubleQuotes == xbTrue )
+ bInDoubleQuotes = xbFalse;
+ else
+ bInDoubleQuotes = xbTrue;
+ }
+ }
+ cPrevChar = *s;
+ s++;
+ }
+
+ /* at the beginning of the field */
+ pAnchor = s;
+ xbBool bDone = xbFalse;
+ while( *s && !bDone ){
+ if( iOpt == 0 ){
+ if( *s == cDelim )
+ bDone = xbTrue;
+ } else {
+ if( *s == cDelim && !bInSingleQuotes && !bInDoubleQuotes ){
+ bDone = xbTrue;
+ } else if( *s == '\'' && !bInDoubleQuotes && cPrevChar != '\\' ){
+ if( bInSingleQuotes == xbTrue )
+ bInSingleQuotes = xbFalse;
+ else
+ bInSingleQuotes = xbTrue;
+ } else if( *s == '"' && !bInSingleQuotes && cPrevChar != '\\' ){
+ if( bInDoubleQuotes == xbTrue )
+ bInDoubleQuotes = xbFalse;
+ else
+ bInDoubleQuotes = xbTrue;
+ }
+ }
+ cPrevChar = *s;
+ s++;
+ }
+
+ // if at end of string, go back one and drop the delimiter
+ if( *s ) s--;
+
+ lLen = (xbUInt32)(s - pAnchor);
+
+ /* copy data */
+ data = (char *) realloc( data, lLen+1 );
+ memcpy( data, pAnchor, lLen );
+ data[lLen] = 0;
+ this->size = lLen+1;
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Get a character by position
+/*!
+ \param n - Position in string to extract. First position is 1 (not 0).
+ \returns Character from position n, or null.
+*/
+char xbString::GetCharacter( xbUInt32 n ) const {
+ if( n > 0 && n <= size )
+ return data[n-1];
+ else
+ return 0x00;
+}
+/************************************************************************/
+//! @brief Get the position of the last occurrence of a given character.
+/*!
+ \param c - Character to search for.
+ \returns Last position of character in the string.
+*/
+xbUInt32 xbString::GetLastPos(char c) const {
+
+ if (data == NULL)
+ return 0;
+
+ char *p = data;
+ xbUInt32 iPos = 0;
+ xbUInt32 hPos = 0;
+ while( *p && iPos++ < ( size - 1 )){
+ if( *p == c )
+ hPos = iPos;
+ p++;
+ }
+ if( hPos )
+ return hPos;
+ else
+ return 0;
+}
+/************************************************************************/
+//! @brief Get the position of the last occurrence of a given string.
+/*!
+ \param s - String to search for.
+ \returns Last position of character in the string.
+*/
+xbUInt32 xbString::GetLastPos(const char* s) const{
+
+ if (data == NULL)
+ return 0;
+
+ char *p = data;
+ char *saveP = NULL;
+ while( p ){
+ p = strstr( p, s);
+ if( p ){
+ saveP = p;
+ p++;
+ }
+ }
+ if (saveP == NULL)
+ return 0;
+ return (xbUInt32)(saveP - data) + 1;
+}
+/************************************************************************/
+//! @brief Get the path separator out of the string.
+/*!
+ This method assumes the string is a valid path name.
+ If it is, it returns either / or \.
+ \returns Char value containing either / or \ depending on OS.
+*/
+char xbString::GetPathSeparator() const {
+
+ if (data == NULL)
+ return 0x00;
+ char *p = data;
+ while( *p ){
+ if( *p == '\\' || *p == '/' )
+ return *p;
+ else
+ p++;
+ }
+ return 0x00;
+}
+
+/************************************************************************/
+//! @brief Retrieve the size of the string buffer.
+/*!
+ \returns Size of string buffer including the null terminating byte.
+*/
+xbUInt32 xbString::GetSize() const {
+ return size;
+}
+
+/************************************************************************/
+//! @brief Determine if the string has any alpha characters
+/*!
+ \returns xbTrue - String contains one or more aloha characters.<br>
+ xbFalse - String contains no alpha characters.
+*/
+xbBool xbString::HasAlphaChars() const {
+ for( xbUInt32 i = 0; i < size; i++ )
+ if( isalpha( data[i] ))
+ return xbTrue;
+ return xbFalse;
+}
+
+
+/************************************************************************/
+//! @brief Determine if string is empty
+/*!
+ \returns xbTrue if string is empty.<br>
+ xbFalse if string is not empty.
+*/
+xbBool xbString::IsEmpty() const {
+ if( data == NULL )
+ return true;
+ if( data[0] == 0 )
+ return xbTrue;
+ return xbFalse;
+}
+
+/************************************************************************/
+//! @brief Determine if string is NULL
+/*!
+ \returns xbTrue if string is NULL.<br>
+ xbFalse if string is not NULL.
+*/
+xbBool xbString::IsNull() const {
+ return( data == NULL );
+}
+
+
+/************************************************************************/
+//! @brief Retain left part of string, drop rightmost characters.
+/*!
+ \param ulLen New string length, truncate rightmost excess.
+ \returns Reference to string.
+*/
+xbString &xbString::Left( xbUInt32 ulLen ) {
+ return Mid( 1, ulLen );
+}
+
+/************************************************************************/
+//! @brief Retrieve length of current string.
+/*!
+ \returns String length, excluding the terminating null byte.
+*/
+// return length of string
+xbUInt32 xbString::Len() const {
+ return( data ? (xbUInt32) strlen(data) : 0 );
+}
+
+/************************************************************************/
+//! @brief Left trim white space from string.
+/*!
+ \returns Reference to this string.
+*/
+xbString &xbString::Ltrim(){
+
+ if( !data )
+ return *this;
+
+ char *p = data;
+ if( !*p || (*p && *p != ' ') )
+ return *this; /* nothing to do */
+
+ xbUInt32 s = 0;
+ while( *p && *p == ' ' ){
+ p++;
+ s++;
+ size--;
+ }
+
+ xbUInt32 i;
+ for( i = 0; i < size; i++ )
+ data[i] = data[i+s];
+ data[i] = 0x00;
+ data = (char *) realloc( data, size );
+
+ return *this;
+
+}
+
+/************************************************************************/
+//! @brief Left truncate string
+/*!
+ \param ulCnt Number of bytes to remove from the left.
+ \returns Reference to this string.
+*/
+xbString &xbString::Ltrunc( xbUInt32 ulCnt ){
+ if( ulCnt >= size ){
+ if( size > 0 ){
+ free( data );
+ data = NULL;
+ size = 0;
+ }
+ return *this;
+ }
+
+ char * ndata;
+ char * p;
+ ndata = (char *) calloc( 1, size - ulCnt );
+ p = data;
+ p += ulCnt;
+ xb_strcpy( ndata, p );
+ free( data );
+ data = ndata;
+ size = size - ulCnt;
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Extract portion of data from string
+/*!
+ \param ulStartPos Starting position
+ \param ulTargLen Length
+ \returns Reference to string
+*/
+xbString &xbString::Mid( xbUInt32 ulStartPos, xbUInt32 ulTargLen ){
+
+ // this is a 1 based routine
+ if( ulStartPos == 0 )
+ return *this;
+
+ if( data == NULL )
+ return( *this );
+ if( data[0] == 0 )
+ return( *this );
+ if( ulStartPos > Len() )
+ return( *this );
+/*
+ // Resize( ulTargLen + 1 );
+ char *pTarg = data;
+ char *pSrc = data + ulStartPos - 1;
+ for( xbUInt32 l = 0; l < ulTargLen; l++ )
+ *pTarg++ = *pSrc++;
+ *pTarg = 0x00;
+ // Resize( ulTargLen + 1 );
+ */
+
+ char * newData = (char *) calloc( 1, ulTargLen + 1 );
+ char *pTarg = newData;
+ char *pSrc = data + ulStartPos - 1;
+ for( xbUInt32 l = 0; l < ulTargLen; l++ )
+ *pTarg++ = *pSrc++;
+ *pTarg = 0x00;
+
+ free( data );
+ data = newData;
+ size = ulTargLen + 1;
+
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Left pad string
+/*!
+ \param c Padding character.
+ \param ulNewLen New string length.
+ \returns Reference to this string.
+*/
+xbString &xbString::PadLeft( char c, xbUInt32 ulNewLen ){
+
+ xbUInt32 srcLen;
+ if( data )
+ srcLen = (xbUInt32) strlen( data );
+ else
+ srcLen = 0;
+
+ if( srcLen >= ulNewLen )
+ return *this;
+
+ char * newData = (char *) calloc( 1, ulNewLen + 1 );
+ xbUInt32 i;
+ for( i = 0; i < ulNewLen - srcLen; i++ )
+ newData[i] = c;
+
+ char *targ = &newData[i];
+ xb_strcpy( targ, data );
+ free( data );
+ data = newData;
+ size = ulNewLen + 1;
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Right pad string
+/*!
+ \param c Padding character.
+ \param ulNewLen New string length.
+ \returns Reference to this string.
+*/
+xbString &xbString::PadRight( char c, xbUInt32 ulNewLen ){
+ xbUInt32 srcLen = (xbUInt32) strlen( data );
+ if( srcLen >= ulNewLen )
+ return *this;
+ data = (char *) realloc( data, ulNewLen + 1 );
+ xbUInt32 i;
+ for( i = srcLen; i < ulNewLen; i++ )
+ data[i] = c;
+ data[i] = 0x00;
+ size = ulNewLen + 1;
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Determine position of a given character
+/*!
+ \param c Seek character
+ \param ulStartPos starting position for search, first position is 1
+ \returns Position within string. Returns 0 if not found.
+*/
+xbUInt32 xbString::Pos(char c, xbUInt32 ulStartPos ) const {
+
+ if (data == NULL)
+ return 0;
+ char *p = data;
+
+ if( ulStartPos >= size )
+ return 0;
+
+ xbUInt32 iPos = 0;
+ while( (iPos+1) < ulStartPos ){
+ p++;
+ iPos++;
+ }
+ xbBool bFound = 0;
+ while( *p && !bFound && iPos < ( size - 1 )){
+ if( *p == c )
+ bFound = 1;
+ else {
+ iPos++;
+ p++;
+ }
+ }
+
+ if( bFound )
+ return iPos + 1;
+ else
+ return 0;
+}
+
+/************************************************************************/
+//! @brief Determine position of a given character
+/*!
+ \param c Seek character
+ \returns Position within string. Returns 0 if not found.
+*/
+xbUInt32 xbString::Pos(char c) const {
+
+ if (data == NULL)
+ return 0;
+ char *p = data;
+ xbUInt32 iPos = 0;
+ int iFound = 0;
+ while( *p && !iFound && iPos < ( size - 1 )){
+ if( *p == c )
+ iFound = 1;
+ else {
+ iPos++;
+ p++;
+ }
+ }
+ if( iFound )
+ return iPos + 1;
+ else
+ return 0;
+}
+
+/************************************************************************/
+//! @brief Determine position of a given substring
+/*!
+ \param s Substring
+ \returns Position within string. Returns 0 if not found.
+*/
+xbUInt32 xbString::Pos(const char* s) const{
+
+ if (data == NULL)
+ return 0;
+
+ char *p = strstr(data, s);
+ if ( p == NULL)
+ return 0;
+
+ return (xbUInt32)(p - data + 1);
+}
+
+/************************************************************************/
+//! @brief Insert character into string
+/*!
+ \param ulPos Insertion position.
+ \param c Character to insert.
+ \returns Reference to this string.
+*/
+xbString &xbString::PutAt(xbUInt32 ulPos, char c){
+ if((ulPos-1) > Len() )
+ return *this;
+ data[ulPos-1] = c;
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Remove portion of string.
+/*!
+ \param ulStartPos Starting position for removal operation.
+ \param ulDelSize Size of deletion.
+ \returns Reference to string.
+*/
+xbString &xbString::Remove(xbUInt32 ulStartPos, xbUInt32 ulDelSize ) {
+ if( data == NULL )
+ return( *this );
+ if( data[0] == 0 )
+ return( *this );
+ xbUInt32 srcLen = Len();
+
+ if( ulStartPos > srcLen || ulStartPos < 1 || ulDelSize < 1 )
+ return( *this );
+
+ if(( ulStartPos + ulDelSize - 1) >= size ){
+ data[ulStartPos-1] = 0x00;
+ size = ulStartPos;
+ return( *this );
+ }
+
+ char *t;
+ char *s;
+ t = data + (ulStartPos - 1);
+ s = t + ulDelSize;
+ size -= ulDelSize;
+ while( *s )
+ *t++ = *s++;
+ *t = 0x00;
+ return( *this );
+}
+
+/************************************************************************/
+//! @brief Replace a value within a string with another value
+/*!
+ \param sReplace - Character string to replace.
+ \param sReplaceWith - Character string to replace with
+ \param iOption - 0 = All occurrences, 1 = first occurrence
+ \returns Reference to this string.
+*/
+
+//the new size includes the null termination byte
+xbString &xbString::Replace( const char *sReplace, const char *sReplaceWith, xbInt16 iOption ){
+
+ xbBool bDone = xbFalse;
+ xbUInt32 ulPos;
+ xbUInt32 ulNewLen;
+ xbUInt32 ulReplaceWithLen;
+ xbUInt32 ulRsLen; // size of right side of string after replaced data
+ xbUInt32 ulSp2;
+ char *sBuf2;
+
+ const char *s; // source ptr
+ char *t; // target ptr
+
+ while( !bDone ){
+ ulPos = Pos( sReplace );
+ if( ulPos == 0 ){
+ bDone = xbTrue;
+ } else {
+
+ ulReplaceWithLen = (xbUInt32) strlen( sReplaceWith );
+ ulNewLen = this->size + ulReplaceWithLen - (xbUInt32) strlen( sReplace );
+ sBuf2 = (char *) calloc( 1, ulNewLen );
+
+ // copy part1
+ t = sBuf2;
+ s = data;
+ for( xbUInt32 ul = 0; ul < ulPos-1; ul++ )
+ *t++ = *s++;
+
+ // copy part2
+ s = sReplaceWith;
+ for( xbUInt32 ul = 0; ul < ulReplaceWithLen; ul++ )
+ *t++ = *s++;
+
+ // copy part3
+ ulSp2 = ulPos + (xbUInt32) strlen( sReplace );
+ s = data;
+ s+= (ulSp2 - 1);
+ ulRsLen = (xbUInt32) strlen( s );
+ for( xbUInt32 ul = 0; ul < ulRsLen; ul++ )
+ *t++ = *s++;
+
+ if( iOption )
+ bDone = xbTrue;
+
+ free(data);
+ data = sBuf2;
+ size = ulNewLen;
+ }
+ }
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Resize a string
+/*!
+ \param ulSize - New string size, including null termination byte.
+ \returns Reference to this string.
+*/
+//the new size includes the null termination byte
+xbString &xbString::Resize(xbUInt32 ulSize) {
+
+// data = (char *) realloc( data, ulSize );
+
+
+// original
+ data = (char *) realloc( data, ulSize );
+
+ if( ulSize > 0 )
+ data[ulSize-1] = 0;
+ this->size = ulSize;
+ return *this;
+}
+/************************************************************************/
+//! @brief Right trim the string.
+/*!
+ This routine removes any trailing white space on the string.
+
+ \returns Reference to string.
+*/
+xbString &xbString::Rtrim(){
+
+ xbUInt32 l = Len();
+ if( l == 0 )
+ return *this;
+
+ xbUInt32 ulOrigSize = size;
+ l--;
+
+ for(;;) {
+ if( data[l] != ' ' )
+ break;
+ data[l] = 0;
+ size--;
+ if( l == 0 )
+ break;
+ l--;
+ }
+
+ if( ulOrigSize != size )
+ data = (char * ) realloc( data, size );
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Set the value of the string.
+/*!
+
+ Note: This routine fails if you try to set the string to itself or some part of itself.
+
+ \param s Value to set the string.
+ \returns Reference to string.
+
+*/
+xbString &xbString::Set( const char *s ) {
+
+ if(data != NULL){
+ free(data);
+ data = NULL;
+ }
+ if( s == NULL || !*s ) {
+ if( data )
+ free( data );
+ data = NULL;
+ size = 0;
+ } else {
+ //data = (char *)calloc(1, strlen(s) + 1 );
+ data = (char *) realloc( data, strlen(s) + 1 );
+ xb_strcpy(data, s);
+ size = (xbUInt32) (strlen(data) + 1);
+ }
+ return (*this);
+}
+/************************************************************************/
+//! @brief Set the value of the string.
+/*!
+ \param s Value to set the string.
+ \returns Reference to string.
+*/
+xbString &xbString::Set( const xbString &s ) {
+
+// if( s.Str() == NULL || s.Len() == 0 ){
+
+ if( s.Str() == NULL ){
+ if( data ) free( data );
+ data = NULL;
+ size = 0;
+ } else {
+ xbUInt32 ulLen = s.Len();
+ char *p = (char *) calloc( 1, ulLen + 1 );
+ xb_strcpy( p, s.Str());
+ size = ulLen + 1;
+ if( data ) free( data );
+ data = p;
+ }
+ return (*this );
+}
+
+/************************************************************************/
+//! @brief Set the value of the string.
+/*!
+
+ Note: This routine fails if you try to set the string to itself or some part of itself.
+
+ \param s Value to set the string.
+ \param ulSize Maximum size of resultant string.
+ \returns Reference to string.
+*/
+
+xbString &xbString::Set(const char *s, xbUInt32 ulSize) {
+
+ if( data != NULL )
+ free( data );
+
+ if(s == NULL) {
+ data = NULL;
+ size = 0;
+ return (*this);
+ }
+
+ data = (char *) calloc( 1, ulSize+1 );
+ char *pTarget = data;
+ for( xbUInt32 i = 0; i < ulSize; i++ ){
+ *pTarget = *s;
+ pTarget++;
+ s++;
+ }
+ this->size = ulSize + 1;
+ return *this;
+}
+
+
+/************************************************************************/
+//! @brief Set the string to long integer numeric value.
+/*!
+ \param lNum Value to set the string
+ \returns Reference to this string.
+*/
+xbString &xbString::SetNum(xbInt32 lNum) {
+ Sprintf("%ld", lNum);
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Printf routine for formatting a string.
+/*!
+ See documentation on the standard C printf function for how to use this.
+
+ MyString.Sprintf( "a number %d some text %s", 100, "test text data" );
+
+ \param sFormat A format specifier
+ \returns Reference to a formatted string
+*/
+xbString &xbString::Sprintf( const char *sFormat, ...) {
+
+ xbInt32 iRc;
+ va_list ap;
+ char *t;
+
+#ifdef HAVE__VSNPRINTF_S_F
+
+ va_start( ap, sFormat );
+ size = (xbUInt32) _vsnprintf_s( NULL, 0, sFormat, ap ) + 1;
+ va_end( ap );
+
+ t = (char *) malloc( size );
+ if( !t ){
+ size = 0;
+ return( *this );
+ }
+
+ va_start( ap, sFormat );
+ iRc = _vsnprintf_s( t, size, sFormat, ap );
+ va_end( ap );
+
+#else
+#ifdef HAVE_VSPRINTF_S_F
+
+ va_start( ap, sFormat );
+ // size = (xbUInt32) vsprintf_s( NULL, 0, sFormat, ap ) + 1;
+ size = (xbUInt32) _vscprintf( sFormat, ap ) + 1;
+ va_end( ap );
+
+ t = (char *) malloc( size );
+ if( !t ){
+ size = 0;
+ return( *this );
+ }
+
+ va_start( ap, sFormat );
+ iRc = vsprintf_s( t, size, sFormat, ap );
+ va_end( ap );
+
+#else
+#ifdef HAVE_VSNPRINTF_F
+
+ va_start( ap, sFormat );
+ size = (xbUInt32) vsnprintf( NULL, 0, sFormat, ap) + 1;
+ va_end( ap );
+
+ t = (char *) calloc( 1, size );
+ if( !t ){
+ size = 0;
+ return( *this );
+ }
+ va_start( ap, sFormat );
+ iRc = vsnprintf( t, size, sFormat, ap );
+ va_end( ap );
+
+# else
+# error "Fatal error building [xbstring.cpp] - You have neither _vsnprintf_s nor vsnprintf_s."
+# endif
+#endif
+#endif
+
+ if( iRc < 0 ){
+ if( data )
+ free( data );
+ data = NULL;
+ size = 0;
+ } else {
+ if( data )
+ free( data );
+ data = t;
+ }
+ return( *this );
+}
+
+/************************************************************************/
+//! @brief Return string data
+/*!
+ \returns char * to string data or NULL if string is empty
+*/
+const char *xbString::Str() const {
+ return data ? data : NullString;
+}
+
+/************************************************************************/
+//! @brief Copy all or part of string to character array
+/*!
+ \param cDest pointer to destination buffer.
+ \param n Number of bytest to copy. It is the responsibility of the application
+ to verify the buffer is large enough to hold the string contents.
+ \returns char * to result
+
+*/
+char *xbString::strncpy( char * cDest, xbUInt32 n ) const {
+ xbUInt32 i;
+ xbUInt32 ulLen;
+ n > (size-1) ? ulLen = size-1 : ulLen = n;
+ memset( cDest, 0x00, ulLen );
+ for( i = 0; i < ulLen; i++ )
+ cDest[i] = data[i];
+// cDest[i] = 0x00;
+ return cDest;
+}
+/************************************************************************/
+//! @brief Swap characters
+/*!
+ \param cFrom character to replace.
+ \param cTo character to replace with.
+ \returns Reference to this string.
+*/
+xbString &xbString::SwapChars( char cFrom, char cTo ){
+ xbUInt32 i;
+ for( i = 0; i < size; i++ )
+ if( data[i] == cFrom )
+ data[i] = cTo;
+ return *this;
+}
+
+
+/************************************************************************/
+//! @brief Replace all upper case charaters with lower case characters
+/*!
+ \returns Reference to this string.
+*/
+xbString &xbString::ToLowerCase(){
+ xbUInt32 Len = this->Len();
+ for (xbUInt32 i=0; i<Len; i++)
+ data[i] = (char)tolower(data[i]);
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Replace all lower case charaters with lower case characters
+/*!
+ \returns Reference to this string.
+*/
+xbString &xbString::ToUpperCase(){
+ xbUInt32 Len = this->Len();
+ for (xbUInt32 i=0;i<Len;i++)
+ data[i] = (char)toupper(data[i]);
+ return *this;
+}
+/************************************************************************/
+//! @brief Trim all leading and trailing white space from string.
+/*!
+ \returns Reference to string.
+*/
+xbString &xbString::Trim(){
+ Rtrim();
+ Ltrim();
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Private function used for reallocateing memory
+/*!
+ This function is designed to be a drop in replacement for the realloc
+ function call.
+*/
+/*
+char * xbString::xb_realloc( char * pIn, xbUInt32 iLen ){
+
+ if( iLen == 0 ){
+ if( pIn ){
+ free( pIn );
+ return NULL;
+ }
+ }
+
+ char *pNew = (char *) calloc( 1, (size_t) iLen );
+ if( !pNew ) return NULL;
+ char *s = pIn;
+ char *t = pNew;
+ xbUInt32 iCnt = 0;
+ while( *s && iCnt++ < iLen )
+ *t++ = *s++;
+ return pNew;
+}
+*/
+/************************************************************************/
+//! @brief Private function used for copying a string
+/*!
+ For performance reasons, this is an internal function that does no
+ memory checking and assumes a valid buffer area is available to be copied.
+
+ This function is marked as private because of the above reason and
+ is used by "stronger" calling functions.
+
+ \param sTarget Target destination of copied string
+ \param sSource Source string to copy
+ \returns Reference to string.
+*/
+
+char * xbString::xb_strcpy( char *sTarget, const char *sSource ){
+
+ char *temp = sTarget;
+ while( *sSource != '\0')
+ *sTarget++ = *sSource++;
+ *sTarget= '\0';
+ return temp;
+}
+
+/************************************************************************/
+//! @brief Check for valid logical field data
+/*!
+ Valid logical data is one 'T', 'F', 'N' or 'Y'.<br>
+
+ \returns xbTrue if the data is valid logical data.<br>
+ xbFalse if not valid logical data.
+*/
+
+xbBool xbString::ValidLogicalValue() const {
+ if( Len() == 1 )
+ if( data[0] == 'T' || data[0] == 'F' || data[0] == 'Y' || data[0] == 'N' || data[0] == '?' )
+ return xbTrue;
+ return xbFalse;
+}
+/************************************************************************/
+//! @brief This function returns true if the data is valid numeric data
+/*!
+ \returns xbTrue if valid numeric data.<br>
+ xbFalse if not valid numeric daata.
+*/
+
+xbBool xbString::ValidNumericValue() const {
+ const char *p;
+ p = data;
+ while( *p ){
+ if( *p != '+' && *p != '-' && *p != '.' && *p != '0' && *p != '1' &&
+ *p != '2' && *p != '3' && *p != '4' && *p != '5' && *p != '6' &&
+ *p != '7' && *p != '8' && *p != '9' )
+ return xbFalse;
+ else
+ p++;
+ }
+ return xbTrue;
+}
+
+
+/************************************************************************/
+//! @brief Remove every instance of a character from a string.
+/*!
+ \param c character to remove from string.
+ \returns Reference to this stirng.void
+*/
+
+xbString &xbString::ZapChar( char c ){
+
+ if( data == NULL )
+ return *this;
+ if( data[0] == 0 )
+ return *this;
+
+ char *s;
+ char *t;
+
+ s = data;
+ t = data;
+ while( *s ){
+ if( *s == c ){
+ s++;
+ size--;
+ } else {
+ *t++ = *s++;
+ }
+ }
+ *t = 0x00;
+
+ data = (char *) realloc( data, size );
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Remove leading character from a string.
+/*!
+ \param c character to remove from beginning of string.
+ \returns Reference to this string.
+*/
+xbString &xbString::ZapLeadingChar( char c ){
+ /* left truncate all of character c */
+ xbUInt32 iLen = 0;
+ char *p;
+ p = data;
+ while( *p && *p == c ){
+ iLen++;
+ p++;
+ }
+ if( iLen )
+ Ltrunc( iLen );
+ return *this;
+}
+
+/************************************************************************/
+//! @brief Remove trailing character from a string.
+/*!
+ \param c character to remove from ending of string.
+ \returns Reference to this string.
+*/
+xbString &xbString::ZapTrailingChar( char c ){
+
+ xbUInt32 l = Len();
+ if( l == 0 )
+ return *this;
+ xbUInt32 ulOrigSize = size;
+ l--;
+ for(;;) {
+ if( data[l] != c )
+ break;
+ data[l] = 0;
+ size--;
+ if( l == 0 )
+ break;
+ l--;
+ }
+ if( ulOrigSize != size )
+ data = (char *) realloc( data, size );
+ return *this;
+}
+
+} /* namespace */ \ No newline at end of file
diff --git a/src/core/xbtag.cpp b/src/core/xbtag.cpp
new file mode 100755
index 0000000..621d44b
--- /dev/null
+++ b/src/core/xbtag.cpp
@@ -0,0 +1,121 @@
+/* xbtag.cpp
+
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_INDEX_SUPPORT
+
+
+namespace xb{
+
+/************************************************************************/
+//! @brief Constructor
+/*!
+ \param pIx Pointer to index file instance.
+ \param vpTag Pointer to tag structure within file instance.
+ \param sType NDX or MDX
+ \param sTagName Name of tag.
+ \param sExpression Tag key definition.
+ \param sFilter MDX only - tag qualification expression.
+ \param bUnique xbTrue - Index is unique.<br>xbFalse - Index is not unique.
+ \param bSort MDX only<br>xbTrue - Descending.<br>xbFalse - Ascending.
+*/
+
+xbTag::xbTag( xbIx *pIx, void *vpTag, xbString &sType, xbString &sTagName, xbString &sExpression, xbString &sFilter,
+xbBool bUnique, xbBool bSort )
+{
+ this->pIx = pIx;
+ this->vpTag = vpTag;
+ this->sType = sType;
+ this->sTagName = sTagName;
+ this->sExpression = sExpression;
+ this->sFilter = sFilter;
+ this->bUnique = bUnique;
+ this->bSort = bSort;
+}
+
+
+/************************************************************************/
+//! @brief Get tag key expression.
+/*!
+ \returns Tag key expression.
+*/
+const xbString &xbTag::GetExpression() const {
+ return sExpression;
+}
+/************************************************************************/
+//! @brief Get tag filter expression.
+/*!
+ \returns Tag filter expression (mdx only).
+*/
+
+const xbString &xbTag::GetFilter() const {
+ return sFilter;
+}
+/************************************************************************/
+//! @brief Get index file pointer.
+/*!
+ \returns Pointer to index file instance.
+*/
+xbIx *xbTag::GetIx() const {
+ return pIx;
+}
+/************************************************************************/
+//! @brief Get tag ascending setting.
+/*!
+ \returns Tag sort setting - MDX only.<br>xbTrue - Descending.<br>xbFalse - Ascending.
+*/
+xbBool xbTag::GetSort() const {
+ return bSort;
+}
+/************************************************************************/
+//! @brief Get tag name.
+/*!
+ \returns Tag name.
+*/
+
+const xbString &xbTag::GetTagName() const {
+ return sTagName;
+}
+/************************************************************************/
+//! @brief Get tag type.
+/*!
+ \returns Tag type.
+*/
+const xbString &xbTag::GetType() const {
+ return sType;
+}
+
+/************************************************************************/
+//! @brief Get tag unique setting.
+/*!
+ \returns Tag unique setting.<br>xbTrue - Unique.<br>xbFalse - Not unique.
+*/
+
+xbBool xbTag::GetUnique() const {
+ return bUnique;
+}
+/************************************************************************/
+//! @brief Get tag pointer for tag within index file.
+/*!
+ \returns Pointer to tag within index file instance.
+*/
+void *xbTag::GetVpTag() const {
+ return vpTag;
+}
+/************************************************************************/
+} /* namespace */
+#endif /* XB_INDEX_SUPPORT */ \ No newline at end of file
diff --git a/src/core/xbtblmgr.cpp b/src/core/xbtblmgr.cpp
new file mode 100755
index 0000000..53b6dd9
--- /dev/null
+++ b/src/core/xbtblmgr.cpp
@@ -0,0 +1,312 @@
+/* xbtblmgr.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+//#ifdef HAVE_STRING_H
+//#include <string.h>
+//#endif
+
+namespace xb{
+
+/*************************************************************************/
+xbTblMgr::xbTblMgr(){
+ TblList = NULL;
+ iOpenTableCount = 0;
+}
+
+/*************************************************************************/
+xbTblMgr::~xbTblMgr(){
+ xbTblList *l;
+ if( TblList ){
+ while( TblList ){
+ l = TblList;
+ TblList = TblList->pNext;
+ delete l->psFqTblName;
+ delete l->psTblName;
+ delete l->psTblAlias;
+ free( l );
+ }
+ }
+}
+
+/*************************************************************************/
+xbInt16 xbTblMgr::AddTblToTblList( xbDbf *d, const xbString & sFqTblName ){
+ return AddTblToTblList( d, sFqTblName, "" );
+}
+
+/*************************************************************************/
+xbInt16 xbTblMgr::AddTblToTblList( xbDbf *d, const xbString & sFqTblName, const xbString & sTblAlias ) {
+
+ // Set the Fq (fully qualified name)
+ // Pull the table name from the FQ name
+ // Set the Alias to the table name if the alias name is not provided
+
+
+ xbTblList *i, *s, *t;
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbString sTblName;
+ xbString sAlias;
+ xbString sTemp;
+ xbString sFqTemp;
+ xbUInt32 iSlashPos;
+
+ // std::cout << "AddTblToTblList fq in = [" << sFqTblName.Str() << "] alias in =[" << sTblAlias.Str() << "]\n";
+
+ try{
+
+ if( sFqTblName.Len() == 0 ){
+ iErrorStop = 100;
+ iRc = XB_INVALID_TABLE_NAME;
+ throw iRc;
+ }
+
+ sTblName = sFqTblName;
+ sTblName.SwapChars( '\\', '/' );
+ iSlashPos = sTblName.GetLastPos( '/' );
+
+ // std::cout << "slashpos = " << iSlashPos << "\n";
+
+
+ if( iSlashPos > 0 ){
+ sTblName.Ltrunc( iSlashPos ); // remove the directory part from the table name
+ sFqTemp = sFqTblName;
+ } else{
+ sFqTemp.Sprintf( "%s%s", GetDataDirectory().Str(), sFqTblName.Str()); // add the dir part to the FQ name
+ }
+
+ xbUInt32 iDbfPos = sFqTemp.Pos( ".DBF" );
+ if( iDbfPos == 0 )
+ sFqTemp += ".DBF";
+ else
+ sTblName.Resize( sTblName.Len() - 3 );
+
+ if( sTblAlias.Len() == 0 )
+ sAlias = sTblName;
+ else
+ sAlias = sTblAlias;
+
+ //std::cout << "fq=[" << sFqTemp.Str() << "] tblname = [" << sTblName.Str() << "] alias = [" << sAlias.Str() << "]\n";
+
+ if((i = (xbTblList *) calloc(1, sizeof(xbTblList))) == NULL){
+ iErrorStop = 110;
+ iRc = XB_NO_MEMORY;
+ throw iRc;
+ }
+ i->psFqTblName = new xbString( sFqTemp );
+ i->psTblName = new xbString( sTblName );
+ i->psTblAlias = new xbString( sAlias );
+ i->pDbf = d;
+ i->pNext = NULL;
+
+ // insert new table into the list of open tables, sorted by table name
+ s = NULL;
+ t = TblList;
+
+ while(t && (strcmp( t->psTblAlias->Str(), sAlias.Str()) < 0 )){
+ s = t;
+ t = t->pNext;
+ }
+
+ if( t && (strcmp( t->psTblAlias->Str(), sAlias.Str()) == 0 )){
+ iErrorStop = 120;
+ delete i->psFqTblName;
+ delete i->psTblName;
+ delete i->psTblAlias;
+ free( i );
+ iRc = XB_DUP_TABLE_OR_ALIAS;
+ throw iRc;
+ }
+ i->pNext = t;
+ if (s == NULL)
+ TblList = i;
+ else
+ s->pNext = i;
+ }
+ catch (xbInt16 iRc ){
+ if( iErrorStop != 120 ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbTblMgr::AddTblToTblList() Exception Caught. Error Stop = [%d] iRc = [%d] Tbl Name = [%s] Alias = [%s]", iErrorStop, iRc, sTblName.Str(), sTblAlias.Str() );
+ std::cout << sMsg << std::endl;
+ }
+ }
+ if( iRc == XB_NO_ERROR )
+ iOpenTableCount++;
+ return iRc;
+}
+
+/*************************************************************************/
+xbInt16 xbTblMgr::DisplayTableList() const {
+ xbInt16 iTblCnt = 0;
+ xbTblList * l = TblList;
+ std::cout << "-- Open Table List --" << std::endl;
+ if( l == NULL )
+ std::cout << "Table list is empty" << std::endl;
+ else{
+ while( l ){
+ iTblCnt++;
+ std::cout << iTblCnt << " FqFileName=[" << l->psFqTblName->Str() << "] TableName=[" << l->psTblName->Str() << "] Alias=[" << l->psTblAlias->Str() << "]" << std::endl;
+ l = l->pNext;
+ }
+ }
+ return iTblCnt;
+}
+/*************************************************************************/
+/* Get pointer to named dbf.
+ Looks up an open DBF file by Name.
+
+ returns A pointer to the xbDbf class instance if found or NULL if not found.
+
+ // looks for a match as an alias first, if not found as an alias, looks at the name
+
+*/
+
+xbDbf *xbTblMgr::GetDbfPtr(const xbString& sTblAlias) const {
+
+
+ xbTblList *t;
+ t = TblList;
+ xbString s;
+ xbUInt32 ui = sTblAlias.Pos( ".DBF" );
+ if( ui > 0 )
+ s.Assign( sTblAlias.Str(), 1, ui - 1 );
+ else
+ s.Set( sTblAlias );
+
+ while( t ){
+ if( s == t->psTblAlias->Str()){
+ return t->pDbf;
+ }
+ t = t->pNext;
+ }
+
+ t = TblList;
+ while( t ){
+ // std::cout << "s = [" << s.Str() << "] tbl name = [" << t->psTblName->Str() << "]\n";
+ if( s == t->psTblName->Str()){
+ std::cout << "found\n";
+ return t->pDbf;
+ }
+ t = t->pNext;
+ }
+
+
+ t = TblList;
+ while( t ){
+ if( sTblAlias == t->psFqTblName->Str())
+ return t->pDbf;
+ t = t->pNext;
+ }
+ return NULL;
+}
+/*************************************************************************/
+/* Get pointer to named dbf.
+ Looks up an open DBF file by Name.
+
+ returns pointer to the xbDbf class instance if found or NULL if not found.
+*/
+
+xbDbf *xbTblMgr::GetDbfPtr(xbInt16 iItemNo ) const {
+
+ xbTblList *t;
+ t = TblList;
+ xbInt16 iCnt = 1;
+
+ if( iItemNo < 1 || iItemNo > iOpenTableCount )
+ return NULL;
+
+ while( t && iCnt < iItemNo ){
+ t = t->pNext;
+ iCnt++;
+ }
+ if( t )
+ return t->pDbf;
+ else
+ return NULL;
+}
+/*************************************************************************/
+xbInt16 xbTblMgr::GetOpenTableCount() const {
+ return iOpenTableCount;
+}
+/*************************************************************************/
+xbTblList * xbTblMgr::GetTblListEntry( xbDbf *pTbl ){
+
+ xbTblList * i = TblList;
+ while( i ){
+ if( i->pDbf == pTbl )
+ return i;
+ i = i->pNext;
+ }
+ return NULL;
+}
+/*************************************************************************/
+xbInt16 xbTblMgr::RemoveTblFromTblList( const xbString & sTblAlias ) {
+ xbTblList *i, *s;
+
+ i = TblList;
+ s = NULL;
+
+ while( i ){
+
+ if( strcmp( i->psTblAlias->Str(), sTblAlias.Str()) == 0 ) {
+ if(s)
+ s->pNext = i->pNext;
+ else
+ TblList = i->pNext;
+
+ delete i->psFqTblName;
+ delete i->psTblName;
+ delete i->psTblAlias;
+ free( i );
+ iOpenTableCount--;
+ return XB_NO_ERROR;
+ } else {
+ s = i;
+ i = i->pNext;
+ }
+ }
+ return XB_NOT_FOUND;
+}
+/*************************************************************************/
+xbInt16 xbTblMgr::RemoveTblFromTblList( xbDbf *pTbl ) {
+ xbTblList *i, *s;
+
+ i = TblList;
+ s = NULL;
+
+ while( i ){
+
+ if( i->pDbf == pTbl ) {
+ if(s)
+ s->pNext = i->pNext;
+ else
+ TblList = i->pNext;
+
+ delete i->psFqTblName;
+ delete i->psTblName;
+ delete i->psTblAlias;
+ free( i );
+ iOpenTableCount--;
+ return XB_NO_ERROR;
+ } else {
+ s = i;
+ i = i->pNext;
+ }
+ }
+ return XB_NOT_FOUND;
+}
+/*************************************************************************/
+} /* namespace */
diff --git a/src/core/xbuda.cpp b/src/core/xbuda.cpp
new file mode 100755
index 0000000..5db7aec
--- /dev/null
+++ b/src/core/xbuda.cpp
@@ -0,0 +1,78 @@
+/* xbuda.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+This module handles uda (user data area) methods
+
+*/
+
+#include "xbase.h"
+
+
+// might need to change thisto XB_EXPRESSION_SUPPORT
+#ifdef XB_EXPRESSION_SUPPORT
+
+
+namespace xb{
+
+/************************************************************************/
+xbUda::xbUda() {
+ llOrd. SetDupKeys( xbFalse );
+}
+/************************************************************************/
+xbUda::~xbUda() {}
+/************************************************************************/
+void xbUda::Clear() {
+ llOrd.Clear();
+}
+/************************************************************************/
+xbInt16 xbUda::GetTokenCnt() const {
+ return llOrd.GetNodeCnt();
+}
+/************************************************************************/
+
+xbInt16 xbUda::GetTokenForKey( const xbString &sKey, xbString &sToken ) {
+ return llOrd.GetDataForKey( sKey, sToken );
+}
+
+/************************************************************************/
+xbInt16 xbUda::AddTokenForKey( const xbString &sKey, const xbString &sToken ) {
+ return llOrd.InsertKey( sKey, sToken );
+}
+/************************************************************************/
+
+xbInt16 xbUda::UpdTokenForKey( const xbString &sKey, const xbString &sData ) {
+ return llOrd.UpdateForKey( sKey, sData );
+}
+
+/************************************************************************/
+xbInt16 xbUda::DelTokenForKey( const xbString &sKey ) {
+ return llOrd.RemoveKey( sKey ); //, sToken );
+}
+
+
+/************************************************************************/
+
+void xbUda::DumpUda() const{
+
+ xbLinkListNode<xbString> *lln = llOrd.GetHeadNode();
+
+ xbInt32 l = 0;
+ while( lln ){
+ std::cout << ++l << " Key=[" << lln->GetKey() << "] Data=[" << lln->GetData() << "]" << std::endl;
+ lln = lln->GetNextNode();
+ }
+}
+
+/************************************************************************/
+} /* namespace */
+#endif /* XB_EXPRESSION_SUPPORT */ \ No newline at end of file
diff --git a/src/core/xbxbase.cpp b/src/core/xbxbase.cpp
new file mode 100755
index 0000000..7267f98
--- /dev/null
+++ b/src/core/xbxbase.cpp
@@ -0,0 +1,803 @@
+/* xbxbase.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+namespace xb{
+
+/*************************************************************************/
+//! @brief Class Constructor.
+xbXBase::xbXBase() {
+ SetEndianType();
+
+ xbFile f( this );
+ f.SetHomeFolders();
+ xbDate d( (xbUInt16) 1); // initialize xbDate static variables
+ #ifdef XB_LOGGING_SUPPORT
+ xLog = new xbLog();
+ #endif
+}
+/*************************************************************************/
+//! @brief Class Deconstructor.
+xbXBase::~xbXBase(){
+ CloseAllTables();
+ #ifdef XB_LOGGING_SUPPORT
+ delete xLog;
+ #endif
+}
+/*************************************************************************/
+//! @brief Close all tables / files.
+/*!
+ This closes everything and deletes references to the associated xbDbf objects.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::CloseAllTables(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iOpenTableCnt = GetOpenTableCount();
+ try{
+ xbDbf *d;
+ for( xbInt16 i = 0; i < iOpenTableCnt; i++ ){
+ d = (xbDbf *) GetDbfPtr( 1 );
+ if( d ){
+ if(( iRc = d->Close()) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ delete d;
+ } else {
+ iRc = XB_INVALID_OBJECT;
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbxbase::CloseAllTables() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ WriteLogMessage( sMsg.Str() );
+ WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/************************************************************************/
+//! @brief Fully qualified file name from a directory, filename and extension.
+/*!
+ Given a directory, file name and file extension as inputs, create a fully qualified file name.
+
+ \param sDirIn Directory
+ \param sFileIn File Name
+ \param sExtIn File Extension
+ \param sFqnOut A fully qualifed unique file name as output
+ \returns XB_INVALIED_PARAMETER or XB_NO_ERROR
+*/
+xbInt16 xbXBase::CreateFqn( const xbString &sDirIn, const xbString &sNameIn, const xbString &sExtIn, xbString &sFqnOut ){
+
+ if( sNameIn == "" || sExtIn == "" )
+ return XB_INVALID_PARAMETER;
+
+ sFqnOut = sDirIn;
+ #ifdef WIN32
+ sFqnOut.SwapChars( '/', '\\' );
+ if( sFqnOut[sFqnOut.Len()] != '\\' )
+ sFqnOut += '\\';
+ #else
+ sFqnOut.SwapChars( '\\', '/' );
+ if( sFqnOut[sFqnOut.Len()] != '/' )
+ sFqnOut += '/';
+ #endif
+ sFqnOut += sNameIn;
+ if( sExtIn != "" ){
+ sFqnOut += '.';
+ sFqnOut += sExtIn;
+ }
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+//! @brief Parse commmand line options for a given parm request
+/*!
+ \param lArgc Value passed from main( argc, argv[] )
+ \param sArgv Valued passed from main
+ \param sOptRqst Option to search for in the arguments list
+ \param sParmOut String token immediately to the right of the the option request, if found
+ \returns 0 - paramater request not found<br> 1 - Parameter found
+*/
+
+xbInt16 xbXBase::GetCmdLineOpt( xbInt32 lArgc, char **sArgv, const char *sOptRqst, xbString &sParmOut ){
+ xbString sOpt( sOptRqst );
+ return GetCmdLineOpt( lArgc, sArgv, sOpt, sParmOut );
+}
+
+/*************************************************************************/
+//! @brief Parse commmand line options for a given parm request
+/*!
+ \param lArgc Value passed from main( argc, argv[] )
+ \param sArgv Valued passed from main
+ \param sOptRqst Option to search for in the arguments list
+ \param sParmOut String token immediately to the right of the the option request, if found
+ \returns 0 - paramater request not found<br> 1 - Parameter found
+*/
+
+xbInt16 xbXBase::GetCmdLineOpt( xbInt32 lArgc, char **sArgv, xbString &sOptRqst, xbString &sParmOut ){
+
+ xbInt16 iFound = 0;
+ sParmOut = "";
+ if( lArgc < 2 ) // first string is the program name
+ return iFound;
+
+ xbInt32 i = 1;
+ while( iFound == 0 && i < lArgc ){
+ if( sOptRqst == sArgv[i] ){
+ iFound = 1;
+ if( i < (lArgc-1))
+ sParmOut = sArgv[i+1];
+ }
+ i++;
+ }
+ return iFound;
+}
+
+/*************************************************************************/
+#ifdef XB_LOGGING_SUPPORT
+//! @brief Get fully qualified log file name.
+/*!
+ \returns Returns the fully qualified log file name.
+*/
+const xbString & xbXBase::GetLogFqFileName() const {
+ return xLog->GetFqFileName();
+}
+
+//! @brief Get the log file name.
+/*!
+ \returns Returns the log file name.
+*/
+/*
+const xbString & xbXBase::GetLogFileName() const {
+ return xLog->GetFileName();
+}
+*/
+
+//! @brief Get the log directory.
+/*!
+ \returns Returns the log directory.
+*/
+/*
+const xbString & xbXBase::GetLogDirectory() const {
+ return GetLogDirectory();
+}
+*/
+//! @brief Get the log directory.
+/*!
+ \returns xbTrue - Logging enabled.<br>xbFalse - Logging disables.
+*/
+xbBool xbXBase::GetLogStatus() const {
+ return xLog->LogGetStatus();
+}
+
+//! @brief Set the log file name.
+/*!
+ \param sLogFileName - Log File Name.
+ \return void
+*/
+/*
+void xbXBase::SetLogFileName( const xbString & sLogFileName ){
+
+ xLog->SetFileName( sLogFileName );
+}
+*/
+//! @brief Set the log directory.
+/*!
+ \param sLogDirectory - Log File Directory.
+ \return void
+*/
+/*
+void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){
+ xLog->SetDirectory( sLogDirectory );
+}
+*/
+
+
+//! @brief Set the logfile size.
+/*!
+ \param lSize - Log File Size.
+ \return void
+*/
+void xbXBase::SetLogSize( size_t lSize ) {
+ xLog->LogSetLogSize( lSize );
+}
+
+//! @brief Get the logfile size.
+/*!
+ \return log file size
+*/
+size_t xbXBase::GetLogSize() const {
+ return xLog->LogGetLogSize();
+}
+
+
+//! @brief Write message to logfile.
+/*!
+ \param sLogMessage - Message to write.
+ \param iOpt 0 = stdout<br>
+ 1 = Syslog<br>
+ 2 = Both<br>
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbXBase::WriteLogMessage( const xbString & sLogMessage, xbInt16 iOpt ){
+ return xLog->LogWrite( sLogMessage, iOpt );
+}
+
+//! @brief Write message to logfile.
+/*!
+ \param lCnt - Number of bytes to write.
+ \param p - Pointer to bytes to write to log file.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+xbInt16 xbXBase::WriteLogBytes( xbUInt32 lCnt, const char *p ){
+ return xLog->LogWriteBytes( lCnt, p );
+}
+
+//! @brief Enable message logging.
+void xbXBase::EnableMsgLogging() {
+ xLog->LogSetStatus( xbTrue );
+}
+
+//! @brief Disable message logging.
+void xbXBase::DisableMsgLogging() {
+ xLog->LogSetStatus( xbFalse );
+}
+
+//! @brief Flush log file updates to disk.
+xbInt16 xbXBase::FlushLog() {
+ return xLog->xbFflush();
+}
+#else
+
+// if logging not compiled in, these stubs are called with no effect
+const xbString & xbXBase::GetLogFqFileName() const {
+ return sNullString;
+}
+/*
+const xbString & xbXBase::GetLogFileName() const {
+ return sNullString;
+}
+const xbString & xbXBase::GetLogDirectory() const {
+ return sNullString;
+}
+*/
+/*
+void xbXBase::SetLogFileName( const xbString & sLogFileName ){
+ return;
+}
+void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){
+ return;
+}
+*/
+xbBool xbXBase::GetLogStatus() const {
+ return xbFalse;
+}
+xbInt16 xbXBase::WriteLogMessage( const xbString & sLogMessage, xbInt16 ){
+ return XB_NO_ERROR;
+}
+xbInt16 xbXBase::WriteLogBytes( xbUInt32 lCnt, const char *p ){
+ return XB_NO_ERROR;
+}
+void xbXBase::EnableMsgLogging() {
+ return;
+}
+void xbXBase::DisableMsgLogging() {
+ return;
+}
+xbInt16 xbXBase::FlushLog() {
+ return XB_NO_ERROR;
+}
+void xbXBase::SetLogSize( size_t lSize ) {
+ return;
+}
+
+#endif // XB_LOGGING_SUPPORT
+
+/*************************************************************************/
+#ifdef XB_FUNCTION_SUPPORT
+
+//! @brief Get information regarding expression functions.
+/*!
+ \param sExpLine An expression beginning with function name.
+ \param cReturnType Output - return type of function.
+ \param iCalc Used to calculate the function return value is<br>
+ 1 = use value specified in lReturnLenVal<br>
+ 2 = use length of operand specified in col 4<br>
+ 3 = use valued of numeric operand specified in col 4<br>
+ 4 = length of parm 1 * numeric value parm<br>
+ 5 = larger length of parm 2 or length of parm 3<br>
+ 6 = if two or more parms, use numeric value from second parm, otherwise use col4 value
+ \param lReturnLenVal Used in combination with iReturnLenCalc.
+ \returns <a href="xbretcod_8h.html">Return Codes</a>
+*/
+
+xbInt16 xbXBase::GetFunctionInfo( const xbString &sExpLine, char &cReturnType, xbInt16 &iCalc, xbInt32 &lReturnLenVal ) const{
+
+ xbUInt32 iLen;
+ const char *s;
+ if( sExpLine.Len() == 0 )
+ return XB_INVALID_FUNCTION;
+
+ s = sExpLine;
+ iLen = 0;
+ while( *s && *s != '(' && *s != ' ' ) { s++; iLen++; }
+ xbString sFunction( sExpLine, iLen );
+ cReturnType = 0x00;
+ char cFunc1 = sFunction[1];
+
+ if( cFunc1 < 'L' ){
+ // std::cout << "less than L\n";
+ if( cFunc1 < 'D' ){
+ // std::cout << "less than D\n";
+ if( sFunction == "ABS" ){
+ // { "ABS", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "ALLTRIM" ){
+ // { "ALLTRIM", 'C', 2, 1 },
+ cReturnType = 'C';
+ iCalc = 2;
+ lReturnLenVal = 1;
+ } else if( sFunction == "ASC" ){
+ // { "ASC", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "AT" ){
+ // { "AT", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "CDOW" ){
+ // { "CDOW", 'C', 1, 9 },
+ cReturnType = 'C';
+ iCalc = 1;
+ lReturnLenVal = 9;
+ } else if( sFunction == "CHR" ){
+ // { "CHR", 'C', 1, 1 },
+ cReturnType = 'C';
+ iCalc = 1;
+ lReturnLenVal = 1;
+ } else if( sFunction == "CMONTH" ){
+ // { "CMONTH", 'C', 1, 9 },
+ cReturnType = 'C';
+ iCalc = 1;
+ lReturnLenVal = 9;
+ } else if( sFunction == "CTOD" ){
+ // { "CTOD", 'D', 1, 8 },
+ cReturnType = 'D';
+ iCalc = 1;
+ lReturnLenVal = 8;
+ }
+ } else {
+ // std::cout << ">= D\n";
+ if( sFunction == "DATE" ){
+ // { "DATE", 'D', 1, 8 },
+ cReturnType = 'D';
+ iCalc = 1;
+ lReturnLenVal = 8;
+ } else if( sFunction == "DAY" ){
+ // { "DAY", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "DEL" ){
+ // { "DEL", 'C', 1, 1 },
+ cReturnType = 'C';
+ iCalc = 1;
+ lReturnLenVal = 1;
+ } else if( sFunction == "DELETED" ){
+ // { "DELETED", 'L', 1, 1 },
+ cReturnType = 'L';
+ iCalc = 1;
+ lReturnLenVal = 1;
+ } else if( sFunction == "DESCEND" ){
+ // { "DESCEND", '1', 2, 1 },
+ cReturnType = '1';
+ iCalc = 2;
+ lReturnLenVal = 1;
+ } else if( sFunction == "DOW" ){
+ // { "DOW", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "DTOC" ){
+ // { "DTOC", 'C', 1, 8 },
+ cReturnType = 'C';
+ iCalc = 1;
+ lReturnLenVal = 8;
+ } else if( sFunction == "DTOS" ){
+ // { "DTOS", 'C', 1, 8 },
+ cReturnType = 'C';
+ iCalc = 1;
+ lReturnLenVal = 8;
+ } else if( sFunction == "EXP" ){
+ // { "EXP", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "IIF" ){
+ // { "IIF", 'C', 5, 0 },
+ cReturnType = 'C';
+ iCalc = 5;
+ lReturnLenVal = 0;
+ } else if( sFunction == "INT" ){
+ // { "INT", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "ISALPHA" ){
+ // { "ISALPHA", 'L', 1, 1 },
+ cReturnType = 'L';
+ iCalc = 1;
+ lReturnLenVal = 1;
+ } else if( sFunction == "ISLOWER" ){
+ // { "ISLOWER", 'L', 1, 1 },
+ cReturnType = 'L';
+ iCalc = 1;
+ lReturnLenVal = 1;
+ } else if( sFunction == "ISUPPER" ){
+ // { "ISUPPER", 'L', 1, 1 },
+ cReturnType = 'L';
+ iCalc = 1;
+ lReturnLenVal = 1;
+ }
+ }
+ } else {
+ // std::cout << ">= L\n";
+ if( cFunc1 < 'R' ) {
+ // std::cout << " < R\n";
+ if( sFunction == "LEFT" ){
+ // { "LEFT", 'C', 3, 2 },
+ cReturnType = 'C';
+ iCalc = 3;
+ lReturnLenVal = 2;
+ } else if( sFunction == "LEN" ){
+ // { "LEN", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 3;
+ } else if( sFunction == "LOG" ){
+ // { "LOG", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "LOWER" ){
+ // { "LOWER", 'C', 2, 1 },
+ cReturnType = 'C';
+ iCalc = 2;
+ lReturnLenVal = 1;
+ } else if( sFunction == "LTRIM" ){
+ // { "LTRIM", 'C', 2, 1 },
+ cReturnType = 'C';
+ iCalc = 2;
+ lReturnLenVal = 1;
+ } else if( sFunction == "MAX" ){
+ // { "MAX", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "MIN" ){
+ // { "MIN", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "MONTH" ){
+ // { "MONTH", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ }
+ } else if( cFunc1 == 'R' ){
+ // std::cout << "==R\n";
+ if( sFunction == "RECNO" ){
+ // { "RECNO", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "RECCOUNT" ){
+ // { "RECCOUNT", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "REPLICATE" ){
+ // { "REPLICATE", 'C', 4, 0 },
+ cReturnType = 'C';
+ iCalc = 4;
+ lReturnLenVal = 0;
+ } else if( sFunction == "RIGHT" ){
+ // { "RIGHT", 'C', 3, 2 },
+ cReturnType = 'C';
+ iCalc = 3;
+ lReturnLenVal = 2;
+ } else if( sFunction == "RTRIM" ){
+ // { "RTRIM", 'C', 2, 1 },
+ cReturnType = 'C';
+ iCalc = 2;
+ lReturnLenVal = 1;
+ }
+ } else if( cFunc1 == 'S' ){
+ // std::cout << "==S\n";
+ if( sFunction == "SPACE" ){
+ // { "SPACE", 'C', 3, 1 },
+ cReturnType = 'C';
+ iCalc = 3;
+ lReturnLenVal = 1;
+ } else if( sFunction == "SQRT" ){
+ // { "SQRT", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ } else if( sFunction == "STOD" ){
+ // { "STOD", 'D', 1, 8 },
+ cReturnType = 'D';
+ iCalc = 1;
+ lReturnLenVal = 8;
+ } else if( sFunction == "STR" ){
+ // { "STR", 'C', 6, 10 },
+ cReturnType = 'C';
+ iCalc = 6;
+ lReturnLenVal = 10;
+ } else if( sFunction == "STRZERO" ){
+ // { "STRZERO", 'C', 3, 2 },
+ cReturnType = 'C';
+ iCalc = 3;
+ lReturnLenVal = 2;
+ } else if( sFunction == "SUBSTR" ){
+ // { "SUBSTR", 'C', 3, 3 },
+ cReturnType = 'C';
+ iCalc = 3;
+ lReturnLenVal = 3;
+ }
+ } else {
+ // std::cout << ">S\n";
+ if( sFunction == "TRIM" ){
+ // { "TRIM", 'C', 2, 1 },
+ cReturnType = 'C';
+ iCalc = 2;
+ lReturnLenVal = 1;
+ } else if( sFunction == "UPPER" ){
+ // { "UPPER", 'C', 2, 1 },
+ cReturnType = 'C';
+ iCalc = 2;
+ lReturnLenVal = 1;
+ } else if( sFunction == "VAL" ){
+ // { "VAL", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 3;
+ } else if( sFunction == "YEAR" ){
+ // { "YEAR", 'N', 1, 4 },
+ cReturnType = 'N';
+ iCalc = 1;
+ lReturnLenVal = 4;
+ }
+ }
+ }
+ if( cReturnType == 0x00 )
+ return XB_INVALID_FUNCTION;
+ else
+ return XB_NO_ERROR;
+}
+#endif
+/*************************************************************************/
+//! @brief Cross platform sleep function.
+/*!
+ \param lMillisecs Milliseconds to sleep.
+*/
+void xbXBase::xbSleep( xbInt32 lMillisecs ){
+ #ifdef WIN32
+ Sleep( (xbUInt32) lMillisecs );
+ #else
+ usleep( (xbInt64) lMillisecs * 1000 );
+ #endif
+
+}
+/***********************************************************************/
+//! @brief Cross memcmp function.
+/*!
+ \param s1 Left operand to compare.
+ \param s2 Right operand to compare.
+ \param n Number of bytes to compare.
+ \returns 1 s1 > s2<br>
+ 0 s1 == s2<br>
+ -1 s1 < s2
+*/
+xbInt16 xbXBase::xbMemcmp( const unsigned char *s1, const unsigned char *s2, size_t n ){
+ // The standard memcmp function was found not to behave the same across all platforms
+ for( size_t i = 0; i < n; i++ ){
+ if( s1[i] > s2[i] )
+ return 1;
+ else if( s1[i] < s2[i] )
+ return -1;
+ }
+ return 0;
+}
+
+/***********************************************************************/
+//! @brief Open highest qualified class available for dbf file.
+/*!
+ This routine opens the highest available version of the dbf file.
+ Defaults to XB_READ_WRITE and XB_MULTI_USER mode.
+ \returns param dbf - Output pointer to dbf file opened or null if error
+*/
+xbDbf* xbXBase::Open( const xbString &sTableName, xbInt16 &iRc ){
+ return Open( sTableName, "", XB_READ_WRITE, XB_MULTI_USER, 0, iRc );
+}
+/***********************************************************************/
+//! @brief Open highest qualified class available for dbf file.
+/*!
+ This routine can open various versions of the dbf file dependent on the iVersion field
+
+ \param sTableName - Table name to open.
+ \param sAlias - Optional alias name.
+ \param iOpenMode - XB_READ_WRITE or XB_READ
+ \param iShareMode - XB_SINGLE_USER or XB_MULTI_USER
+ \param iRequestVersion 0 - Highest available
+ 4 - Version four dbf
+ 3 - Version three dbf
+ \param iRc - Return code from open request
+ \returns param dbf - Output pointer to dbf file opened or null if error
+*/
+
+
+xbDbf* xbXBase::Open( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode,
+ xbInt16 iShareMode, xbInt16 iRequestVersion, xbInt16 &iRc ){
+
+ xbInt16 iErrorStop = 0;
+ xbDbf * pDbf = 0;
+ iRc = 0;
+ xbString sFqFileName;
+
+ try{
+
+ if( sTableName.Len() == 0 ){
+ iErrorStop = 100;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ xbFile *f = new xbFile(this);
+ f->SetFileName( sTableName );
+ if(( iRc = f->FileExists( f->GetFqFileName())) != xbTrue ){
+ iErrorStop = 110;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ unsigned char cFileTypeByte;
+ if(( iRc = f->GetXbaseFileTypeByte( f->GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ xbInt16 iTblVsn = f->DetermineXbaseTableVersion( cFileTypeByte );
+ f->xbFclose();
+ sFqFileName.Set( f->GetFqFileName() );
+ delete f;
+
+ if( iTblVsn == 4 && ( iRequestVersion == 0 || iRequestVersion == 4 )){
+ #ifdef XB_DBF4_SUPPORT
+ pDbf = new xbDbf4( this );
+ iRc = pDbf->Open( sFqFileName, sAlias, iOpenMode, iShareMode );
+ #else
+ // std::cout << "Dbase IV file support not build into library. See XB_DBF4_SUPPORT" << std::endl;
+ iErrorStop = 130;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ #endif
+ }
+ else if( iTblVsn == 3 && ( iRequestVersion == 0 || iRequestVersion == 3 )){
+
+ #ifdef XB_DBF3_SUPPORT
+ pDbf = new xbDbf3( this );
+ iRc = pDbf->Open( sFqFileName, sAlias, iOpenMode, iShareMode );
+ #else
+ //std::cout << "Dbase III file support not build into library. See XB_DBF3_SUPPORT" << std::endl;
+ iErrorStop = 140;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ #endif
+
+ } else {
+ iErrorStop = 150;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ }
+
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbxbase::Open() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ WriteLogMessage( sMsg.Str() );
+ WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return pDbf;
+}
+
+xbInt16 xbXBase::OpenHighestVersion( const xbString &sTableName, const xbString &sAlias,
+ xbDbf **dbf )
+{
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+ xbFile f(this);
+ if( sTableName.Len() == 0 ){
+ iErrorStop = 100;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ f.SetFileName( sTableName );
+ if(( iRc = f.FileExists( f.GetFqFileName() )) != xbTrue ){
+ iErrorStop = 110;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+
+ unsigned char cFileTypeByte;
+ if(( iRc = f.GetXbaseFileTypeByte( f.GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ return iRc;
+ }
+ if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 4 ){
+ #ifdef XB_DBF4_SUPPORT
+ xbDbf *pwDbf = new xbDbf4( this );
+ pwDbf->Open( f.GetFqFileName(), sAlias );
+ *dbf = pwDbf;
+ pwDbf = 0;
+ #else
+ // std::cout << "Dbase IV file support not build into library. See XB_DBF4_SUPPORT" << std::endl;
+ iErrorStop = 130;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ #endif
+
+ } else if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 3 ){
+ #ifdef XB_DBF3_SUPPORT
+ *dbf = new xbDbf3( this );
+ #else
+ //std::cout << "Dbase III file support not build into library. See XB_DBF3_SUPPORT" << std::endl;
+ iErrorStop = 140;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ #endif
+
+ } else {
+ iErrorStop = 150;
+ iRc = XB_FILE_TYPE_NOT_SUPPORTED;
+ throw iRc;
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbxbase::OpenHighestVersion() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ WriteLogMessage( sMsg.Str() );
+ WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+/***********************************************************************/
+} /* namespace */
diff --git a/src/examples/xb_ex_date.cpp b/src/examples/xb_ex_date.cpp
new file mode 100755
index 0000000..7b92dbe
--- /dev/null
+++ b/src/examples/xb_ex_date.cpp
@@ -0,0 +1,212 @@
+/* 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()
+{
+
+ xbXBase x; /* initial date static variables */
+
+ 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_expression.cpp b/src/examples/xb_ex_expression.cpp
new file mode 100755
index 0000000..f16c79f
--- /dev/null
+++ b/src/examples/xb_ex_expression.cpp
@@ -0,0 +1,231 @@
+/* xb_ex_expression.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 example program demonstrates expression usage
+
+*/
+
+
+#include <xbase.h>
+
+using namespace xb;
+
+
+
+ xbSchema MyV4Record[] =
+ {
+ { "CFLD1", XB_CHAR_FLD, 20, 0 },
+ { "CFLD2", XB_CHAR_FLD, 10, 0 },
+ { "NFLD1", XB_NUMERIC_FLD, 5, 0 },
+ { "DATE1", XB_DATE_FLD, 8, 0 },
+ { "",0,0,0 }
+ };
+
+//*********************************************************************************************************************************
+void PrintResult( xbString *sExpression, xbExp *exp );
+void PrintResult( xbString *sExpression, xbExp *exp ){
+
+ // Determine the expression return type
+ char cExpType = exp->GetReturnType();
+
+ // Process the expression results, dependent on return type
+ if( cExpType == XB_EXP_NUMERIC ){
+ xbDouble dResult;
+ exp->GetNumericResult( dResult );
+ std::cout << "Numeric result from expression [" << sExpression->Str() << "] is [" << dResult << "]" << std::endl;
+
+ } else if( cExpType == XB_EXP_DATE ){
+ xbDate dt;
+ exp->GetDateResult( dt );
+ std::cout << "Date result from expression [" << sExpression->Str() << "] is [" << dt.Str() << "]" << std::endl;
+
+ } else if( cExpType == XB_EXP_LOGICAL ){
+ xbBool bResult;
+ exp->GetBoolResult( bResult );
+ std::cout << "Bool result from expression [" << sExpression->Str() << "] is [" << (bResult ? " True" : "False") << "]" << std::endl;
+
+ } else if( cExpType == XB_EXP_CHAR ){
+ xbString sResult;
+ exp->GetStringResult( sResult );
+ std::cout << "Char result from expression [" << sExpression->Str() << "] is [" << sResult.Str() << "]" << std::endl;
+ }
+
+}
+
+//*********************************************************************************************************************************
+//int main( int ac, char ** av ){
+
+int main(){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbIx *pIx = NULL;
+ void *pTag = NULL;
+
+ xbXBase x;
+ xbDbf *MyFile = new xbDbf4( &x );
+
+ try{
+
+ if(( iRc = MyFile->CreateTable( "EXPEXAMPL.DBF", "TestMdxX2", MyV4Record, XB_OVERLAY, XB_MULTI_USER )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ /*
+ CreateTag( const xbString &sIxType, const xbString &sName, const xbString &sKey, const xbString &sFilter,
+ xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverLay, xbIx **xbIxOut, void **vpTagOut );
+ */
+
+ // the following index definition has two expressions
+ // 1) CFLD1+CFLD2 -- concat two char fields into an index key
+ // 2) .NOT. DELETED() -- don't include any deleted records in the index
+ if(( iRc = MyFile->CreateTag( "MDX", "TAG1", "CFLD1+CFLD2", ".NOT. DELETED()", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+ // add a record to the table
+ if(( iRc = MyFile->BlankRecord()) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = MyFile->PutField( "CFLD1", "Some text" )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ if(( iRc = MyFile->PutField( "CFLD2", "Other text" )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if(( iRc = MyFile->PutLongField( "NFLD1", 1000 )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ xbDate dt;
+ dt.Set( "19890209" );
+ if(( iRc = MyFile->PutDateField( "DATE1", dt )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+ if(( iRc = MyFile->AppendRecord()) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+
+ if(( iRc = MyFile->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+
+ // To use the XBase64 expression processing logic
+ // 1) Parse an expression with the xbExp::ParseExpression() method
+ // 2) Process the parsed expression with the xbExp::ProcessExpression() method
+ // 3) If needed, determine the expression return type with the xbExp::GetReturnType() method
+ // 4) Use the appriate methid to retrieve the expression value:
+ // xbExp::GetNumericResult()
+ // xbExp::GetDateResult()
+ // xbExp::GetLogicalResult()
+ // xbExp::GetStringResult()
+
+
+ // The expression only needs to be parsed once. The ProcessExpression() method can be used
+ // zero, one or many times after it is initially parsed.
+
+ // see docs/html/xbc5.html for expression documentation
+ // see example below
+
+
+ // Numeric expression example
+ xbString sExpression = "NFLD1 * (2 + RECNO())";
+ xbExp exp( &x );
+ // Parse the expression
+ if(( iRc = exp.ParseExpression( MyFile, sExpression )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ // Process the parsed expression
+ if(( iRc = exp.ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ return -1;
+ }
+ PrintResult( &sExpression, &exp );
+
+ // String expression example
+ sExpression = "CFLD1+CFLD2+'{'+DTOS(DATE1)+'}'";
+ xbExp exp2( &x );
+ if(( iRc = exp2.ParseExpression( MyFile, sExpression )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+
+ // Process the parsed expression
+ if(( iRc = exp2.ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ return -1;
+ }
+
+ PrintResult( &sExpression, &exp2 );
+
+ // Date example
+ sExpression = "DATE() + 6";
+ xbExp exp3( &x );
+ if(( iRc = exp3.ParseExpression( MyFile, sExpression )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+ // Process the parsed expression
+ if(( iRc = exp3.ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ return -1;
+ }
+ PrintResult( &sExpression, &exp3 );
+
+ // Logic example
+ sExpression = "NFLD1 = 5";
+ xbExp exp4( &x );
+ if(( iRc = exp4.ParseExpression( MyFile, sExpression )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw iRc;
+ }
+ // Process the parsed expression
+ if(( iRc = exp4.ProcessExpression()) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ return -1;
+ }
+ PrintResult( &sExpression, &exp4 );
+
+ // Cleanup
+ MyFile->DeleteTable();
+ delete MyFile;
+
+
+ } catch (xbInt16 iRc ){
+
+ std::cout << "Error in program xb_ex_expression at location " << iErrorStop << std::endl;
+ std::cout << x.GetErrorMessage( iRc ) << std::endl;
+
+ }
+
+
+
+
+ return iRc;
+}
diff --git a/src/examples/xb_ex_log.cpp b/src/examples/xb_ex_log.cpp
new file mode 100755
index 0000000..cf5320f
--- /dev/null
+++ b/src/examples/xb_ex_log.cpp
@@ -0,0 +1,77 @@
+/* xb_ex_log.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This program demostrates how to use logging
+
+
+#include "xbase.h"
+
+using namespace xb;
+
+int main( int argCnt, char **av )
+{
+
+ #ifdef XB_LOGGING_SUPPORT
+
+ xbXBase x;
+ xbString sMsg;
+
+
+ std::cout << "Default Logfile Name is: [" << x.GetLogFqFileName().Str()
+ << "] Rollover size = [" << x.GetLogSize()
+ << "]" << std::endl;
+
+ if( x.GetLogStatus() )
+ std::cout << "Logging is active" << std::endl;
+ else
+ std::cout << "Logging is inactive" << std::endl;
+
+ x.SetLogDirectory( PROJECT_LOG_DIR ); // use the library log directory
+ x.SetLogFileName ( "MySpecialLogFile.txt" ); // set to use a special name
+ x.SetLogSize ( x.GetLogSize() * 2 ); // double the log file size
+
+ // enable the logfile and write a message for the new settings to take effect
+ x.EnableMsgLogging();
+ sMsg.Sprintf( "Program [%s] initializing...", av[0] );
+ x.WriteLogMessage( sMsg );
+
+ std::cout << "New Logfile Name is: [" << x.GetLogFqFileName().Str()
+ << "] Rollover size = [" << x.GetLogSize()
+ << "]" << std::endl;
+
+ if( x.GetLogStatus() )
+ std::cout << "Logging is active" << std::endl;
+ else
+ std::cout << "Logging is inactive" << std::endl;
+
+ // write some messages to the logfile
+ for( int i = 0; i < 5; i++ ){
+ sMsg.Sprintf( "Test message [%d]", i );
+ x.WriteLogMessage( sMsg );
+ }
+
+ sMsg.Sprintf( "Program [%s] terminating..", av[0] );
+ x.WriteLogMessage( sMsg );
+
+ x.FlushLog(); // not really needed, but here for demonstration purposes
+
+ #endif // B_LOGGING_SUPPORT
+
+ 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 <xbase.h>
+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_ssv.cpp b/src/examples/xb_ex_ssv.cpp
new file mode 100755
index 0000000..61d72a2
--- /dev/null
+++ b/src/examples/xb_ex_ssv.cpp
@@ -0,0 +1,136 @@
+/* xb_ex_ssv.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2021,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+This program demonstrates using functionality of the xbSsv class (Shared system values)
+
+*/
+
+#include "xbase.h"
+
+using namespace xb;
+
+//int main( int ac, char ** av ){
+
+int main( int, char ** av ){
+
+ xbXBase x; // set up xbase for business
+ xbString sMsg; // a message string
+
+ sMsg.Sprintf( "Program [%s] initializing...", av[0] );
+ std::cout << sMsg.Str() << std::endl;
+
+
+ // example code to set up log file usage
+ #ifdef XB_LOGGING_SUPPORT
+ char cSeperator; // is this a unix (/) or windows (\) file system
+ xbString sLog; // general string for log file activities
+ sLog = x.GetLogFqFileName().Str(); // get the system default log file name
+ std::cout << "System default logfile is [" << sLog.Str() << "]" << std::endl;
+
+ cSeperator = sLog.GetPathSeparator(); // get the seperator from
+ std::cout << "Path seperator = [" << cSeperator << "]" << std::endl;
+
+ sLog.Sprintf( "..%c", cSeperator );
+ x.SetLogDirectory( sLog );
+ std::cout << "sLog = [" << sLog.Str() << "]\n";
+
+ sLog = x.GetLogFqFileName().Str(); // get the system default log file name
+ std::cout << "New logfile is [" << sLog.Str() << "]" << std::endl;
+
+ // turn on logging after file name set
+ x.EnableMsgLogging();
+ #endif // XB_LOGGING_SUPPORT
+
+ // const char *GetErrorMessage( xbInt16 ErrorCode ) const;
+ // void DisplayError( xbInt16 ErrorCode ) const;
+ std::cout << "DisplayError( -100 ) - ";
+ x.DisplayError( -100 );
+ // << "]" << std::endl;
+
+ // void SetDefaultDateFormat( const xbString &sDefaultDateFormat );
+ // xbString& GetDefaultDateFormat() const;
+ std::cout << "GetDefaultDateFormat() - " << x.GetDefaultDateFormat().Str() << std::endl;
+
+
+ // void SetDataDirectory ( const xbString &sDataDirectory );
+ // xbString& GetDataDirectory() const;
+ std::cout << "GetDataDirectory() - " << x.GetDataDirectory().Str() << std::endl;
+
+ // xbInt16 GetEndianType() const;
+ if( x.GetEndianType() == 'L' )
+ std::cout << "Little Endian Architecture." << std::endl;
+ else
+ std::cout << "Bid Endian Architecture." << std::endl;
+
+ //xbBool GetDefaultAutoCommit() const;
+ //void SetDefaultAutoCommit( xbBool bDefaultAutoCommit );
+ if( x.GetDefaultAutoCommit())
+ std::cout << "AutoCommit is on." << std::endl;
+ else
+ std::cout << "AutoCommit is off." << std::endl;
+
+ //xbBool GetMultiUser () const;
+ //void SetMultiUser ( xbBool bMultiUser );
+ if( x.GetMultiUser())
+ std::cout << "Multi user (locking) is enabled." << std::endl;
+ else
+ std::cout << "Multi user (locking) not enabled." << std::endl;
+
+ #if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT)
+ // xbInt16 GetUniqueKeyOpt () const;
+ // xbInt16 SetUniqueKeyOpt ( xbInt16 iUniqueKeyOpt );
+ // XB_HALT_ON_DUPKEY
+ // XB_EMULATE_DBASE
+ if( x.GetUniqueKeyOpt() == XB_HALT_ON_DUPKEY )
+ std::cout << "UniqueKey option - XB_HALT_ON_DUPKEY" << std::endl;
+ else if( x.GetUniqueKeyOpt() == XB_EMULATE_DBASE )
+ std::cout << "UniqueKey option - XB_EMULATE_DBASE" << std::endl;
+ #endif // (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT)
+
+ #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 ();
+
+ std::cout << "GetDefaultLockRetries() - " << x.GetDefaultLockRetries() << std::endl;
+ std::cout << "GetDefaultLockWait() - " << x.GetDefaultLockWait() << std::endl;
+ std::cout << "GetDefaultAutoLock() - " << x.GetDefaultAutoLock() << std::endl;
+ #endif // XB_LOCKING_SUPPORT
+
+ #ifdef XB_MDX_SUPPORT
+ // xbInt16 GetCreateMdxBlockSize() const;
+ // xbInt16 SetCreateMdxBlockSize( xbInt16 ulBlockSize );
+ std::cout << "GetCreateMdxBlockSize() - " << x.GetCreateMdxBlockSize() << std::endl;
+ #endif // XB_MDX_SUPPORT
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ // xbUInt32 GetDefaultBlockReadSize() const;
+ // void SetDefaultBlockReadSize( xbUInt32 ulDfltBlockReadSize );
+ std::cout << "GetDefaultBlockReadSize() - " << x.GetDefaultBlockReadSize() << std::endl;
+ #endif // XB_BLOCKREAD_SUPPORT
+
+ //xbBool BitSet ( unsigned char c, xbInt16 iBitNo ) const;
+ //void BitDump ( unsigned char c ) const;
+ //void BitDump ( char c ) const;
+ std::cout << "BitDump( 'A' ) - ";
+ x.BitDump( 'A' );
+
+ return 0;
+}
diff --git a/src/examples/xb_ex_string.cpp b/src/examples/xb_ex_string.cpp
new file mode 100755
index 0000000..0cd7671
--- /dev/null
+++ b/src/examples/xb_ex_string.cpp
@@ -0,0 +1,381 @@
+/* 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();
+ #ifdef XB_DEBUG_SUPPORT
+ s3.Dump( "LTrim test" );
+ #else
+ std::cout << s3.Str() << std::endl;
+ #endif
+
+ s3 = " abc ";
+ s3.Rtrim();
+ std::cout << "RTrim test - " << s3.Str() << std::endl;
+
+ s3.Trim();
+ std::cout << "Trim test - " << s3.Str() << std::endl;
+ 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;
+ std::cout << "Concatenation test 2 - " << s1.Str() << std::endl;
+
+ // Concatenation tests - III
+ s1 = "s1data ";
+ s2 = "s2data ";
+
+ s3 = s1 - s2;
+ std::cout << "Concatenation test 3a - " << s3.Str() << std::endl;
+
+ s3 = s1 + s2;
+ std::cout << "Concatenation test 3b - " << s3.Str() << std::endl;
+
+ s3 = s1 + " char * data ";
+ std::cout << "Concatenation test 3c - " << s3.Str() << std::endl;
+
+ s3 = s1 + 'Z';
+ std::cout << "Concatenation test 3d - " << s3.Str() << std::endl;
+
+ s3 = 'A';
+
+ std::cout << s3.Str() << std::endl;
+ s3 += s1;
+
+ std::cout << s3.Str() << std::endl;
+
+ // The following compiles and runs, but is not valid
+ // s3 = 'A' + s1;
+
+ 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";
+ std::cout << "s1 - " << s1.Str() << std::endl;
+ std::cout << "s2 - " << s2.Str() << 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]" << 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();
+ std::cout << "s1.Copy() results" << s1.Str() << std::endl;
+
+ 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 );
+ std::cout << "ExtractElement() " << s1.Str() << std::endl;
+
+ 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;
+
+ std::cout << "Remove( 3, 5 ) before " << s1.Str() << std::endl;
+ std::cout << s1.Str() << " s1.Remove( 3, 5 ) = [" << s1.Remove( 3, 5 ).Str() << "]" << std::endl;
+ std::cout << "Remove( 3, 5 ) after " << s1.Str() << std::endl;
+ s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ std::cout << "s1.Remove( 20, 10 ) = [" << s1.Remove( 20, 10 ).Str() << "]" << std::endl;
+
+ std::cout << "Remove( 20, 10 ) - " << s1.Str() << std::endl;
+
+
+ s1.Sprintf( "%d", 12345 );
+ std::cout << "Sprintf( %d, 12345 ) " << s1.Str() << std::endl;
+
+ s1.SetNum( (long) 123456 );
+ std::cout << "s1.SetNum( 123456 ) = " << s1.Str() << std::endl;
+
+ s1.Set( "Yet another way to set a string value" );
+ std::cout << "Set - " << s1.Str() << std::endl;
+
+ s1 = "ABCABCABZ";
+ std::cout << "SwapChars( 'A', '9' ) before - " << s1.Str() << std::endl;
+ s1.SwapChars( 'A', '9' );
+ std::cout << "SwapChars( 'A', '9' ) after - " << s1.Str() << std::endl;
+
+ s1.ToLowerCase();
+ std::cout << "ToLowerCase - " << s1.Str() << std::endl;
+
+ s1.ToUpperCase();
+ std::cout << "ToUpperCase - " << s1.Str() << std::endl;
+
+ s1.ZapChar( '9' );
+ std::cout << "ZapChar( '9' )" << s1.Str() << std::endl;
+
+ s1.ZapLeadingChar( 'B' );
+ std::cout << "ZapLeadingChar( 'B' )" << s1.Str() << std::endl;
+
+ s1.ZapTrailingChar( 'Z' );
+ std::cout << "ZapLeadingChar( 'Z' ) - " << s1.Str() << std::endl;
+
+ s1 = "123";
+ s1.PadLeft( '0', 9 );
+ std::cout << "s1.PadLeft('0', 9 ) - " << s1.Str() << std::endl;
+
+ s1 = "abc";
+ s1.PadRight( 'Z', 9 );
+ std::cout << "s1.PadRight('Z', 9 ) " << s1.Str() << std::endl;
+
+ 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..d6f7047
--- /dev/null
+++ b/src/examples/xb_ex_v3_create_dbf.cpp
@@ -0,0 +1,110 @@
+/* xb_ex_v3_create_dbf.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+ This example demonstrates the creation of a Version III file and and indices
+
+*/
+
+#include <xbase.h>
+
+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 );
+
+ std::cout << "Tag Count in MYINDEX3.NDX = " << pIx->GetTagCount() << "\n";
+ xbString sTagName;
+ sTagName = pIx->GetTagName( &pTag );
+ std::cout << "Tag Name in MYINDEX3.NDX = " << sTagName.Str() << "\n";
+
+
+ #endif
+
+ }
+
+ MyDbfFile->Close(); /* Close database and associated indexes */
+ delete MyDbfFile;
+
+ #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..eecba16
--- /dev/null
+++ b/src/examples/xb_ex_v3_upd_dbf.cpp
@@ -0,0 +1,326 @@
+/* xb_ex_v3_upd_dbf.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2021,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+ 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 <xbase.h>
+
+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 */
+
+ xbString sSearchKey; /* string for doing an index lookup */
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if(( iRc = MyTable->Open( "MyV3Table1.DBF" )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ 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 = 110;
+ throw iRc;
+ }
+
+
+/*
+ if(( iRc = MyTable->OpenIndex( "NDX", "MYINDEX2.NDX" )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->OpenIndex( "NDX", "MYINDEX3.NDX" )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ 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" );
+
+
+
+ // do an index lookup for (key does not exist in this example)
+ sSearchKey = "abc123";
+ if(( iRc = MyTable->Find( sSearchKey )) != XB_NOT_FOUND ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+ std::cout << "RC = " << iRc << "\n";
+
+
+
+ #ifdef XB_MEMO_SUPPORT
+ fld_MEMO1 = MyTable->GetFieldNo( "MEMO1" );
+ #endif
+
+
+
+ // Blank the record buffer
+ if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+
+ // put field examples - using field numbers
+ if(( iRc = MyTable->PutField( fld_LASTNAME, "JONES" )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+
+ if(( iRc = MyTable->PutField( fld_FIRSTNAME, "JERRY" )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+
+ if(( iRc = MyTable->PutField( fld_AMOUNT, "12.35" )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutField( fld_BIRTHDATE, "19880208" )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutLogicalField( fld_RETIRED, "Y" )) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutLongField( fld_ZIPCODE, 12345 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ sMemoData = "Memo data record 1";
+ if(( iRc = MyTable->UpdateMemoField( fld_MEMO1, sMemoData )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ }
+ #endif
+
+ // Append the first record
+ if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){
+ // here is where you would address any errors.
+ // in this program, we simply abort and continue
+ MyTable->Abort();
+ }
+
+ // 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 = 220;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutField( "LASTNAME", "EINSTIEN" )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutField( "FIRSTNAME", "ALBERT" )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutField( "AMOUNT", "987.55" )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutField( fld_BIRTHDATE, "19890209" )) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutLogicalField( "RETIRED?", "N" )) != XB_NO_ERROR ){
+ iErrorStop = 270;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutLongField( "ZIPCODE", 44256 )) != XB_NO_ERROR ){
+ iErrorStop = 280;
+ throw iRc;
+ }
+
+ #ifdef XB_MEMO_SUPPORT
+ sMemoData = "Memo data record 2";
+ if(( iRc = MyTable->UpdateMemoField( fld_MEMO1, sMemoData )) != XB_NO_ERROR ){
+ iErrorStop = 290;
+ throw iRc;
+ }
+ #endif
+
+ // Append the second record
+ if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){
+ // here is where you would address any errors.
+ // in this program, we simply abort and continue
+ MyTable->Abort();
+ }
+
+
+ if(( iRc = MyTable->GetRecord( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+ // get a field with a field number
+ xbString sFirstName;
+ if(( iRc = MyTable->GetField( fld_FIRSTNAME, sFirstName )) < 0 ){
+ iErrorStop = 310;
+ throw iRc;
+ }
+ std::cout << "First Name is [" << sFirstName.Str() << "]" << std::endl;
+
+ xbString sLastName;
+ if(( iRc = MyTable->GetField( "LASTNAME", sLastName )) < 0 ){
+ iErrorStop = 320;
+ throw iRc;
+ }
+ std::cout << "Last Name is [" << sLastName.Str() << "]" << std::endl;
+
+ xbInt16 iNoOfDecimals;
+ if(( iRc = MyTable->GetFieldDecimal( "AMOUNT", iNoOfDecimals )) != XB_NO_ERROR ){
+ iErrorStop = 330;
+ 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 = 340;
+ throw iRc;
+ }
+ std::cout << "Field #4 name is " << FieldName.Str() << std::endl;
+
+ xbString sRetired;
+ if(( iRc = MyTable->GetLogicalField( "RETIRED?", sRetired )) < 0 ){
+ iErrorStop = 350;
+ throw iRc;
+ }
+ std::cout << "Switch value = [" << sRetired.Str() << "]" << std::endl;
+
+ xbInt32 lZip;
+ if(( iRc = MyTable->GetLongField( "ZIPCODE", lZip )) < 0 ){
+ iErrorStop = 360;
+ 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 = 370;
+ throw iRc;
+ }
+
+ // Append another record (it will be blank)
+ if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){
+ // here is where you would address any errors.
+ // in this program, we simply abort and continue
+ MyTable->Abort();
+ };
+
+ // mark record 1 for deletion
+ if(( iRc = MyTable->GetRecord( 1 )) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 380;
+ throw iRc;
+ };
+
+ // save current record
+ if(( iRc = MyTable->PutRecord()) != XB_NO_ERROR ){
+ iErrorStop = 390;
+ throw iRc;
+ }
+ // pack the table with no options
+ if(( iRc = MyTable->Pack()) != XB_NO_ERROR ){
+ iErrorStop = 400;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->Commit()) != XB_NO_ERROR ){
+ // here is where you would address any errors.
+ // in this program, we simply abort and continue
+ MyTable->Abort();
+ }
+
+ /* Close database and associated indexes */
+ if(( iRc = MyTable->Close()) != XB_NO_ERROR ){
+ iErrorStop = 410;
+ 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;
+ }
+
+ delete MyTable;
+
+ #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..d6bfb2f
--- /dev/null
+++ b/src/examples/xb_ex_v4_create_dbf.cpp
@@ -0,0 +1,100 @@
+/* xb_ex_v4_create_dbf.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+ This example demonstrates the creation of a Version IV file and and indices
+
+ Creates three files in folder "PROJECT_DATA_DIR"
+ Address.DBF - Table with all the data
+ Address.DBT - Memo (variable lenght char field) data
+ Address.MDX - File with index data
+
+*/
+
+#include <xbase.h>
+
+using namespace xb;
+
+int main()
+{
+
+#ifdef XB_DBF4_SUPPORT
+
+ xbSchema MyAddressBookRecord[] =
+ {
+ { "LASTNAME", XB_CHAR_FLD, 20, 0 },
+ { "FIRSTNAME", XB_CHAR_FLD, 15, 0 },
+ { "COMPANY", XB_CHAR_FLD, 20, 0 },
+ { "ADDRESS", XB_CHAR_FLD, 35, 0 },
+ { "CITY", XB_CHAR_FLD, 30, 0 },
+ { "STATECD", XB_CHAR_FLD, 2, 0 },
+ { "ZIPCD", XB_CHAR_FLD, 10, 0 },
+
+ { "BIRTHDATE", XB_DATE_FLD, 8, 0 },
+
+ { "AMOUNT1", XB_NUMERIC_FLD, 9, 2 },
+ { "AMOUNT2", XB_FLOAT_FLD, 12, 2 },
+
+ { "FRIEND?", XB_LOGICAL_FLD, 1, 0 },
+ { "FAMILY?", XB_LOGICAL_FLD, 1, 0 },
+ { "BUSASSOC?", XB_LOGICAL_FLD, 1, 0 },
+
+ #ifdef XB_MEMO_SUPPORT
+ { "NOTES", 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;
+
+ #ifdef XB_MDX_SUPPORT
+ xbIx *pIx;
+ void *pTag;
+ #endif // XB_MDX_SUPPORT
+
+ MyDbfFile = new xbDbf4( &x );
+
+ if(( iRc = MyDbfFile->CreateTable( "Address.DBF", "Address", MyAddressBookRecord, 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", "LASTNAME+FIRSTNAME", ".NOT. DELETED()", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR )
+ x.DisplayError( iRc );
+ if(( iRc = MyDbfFile->CreateTag( "MDX", "BDDATE", "BIRTHDATE", ".NOT. DELETED()", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR )
+ x.DisplayError( iRc );
+ if(( iRc = MyDbfFile->CreateTag( "MDX", "COMPANY", "COMPANY+LASTNAME+FIRSTNAME", ".NOT. DELETED()", 0, 0, XB_OVERLAY, &pIx, &pTag )) != XB_NO_ERROR )
+ x.DisplayError( iRc );
+
+ #endif // XB_MDX_SUPPORT
+ }
+
+ MyDbfFile->Close(); /* Close database and associated indexes */
+ delete MyDbfFile;
+
+ #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..d128eba
--- /dev/null
+++ b/src/examples/xb_ex_v4_upd_dbf.cpp
@@ -0,0 +1,294 @@
+/* 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 IV table created by xb_ex_v4_create_dbf
+ and apply various updates to the table.
+
+*/
+
+#include <xbase.h>
+
+using namespace xb;
+
+int main()
+{
+
+#ifdef XB_DBF4_SUPPORT
+
+
+ /* 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 DBase V4 table */
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ if(( iRc = MyTable->Open( "Address.DBF" )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+ /* get the field numbers for all the fields in the table */
+
+ xbInt16 iFld_FIRSTNAME = MyTable->GetFieldNo( "FIRSTNAME" );
+ xbInt16 iFld_LASTNAME = MyTable->GetFieldNo( "LASTNAME" );
+ xbInt16 iFld_BIRTHDATE = MyTable->GetFieldNo( "BIRTHDATE" );
+ xbInt16 iFld_AMOUNT1 = MyTable->GetFieldNo( "AMOUNT1" );
+ xbInt16 iFld_FRIEND = MyTable->GetFieldNo( "FRIEND?" );
+ xbInt16 iFld_ZIPCD = MyTable->GetFieldNo( "ZIPCD" );
+ xbInt16 iFld_AMOUNT2 = MyTable->GetFieldNo( "AMOUNT2" );
+
+ #ifdef XB_MEMO_FIELDS
+ zbInt16 iFld_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 = 110;
+ throw iRc;
+ }
+
+ // put field examples - using field numbers
+ if(( iRc = MyTable->PutField( iFld_LASTNAME, "JONES" )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ // could also reference by field name (see below) but referencing by number
+ // is a little bit faster because it doesn't need to look up the number for the field name
+ // Alternative--> if(( iRc = MyTable->PutField( "LASTNAME", "JONES" )) != XB_NO_ERROR ){
+
+ if(( iRc = MyTable->PutField( iFld_FIRSTNAME, "JERRY" )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutField( iFld_AMOUNT1, "12.35" )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutLogicalField( iFld_FRIEND, "Y" )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutField( iFld_BIRTHDATE, "19880209" )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutLongField( iFld_ZIPCD, 12345 )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+
+ // Append the first record
+ if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+
+ // Commit the updates
+ if(( iRc = MyTable->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ // Blank the record buffer
+ if(( iRc = MyTable->BlankRecord()) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ 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 = 210;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutField( "FIRSTNAME", "ALBERT" )) != XB_NO_ERROR ){
+ iErrorStop = 220;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutDoubleField( "AMOUNT1", (xbDouble) 987.55 )) != XB_NO_ERROR ){
+ iErrorStop = 230;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutLogicalField( "FRIEND?", "N" )) != XB_NO_ERROR ){
+ iErrorStop = 240;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->PutLongField( "ZIPCD", 44256 )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw iRc;
+ }
+
+ xbFloat f = (xbFloat) 12345.67;
+ std::cout << f << std::endl;
+
+ if(( iRc = MyTable->PutFloatField( iFld_AMOUNT2, f )) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc;
+ }
+ xbDouble d = 76543.21;
+ if(( iRc = MyTable->PutDoubleField( iFld_AMOUNT1, d )) != XB_NO_ERROR ){
+ iErrorStop = 270;
+ throw iRc;
+ }
+
+ // Append the second record
+ if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){
+ iErrorStop = 280;
+ throw iRc;
+ }
+
+ // Commit the updates
+ if(( iRc = MyTable->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 290;
+ throw iRc;
+ }
+
+
+ // get a field with a field number
+ xbString FirstName;
+ if(( iRc = MyTable->GetField( iFld_FIRSTNAME, FirstName )) < 0 ){
+ iErrorStop = 300;
+ throw iRc;
+ }
+ std::cout << "First Name is [" << FirstName.Str() << "]" << std::endl;
+
+ xbString LastName;
+ if(( iRc = MyTable->GetField( "LASTNAME", LastName )) < 0 ){
+ iErrorStop = 310;
+ throw iRc;
+ }
+ std::cout << "Last Name is [" << LastName.Str() << "]" << std::endl;
+
+ xbInt16 iNoOfDecimals;
+ if(( iRc = MyTable->GetFieldDecimal( "AMOUNT2", iNoOfDecimals )) != XB_NO_ERROR ){
+ iErrorStop = 320;
+ 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 = 330;
+ throw iRc;
+ }
+ std::cout << "Field #4 name is " << FieldName.Str() << std::endl;
+
+ xbString sFriend;
+ if(( iRc = MyTable->GetLogicalField( "FRIEND?", sFriend )) < 0 ){
+ iErrorStop = 340;
+ throw iRc;
+ }
+ std::cout << "Switch value = [" << sFriend.Str() << "]" << std::endl;
+
+ xbInt32 lZip = 0;
+ if(( iRc = MyTable->GetLongField( "ZIPCODE", lZip )) < 0 ){
+ iErrorStop = 350;
+ throw iRc;
+ }
+ std::cout << "Long value = [" << lZip << "]" << std::endl;
+
+ if(( iRc = MyTable->GetFloatField( iFld_AMOUNT2, f )) < 0 ){
+ iErrorStop = 360;
+ throw iRc;
+ }
+ printf( "Field NUMFLD1 %8.2f\n", f );
+
+
+ if(( iRc = MyTable->GetDoubleField( iFld_AMOUNT1, d )) < 0 ){
+ iErrorStop = 370;
+ 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 = 380;
+ throw iRc;
+ }
+
+ // Append another record (it will be blank)
+ if(( iRc = MyTable->AppendRecord()) != XB_NO_ERROR ){
+ iErrorStop = 390;
+ throw iRc;
+ };
+
+ // mark current record for deletion
+ if(( iRc = MyTable->DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 400;
+ throw iRc;
+ };
+
+ // save current record
+ if(( iRc = MyTable->PutRecord()) != XB_NO_ERROR ){
+ iErrorStop = 410;
+ throw iRc;
+ }
+
+ if(( iRc = MyTable->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 420;
+ throw iRc;
+ }
+
+ // example code to loop through the table
+ xbUInt32 ulRecCnt;
+ if(( iRc = MyTable->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){
+ iErrorStop = 430;
+ throw iRc;
+ }
+
+ for( xbUInt32 ul = 1; ul <= ulRecCnt; ul++ ){
+ if(( iRc = MyTable->GetRecord( ul )) != XB_NO_ERROR ){
+ iErrorStop = 440;
+ throw iRc;
+ }
+ // do something with the record here
+ std::cout << "Tuple = " << MyTable->GetCurRecNo() << std::endl;
+
+ }
+
+ /* Close database and associated indexes */
+ if(( iRc = MyTable->Close()) != XB_NO_ERROR ){
+ iErrorStop = 450;
+ throw iRc;
+ }
+ delete MyTable;
+
+
+ }
+ 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..4ab8892
--- /dev/null
+++ b/src/include/xbase.h
@@ -0,0 +1,100 @@
+/* xbase.h
+
+
+Xbase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include <xbconfig.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <math.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+// #ifdef STDC_HEADERS
+#ifdef HAVE_STDARGS_H
+#include <stdargs.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_VARARGS_H
+#ifndef HAVE_STDARG_H
+#include <varargs.h>
+#endif
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#include <io.h>
+#endif
+
+#include <iomanip>
+
+#include <xbretcod.h>
+#include <xbtypes.h>
+#include <xbstring.h>
+#include <xbssv.h>
+#include <xbdate.h>
+#include <xbtblmgr.h>
+#include <xbxbase.h>
+#include <xblnknod.h>
+#include <xblnklst.h>
+#include <xblnklstord.h>
+#include <xbfile.h>
+#include <xblog.h>
+#include <xbmemo.h>
+#include <xbbcd.h>
+#include <xbuda.h>
+#include <xbexpnode.h>
+#include <xbexp.h>
+
+#include <xbtag.h>
+#include <xbblkread.h>
+#include <xbdbf.h> /* dbf base class */
+#include <xbindex.h> /* index base class */
+#include <xbfilter.h>
+#include <xbsql.h>
diff --git a/src/include/xbbcd.h b/src/include/xbbcd.h
new file mode 100755
index 0000000..8c931d3
--- /dev/null
+++ b/src/include/xbbcd.h
@@ -0,0 +1,93 @@
+/* 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 );
+
+ 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/xbblkread.h b/src/include/xbblkread.h
new file mode 100755
index 0000000..353c618
--- /dev/null
+++ b/src/include/xbblkread.h
@@ -0,0 +1,70 @@
+/* xbblkread.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_BLKREAD_H__
+#define __XB_BLKREAD_H__
+
+namespace xb{
+#ifdef XB_BLOCKREAD_SUPPORT
+
+
+
+//! @brief xbBlockRead class.
+/*!
+The xbBlockRead class can be used to read blocks of records at one time
+rather than one record at a time for perfomance improvements when reading a DBF file sequentially.<br>
+
+*/
+
+
+class XBDLLEXPORT xbBlockRead {
+
+ public:
+ xbBlockRead( xbDbf * d );
+ ~xbBlockRead();
+
+ #ifdef XB_DEBUG_SUPPORT
+ void DumpReadBlockInternals();
+ #endif
+
+ xbInt16 Init( xbUInt32 ulBlkSize = 0 );
+
+ xbUInt32 GetBlockFirstRecNo() const;
+ xbUInt32 GetBlockRecCnt() const;
+ xbUInt32 GetBlockSize() const;
+
+ xbInt16 GetRecord( xbUInt32 ulRecNo ); // retrieve a data record from a block to RecBuf
+ xbInt16 SetBlockSize( xbUInt32 ulBlkSize );
+
+
+ private:
+
+ xbInt16 GetBlockForRecNo( xbUInt32 ulRecNo ); // retrieve block from disk for a given record number
+
+ char *pBlock; // block pointer
+ xbUInt32 ulBlkSize; // block size in bytes
+ xbUInt32 ulFirstBlkRec; // first recod number in the block
+ xbUInt32 ulRecCnt; // number of records in block
+ xbUInt32 ulMaxRecs; // max number of records block can handle
+ xbBool bEof; // EOF flag
+ xbDbf *dbf; // reference to associated dbf file
+// xbXBase *xbase; // reference to main xbXBase structure
+ time_t tFmTime; // file modify time at time of block read
+
+};
+
+#endif // XB_BLOCKREAD
+} /* namespace */
+#endif /* __XB_BLOCKREAD_H__ */
diff --git a/src/include/xbconfig.h.in b/src/include/xbconfig.h.in
new file mode 100755
index 0000000..f084038
--- /dev/null
+++ b/src/include/xbconfig.h.in
@@ -0,0 +1,128 @@
+// 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_TEMP_DIR "@PROJECT_TEMP_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_PWD_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_GETENV_S_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_TDX_SUPPORT
+#cmakedefine XB_SQL_SUPPORT
+#cmakedefine XB_INF_SUPPORT
+#cmakedefine XB_FILTER_SUPPORT
+#cmakedefine XB_BLOCKREAD_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/xbcrix.cpp b/src/include/xbcrix.cpp
new file mode 100755
index 0000000..b89baaa
--- /dev/null
+++ b/src/include/xbcrix.cpp
@@ -0,0 +1,292 @@
+/* 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{
+
+
+/***********************************************************************/
+#ifdef XB_INDEX_SUPPORT
+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;
+ xbBool bTableLocked = xbFalse;
+
+ try{
+
+ // 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 = 100;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_NDX_SUPPORT
+ if( sIxType == "NDX" ){
+ xbFile f( xbase );
+ f.SetFileName( sIxName );
+ if( f.FileExists()){
+
+ iErrorStop = 110;
+ 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 = 120;
+ throw iRc;
+ }
+ }
+
+ if( dbf == NULL ){
+ iErrorStop = 130;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ sCmd.Ltrunc( ulPos );
+
+ //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] == ',' && iParenCtr == 0 ){
+ 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 + 6 );
+ sFilter.Trim();
+ }
+ }
+ // std::cout << "sCmd - FILTER = [" << sFilter << "]\n";
+
+ #ifdef XB_LOCKING_SUPPORT
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ } else {
+ bTableLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ xbIx *pIx;
+ void *vpTag;
+
+ if(( iRc = dbf->CreateTag( sIxType, sIxName, sKeyExpression, sFilter, bDesc, bUnique, xbFalse, &pIx, &vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ #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 = 160;
+ throw iRc;
+ }
+ }
+ }
+ }
+ #endif // XB_NDX_SUPPORT
+
+ if(( iRc = pIx->Reindex( &vpTag )) != XB_NO_ERROR ){
+
+// if(( iRc = dbf->Reindex( 2, &vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+
+
+ //std::cout << "Tag count = " << pIx->GetTagCount() << "\n";
+ // s = pIx->GetTagName( &vpTag );
+ // std::cout << "tagname = [" << s.Str() << "]\n";
+
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bTableLocked ){
+ if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ } else {
+ bTableLocked = xbFalse;
+ }
+ }
+ #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
+ if( bTableLocked && dbf )
+ dbf->LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_SQL_SUPPORT */
+
diff --git a/src/include/xbdate.h b/src/include/xbdate.h
new file mode 100755
index 0000000..e914d65
--- /dev/null
+++ b/src/include/xbdate.h
@@ -0,0 +1,125 @@
+/* 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:<br>
+
+1) Gregorian dates with a format of CCYYMMDD. This is how dates are stored in dbf files.<br>
+2) Julian dates calculated as the number of days since 1901-01-01 + 1721425.<br>
+
+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.<br>
+Leap centuries are years which are evenly divisible by 400.<br>
+
+From a programming perspective, Julian dates are useful for date
+arithmetic, determining the difference between two dates or calculating
+a future or past date.<br>
+
+To determine the difference between two dates, convert both dates to a
+Julian date and subtract one from the other.<br>
+
+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( xbUInt16 iInit ); // Constructor used to set the static variables, also defaults to sysdate
+ ~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 xbString &sFmtIn, xbString &sFmtOut );
+ const char *Str() const;
+ xbBool IsLeapYear( xbInt16 iYear ) const;
+ xbBool IsLeapYear() const;
+ xbBool IsNull() 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
+ // Null date is identified by sDate.Len() < 8
+
+ 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..fe24e72
--- /dev/null
+++ b/src/include/xbdbf.h
@@ -0,0 +1,568 @@
+/* xbdbf.h
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+
+#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.<br>
+
+This module handles methods for accessing and updating dbf files.
+
+<br>
+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.<br>
+See the following for examples on how to start on this:<br>
+xbDbf3 is a derived class from xbDbf and supports the original Dbase III+ file version.<br>
+xbDbf4 is a derived class from xbDbf and supports the original Dbase IV file version.<br>
+
+*/
+
+
+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 );
+ virtual xbInt16 DumpRecord ( xbUInt32 ulRecNo, xbInt16 iOutputDest = 0, xbInt16 iOutputFmt = 0 );
+ virtual xbInt16 GetAutoCommit () const;
+ virtual xbInt16 GetAutoCommit ( xbInt16 iOption ) const;
+ virtual xbBool GetBof ();
+ virtual xbUInt32 GetCurRecNo () const;
+ virtual xbInt16 GetDbfStatus () const;
+ virtual xbBool GetEof ();
+ virtual xbInt32 GetFieldCnt () const;
+ virtual xbInt16 GetFirstRecord ();
+ virtual xbInt16 GetFirstRecord ( xbInt16 iOption );
+ virtual xbUInt16 GetHeaderLen () const;
+ virtual xbInt16 GetLastRecord ();
+ virtual xbInt16 GetLastRecord ( xbInt16 iOption );
+
+ virtual xbInt16 GetNextRecord ();
+ virtual xbInt16 GetNextRecord ( xbInt16 iOption );
+ virtual xbInt16 GetNextRecord ( xbInt16 iOption, xbUInt32 ulStartRec );
+ virtual xbInt16 GetPrevRecord ();
+ virtual xbInt16 GetPrevRecord ( xbInt16 iOption );
+ virtual xbInt16 GetRecord ( xbUInt32 ulRecNo );
+
+ // virtual xbUInt32 GetRecordCount ();
+ 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 Rename ( const xbString sNewName ) = 0;
+
+ 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 &sFieldName, 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 );
+
+ virtual xbInt16 GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull ) const;
+ virtual xbInt16 GetNullSts( const xbString &sFieldName, xbBool &bIsNull ) const;
+ virtual xbInt16 GetNullSts( xbInt16 iFieldNo, xbBool &bIsNull, xbInt16 iRecBufSw ) const;
+
+
+
+ #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 &ulMemoFieldLen );
+ 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<xbUInt32> * GetFirstRecLock () const;
+
+ #ifdef XB_DEBUG_SUPPORT
+ void DumpTableLockStatus() const;
+ #endif // XB_DEBUG_SUPPORT
+ #endif // XB_LOCKING_SUPPORT
+
+
+ #ifdef XB_INDEX_SUPPORT
+ virtual xbInt16 CheckTagIntegrity( xbInt16 iTagOpt, xbInt16 iOutputOpt );
+ 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;
+ virtual xbInt32 GetPhysicalIxCnt () const;
+ xbLinkListNode<xbTag *> *GetTagList () const;
+
+ virtual xbInt16 OpenIndex( const xbString &sIxType, const xbString &sIndexName );
+ virtual xbInt16 Reindex( xbInt16 iTagOpt = 0, xbInt16 iErrorOpt = 1, xbIx **pIx = NULL, void **vpTag = NULL );
+ virtual xbInt16 SetCurTag( const xbString &sTagName );
+ virtual void SetCurTag( const xbString &sIxType, xbIx *pIx, void *vpTag );
+
+ #endif // XB_INDEX_SUPPORT
+
+ #ifdef XB_INF_SUPPORT
+ virtual xbInt16 AssociateIndex( const xbString &sType, const xbString &sName, xbInt16 iOption );
+ xbLinkListNode<xbString> *GetInfList() const;
+ #endif // XB_INF_SUPPORT
+
+ //#ifdef XB_MDX_SUPPORT
+ //virtual xbInt16 GetCreateMdxBlockSize() const;
+ //virtual xbInt16 SetCreateMdxBlockSize( xbInt16 ulBlockSize );
+ //#endif
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ xbInt16 DisableBlockReadProcessing();
+ xbInt16 EnableBlockReadProcessing();
+ xbBool GetBlockReadStatus() const;
+ #endif // XB_BLOCKREAD_SUPPORT
+
+ protected:
+ #ifdef XB_INDEX_SUPPORT
+ friend class xbIx;
+ friend class xbIxMdx;
+ friend class xbIxTdx;
+
+ xbInt16 AddIndex( xbIx *ix, const xbString &sFmt );
+ void ClearTagList();
+ xbInt16 RemoveIndex( xbIx * ix );
+ void UpdateSchemaIxFlag( xbInt16 iFldNo, unsigned char cVal );
+
+
+ virtual xbInt16 UpdateTagList ();
+ #endif // XB_INDEX_SUPPORT
+
+ virtual xbInt16 ReadHeader ( xbInt16 iFilePositionOption, xbInt16 iReadOption );
+
+
+ #ifdef XB_INF_SUPPORT
+ virtual xbInt16 GetInfFileName( xbString &sNdxIdxFileName );
+ #endif // XB_INF_SUPPORT
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ friend class xbBlockRead;
+ #endif // XB_BLOCKREAD_SUPPORT
+
+
+ #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_INF_SUPPORT
+ virtual xbInt16 DeleteInfData();
+ virtual xbInt16 LoadInfData();
+ virtual xbInt16 SaveInfData();
+ #endif // XB_INF_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 allocation
+
+
+/* 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<xbUInt32> lloRecLocks; // ordered link list of locked records
+ #endif
+
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *ixList; // pointer to a list of indices associated with the table
+ xbIx *pCurIx; // Pointer to current index class
+ void *vpCurIxTag; // Pointer to current tag
+ xbString sCurIxType; // Current index type
+ xbLinkList<xbTag *> llTags; // linked list of open tags
+ #endif // XB_INDEX_SUPPORT
+
+ #ifdef XB_INF_SUPPORT
+ xbLinkList<xbString> llInfData; // linked list of strings containing ndx file entries
+ #endif // XB_INF_SUPPORT
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ xbBlockRead *pRb;
+ xbBool bBlockReadEnabled; // if true, then block read mode is on
+ #endif
+
+
+
+};
+
+
+#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 );
+ virtual xbInt16 Rename ( const xbString sNewName );
+
+ #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 );
+ virtual xbInt16 Rename ( const xbString sNewName );
+
+ #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..4792b0e
--- /dev/null
+++ b/src/include/xbexp.h
@@ -0,0 +1,216 @@
+/* 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
+
+#define XB_NULL_DATE 21474835648
+
+
+#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.<br><br>
+
+<h3>Internal fuctioning</h3>
+The expression module works in two phases. Firstly, method
+<em>ParseExpression</em> 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.
+<br>
+Secondly, method <em>ProcessExpression</em> 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.<br>
+
+If an expression will be processed repeatedly, it is best to pre-parse the
+tree using <em>ParseExpression</em>, then for each new call to the expression,
+execute method <em>ProcessExpression</em> which processes the tree.<br><br>
+
+<h3>Expression Return Types</h3>
+Expressions will return a type of CHAR, NUMERIC, DATE or LOGICAL.<br>
+
+An expression return type can be determined with method <em>
+GetExpressionResultType</em> after parsing it.<br>
+
+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
+<em>enum { WorkBufMaxLen = 200 };</em> in file <em>exp.h</em>.<br><br>
+
+<h3>Expression Functions</h3>
+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.<br><br>
+
+<h3>Expression Components</h3>
+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 "-'".
+<br><br>
+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<xbString> &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..51efa9b
--- /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.<br>
+
+Expressions are parsed into nodes and a logical tree of nodes is created
+out of all the individual components within the expression.<br>
+
+This class is used for handling a specific node within a tree.<br>
+
+An application program will typically not need to be concerned with
+this class.<br>
+
+*/
+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<xbExpNode *> 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 building 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..e346a75
--- /dev/null
+++ b/src/include/xbfile.h
@@ -0,0 +1,198 @@
+/* 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.<br>
+This class handles:<br>
+1) Big and little endian reads/writes<br>
+2) 32 or 64 bit system calls, depending on OS<br>
+3) Various different c/c++ calls to open/close/read/write based on OS and compiler version<br>
+<br>
+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( xbXBase * x );
+ ~xbFile();
+
+ const xbString& GetDirectory() const;
+ const xbString& GetFileName() const;
+ const xbString& GetFqFileName() const;
+
+
+ xbInt16 CreateUniqueFileName( const xbString &sDirIn, const xbString &sExtIn, xbString &sFqnOut, xbInt16 iOption = 0 );
+
+ xbInt16 DetermineXbaseTableVersion( unsigned char cFileTypeByte ) const;
+ xbInt16 DetermineXbaseMemoVersion( unsigned char cFileTypeByte ) const;
+
+ 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 );
+
+ 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;
+
+ xbUInt32 GetBlockSize () 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 GetFileMtime ( time_t &mtime );
+ xbInt16 GetFileNamePart( xbString &sFileNamePartOut ) const;
+ xbInt16 GetFileNamePart( const xbString &sCompleteFileNameIn, xbString &sFileNamePartOut ) const;
+ xbInt16 GetFileSize ( xbUInt64 &ullFileSize );
+ xbInt16 GetFileType ( xbString &sFileType ) const;
+
+
+ xbInt16 GetOpenMode () const;
+ xbInt16 GetShareMode () 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 NameSuffixMissing( const xbString &sFileName, xbInt16 iOption ) const;
+
+ xbInt16 ReadBlock ( xbUInt32 ulBlockNo, size_t readSize, void *buf );
+ xbInt16 ReadBlock ( xbUInt32 ulBlockNo, xbUInt32 ulBlockSize, size_t readSize, void *buf );
+
+ xbInt16 SetBlockSize ( xbUInt32 ulBlockSize );
+ void SetDirectory ( const xbString &sDirectory);
+ void SetFileName ( const xbString &sFileName );
+ void SetFqFileName ( const xbString &sFqName );
+ xbInt16 SetHomeFolders();
+
+ xbInt16 WriteBlock ( xbUInt32 ulBlockNo, size_t writeSize, void *buf );
+
+ xbInt16 xbFclose ();
+ xbInt16 xbFeof ();
+ xbInt16 xbFflush ();
+ xbInt16 xbFgetc ( xbInt32 &c );
+ xbInt16 xbFgetc ( char &c );
+ xbInt16 xbFgets ( size_t lSize, xbString &sLine );
+
+
+ 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 xbFseek ( xbInt64 llOffset, xbInt32 whence );
+ size_t xbFtell ();
+ void xbFTurnOffFileBuffering();
+
+ 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 );
+ void xbRewind ();
+
+ xbInt16 xbFwrite ( const void *ptr, size_t lSize, size_t lNmemb );
+
+ xbInt16 xbTruncate ( xbInt64 llSize );
+
+
+ #ifdef XB_LOCKING_SUPPORT
+ xbInt16 xbLock ( xbInt16 iFunction, xbInt64 llOffset, size_t stLen );
+ xbInt16 GetLockRetryCount() const;
+ void SetLockRetryCount( xbInt16 iLockRetries );
+ #endif
+
+ #ifdef XB_DEBUG_SUPPORT
+ xbInt16 DumpBlockToDisk( xbUInt32 ulBlockNo, size_t lBlockSize );
+ xbInt16 DumpMemToDisk ( char *p, 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..635d117
--- /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( xbDbf *dbf );
+ ~xbFilter();
+ xbInt16 Set( xbString &sFilterExpression );
+ xbInt16 Set( const char *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
+ 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..959562c
--- /dev/null
+++ b/src/include/xbindex.h
@@ -0,0 +1,613 @@
+/* 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.
+
+
+<br>
+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.<br>
+See the following for examples on how to start on this:<br>
+xbIxNdx is a derived class and supports a single tag.<br>
+xbIxMdx is a derived class and supports multiple tags.<br>
+
+
+
+<br>
+How data fields are stored in index files:
+<table>
+<tr><th>Field Type<th>Stored in DBF as<th>Stored in NDX as<th>Stored in MDX as</tr>
+<tr><td>C<td>char<td>char<td>char
+<tr><td>F<td>text numbers<td>xbDouble<td>xbBcd
+<tr><td>N<td>text numbers<td>xbDouble<td>xbBcd
+<tr><td>D<td>text CCYYMMDD<td>xbDouble - julian<td>xbDouble - julian
+</table>
+
+
+<br>
+Pages Vs Blocks
+<br>
+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.
+<br>Default block sixe of NDX indices is one 512 byte page.
+<br>Default block size of MDX indices is two 512 byte pages or 1024 bytes.
+
+<br>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.
+
+<br>Page numbers are stored in the physical file, but block reads and writes
+are performed.
+
+<br>
+Duplicate Keys
+<br>
+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.
+<br>
+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.
+<br>
+<table>
+<tr><th>Option<th>Description</tr>
+<tr><td>XB_HALT_ON_DUPKEY</td><td>Return error XB_KEY_NOT_UNIQUE when attempting to append record with duplicate key</td></tr>
+<tr><td>XB_EMULATE_DBASE</td><td>Emulate DBase, allow duplicate records with the same key, only the first record is indexed</td></tr>
+</table>
+*/
+
+
+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 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 ) {};
+
+
+
+ virtual xbInt16 Reindex( void **vpTag ) = 0;
+
+
+ #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();
+ 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 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 void NodeFree( xbIxNode * ixNode );
+ virtual xbInt16 ReadHeadBlock( xbInt16 iOpt = 0 ) = 0;
+ // virtual xbInt16 Reindex( void **vpTag ) = 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
+ 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 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 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
+
+ xbInt16 Reindex( void **vpTag );
+
+
+ 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 );
+ 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 );
+ xbInt16 ReadHeadBlock(xbInt16 iOpt); // read the header node of the disk NDX file
+// xbInt16 Reindex( void **vpTag );
+ 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 );
+ 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
+ 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();
+ virtual xbInt16 CheckTagIntegrity( void *vpTag, xbInt16 iOpt );
+ virtual xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag );
+ virtual xbInt16 FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw );
+ virtual xbInt16 FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw );
+ virtual xbInt16 GetFirstKey( void *vpTag, xbInt16 lRetrieveSw );
+ virtual xbString &GetKeyExpression( const void *vpTag ) const;
+ virtual xbString &GetKeyFilter( const void *vpTag ) const;
+ virtual char GetKeyType( const void *vpTag ) const;
+ virtual xbInt16 GetLastKey( void *vpTag, xbInt16 lRetrieveSw );
+ virtual xbInt16 GetNextKey( void *vpTag, xbInt16 lRetrieveSw );
+ virtual xbInt16 GetPrevKey( void *vpTag, xbInt16 lRetrieveSw );
+ virtual xbBool GetReuseEmptyNodesSw() const;
+ virtual xbBool GetSortOrder( void *vpTag ) const;
+ virtual void *GetTag( xbInt16 iTagNo ) const;
+ virtual void *GetTag( xbString &sTagName ) const;
+ virtual xbInt16 GetTagCount() const;
+
+ virtual xbString &GetTagName( void *vpTag ) const;
+ virtual const char * GetTagName( void *vpTag, xbInt16 iOpt ) const;
+ virtual void GetTagName( void *vpTag, xbString &sTagName );
+
+ virtual xbInt16 GetUnique( void *vpTag ) const;
+ virtual xbInt16 SetCurTag( xbInt16 iTagNo );
+ virtual xbInt16 SetCurTag( xbString &sTagName );
+ void SetReuseEmptyNodesSw( xbBool bReuse );
+
+ void TestStub( char *s, void *vpTag );
+
+
+ virtual xbInt16 Reindex( void **vpTag );
+
+
+ protected:
+ friend class xbDbf;
+
+ xbInt16 AddKey( void *vpTag, xbUInt32 ulRecNo );
+ xbIxNode *AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt32 ulBlock2 );
+ void CalcBtreePointers();
+ char CalcTagKeyFmt( xbExp &exp );
+
+ xbInt16 CheckForDupKey( void *vpTag );
+ virtual xbInt16 Close();
+ xbInt16 CreateKey( void * vpTag, xbInt16 iOpt );
+ xbInt16 DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo );
+ xbInt16 DeleteKey( void *vpTag );
+
+ virtual xbInt16 DeleteTag( void *vpTag );
+
+ xbInt16 FindKeyForCurRec( void *vpTag );
+ xbInt16 GetKeySts( void *vpTag ) const;
+ xbInt16 GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 lRetrieveSw );
+ void *GetTagTblPtr() const;
+
+ xbInt16 HarvestTagNodes( xbMdxTag *mpTag, xbBool bRecycleRoot = xbFalse );
+ 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
+ //virtual xbInt16 Reindex( void **vpTag );
+ 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
+
+
+ xbMdxTag *mdxTagTbl;
+
+// 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
+
+
+ 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.
+
+
+ private:
+ xbInt16 AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight );
+ void AppendNodeChain( void *vpTag, xbIxNode *npNode );
+ xbUInt32 BlockToPage( xbUInt32 ulBlockNo );
+ 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 );
+ 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
+
+
+ };
+ #endif /* XB_MDX_SUPPORT */
+
+
+#ifdef XB_TDX_SUPPORT
+
+class XBDLLEXPORT xbIxTdx : public xbIxMdx {
+ public:
+ xbIxTdx( xbDbf * d );
+ ~xbIxTdx();
+
+ xbInt16 CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag );
+
+ protected:
+ friend class xbDbf;
+ xbInt16 Close();
+ xbInt16 DeleteTag( void *vpTag );
+
+ private:
+};
+
+#endif /* XB_TDX_SUPPORT */
+
+
+ } /* namespace xb */
+ #endif /* XB_INDEX_SUPPORT */
+#endif /* __XB_INDEX_H__ */
diff --git a/src/include/xbindex.h.nope b/src/include/xbindex.h.nope
new file mode 100755
index 0000000..b42f76e
--- /dev/null
+++ b/src/include/xbindex.h.nope
@@ -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.
+
+
+<br>
+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.<br>
+See the following for examples on how to start on this:<br>
+xbIxNdx is a derived class and supports a single tag.<br>
+xbIxMdx is a derived class and supports multiple tags.<br>
+
+
+
+<br>
+How data fields are stored in index files:
+<table>
+<tr><th>Field Type<th>Stored in DBF as<th>Stored in NDX as<th>Stored in MDX as</tr>
+<tr><td>C<td>char<td>char<td>char
+<tr><td>F<td>text numbers<td>xbDouble<td>xbBcd
+<tr><td>N<td>text numbers<td>xbDouble<td>xbBcd
+<tr><td>D<td>text CCYYMMDD<td>xbDouble - julian<td>xbDouble - julian
+</table>
+
+
+<br>
+Pages Vs Blocks
+<br>
+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.
+<br>Default block sixe of NDX indices is one 512 byte page.
+<br>Default block size of MDX indices is two 512 byte pages or 1024 bytes.
+
+<br>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.
+
+<br>Page numbers are stored in the physical file, but block reads and writes
+are performed.
+
+<br>
+Duplicate Keys
+<br>
+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.
+<br>
+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.
+<br>
+<table>
+<tr><th>Option<th>Description</tr>
+<tr><td>XB_HALT_ON_DUPKEY</td><td>Return error XB_KEY_NOT_UNIQUE when attempting to append record with duplicate key</td></tr>
+<tr><td>XB_EMULATE_DBASE</td><td>Emulate DBase, allow duplicate records with the same key, only the first record is indexed</td></tr>
+</table>
+*/
+
+
+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 xbNodeType>
+class XBDLLEXPORT xbLinkList {
+ public:
+ xbLinkList();
+ ~xbLinkList();
+
+ xbLinkListNode<xbNodeType> *GetHeadNode() const;
+ xbLinkListNode<xbNodeType> *GetEndNode() const;
+ xbLinkListNode<xbNodeType> *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<xbNodeType> *llStartPtr;
+ xbLinkListNode<xbNodeType> *llEndPtr;
+};
+
+template<class xbNodeType>
+xbLinkList<xbNodeType>::xbLinkList(){
+ ulNodeCnt = 0;
+ llStartPtr = NULL;
+ llEndPtr = NULL;
+}
+
+template<class xbNodeType>
+xbLinkList<xbNodeType>::~xbLinkList(){
+ Clear();
+}
+
+template<class xbNodeType>
+void xbLinkList<xbNodeType>::Clear(){
+ xbLinkListNode<xbNodeType> *cPtr = llStartPtr, *tPtr;
+ for( xbUInt32 i = 0; i < ulNodeCnt; i++ ){
+ tPtr = cPtr;
+ cPtr = cPtr->GetNextNode();
+ delete tPtr;
+ }
+ ulNodeCnt = 0;
+ llStartPtr = NULL;
+ llEndPtr = NULL;
+}
+
+
+template<class xbNodeType>
+xbLinkListNode<xbNodeType> *xbLinkList<xbNodeType>::GetHeadNode() const{
+ return llStartPtr;
+}
+
+template<class xbNodeType>
+xbLinkListNode<xbNodeType> *xbLinkList<xbNodeType>::GetEndNode() const{
+ return llEndPtr;
+}
+
+
+template<class xbNodeType>
+xbLinkListNode<xbNodeType> *xbLinkList<xbNodeType>::GetNodeForNo( xbUInt32 ulNo ) const{
+
+ xbLinkListNode<xbNodeType> *cPtr = llStartPtr;
+ xbUInt32 i;
+ for( i = 0; i < ulNo && i < ulNodeCnt; i++ )
+ cPtr = cPtr->GetNextNode();
+
+ if( i == ulNo )
+ return cPtr;
+ else
+ return 0;
+}
+
+
+
+template<class xbNodeType>
+xbUInt32 xbLinkList<xbNodeType>::GetNodeCnt() const{
+ return ulNodeCnt;
+}
+
+template<class xbNodeType>
+xbInt16 xbLinkList<xbNodeType>::InsertAtFront( const xbNodeType & ntKey ){
+
+ xbLinkListNode<xbNodeType> *p = new xbLinkListNode<xbNodeType>( 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<class xbNodeType>
+xbInt16 xbLinkList<xbNodeType>::InsertAtEnd( const xbNodeType & ntKey ){
+
+ xbLinkListNode<xbNodeType> *p = new xbLinkListNode<xbNodeType>( 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<class xbNodeType>
+xbInt16 xbLinkList<xbNodeType>::RemoveByVal( const xbNodeType & ntKey ){
+ // Remove the first instance of ntKey from the node chain
+ xbLinkListNode<xbNodeType> *currPtr = llStartPtr;
+ xbLinkListNode<xbNodeType> *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<class xbNodeType>
+xbInt16 xbLinkList<xbNodeType>::RemoveFromFront( xbNodeType & ntKey ){
+
+ if( ulNodeCnt <= 0 )
+ return XB_INVALID_NODELINK;
+ xbLinkListNode<xbNodeType> *p = llStartPtr;
+ llStartPtr = p->GetNextNode();
+ if( llStartPtr )
+ llStartPtr->SetPrevNode( NULL );
+ ntKey = p->GetKey();
+ delete p;
+ ulNodeCnt--;
+ return XB_NO_ERROR;
+}
+
+
+template<class xbNodeType>
+xbInt16 xbLinkList<xbNodeType>::RemoveFromEnd( xbNodeType & ntKey ){
+
+ if( ulNodeCnt <= 0 )
+ return XB_INVALID_NODELINK;
+ xbLinkListNode<xbNodeType> *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<class xbNodeType>
+xbInt16 xbLinkList<xbNodeType>::RemoveFromEnd(){
+
+ if( ulNodeCnt <= 0 )
+ return XB_INVALID_NODELINK;
+ xbLinkListNode<xbNodeType> *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<class xbNodeType>
+xbInt16 xbLinkList<xbNodeType>::SearchFor( const xbNodeType & ntKey ){
+
+ xbLinkListNode<xbNodeType> *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 xbNodeType>
+class XBDLLEXPORT xbLinkListOrd {
+ public:
+ xbLinkListOrd();
+ ~xbLinkListOrd();
+
+ void Clear();
+ xbLinkListNode<xbNodeType> *GetHeadNode() const;
+ xbLinkListNode<xbNodeType> *GetEndNode() const;
+ xbLinkListNode<xbNodeType> *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<xbNodeType> *llStartPtr;
+ xbLinkListNode<xbNodeType> *llEndPtr;
+
+};
+
+
+template<class xbNodeType>
+xbLinkListOrd<xbNodeType>::xbLinkListOrd(){
+ bAllowDupKeys = xbTrue; // default setting - allow duplicate keys
+ ulNodeCnt = 0;
+ llStartPtr = NULL;
+ llEndPtr = NULL;
+}
+
+template<class xbNodeType>
+xbLinkListOrd<xbNodeType>::~xbLinkListOrd(){
+ Clear();
+}
+
+template<class xbNodeType>
+void xbLinkListOrd<xbNodeType>::Clear(){
+ xbLinkListNode<xbNodeType> *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<class xbNodeType>
+xbLinkListNode<xbNodeType> * xbLinkListOrd<xbNodeType>::GetHeadNode() const{
+ return llStartPtr;
+}
+
+template<class xbNodeType>
+xbLinkListNode<xbNodeType> * xbLinkListOrd<xbNodeType>::GetEndNode() const{
+ return llEndPtr;
+}
+
+template<class xbNodeType>
+xbUInt32 xbLinkListOrd<xbNodeType>::GetNodeCnt() const{
+ return ulNodeCnt;
+}
+
+template<class xbNodeType>
+xbUInt32 xbLinkListOrd<xbNodeType>::GetNodeCnt( const xbString &sNodeKey ) const{
+
+ // won't work if nodekey is not a string
+ xbLinkListNode<xbNodeType> *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<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::InsertKey( const xbNodeType &ntKey ){
+ xbString s;
+ return InsertKey( ntKey, s );
+}
+
+
+
+
+template<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::InsertKey( const xbNodeType &ntKey, xbUInt32 ul ){
+
+ xbString s;
+ s.Sprintf( "%ld", ul );
+ return InsertKey( ntKey, s );
+}
+
+
+template<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::InsertKey( const xbNodeType &ntKey, const xbString &sData ){
+
+ xbLinkListNode<xbNodeType> *p = new xbLinkListNode<xbNodeType>( ntKey, sData );
+ if( p == 0 )
+ return XB_NO_MEMORY;
+
+ if( ulNodeCnt > 0 ){
+ xbLinkListNode<xbNodeType> *currPtr = llStartPtr;
+ xbLinkListNode<xbNodeType> *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<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::RemoveKey( const xbNodeType &ntKey ){
+ // Remove the first instance of ntKey from the node chain
+ xbLinkListNode<xbNodeType> *currPtr = llStartPtr;
+ xbLinkListNode<xbNodeType> *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<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::RemoveFromFront( xbNodeType &ntKey ){
+
+ if( ulNodeCnt <= 0 )
+ return XB_INVALID_NODELINK;
+ xbLinkListNode<xbNodeType> *p = llStartPtr;
+ llStartPtr = p->GetNextNode();
+ if( llStartPtr )
+ llStartPtr->SetPrevNode( NULL );
+ ntKey = p->GetKey();
+ delete p;
+ ulNodeCnt--;
+ return XB_NO_ERROR;
+}
+
+template<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::RemoveFromFront(){
+
+ if( ulNodeCnt <= 0 )
+ return XB_INVALID_NODELINK;
+ xbLinkListNode<xbNodeType> *p = llStartPtr;
+ llStartPtr = p->GetNextNode();
+ if( llStartPtr )
+ llStartPtr->SetPrevNode( NULL );
+
+ if( p->GetKey())
+ delete p->GetKey();
+
+ delete p;
+ ulNodeCnt--;
+
+ return XB_NO_ERROR;
+}
+
+
+template<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::RemoveFromEnd( xbNodeType &ntKey ){
+
+ if( ulNodeCnt <= 0 )
+ return XB_INVALID_NODELINK;
+ xbLinkListNode<xbNodeType> *p = llEndPtr;
+ llEndPtr = p->GetPrevNode();
+ llEndPtr->SetNextNode( NULL );
+ ntKey = p->GetKey();
+ delete p;
+ ulNodeCnt--;
+ return XB_NO_ERROR;
+}
+
+template<class xbNodeType>
+xbBool xbLinkListOrd<xbNodeType>::GetDupKeys(){
+ return bAllowDupKeys;
+}
+
+template<class xbNodeType>
+void xbLinkListOrd<xbNodeType>::SetDupKeys( xbBool bAllowDupKeys ){
+ this->bAllowDupKeys = bAllowDupKeys;
+}
+
+
+template<class xbNodeType>
+xbBool xbLinkListOrd<xbNodeType>::KeyExists( const xbNodeType &ntKey ) const {
+
+ xbLinkListNode<xbNodeType> *currPtr = llStartPtr;
+ while( currPtr && ntKey > currPtr->GetKey() ){
+ currPtr = currPtr->GetNextNode();
+ }
+ if( currPtr && ntKey == currPtr->GetKey()){
+ return xbTrue;
+ } else {
+ return xbFalse;
+ }
+}
+
+
+template<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::GetDataForKey( const xbNodeType &ntKey, xbString &sData ){
+
+ xbLinkListNode<xbNodeType> *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<class xbNodeType>
+xbInt16 xbLinkListOrd<xbNodeType>::UpdateForKey( const xbNodeType &ntKey, const xbString &sData ){
+
+ if( ulNodeCnt == 0 )
+ return InsertKey( ntKey, sData );
+ xbLinkListNode<xbNodeType> * currPtr = llStartPtr;
+ xbLinkListNode<xbNodeType> * prevPtr = NULL;
+ while( currPtr && ntKey > currPtr->GetKey() ) {
+ prevPtr = currPtr;
+ currPtr = currPtr->GetNextNode();
+ }
+
+ if( currPtr && ntKey == currPtr->GetKey() ) {
+ xbLinkListNode<xbNodeType> *p = new xbLinkListNode<xbNodeType>( 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 xbNodeType>
+class XBDLLEXPORT xbLinkListNode {
+ public:
+ xbLinkListNode( const xbNodeType & );
+ xbLinkListNode( const xbNodeType &, const xbString & );
+ xbNodeType GetKey() const;
+ xbString &GetData();
+ xbLinkListNode<xbNodeType> *GetNextNode() const;
+ xbLinkListNode<xbNodeType> *GetPrevNode() const;
+ void SetNextNode( xbLinkListNode<xbNodeType> *llNext );
+ void SetPrevNode( xbLinkListNode<xbNodeType> *llPrev );
+
+ private:
+ xbNodeType ntKey;
+ xbString sData;
+ xbLinkListNode *llNext;
+ xbLinkListNode *llPrev;
+};
+
+ template<class xbNodeType>
+ xbLinkListNode<xbNodeType>::xbLinkListNode( const xbNodeType &key ){
+ ntKey = key;
+ llNext = NULL;
+ llPrev = NULL;
+ }
+
+ template<class xbNodeType>
+ xbLinkListNode<xbNodeType>::xbLinkListNode( const xbNodeType &key, const xbString &s ){
+ ntKey = key;
+ sData = s;
+ llNext = NULL;
+ llPrev = NULL;
+ }
+
+ template<class xbNodeType>
+ xbNodeType xbLinkListNode<xbNodeType>::GetKey() const {
+ return ntKey;
+ }
+
+ template<class xbNodeType>
+ xbString &xbLinkListNode<xbNodeType>::GetData(){
+ return sData;
+ }
+
+ template<class xbNodeType>
+ xbLinkListNode<xbNodeType> *xbLinkListNode<xbNodeType>::GetNextNode() const {
+ return llNext;
+ }
+
+ template<class xbNodeType>
+ xbLinkListNode<xbNodeType> *xbLinkListNode<xbNodeType>::GetPrevNode() const {
+ return llPrev;
+ }
+
+ template<class xbNodeType>
+ void xbLinkListNode<xbNodeType>::SetNextNode( xbLinkListNode<xbNodeType> *lln ){
+ llNext = lln;
+ }
+
+ template<class xbNodeType>
+ void xbLinkListNode<xbNodeType>::SetPrevNode( xbLinkListNode<xbNodeType> *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..5c7d721
--- /dev/null
+++ b/src/include/xblog.h
@@ -0,0 +1,67 @@
+/* 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 );
+ size_t LogGetLogSize() const { return lLogSize; }
+
+ 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..ad9aac2
--- /dev/null
+++ b/src/include/xbmemo.h
@@ -0,0 +1,224 @@
+/* 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.<br>
+
+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.<br>
+
+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;
+ virtual xbInt16 CreateMemoFile () = 0;
+
+ virtual xbInt16 DumpMemoHeader () = 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;
+ #endif
+
+ #ifdef XB_LOCKING_SUPPORT
+ virtual xbInt16 LockMemo ( xbInt16 iLockFunction );
+ virtual xbBool GetMemoLocked () const;
+ #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.<br>
+
+*/
+
+
+class XBDLLEXPORT xbMemoDbt3 : public xbMemo {
+ public:
+ xbMemoDbt3( xbDbf *dbf, xbString const &sFileName );
+ ~xbMemoDbt3();
+ virtual xbInt16 Abort ();
+ virtual xbInt16 Commit ();
+ virtual xbInt16 CreateMemoFile ();
+ virtual xbInt16 DumpMemoHeader ();
+ 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();
+ #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.<br>
+*/
+
+class XBDLLEXPORT xbMemoDbt4 : public xbMemo {
+ public:
+ xbMemoDbt4( xbDbf *dbf, xbString const &sFileName );
+ ~xbMemoDbt4();
+ virtual xbInt16 Abort ();
+ virtual xbInt16 Commit ();
+ virtual xbInt16 CreateMemoFile ();
+ virtual xbInt16 DumpMemoHeader ();
+ 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 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<xbUInt32> llOldBlocks; // list of previously used memo blocks for field, used by Commit() / Abort()
+ xbLinkList<xbUInt32> 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..f2885b1
--- /dev/null
+++ b/src/include/xbretcod.h
@@ -0,0 +1,98 @@
+/* xbretcod.h
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#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_INVALID_PARAMETER -102 // general
+ #define XB_DUP_TABLE_OR_ALIAS -110 // table manager
+ #define XB_INVALID_NODELINK -120 // linklist
+ #define XB_KEY_NOT_UNIQUE -121 // linklist, index
+ #define XB_MEMCPY_ERROR -122 // memcpy failure
+
+ #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_BLOCKREAD_NOT_ENABLED -223 // file
+ #define XB_DIRECTORY_ERROR -224 // 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..07ffb70
--- /dev/null
+++ b/src/include/xbsql.h
@@ -0,0 +1,161 @@
+/* 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{
+
+
+struct XBDLLEXPORT xbSqlFld{
+ char cType; // F - Database field
+ // L - Literal
+ // E - Expression
+ xbInt16 iFldNo; // Field number if db field
+ xbExp * pExp; // If cType=E, pointer to parsed expression
+ xbString sFldAlias; // Alias name for query display
+ xbSqlFld * Next; // Next field in list
+};
+
+class xbStmt;
+
+
+struct XBDLLEXPORT xbTblJoin { // structure for defining joined tables in the query
+
+ xbString sTableName;
+ xbString sAlias;
+ xbString sJoinExp; // table join expression
+ xbDbf *pDbf; // pointer the the dbf structure for this table
+ xbTag *pTag; // pointer to relevant tag for this table, null for record order
+ xbExp *pLinkExp; // pointer to parsed linkage expression
+ char cJoinType; // M - Master
+ // L - Left or Full Left
+ // R - Right or Full Right
+ // I - Inner
+ // O - Outer or Full Outer Outer
+
+ xbTblJoin *pMaster; // pointer to master table, null if this is the primary master
+ xbLinkList<xbTblJoin *> llSubList; // linked list of subordinate tables
+// xbStmt *pSubQuery; // pointer to sub query
+ xbTblJoin *next; // next tbl
+};
+
+
+class XBDLLEXPORT xbStmt : public xbSsv{
+
+ public:
+ xbStmt( xbXBase *x );
+ ~xbStmt();
+
+ #ifdef XB_DEBUG_SUPPORT
+ xbInt16 DumpStmtInternals();
+ xbInt16 Test();
+ #endif
+
+ xbInt16 ExecuteQuery( const xbString &sCmdLine );
+
+// xbInt16 FetchFirst();
+// xbInt16 FetchNext();
+// xbInt16 FetchPrev();
+// xbInt16 FetchLast();
+// xbString &GetField( const xbString sFldName );
+
+ protected:
+
+ private:
+
+ xbInt16 AddQryTbl( const xbString &sTable, const xbString &sAlias, const xbString &sExpression, const char cJoinType );
+ xbInt16 CvtSqlExp2DbaseExp( const xbString &sExpIn, xbString &sExpOut );
+ xbInt16 GetNextFromSeg( const xbString &sLineIn, xbString &sFromSegOut );
+ xbUInt32 GetNextToken( const xbString &sCmdLineIn, xbString &sTokenOut, xbUInt32 ulStartPos );
+ xbInt16 GetParenCnt( const xbString &sToken );
+ xbInt16 ParseFromStmt( const xbString &sFromLine );
+ xbInt16 ParseStmt( const xbString &sCmdLine );
+ xbInt16 ProcessFromSeg( const xbString &sFromStmtSeg );
+ xbInt16 UpdateJoinList( char cType, const xbString &sTableName, const xbString &sAlias, const xbString &sExp, xbDbf *d, xbExp *e ); //, xbTag *t);
+
+ xbXBase *xbase; // pointer to xbase structure
+
+
+ xbTblJoin * pTblList;
+ xbSqlFld *fl; // field list in sql select statement
+ xbString sStmt; // complete query statment
+ xbString sFields; // list of fields
+ xbString sFrom; // from statment
+ xbString sWhere; // where clause
+ xbString sOrderBy; // sort order
+ xbString sGroupBy; // group by
+ xbString sHaving; // having
+ xbUInt32 ulFromPos;
+ xbUInt32 ulWherePos;
+ xbUInt32 ulOrderByPos;
+ xbUInt32 ulGroupByPos;
+ xbUInt32 ulHavingPos;
+
+
+};
+
+
+class XBDLLEXPORT xbSql : public xbSsv {
+ public:
+ // xbSql();
+ xbSql( xbXBase *x );
+ ~xbSql();
+
+ xbInt16 ExecuteNonQuery( const xbString &sCmdLine );
+ xbXBase *GetXbasePtr() const;
+
+ protected:
+
+ private:
+
+ xbInt16 SqlAlterTable( const xbString &sCmdLine );
+ xbInt16 SqlCreateTable( const xbString &sCmdLine );
+ xbInt16 SqlDelete( const xbString &sCmdLine );
+ xbInt16 SqlDropTable( const xbString &sCmdLine );
+
+ #ifdef XB_INDEX_SUPPORT
+ xbInt16 SqlCreateIndex( const xbString &sCmdLine );
+ xbInt16 SqlDropIndex( const xbString &sCmdLine );
+ #endif // XB_INDEX_SUPPORT
+
+// 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..3b87a13
--- /dev/null
+++ b/src/include/xbssv.h
@@ -0,0 +1,194 @@
+/* xbssv.h
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+
+#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;
+ xbString& GetTempDirectory () const;
+
+ void GetHomeDir ( xbString &sHomeDirOut );
+
+
+ xbInt16 GetEndianType () const;
+ const char *GetErrorMessage ( xbInt16 ErrorCode ) const;
+ char GetPathSeparator () const;
+
+ void SetDataDirectory ( const xbString &sDataDirectory );
+ void SetDefaultDateFormat ( const xbString &sDefaultDateFormat );
+ void SetTempDirectory ( const xbString &sTempDirectory );
+
+
+ xbBool BitSet ( unsigned char c, xbInt16 iBitNo ) const;
+ void BitDump ( unsigned char c ) const;
+ void BitDump ( char c ) const;
+
+ xbBool GetDefaultAutoCommit () const;
+ void SetDefaultAutoCommit ( xbBool bDefaultAutoCommit );
+
+ xbString& GetLogDirectory () const;
+ xbString& GetLogFileName () const;
+ void SetLogDirectory ( const xbString &sLogDirectory );
+ void SetLogFileName ( const xbString &sLogFileName );
+
+
+ xbBool GetMultiUser () const;
+ void SetMultiUser ( xbBool bMultiUser );
+
+ #if defined (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT)
+ xbInt16 GetUniqueKeyOpt () const;
+ xbInt16 SetUniqueKeyOpt ( xbInt16 iUniqueKeyOpt );
+ #endif // (XB_NDX_SUPPORT) || defined (XB_MDX_SUPPORT)
+
+ #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 // XB_LOCKING_SUPPORT
+
+ #ifdef XB_MDX_SUPPORT
+ xbInt16 GetCreateMdxBlockSize() const;
+ xbInt16 SetCreateMdxBlockSize( xbInt16 ulBlockSize );
+ #endif // XB_MDX_SUPPORT
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ xbUInt32 GetDefaultBlockReadSize() const;
+ void SetDefaultBlockReadSize( xbUInt32 ulDfltBlockReadSize );
+ #endif // XB_BLOCKREAD_SUPPORT
+
+
+ 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
+ static xbString sTempDirectory; //Temp file directory
+
+ #ifdef XB_LOGGING_SUPPORT
+ static xbString sLogDirectory; //Default location to store log files
+ static xbString sLogFileName; //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
+
+
+#ifdef XB_BLOCKREAD_SUPPORT
+ static xbUInt32 ulDefaultBlockReadSize;
+#endif // XB_BLOCKREAD_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..7fe1692
--- /dev/null
+++ b/src/include/xbstring.h
@@ -0,0 +1,188 @@
+/* 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 <stdlib.h>
+#include <iostream>
+
+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.<br><br>
+
+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.<br><br>
+
+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.<br><br>
+
+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 ulStartPos, xbUInt32 lCopyLen );
+ xbString &Assign(const xbString &s, xbUInt32 ulStartPos );
+
+ 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(xbString &s, char delim, xbUInt32 iCnt, xbInt16 iOpt = 0 );
+ 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, xbUInt32 ulStartPos ) const;
+ xbUInt32 Pos(char c) const;
+ xbUInt32 Pos(const char *s) const;
+ xbString &PutAt(xbUInt32 ulPos, char c);
+
+ xbString &Remove( xbUInt32 ulPos, xbUInt32 ulN );
+ xbString &Replace( const char *sReplace, const char *sReplaceWith, xbInt16 iOpt = 0 );
+ 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.<br>
+ 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..2c31e45
--- /dev/null
+++ b/src/include/xbtblmgr.h
@@ -0,0 +1,63 @@
+/* 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 *psFqTblName; // Fully qualified name of table - same table can be opened multiple times, but must have unique alias
+ xbString *psTblName; // Table name without path, without extension
+ xbString *psTblAlias; // TblAliasName must be unique, same as TblNAme if Alias not provided
+ xbDbf *pDbf;
+};
+
+class XBDLLEXPORT xbTblMgr : public xbSsv {
+ public:
+ xbTblMgr();
+ ~xbTblMgr();
+ xbInt16 AddTblToTblList ( xbDbf *d, const xbString &sFqTblName );
+ xbInt16 AddTblToTblList ( xbDbf *d, const xbString &sFqTblName, const xbString &sTblAlias );
+ xbInt16 DisplayTableList () const;
+ xbDbf * GetDbfPtr ( const xbString &sTblAlias ) const;
+ xbDbf * GetDbfPtr ( xbInt16 sItemNo ) const;
+ xbTblList * GetTblListEntry ( xbDbf *d );
+ 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..e5c0e91
--- /dev/null
+++ b/src/include/xbuda.h
@@ -0,0 +1,52 @@
+/* xbuda.h
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+This class 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<xbString> 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..702da23
--- /dev/null
+++ b/src/include/xbxbase.h
@@ -0,0 +1,235 @@
+/* xbxbase.h
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+
+#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();
+ xbInt16 CreateFqn( const xbString &sDirIn, const xbString &sNameIn, const xbString &sExtIn, xbString &sFqfnOut );
+ 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 );
+
+ // next three methods moved to xbssv for consistency
+ // void SetLogDirectory( const xbString &sLogFileDirectory );
+ // void SetLogFileName ( const xbString &sLogFileName );
+ void SetLogSize ( size_t lSize );
+ size_t GetLogSize () const;
+
+ xbInt16 WriteLogMessage( const xbString &sLogMessage, xbInt16 iOutputOpt = 0 );
+ xbInt16 WriteLogBytes ( xbUInt32 lCnt, const char *p );
+
+ void xbSleep ( xbInt32 lMillisecs );
+
+ xbInt16 GetCmdLineOpt ( xbInt32 lArgc, char **argv, const char *sOptRqst, xbString &sParmOut );
+ xbInt16 GetCmdLineOpt ( xbInt32 lArgc, char **argv, xbString &sOptRqst, xbString &sParmOut );
+
+
+ /* 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;
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ friend class xbBlockRead;
+ #endif // XB_BLOCKREAD_SUPPORT
+
+
+ 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/xbalttbl.cpp b/src/sql/xbalttbl.cpp
new file mode 100755
index 0000000..8a36b46
--- /dev/null
+++ b/src/sql/xbalttbl.cpp
@@ -0,0 +1,120 @@
+/* xbslttbl.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::SqlAlterTable( const xbString &sCmdLine ){
+
+ // expected format:
+ // ALTER TABLE tablename.DBF RENAME TO newtablename.DBF
+
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbString sTableName = "";
+ xbUInt32 ulPos = 0;
+ xbString sCmd = sCmdLine;
+ xbString sNode = "";
+
+ xbString sSrcTbl;
+ xbString sTrgTbl;
+ xbDbf * dbf = NULL;
+
+ try{
+
+// std::cout << "xbSql::SqlAlterTable( " << sCmdLine.Str() << " )\n";
+
+ // 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();
+
+ // pull off the source table
+ sSrcTbl.ExtractElement( sCmd, ' ', 1, 0 );
+ sSrcTbl.Trim();
+
+ // pull off the action
+ sNode.ExtractElement( sCmd, ' ', 2, 0 );
+ sNode.Trim();
+ sNode.ToUpperCase();
+ if( sNode != "RENAME" ){
+ iErrorStop = 100;
+ iRc = XB_SYNTAX_ERROR;
+ throw iRc;
+ }
+
+ // pull off "TO"
+ sNode.ExtractElement( sCmd, ' ', 3, 0 );
+ sNode.Trim();
+ sNode.ToUpperCase();
+ if( sNode != "TO" ){
+ iErrorStop = 110;
+ iRc = XB_SYNTAX_ERROR;
+ throw iRc;
+ }
+
+ sTrgTbl.ExtractElement( sCmd, ' ', 4, 0 );
+ sTrgTbl.Trim();
+
+// std::cout << "source table name = " << sSrcTbl.Str() << "\n";
+// std::cout << "target table name = " << sTrgTbl.Str() << "\n";
+
+ // if not open, attempt to open it
+ dbf = xbase->GetDbfPtr( sSrcTbl );
+
+ if( !dbf ){
+ if(( iRc = xbase->OpenHighestVersion( sSrcTbl, "", &dbf )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+ }
+ if( !dbf ){
+ iErrorStop = 130;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+
+ if(( iRc = dbf->Rename( sTrgTbl )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ }
+
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbSql::SqlAlterTable() 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/xbcrix.cpp b/src/sql/xbcrix.cpp
new file mode 100755
index 0000000..8efdc39
--- /dev/null
+++ b/src/sql/xbcrix.cpp
@@ -0,0 +1,301 @@
+/* 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{
+
+
+/***********************************************************************/
+#ifdef XB_INDEX_SUPPORT
+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;
+ xbBool bTableLocked = xbFalse;
+
+ try{
+
+ // 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 = 100;
+ throw iRc;
+ }
+ }
+
+ #ifdef XB_NDX_SUPPORT
+ if( sIxType == "NDX" ){
+ xbFile f( xbase );
+ f.SetFileName( sIxName );
+ if( f.FileExists()){
+
+ iErrorStop = 110;
+ 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 = 120;
+ throw iRc;
+ }
+ }
+
+ if( dbf == NULL ){
+ iErrorStop = 130;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+ sCmd.Ltrunc( ulPos );
+
+ //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] == ',' && iParenCtr == 0 ){
+ 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 + 6 );
+ sFilter.Trim();
+ }
+ }
+ // std::cout << "sCmd - FILTER = [" << sFilter << "]\n";
+
+ #ifdef XB_LOCKING_SUPPORT
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ throw iRc;
+ } else {
+ bTableLocked = xbTrue;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ xbIx *pIx;
+ void *vpTag;
+
+ if(( iRc = dbf->CreateTag( sIxType, sIxName, sKeyExpression, sFilter, bDesc, bUnique, xbFalse, &pIx, &vpTag )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ #ifdef XB_NDX_SUPPORT
+ xbBool bAssociate = xbFalse;
+ if( sIxType == "NDX"){
+ sCmd.Ltrunc( ulPos );
+ sCmd.Trim();
+ if( sCmd.Len() > 0 ){
+ sCmd.ToUpperCase();
+ if( sCmd.Pos( "ASSOCIATE" )){
+ bAssociate = xbTrue;
+ if(( iRc = dbf->AssociateIndex( "NDX", sIxName, 0 )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ }
+ }
+ }
+ #endif // XB_NDX_SUPPORT
+
+ iRc = dbf->Reindex( 2, 1, &pIx, &vpTag );
+
+ #ifdef XB_NDX_SUPPORT
+ if( iRc != XB_NO_ERROR && sIxType == "NDX" && bAssociate ){
+ xbInt16 iRc2;
+ if(( iRc2 = dbf->AssociateIndex( "NDX", sIxName, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc2;
+ }
+ iErrorStop = 190;
+ throw iRc;
+ }
+ #endif // XB_NDX_SUPPORT
+
+ if( iRc != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc;
+ }
+
+
+ #ifdef XB_LOCKING_SUPPORT
+ if( bTableLocked ){
+ if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ throw iRc;
+ } else {
+ bTableLocked = xbFalse;
+ }
+ }
+ #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
+ if( bTableLocked && dbf )
+ dbf->LockTable( XB_UNLOCK );
+ #endif // XB_LOCKING_SUPPORT
+
+ return iRc;
+}
+#endif // XB_INDEX_SUPPORT
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_SQL_SUPPORT */
+
diff --git a/src/sql/xbcrtbl.cpp b/src/sql/xbcrtbl.cpp
new file mode 100755
index 0000000..e22b0b5
--- /dev/null
+++ b/src/sql/xbcrtbl.cpp
@@ -0,0 +1,274 @@
+/* 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 field 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<xbString> ll;
+ xbSchema *schema = NULL;
+ xbString sMsg;
+ xbString sWork;
+
+ try{
+ // retrieve table name
+ s.ExtractElement( sCmdLine, '(', 1, 0 );
+ sTableName.ExtractElement( s, ' ', 3, 0 );
+ sTableName.Trim();
+
+// std::cout << "Create table - Tablename = [" << sTableName.Str() << "]\n";
+// std::cout << "Cm line = [" << sCmdLine.Str() << "]\n";
+
+ // determine if it already exists
+ xbFile f( xbase );
+ f.SetFileName( sTableName );
+ if( f.FileExists() ){
+ iErrorStop = 100;
+ 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();
+
+ 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 = 110;
+ 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 = 120;
+ iRc = XB_INVALID_FIELD_LEN;
+ throw iRc;
+ }
+ s.Ltrunc( lPos );
+
+ lPos = s.Pos( ')' );
+ if( lPos <= 0 ){
+ iErrorStop = 130;
+ iRc = XB_INVALID_FIELD_LEN;
+ throw iRc;
+ }
+ sWork.Assign( s, 1, lPos - 1);
+ sWork.Trim();
+
+ if( iAttribCnt == 1 ){
+ sAttrib1 = sWork;
+ } else {
+
+ lCmPos = sWork.Pos( ',' );
+ if( lCmPos <= 0 ){
+ iErrorStop = 140;
+ iRc = XB_INVALID_FIELD_LEN;
+ throw iRc;
+ }
+
+ sAttrib1.Assign( sWork, 1, lCmPos - 1);
+ sAttrib1.Trim();
+
+ sWork.Ltrunc( lCmPos );
+ sAttrib2 = sWork;
+ sAttrib2.Trim();
+
+ }
+ 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<xbString> * 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 defined (XB_DBF3_SUPPORT)
+ dbf = new xbDbf3( xbase );
+ #endif
+
+ if(( iRc = dbf->CreateTable( sTableName, "", schema, 0, XB_MULTI_USER )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ 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/xbdelete.cpp b/src/sql/xbdelete.cpp
new file mode 100755
index 0000000..9bd4279
--- /dev/null
+++ b/src/sql/xbdelete.cpp
@@ -0,0 +1,148 @@
+/* xbdelete.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::SqlDelete( const xbString &sCmdLine ){
+
+ // expected format:
+ // DELETE FROM tablename.DBF [WHERE expression]
+
+
+ xbInt16 iRc = 0;
+ xbInt16 iRc2 = 0;
+ xbInt16 iErrorStop = 0;
+ xbString sTableName = "";
+ xbUInt32 ulPos = 0;
+ xbString sCmd = sCmdLine;
+ xbString sNode = "";
+ xbString sFilter;
+ xbInt16 iDelOpt = 0;
+
+ xbString sTable;
+ xbDbf * dbf = NULL;
+
+ try{
+
+// std::cout << "xbSql::SqlDelete( " << sCmdLine.Str() << " )\n";
+
+ sNode.ExtractElement( sCmd, ' ', 1, 0 );
+ sNode.Trim();
+ sNode.ToUpperCase();
+ if( sNode == "UNDELETE" )
+ iDelOpt = 1;
+
+ // pull off the action
+ sNode.ExtractElement( sCmd, ' ', 2, 0 );
+ sNode.Trim();
+ sNode.ToUpperCase();
+ if( sNode != "FROM" ){
+ iErrorStop = 100;
+ iRc = XB_SYNTAX_ERROR;
+ throw iRc;
+ }
+
+ // pull off the table name
+ sTable.ExtractElement( sCmd, ' ', 3, 0 );
+ sTable.Trim();
+
+ // pull off the "WHERE" statement if it exists
+ sNode.ExtractElement( sCmd, ' ', 4, 0 );
+ sNode.Trim();
+ sNode.ToUpperCase();
+ if( sNode == "WHERE" ){
+ ulPos = sCmd.Pos( "WHERE" );
+ sFilter = sCmd;
+ sFilter.Ltrunc( ulPos + 5 );
+ }
+
+ // if not open, attempt to open it
+ dbf = xbase->GetDbfPtr( sTable );
+ if( !dbf ){
+ if(( iRc = xbase->OpenHighestVersion( sTable, "", &dbf )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ if( !dbf ){
+ iErrorStop = 120;
+ iRc = XB_DBF_FILE_NOT_OPEN;
+ throw iRc;
+ }
+
+ if( sFilter == "" ){
+ if(( iRc = dbf->DeleteAll( iDelOpt )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ } else {
+
+ xbFilter f( dbf );
+ if(( iRc = f.Set( sFilter )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+
+ iRc2 = f.GetFirstRecord( XB_ALL_RECS );
+ while( iRc2 == XB_NO_ERROR ){
+ if( iDelOpt == 0 ){
+ if( !dbf->RecordDeleted()){
+ if(( iRc = dbf->DeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ throw iRc;
+ }
+ if(( iRc = dbf->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 170;
+ throw iRc;
+ }
+ }
+
+ } else { // undelete
+ if( dbf->RecordDeleted()){
+ if(( iRc = dbf->UndeleteRecord()) != XB_NO_ERROR ){
+ iErrorStop = 180;
+ throw iRc;
+ }
+ if(( iRc = dbf->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+ }
+ }
+ iRc2 = f.GetNextRecord();
+ }
+ }
+ }
+
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbSql::SqlDelete() 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/xbdrpix.cpp b/src/sql/xbdrpix.cpp
new file mode 100755
index 0000000..2815264
--- /dev/null
+++ b/src/sql/xbdrpix.cpp
@@ -0,0 +1,155 @@
+/* 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{
+
+#ifdef XB_INDEX_SUPPORT
+/***********************************************************************/
+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 = 100;
+ 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 = 110;
+ throw iRc;
+ }
+ }
+ if( dbf == NULL ){
+ iErrorStop = 120;
+ iRc = XB_FILE_NOT_FOUND;
+ throw iRc;
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ #endif // XB_LOCKING_SUPPORT
+
+ ulPos = sIxName.Pos( ".NDX" );
+ if( ulPos > 0 ){
+ iRc = dbf->DeleteTag( "NDX", sIxName );
+ if( iRc == XB_FILE_NOT_FOUND && !bIfExists ){
+ iErrorStop = 140;
+ throw( iRc );
+ } else if( iRc != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc;
+ }
+ } else { // assuming MDX tag
+ iRc = dbf->DeleteTag( "MDX", sIxName );
+ if( iRc == XB_FILE_NOT_FOUND && !bIfExists ){
+ iErrorStop = 160;
+ throw( iRc );
+ } else if( iRc != XB_NO_ERROR ){
+ iErrorStop = 170;
+ 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;
+}
+#endif // XB_INDEX_SUPPORT
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_SQL_SUPPORT */
+
diff --git a/src/sql/xbdrptbl.cpp b/src/sql/xbdrptbl.cpp
new file mode 100755
index 0000000..ee3e325
--- /dev/null
+++ b/src/sql/xbdrptbl.cpp
@@ -0,0 +1,129 @@
+/* xbdrptbl.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+#ifdef XB_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 = 100;
+ 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 );
+ iRc = xbase->OpenHighestVersion( sTableName, "", &dbf );
+ if( iRc != XB_NO_ERROR ){
+ if( iRc == XB_FILE_NOT_FOUND && bIfExists ){
+ return XB_NO_ERROR;
+ } else {
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ }
+ if( dbf == NULL ){
+ iErrorStop = 120;
+ iRc = XB_OPEN_ERROR;
+ throw iRc;
+ }
+
+ if(( dbf->DeleteTable()) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+
+ delete dbf;
+ // dbf = NULL;
+ }
+
+ 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..835f9e6
--- /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 <xbInt16> llMemoFieldNos;
+ xbLinkList <xbString> 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 = 100;
+ throw iRc;
+ }
+ }
+
+ if( !dbf ){
+ iErrorStop = 110;
+ 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 = 120;
+ 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 = 130;
+ throw iRc;
+ }
+ if(( iRc = dbf->GetFieldType( iFldNo, cFldType )) != XB_NO_ERROR ){
+ iErrorStop = 140;
+ 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 = 150;
+ throw iRc;
+ }
+ break;
+
+ case 'D':
+ // assumes input date format of yyyy-mm-dd
+ if( sFieldData.Len() != 10 || sFieldData[5] != '-' || sFieldData[8] != '-' ){
+ iErrorStop = 160;
+ 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 = 170;
+ throw iRc;
+ }
+ break;
+
+ case 'M':
+ llMemoFieldNos.InsertAtFront( iFldNo );
+ llMemoFieldData.InsertAtFront( sFieldData );
+ break;
+
+ default:
+ iErrorStop= 180;
+ iRc = XB_INVALID_FIELD_TYPE;
+ throw iRc;
+ }
+ }
+
+ if(( iRc = dbf->AppendRecord()) != XB_NO_ERROR ){
+ iErrorStop = 190;
+ throw iRc;
+ }
+
+ // Add any memo fields
+ xbLinkListNode<xbInt16> * llN = llMemoFieldNos.GetHeadNode();
+ xbLinkListNode<xbString> * 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 = 200;
+ throw iRc;
+ }
+ llN = llN->GetNextNode();
+ llD = llD->GetNextNode();
+ }
+
+ if(( iRc = dbf->Commit()) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ 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/xbselect.cpp b/src/sql/xbselect.cpp
new file mode 100755
index 0000000..7f12e99
--- /dev/null
+++ b/src/sql/xbselect.cpp
@@ -0,0 +1,97 @@
+/* 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 xbStmt::ExecuteQuery( const xbString &sCmdLine ){
+
+ std::cout << "xbStmt::ExecuteQuery() - SELECT [" << sCmdLine << "]\n";
+ // expected format:
+ // SELECT
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ // xbUInt32 ulPos;
+
+ try{
+
+ if(( iRc = ParseStmt( sCmdLine )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+
+/*
+ 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 = 100;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = uda.UpdTokenForKey( sKey, sToken )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ }
+ }
+*/
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbStmt::ExecuteQuery() 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/xbset.cpp b/src/sql/xbset.cpp
new file mode 100755
index 0000000..d0e8821
--- /dev/null
+++ b/src/sql/xbset.cpp
@@ -0,0 +1,90 @@
+/* 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 = 100;
+ throw iRc;
+ }
+ } else {
+ if(( iRc = uda.UpdTokenForKey( sKey, sToken )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ 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..12d3379
--- /dev/null
+++ b/src/sql/xbsql.cpp
@@ -0,0 +1,176 @@
+/* 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 == "ALTER" ){
+ xbString sNode2;
+ sNode2.ExtractElement( sCmd.Str(), ' ', 2, 0 );
+ sNode2.ToUpperCase();
+ sNode2.Trim();
+
+ if( sNode2 == "TABLE" )
+ iRc = SqlAlterTable( sCmd );
+ else
+ iRc = XB_INVALID_FUNCTION;
+
+
+
+ } else if( sNode1 == "CREATE" ){
+
+ xbString sNode2;
+ sNode2.ExtractElement( sCmd.Str(), ' ', 2, 0 );
+ sNode2.ToUpperCase();
+ sNode2.Trim();
+ if( sNode2 == "TABLE" )
+ iRc = SqlCreateTable( sCmd );
+
+ #ifdef XB_INDEX_SUPPORT
+ else if( sNode2 == "INDEX" || sNode2 == "UNIQUE" )
+ iRc = SqlCreateIndex( sCmd );
+ #endif // XB_INDEX_SUPPORT
+
+ 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 );
+
+ #ifdef XB_INDEX_SUPPORT
+ else if( sNode2 == "INDEX" )
+ iRc = SqlDropIndex( sCmd );
+ #endif // XB_INDEX_SUPPORT
+
+ else
+ iRc = XB_INVALID_FUNCTION;
+
+ } else if( sNode1 == "DELETE" || sNode1 == "UNDELETE" ){
+
+ iRc = SqlDelete( 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 << "ALTER TABLE tablename.DBF RENAME TO newtablename.DBF" << std::endl;
+ std::cout << "CREATE INDEX ixname.NDX ON tablename.dbf ( EXPRESSION ) [ASSOCIATE]" << std::endl;
+ std::cout << "CREATE [UNIQUE] INDEX tagname ON tablename.dbf ( EXPRESSION ) [DESC] [FILTER .NOT. DELETED()]" << std::endl;
+ std::cout << "CREATE TABLE tablename.DBF (Field1 CHAR(10), INTFLD1 INTEGER, ... )" << std::endl;
+ std::cout << "DELETE FROM tablename.DBF [WHERE expression]" << std::endl;
+ std::cout << "DROP INDEX [IF EXISTS] ixname.NDX ON tablename.DBF" << std::endl;
+ std::cout << "DROP TABLE [IF EXISTS] tablename.DBF" << std::endl;
+ std::cout << "INSERT INTO tablename (field1, field2, field3,...) VALUES ( 'charval', numval, {MM/DD/YY})" << std::endl;
+ std::cout << "SELECT FIELD1,FIELD2... FROM TABLE.DBF [WHERE expression] [ORDER BY TAG TAGNAME" << std::endl;
+ std::cout << "SET ATTRIBUTE = DATAVALUE" << std::endl;
+ std::cout << "SET ATTRIBUTE = ^ (to delete an entry)" << 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/sql/xbstmt.cpp b/src/sql/xbstmt.cpp
new file mode 100755
index 0000000..61c84fd
--- /dev/null
+++ b/src/sql/xbstmt.cpp
@@ -0,0 +1,679 @@
+/* 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{
+
+/***********************************************************************/
+xbStmt::xbStmt( xbXBase *x ){
+ xbase = x;
+ pTblList = NULL;
+ fl = NULL;
+ ulFromPos = 0;
+ ulWherePos = 0;
+ ulOrderByPos = 0;
+ ulGroupByPos = 0;
+ ulHavingPos = 0;
+}
+
+/***********************************************************************/
+xbStmt::~xbStmt(){
+
+ std::cout << "xbStmt::~xbStmt() - need to release all allocated structures and memory here\n";
+
+}
+
+/***********************************************************************/
+xbInt16 xbStmt::CvtSqlExp2DbaseExp( const xbString &sExpIn, xbString &sExpOut ){
+
+ // convert Ansi SQL expression to a DBase compatible expression
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+
+ try{
+
+ // convert tablename.fieldname to tablename->fieldname
+ sExpOut = sExpIn[1];
+ xbUInt32 ulPos = 2;
+ while( ulPos < sExpIn.Len() ){
+ if( sExpIn[ulPos] == '.' && sExpIn[ulPos-1] != ' ' && sExpIn[ulPos+1] != ' ' )
+ sExpOut += "->";
+ else
+ sExpOut += sExpIn[ulPos];
+ ulPos++;
+ }
+ sExpOut += sExpIn[ulPos];
+
+ sExpOut.Replace( " AND ", " .AND. ", 0 );
+ sExpOut.Replace( " and ", " .AND. ", 0 );
+ sExpOut.Replace( " OR ", " .OR. ", 0 );
+ sExpOut.Replace( " or ", " .OR. ", 0 );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbStmt::CvtSqlExp2DbaseExp() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+
+/***********************************************************************/
+#ifdef XB_DEBUG_SUPPORT
+xbInt16 xbStmt::DumpStmtInternals(){
+
+ std::cout << "************** Dump Statment Internals ***************" << std::endl;
+ std::cout << "Statement [" << sStmt << "]\n";
+ std::cout << "Fields [" << sFields << "]\n";
+ std::cout << "From [" << sFrom << "]\n";
+ std::cout << "Where [" << sWhere << "]\n";
+ std::cout << "Order By [" << sOrderBy << "]\n";
+ std::cout << "Group By [" << sGroupBy << "]\n";
+ std::cout << "Having [" << sHaving << "]\n";
+
+ xbTblJoin *p = pTblList;
+ if( p ){
+ std::cout << "Tbl List\n";
+ std::cout << "Type,Name,Alias,Exp\n";
+ while( p ){
+ std::cout << p->cJoinType << "," << p->sTableName.Str() << "," << p->sAlias.Str() << "," << p->sJoinExp.Str() << "\n";
+ p = p->next;
+ }
+ }
+ std::cout << "*************** End of Internals ************" << std::endl;
+ return 0;
+}
+
+xbInt16 xbStmt::Test(){
+ xbString s;
+
+ s = "(ABC)";
+ std::cout << "[" << s.Str() << "][" << GetParenCnt( s ) << "]\n";
+ s = "(ABC) )";
+ std::cout << "[" << s.Str() << "][" << GetParenCnt( s ) << "]\n";
+ s = "(";
+ std::cout << "[" << s.Str() << "][" << GetParenCnt( s ) << "]\n";
+ s = "( (";
+ std::cout << "[" << s.Str() << "][" << GetParenCnt( s ) << "]\n";
+ s = "aaa)";
+ std::cout << "[" << s.Str() << "][" << GetParenCnt( s ) << "]\n";
+ s = "aaa";
+ std::cout << "[" << s.Str() << "][" << GetParenCnt( s ) << "]\n";
+
+ return 0;
+
+}
+#endif // XB_DEBUG_SUPPORT
+
+
+/***********************************************************************/
+xbInt16 xbStmt::GetNextFromSeg( const xbString &sLineIn, xbString &sFromSegOut ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iParenCtr = 0;
+ xbUInt32 ulPos = 1;
+ xbUInt32 ulTsp = 0; // token start position
+ xbBool bDone = xbFalse;
+ xbString sToken;
+
+ try{
+ sFromSegOut = "";
+ while( !bDone ){
+ ulTsp = GetNextToken( sLineIn, sToken, ulPos );
+ if( sToken == "" ){
+ bDone = xbTrue;
+
+ } else {
+ iParenCtr += GetParenCnt( sToken );
+ if( iParenCtr == 0 && ulPos > 1 ){
+ sToken.ToUpperCase();
+ if( sToken == "LEFT" || sToken == "RIGHT" || sToken == "FULL" ||
+ sToken == "INNER" || sToken == "OUTER" || sToken[1] == '(' ){
+ bDone = xbTrue;
+ }
+ }
+ }
+ ulPos += (sToken.Len() + 1);
+ }
+ if( ulTsp > 1 )
+ sFromSegOut.Assign( sLineIn, 1, ulTsp - 2 );
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbStmt::GetNextFromSeg() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+
+xbUInt32 xbStmt::GetNextToken( const xbString &sCmdLineIn, xbString &sTokenOut, xbUInt32 ulStartPos ){
+
+ // input
+ // sCmdLine - Entire Command line
+ // ulStartPos - Where to start searching for the next token
+ // returns the position of the beginning of the token in the main command line
+
+ xbUInt32 ul = ulStartPos;
+ xbUInt32 ulTokenStartPos = 0;
+ sTokenOut = "";
+
+ while( ul <= sCmdLineIn.Len() && sCmdLineIn[ul] == ' ' )
+ ul++;
+ ulTokenStartPos = ul;
+
+ while( ul <= sCmdLineIn.Len() && sCmdLineIn[ul] != ' ' ){
+ sTokenOut += sCmdLineIn[ul];
+ ul++;
+ }
+
+ return ulTokenStartPos;
+}
+/***********************************************************************/
+xbInt16 xbStmt::GetParenCnt( const xbString &sToken ){
+
+ xbInt16 iParenCnt = 0;
+
+ for( xbUInt32 i = 1; i <= sToken.Len(); i++ ){
+ if( sToken[i] == '(' )
+ iParenCnt++;
+ else if( sToken[i] == ')' )
+ iParenCnt--;
+ }
+ return iParenCnt;
+}
+
+
+/***********************************************************************/
+xbInt16 xbStmt::ParseFromStmt( const xbString &sFromLine ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbBool bDone = xbFalse;
+ xbString sFromSeg;
+ xbString sLine = sFromLine;
+
+ try{
+ std::cout << "ParseFromSeg [" << sFromLine.Str() << "]\n";
+
+ while( !bDone ){
+ if(( iRc = GetNextFromSeg( sLine, sFromSeg)) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ if( sFromSeg.Len() == 0 ){
+ bDone = xbTrue;
+ } else {
+ if(( iRc = ProcessFromSeg( sFromSeg )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+ sLine.Ltrunc( sFromSeg.Len() );
+ sLine.Ltrim();
+ }
+ }
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbStmt::ParseFromSeg() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+/***********************************************************************/
+xbInt16 xbStmt::ParseStmt( const xbString &sCmdLine ){
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ xbInt16 iParenCnt = 0;
+ xbUInt32 ulStartPos = 7;
+ xbUInt32 ulLen = 0;
+
+
+ xbBool bFromFound = xbFalse;
+ xbBool bWhereFound = xbFalse;
+ xbBool bOrderByFound = xbFalse;
+ xbBool bGroupByFound = xbFalse;
+ xbBool bHavingFound = xbFalse;
+ xbBool bDone = xbFalse;
+
+ xbString sToken;
+
+ sStmt.Set( sCmdLine );
+ sStmt.Trim();
+
+ try{
+
+ std::cout << "ParseStmt - [" << sStmt << "]\n";
+
+ xbUInt32 ulPos = ulStartPos;
+
+ while( !bFromFound && !bDone ){
+ ulPos = GetNextToken( sStmt, sToken, ulPos );
+ if( sToken == "" ){
+ bDone = xbTrue;
+ } else {
+ iParenCnt += GetParenCnt( sToken );
+ if( iParenCnt == 0 ){
+ sToken.ToUpperCase();
+ if( sToken == "FROM" ){
+ bFromFound = xbTrue;
+ ulFromPos = ulPos;
+ sFrom.Assign( sStmt, ulFromPos );
+ }
+ }
+ ulPos += sToken.Len() + 1;
+ }
+ }
+
+ // look for the where clause
+ if( bFromFound )
+ ulPos = ulFromPos + 5;
+ else
+ ulPos = ulStartPos;
+
+ bDone = xbFalse;
+ while( !bWhereFound && !bDone ){
+ ulPos = GetNextToken( sStmt, sToken, ulPos );
+ if( sToken == "" ){
+ bDone = xbTrue;
+ } else {
+ iParenCnt += GetParenCnt( sToken );
+ if( iParenCnt == 0 ){
+ sToken.ToUpperCase();
+ if( sToken == "WHERE" ){
+ bWhereFound = xbTrue;
+ ulWherePos = ulPos;
+ sWhere.Assign( sStmt, ulWherePos+6 );
+ }
+ }
+ ulPos += sToken.Len() + 1;
+ }
+ }
+
+
+ // look for the order clause
+ if( bWhereFound )
+ ulPos = ulWherePos + 6;
+ else if( bFromFound )
+ ulPos = ulFromPos + 5;
+ else
+ ulPos = ulStartPos;
+
+ bDone = xbFalse;
+ while( !bOrderByFound && !bDone ){
+ ulPos = GetNextToken( sStmt, sToken, ulPos );
+ if( sToken == "" ){
+ bDone = xbTrue;
+ } else {
+ iParenCnt += GetParenCnt( sToken );
+ if( iParenCnt == 0 ){
+ sToken.ToUpperCase();
+ if( sToken == "ORDER" ){
+ xbString sToken2;
+ xbUInt32 ulPos2 = GetNextToken( sStmt, sToken2, ulPos + 6 );
+ if( sToken2 != "" ){
+ sToken2.ToUpperCase();
+ if( sToken2 == "BY" ){
+ bOrderByFound = xbTrue;
+ ulOrderByPos = ulPos;
+ sOrderBy.Assign( sStmt, ulPos2 + 3 );
+ }
+ }
+ }
+ }
+ ulPos += sToken.Len() + 1;
+ }
+ }
+
+
+ bDone = xbFalse;
+ while( !bGroupByFound && !bDone ){
+ ulPos = GetNextToken( sStmt, sToken, ulPos );
+ if( sToken == "" ){
+ bDone = xbTrue;
+ } else {
+ iParenCnt += GetParenCnt( sToken );
+ if( iParenCnt == 0 ){
+ sToken.ToUpperCase();
+ if( sToken == "GROUP" ){
+ xbString sToken2;
+ xbUInt32 ulPos2 = GetNextToken( sStmt, sToken2, ulPos + 6 );
+ if( sToken2 != "" ){
+ sToken2.ToUpperCase();
+ if( sToken2 == "BY" ){
+ bGroupByFound = xbTrue;
+ ulGroupByPos = ulPos;
+ sGroupBy.Assign( sStmt, ulPos2 + 3 );
+ }
+ }
+ }
+ }
+ ulPos += sToken.Len() + 1;
+ }
+ }
+
+ bDone = xbFalse;
+ while( !bHavingFound && !bDone ){
+ ulPos = GetNextToken( sStmt, sToken, ulPos );
+ if( sToken == "" ){
+ bDone = xbTrue;
+ } else {
+ iParenCnt += GetParenCnt( sToken );
+ if( iParenCnt == 0 ){
+ sToken.ToUpperCase();
+ if( sToken == "HAVING" ){
+ bHavingFound = xbTrue;
+ ulHavingPos = ulPos;
+ sHaving.Assign( sStmt, ulHavingPos + 7 );
+ }
+ }
+ ulPos += sToken.Len() + 1;
+ }
+ }
+
+
+ // do the fields part
+ if( bFromFound )
+ ulLen = ulFromPos - 7;
+ else if( bWhereFound )
+ ulLen = ulWherePos - 7;
+ else if( bOrderByFound )
+ ulLen = ulOrderByPos - 7;
+ else if( bGroupByFound )
+ ulLen = ulGroupByPos - 7;
+ else if( bHavingFound )
+ ulLen = ulHavingPos - 7;
+ else
+ ulLen = sStmt.Len() - 7;
+ sFields.Assign( sStmt, 7, ulLen );
+ sFields.Trim();
+
+ // do the FROM part
+ if( bFromFound ){
+ if( bWhereFound )
+ ulLen = ulWherePos - ulFromPos;
+ else if( bOrderByFound )
+ ulLen = ulOrderByPos - ulFromPos;
+ else if( bGroupByFound )
+ ulLen = ulGroupByPos - ulFromPos;
+ else if( bHavingFound )
+ ulLen = ulHavingPos - ulFromPos;
+ else
+ ulLen = sStmt.Len() - ulFromPos;
+ sFrom.Resize( ulLen );
+ sFrom.Trim();
+
+ }
+
+
+ // do the WHERE part
+ if( bWhereFound ){
+ ulLen = 0;
+ if( bOrderByFound )
+ ulLen = ulOrderByPos - ulWherePos - 6;
+ else if( bGroupByFound )
+ ulLen = ulGroupByPos - ulWherePos - 6;
+ else if( bHavingFound )
+ ulLen = ulHavingPos - ulWherePos - 6;
+
+ if( ulLen > 0 )
+ sWhere.Resize( ulLen );
+ sWhere.Trim();
+ }
+
+ // FIXME if there is more than one space between ORDER and BY then this doesn't work quite right
+ // do the ORDER BY part
+ if( bOrderByFound ){
+ if( bGroupByFound )
+ ulLen = ulGroupByPos - ulOrderByPos - 9;
+ else if( bHavingFound )
+ ulLen = ulHavingPos - ulOrderByPos - 9;
+ else
+ ulLen = sStmt.Len() - ulOrderByPos - 9;
+ sOrderBy.Resize( ulLen );
+ sOrderBy.Trim();
+ }
+
+ // FIXME if there is more than one space between ORDER and BY then this doesn't work quite right
+ // do the GROUP BY part
+ if( bGroupByFound ){
+ if( bHavingFound )
+ ulLen = ulHavingPos - ulGroupByPos - 9;
+ else
+ ulLen = sStmt.Len() - ulGroupByPos - 9;
+ sGroupBy.Resize( ulLen );
+ sGroupBy.Trim();
+ }
+
+ if( bFromFound ){
+ if(( iRc = ParseFromStmt( sFrom )) != XB_NO_ERROR ){
+ iErrorStop = 100;
+ throw iRc;
+ }
+ }
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbStmt::ParseStmt() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+
+ return iRc;
+}
+
+/***********************************************************************/
+xbInt16 xbStmt::ProcessFromSeg( const xbString &sFromSeg ){
+
+
+ xbInt16 iRc = 0;
+ xbInt16 iErrorStop = 0;
+ char cType = ' ';
+ xbString sToken;
+ xbString sTable;
+ xbString sAlias;
+ xbString sWork;
+ xbString sExp;
+ xbUInt32 iPos;
+ xbBool bDone;
+ xbExp * pJoinExp = NULL;
+
+ try{
+
+ std::cout << "ProcessFromSeg for segment [" << sFromSeg.Str() << "]\n";
+
+ GetNextToken( sFromSeg, sToken, 1 );
+ sToken.ToUpperCase();
+
+ std::cout << "sToken1 = [" << sToken.Str() << "]\n";
+
+ if( sToken == "FROM" ){
+ // FROM has to be the first statement, and exist only once
+ if( pTblList ){
+ iErrorStop = 100;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ cType = 'M';
+ } else if( sToken == "LEFT" )
+ cType = 'L';
+ else if( sToken == "RIGHT" )
+ cType = 'R';
+ else if( sToken == "RIGHT" )
+ cType = 'R';
+ else if( sToken == "INNER" )
+ cType = 'I';
+ else if( sToken == "OUTER" || sToken == "FULL" )
+ cType = 'O';
+ else if( sToken[1] == '(' )
+ cType = 'Q';
+ else{
+ iErrorStop = 100;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+
+
+ if( cType == 'M' ){
+ // expecting "FROM table" or "FROM table alias"
+ iPos = GetNextToken( sFromSeg, sToken, 5 );
+ sTable = sToken;
+
+ iPos = GetNextToken( sFromSeg, sToken, iPos + sToken.Len() + 1 );
+ if( sToken.Len() > 0 )
+ sAlias = sToken;
+ else
+ sAlias = "";
+
+ sWork = sToken;
+ sWork.ToUpperCase();
+ iPos = sWork.Pos( ".DBF" );
+ if( iPos == 0 )
+ sTable += ".DBF";
+
+ // std::cout << "table = [" << sTable.Str() << "] alias = [" << sAlias.Str() << "]\n";
+
+ } else if( cType != 'Q' ){
+ sWork = sFromSeg;
+ sWork.ToUpperCase();
+ iPos = sWork.Pos( " ON " );
+ if( iPos == 0 ){
+ iErrorStop = 110;
+ iRc = XB_PARSE_ERROR;
+ throw iRc;
+ }
+ bDone = xbFalse;
+
+ sWork.Assign( sFromSeg, iPos + 4 );
+ CvtSqlExp2DbaseExp( sWork, sExp );
+ sExp.Trim();
+ std::cout << "ON = [" << sExp.Str() << "]\n";
+
+
+ // need to get past the keywords LEFT, FULL, OUTER, RIGHT, INNER and get the table name here
+ iPos = sToken.Len() + 1;
+ while( !bDone ){
+ iPos = GetNextToken( sFromSeg, sToken, iPos );
+ sWork = sToken;
+ sWork.ToUpperCase();
+ if( sWork != "LEFT" && sWork != "RIGHT" && sWork != "INNER" && sWork != "OUTER" && sWork != "FULL" && sWork != "JOIN" )
+ bDone = xbTrue;
+ else
+ iPos += (sWork.Len() + 1);
+ }
+
+ sTable = sToken;
+ GetNextToken( sFromSeg, sAlias, iPos + sTable.Len() + 1 );
+ sWork.ToUpperCase();
+ iPos = sWork.Pos( ".DBF" );
+ if( iPos == 0 )
+ sTable += ".DBF";
+ sWork = sAlias;
+ sWork.ToUpperCase();
+ if( sWork == "ON" )
+ sAlias = "";
+ }
+
+
+ // open table
+ //std::cout << "attempting to open table " << sTable.Str() << "\n";
+ xbDbf *d = NULL;
+ if(( iRc = xbase->OpenHighestVersion( sTable, sAlias, &d )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc;
+ }
+
+
+
+ // build out table linkage expression
+ if( cType == 'M' )
+ sExp = "MASTER";
+ else if( cType == 'L' ){
+ pJoinExp = new xbExp( xbase );
+
+ // std::cout << "Parsing expression [" << sExp.Str() << "]\n";
+ if(( iRc = pJoinExp->ParseExpression( d, sExp )) != XB_NO_ERROR ){
+ iErrorStop = 130;
+ throw iRc;
+ }
+ }
+
+
+ // attach to correct location in list
+
+
+ // update the UpdateJoinList routine as appropriate
+// if(( iRc = UpdateJoinList( cType, sTable, sAlias, NULL, NULL, NULL, NULL )) != XB_NO_ERROR ){
+
+ if(( iRc = UpdateJoinList( cType, sTable, sAlias, sExp, d, pJoinExp )) != XB_NO_ERROR ){
+ iErrorStop = 110;
+ throw iRc;
+ }
+
+
+ }
+ catch (xbInt16 iRc ){
+ xbString sMsg;
+ sMsg.Sprintf( "xbStmt::ProcessFromSeg() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
+ xbase->WriteLogMessage( sMsg.Str() );
+ xbase->WriteLogMessage( GetErrorMessage( iRc ));
+ }
+ return iRc;
+}
+
+/***********************************************************************/
+xbInt16 xbStmt::UpdateJoinList( char cType, const xbString &sTableName, const xbString &sAlias, const xbString &sExp, xbDbf *d, xbExp *e ){
+// xbTag *t ){
+
+
+ std::cout << "Update join list for table " << sTableName.Str() << "\n";
+
+ xbTblJoin *pTj;
+ if(( pTj = (xbTblJoin *) calloc( 1, sizeof( xbTblJoin ))) == NULL )
+ return XB_NO_MEMORY;
+
+ pTj->cJoinType = cType;
+ pTj->sTableName.Set( sTableName );
+ pTj->sAlias.Set( sAlias );
+ pTj->sJoinExp.Set( sExp );
+ pTj->pDbf = d;
+ pTj->pLinkExp = e;
+
+/*
+
+ pTj->pTag = t;
+*/
+
+ if( cType == 'M' )
+ pTblList = pTj;
+
+ return 0;
+}
+/***********************************************************************/
+} /* namespace */
+#endif /* XB_SQL_SUPPORT */
+
diff --git a/src/tests/tstfuncs.cpp b/src/tests/tstfuncs.cpp
new file mode 100755
index 0000000..377b199
--- /dev/null
+++ b/src/tests/tstfuncs.cpp
@@ -0,0 +1,583 @@
+/* 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(), "w", 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<xbInt32> 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..f735935
--- /dev/null
+++ b/src/tests/xb_test_bcd.cpp
@@ -0,0 +1,127 @@
+/* xb_test_bcd.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2017,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ 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_blockread.cpp b/src/tests/xb_test_blockread.cpp
new file mode 100755
index 0000000..ff55d23
--- /dev/null
+++ b/src/tests/xb_test_blockread.cpp
@@ -0,0 +1,168 @@
+/* xb_test_blockread.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This program tests the class xbBlockRead
+// usage: xb_test_blockread QUITE|NORMAL|VERBOSE
+
+
+#include "xbase.h"
+
+using namespace xb;
+
+#include "tstfuncs.cpp"
+
+ xbSchema MyDbfRec[] =
+ {
+ { "NFLD", XB_NUMERIC_FLD, 10, 0 },
+ { "CFLD", XB_CHAR_FLD, 89, 0 },
+ { "",0,0,0 }
+ };
+
+
+int main( int argCnt, char **av )
+{
+ xbInt16 iRc = 0;
+ xbInt16 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;
+ }
+
+ xbXBase x;
+ x.SetDataDirectory( PROJECT_DATA_DIR );
+
+ xbDbf4 dbf( &x );
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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
+
+
+ InitTime();
+
+
+ iRc2 = dbf.CreateTable( "BLOCKRD.DBF", "BlockRead", MyDbfRec, XB_OVERLAY, XB_MULTI_USER );
+ iRc += TestMethod( iPo, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+
+ // fill the file with several blocks of data
+ for( xbInt32 i = 0; i < 50; i++ ){
+ iRc2 = dbf.BlankRecord();
+ if( iRc2 != XB_NO_ERROR )
+ iRc += TestMethod( iPo, "BlankRecord()", (xbInt32) iRc2, XB_NO_ERROR );
+
+ iRc2 = dbf.PutLongField( 0, i+1 );
+ if( iRc2 != XB_NO_ERROR )
+ iRc += TestMethod( iPo, "PutLongField()", (xbInt32) iRc2, XB_NO_ERROR );
+
+ iRc2 = dbf.AppendRecord();
+ if( iRc2 != XB_NO_ERROR )
+ iRc += TestMethod( iPo, "AppendRecord()", (xbInt32) iRc2, XB_NO_ERROR );
+
+ iRc2 = dbf.Commit();
+ if( iRc2 != XB_NO_ERROR )
+ iRc += TestMethod( iPo, "Commit()", (xbInt32) iRc2, XB_NO_ERROR );
+ }
+
+
+ dbf.SetDefaultBlockReadSize( 2000 );
+
+ // enable block read for this table
+ iRc += TestMethod( iPo, "EnableBlockReadProcessing()", dbf.EnableBlockReadProcessing(), XB_NO_ERROR );
+
+ xbUInt32 ulFld = 0;
+ xbUInt32 ulCtr = 1;
+
+ iRc2 = XB_NO_ERROR;
+ while( iRc2 == XB_NO_ERROR ){
+ // std::cout << ulCtr << "\n";
+ iRc2 += dbf.GetRecord( ulCtr );
+ if( iRc2 != XB_NO_ERROR && iRc != XB_INVALID_RECORD )
+ TestMethod( iPo, "GetRecord()", iRc, XB_NO_ERROR );
+
+ if( iRc2 == XB_NO_ERROR ){
+ dbf.GetULongField( "NFLD", ulFld );
+ if( ulFld != ulCtr ){
+ std::cout << "ulFld = " << ulFld << " ulCtr = " << ulCtr << "\n";
+ iRc += TestMethod( iPo, "Field Compare", (xbDouble) ulFld, (xbDouble) ulCtr );
+ }
+ }
+ ulCtr++;
+ }
+
+ // std::cout << "**********Delete every other record\n";
+ iRc2 = dbf.GetFirstRecord();
+ while( iRc2 == XB_NO_ERROR ){
+ if( (dbf.GetCurRecNo() % 2) != 0 ){
+ dbf.DeleteRecord();
+ dbf.Commit();
+ }
+ iRc2 = dbf.GetNextRecord();
+ }
+
+
+ // test filter for deleted records
+ #ifdef XB_FILTER_SUPPORT
+ xbFilter f( &dbf );
+ xbString sFilt = ".NOT. DELETED()";
+ f.Set( sFilt );
+ iRc2 = f.GetFirstRecord();
+ while( iRc2 == XB_NO_ERROR ){
+ dbf.GetULongField( "NFLD", ulFld );
+ if( (ulFld % 2) != 0 ){
+ iRc += TestMethod( iPo, "Filter GetNextRecord()", ulFld % 2, 0 );
+ }
+ iRc2 = f.GetNextRecord();
+ }
+ #endif // XB_FILTER_SUPPORT
+
+
+ iRc += TestMethod( iPo, "DisableBlockReadProcessing()", dbf.DisableBlockReadProcessing(), XB_NO_ERROR );
+ //iRc2 = dbf.DeleteTable();
+
+ iRc2 = dbf.Close();
+ iRc += TestMethod( iPo, "Close()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+
+ #ifdef XB_LOGGING_SUPPORT
+ sMsg.Sprintf( "Program [%s] terminating with [%d] errors...", av[0], iRc * -1 );
+ x.WriteLogMessage( sMsg );
+ #endif
+
+
+ if( iPo > 0 || iRc < 0 )
+ fprintf( stdout, "Total Errors = %d\n", iRc * -1 );
+
+ return iRc;
+}
+
diff --git a/src/tests/xb_test_date.cpp b/src/tests/xb_test_date.cpp
new file mode 100755
index 0000000..0efe102
--- /dev/null
+++ b/src/tests/xb_test_date.cpp
@@ -0,0 +1,214 @@
+/* xb_test_date.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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;
+ }
+
+ xbXBase x;
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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, "d1<d2", d1<d2, 1 );
+ iRc+= TestMethod( po, "d1<=d2", d1<=d2, 1 );
+
+ d2-=2;
+ iRc+= TestMethod( po, "d1>d2", d1>d2, 1 );
+ 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, "d1-d2", d1-d2, 1 );
+
+
+ // null date testing follows
+ iRc+= TestMethod( po, "IsNull", d1.IsNull(), xbFalse );
+ xbDate dN;
+ iRc+= TestMethod( po, "IsNull", dN.IsNull(), xbTrue );
+
+ xbDate dtNull1;
+ xbDate dtNull2;
+
+ iRc+= TestMethod( po, "dtNull1==dtNull2", dtNull1 == dtNull2, 1 );
+ iRc+= TestMethod( po, "dtNull1!=dtNull2", dtNull1 != dtNull2, 0 );
+
+ iRc+= TestMethod( po, "dtNull1 < d2", dtNull1 < d2, 1 );
+ iRc+= TestMethod( po, "dtNull1 > d2", dtNull1 > d2, 0 );
+ iRc+= TestMethod( po, "dtNull1 <= d2", dtNull1 <= d2, 1 );
+ iRc+= TestMethod( po, "dtNull1 >= d2", dtNull1 >= d2, 0 );
+
+ iRc+= TestMethod( po, "d2 < dtNull1", d2 < dtNull1, 0 );
+ iRc+= TestMethod( po, "d2 > dtNull1", d2 > dtNull1, 1 );
+ iRc+= TestMethod( po, "d2 <= dtNull1", d2 <= dtNull1, 0 );
+ iRc+= TestMethod( po, "d2 >= dtNull1", d2 >= dtNull1, 1 );
+
+
+/*
+ xbDate d4( "20171015" );
+ std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n";
+ d4++;
+ std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n";
+ d4++;
+ std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n";
+ d4++;
+ std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n";
+ d4++;
+ std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n";
+ d4++;
+ std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n";
+ d4++;
+ std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n";
+ d4++;
+ std::cout << "day of [" << d4.Str() << "][" << d4.DayOf( XB_FMT_WEEK ) << "]\n";
+*/
+
+ 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_dbf_v3_memos.cpp b/src/tests/xb_test_dbf_v3_memos.cpp
new file mode 100755
index 0000000..7940c35
--- /dev/null
+++ b/src/tests/xb_test_dbf_v3_memos.cpp
@@ -0,0 +1,259 @@
+/* xb_test_xbdbf_v3_memos.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ #ifdef XB_LOCKING_SUPPORT
+ x.DisableDefaultAutoLock();
+ #endif // XB_LOCKING_SUPPORT
+
+ 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 );
+
+ xbString sDir;
+ V3Dbf.GetFileDirPart( sDir );
+ xbString sDbfName;
+ xbString sDbtName;
+ sDbfName.Sprintf( "%snewV3nm.DBF", sDir.Str());
+ sDbtName.Sprintf( "%snewV3nm.DBT", sDir.Str());
+
+ //std::cout << "remove [" << sDbfName.Str() << "]\n";
+ //std::cout << "remove [" << sDbtName.Str() << "]\n";
+
+ V3Dbf.xbRemove( sDbfName );
+ V3Dbf.xbRemove( sDbtName );
+
+ x.DisplayTableList();
+
+ rc += TestMethod( po, "Rename()", V3Dbf.Rename( "newV3nm.DBF" ), XB_NO_ERROR );
+ x.DisplayTableList();
+
+
+ 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..2dfeb7e
--- /dev/null
+++ b/src/tests/xb_test_dbf_v3_nomemos.cpp
@@ -0,0 +1,327 @@
+/* xb_test_xbdbf_v3_nomemos.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ #ifdef XB_LOCKING_SUPPORT
+ x.DisableDefaultAutoLock();
+ #endif // XB_LOCKING_SUPPORT
+
+ 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, should generate an error." );
+ 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..47a9053
--- /dev/null
+++ b/src/tests/xb_test_dbf_v4_memos.cpp
@@ -0,0 +1,357 @@
+/* xb_test_xbdbf_v4_memos.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ #ifdef XB_LOCKING_SUPPORT
+ x.DisableDefaultAutoLock();
+ #endif // XB_LOCKING_SUPPORT
+
+ 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 );
+
+ xbString sDir;
+ V4Dbf.GetFileDirPart( sDir );
+ xbString sDbfName;
+ xbString sDbtName;
+ sDbfName.Sprintf( "%snew4name.DBF", sDir.Str());
+ sDbtName.Sprintf( "%snew4name.DBT", sDir.Str());
+
+// std::cout << "remove [" << sDbfName.Str() << "\n";
+// std::cout << "remove [" << sDbtName.Str() << "\n";
+
+ V4Dbf.xbRemove( sDbfName );
+ V4Dbf.xbRemove( sDbtName );
+
+ x.DisplayTableList();
+
+ rc += TestMethod( po, "Rename()", V4Dbf.Rename( "new4name.DBF" ), XB_NO_ERROR );
+
+ x.DisplayTableList();
+
+
+ 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..dd4976d
--- /dev/null
+++ b/src/tests/xb_test_dbf_v4_nomemos.cpp
@@ -0,0 +1,373 @@
+/* xb_test_xbdbf_v4_nomemos.cpp
+
+XBase Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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 }
+ };
+
+ xbSchema MyV4ZipRecord[] =
+ {
+ { "ZIPCODE", XB_NUMERIC_FLD, 5, 0 },
+ { "CITY", XB_CHAR_FLD, 30, 0 },
+ { "STATE", XB_CHAR_FLD, 2, 0 },
+ { "",0,0,0 }
+ };
+
+
+ xbXBase x;
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ #ifdef XB_LOCKING_SUPPORT
+ x.DisableDefaultAutoLock();
+ #endif // XB_LOCKING_SUPPORT
+
+ 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 );
+
+ xbBool bIsNull;
+ rc += TestMethod( po, "GetNullSts()", V4Dbf.GetNullSts( V4Dbf.GetFieldNo("LASTNAME"), bIsNull, 0 ), XB_NO_ERROR );
+ rc += TestMethod( po, "GetNullSts()", bIsNull, xbTrue );
+
+
+
+ xbInt16 fldLastName = V4Dbf.GetFieldNo( "LASTNAME" );
+ rc += TestMethod( po, "PutField()", V4Dbf.PutField( fldLastName, "NELSON" ), XB_NO_ERROR );
+
+ rc += TestMethod( po, "GetNullSts()", V4Dbf.GetNullSts( V4Dbf.GetFieldNo("LASTNAME"), bIsNull, 0 ), XB_NO_ERROR );
+ rc += TestMethod( po, "GetNullSts()", bIsNull, xbFalse );
+
+ rc += TestMethod( po, "GetNullSts()", V4Dbf.GetNullSts( V4Dbf.GetFieldNo("FIRSTNAME"), bIsNull, 0 ), XB_NO_ERROR );
+ rc += TestMethod( po, "GetNullSts()", bIsNull, xbTrue );
+
+
+ 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 );
+
+
+ xbDbf4 V4DbfZ( &x ); // version 4 dbf file
+ rc2 = V4DbfZ.CreateTable( "TestV4Zip.DBF", "TestV4Zip", MyV4ZipRecord, XB_OVERLAY, XB_MULTI_USER );
+ rc += TestMethod( po, "CreateTable()", (xbInt32) rc2, XB_NO_ERROR );
+ if( rc2 )
+ x.DisplayError( rc2 );
+
+
+
+ rc += TestMethod( po, "Close()", V4Dbf.Close(), XB_NO_ERROR );
+ if( po == 2 )
+ x.DisplayTableList();
+
+
+ rc += TestMethod( po, "Close()", V4DbfZ.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..cbd79c9
--- /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,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+ 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..52c0b5c
--- /dev/null
+++ b/src/tests/xb_test_expression.cpp
@@ -0,0 +1,816 @@
+/* xb_test_expression.cpp
+
+XBase Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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 ){
+ #ifdef XB_DEBUG_SUPPORT
+ exp.DumpTree( xbTrue );
+ #endif // XB_DEBUG_SUPPORT
+ 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;
+ }
+}
+
+/**************************************************************************/
+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;
+ #ifdef XB_DEBUG_SUPPORT
+ exp.DumpTree( 1 );
+ #endif // XB_DEBUG_SUPPORT
+ 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;
+ #ifdef XB_DEBUG_SUPPORT
+ exp.DumpTree( 1 );
+ #endif // XB_DEBUG_SUPPORT
+ 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
+ #ifdef XB_DEBUG_SUPPORT
+ exp.DumpTree( 1 );
+ #endif // XB_DEBUG_SUPPORT
+ 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
+ #ifdef XB_DEBUG_SUPPORT
+ exp.DumpTree( 1 );
+ #endif // XB_DEBUG_SUPPORT
+ 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
+ #ifdef XB_DEBUG_SUPPORT
+ exp.DumpTree( 1 );
+ #endif //XB_DEBUG_SUPPORT
+ 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 },
+ { "NULLDATE1", XB_DATE_FLD, 8, 0 },
+ { "NULLDATE2", XB_DATE_FLD, 8, 0 },
+ { "NUM1", XB_NUMERIC_FLD, 9, 2 },
+ { "",0,0,0 }
+ };
+
+ xbXBase x;
+ xbDbf * MyFile;
+ xbDate d;
+ xbDate dtTest1( "19890303" );
+ xbDate dtTest2( "20120708" );
+
+
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+ 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( "ExpTest.DBF", "ExpTest", MyRecord, XB_OVERLAY, XB_MULTI_USER );
+
+ 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 );
+
+
+
+ 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 );
+ iRc += TestMethod( &x, MyFile, po, "FuncTest44", "CTOD( \"07\\08\\12\" )", dtTest2 );
+ xbDate dtToday;
+ dtToday.Sysdate();
+ 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( \"19890303\" )", dtTest1 );
+
+
+ // date logic tests
+
+ iRc += TestMethod( &x, MyFile, po, "DateTest1", "ExpTest->DATE1", dtTest1 );
+ iRc += TestMethod( &x, MyFile, po, "DateTest2", "DATE1", dtTest1 );
+ dtTest2.Set( "20120708" );
+ 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, "DateTest18", "ExpTest->NULLDATE1 = {07/06/2012}", (xbBool) xbFalse );
+ iRc += TestMethod( &x, MyFile, po, "DateTest19", "ExpTest->NULLDATE1 != {07/06/2012}", (xbBool) xbTrue );
+ iRc += TestMethod( &x, MyFile, po, "DateTest20", "ExpTest->NULLDATE1 < {07/06/2012}", (xbBool) xbTrue );
+ iRc += TestMethod( &x, MyFile, po, "DateTest21", "ExpTest->NULLDATE1 <= {07/06/2012}", (xbBool) xbTrue );
+ iRc += TestMethod( &x, MyFile, po, "DateTest22", "ExpTest->NULLDATE1 > {07/06/2012}", (xbBool) xbFalse );
+ iRc += TestMethod( &x, MyFile, po, "DateTest23", "ExpTest->NULLDATE1 >= {07/06/2012}", (xbBool) xbFalse );
+
+ iRc += TestMethod( &x, MyFile, po, "DateTest24", "{07/06/2012} = ExpTest->NULLDATE1", (xbBool) xbFalse );
+ iRc += TestMethod( &x, MyFile, po, "DateTest25", "{07/06/2012} != ExpTest->NULLDATE1", (xbBool) xbTrue );
+ iRc += TestMethod( &x, MyFile, po, "DateTest26", "{07/06/2012} < ExpTest->NULLDATE1", (xbBool) xbFalse );
+ iRc += TestMethod( &x, MyFile, po, "DateTest27", "{07/06/2012} <= ExpTest->NULLDATE1", (xbBool) xbFalse );
+ iRc += TestMethod( &x, MyFile, po, "DateTest28", "{07/06/2012} > ExpTest->NULLDATE1", (xbBool) xbTrue );
+ iRc += TestMethod( &x, MyFile, po, "DateTest29", "{07/06/2012} >= ExpTest->NULLDATE1", (xbBool) xbTrue );
+
+ iRc += TestMethod( &x, MyFile, po, "DateTest18", "ExpTest->NULLDATE1 = ExpTest->NULLDATE2", (xbBool) xbTrue );
+ iRc += TestMethod( &x, MyFile, po, "DateTest19", "ExpTest->NULLDATE1 != ExpTest->NULLDATE2", (xbBool) xbFalse );
+ iRc += TestMethod( &x, MyFile, po, "DateTest20", "ExpTest->NULLDATE1 < ExpTest->NULLDATE2", (xbBool) xbFalse );
+ iRc += TestMethod( &x, MyFile, po, "DateTest21", "ExpTest->NULLDATE1 <= ExpTest->NULLDATE2", (xbBool) xbTrue );
+ iRc += TestMethod( &x, MyFile, po, "DateTest22", "ExpTest->NULLDATE1 > ExpTest->NULLDATE2", (xbBool) xbFalse );
+ iRc += TestMethod( &x, MyFile, po, "DateTest23", "ExpTest->NULLDATE1 >= ExpTest->NULLDATE2", (xbBool) xbTrue );
+
+ // 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..0db6eca
--- /dev/null
+++ b/src/tests/xb_test_file.cpp
@@ -0,0 +1,209 @@
+/* xb_test_file.cpp
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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 iRc = 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;
+ }
+ xbXBase x;
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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
+
+ 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
+
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", f.GetDataDirectory(), sWrkStr, sWrkStr.Len());
+
+ f.SetFileName( "TestFile.txt" );
+ sWrkStr = "TestFile.txt";
+ iRc += TestMethod( iPo, "Set/GetFileName()", f.GetFileName(), sWrkStr, sWrkStr.Len());
+
+ f.GetFileType( sWrkStr );
+ iRc += TestMethod( iPo, "GetFileType()", sWrkStr, "TXT", 3 );
+
+ #ifdef WIN32
+ sWrkStr = "\\my\\directory\\";
+ #else
+ sWrkStr = "/my/directory/";
+ #endif
+
+ f.SetDirectory( sWrkStr );
+ iRc += TestMethod( iPo, "Set/GetDirectory()", f.GetDirectory(), sWrkStr, sWrkStr.Len());
+
+ sWrkStr += "TestFile.txt";
+ iRc += TestMethod( iPo, "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 );
+ iRc += TestMethod( iPo, "GetDirectory()", f.GetDirectory(), sWrkStr2, 16 );
+ iRc += TestMethod( iPo, "GetFileName()", f.GetFileName(), "myfile.dat", 10 );
+
+ iRc += TestMethod( iPo, "NameSuffixMissing()", f.NameSuffixMissing( "myfile.dbf", 1 ), 0 );
+ iRc += TestMethod( iPo, "NameSuffixMissing()", f.NameSuffixMissing( "myfile", 1 ), 1 );
+ iRc += TestMethod( iPo, "NameSuffixMissing()", f.NameSuffixMissing( "MYFILE", 1 ), 2 );
+
+ f.SetDirectory( PROJECT_DATA_DIR );
+ f.SetFileName( "xbfile.txt" );
+
+ iRc += TestMethod( iPo, "xbFopen()", f.xbFopen( "w+b", XB_MULTI_USER ), XB_NO_ERROR );
+
+ xbString sTest;
+ sTest = "Test Data";
+ iRc += TestMethod( iPo, "xbWrite()", f.xbFwrite( sTest.Str(), 9, 1 ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "xbFclose()", f.xbFclose(), XB_NO_ERROR );
+
+ iRc += TestMethod( iPo, "xbFopen()", f.xbFopen( "r+b", XB_MULTI_USER ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "xbFseek()", f.xbFseek( 0, SEEK_SET ), XB_NO_ERROR );
+ char buf[10];
+ for( int i = 0; i < 10; i++ )
+ buf[i] = 0x00;
+ iRc += TestMethod( iPo, "xbFread()", f.xbFread( buf, 5, 1 ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "xbFread()", buf, "Test ", 5 );
+
+ iRc += TestMethod( iPo, "xbFclose()", f.xbFclose(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "xbRemove()", f.xbRemove(), XB_NO_ERROR );
+ xbInt16 iWork = 100;
+ char cBuf[9];
+ char *p = cBuf;
+ f.ePutInt16( cBuf, iWork );
+ iRc += TestMethod( iPo, "Put/GetShort()", f.eGetInt16( p ), 100 );
+
+ xbInt32 lWork = 10101;
+ f.ePutInt32( p, lWork );
+ iRc += TestMethod( iPo, "Put/GetLong()", f.eGetInt32( p ), 10101 );
+
+ lWork = 2147483647;
+ f.ePutInt32( p, lWork );
+ iRc += TestMethod( iPo, "Put/GetLong()", f.eGetInt32( p ), 2147483647 );
+ iRc += TestMethod( iPo, "Put/GetLong()", (xbInt32) f.eGetUInt32( p ), 2147483647 );
+
+ xbDouble d = 123456.789;
+ f.ePutDouble( p, d );
+ iRc += TestMethod( iPo, "Put/GetDouble()", f.eGetDouble( p ), 123456.789 );
+
+ xbString sFqnS;
+ xbString sFqnT;
+ xbFile f2( &x );
+ iRc += TestMethod( iPo, "CreateUniqueFileName()", f2.CreateUniqueFileName( PROJECT_DATA_DIR, "dbf", sFqnS ), XB_NO_ERROR );
+
+ iRc += TestMethod( iPo, "FileExists()", f2.FileExists( sFqnS ), xbFalse );
+ iRc += TestMethod( iPo, "xbFopen()", f2.xbFopen( "w+b", sFqnS, XB_SINGLE_USER ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "xbFclose()", f2.xbFclose(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "FileExists()", f2.FileExists( sFqnS ), xbTrue );
+
+ iRc += TestMethod( iPo, "CreateUniqueFileName()", f2.CreateUniqueFileName( PROJECT_DATA_DIR, "dbf", sFqnT ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "xbRename()", f2.xbRename( sFqnS, sFqnT ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "xbRemove()", f.xbRemove( sFqnT ), XB_NO_ERROR );
+
+ xbString sFn;
+ iRc += TestMethod( iPo, "GetFileNamePart()", f2.GetFileNamePart( sFqnS , sFn ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetFileExtPart()", f2.GetFileExtPart( sFqnS , sFn ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetFileExtPart()", f2.GetFileDirPart( sFqnS , sFn ), XB_NO_ERROR );
+
+ iRc += TestMethod( iPo, "SetBlockSize()", f.SetBlockSize( 100 ), XB_INVALID_BLOCK_SIZE );
+ iRc += TestMethod( iPo, "SetBlockSize()", f.SetBlockSize( 512 ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetBlockSize()", (xbInt32) f.GetBlockSize(), 512 );
+
+ char BlockBuf[513];
+ memset( BlockBuf, 0x00, 513 );
+ iRc += TestMethod( iPo, "xbFopen()", f.xbFopen( "w+b", XB_SINGLE_USER ), XB_NO_ERROR );
+
+ for( int i = 0; i < 512; i++ )
+ BlockBuf[i] = 'A';
+ iRc += TestMethod( iPo, "WriteBlock()", f.WriteBlock( 0L, 512, BlockBuf ), XB_NO_ERROR );
+
+ for( int i = 0; i < 512; i++ )
+ BlockBuf[i] = 'B';
+ iRc += TestMethod( iPo, "WriteBlock()", f.WriteBlock( 1L, 512, BlockBuf ), XB_NO_ERROR );
+
+ for( int i = 0; i < 512; i++ )
+ BlockBuf[i] = 'C';
+ iRc += TestMethod( iPo, "WriteBlock()", f.WriteBlock( 2L, 512, BlockBuf ), XB_NO_ERROR );
+
+ char BlockBuf2[513];
+ memset( BlockBuf2, 0x00, 513 );
+ iRc += TestMethod( iPo, "ReadBlock()", f.ReadBlock( 2L, 512, BlockBuf2 ), XB_NO_ERROR );
+
+ xbString s1 = BlockBuf;
+ xbString s2 = BlockBuf2;
+
+ iRc += TestMethod( iPo, "ReadBlock()", s1, s2, 512 );
+ iRc += TestMethod( iPo, "xbTruncate()", f.xbTruncate( 1000 ), XB_NO_ERROR );
+
+ xbUInt64 ullFsize;
+ iRc += TestMethod( iPo, "GetFileSize()", f.GetFileSize( ullFsize ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "xbGetFileSize()", (xbInt32) ullFsize, 1000 );
+ iRc += TestMethod( iPo, "xbFclose()", f.xbFclose(), XB_NO_ERROR );
+
+
+ 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_filter.cpp b/src/tests/xb_test_filter.cpp
new file mode 100755
index 0000000..000e196
--- /dev/null
+++ b/src/tests/xb_test_filter.cpp
@@ -0,0 +1,193 @@
+/* xb_test_filter.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2020,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ char c;
+ xbString s;
+ xbInt32 lRecCnt = 0;
+ iRc = 0;
+
+ if( iPo > 0 )
+ std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl;
+
+ #ifdef XB_DBF4_SUPPORT
+ xbDbf *MyFile = new xbDbf4( &x );
+ #else
+ xbDbf *MyFile = new xbDbf3( &x );
+ #endif
+
+ iRc2 = MyFile->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 = MyFile->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 );
+ MyFile->BlankRecord();
+ MyFile->PutField( "CFLD", s );
+ MyFile->PutLongField( "NFLD", ++lRecCnt );
+ iRc = MyFile->AppendRecord();
+ }
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ iRc += TestMethod( iPo, "SetCurTag()", MyFile->SetCurTag( "" ), XB_NO_ERROR );
+ #endif // XB_INDEX_SUPPORT
+
+ xbFilter f1( MyFile );
+ xbString sMyFilterExpression = "LEFT( CFLD, 2 ) = 'YY'";
+ iRc += TestMethod( iPo, "Set()", f1.Set( sMyFilterExpression ), XB_NO_ERROR );
+
+ #ifdef XB_INDEX_SUPPORT
+ iRc += TestMethod( iPo, "SetCurTag()", MyFile->SetCurTag( "" ), XB_NO_ERROR );
+ #endif // XB_INDEX_SUPPORT
+
+ iRc += TestMethod( iPo, "Set()", f1.Set( sMyFilterExpression ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetFirstRecord()", f1.GetFirstRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 28 );
+ iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 54 );
+ iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 80 );
+ iRc += TestMethod( iPo, "GetLast()", f1.GetLastRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 106 );
+ iRc += TestMethod( iPo, "GetPrev()", f1.GetPrevRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->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) MyFile->GetCurRecNo(), 50 );
+ iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 76 );
+ iRc += TestMethod( iPo, "GetNextRecord()", f1.GetNextRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 102 );
+ iRc += TestMethod( iPo, "GetLast()", f1.GetLastRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 128 );
+ iRc += TestMethod( iPo, "GetPrev()", f1.GetPrevRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 102 );
+
+
+ #ifdef XB_NDX_SUPPORT
+ f1.SetLimit( 0 );
+
+ // change things up a bit
+ iRc += TestMethod( iPo, "GetRecord()", MyFile->GetRecord( 32 ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "PutField()", MyFile->PutField( "ZFLD", "Z" ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetRecord()", MyFile->GetRecord( 52 ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "PutField()", MyFile->PutField( "ZFLD", "Z" ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetRecord()", MyFile->GetRecord( 76 ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "PutField()", MyFile->PutField( "ZFLD", "Z" ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetRecord()", MyFile->GetRecord( 103 ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "PutField()", MyFile->PutField( "ZFLD", "Z" ), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "Commit()", MyFile->Commit(), XB_NO_ERROR );
+
+ iRc += TestMethod( iPo, "SetCurTag()", MyFile->SetCurTag( "TestFilt" ), XB_NO_ERROR );
+ sMyFilterExpression = "ZFLD = 'Z'";
+ iRc += TestMethod( iPo, "Set()", f1.Set( sMyFilterExpression ), XB_NO_ERROR );
+
+ iRc += TestMethod( iPo, "GetFirstRecordIx()", f1.GetFirstRecordIx(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 52 );
+ iRc += TestMethod( iPo, "GetNextRecordIx()", f1.GetNextRecordIx(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 103 );
+ iRc += TestMethod( iPo, "GetNextRecordIx()", f1.GetNextRecordIx(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 76 );
+ iRc += TestMethod( iPo, "GetLastRecordIx()", f1.GetLastRecordIx(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 32 );
+ iRc += TestMethod( iPo, "GetPrevRecordIx()", f1.GetPrevRecordIx(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "GetCurRecNo()", (xbInt32) MyFile->GetCurRecNo(), 76 );
+
+ #endif // XB_NDX_SUPPORT
+
+
+ iRc += TestMethod( iPo, "Close()", MyFile->Close(), XB_NO_ERROR );
+ delete MyFile;
+
+ 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..7eb8b16
--- /dev/null
+++ b/src/tests/xb_test_funcs.cpp
@@ -0,0 +1,296 @@
+/* xb_test_funcs.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ #ifdef XB_LOCKING_SUPPORT
+ MyFile->SetAutoLock( xbFalse );
+ #endif // XB_LOCKING_SUPPORT
+
+
+ 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 );
+
+ d.Sysdate();
+ 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..288ee99
--- /dev/null
+++ b/src/tests/xb_test_linklist.cpp
@@ -0,0 +1,342 @@
+/* xb_test_linklist.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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<xbString> 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<xbString> * 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<xbString> 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<xbString> * 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<xbUInt32> 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<xbUInt32> ll32;
+ // xbLinkListNode<xbUInt32> * 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..2df71a8
--- /dev/null
+++ b/src/tests/xb_test_lock.cpp
@@ -0,0 +1,1066 @@
+/* xb_test_lock.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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;
+ xbString sLockFile2;
+ 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+ 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 );
+
+ #ifdef XB_MDX_SUPPORT
+ xbIx *ixPtr;
+ void *pTag;
+ iRc2 = MyFile->CreateTag( "MDX", "LockTag", "LOCKTEST", "", 0, 0, XB_OVERLAY, &ixPtr, &pTag );
+ iRc += TestMethod( po, "CreateTag()", iRc2, XB_NO_ERROR );
+ #endif // XB_MDX_SUPPORT
+
+
+ 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 );
+
+ sLockFile.Sprintf( "%slocktest.txt", PROJECT_DATA_DIR );
+ // std::cout << "xb_test_lock - lockfile = [ " << sLockFile.Str() << "]\n";
+
+ remove( sLockFile.Str() );
+
+ #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
+
+ } else if( sLockCmd == "IL" ){
+ // index lock
+ #ifdef XB_MDX_SUPPORT
+ if(( iRcChld = MyFileChld->LockIndices( XB_LOCK )) != XB_NO_ERROR )
+ sResult = "FAIL";
+ else
+ sResult = "OK";
+ #else
+ sLockCmd = "OK";
+ #endif // XB_MDX_SUPPORT
+
+ } else if( sLockCmd == "IU" ){
+ // index unlock
+ #ifdef XB_MDX_SUPPORT
+ if(( iRcChld = MyFileChld->LockIndices( XB_UNLOCK )) != XB_NO_ERROR )
+ sResult = "FAIL";
+ else
+ sResult = "OK";
+ #else
+ sLockCmd = "OK";
+ #endif // XB_MDX_SUPPORT
+ }
+
+
+ #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;
+
+ remove( sLockFile );
+
+ 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 = 100;
+ 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 = 110;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ // attempt to lock table, should fail
+ if(( iRc2 = MyFile->Open( "LockTest.DBF" )) != XB_NO_ERROR ){
+ iErrorStop = 120;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockTable( XB_LOCK )) == XB_NO_ERROR ){
+ iErrorStop = 130;
+ 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 = 140;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 150;
+ throw iRc2;
+ }
+ if( po > 0 )
+ std::cout << "[PASS] LockTable Test 2\n";
+
+ if(( iRc2 = MyFile->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 160;
+ 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 = 170;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) == XB_NO_ERROR ){
+ iErrorStop = 180;
+ 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 = 190;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 200;
+ throw iRc2;
+ }
+
+ std::cout << "[PASS] LockRecord Test 2\n";
+ if(( iRc2 = MyFile->LockRecord( XB_UNLOCK, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 210;
+ 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 // XB_LOGGING_SUPPORT
+
+ 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 // XB_LOGGING_SUPPORT
+
+ if( sResult != "OK" ){
+ iErrorStop = 220;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockMemo( XB_LOCK )) == XB_NO_ERROR ){
+ iErrorStop = 230;
+ 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 // XB_LOGGING_SUPPORT
+
+ 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 // XB_LOGGING_SUPPORT
+
+ if( sResult != "OK" ){
+ iErrorStop = 240;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockMemo( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 250;
+ throw iRc2;
+ }
+
+ std::cout << "[PASS] LockMemo Test 2\n";
+ if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 260;
+ throw iRc2;
+ }
+ #endif // XB_MEMO_SUPPORT
+
+ // index lock
+ #ifdef XB_MDX_SUPPORT
+ #ifdef XB_LOGGING_SUPPORT
+ sMsg.Sprintf( "Program [%s] Parent task issuing IL command", av[0] );
+ x.WriteLogMessage( sMsg );
+ #endif // XB_LOGGING_SUPPORT
+
+ sLockCmd = "IL";
+ 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 // XB_LOGGING_SUPPORT
+
+ if( sResult != "OK" ){
+ iErrorStop = 270;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockIndices( XB_LOCK )) == XB_NO_ERROR ){
+ iErrorStop = 280;
+ throw iRc2;
+ }
+ if( po > 0 )
+ std::cout << "[PASS] LockIndex Test 1\n";
+
+ // memo unlock
+ #ifdef XB_LOGGING_SUPPORT
+ sMsg.Sprintf( "Program [%s] Parent task issuing IU command", av[0] );
+ x.WriteLogMessage( sMsg );
+ #endif // XB_LOGGING_SUPPORT
+
+ sLockCmd = "IU";
+ 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 // XB_LOGGING_SUPPORT
+
+ if( sResult != "OK" ){
+ iErrorStop = 290;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockIndices( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 300;
+ throw iRc2;
+ }
+
+ std::cout << "[PASS] LockIndex Test 2\n";
+ if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 310;
+ throw iRc2;
+ }
+ #endif // XB_MDX_SUPPORT
+
+
+
+ // 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( 300 );
+ 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( 310 );
+ 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 = 320;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ // attempt to lock table, should fail
+ if(( iRc2 = MyFile->Open( "LockTest.DBF" )) != XB_NO_ERROR ){
+ iErrorStop = 330;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockTable( XB_LOCK )) == XB_NO_ERROR ){
+ iErrorStop = 340;
+ 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 = 350;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockTable( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 360;
+ 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 = 370;
+ 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( 380 );
+ 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 = 390;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) == XB_NO_ERROR ){
+ iErrorStop = 400;
+ 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 = 410;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockRecord( XB_LOCK, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 420;
+ throw iRc2;
+ }
+
+ std::cout << "[PASS] LockRecord Test 2\n";
+ if(( iRc2 = MyFile->LockRecord( XB_UNLOCK, 1 )) != XB_NO_ERROR ){
+ iErrorStop = 430;
+ 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 = 440;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockMemo( XB_LOCK )) == XB_NO_ERROR ){
+ iErrorStop = 450;
+ 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 = 460;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockMemo( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 470;
+ throw iRc2;
+ }
+
+ std::cout << "[PASS] LockMemo Test 2\n";
+ if(( iRc2 = MyFile->LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 480;
+ throw iRc2;
+ }
+ #endif
+
+ // index lock
+ #ifdef XB_MDX_SUPPORT
+ #ifdef XB_LOGGING_SUPPORT
+ sMsg.Sprintf( "Program [%s] Parent task issuing IL command", av[0] );
+ x.WriteLogMessage( sMsg );
+ #endif
+
+ sLockCmd = "IL";
+ 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( 490 );
+ 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 = 500;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockIndices( XB_LOCK )) == XB_NO_ERROR ){
+ iErrorStop = 510;
+ throw iRc2;
+ }
+ if( po > 0 )
+ std::cout << "[PASS] LockIndex Test 1\n";
+
+ // index unlock
+ #ifdef XB_LOGGING_SUPPORT
+ sMsg.Sprintf( "Program [%s] Parent task issuing IU command", av[0] );
+ x.WriteLogMessage( sMsg );
+ #endif
+
+ sLockCmd = "IU";
+ 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 = 520;
+ iRc2 = -1;
+ throw iRc2;
+ }
+
+ if(( iRc2 = MyFile->LockIndices( XB_LOCK )) != XB_NO_ERROR ){
+ iErrorStop = 530;
+ throw iRc2;
+ }
+
+ std::cout << "[PASS] LockIndex Test 2\n";
+ if(( iRc2 = MyFile->LockIndices( XB_UNLOCK )) != XB_NO_ERROR ){
+ iErrorStop = 540;
+ 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..82b4dea
--- /dev/null
+++ b/src/tests/xb_test_lock2.cpp
@@ -0,0 +1,233 @@
+/* xb_test_lock2.cpp
+
+XBase Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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;
+ xbString sLockFile;
+ 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
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+ InitTime();
+
+ sLockFile.Sprintf( "%slocktest.txt", PROJECT_DATA_DIR );
+ // std::cout << "xb_test_lock2 - lockfile = [ " << sLockFile.Str() << "]\n";
+
+ #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
+
+ } else if( sLockCmd == "IL" ){
+ // index lock
+ #ifdef XB_MDX_SUPPORT
+ if(( iRc2 = MyFile->LockIndices( XB_LOCK )) != XB_NO_ERROR )
+ sResult = "FAIL";
+ else
+ sResult = "OK";
+ #else
+ sLockCmd = "OK";
+ #endif
+
+ } else if( sLockCmd == "IU" ){
+ // index unlock
+ #ifdef XB_MDX_SUPPORT
+ if(( iRc2 = MyFile->LockIndices( 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.Str() );
+ #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( (xbUInt32) iRc );
+}
+
diff --git a/src/tests/xb_test_log.cpp b/src/tests/xb_test_log.cpp
new file mode 100755
index 0000000..a586809
--- /dev/null
+++ b/src/tests/xb_test_log.cpp
@@ -0,0 +1,111 @@
+/* xb_test_log.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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 );
+
+ // verify first logfile location is correct
+ xbString sWork;
+ x.GetHomeDir( sWork );
+ std::cout << "Home Dir = " << sWork.Str() << std::endl;
+
+ // verify home directory > 0 length
+ xbInt32 iLen = (xbInt32) sWork.Len();
+ if( sWork.Len() == 0 )
+ rc += TestMethod( po, "GetHomeDir()", iLen, 1 );
+ else
+ rc += TestMethod( po, "GetHomeDir()", iLen, iLen );
+
+ sWork.Sprintf( "%s%cxbase64%clogs", sWork.Str(),
+ x.GetPathSeparator(), x.GetPathSeparator());
+
+ // verify path exists
+ // std::cout << "Home Dir = " << sHomeDir.Str() << std::endl;
+ xbFile f( &x );
+ rc += TestMethod( po, "FileExists()", f.FileExists( sWork ), xbTrue );
+
+
+ sWork.Sprintf( "%s%c%s", sWork.Str(),
+ x.GetPathSeparator(), x.GetLogFileName().Str() );
+
+ // std::cout << "Home Dir = " << sWork.Str() << std::endl;
+ rc += TestMethod( po, "FileExists()", f.FileExists( sWork ), xbTrue );
+ rc += TestMethod( po, "Get Log Status()", x.GetLogStatus(), xbTrue );
+
+
+ x.DisableMsgLogging();
+ rc += TestMethod( po, "Get Log Status()", x.GetLogStatus(), xbFalse );
+
+ xbString sNewLogFileName = "Logfile2.txt";
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ x.SetLogFileName( sNewLogFileName );
+ sWork.Sprintf( "%s%c%s",
+ PROJECT_LOG_DIR, x.GetPathSeparator(), sNewLogFileName.Str());
+
+ sMsg.Sprintf( "Switching to logfile [%s]", sWork.Str() );
+ x.EnableMsgLogging();
+ rc += TestMethod( po, "Get Log Status()", x.GetLogStatus(), xbTrue );
+ rc += TestMethod( po, "FileExists()", f.FileExists( sWork ), xbTrue );
+
+ x.WriteLogMessage( sMsg );
+ rc += TestMethod( po, "Get Log Status()", x.GetLogStatus(), xbTrue );
+ x.WriteLogMessage( "Test Log Message" );
+
+ 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..680be5c
--- /dev/null
+++ b/src/tests/xb_test_mdx.cpp
@@ -0,0 +1,305 @@
+/* xb_test_mdx.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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 }
+ };
+
+
+ xbSchema MyV4Record2[] =
+ {
+ { "CHAR1", XB_CHAR_FLD, 1, 0 },
+ { "CHAR27", XB_CHAR_FLD, 27, 0 },
+ { "",0,0,0 }
+ };
+
+
+ xbXBase x;
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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.SetMultiUser( xbFalse );
+ 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";
+
+
+ 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(1)", 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(2)", 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 );
+
+ // next check throws an error message on the display, that is what it is testing, don't need to debug it
+ iRc += TestMethod( iPo, "CheckTagIntegrity(3)", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_INVALID_INDEX );
+
+
+ iRc2 = V4DbfX1->Reindex( 0 );
+ iRc += TestMethod( iPo, "Reindex( 0 )", iRc2, XB_NO_ERROR );
+ iRc += TestMethod( iPo, "CheckTagIntegrity(4)", 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(5)", 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(6)", 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(7)", 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 );
+
+ xbString sDir;
+ V4DbfX1->GetFileDirPart( sDir );
+ xbString sDbfName;
+ xbString sMdxName;
+ sDbfName.Sprintf( "%sTestMdxR.DBF", sDir.Str());
+ sMdxName.Sprintf( "%sTestMdxR.MDX", sDir.Str());
+ V4DbfX1->xbRemove( sDbfName );
+ V4DbfX1->xbRemove( sMdxName );
+
+ iRc += TestMethod( iPo, "Rename()", V4DbfX1->Rename( "TestMdxR.DBF" ), XB_NO_ERROR );
+
+
+ xbDbf *V4DbfX2 = new xbDbf4( &x );
+ iRc2 = V4DbfX2->CreateTable( "TMDXDB02.DBF", "TestMdxX4", MyV4Record2, XB_OVERLAY, XB_MULTI_USER );
+ iRc += TestMethod( iPo, "CreateTable(2)", iRc2, 0 );
+
+
+ iRc2 = V4DbfX2->CreateTag( "MDX", "TAG1", "CHAR27", ".NOT. DELETED()", 0, 0, XB_OVERLAY, &pIx, &pTag );
+// iRc2 = V4DbfX2->CreateTag( "MDX", "TAG2", "CHAR1", "CHAR1 = 'O' .AND. .NOT. DELETED()", 0, 0, XB_OVERLAY, &pIx, &pTag );
+ iRc += TestMethod( iPo, "CreateTag(4)", iRc2, 0 );
+
+
+
+ for( xbUInt32 ul = 0; ul < 500 && iRc2 == XB_NO_ERROR; ul++ ){
+ c = 'O';
+ V4DbfX2->BlankRecord();
+ iRc2 = V4DbfX2->PutField( 1, c );
+ if( iRc2 != XB_NO_ERROR ){
+ iRc += TestMethod( iPo, "PutField()", iRc2, XB_NO_ERROR );
+ } else {
+ iRc2 = V4DbfX2->AppendRecord();
+ if( iRc2 != XB_NO_ERROR ){
+ iRc += TestMethod( iPo, "AppendRecord()", iRc2, XB_NO_ERROR );
+ } else {
+ iRc2 = V4DbfX2->Commit();
+ if( iRc2 != XB_NO_ERROR ){
+ iRc += TestMethod( iPo, "Commit()", iRc2, XB_NO_ERROR );
+ }
+ }
+ }
+ }
+
+ iRc += TestMethod( iPo, "CheckTagIntegrity(7)", V4DbfX2->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR );
+
+
+
+ iRc2 = V4DbfX2->CreateTag( "MDX", "TAG2", "CHAR27", ".NOT. DELETED()", 0, 1, XB_OVERLAY, &pIx, &pTag );
+ iRc += TestMethod( iPo, "CreateTag(5)", iRc2, 0 );
+
+ iRc2 += V4DbfX2->Reindex( 1 );
+ iRc += TestMethod( iPo, "Reindex()", iRc2, XB_KEY_NOT_UNIQUE );
+
+
+ x.CloseAllTables();
+// delete V4DbfX1;
+// delete V4DbfX2;
+
+ 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..320a1c9
--- /dev/null
+++ b/src/tests/xb_test_ndx.cpp
@@ -0,0 +1,450 @@
+/* xb_test_ndx.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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;
+ xbString sMsg;
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ x.EnableMsgLogging();
+ if( iPo ){
+ std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl;
+ }
+ sMsg.Sprintf( "Program [%s] initializing...", av[0] );
+ x.WriteLogMessage( sMsg );
+ x.SetLogSize( 1000000 );
+ #endif
+
+ x.SetDataDirectory( PROJECT_DATA_DIR );
+ x.SetMultiUser( xbFalse );
+
+ 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 );
+
+ xbInt16 iTagCnt = ixPtr->GetTagCount();
+ iRc += TestMethod( iPo, "GetTagCount()", (xbInt32) iTagCnt, 1 );
+
+ xbString sTagName;
+ sTagName = ixPtr->GetTagName( &ndx );
+ iRc += TestMethod( iPo, "GetTagName()", sTagName.Str(), "TestNdxN", 8 );
+
+
+ #ifdef XB_LOCKING_SUPPORT
+ iRc += TestMethod( iPo, "LockTable()", V3Dbf->LockTable( XB_LOCK ), XB_NO_ERROR );
+ #endif
+
+ 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 );
+
+ 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;
+ 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(), 0 ), XB_NO_ERROR );
+ ixl = ixl->next;
+ }
+ }
+
+ #ifdef XB_LOCKING_SUPPORT
+ iRc += TestMethod( iPo, "LockTable()", V3Dbf->LockTable( XB_UNLOCK ), XB_NO_ERROR );
+ #endif
+
+ xbString sDir;
+ V3Dbf->GetFileDirPart( sDir );
+ xbString sDbfName;
+ xbString sInfName;
+ sDbfName.Sprintf( "%sTestNdxR.DBF", sDir.Str());
+ sInfName.Sprintf( "%sTestNdxR.INF", sDir.Str());
+
+ V3Dbf->xbRemove( sDbfName );
+ V3Dbf->xbRemove( sInfName );
+
+ iRc += TestMethod( iPo, "Rename()", V3Dbf->Rename( "TestNdxR.DBF" ), XB_NO_ERROR );
+
+ iRc += TestMethod( iPo, "DeleteTable()", V3Dbf->DeleteTable(), XB_NO_ERROR );
+ // iRc += TestMethod( iPo, "Close()", V3Dbf->Close(), XB_NO_ERROR ); // did a delete instead of close
+
+ delete V3Dbf;
+
+ x.SetUniqueKeyOpt( XB_EMULATE_DBASE );
+ V3Dbf = new xbDbf3( &x );
+
+ 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, xbTrue, 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 );
+
+
+ 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 );
+
+ iRc += TestMethod( iPo, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "CFLD", "BBB" ), 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 );
+
+ iRc += TestMethod( iPo, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "CFLD", "BBB" ), 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 );
+
+
+ ixl = V3Dbf->GetIxList();
+ 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(), 0 ), XB_NO_ERROR );
+ }
+ ixl = ixl->next;
+ }
+
+ iRc2 = V3Dbf->Reindex( 1 ); // reindex all tags
+ iRc += TestMethod( iPo, "Reindex()", (xbInt32) iRc2, XB_NO_ERROR );
+
+ iRc += TestMethod( iPo, "DeleteTable()", V3Dbf->DeleteTable(), XB_NO_ERROR );
+
+
+
+ // test tag delete on unsuccessful reindex
+
+ iRc2 = V3Dbf->CreateTable( "TestNdx.DBF", "TestNdx", MyV3Record, XB_OVERLAY, XB_MULTI_USER );
+ iRc += TestMethod( iPo, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR );
+
+ 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 );
+
+ iRc += TestMethod( iPo, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "CFLD", "BBB" ), 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 );
+
+ iRc += TestMethod( iPo, "BlankRecord()", V3Dbf->BlankRecord(), XB_NO_ERROR );
+ iRc += TestMethod( iPo, "Putfield()", V3Dbf->PutField( "CFLD", "BBB" ), 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 );
+
+ iRc2 = V3Dbf->Reindex( 1 ); // verify reindex works with no tags
+ iRc += TestMethod( iPo, "Reindex()", (xbInt32) iRc2, XB_NO_ERROR );
+
+
+ x.SetUniqueKeyOpt( XB_HALT_ON_DUPKEY );
+ iRc2 = V3Dbf->CreateTag( "NDX", "TestNdxX.NDX", "CFLD", "", 0, 1, XB_OVERLAY, &ixPtr, &ndx );
+ iRc += TestMethod( iPo, "CreateTag()", (xbInt32) iRc2, XB_NO_ERROR );
+
+ iRc2 = V3Dbf->Reindex( 1 ); // verify reindex fails with dup key
+ iRc += TestMethod( iPo, "Reindex()", (xbInt32) iRc2, XB_KEY_NOT_UNIQUE );
+
+ x.CloseAllTables();
+// 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_sql.cpp b/src/tests/xb_test_sql.cpp
new file mode 100755
index 0000000..3341895
--- /dev/null
+++ b/src/tests/xb_test_sql.cpp
@@ -0,0 +1,270 @@
+/* xb_test_sql.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014, 2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ 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 */
+
+ xbString sSql;
+
+ if( argCnt > 1 ) {
+ if( av[1][0] == 'Q' )
+ po = 0;
+ else if( av[1][0] == 'V' )
+ po = 2;
+ }
+
+/*
+ xbSchema MyAddressRecord[] =
+ {
+ { "ADDRESS", XB_CHAR_FLD, 30, 0 },
+ { "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 }
+ };
+
+ above structure corresponds to sql below
+
+ sSql = "CREATE TABLE Address.DBF ( ADDRESS CHAR(30), CITY CHAR(30), STATE CHAR(2), ZIPCODE NUMERIC(9,0), NOTES VARCHAR, LASTUPDATE DATE, ACTIVE LOGICAL )";
+*/
+
+/*
+ xbSchema MyZipRecord[] =
+ {
+ { "ZIPCODE", XB_NUMERIC_FLD, 9, 0 },
+ { "CITY", XB_CHAR_FLD, 30, 0 },
+ { "STATE", XB_CHAR_FLD, 2, 0 },
+ { "",0,0,0 }
+ };
+*/
+
+ xbXBase x;
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ xbSql sql( &x );
+
+ if( po > 0 )
+ std::cout << "Default Data Directory is [" << x.GetDataDirectory().Str() << "]" << std::endl;
+
+/*
+ // verify a delete on a non existant table doesn't crash things
+ sSql = "DROP TABLE IF EXISTS NoTable.DBF";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "DROP TABLE IF EXISTS Address.DBF";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "CREATE TABLE Address.DBF ( ADDRESS CHAR(30), CITY CHAR(30), STATE CHAR(2), ZIPCODE NUMERIC(9,0), NOTES VARCHAR, LASTUPDATE DATE, ACTIVE LOGICAL )";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ #ifdef XB_MDX_SUPPORT
+ sSql = "CREATE INDEX tag1 ON Address.DBF( CITY, STATE, DTOS( LASTUPDATE )) FILTER .NOT. DELETED()";
+ // xbString sSql = "CREATE INDEX tag1 ON Address.DBF( CITY, STATE )";
+
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "SqL CreateIndex()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+ #endif // XB_MDX_SUPPORT
+
+ sSql = "INSERT INTO Address (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, "SqlInsert()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "INSERT INTO Address (CITY, STATE, ZIPCODE, NOTES, LASTUPDATE, ACTIVE ) VALUES ( 'Dallas', 'TX', 76248, 'Dallas is hot in the summer', '1989-02-09', 'N')";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "SqlInsert()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "DROP TABLE IF EXISTS AddressR.DBF";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "ALTER TABLE Address.DBF RENAME TO AddressR.DBF";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "SqlAlterTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "DELETE FROM AddressR.DBF WHERE CITY='San Diego'";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "SqlDelete()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "UNDELETE FROM AddressR.DBF WHERE CITY='San Diego'";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "SqlUndelete()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "DELETE FROM AddressR.DBF";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "SqlDelete()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "UNDELETE FROM AddressR.DBF";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "SqlDelete()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "DELETE FROM AddressR.DBF WHERE BAD='EXPRESSION'";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "SqlDelete()", (xbInt32) iRc2, XB_INVALID_FIELD_NAME );
+
+
+ sSql = "DROP TABLE IF EXISTS AddressR.DBF";
+ iRc += TestMethod( po, "Drop Table()", sql.ExecuteNonQuery( sSql ), XB_NO_ERROR );
+ iRc += TestMethod( po, "Drop Table()", sql.ExecuteNonQuery( sSql ), XB_NO_ERROR );
+
+*/
+
+
+ sSql = "DROP TABLE IF EXISTS ZipCode.DBF";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+
+ sSql = "CREATE TABLE ZipCode.DBF ( ZIPCODE NUMERIC(9,0), CITY CHAR(30), STATE CHAR(2) )";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+
+ sSql = "INSERT INTO ZipCode.DBF ( ZIPCODE, CITY, STATE ) VALUES ( 75087, 'Rockwall', 'TX' )";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+
+ sSql = "INSERT INTO ZipCode.DBF ( ZIPCODE, CITY, STATE ) VALUES ( 75087, 'Rockwall', 'TX' )";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+
+
+
+/*
+
+ sSql = "CREATE INDEX ZipCode1.NDX ON ZipCode.DBF( ZIPCODE ) ASSOCIATE";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+ iRc += TestMethod( po, "Create Index()", (xbInt32) iRc2, XB_NO_ERROR );
+*/
+
+
+ sSql = "CREATE UNIQUE INDEX ZipCode2.NDX ON ZipCode.DBF( ZIPCODE ) ASSOCIATE";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "Create Index()", (xbInt32) iRc2, XB_KEY_NOT_UNIQUE );
+
+
+
+/*
+ sSql = "DROP TABLE IF EXISTS ZipCode.DBF";
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "DropTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+*/
+
+
+// sSql = "INSERT INTO ZipCode.DBF ( ZIPCODE, CITY, STATE ) VALUES ( 75087, 'Rockwall', 'TX' )";
+// iRc2 = sql.ExecuteNonQuery( sSql );
+
+
+ x.DisplayTableList();
+
+
+
+
+/*
+ sSql = "CREATE INDEX ZipCode.NDX ON Addres.DBF( ZIPCODE )";
+
+ iRc2 = sql.ExecuteNonQuery( sSql );
+ iRc += TestMethod( po, "CreateTable()", (xbInt32) iRc2, XB_NO_ERROR );
+ if( iRc2 )
+ x.DisplayError( iRc2 );
+*/
+
+// sSql = "DROP TABLE IF EXISTS AddressR.DBF";
+// iRc += TestMethod( po, "Drop Table()", sqlQry1.ExecuteQuery( sSql ), XB_NO_ERROR );
+
+
+
+
+ // x.DisplayTableList();
+
+ x.CloseAllTables();
+
+ 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;
+}
+
+ \ No newline at end of file
diff --git a/src/tests/xb_test_string.cpp b/src/tests/xb_test_string.cpp
new file mode 100755
index 0000000..9dd48af
--- /dev/null
+++ b/src/tests/xb_test_string.cpp
@@ -0,0 +1,459 @@
+/* xb_test_string.cpp
+
+XBase63 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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;
+ xbString sMsg;
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ x.EnableMsgLogging();
+ if( po ){
+ 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 );
+
+ 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 );
+
+ xbString sAppend1;
+ sAppend1.Append( "abc123", 6 );
+ rc += TestMethod( po, "Append", sAppend1, "abc123", 6 );
+
+ xbString sAppend2;
+ sAppend2.Append( "abc123", 8 );
+ rc += TestMethod( po, "Append", sAppend2, "abc123", 6 );
+
+ xbString sAppend3;
+ sAppend3.Append( "abc123", 3 );
+ rc += TestMethod( po, "Append", sAppend3, "abc", 3 );
+
+
+ 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.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 = "aaaa|bbbb|cccc|dddd";
+ s2.ExtractElement( s1, '|', 2, 0 );
+ rc += TestMethod( po, "ExtractElement() ", s2, "bbbb", 4 );
+
+ 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 = "\\ABC\\XYZ";
+ rc += TestMethod( po, "GetPathSeparator()", s1.GetPathSeparator(), '\\' );
+
+ s1 = "/ABC/XYZ";
+ rc += TestMethod( po, "GetPathSeparator()", s1.GetPathSeparator(), '/' );
+
+
+ s1 = "123";
+ s2 = "ABC";
+ rc += TestMethod( po, "HasAlphaChars()", s1.HasAlphaChars(), 0 );
+ rc += TestMethod( po, "HasAlphaChars()", s2.HasAlphaChars(), 1 );
+
+
+ s2 = "";
+ rc += TestMethod( po, "IsEmpty()", s2.IsEmpty(), 1 );
+
+
+ s1.SetNum( (long) 123456 );
+
+ s1 = "abcZZZ123";
+ 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 );
+
+ // 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 );
+
+ 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.PadRight( 'Z', 4 );
+ rc += TestMethod( po, "PadRight() ", s1, "abcZZZZZZ", 9 );
+
+
+
+ s1 = "DEFGHI";
+ 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 = "ABC.123.abc";
+ rc += TestMethod( po, "Pos( '.', 4 )", (xbInt32) s1.Pos( '.', 4 ), 4 );
+ rc += TestMethod( po, "Pos( '.', 5 )", (xbInt32) s1.Pos( '.', 5 ), 8 );
+ rc += TestMethod( po, "Pos( '.', 9 )", (xbInt32) s1.Pos( '.', 9 ), 0 );
+
+
+
+ s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ rc += TestMethod( po, "Remove(3,5) ", s1.Remove( 3, 5 ), "ABHIJKLMNOPQRSTUVWXYZ", 21 );
+ s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ rc += TestMethod( po, "Remove(22,5) ", s1.Remove( 22, 5 ), "ABCDEFGHIJKLMNOPQRSTU", 21 );
+ s1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ rc += TestMethod( po, "Remove(24,5) ", s1.Remove( 24, 5 ), "ABCDEFGHIJKLMNOPQRSTUVW", 23 );
+
+ s1.Set( "abcdef.dbf" );
+ s1.Replace( "def", "DEF" );
+ rc += TestMethod( po, "Replace", s1, "abcDEF.dbf", 10 );
+ s1.Replace( ".dbf", ".DBF" );
+ rc += TestMethod( po, "Replace", s1, "abcDEF.DBF", 10 );
+ s1.Set( "abcdef.dbf" );
+ s1.Replace( "def", "DEFG" );
+ rc += TestMethod( po, "Replace", s1, "abcDEFG.dbf", 11 );
+ s1.Set( "abcdefdef.dbf" );
+ s1.Replace( "def", "DEFG" );
+ rc += TestMethod( po, "Replace", s1, "abcDEFGDEFG.dbf", 15 );
+
+
+ s1.SetNum( (long) 123456 );
+ rc += TestMethod( po, "SetNum() ", s1, "123456", 6 );
+
+ xbFloat f = (xbFloat) 12.35;
+ // %f format varies depending on compiler
+ s1.Sprintf( "%6.2f", f );
+ s1.Ltrim();
+ rc += TestMethod( po, "s1.Sprintf()/s.Trim()", s1, "12.35", 5 );
+
+ char buf[5];
+ buf[0] = 'W';
+ buf[1] = 'X';
+ buf[2] = 'Y';
+ buf[3] = 'Z';
+ buf[4] = 0x00;
+ xbInt32 l = 1234567;
+ 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 );
+
+
+ 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 = "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 = "BC9BC99BZ";
+ 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, "ZapTrailingChar('Z') ", s1, "CBCB", 4 );
+ s1.ZapTrailingChar( 'Z' );
+ rc += TestMethod( po, "ZapTrailingChar('Z') ", s1, "CBCB", 4 );
+
+
+ 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..4e177d0
--- /dev/null
+++ b/src/tests/xb_test_tblmgr.cpp
@@ -0,0 +1,125 @@
+/* xb_test_tblmgr.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This program tests the table manager functions.
+// usage: xb_test_tblmgr QUITE|NORMAL|VERBOSE
+
+
+#include "xbase.h"
+
+using namespace xb;
+
+#include "tstfuncs.cpp"
+
+int main( int argCnt, char **av )
+{
+ int iRc = 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;
+ }
+
+ xbXBase x;
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+ 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
+
+ InitTime();
+
+ if( iPo == 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
+
+ iRc += TestMethod( iPo, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableC" ), 0 );
+ iRc += TestMethod( iPo, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableA" ), 0 );
+ iRc += TestMethod( iPo, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableB" ), 0 );
+ iRc += TestMethod( iPo, "AddTblToTblLst()", x.AddTblToTblList( d1, "TestTableD" ), 0 );
+
+
+ // Next line should generate an exception
+ iRc += TestMethod( iPo, "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;
+ iRc--;
+ }
+
+
+
+ iRc += TestMethod( iPo, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableB" ), 0 );
+ iRc += TestMethod( iPo, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableB" ), XB_NOT_FOUND );
+ std::cout << "**** Next list should not have TestTableB in it ****" << std::endl;
+ x.DisplayTableList();
+
+
+ iRc += TestMethod( iPo, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableA" ), 0 );
+ iRc += TestMethod( iPo, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableC" ), 0 );
+ iRc += TestMethod( iPo, "RemoveTblFromDbList()", x.RemoveTblFromTblList( "TestTableD" ), 0 );
+
+
+ delete d1;
+
+ 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_tdx.cpp b/src/tests/xb_test_tdx.cpp
new file mode 100755
index 0000000..5d79f04
--- /dev/null
+++ b/src/tests/xb_test_tdx.cpp
@@ -0,0 +1,162 @@
+/* xb_test_tdx.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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.SetTempDirectory( PROJECT_TEMP_DIR );
+ x.SetMultiUser( xbFalse );
+ 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( "TTDXDB01.DBF", "TestMdxX2", MyV4Record, XB_OVERLAY, XB_MULTI_USER );
+ iRc += TestMethod( iPo, "CreateTable(1)", iRc2, 0 );
+
+ iRc2 = V4DbfX1->CreateTag( "TDX", "CITY_TAGA", "CITY", "", 0, 0, XB_OVERLAY, &pIx, &pTag );
+ iRc += TestMethod( iPo, "CreateTag(1)", iRc2, 0 );
+
+ std::cout << "Cur Tag Name = " << V4DbfX1->GetCurTagName().Str() << "\n";
+
+ xbString sFqFileName = pIx->GetFqFileName();
+ iRc2 = V4DbfX1->DeleteTag( "TDX", V4DbfX1->GetCurTagName() );
+
+ iRc += TestMethod( iPo, "DeleteTag(3)", iRc2, 0 );
+
+ // only one tag, file should not exist anymore
+ iRc2 = pIx->FileExists(sFqFileName);
+ iRc += TestMethod( iPo, "FileExists()", iRc2, 0 );
+
+ iRc2 = V4DbfX1->CreateTag( "TDX", "ZIP_TAG", "ZIP", "", xbTrue, 0, XB_OVERLAY, &pIx, &pTag );
+ iRc += TestMethod( iPo, "CreateTag(2)", iRc2, 0 );
+
+ xbInt32 uZip = 10000;
+ for( xbUInt16 i = 0; i < 5; i++ ){
+ for( xbUInt16 j = 0; j < 4; 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(1)", V4DbfX1->CheckTagIntegrity( 1, 2 ), XB_NO_ERROR );
+
+
+ x.CloseAllTables();
+
+// delete V4DbfX1;
+// delete V4DbfX2;
+
+ 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_uda.cpp b/src/tests/xb_test_uda.cpp
new file mode 100755
index 0000000..18693b5
--- /dev/null
+++ b/src/tests/xb_test_uda.cpp
@@ -0,0 +1,97 @@
+/* xb_test_uda.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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.SetLogDirectory( PROJECT_LOG_DIR );
+ 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 );
+
+ 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..a0affd6
--- /dev/null
+++ b/src/tests/xb_test_xbase.cpp
@@ -0,0 +1,217 @@
+/* xb_test_xbase.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ XDB-devel@lists.sourceforge.net
+ XDB-users@lists.sourceforge.net
+
+*/
+
+// This 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 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;
+ xbString sMsg;
+
+ #ifdef XB_LOGGING_SUPPORT
+
+
+ xbString sLogDir = PROJECT_LOG_DIR;
+// x.SetLogDirectory( sLogDir );
+ x.SetLogDirectory( PROJECT_LOG_DIR );
+
+
+ x.EnableMsgLogging();
+ if( iPo ){
+ std::cout << "Logfile is [" << x.GetLogFqFileName().Str() << "]" << std::endl;
+ }
+
+ sMsg.Sprintf( "Program [%s] initializing...", av[0] );
+ x.WriteLogMessage( sMsg );
+ #endif
+ InitTime();
+
+
+ x.SetDefaultDateFormat( "YY-MM-DD" );
+ iRc += TestMethod( iPo, "SetDefaultDateFormat", x.GetDefaultDateFormat(), "YY-MM-DD", 8 );
+ if( iPo == 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( iPo, "GetErrorMessage", x.GetErrorMessage( XB_DBF_FILE_NOT_OPEN ), "DBF File Not Open", 17 );
+
+
+ #ifdef XB_LOGGING_SUPPORT
+ iRc += TestMethod( iPo, "GetLogDirectory()", x.GetLogDirectory(), sLogDir, sLogDir.Len());
+ xbString sLogName = CMAKE_SYSTEM_NAME;
+ sLogName += "_";
+ sLogName += XB_PLATFORM;
+ sLogName += ".xbLog.txt";
+ iRc += TestMethod( iPo, "GetLogFileName()", x.GetLogFileName(), sLogName, sLogName.Len());
+ x.WriteLogMessage( "Program xb_test_xbase - test logfile message" );
+ #endif
+
+ x.xbSleep( 250 );
+ if( iPo == 2 ){
+ std::cout << "DisplayError Test ==> ";
+ x.DisplayError( 0 );
+ }
+
+ #ifdef WIN32
+ char cSep = '\\';
+ iRc += TestMethod( iPo, "GetPathSeparator()", x.GetPathSeparator(), cSep );
+ #else
+ char cSep = '/';
+ iRc += TestMethod( iPo, "GetPathSeparator()", x.GetPathSeparator(), cSep );
+ #endif
+
+
+
+ xbString sDir = "\\dir\\path";
+ xbString sFile = "myfile";
+ xbString sExt = "DBF";
+ xbString sFqn;
+ iRc += TestMethod( iPo, "CreateFQN()", x.CreateFqn( sDir, sFile, sExt, sFqn ), XB_NO_ERROR );
+ //std::cout << "FQN = [" << sFqn.Str() << "]\n";
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "CreateFQN()", sFqn.Str(), "\\dir\\path\\myfile.DBF", 20 );
+ #else
+ iRc += TestMethod( iPo, "CreateFQN()", sFqn.Str(), "/dir/path/myfile.DBF", 20 );
+ #endif
+
+ sDir = "/dir/path";
+ iRc += TestMethod( iPo, "CreateFQN()", x.CreateFqn( sDir, sFile, sExt, sFqn ), XB_NO_ERROR );
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "CreateFQN()", sFqn.Str(), "\\dir\\path\\myfile.DBF", 20 );
+ #else
+ iRc += TestMethod( iPo, "CreateFQN()", sFqn.Str(), "/dir/path/myfile.DBF", 20 );
+ #endif
+
+
+ x.SetDataDirectory( "/ABCDEFG/" );
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG\\", 9 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG/", 9 );
+ #endif
+
+ x.SetDataDirectory( "/ABCDEFG" );
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG", 8 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG", 8 );
+ #endif
+
+ x.SetDataDirectory( "\\ABCDEFG\\");
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG\\", 9 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG/", 9 );
+ #endif
+
+ x.SetDataDirectory( "\\ABCDEFG" );
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "\\ABCDEFG", 8 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetDataDirectory()", x.GetDataDirectory(), "/ABCDEFG", 8 );
+ #endif
+
+#ifdef XB_LOGGING_SUPPORT
+
+ x.SetLogDirectory( "ABCDEFG" );
+ iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "ABCDEFG", 7 );
+ x.SetLogDirectory( "/ABCDEFG/" );
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "\\ABCDEFG\\", 9 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "/ABCDEFG/", 9 );
+ #endif
+
+ x.SetLogDirectory( "/ABCDEFG" );
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "\\ABCDEFG", 8 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "/ABCDEFG", 8 );
+ #endif
+
+ x.SetLogDirectory( "\\ABCDEFG\\");
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "\\ABCDEFG\\", 9 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetLogDirectory()", x.GetLogDirectory(), "/ABCDEFG/", 9 );
+ #endif
+
+ x.SetLogFileName( "LogFileNameTest" );
+ iRc += TestMethod( iPo, "Set/GetLogFileName()", x.GetLogFileName(), "LogFileNameTest", 15 );
+#endif
+
+ x.SetTempDirectory( "/ABCDEFG/" );
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "\\ABCDEFG\\", 9 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "/ABCDEFG/", 9 );
+ #endif
+
+ x.SetTempDirectory( "/ABCDEFG" );
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "\\ABCDEFG", 8 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "/ABCDEFG", 8 );
+ #endif
+
+ x.SetTempDirectory( "\\ABCDEFG\\");
+ #ifdef WIN32
+ iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "\\ABCDEFG\\", 9 );
+ #else
+ iRc += TestMethod( iPo, "Set/GetTempDirectory()", x.GetTempDirectory(), "/ABCDEFG/", 9 );
+ #endif
+
+
+// std::cout << "path separator = [" << x.GetPathSeparator() << "]\n";
+
+ 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/utils/xb_cfg_check.cpp b/src/utils/xb_cfg_check.cpp
new file mode 100755
index 0000000..c979bac
--- /dev/null
+++ b/src/utils/xb_cfg_check.cpp
@@ -0,0 +1,411 @@
+/* xb_cfg_check.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2019,2023 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 <stdio.h>
+#include <stdlib.h>
+#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_NDX_SUPPORT
+ fprintf( stdout, "XB_NDX_SUPPORT = [ON]\n" );
+ #else
+ fprintf( stdout, "XB_NDX_SUPPORT = [OFF]\n" );
+ #endif
+
+ #ifdef XB_MDX_SUPPORT
+ fprintf( stdout, "XB_MDX_SUPPORT = [ON]\n" );
+ #else
+ fprintf( stdout, "XB_MDX_SUPPORT = [OFF]\n" );
+ #endif
+
+ #ifdef XB_TDX_SUPPORT
+ fprintf( stdout, "XB_TDX_SUPPORT = [ON]\n" );
+ #else
+ fprintf( stdout, "XB_TDX_SUPPORT = [OFF]\n" );
+ #endif
+
+
+ #ifdef XB_SQL_SUPPORT
+ fprintf( stdout, "XB_SQL_SUPPORT = [ON]\n" );
+ #else
+ fprintf( stdout, "XB_SQL_SUPPORT = [OFF]\n" );
+ #endif
+
+ #ifdef XB_INF_SUPPORT
+ fprintf( stdout, "XB_INF_SUPPORT = [ON]\n" );
+ #else
+ fprintf( stdout, "XB_INF_SUPPORT = [OFF]\n" );
+ #endif
+
+ #ifdef XB_FILTER_SUPPORT
+ fprintf( stdout, "XB_FILTER_SUPPORT = [ON]\n" );
+ #else
+ fprintf( stdout, "XB_FILTER_SUPPORT = [OFF]\n" );
+ #endif
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ fprintf( stdout, "XB_BLOCKREAD_SUPPORT = [ON]\n" );
+ #else
+ fprintf( stdout, "XB_BLOCKREAD_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_TEMP_DIR = [%s]\n", PROJECT_TEMP_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, "sizeof(char *) = [%d]\n", (xbInt32) sizeof( char *));
+ fprintf( stdout, "sizeof(int) = [%d]\n", (xbInt32) sizeof( int ));
+ fprintf( stdout, "sizeof(long) = [%d]\n", (xbInt32) sizeof( long ));
+ fprintf( stdout, "sizeof(char) = [%d]\n", (xbInt32) sizeof( char ));
+ #ifdef HAVE_WCHAR_H
+ fprintf( stdout, "sizeof(wchar_t) = [%d]\n", (xbInt32) sizeof( wchar_t ));
+ #endif
+
+ #ifdef HAVE_WINDOWS_H
+ fprintf( stdout, "sizeof(DWORD) = [%d]\n", (xbInt32) sizeof( DWORD ));
+ #endif
+
+ fprintf( stdout, "sizeof(double) = [%d]\n", (xbInt32) sizeof( double ));
+ fprintf( stdout, "sizeof(float) = [%d]\n", (xbInt32) sizeof( float ));
+ fprintf( stdout, "sizeof(size_t) = [%d]\n", (xbInt32) sizeof( size_t ));
+ fprintf( stdout, "sizeof(off_t) = [%d]\n\n", (xbInt32) sizeof( off_t ));
+
+ fprintf( stdout, "sizeof(xbBool) = [%d]\n", (xbInt32) sizeof( xbBool ));
+ fprintf( stdout, "sizeof(xbInt16) = [%d]\n", (xbInt32) sizeof( xbInt16 ));
+ fprintf( stdout, "sizeof(xbUInt16) = [%d]\n", (xbInt32) sizeof( xbUInt16 ));
+ fprintf( stdout, "sizeof(xbInt32) = [%d]\n", (xbInt32) sizeof( xbInt32 ));
+ fprintf( stdout, "sizeof(xbUInt32) = [%d]\n", (xbInt32) sizeof( xbUInt32 ));
+ fprintf( stdout, "sizeof(xbInt64) = [%d]\n", (xbInt32) sizeof( xbInt64 ));
+ fprintf( stdout, "sizeof(xbUInt64) = [%d]\n", (xbInt32) sizeof( xbUInt64 ));
+ fprintf( stdout, "sizeof(xbFloat) = [%d]\n", (xbInt32) sizeof( xbFloat ));
+ fprintf( stdout, "sizeof(xbDouble) = [%d]\n", (xbInt32) 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..2466b1b
--- /dev/null
+++ b/src/utils/xb_copydbf.cpp
@@ -0,0 +1,100 @@
+/* xb_copydbf.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2019,2023 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>
+using namespace xb;
+
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_copydbf [-h] [-?] [--help] [-v] [--version] -s source.DBF -t target.DBF" << std::endl << std::endl;
+ std::cout << "This program copies the structure of a table defined in source.DBF to target.DBF. Data and indices are not included.";
+ std::cout << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+
+int main(int argc, char *argv[])
+{
+
+ xbXBase x;
+ xbInt16 iRc;
+ xbDbf *dSrc = NULL;
+ xbDbf *dTrg = NULL;
+
+ xbString sParm;
+ xbString sSrcDbf;
+ xbString sTrgDbf;
+
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-s", sSrcDbf ) || sSrcDbf == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-t", sTrgDbf ) || sTrgDbf == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ #ifdef XB_LOGGING_SUPPORT
+ x.EnableMsgLogging();
+ xbString sMsg;
+ sMsg.Sprintf( "Program [%s] initializing...", argv[0] );
+ x.WriteLogMessage( sMsg );
+ #endif
+
+ if(( iRc = x.OpenHighestVersion( sSrcDbf, "", &dSrc )) != XB_NO_ERROR ){
+ std::cout << "Could not open file iRc = " << iRc << " file = " << sSrcDbf.Str() << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+ #ifdef XB_DBF4_SUPPORT
+ dTrg = new xbDbf4( &x );
+ #else
+ dTrg = new xbDbf3( &x );
+ #endif
+
+ if(( iRc = dSrc->CopyDbfStructure( dTrg, sTrgDbf, sTrgDbf, XB_DONTOVERLAY, XB_SINGLE_USER )) != XB_NO_ERROR ){
+ std::cout << "Could not copy file " << sTrgDbf.Str() << " Error = " << iRc << "\n";
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+ x.CloseAllTables();
+ return 0;
+}
+
+
+
+
+
diff --git a/src/utils/xb_dbfutil.cpp b/src/utils/xb_dbfutil.cpp
new file mode 100755
index 0000000..76988c0
--- /dev/null
+++ b/src/utils/xb_dbfutil.cpp
@@ -0,0 +1,3836 @@
+/* xb_dbfutil.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2021,2022,2023 Gary A Kunkel
+
+The xb64 software library is covered under
+the terms of the GPL Version 3, 2007 license.
+
+Email Contact:
+
+ xb64-devel@lists.sourceforge.net
+ xb64-users@lists.sourceforge.net
+
+*/
+
+#include "xbase.h"
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+using namespace xb;
+
+///@cond DOXYOFF
+class xbUtil{
+ public:
+ xbUtil( xbXBase *x );
+ ~xbUtil();
+ xbDbf *GetTablePtr( const char * cTitle );
+ void DisplayActiveTable() const;
+
+ void Help();
+
+ // 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();
+ void RenameTable();
+ void DeleteTable();
+
+ // 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 // XB_MEMO_SUPPORT
+
+ void LockHeader();
+ void UnlockHeader();
+ void xbFileLock();
+ void xbFileUnlock();
+ #endif // XB_LOCKING_SUPPORT
+
+ // 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 // XB_DEBUG_SUPPORT
+ #endif // XB_EXPRESSION_SUPPORT
+
+ // 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( xbXBase *x )
+{
+ this->x = x;
+ 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
+}
+
+
+/*************************************************************************************/
+void xbUtil::Help(){
+ std::cout << "Program xb_dbfutil provides a menu driven application for accessing" << std::endl;
+ std::cout << "the xbase64 library functions." << std::endl << std::endl;
+ std::cout << "Menu choices can be selected directly with out accessing the given menus" << std::endl;
+ std::cout << "by entering a '=' followed by the menu choices. For example, entering '=3.4'" << std::endl;
+ std::cout << "would be the same as going to menu #3 and entering a 4, which is the sequence" << std::endl;
+ std::cout << "for opening a file." << std::endl;
+}
+
+/*************************************************************************************/
+#ifdef XB_NDXINF_SUPPORT
+
+void xbUtil::ListInfFileData()
+{
+ xbLinkListNode<xbString> *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
+#endif // XB_INDEX_SUPPORT
+
+
+/*************************************************************************************/
+#ifdef XB_EXPRESSION_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<xbUInt32> * 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 ));
+ iRc = dActiveTable->LockRecord( XB_LOCK, strtoul( cBuf, NULL, 0 ));
+
+ 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 ));
+ iRc = dActiveTable->LockRecord( XB_UNLOCK, strtoul( cBuf, NULL, 0 ));
+ 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 );
+ ulRecNo = strtoul( cBuf, NULL, 0 );
+ 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 );
+ ulRecNo = strtoul( cBuf, NULL, 0 );
+ 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 );
+ ulRecNo = strtoul( cBuf, NULL, 0 );
+ 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 ulRecNo;
+ 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 );
+ ulRecNo = strtoul( cBuf, NULL, 0 );
+ if(( rc = dActiveTable->GetRecord( ulRecNo )) != 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 ulRecNo;
+
+ 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 );
+ ulRecNo = strtoul( cBuf, NULL, 0 );
+ if(( rc = dActiveTable->GetRecord( ulRecNo )) != 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 ulRecNo;
+
+ 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;
+ }
+ //ulRecNo = atol( cBuf );
+
+ ulRecNo = strtoul( cBuf, NULL, 0 );
+ if(( rc = dActiveTable->GetRecord( ulRecNo )) != 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 ulRecNo;
+
+ 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 );
+ ulRecNo = strtoul( cBuf, NULL, 0 );
+ if(( rc = dActiveTable->PutRecord( ulRecNo )) == 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::DeleteTable(){
+
+ if( !dActiveTable )
+ dActiveTable = GetTablePtr( " - select table" );
+
+ if( !dActiveTable ){
+ std::cout << "No table selected" << std::endl;
+ return;
+ }
+
+ dActiveTable->DeleteTable();
+ dActiveTable = NULL;
+ std::cout << "Table deleted.\n";
+
+}
+
+
+/*************************************************************************************/
+void xbUtil::RenameTable(){
+
+ if( !dActiveTable )
+ dActiveTable = GetTablePtr( " - select table" );
+
+ if( !dActiveTable ){
+ std::cout << "No table selected" << std::endl;
+ return;
+ }
+
+ char cBuf[128];
+ std::cout << "Enter new tablefile name (filename.DBF)" << std::endl;
+ std::cin.getline( cBuf, 128 );
+
+ if( cBuf[0] ){
+ dActiveTable->Rename( cBuf );
+ dActiveTable->Close();
+ dActiveTable = NULL;
+ std::cout << "Table closed. Reopen if needed.\n";
+ }
+}
+
+/*************************************************************************************/
+void xbUtil::GetRecord(){
+
+ char cBuf[15];
+ xbInt16 iRc;
+ xbUInt32 ulRecNo;
+
+ 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 );
+ ulRecNo = strtoul( cBuf, NULL, 0 );
+ if(( iRc = dActiveTable->GetRecord( ulRecNo )) == 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;
+ xbInt16 iRc;
+ if(( iRc = dActiveTable->GetRecordCnt( ulRecCnt )) == XB_NO_ERROR ){
+ 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;
+ } else {
+ x->DisplayError( iRc );
+ }
+ }
+}
+/*************************************************************************************/
+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): ";
+ 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 ); moved to DumpHeader routine
+ 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): ";
+ 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->GetLogDirectory().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->GetLogDirectory());
+ 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->GetLogFileName().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->GetLogFileName());
+ 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 << "Log Directory = [" << x->GetLogDirectory().Str() << "]" << std::endl;
+ std::cout << "Logfile Name = [" << x->GetLogFileName().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 == "=1" )
+ Help();
+ 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 == "=3.15" )
+ RenameTable();
+ else if( sOption == "=3.16" )
+ DeleteTable();
+ 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 // XB_DEBUG_SUPPORT
+ #endif // XB_LOCKING_SUPPORT
+
+ #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 << "15 - Rename Table" << std::endl;
+ std::cout << "16 - Delete Table" << 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 15: RenameTable(); break;
+ case 16: DeleteTable(); 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 " <<
+ xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." <<
+ xbase_VERSION_PATCH << "\n";
+
+ 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 1: Help(); 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<xbTag *> *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 );
+ xbInt16 iRc = dActiveTable->Reindex( 0 );
+ 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( 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
+
+
+/*************************************************************************************/
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_dbfutil [-h] [-?] [--help] [-v] [--version]" << std::endl << std::endl;
+ std::cout << "This program provides a menu driven tool for accissing the functionality og the xbase64 library" << std::endl;
+ std::cout << "and provides access to DBF, MDX, NDX and MDT xbase formatted data files." << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+/*************************************************************************************/
+int main(int argc, char *argv[] )
+{
+
+ // std::cout << "xb_dbfutil initializing" << std::endl;
+ xbXBase x;
+
+ xbString sParm;
+ if (x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ xbUtil u( &x );
+ u.MainMenu();
+ return 0;
+}
diff --git a/src/utils/xb_deletall.cpp b/src/utils/xb_deletall.cpp
new file mode 100755
index 0000000..3a8f6e4
--- /dev/null
+++ b/src/utils/xb_deletall.cpp
@@ -0,0 +1,85 @@
+/* xb_deletall.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2017,2019,2023 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 <xbase.h>
+
+
+using namespace xb;
+
+
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_deleteall [-h] [-?] [--help] [-v] [--version] -i myfile" << std::endl << std::endl;
+ std::cout << "This program flags all the records in a DBF table for deletion. To physically remove the deleted records, see program xb_pack.";
+ std::cout << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+
+int main(int argc, char *argv[])
+{
+
+ xbXBase x;
+ xbDbf *MyFile = NULL;
+ xbFile f( &x );
+ xbInt16 iRc = 0;
+
+ xbString sParm;
+
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-i", sParm ) || sParm == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ if(( iRc = x.OpenHighestVersion( sParm.Str(), "", &MyFile )) != XB_NO_ERROR ){
+ std::cout << "Could not open file RC = " << iRc << " file = " << sParm.Str() << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+ iRc = MyFile->DeleteAllRecords();
+ if( iRc != XB_NO_ERROR ) {
+ std::cout << "Error Deleting all records - database ==> " << sParm.Str() << "\n";
+ std::cout << "Return Code = " << iRc;
+ x.DisplayError( iRc );
+ return 1;
+ }
+ MyFile->Close();
+ delete MyFile;
+ 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..05b7209
--- /dev/null
+++ b/src/utils/xb_dumpdbt.cpp
@@ -0,0 +1,109 @@
+/* xb_dumpdbt.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2017,2023 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>
+using namespace xb;
+
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_dumpdbt [-h] [-?] [--help] [-v] [--version] -i filename.DBF" << std::endl << std::endl;
+ std::cout << "This program dumps memo record data from an xbase formatted DBT file to stdout." << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+int main(int argc, char *argv[] )
+{
+
+
+ #ifdef XB_MEMO_SUPPORT
+ xbXBase x;
+ xbInt16 iRc;
+ char cFieldType;
+ xbDbf *MyFile = NULL;
+ xbUInt32 ulMemoFieldLen;
+ xbString sFldName;
+ xbInt32 lBlockPtr;
+ xbString sMemoFldData;
+
+ xbString sParm;
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-i", sParm ) || sParm == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ if(( iRc = x.OpenHighestVersion( sParm.Str(), "", &MyFile )) != XB_NO_ERROR ){
+ std::cout << "Could not open file iRc = " << iRc << " file = " << sParm.Str() << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+ if( MyFile->GetMemoFieldCnt() == 0 ) {
+ std::cout << "No memo fields exist in " << sParm.Str() << 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_dumprecs.cpp b/src/utils/xb_dumprecs.cpp
new file mode 100755
index 0000000..cf6e33c
--- /dev/null
+++ b/src/utils/xb_dumprecs.cpp
@@ -0,0 +1,98 @@
+/* xb_dumprecs.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2019,2023 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>
+using namespace xb;
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_dumprecs [-h] [-?] [--help] [-v] [--version] -i filename.DBF" << std::endl << std::endl;
+ std::cout << "This program dumps data records in an xbase formatted DBF file to stdout in csv format." << std::endl;
+ std::cout << "Memo data (variable length) is not included in this export. See program xb_dumpdpt for memo data." << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+int main(int argc, char *argv[] )
+{
+ xbXBase x;
+
+
+ xbInt16 iRc;
+ x.EnableMsgLogging();
+ x.SetLogSize( 1000000L );
+
+ xbString sParm;
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-i", sParm ) || sParm == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ xbDbf *MyFile = NULL;
+ if(( iRc = x.OpenHighestVersion( sParm.Str(), "", &MyFile )) != XB_NO_ERROR ){
+ std::cout << "Could not open file RC = " << iRc << " file = " << sParm.Str() << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+ // std::cout << "Processing file sequentially from beginning..." << std::endl;
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ MyFile->EnableBlockReadProcessing();
+ #endif
+
+ 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 ); // header + data
+ else
+ iRc = MyFile->DumpRecord(++j, 2, 1 ); // data only
+ if( iRc != XB_NO_ERROR ){
+ x.DisplayError( iRc );
+ return 1;
+ }
+ }
+ std::cout << j << " Records processed." << std::endl;
+
+ #ifdef XB_BLOCKREAD_SUPPORT
+ MyFile->DisableBlockReadProcessing();
+ #endif
+
+ MyFile->Close();
+
+ return 0;
+}
diff --git a/src/utils/xb_execsql.cpp b/src/utils/xb_execsql.cpp
new file mode 100755
index 0000000..3441e91
--- /dev/null
+++ b/src/utils/xb_execsql.cpp
@@ -0,0 +1,130 @@
+/* xb_execsql.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2023 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>
+using namespace xb;
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_execsql [-h] [-?] [--help] [-v] [--version] -i filename.SQL -q --quiet" << std::endl << std::endl;
+ std::cout << "This program processes sql commands from input file 'filename.SQL'" << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+
+xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd, xbBool bQuiet );
+xbInt16 GetNextSqlCmd( xbFile &f, xbString &sCmd, xbBool bQuiet ){
+ 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 {
+
+ if( !bQuiet ){
+ std::cout << sLine;
+ if( sLine.Pos( 0x0a ) == 0 )
+ std::cout << std::endl;
+ }
+
+ // 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 argc, char* argv[])
+{
+ xbXBase x;
+ xbSql sql( &x );
+ xbFile f( sql.GetXbasePtr() );
+ xbInt16 iRc = XB_NO_ERROR;
+ xbString sFileName = "";
+ xbString sSqlLine = "";
+ xbString sParm = "";
+ xbBool bQuiet = xbFalse;
+
+ x.EnableMsgLogging();
+
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-q", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--quiet", sParm )){
+ bQuiet = xbTrue;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-i", sFileName ) || sFileName == "" ){
+ PrintHelp();
+ return 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, bQuiet );
+
+ 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_import.cpp b/src/utils/xb_import.cpp
new file mode 100755
index 0000000..272a0d0
--- /dev/null
+++ b/src/utils/xb_import.cpp
@@ -0,0 +1,242 @@
+/* xb_import.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2023 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>
+using namespace xb;
+
+
+struct sFldMap{
+ xbUInt32 iRecPos;
+ xbInt16 iFldNo;
+ char cFldType;
+ sFldMap *next;
+};
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_execsql [-h] [-?] [--help] [-v] [--version] -i infilename.txt -d delimeter -t table.DBF -q --quiet" << std::endl << std::endl;
+ std::cout << "This program imports data from a text file into a specified DBF file/table." << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ xbXBase x;
+ xbSql sql( &x );
+ xbFile f( sql.GetXbasePtr() );
+ xbInt16 iRc = XB_NO_ERROR;
+ xbString sFileName = "";
+ xbString sTableName = "";
+ xbString sSqlLine = "";
+ xbString sParm = "";
+ xbString sMsg;
+ xbString sLine;
+ xbString sFld;
+ xbBool bQuiet = xbFalse;
+ char cDelimiter = ',';
+ char cType = ' ';
+ sFldMap *fmFldList = 0;
+ sFldMap *fmTemp = 0;
+ xbInt16 iFldNo = 0;
+ xbUInt32 ulRecCtr = 0;
+ xbBool bRecUpdated;
+ x.EnableMsgLogging();
+
+
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-q", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--quiet", sParm )){
+ bQuiet = xbTrue;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-d", sParm )){
+ if( sParm.Len() > 0 ){
+ cDelimiter = sParm[1];
+ }
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-i", sFileName ) || sFileName == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-t", sTableName ) || sTableName == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ xbDbf *MyFile = NULL;
+ if(( iRc = x.OpenHighestVersion( sTableName.Str(), "", &MyFile )) != XB_NO_ERROR ){
+ std::cout << "Could not open table/file RC = " << iRc << " file = " << sTableName.Str() << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+ if(( iRc = f.xbFopen( "r", sFileName, XB_SINGLE_USER )) != XB_NO_ERROR ){
+ sMsg.Sprintf( "Error opening [%s]\n", sFileName.Str() );
+ std::cout << sMsg.Str();
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+
+ if(( iRc = f.xbFgets( 1024, sLine )) != XB_NO_ERROR ){
+ sMsg.Sprintf( "Error reading [%s]\n", sFileName.Str() );
+ std::cout << sMsg.Str();
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+
+ // determine how many fields in a record
+ xbUInt32 lFldCnt = sLine.CountChar( cDelimiter, 1 );
+ // std::cout << "in rec = [" << sLine.Str() << "]\n";
+ // std::cout << "fld cnt = [" << lFldCnt << "]\n";
+
+ // do the mapping between field names in source data and field numbers in target table
+ for( xbUInt32 l = 0; l < (lFldCnt + 1); l++ ){
+
+ // get the field
+ sFld.ExtractElement( sLine.Str(), cDelimiter, l+1, 1 );
+ sFld.ZapTrailingChar( 0x0a ); // eliminate CRLF
+ sFld.ZapTrailingChar( 0x0d ); // eliminate CRLF
+
+
+ // do the lookup
+ // std::cout << "processing field [" << l << "] [" << sFld.Str() << "]\n";
+ // if found, create an entry in the field list structure
+ // else if not quiet, display a message
+
+ //iRc = MyFile->GetFieldNo( sFld, &iFldNo );
+
+ if(( iRc = MyFile->GetFieldNo( sFld, iFldNo )) == XB_NO_ERROR ){
+ MyFile->GetFieldType( iFldNo, cType );
+ fmTemp = (sFldMap *) calloc( 1, sizeof( sFldMap ));
+ if( !fmTemp ){
+ std::cout << "Memory allocation error\n";
+ exit(1);
+ } else {
+ fmTemp->iRecPos = l;
+ fmTemp->iFldNo = iFldNo;
+ fmTemp->cFldType = cType;
+ fmTemp->next = fmFldList;
+ fmFldList = fmTemp;
+
+ }
+ } else {
+ if( !bQuiet ){
+ std::cout << "Field [" << sFld.Str() << "] not found in target table" << std::endl;
+ }
+ }
+ }
+
+
+ while( f.xbFgets( 1024, sLine ) == XB_NO_ERROR ){
+ bRecUpdated = xbFalse;
+ // std::cout << sLine.Str() << "\n";
+
+ if(( iRc = MyFile->BlankRecord()) != XB_NO_ERROR ){
+ sMsg.Sprintf( "MyFile->BlankRecord() error [%d]\n", iRc );
+ std::cout << sMsg.Str();
+ x.DisplayError( iRc );
+ } else {
+
+ fmTemp = fmFldList;
+ while( fmTemp ){
+
+ // std::cout << "*** RecPos = " << fmTemp->iRecPos << " FldNo = " << fmTemp->iFldNo << " Type = " << fmTemp->cFldType << "\n";
+
+ sFld.ExtractElement( sLine.Str(), cDelimiter, fmTemp->iRecPos+1, 1 );
+ sFld.ZapTrailingChar( 0x0a ); // eliminate CRLF
+ sFld.ZapTrailingChar( 0x0d ); // eliminate CRLF
+
+ // remove any matching leading and trailing quotes
+ if( sFld[1] == '\'' && sFld[sFld.Len()] == '\'' ){
+ sFld.ZapTrailingChar( '\'' );
+ sFld.ZapLeadingChar ( '\'' );
+ //std::cout << "DataNq = " << sFld.Str() << "\n";
+ } else if( sFld[1] == '"' && sFld[sFld.Len()] == '"' ){
+ sFld.ZapTrailingChar( '"' );
+ sFld.ZapLeadingChar ( '"' );
+ }
+
+ // std::cout << "Data = " << sFld.Str() << "\n";
+ if( sFld.Len() > 0 ){
+ bRecUpdated = xbTrue;
+ if( fmTemp->cFldType == 'C' || fmTemp->cFldType == 'L' || fmTemp->cFldType == 'D' || fmTemp->cFldType == 'N' || fmTemp->cFldType == 'F' ){
+ iRc = MyFile->PutField( fmTemp->iFldNo, sFld );
+ } else if( fmTemp->cFldType == 'M' ){
+ iRc = MyFile->UpdateMemoField( fmTemp->iFldNo, sFld );
+ } else {
+ std::cout << "Field type [" << fmTemp->cFldType << "] not built yet" << std::endl;
+ }
+
+ if( iRc != XB_NO_ERROR && !bQuiet ){
+ std::cout << "Error [" << iRc << "] on field [" << fmTemp->iFldNo << "] on record [" << ulRecCtr << "]" << std::endl;
+ }
+ }
+ fmTemp = fmTemp->next;
+ }
+
+ if( bRecUpdated ){
+ iRc = MyFile->AppendRecord();
+ if( iRc != XB_NO_ERROR ){
+ if( !bQuiet ){
+ std::cout << "Error [" << iRc << "] on appending record [" << ulRecCtr << "]" << std::endl;
+ }
+ MyFile->Abort();
+ } else {
+ iRc = MyFile->Commit();
+ if( iRc != XB_NO_ERROR ){
+ if( !bQuiet ){
+ std::cout << "Error [" << iRc << "] on appending record [" << ulRecCtr << "]" << std::endl;
+ }
+ MyFile->Abort();
+ }
+ }
+ }
+
+ }
+
+ ulRecCtr++;
+
+ }
+
+
+
+
+
+ f.xbFclose();
+ return 0;
+}
+
diff --git a/src/utils/xb_pack.cpp b/src/utils/xb_pack.cpp
new file mode 100755
index 0000000..0cbba7e
--- /dev/null
+++ b/src/utils/xb_pack.cpp
@@ -0,0 +1,84 @@
+/* xb_pack.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2017,2023 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>
+using namespace xb;
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_pack [-h] [-?] [--help] [-v] [--version] -i filename.DBF" << std::endl << std::endl;
+ std::cout << "This program removes data records that are flagged for deletion in a DBF file and rebuilds indices." << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+int main(int argc, char *argv[] )
+{
+ xbXBase x;
+ xbInt16 iRc;
+ xbDbf * MyFile = NULL;
+
+ xbString sParm;
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-i", sParm ) || sParm == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ if(( iRc = x.OpenHighestVersion( sParm.Str(), "", &MyFile )) != XB_NO_ERROR ){
+ std::cout << "Could not open file iRc = " << iRc << " file = " << sParm.Str() << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+ if(( iRc = MyFile->Pack()) != XB_NO_ERROR ) {
+ std::cout << "\nError packing DBF database ==> " << sParm.Str() << std::endl;
+ std::cout << " Return Code = " << iRc << std::endl;
+ return 1;
+ }
+
+ #ifdef XB_INDEX_SUPPORT
+ if(( iRc = MyFile->Reindex( 1 )) != XB_NO_ERROR ) {
+ std::cout << "\nError reindexing DBF database ==> " << sParm.Str() << std::endl;
+ std::cout << " Return Code = " << iRc << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+ #endif // XB_INDEX_SUPPORT
+
+ 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..241642a
--- /dev/null
+++ b/src/utils/xb_tblinfo.cpp
@@ -0,0 +1,104 @@
+/* xb_tblinfo.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2021,2023 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>
+
+using namespace xb;
+
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_tblinfo [-h] [-?] [--help] [-v] [--version] -i filename.DBF" << std::endl << std::endl;
+ std::cout << "This program dumps table definition information from a DBF file and associated indices." << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+int main(int argc, char *argv[] ){
+
+ xbXBase x;
+ xbInt16 iRc;
+ xbString sTagName;
+ xbDbf *MyFile = NULL;
+
+ x.EnableMsgLogging();
+ x.SetLogSize( 1000000L );
+
+ xbString sParm;
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-i", sParm ) || sParm == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ if(( iRc = x.OpenHighestVersion( sParm.Str(), "", &MyFile )) != XB_NO_ERROR ){
+ std::cout << "Could not open file iRc = " << iRc << " file = " << sParm.Str() << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+ MyFile->DumpHeader( 4 );
+
+
+
+ #ifdef XB_INDEX_SUPPORT
+ xbIxList *ixl;
+ xbIx *ixp;
+ xbString sFileType;
+
+ #ifdef XB_MDX_SUPPORT
+ ixl = MyFile->GetIxList();
+ while( ixl ){
+ ixp = ixl->ix;
+ ixp->GetFileType( sFileType );
+ if( sFileType == "MDX" )
+ ixp->DumpHeader( 1, 3 );
+ ixl = ixl->next;
+ }
+ #endif // XB_MDX_SUPPORT
+
+
+ #ifdef XB_NDX_SUPPORT
+ ixl = MyFile->GetIxList();
+ while( ixl ){
+ ixp = ixl->ix;
+ ixp->GetFileType( sFileType );
+ if( sFileType == "NDX" )
+ ixp->DumpHeader( 1, 3 );
+ ixl = ixl->next;
+ }
+ #endif // XB_NDX_SUPPORT
+
+ #endif // XB_INDEX_SUPPORT
+
+ 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..5fb1323
--- /dev/null
+++ b/src/utils/xb_undelall.cpp
@@ -0,0 +1,82 @@
+/* xb_undelall.cpp
+
+XBase64 Software Library
+
+Copyright (c) 1997,2003,2014,2017,2023 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 removes the delete flag on all records flagged for deletion in a DBF file
+
+*/
+
+
+#include <xbase.h>
+
+
+using namespace xb;
+
+
+
+void PrintHelp();
+void PrintHelp(){
+ std::cout << "Usage: xb_undelall [-h] [-?] [--help] [-v] [--version] -i myfile" << std::endl << std::endl;
+ std::cout << "This program removes the deletion flags all the records in a DBF table that are flagged for deletion.";
+ std::cout << std::endl << std::endl;
+}
+void PrintVersion();
+void PrintVersion(){
+ std::cout << "Xbase64 Version: " << xbase_VERSION_MAJOR << "." << xbase_VERSION_MINOR << "." << xbase_VERSION_PATCH << std::endl;
+}
+
+
+int main(int argc, char *argv[])
+{
+
+ xbXBase x;
+ xbDbf *MyFile = NULL;
+ xbInt16 iRc = 0;
+
+ xbString sParm;
+
+ if (argc < 2 || x.GetCmdLineOpt( argc, argv, "-h", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "-?", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--help", sParm )){
+ PrintHelp();
+ return 1;
+ }
+
+ if ( x.GetCmdLineOpt( argc, argv, "-v", sParm ) ||
+ x.GetCmdLineOpt( argc, argv, "--version", sParm )){
+ PrintVersion();
+ return 1;
+ }
+
+ if( !x.GetCmdLineOpt( argc, argv, "-i", sParm ) || sParm == "" ){
+ PrintHelp();
+ return 1;
+ }
+
+ if(( iRc = x.OpenHighestVersion( sParm.Str(), "", &MyFile )) != XB_NO_ERROR ){
+ std::cout << "Could not open file RC = " << iRc << " file = " << sParm.Str() << std::endl;
+ x.DisplayError( iRc );
+ return 1;
+ }
+
+ iRc = MyFile->UndeleteAllRecords();
+ if( iRc != XB_NO_ERROR ) {
+ std::cout << "Error Undeleting all records - database ==> " << sParm.Str() << "\n";
+ std::cout << " Return Code = " << iRc;
+ return 1;
+ }
+ MyFile->Close();
+ delete MyFile;
+ return 0;
+} \ No newline at end of file