diff options
Diffstat (limited to 'src/core/xbdbf4.cpp')
-rwxr-xr-x | src/core/xbdbf4.cpp | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/src/core/xbdbf4.cpp b/src/core/xbdbf4.cpp new file mode 100755 index 0000000..c421add --- /dev/null +++ b/src/core/xbdbf4.cpp @@ -0,0 +1,590 @@ +/* xbdbf4.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022 Gary A Kunkel + +The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. + +Email Contact: + + XDB-devel@lists.sourceforge.net + XDB-users@lists.sourceforge.net + +*/ + +#include "xbase.h" + + +#ifdef XB_DBF4_SUPPORT + +namespace xb{ + + +/************************************************************************/ +//! @brief Constructor. +xbDbf4::xbDbf4(xbXBase * x) : xbDbf( x ) { + + iFileVersion = 4; + #ifdef XB_MEMO_SUPPORT + ulCreateMemoBlockSize = 1024; + #endif +}; + +/************************************************************************/ +//! @brief Destructor. +xbDbf4::~xbDbf4() {}; + +/************************************************************************/ +//! @brief Create Version 4 table. +/*! + This routine creates a Dbase IV (tm) DBF file. + + \param sTableName DBF table name. + \param sAlias Table alias + \param pSchema Pointer to schema structure with field definitions. + \param iOverlay xbTrue - Overlay.<br> xbFalse - Don't overlay. + \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER + \returns <a href="xbretcod_8h.html">Return Codes</a> +*/ + +xbInt16 xbDbf4::CreateTable( const xbString &sTableName, const xbString &sAlias, xbSchema * pSchema, xbInt16 iOverlay, xbInt16 iShareMode ){ + + xbInt16 i, k, k2; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + iDbfStatus = XB_CLOSED; + + xbString sNfn; + + try{ + sNfn = sTableName; + xbase->GetLogStatus(); + + rc = NameSuffixMissing( sNfn, 1 ); + if( rc > 0 ) + sNfn += ".DBF"; + + SetFileName( sNfn ); + this->sAlias = sAlias; + + /* check if the file already exists */ + if( FileExists( 0 )){ + if( !iOverlay ){ + iErrorStop = 10; + rc = XB_FILE_EXISTS; + throw rc; + } + #ifdef XB_NDXINF_SUPPORT + // remove the metadata file if there is one + xbString sMname = sNfn; + xbUInt32 iMnameLen = sMname.Len(); + sMname.PutAt( iMnameLen-2, 'X' ); + sMname.PutAt( iMnameLen-1, 'B' ); + sMname.PutAt( iMnameLen, 'M' ); + xbFile fTemp( xbase ); + fTemp.SetFileName( sMname ); + if( fTemp.FileExists() ) + fTemp.xbRemove(); + #endif // XB_NDXINF_SUPPORT + } + + /* check if we already have a file with this alias */ + if(( rc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw rc; + } + + rc = ValidateSchema( pSchema ); + if( rc < 0 ){ + iErrorStop = 30; + throw rc; + } else + iNoOfFields = rc; + + #ifdef XB_MEMO_SUPPORT + // if we have memo fields + iMemoFieldCnt = 0; + i = 0; + while( pSchema[i].cType != 0){ + if( pSchema[i].cType == 'M' ) + iMemoFieldCnt++; /* number of memo fields in the incoming definition */ + i++; + } + + if( iMemoFieldCnt > 0 ){ + xbString sMfn = sNfn; /* memo file name, same as filename except ends with a "t", not an "f" */ + xbUInt32 ulMfnLen = sMfn.Len(); + sMfn.PutAt( ulMfnLen, 'T' ); + + // dont overlay the memo file if it exists, and Overlay switch is off + xbFile fTemp( xbase ); + fTemp.SetFileName( sMfn ); + if( fTemp.FileExists() && !iOverlay ){ + iErrorStop = 40; + rc = XB_FILE_EXISTS; + throw rc; + } + + Memo = new xbMemoDbt4( this, fTemp.GetFqFileName()); + + if(( rc = Memo->CreateMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 50; + throw rc; + } + } + #endif + + /* this is the dBase IV version of the class */ + cVersion = 0x03; // 0x03 for Dbase level 5 + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ +// cVersion = cVersion |= 0x88; // version IV memos, compiler complains about this + cVersion |= 0x88; // version IV memos + } + #endif + + if(( rc = xbFopen( "w+b", iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw rc; + } + uiRecordLen++; /* add one byte for 0x0D */ + + if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){ + iErrorStop = 70; + throw rc; + } + + if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ){ + iErrorStop = 80; + rc = XB_NO_MEMORY; + throw rc; + } + + /* BlankRecord(); */ + memset( RecBuf, 0x20, uiRecordLen ); + memset( RecBuf2, 0x20, uiRecordLen ); + ulCurRec = 0L; + uiHeaderLen = 33 + iNoOfFields * 32; + xbDate d; + cUpdateYY = (char) (d.YearOf() - 1900); + cUpdateMM = (char) d.MonthOf(); + cUpdateDD = (char) d.DayOf( XB_FMT_MONTH ); + + // Default language driver to 0x1b + cLangDriver = 0x1b; + + /* write the header prolog */ + if(( rc = WriteHeader( 0, 0 )) != XB_NO_ERROR ){ + iErrorStop = 90; + rc = XB_WRITE_ERROR; + throw rc; + } + if((SchemaPtr=(xbSchemaRec *)malloc( (size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){ + iErrorStop = 100; + rc = XB_NO_MEMORY; + throw rc; + } + + /* write the field information into the header */ + for( i = 0, k = 1; i < iNoOfFields; i++ ){ + + memset( SchemaPtr[i].cFieldName, 0x00, 11 ); + for( int x = 0; x < 10 && pSchema[i].cFieldName[x]; x++ ) + SchemaPtr[i].cFieldName[x] = pSchema[i].cFieldName[x]; + + SchemaPtr[i].cType = pSchema[i].cType; + SchemaPtr[i].cFieldLen = (unsigned char) pSchema[i].iFieldLen; + SchemaPtr[i].cNoOfDecs = (unsigned char) pSchema[i].iNoOfDecs; + + if( SchemaPtr[i].cNoOfDecs > SchemaPtr[i].cFieldLen ){ + iErrorStop = 110; + rc = XB_WRITE_ERROR; + throw rc; + } + + k2 = k; + k += SchemaPtr[i].cFieldLen; + + if(( xbFwrite( &SchemaPtr[i].cFieldName, 1, 11 )) != XB_NO_ERROR ) { + iErrorStop = 120; + rc = XB_WRITE_ERROR; + throw rc; + } + + if(( xbFwrite( &SchemaPtr[i].cType, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 130; + rc = XB_WRITE_ERROR; + throw rc; + } + + for( int j = 0; j < 4; j++ ) + xbFputc( 0x00 ); + + if(( xbFwrite( &SchemaPtr[i].cFieldLen, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 140; + rc = XB_WRITE_ERROR; + throw rc; + } + + if(( xbFwrite( &SchemaPtr[i].cNoOfDecs, 1, 1 )) != XB_NO_ERROR ) { + iErrorStop = 150; + rc = XB_WRITE_ERROR; + throw rc; + } + + /* 14 bytes reserved */ + for( int j = 0; j < 14; j++ ) + xbFputc( 0x00 ); + + SchemaPtr[i].pAddress = RecBuf + k2; + SchemaPtr[i].pAddress2 = RecBuf2 + k2; + } + + /* write the header terminator */ + if(( xbFputc( XB_CHARHDR )) != XB_NO_ERROR ){ + iErrorStop = 160; + rc = XB_WRITE_ERROR; + throw rc; + } + } + catch( xbInt16 rc ) + { + xbString sMsg; + sMsg.Sprintf( "xbdbf4::CreateTable() Exception Caught Error Stop = %d rc = %d", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + + sMsg.Sprintf( "Table Name = [%s]", GetFqFileName().Str()); + xbase->WriteLogMessage( sMsg ); + sMsg.Sprintf( "Alias Name = [%s]", sAlias.Str()); + xbase->WriteLogMessage( sMsg ); + + xbFclose(); + if( RecBuf ){ + free( RecBuf ); + RecBuf = NULL; + } + if( RecBuf2 ){ + free( RecBuf2 ); + RecBuf2 = NULL; + } + if( SchemaPtr ){ + free( SchemaPtr ); + SchemaPtr = NULL; + } + + InitVars(); + if( rc != XB_FILE_EXISTS ) + xbase->RemoveTblFromTblList( sAlias ); + } + + if( rc == XB_NO_ERROR ) + iDbfStatus = XB_OPEN; + + return rc; +} + +/************************************************************************/ +//! @brief Get version. +/*! + The version info can be retrieved to determine + which class is being used for a given dbf instance. + \returns 4 +*/ + +xbInt16 xbDbf4::GetVersion() const { + return 4; +} + +/************************************************************************/ +//! @brief Open dbf file/table. +/*! + \param sTableName DBF table name. + \param sAlias Table alias + \param iOpenMode XB_READ<br>XB_READ_WRITE + \param iShareMode XB_SINGLE_USER<br>XB_MULTI_USER + \returns <a href="xbretcod_8h.html">Return Codes</a> +*/ + +xbInt16 xbDbf4::Open( const xbString & sTableName, const xbString & sAlias, + xbInt16 iOpenMode, xbInt16 iShareMode ){ + xbInt16 i, j, iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char buf[33]; + char *p; + + #ifdef XB_MEMO_SUPPORT + iMemoFieldCnt = 0; + #endif + + try{ + /* verify the file is not already open */ + if( iDbfStatus != XB_CLOSED ){ + iErrorStop = 10; + iRc = XB_ALREADY_OPEN; + throw iRc; + } + /* copy the file name to the class variable */ + SetFileName( sTableName ); + this->sAlias = sAlias; + + if( !FileExists()){ + iErrorStop = 20; + iRc = XB_FILE_NOT_FOUND; + throw iRc; + } + + if(( iRc = xbase->AddTblToTblList( this, GetFqFileName(), sAlias )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + + /* open the file */ + if(( iRc = xbFopen( iOpenMode, iShareMode )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + + /* copy the header into memory */ + if(( iRc = ReadHeader( 1, 0 )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + + /* check the version */ + //if(( xFileVersion = DetermineXbaseTableVersion( cVersion )) != 4 ){ + if(( cVersion & 0x07 ) != 3 ){ + iErrorStop = 60; + iRc = XB_FILE_TYPE_NOT_SUPPORTED; + throw iRc; + } + iFileVersion = 4; + + /* calculate the number of fields */ + if( cVersion == (char)0x30 ) { + iNoOfFields = ( uiHeaderLen - 296 ) / 32 ; + } else { + iNoOfFields = ( uiHeaderLen - 33 ) / 32; + } + + if(( RecBuf = (char *) malloc( uiRecordLen )) == NULL ){ + iErrorStop = 70; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if(( RecBuf2 = (char *) malloc( uiRecordLen )) == NULL ) { + iErrorStop = 80; + iRc = XB_NO_MEMORY; + throw iRc; + } + + if((SchemaPtr=(xbSchemaRec *)malloc((size_t) iNoOfFields * sizeof( xbSchemaRec ))) == NULL){ + iErrorStop = 90; + iRc = XB_NO_MEMORY; + throw iRc; + } + + memset( SchemaPtr, 0x00, (size_t) iNoOfFields * (size_t) sizeof( xbSchemaRec )); + /* copy field info into memory */ + for( i = 0, j = 1; i < iNoOfFields; i++ ){ + xbFseek( ((xbInt64)i*32+32), SEEK_SET ); + xbFread( &buf, 1, 32 ); + p = buf; + for( int x = 0; x < 10 && buf[x]; x++ ){ + SchemaPtr[i].cFieldName[x] = buf[x]; + } + p = buf + 11; + SchemaPtr[i].cType = *p++; + SchemaPtr[i].pAddress = RecBuf + j; + SchemaPtr[i].pAddress2 = RecBuf2 + j; + SchemaPtr[i].cFieldLen = (unsigned char) *( p + 4 ); + SchemaPtr[i].cNoOfDecs = (unsigned char) *( p + 5 ); + SchemaPtr[i].cIxFlag = (unsigned char) *( p + 19 ); + j += SchemaPtr[i].cFieldLen; + #ifdef XB_MEMO_SUPPORT + if( (SchemaPtr[i].cType == 'M' || SchemaPtr[i].cType == 'B' || SchemaPtr[i].cType == 'O' )) + iMemoFieldCnt++; + #endif + } + ulCurRec = 0L; + iDbfStatus = XB_OPEN; + if(( iRc = BlankRecord()) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + + #ifdef XB_MEMO_SUPPORT + if( iMemoFieldCnt > 0 ){ /* does this table have memo fields ? */ + + // build the file name + xbString sMfn = GetFqFileName(); /* memo file name, same as filename except ends with a "t", not an "f" */ + xbUInt32 ulMfnLen = sMfn.Len(); + if( sMfn[ulMfnLen] == 'F' ) + sMfn.PutAt( ulMfnLen, 'T' ); + else + sMfn.PutAt( ulMfnLen, 't' ); + xbFile fTemp( xbase ); + fTemp.SetFileName( sMfn ); + + Memo = new xbMemoDbt4( this, fTemp.GetFqFileName()); + + if(( iRc = Memo->OpenMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 110; + throw iRc; + } + } + #endif + + #ifdef XB_MDX_SUPPORT + if( cIndexFlag ){ + // create the file name + xbString sIxFileName = GetFqFileName(); + sIxFileName.Trim(); + xbUInt32 lLen = sIxFileName.Len(); + sIxFileName.PutAt( lLen-2, 'M' ); + sIxFileName.PutAt( lLen-1, 'D' ); + sIxFileName.PutAt( lLen, 'X' ); + if(( iRc = OpenIndex( "MDX", sIxFileName )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + } + #endif + + } + catch ( xbInt16 iRc ) + { + xbString sMsg; + sMsg.Sprintf( "xbdbf4::Open() Exception Caught Error Stop = %d iRc = %d ShareMode = %d OpenMode = %d", iErrorStop, iRc, iShareMode, iOpenMode ); + xbase->WriteLogMessage( sMsg ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + xbFclose(); + if( RecBuf ){ + free( RecBuf ); + RecBuf = NULL; + } + if( RecBuf2 ){ + free( RecBuf2 ); + RecBuf2 = NULL; + } + if( SchemaPtr ){ + free( SchemaPtr ); + SchemaPtr = NULL; + } + InitVars(); + +#ifdef XB_MEMO_SUPPORT + if( Memo ){ + Memo->CloseMemoFile(); + delete Memo; + Memo = NULL; + } +#endif + } + + if( iRc == XB_NO_ERROR ) + iDbfStatus = XB_OPEN; + + return iRc; +} + + +/************************************************************************/ +#ifdef XB_MEMO_SUPPORT + +//! @brief Create memo block size. +/*! + This routine sets the memo file block size. This value is used when + the memo file is created so you if you want to change it, this must be + called before creating the table. + + The default size for version 4 is 1024. + + \param ulBlockSize - Block size, must be evenly divisible by 512. + \returns XB_INVALID_BLOCK_SIZE<br>XB_NO_ERROR +*/ + +xbInt16 xbDbf4::SetCreateMemoBlockSize( xbUInt32 ulBlockSize ){ + + if( ulBlockSize % 512 ) + return XB_INVALID_BLOCK_SIZE; + else + ulCreateMemoBlockSize = ulBlockSize; + + return XB_NO_ERROR; +} +#endif // XB_MEMO_SUPPORT +/************************************************************************/ +//! @brief Set version. +/*! + Sets the version to 4. The version info can be retrieved to determine + which class is being used for a given dbf instance. + \returns 4 +*/ +xbInt16 xbDbf4::SetVersion() { + iFileVersion = 4; + return iFileVersion; +} +/************************************************************************/ +//! @brief Validate schema +/*! + This routine verifies the field types are valid for Dbase IV (tm). + + \param s Pointer to schema structure with field definitions. + + \returns Number of fields or XB_INVALID_FIELD_TYPE. +*/ + + +xbInt16 xbDbf4::ValidateSchema( xbSchema * s ){ + + xbInt16 iFieldCnt = 0; + uiRecordLen = 0; + + // Count the number of fields and check paramaters + xbInt16 i = 0; + while( s[i].cType != 0 ){ + iFieldCnt++; + // Version IV field types + if( s[i].cType != 'C' && + s[i].cType != 'N' && + s[i].cType != 'F' && + s[i].cType != 'D' && + #ifdef XB_MEMO_SUPPORT + s[i].cType != 'M' && + #endif /* XB_MEMO_SUPPORT */ + s[i].cType != 'L' ){ + return XB_INVALID_FIELD_TYPE; + } + + if(s[i].cType == 'D'){ + s[i].iFieldLen = 8; + s[i].iNoOfDecs = 0; + } + + else if(s[i].cType == 'C') + s[i].iNoOfDecs = 0; + + // check for numeric fields which are too long + else if((s[i].cType == 'N' || s[i].cType == 'F') && s[i].iFieldLen > 19 ){ + return XB_INVALID_FIELD_LEN; + } + + #ifdef XB_MEMO_SUPPORT + else if(s[i].cType == 'M'){ + s[i].iFieldLen = 10; + s[i].iNoOfDecs = 0; + } + #endif // XB_MEMO_SUPPORT + + uiRecordLen += s[i].iFieldLen; + i++; + } + return iFieldCnt; +} + +} /* namespace */ +#endif /* XB_DBF4_SUPPORT */
\ No newline at end of file |