/* xbxbase.cpp
XBase64 Software Library
Copyright (c) 1997,2003,2014,2022 Gary A Kunkel
The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
Email Contact:
XDB-devel@lists.sourceforge.net
XDB-users@lists.sourceforge.net
*/
#include "xbase.h"
namespace xb{
/*************************************************************************/
//! @brief Class Constructor.
xbXBase::xbXBase() {
SetEndianType();
#ifdef XB_LOGGING_SUPPORT
xLog = new xbLog();
#endif
}
/*************************************************************************/
//! @brief Class Deconstructor.
xbXBase::~xbXBase(){
CloseAllTables();
#ifdef XB_LOGGING_SUPPORT
delete xLog;
#endif
}
/*************************************************************************/
//! @brief Close all tables / files.
/*!
This closes everything.
\returns Return Codes
*/
xbInt16 xbXBase::CloseAllTables(){
xbInt16 iRc = 0;
xbInt16 iErrorStop = 0;
xbInt16 iOpenTableCnt = GetOpenTableCount();
try{
xbDbf *d;
for( xbInt16 i = 0; i < iOpenTableCnt; i++ ){
d = (xbDbf *) GetDbfPtr( 1 );
if( d ){
if(( iRc = d->Close()) != XB_NO_ERROR ){
iErrorStop = 100;
throw iRc;
}
} else {
iRc = XB_INVALID_OBJECT;
iErrorStop = 110;
throw iRc;
}
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbxbase::CloseAllTables() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
WriteLogMessage( sMsg.Str() );
WriteLogMessage( GetErrorMessage( iRc ));
}
return iRc;
}
/*************************************************************************/
#ifdef XB_LOGGING_SUPPORT
//! @brief Get fully qualified log file name.
/*!
\returns Returns the fully qualified log file name.
*/
const xbString & xbXBase::GetLogFqFileName() const {
return xLog->GetFqFileName();
}
//! @brief Get the log file name.
/*!
\returns Returns the log file name.
*/
const xbString & xbXBase::GetLogFileName() const {
return xLog->GetFileName();
}
//! @brief Get the log directory.
/*!
\returns Returns the log directory.
*/
const xbString & xbXBase::GetLogDirectory() const {
return xLog->GetDirectory();
}
//! @brief Get the log directory.
/*!
\returns xbTrue - Logging enabled.
xbFalse - Logging disables.
*/
xbBool xbXBase::GetLogStatus() const {
return xLog->LogGetStatus();
}
//! @brief Set the log file name.
/*!
\param sLogFileName - Log File Name.
\return void
*/
void xbXBase::SetLogFileName( const xbString & sLogFileName ){
xLog->SetFileName( sLogFileName );
}
//! @brief Set the log directory.
/*!
\param sLogDirectory - Log File Directory.
\return void
*/
void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){
xLog->SetDirectory( sLogDirectory );
}
//! @brief Set the logfile size.
/*!
\param lSize - Log File Size.
\return void
*/
void xbXBase::SetLogSize( size_t lSize ) {
xLog->LogSetLogSize( lSize );
}
//! @brief Write message to logfile.
/*!
\param sLogMessage - Message to write.
\param iOpt 0 = stdout
1 = Syslog
2 = Both
\returns Return Codes
*/
xbInt16 xbXBase::WriteLogMessage( const xbString & sLogMessage, xbInt16 iOpt ){
return xLog->LogWrite( sLogMessage, iOpt );
}
//! @brief Write message to logfile.
/*!
\param lCnt - Number of bytes to write.
\param p - Pointer to bytes to write to log file.
\returns Return Codes
*/
xbInt16 xbXBase::WriteLogBytes( xbUInt32 lCnt, const char *p ){
return xLog->LogWriteBytes( lCnt, p );
}
//! @brief Enable message logging.
void xbXBase::EnableMsgLogging() {
xLog->LogSetStatus( xbTrue );
}
//! @brief Disable message logging.
void xbXBase::DisableMsgLogging() {
xLog->LogSetStatus( xbFalse );
}
//! @brief Flush log file updates to disk.
xbInt16 xbXBase::FlushLog() {
return xLog->xbFflush();
}
#else
// if logging not compiled in, these stubs are called with no effect
const xbString & xbXBase::GetLogFqFileName() const {
return sNullString;
}
const xbString & xbXBase::GetLogFileName() const {
return sNullString;
}
const xbString & xbXBase::GetLogDirectory() const {
return sNullString;
}
void xbXBase::SetLogFileName( const xbString & sLogFileName ){
return;
}
void xbXBase::SetLogDirectory( const xbString & sLogDirectory ){
return;
}
xbBool xbXBase::GetLogStatus() const {
return xbFalse;
}
xbInt16 xbXBase::WriteLogMessage( const xbString & sLogMessage, xbInt16 ){
return XB_NO_ERROR;
}
xbInt16 xbXBase::WriteLogBytes( xbUInt32 lCnt, const char *p ){
return XB_NO_ERROR;
}
void xbXBase::EnableMsgLogging() {
return;
}
void xbXBase::DisableMsgLogging() {
return;
}
xbInt16 xbXBase::FlushLog() {
return XB_NO_ERROR;
}
void xbXBase::SetLogSize( size_t lSize ) {
return;
}
#endif // XB_LOGGING_SUPPORT
/*************************************************************************/
#ifdef XB_FUNCTION_SUPPORT
//! @brief Get information regarding expression functions.
/*!
\param sExpLine An expression beginning with function name.
\param cReturnType Output - return type of function.
\param iCalc Used to calculate the function return value is
1 = use value specified in lReturnLenVal
2 = use length of operand specified in col 4
3 = use valued of numeric operand specified in col 4
4 = length of parm 1 * numeric value parm
5 = larger length of parm 2 or length of parm 3
6 = if two or more parms, use numeric value from second parm, otherwise use col4 value
\param lReturnLenVal Used in combination with iReturnLenCalc.
\returns Return Codes
*/
xbInt16 xbXBase::GetFunctionInfo( const xbString &sExpLine, char &cReturnType, xbInt16 &iCalc, xbInt32 &lReturnLenVal ) const{
xbUInt32 iLen;
const char *s;
if( sExpLine.Len() == 0 )
return XB_INVALID_FUNCTION;
s = sExpLine;
iLen = 0;
while( *s && *s != '(' && *s != ' ' ) { s++; iLen++; }
xbString sFunction( sExpLine, iLen );
cReturnType = 0x00;
char cFunc1 = sFunction[1];
if( cFunc1 < 'L' ){
// std::cout << "less than L\n";
if( cFunc1 < 'D' ){
// std::cout << "less than D\n";
if( sFunction == "ABS" ){
// { "ABS", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "ALLTRIM" ){
// { "ALLTRIM", 'C', 2, 1 },
cReturnType = 'C';
iCalc = 2;
lReturnLenVal = 1;
} else if( sFunction == "ASC" ){
// { "ASC", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "AT" ){
// { "AT", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "CDOW" ){
// { "CDOW", 'C', 1, 9 },
cReturnType = 'C';
iCalc = 1;
lReturnLenVal = 9;
} else if( sFunction == "CHR" ){
// { "CHR", 'C', 1, 1 },
cReturnType = 'C';
iCalc = 1;
lReturnLenVal = 1;
} else if( sFunction == "CMONTH" ){
// { "CMONTH", 'C', 1, 9 },
cReturnType = 'C';
iCalc = 1;
lReturnLenVal = 9;
} else if( sFunction == "CTOD" ){
// { "CTOD", 'D', 1, 8 },
cReturnType = 'D';
iCalc = 1;
lReturnLenVal = 8;
}
} else {
// std::cout << ">= D\n";
if( sFunction == "DATE" ){
// { "DATE", 'D', 1, 8 },
cReturnType = 'D';
iCalc = 1;
lReturnLenVal = 8;
} else if( sFunction == "DAY" ){
// { "DAY", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "DEL" ){
// { "DEL", 'C', 1, 1 },
cReturnType = 'C';
iCalc = 1;
lReturnLenVal = 1;
} else if( sFunction == "DELETED" ){
// { "DELETED", 'L', 1, 1 },
cReturnType = 'L';
iCalc = 1;
lReturnLenVal = 1;
} else if( sFunction == "DESCEND" ){
// { "DESCEND", '1', 2, 1 },
cReturnType = '1';
iCalc = 2;
lReturnLenVal = 1;
} else if( sFunction == "DOW" ){
// { "DOW", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "DTOC" ){
// { "DTOC", 'C', 1, 8 },
cReturnType = 'C';
iCalc = 1;
lReturnLenVal = 8;
} else if( sFunction == "DTOS" ){
// { "DTOS", 'C', 1, 8 },
cReturnType = 'C';
iCalc = 1;
lReturnLenVal = 8;
} else if( sFunction == "EXP" ){
// { "EXP", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "IIF" ){
// { "IIF", 'C', 5, 0 },
cReturnType = 'C';
iCalc = 5;
lReturnLenVal = 0;
} else if( sFunction == "INT" ){
// { "INT", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "ISALPHA" ){
// { "ISALPHA", 'L', 1, 1 },
cReturnType = 'L';
iCalc = 1;
lReturnLenVal = 1;
} else if( sFunction == "ISLOWER" ){
// { "ISLOWER", 'L', 1, 1 },
cReturnType = 'L';
iCalc = 1;
lReturnLenVal = 1;
} else if( sFunction == "ISUPPER" ){
// { "ISUPPER", 'L', 1, 1 },
cReturnType = 'L';
iCalc = 1;
lReturnLenVal = 1;
}
}
} else {
// std::cout << ">= L\n";
if( cFunc1 < 'R' ) {
// std::cout << " < R\n";
if( sFunction == "LEFT" ){
// { "LEFT", 'C', 3, 2 },
cReturnType = 'C';
iCalc = 3;
lReturnLenVal = 2;
} else if( sFunction == "LEN" ){
// { "LEN", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 3;
} else if( sFunction == "LOG" ){
// { "LOG", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "LOWER" ){
// { "LOWER", 'C', 2, 1 },
cReturnType = 'C';
iCalc = 2;
lReturnLenVal = 1;
} else if( sFunction == "LTRIM" ){
// { "LTRIM", 'C', 2, 1 },
cReturnType = 'C';
iCalc = 2;
lReturnLenVal = 1;
} else if( sFunction == "MAX" ){
// { "MAX", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "MIN" ){
// { "MIN", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "MONTH" ){
// { "MONTH", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
}
} else if( cFunc1 == 'R' ){
// std::cout << "==R\n";
if( sFunction == "RECNO" ){
// { "RECNO", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "RECCOUNT" ){
// { "RECCOUNT", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "REPLICATE" ){
// { "REPLICATE", 'C', 4, 0 },
cReturnType = 'C';
iCalc = 4;
lReturnLenVal = 0;
} else if( sFunction == "RIGHT" ){
// { "RIGHT", 'C', 3, 2 },
cReturnType = 'C';
iCalc = 3;
lReturnLenVal = 2;
} else if( sFunction == "RTRIM" ){
// { "RTRIM", 'C', 2, 1 },
cReturnType = 'C';
iCalc = 2;
lReturnLenVal = 1;
}
} else if( cFunc1 == 'S' ){
// std::cout << "==S\n";
if( sFunction == "SPACE" ){
// { "SPACE", 'C', 3, 1 },
cReturnType = 'C';
iCalc = 3;
lReturnLenVal = 1;
} else if( sFunction == "SQRT" ){
// { "SQRT", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
} else if( sFunction == "STOD" ){
// { "STOD", 'D', 1, 8 },
cReturnType = 'D';
iCalc = 1;
lReturnLenVal = 8;
} else if( sFunction == "STR" ){
// { "STR", 'C', 6, 10 },
cReturnType = 'C';
iCalc = 6;
lReturnLenVal = 10;
} else if( sFunction == "STRZERO" ){
// { "STRZERO", 'C', 3, 2 },
cReturnType = 'C';
iCalc = 3;
lReturnLenVal = 2;
} else if( sFunction == "SUBSTR" ){
// { "SUBSTR", 'C', 3, 3 },
cReturnType = 'C';
iCalc = 3;
lReturnLenVal = 3;
}
} else {
// std::cout << ">S\n";
if( sFunction == "TRIM" ){
// { "TRIM", 'C', 2, 1 },
cReturnType = 'C';
iCalc = 2;
lReturnLenVal = 1;
} else if( sFunction == "UPPER" ){
// { "UPPER", 'C', 2, 1 },
cReturnType = 'C';
iCalc = 2;
lReturnLenVal = 1;
} else if( sFunction == "VAL" ){
// { "VAL", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 3;
} else if( sFunction == "YEAR" ){
// { "YEAR", 'N', 1, 4 },
cReturnType = 'N';
iCalc = 1;
lReturnLenVal = 4;
}
}
}
if( cReturnType == 0x00 )
return XB_INVALID_FUNCTION;
else
return XB_NO_ERROR;
}
#endif
/*************************************************************************/
//! @brief Cross platform sleep function.
/*!
\param lMillisecs Milliseconds to sleep.
*/
void xbXBase::xbSleep( xbInt32 lMillisecs ){
#ifdef WIN32
Sleep( lMillisecs );
#else
usleep( (xbInt64) lMillisecs * 1000 );
#endif
}
/***********************************************************************/
//! @brief Cross memcmp function.
/*!
\param s1 Left operand to compare.
\param s2 Right operand to compare.
\param n Number of bytes to compare.
\returns 1 s1 > s2
0 s1 == s2
-1 s1 < s2
*/
xbInt16 xbXBase::xbMemcmp( const unsigned char *s1, const unsigned char *s2, size_t n ){
// The standard memcmp function was found not to behave the same across all platforms
for( size_t i = 0; i < n; i++ ){
if( s1[i] > s2[i] )
return 1;
else if( s1[i] < s2[i] )
return -1;
}
return 0;
}
/***********************************************************************/
//! @brief Open highest qualified class available for dbf file.
/*!
This routine opens the highest available version of the dbf file.
Defaults to XB_READ_WRITE and XB_MULTI_USER mode.
\returns param dbf - Output pointer to dbf file opened or null if error
*/
xbDbf* xbXBase::Open( const xbString &sTableName, xbInt16 &iRc ){
return Open( sTableName, "", XB_READ_WRITE, XB_MULTI_USER, 0, iRc );
}
/***********************************************************************/
//! @brief Open highest qualified class available for dbf file.
/*!
This routine can open various versions of the dbf file dependent on the iVersion field
\param sTableName - Table name to open.
\param sAlias - Optional alias name.
\param iOpenMode - XB_READ_WRITE or XB_READ
\param iShareMode - XB_SINGLE_USER or XB_MULTI_USER
\param iRequestVersion 0 - Highest available
4 - Version four dbf
3 - Version three dbf
\param iRc - Return code from open request
\returns param dbf - Output pointer to dbf file opened or null if error
*/
xbDbf* xbXBase::Open( const xbString &sTableName, const xbString &sAlias, xbInt16 iOpenMode,
xbInt16 iShareMode, xbInt16 iRequestVersion, xbInt16 &iRc ){
xbInt16 iErrorStop = 0;
xbDbf * pDbf = 0;
iRc = 0;
xbString sFqFileName;
try{
if( sTableName.Len() == 0 ){
iErrorStop = 100;
iRc = XB_FILE_NOT_FOUND;
throw iRc;
}
xbFile *f = new xbFile(this);
f->SetFileName( sTableName );
if(( iRc = f->FileExists( f->GetFqFileName())) != xbTrue ){
iErrorStop = 110;
iRc = XB_FILE_NOT_FOUND;
throw iRc;
}
unsigned char cFileTypeByte;
if(( iRc = f->GetXbaseFileTypeByte( f->GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){
iErrorStop = 120;
throw iRc;
}
xbInt16 iTblVsn = f->DetermineXbaseTableVersion( cFileTypeByte );
f->xbFclose();
sFqFileName.Set( f->GetFqFileName() );
delete f;
if( iTblVsn == 4 && ( iRequestVersion == 0 || iRequestVersion == 4 )){
#ifdef XB_DBF4_SUPPORT
pDbf = new xbDbf4( this );
iRc = pDbf->Open( sFqFileName, sAlias, iOpenMode, iShareMode );
#else
// std::cout << "Dbase IV file support not build into library. See XB_DBF4_SUPPORT" << std::endl;
iErrorStop = 130;
iRc = XB_FILE_TYPE_NOT_SUPPORTED;
throw iRc;
#endif
}
else if( iTblVsn == 3 && ( iRequestVersion == 0 || iRequestVersion == 3 )){
#ifdef XB_DBF3_SUPPORT
pDbf = new xbDbf3( this );
iRc = pDbf->Open( sFqFileName, sAlias, iOpenMode, iShareMode );
#else
//std::cout << "Dbase III file support not build into library. See XB_DBF3_SUPPORT" << std::endl;
iErrorStop = 140;
iRc = XB_FILE_TYPE_NOT_SUPPORTED;
throw iRc;
#endif
} else {
iErrorStop = 150;
iRc = XB_FILE_TYPE_NOT_SUPPORTED;
throw iRc;
}
if( iRc != XB_NO_ERROR ){
iErrorStop = 160;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbxbase::Open() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
WriteLogMessage( sMsg.Str() );
WriteLogMessage( GetErrorMessage( iRc ));
}
return pDbf;
}
xbInt16 xbXBase::OpenHighestVersion( const xbString &sTableName, const xbString &sAlias,
xbDbf **dbf )
{
xbInt16 iRc = 0;
xbInt16 iErrorStop = 0;
try{
xbFile f(this);
if( sTableName.Len() == 0 ){
iErrorStop = 100;
iRc = XB_FILE_NOT_FOUND;
throw iRc;
}
f.SetFileName( sTableName );
if(( iRc = f.FileExists( f.GetFqFileName() )) != xbTrue ){
iErrorStop = 110;
iRc = XB_FILE_NOT_FOUND;
throw iRc;
}
unsigned char cFileTypeByte;
if(( iRc = f.GetXbaseFileTypeByte( f.GetFqFileName(), cFileTypeByte )) != XB_NO_ERROR ){
iErrorStop = 120;
return iRc;
}
if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 4 ){
#ifdef XB_DBF4_SUPPORT
xbDbf *pwDbf = new xbDbf4( this );
pwDbf->Open( f.GetFqFileName(), sAlias );
*dbf = pwDbf;
pwDbf = 0;
#else
// std::cout << "Dbase IV file support not build into library. See XB_DBF4_SUPPORT" << std::endl;
iErrorStop = 130;
iRc = XB_FILE_TYPE_NOT_SUPPORTED;
throw iRc;
#endif
} else if( f.DetermineXbaseTableVersion( cFileTypeByte ) == 3 ){
#ifdef XB_DBF3_SUPPORT
*dbf = new xbDbf3( this );
#else
//std::cout << "Dbase III file support not build into library. See XB_DBF3_SUPPORT" << std::endl;
iErrorStop = 140;
iRc = XB_FILE_TYPE_NOT_SUPPORTED;
throw iRc;
#endif
} else {
iErrorStop = 150;
iRc = XB_FILE_TYPE_NOT_SUPPORTED;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbxbase::OpenHighestVersion() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc );
WriteLogMessage( sMsg.Str() );
WriteLogMessage( GetErrorMessage( iRc ));
}
return iRc;
}
/***********************************************************************/
} /* namespace */