From 4875a3dd9b183dcd2256e2abfc4ccf7484c233b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 7 Dec 2022 13:17:14 +0100 Subject: New upstream version 4.0.2 --- src/core/xbmemo3.cpp | 590 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 590 insertions(+) create mode 100755 src/core/xbmemo3.cpp (limited to 'src/core/xbmemo3.cpp') diff --git a/src/core/xbmemo3.cpp b/src/core/xbmemo3.cpp new file mode 100755 index 0000000..b5494fb --- /dev/null +++ b/src/core/xbmemo3.cpp @@ -0,0 +1,590 @@ +/* xbmemo3.cpp + +XBase64 Software Library + +Copyright (c) 1997,2003,2014,2022 Gary A Kunkel + +The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. + +Email Contact: + + XDB-devel@lists.sourceforge.net + XDB-users@lists.sourceforge.net + + This class is used for support dBASE V3 memo files + +*/ + +#include "xbase.h" + +#ifdef XB_MEMO_SUPPORT +#ifdef XB_DBF3_SUPPORT + +namespace xb{ + +/***********************************************************************/ +//! @brief Class Constructor. +/*! + \param dbf Pointer to dbf instance. + \param sFileName Memo file name. +*/ +xbMemoDbt3::xbMemoDbt3( xbDbf * dbf, xbString const & sFileName ) : xbMemo( dbf, sFileName ){ + iMemoFileType = 3; + SetBlockSize( 512 ); +} + +/***********************************************************************/ +//! @brief Class Deconstructor. +xbMemoDbt3::~xbMemoDbt3(){} + +/***********************************************************************/ +//! @brief Abort. +/*! + Abort any pending updates to the memo file. + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::Abort(){ + return XB_NO_ERROR; +}/***********************************************************************/ +//! @brief Commit changes to memo file. +/*! + \returns XB_NO_ERROR. +*/ +xbInt16 xbMemoDbt3::Commit(){ + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Create memo file. +/*! + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::CreateMemoFile(){ + + xbInt16 rc = XB_NO_ERROR; + char cBuf[4]; + if(( rc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ) + return rc; + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + if(( rc = xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){ + xbFclose(); + return rc; + } + for(int i = 0; i < 12; i++ ) + xbFputc( 0x00 ); + xbFputc( 0x03 ); + for(int i = 0; i < 495; i++ ) + xbFputc( 0x00 ); + if(( mbb = (void *) malloc( 512 )) == NULL ){ + xbFclose(); + return XB_NO_MEMORY; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +#ifdef XB_DEBUG_SUPPORT +//! @brief Dump memo file header. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::DumpMemoFreeChain() { + std::cout << "Xbase version 3 file - no free block chain" << std::endl; + return XB_NO_ERROR; +} + +//! @brief Dump memo file header. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::DumpMemoHeader(){ + xbInt16 rc = XB_NO_ERROR; + xbUInt64 stFileSize; + if(( rc = ReadDbtHeader( 1 )) != XB_NO_ERROR ) + return rc; + GetFileSize( stFileSize ); + std::cout << "Version 3 Memo Header Info" << std::endl; + std::cout << "Memo File Name = " << GetFqFileName() << std::endl; + std::cout << "Next Available Block = " << ulHdrNextBlock << std::endl; + std::cout << "Memo File Version = " << (xbInt16) cVersion << " ("; + BitDump( cVersion ); + std::cout << ")" << std::endl; + std::cout << "Block Size = " << GetBlockSize() << std::endl; + std::cout << "File Size = " << stFileSize << std::endl; + std::cout << "Block Count = " << stFileSize / GetBlockSize() << std::endl; + return XB_NO_ERROR; +} +#endif // XB_DEBUG_SUPPORT +/***********************************************************************/ +//! @brief Get a memo field for a given field number. +/*! + \param iFieldNo Field number to retrieve data for. + \param sMemoData Output - string containing memo field data. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::GetMemoField( xbInt16 iFieldNo, xbString & sMemoData ){ + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbUInt32 ulScnt; + char *sp, *spp; + xbUInt32 ulBlockNo; + xbBool bDone = xbFalse; + sMemoData = ""; + try{ + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 10; + throw rc; + } + if( ulBlockNo == 0L ){ + sMemoData = ""; + return XB_NO_ERROR; + } + spp = NULL; + while( !bDone ){ + if(( rc = ReadBlock( ulBlockNo++, GetBlockSize(), mbb )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw rc; + } + ulScnt = 0; + sp = (char *) mbb; + while( ulScnt < 512 && !bDone ){ + if( *sp == 0x1a && *spp == 0x1a ) + bDone = xbTrue; + else{ + ulScnt++; spp = sp; sp++; + } + } + sMemoData.Append( (char *) mbb, ulScnt ); + } + sMemoData.ZapTrailingChar( 0x1a ); + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::GetMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Get a memo field length for a given field number. +/*! + \param iFieldNo Field number to retrieve data for. + \param ulFieldLen Output - length of memo field data. + \returns Return Codes +*/ +xbInt16 xbMemoDbt3::GetMemoFieldLen( xbInt16 iFieldNo, xbUInt32 & ulFieldLen ){ + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iScnt; + char *sp, *spp; + xbUInt32 ulBlockNo; + xbInt16 iNotDone; + try{ + if(( rc = dbf->GetULongField( iFieldNo, ulBlockNo )) < XB_NO_ERROR ){ + iErrorStop = 10; + throw rc; + } + if( ulBlockNo == 0 ){ + ulFieldLen = 0; + return XB_NO_ERROR; + } + ulFieldLen = 0L; + spp = NULL; + iNotDone = 1; + while( iNotDone ){ + if(( rc = ReadBlock( ulBlockNo++, GetBlockSize(), mbb )) != XB_NO_ERROR ){ + iErrorStop = 1; + throw rc; + } + iScnt = 0; + sp = (char *) mbb; + while( iScnt < 512 && iNotDone ){ + if( *sp == 0x1a && *spp == 0x1a ) + iNotDone = 0; + else{ + ulFieldLen++; iScnt++; spp = sp; sp++; + } + } + } + if( ulFieldLen > 0 ) ulFieldLen--; + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::GetMemoFieldLen() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Open memo file. +/*! + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::OpenMemoFile() { + xbInt16 rc = XB_NO_ERROR; + if(( rc = xbFopen( dbf->GetOpenMode(), dbf->GetShareMode())) != XB_NO_ERROR ) + return rc; + if(( mbb = (void *) malloc( 512 )) == NULL ){ + xbFclose(); + return XB_NO_MEMORY; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Pack memo file. +/*! + This routine frees up any unused blocks in the file resulting from field updates. + Version 3 memo files do not reclaim unused space (Version 4 files do). + This routine cleans up the unused space. + \returns Return Codes +*/ +xbInt16 xbMemoDbt3::PackMemo( void (*memoStatusFunc ) ( xbUInt32 ulItemNum, xbUInt32 ulNumItems )) +{ + xbInt16 iRc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + char * cBlock = NULL; + + #ifdef XB_LOCKING_SUPPORT + xbInt16 iAutoLock = dbf->GetAutoLock(); + xbBool bTableLocked = xbFalse; + xbBool bMemoLocked = xbFalse; + #endif + + try{ + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !dbf->GetTableLocked() ){ + if(( iRc = dbf->LockTable( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } else { + bTableLocked = xbTrue; + } + if(( iRc = LockMemo( XB_LOCK )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } else { + bMemoLocked = xbTrue; + } + } + #endif + + // create temp file + xbString sTempMemoName; + if(( iRc = CreateUniqueFileName( GetDirectory(), "dbt", sTempMemoName )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + + xbMemoDbt3 *pMemo = new xbMemoDbt3( dbf, sTempMemoName ); + if(( iRc = pMemo->CreateMemoFile()) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + // for dbase III, block size is always 512, don't need to reset it + // for each record in dbf + xbUInt32 ulRecCnt; + if(( iRc = dbf->GetRecordCnt( ulRecCnt )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + xbInt32 lFldCnt = dbf->GetFieldCnt(); + char cFldType; + xbString sMemoFldData; + + for( xbUInt32 ulI = 1; ulI <= ulRecCnt; ulI++ ){ + if(( iRc = dbf->GetRecord( ulI )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + if( (void *) memoStatusFunc ) + (*memoStatusFunc) ( ulI, ulRecCnt ); + + // for each memo field + for( xbInt32 lFc = 0; lFc < lFldCnt; lFc++ ){ + if(( iRc = dbf->GetFieldType( lFc, cFldType )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw iRc; + } + if( cFldType == 'M' ){ + // copy it to work field + if(( iRc = dbf->GetMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw iRc; + } + // write it to new field + if(( iRc = pMemo->UpdateMemoField( lFc, sMemoFldData )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc; + } + } + } + } + + //copy target back to source + xbUInt32 ulBlkSize = GetBlockSize(); + xbUInt64 ullFileSize; + if(( iRc = pMemo->GetFileSize( ullFileSize )) != XB_NO_ERROR ){ + iErrorStop = 80; + throw iRc; + } + // file size should be evenly divisible by block size + xbUInt32 ulBlkCnt; + + if( ullFileSize % ulBlkSize ){ + iErrorStop = 90; + throw iRc; + } else { + ulBlkCnt = (xbUInt32) (ullFileSize / ulBlkSize); + } + if(( iRc = xbTruncate( 0 )) != XB_NO_ERROR ){ + iErrorStop = 100; + throw iRc; + } + + if(( cBlock = (char *) malloc( (size_t) ulBlkSize )) == NULL ){ + iErrorStop = 110; + throw iRc; + } + + // can't rename files in a multiuser, cross platform environment, causes issues + // copy work table back to source table + for( xbUInt32 ulBc = 0; ulBc < ulBlkCnt; ulBc++ ){ + if(( iRc = pMemo->ReadBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){ + iErrorStop = 120; + throw iRc; + } + if(( iRc = WriteBlock( ulBc, ulBlkSize, cBlock )) != XB_NO_ERROR ){ + iErrorStop = 130; + throw iRc; + } + } + //close and delete target + if(( iRc = pMemo->xbFclose()) != XB_NO_ERROR ){ + iErrorStop = 140; + throw iRc; + } + if(( iRc = pMemo->xbRemove()) != XB_NO_ERROR ){ + iErrorStop = 150; + throw iRc; + } + free( cBlock ); + delete pMemo; + + #ifdef XB_LOCKING_SUPPORT + if( iAutoLock && !dbf->GetTableLocked() ){ + if(( iRc = dbf->LockTable( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 200; + throw iRc; + } + if(( iRc = LockMemo( XB_UNLOCK )) != XB_NO_ERROR ){ + iErrorStop = 210; + throw iRc; + } + } + #endif + } + catch (xbInt16 iRc ){ + free( cBlock ); + + #ifdef XB_LOCKING_SUPPORT + if( bTableLocked ) + dbf->LockTable( XB_UNLOCK ); + if( bMemoLocked ) + LockMemo( XB_UNLOCK ); + #endif + + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::PackMemo() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +//! @brief Read dbt header file. +/*! + \param iOption 0 --> read only first four bytes
+ 1 --> read the entire thing + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::ReadDbtHeader( xbInt16 iOption ){ + char *p; + char MemoBlock[20]; + xbInt16 rc = XB_NO_ERROR; + xbInt16 iErrorStop = 0; + xbUInt32 ulReadSize; + + try{ + if( !FileIsOpen() ){ + iErrorStop = 10; + rc = XB_NOT_OPEN; + throw rc; + } + if( iOption == 0 ) + ulReadSize = 4; + else{ + xbUInt64 stFileSize = 0; + if(( rc = GetFileSize( stFileSize )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw rc; + } + if( stFileSize < 4 ){ + iErrorStop = 30; + rc = XB_INVALID_BLOCK_NO; + throw rc; + } + else if( stFileSize > 20 ) + ulReadSize = 40; + else + ulReadSize = 4; + } + if( xbFseek( 0, SEEK_SET )){ + iErrorStop = 50; + rc = XB_SEEK_ERROR; + throw rc; + } + if(( xbFread( &MemoBlock, ulReadSize, 1 )) != XB_NO_ERROR ){ + iErrorStop = 60; + rc = XB_READ_ERROR; + throw rc; + } + p = MemoBlock; + ulHdrNextBlock = eGetUInt32( p ); + + if( iOption == 0) + return XB_NO_ERROR; + + if( ulReadSize >= 20 ){ + p+=16; + cVersion = *p; + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::ReadDbtHeader() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} +/***********************************************************************/ +//! @brief Update header name. +/*! + \returns XB_NO_ERROR +*/ +xbInt16 xbMemoDbt3::UpdateHeaderName(){ + return XB_NO_ERROR; +} +/***********************************************************************/ +//! @brief Update a memo field for a given field number. +/*! + \param iFieldNo Field number to update data for. + \param sMemoData Data to update memo field data with. + \returns Return Codes +*/ + +xbInt16 xbMemoDbt3::UpdateMemoField( xbInt16 iFieldNo, const xbString & sMemoData ) { + + xbInt16 iErrorStop = 0; + xbInt16 rc = XB_NO_ERROR; + try{ + if( sMemoData == "" ){ + if(( rc = dbf->PutField( iFieldNo, "" )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw rc; + } + } else { + xbUInt32 ulDataLen = sMemoData.Len() + 2; + xbUInt32 ulBlocksNeeded = (ulDataLen / 512) + 1; + xbUInt32 ulLastDataBlock; + if(( rc = CalcLastDataBlock( ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw rc; + } + if(( rc = xbFseek( ((xbInt64) ulLastDataBlock * 512), SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw rc; + } + if(( rc = xbFwrite( sMemoData.Str(), sMemoData.Len(), 1 )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw rc; + } + if(( rc = xbFputc( 0x1a, 2 )) != XB_NO_ERROR ){ + iErrorStop = 50; + throw rc; + } + if(( rc = xbFputc( 0x00, (xbInt32) ( ulBlocksNeeded * 512 ) - (xbInt32) ulDataLen )) != XB_NO_ERROR ){ + iErrorStop = 60; + throw rc; + } + if(( rc = dbf->PutULongField( iFieldNo, ulLastDataBlock )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw rc; + } + ulHdrNextBlock = ulLastDataBlock + ulBlocksNeeded; + if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR ){ + iErrorStop = 80; + throw rc; + } + } + } + catch (xbInt16 rc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::UpdateMemoField() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, rc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( rc )); + } + return rc; +} + +/***********************************************************************/ +//! @brief Empty the memo file. +/*! + This routine clears everything out of the file. It does not address the + block pointers on the dbf file. + \returns Return Codes +*/ +xbInt16 xbMemoDbt3::Zap(){ + + xbInt16 iRc = 0; + xbInt16 iErrorStop = 0; + char cBuf[4]; + + try{ + ulHdrNextBlock = 1L; + ePutUInt32( cBuf, ulHdrNextBlock ); + + if(( iRc != xbFseek( 0, SEEK_SET )) != XB_NO_ERROR ){ + iErrorStop = 10; + throw iRc; + } + if(( iRc != xbFwrite( cBuf, 4, 1 ))!= XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + if(( iRc != xbTruncate( 512 )) != XB_NO_ERROR ){ + iErrorStop = 30; + throw iRc; + } + } + catch (xbInt16 iRc ){ + xbString sMsg; + sMsg.Sprintf( "xbMemoDbt3::Zap() Exception Caught. Error Stop = [%d] rc = [%d]", iErrorStop, iRc ); + xbase->WriteLogMessage( sMsg.Str() ); + xbase->WriteLogMessage( GetErrorMessage( iRc )); + } + return iRc; +} + +/***********************************************************************/ +} /* namespace */ +#endif /* XB_DBF3_SUPPORT */ +#endif /* XB_MEMO_SUPPORT */ -- cgit v1.2.3