diff options
Diffstat (limited to 'src/core/xbmemo4.cpp')
-rwxr-xr-x | src/core/xbmemo4.cpp | 1336 |
1 files changed, 1336 insertions, 0 deletions
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 */ + |