diff options
Diffstat (limited to 'khexedit/hexbuffer.cc')
-rw-r--r-- | khexedit/hexbuffer.cc | 5099 |
1 files changed, 5099 insertions, 0 deletions
diff --git a/khexedit/hexbuffer.cc b/khexedit/hexbuffer.cc new file mode 100644 index 0000000..0c1d598 --- /dev/null +++ b/khexedit/hexbuffer.cc @@ -0,0 +1,5099 @@ +/* + * khexedit - Versatile hex editor + * Copyright (C) 1999-2000 Espen Sand, espensa@online.no + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include <qfileinfo.h> + +#include <klocale.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <knotifyclient.h> + +#include "hexbuffer.h" +#include "hexerror.h" + +// +// There are some comments marked with a "// ##" at various places. +// These indicate a patch from Sergey A. Sukiyazov which I have applied +// "as is" for now. The number of QString::fromLocal8Bit in this modification +// indicates that I should perhaps modify code elsewhere as well +// (espen 2000-11-26) +// + +// #define DEBUG_FIXED_SIZE 1024 +// #define PRINTER_TEST + + +CHexAction::CHexAction( HexAction action, uint offset ) +{ + mAction = action; + mOffset = offset; + mSize = 0; + mData = 0; + mDataSize = 0; + mNext = 0; +} + +CHexAction::~CHexAction( void ) +{ + delete [] mData; +} + +void CHexAction::setData( uint size, char *data, uint dataSize ) +{ + + if( data != 0 && dataSize > 0 ) + { + mData = new char[ dataSize ]; + if( mData == 0 ) + { + return; + } + memcpy( mData, data, dataSize ); + mDataSize = dataSize; + } + else + { + mDataSize = 0; + mData = 0; + } + mSize = size; +} + + +CHexActionGroup::CHexActionGroup( uint startOffset, uint startBit ) +{ + mStartOffset = startOffset; + mStartBit = startBit; + mHexAction = 0; +} + +CHexActionGroup::~CHexActionGroup( void ) +{ + CHexAction *ptr = mHexAction; + while( ptr != 0 ) + { + CHexAction *next = ptr->mNext; + delete ptr; + ptr = next; + } +} + +void CHexActionGroup::insertAction( CHexAction *hexAction ) +{ + hexAction->mNext = mHexAction; + mHexAction = hexAction; +} + + + +int SFilterControl::execute( uchar *dest, uchar *src, uint size ) +{ + if( size == 0 ) + { + return( Err_IllegalArgument ); + } + + uint numElement = operand.size(); + if( operation == OperandAndData ) + { + if( numElement == 0 ) { return( Err_IllegalArgument ); } + if( forward == true ) + { + for( uint i = 0; i < size; ) + { + for( uint j = 0; i < size && j < numElement; j++, i++ ) + { + dest[i] = src[i] & operand[j]; + } + } + } + else + { + for( uint i = size; i > 0; ) + { + for( uint j = numElement; i > 0 && j > 0; j--, i-- ) + { + dest[i-1] = src[i-1] & operand[j-1]; + } + } + } + } + else if( operation == OperandOrData ) + { + if( numElement == 0 ) { return( Err_IllegalArgument ); } + if( forward == true ) + { + for( uint i = 0; i < size; ) + { + for( uint j = 0; i < size && j < numElement; j++, i++ ) + { + dest[i] = src[i] | operand[j]; + } + } + } + else + { + for( uint i = size; i > 0; ) + { + for( uint j = numElement; i > 0 && j > 0; j--, i-- ) + { + dest[i-1] = src[i-1] | operand[j-1]; + } + } + } + } + else if( operation == OperandXorData ) + { + if( numElement == 0 ) { return( Err_IllegalArgument ); } + if( forward == true ) + { + for( uint i = 0; i < size; ) + { + for( uint j = 0; i < size && j < numElement; j++, i++ ) + { + dest[i] = src[i] ^ operand[j]; + } + } + } + else + { + for( uint i = size; i > 0; ) + { + for( uint j = numElement; i > 0 && j > 0; j--, i-- ) + { + dest[i-1] = src[i-1] ^ operand[j-1]; + } + } + } + } + else if( operation == InvertData ) + { + for( uint i = 0; i < size; i++ ) + { + dest[i] = ~src[i]; + } + } + else if( operation == ReverseData ) + { + for( uint i = 0; i < size; i++ ) + { + uchar flag = src[i]; + uchar rev = 0; + for( uint j = 0; j < 8; j++ ) + { + rev |= (((flag & 0x80) >> (7-j))); + flag <<= 1; + } + dest[i] = rev; + } + } + else if( operation == RotateData || operation == ShiftData ) + { + // + // Only forward here + // + bool up = rotate[1] > 0 ? true : false; + int range = rotate[0]; + int shift = abs(rotate[1]); + if( range == 0 || shift == 0 ) { return( Err_IllegalArgument ); } + shift = shift % (range*8); + + int b = shift / 8; + int s = shift - b * 8; + + for( uint i = 0; i < size; ) + { + if( up == true ) + { + int j; + if( operation == RotateData ) + { + for( j=0; j < b && i+range < size ; i++, j++ ) + { + dest[i] = src[i+range-b]; + } + } + else + { + for( j=0; j < b && i < size ; dest[i] = 0, i++, j++ ); + } + for( ; j < range && i < size ; i++, j++ ) + { + dest[i] = src[i-b]; + } + + uchar last = dest[i-1]; + for( int k=1; k <= j; k++ ) + { + dest[i-k] >>= s; + if( k < j ) + { + dest[i-k] |= dest[i-k-1]<<(8-s); + } + else if( j == range && operation == RotateData ) + { + dest[i-k] |= last<<(8-s); + } + } + } + else + { + int j; + for( j=0; j+b < range && i+b < size ; i++, j++ ) + { + dest[i] = src[i+b]; + } + for( ; j < range && i < size ; i++, j++ ) + { + dest[i] = operation == RotateData ? src[i+b-range] : 0; + } + + uchar first = dest[i-j]; + for( int k=j; k>0; k-- ) + { + dest[i-k] <<= s; + if( k>1 ) + { + dest[i-k] |= dest[i-k+1]>>(8-s); + } + else if( j == range && operation == RotateData ) + { + dest[i-k] |= first>>(8-s); + } + } + } + } + } + else if( operation == SwapBits ) + { + // + // Swap bits. Based on Leon Lessing's work. + // + + // + // Make non swapped version first. + // + for( uint i = 0; i < size; i++ ) + { + dest[i] = src[i]; + } + + // + // Swap the pairs the have been defined + // Format of operand (example): + // 7 2 5 0 0 0 0 0 + // Swap bit 7 with bit 2 and swap bit 5 with bit 0 + // + for( uint j=0; j<4; j++ ) + { + uchar b1 = 1 << (uchar)operand[j*2]; + uchar b2 = 1 << (uchar)operand[j*2+1]; + if( b1 == b2 ) { continue; } // Equal, no need to swap. + + for( uint i = 0; i < size; i++ ) + { + uchar b = 0; + if( dest[i] & b1 ) { b |= b2; } + if( dest[i] & b2 ) { b |= b1; } + + // + // A short description so that I will understand what the + // h... is going on five minutes from now. + // + // Destination byte is masked (AND'ed) with the inverse bitmap + // (the noninversed bitmap contains position of the + // two swap bits, eg 7-2 gives 10000100). Then the destination + // is OR'ed with the swapped bitmap. + // + dest[i] = (dest[i] & ~(b1 | b2)) | b; + } + } + } + else + { + return( Err_IllegalArgument ); + } + + return( Err_Success ); +} + + +const char *SExportCArray::printFormatted( const char *b, uint maxSize ) const +{ + static char buf[12]; + if( elementType == Char ) + { + char e = 0; + memcpy( &e, b, QMIN(sizeof(e),maxSize) ); + sprintf( buf, "%d", e ); + return( buf ); + } + else if( elementType == Uchar ) + { + unsigned char e = 0; + memcpy( &e, b, QMIN(sizeof(e),maxSize) ); + if( unsignedAsHexadecimal == true ) + { + sprintf( buf, "0x%02x", e ); + } + else + { + sprintf( buf, "%u", e ); + } + return( buf ); + } + else if( elementType == Short ) + { + short e = 0; + memcpy( &e, b, QMIN(sizeof(e),maxSize) ); + sprintf( buf, "%d", e ); + return( buf ); + + } + else if( elementType == Ushort ) + { + unsigned short e = 0; + memcpy( &e, b, QMIN(sizeof(e),maxSize) ); + if( unsignedAsHexadecimal == true ) + { + sprintf( buf, "0x%04x", e ); + } + else + { + sprintf( buf, "%u", e ); + } + return( buf ); + } + else if( elementType == Int ) + { + int e = 0; + memcpy( &e, b, QMIN(sizeof(e),maxSize) ); + sprintf( buf, "%u", e ); + return( buf ); + } + else if( elementType == Uint ) + { + unsigned int e = 0; + memcpy( &e, b, QMIN(sizeof(e),maxSize) ); + if( unsignedAsHexadecimal == true ) + { + sprintf( buf, "0x%08x", e ); + } + else + { + sprintf( buf, "%u", e ); + } + return( buf ); + } + else if( elementType == Float ) + { + float e = 0; + memcpy( &e, b, QMIN(sizeof(e),maxSize) ); + sprintf( buf, "%f", e ); + return( buf ); + } + else if( elementType == Double ) + { + double e = 0; + memcpy( &e, b, QMIN(sizeof(e),maxSize) ); + sprintf( buf, "%f", e ); + return( buf ); + } + + else + { + return(""); + } +} + + +QString SExportCArray::variableName( uint range ) const +{ + const char *typeString[] = + { + "char", + "unsigned char", + "short", + "unsigned short", + "int", + "unsigned int", + "float", + "double" + }; + + uint es = elementSize(); + uint numElement = range / es + ((range % es) ? 1 : 0); + + return( QString("%1 %2[%2]").arg(typeString[elementType]). + arg(arrayName).arg(numElement) ); +} + + + +int SExportCArray::elementSize( void ) const +{ + if( elementType == Char || elementType == Uchar ) + { + return( sizeof(char) ); + } + else if( elementType == Short || elementType == Ushort ) + { + return( sizeof(short) ); + } + else if( elementType == Int || elementType == Uint ) + { + return( sizeof(int) ); + } + else if( elementType == Float ) + { + return( sizeof(float) ); + } + else if( elementType == Double ) + { + return( sizeof(double) ); + } + else + { + return(1); + } +} + + +char CHexBuffer::mHexBigBuffer[16]= +{ + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' +}; + +char CHexBuffer::mHexSmallBuffer[16]= +{ + '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' +}; + +char CHexBuffer::mDecBuffer[10]= +{ + '0','1','2','3','4','5','6','7','8','9' +}; + +char CHexBuffer::mOctBuffer[8]= +{ + '0','1','2','3','4','5','6','7' +}; + + +SCursorState CHexBuffer::mCursorState; +SFileState CHexBuffer::mFileState; + + + + +CHexBuffer::CHexBuffer( void ) + :QByteArray() +{ + mColorIndex = 0; + mPrintBuf = 0; + mLoadingData = false; + mEditMode = EditReplace; + mActiveEditor = edit_primary; + + mDocumentModified = false; + + #ifdef DEBUG_FIXED_SIZE + setMaximumSize( DEBUG_FIXED_SIZE ); + #else + setMaximumSize( ~0 ); + #endif + + setDocumentSize(0); + + #ifdef PRINTER_TEST + puts("<CHexBuffer> Printer test is activated"); + #endif + + + setInputMode( mInputMode ); + + int errCode = setLayout( mLayout ); + if( errCode != 0 ) + { + return; + } + setColor( mColor ); + setFont( mFontInfo.init() ); + setShowCursor( true ); + setDisableCursor( false ); + setEditMode( EditReplace, false, false ); + setSoundState( false, false ); + + mUndoLimit = 10; + mUndoIndex = 0; + mUndoList.setAutoDelete( TRUE ); + mBookmarkList.setAutoDelete( TRUE ); +} + + +CHexBuffer::~CHexBuffer( void ) +{ + //debug("CHexBuffer::~CHexBuffer"); + delete [] mColorIndex; + delete [] mPrintBuf; +} + + + +bool CHexBuffer::hasFileName( void ) +{ + // + // FIXME: Files can be called "Untitled" so this must be corrected. + // + if( mUrl.isEmpty() || mUrl.contains( i18n( "Untitled" ), false ) ) + { + return( false ); + } + else + { + return( true ); + } +} + + + +int CHexBuffer::setLayout( SDisplayLayout &layout ) +{ + mLayout = layout; + mLayout.verify(); + + if( mLayout.primaryMode == SDisplayLayout::textOnly ) + { + mActiveEditor = edit_primary; + setEditMode( mEditMode ); + } + + mCursor.setLineSize( mLayout.lineSize ); + mCursor.addOffset( 0 ); // This will only reset the cell position + + computeLineWidth(); + cursorCompute(); + + delete [] mColorIndex; mColorIndex = 0; + delete [] mPrintBuf; mPrintBuf = 0; + + mColorIndex = new unsigned char[ mLayout.lineSize ]; + if( mColorIndex == 0 ) + { + return( Err_NoMemory ); + } + setColor( mColor ); + + // + // The 'mPrintBuf' is used to store formatted text. It is used for all + // print operations and must have the size of the 'mDpyState.lineSize' which + // is the number of bytes in one single display line. + // + mPrintBuf = new char[ mLayout.lineSize < 12 ? 12 : mLayout.lineSize ]; + if( mPrintBuf == 0 ) + { + delete [] mColorIndex; mColorIndex = 0; + return( Err_NoMemory ); + } + + return( Err_Success ); +} + + +void CHexBuffer::setColor( SDisplayColor &color ) +{ + mColor = color; + + // + // Test... + // + //mColor.secondTextBg = Qt::yellow; + //mColor.offsetBg = Qt::lightGray; + //mColor.gridFg = Qt::darkCyan; + + /* + mColor.secondTextBg = mColor.textBg; + mColor.offsetBg = mColor.textBg; + mColor.gridFg = mColor.textBg; + */ + + if( mColorIndex != 0 ) + { + uint columnSize = mLayout.columnSize == 0 ? 1 : mLayout.columnSize; + for( uint i = 0, entry = 0; i < mLayout.lineSize; i++ ) + { + if( i > 0 && i % columnSize == 0 ) { entry = entry == 0 ? 1 : 0; } + mColorIndex[i] = entry; + } + } +} + +void CHexBuffer::setInputMode( SDisplayInputMode &mode ) +{ + mInputMode = mode; + if( mInputMode.allowResize == false && mEditMode != EditReplace ) + { + setEditMode( EditReplace ); + } +} + + +bool CHexBuffer::toggleEditor( void ) +{ + bool changed; + if( mLayout.secondaryMode == SDisplayLayout::hide ) + { + changed = mActiveEditor == edit_secondary ? true : false; + mActiveEditor = edit_primary; + } + else + { + changed = true; + mActiveEditor = mActiveEditor == edit_primary ? + edit_secondary : edit_primary; + } + + setEditMode( mEditMode ); // Sets the cursor shapes as well + + if( changed == true ) + { + mCursor.resetCell(); + cursorCompute(); + } + + return( changed ); +} + + + + + + + +bool CHexBuffer::matchWidth( uint width ) +{ + if( documentPresent() == false || (uint)mFixedWidth >= width ) + { + return( false ); + } + + width -= mFixedWidth; + + uint g = mLayout.columnSpacing == 0 ? 1 : mLayout.columnSize; + uint n = g * mNumCell; + uint u = mUnitWidth; + uint s = mLayout.secondaryMode == SDisplayLayout::hide ? 0 : g; + uint o = mLayout.columnSpacing == 0 ? 0 : mSplitWidth; + float x = (float)(width+o)/(float)(u*(n+s)+o); + + uint lineSize = (uint)x * g; + + if( mLayout.lockColumn == false ) + { + // + // Examine if we can add one or more entries from the next column. This + // will make the rightmost column smaller than the rest but we will + // utilize as much of the available space (ie., width) as possible. + // (Note that the entry itself (which represents one byte of filedata) + // can not be splitted, eg., in binary mode the entry is eight byte + // wide and will not be splitted). + // + int w = (int)((float)((int)x)* (float)(u*(n+s)+o) - (float)o); + if( w > 0 && (uint)w < width ) + { + width -= w; + if( width > o ) + { + x = (float)(width-o) / (float)(u*(mNumCell+1)); + lineSize += (uint)x; + } + } + } + + if( lineSize == 0 || lineSize == mLayout.lineSize ) + { + // + // We have to redraw all text if a change occurs so we avoid it if + // possible. + // + return( false ); + } + + mLayout.lineSize = lineSize; + setLayout( mLayout ); + return( true ); +} + + +void CHexBuffer::setNonPrintChar( QChar nonPrintChar ) +{ + mFontInfo.nonPrintChar = nonPrintChar; +} + + +void CHexBuffer::setShowCursor( bool showCursor ) +{ + mShowCursor = showCursor; +} + + +void CHexBuffer::setDisableCursor( bool disableCursor ) +{ + mDisableCursor = disableCursor; +} + + +void CHexBuffer::setCursorShapeModifier( bool alwaysBlock, bool thickInsert ) +{ + mCursor.setShapeModifier( alwaysBlock, thickInsert ); + setEditMode( mEditMode ); +} + +void CHexBuffer::setEditMode( EEditMode editMode, bool alwaysBlock, + bool thickInsert ) +{ + mCursor.setShapeModifier( alwaysBlock, thickInsert ); + setEditMode( editMode ); +} + +void CHexBuffer::setEditMode( EEditMode editMode ) +{ + mEditMode = editMode; + if( mEditMode == EditInsert ) + { + if( mActiveEditor == edit_primary ) + { + mCursor.setShape( SCursorSpec::thin, SCursorSpec::frame, mUnitWidth, + mNumCell ); + } + else + { + mCursor.setShape( SCursorSpec::frame, SCursorSpec::thin, mUnitWidth, + mNumCell ); + } + + } + else + { + if( mActiveEditor == edit_primary ) + { + mCursor.setShape( SCursorSpec::solid, SCursorSpec::frame, mUnitWidth, + mNumCell ); + } + else + { + mCursor.setShape( SCursorSpec::frame, SCursorSpec::solid, mUnitWidth, + mNumCell ); + } + } +} + + + +void CHexBuffer::setMaximumSize( uint maximumSize ) +{ + if( maximumSize == 0 ) { maximumSize = ~0; } + + mMaximumSize = maximumSize; + mFixedSizeMode = maximumSize == (uint)~0 ? false : true; + mCursor.setFixedSizeMode( mFixedSizeMode ); + + if( mLayout.offsetVisible == false ) + { + mOffsetSize = 0; + mOffsetIndex = 0; + printOffset = &CHexBuffer::printDummyOffset; + } + else + { + if( mLayout.offsetMode == SDisplayLayout::decimal ) + { + printOffset = &CHexBuffer::printDecimalOffset; + for( mOffsetSize=0; maximumSize > 0; mOffsetSize += 1 ) + { + maximumSize = maximumSize / 10; + } + mOffsetIndex = 10 - mOffsetSize; + } + else if( mLayout.offsetMode == SDisplayLayout::hexadecimal ) + { + if( mLayout.offsetUpperCase == true ) + { + printOffset = &CHexBuffer::printHexadecimalBigOffset; + } + else + { + printOffset = &CHexBuffer::printHexadecimalSmallOffset; + } + for( mOffsetSize=0; maximumSize > 0; mOffsetSize += 1 ) + { + maximumSize = maximumSize / 16; + } + if( mOffsetSize > 4 ) { mOffsetSize += 1; } // Space for the ':' sign + mOffsetIndex = 9 - mOffsetSize; + } + else + { + mLayout.offsetVisible = false; + mOffsetSize = 0; + mOffsetIndex = 0; + printOffset = &CHexBuffer::printDummyOffset; + } + } +} + + +void CHexBuffer::setDocumentSize( uint size ) +{ + if( size > mMaximumSize ) { size = mMaximumSize; } + mDocumentSize = size; + mCursor.setDocumentSize( size ); + updateBookmarkMap(true); +} + + +void CHexBuffer::setUndoLevel( uint level ) +{ + if( level < 10 ) { level = 10; } + + if( level >= mUndoLimit ) + { + mUndoLimit = level; + return; + } + else + { + // + // The maximum size decreases. If the list is larger than the the new + // limit, then reduce the list size starting with the oldest elements. + // + mUndoLimit = level; + while( mUndoList.count() >= mUndoLimit ) + { + mUndoList.removeFirst(); + mUndoIndex -= (mUndoIndex > 0 ? 1 : 0); + } + } +} + + +void CHexBuffer::setSoundState( bool inputSound, bool fatalSound ) +{ + mInputErrorSound = inputSound; + mFatalErrorSound = fatalSound; +} + + +void CHexBuffer::setBookmarkVisibility( bool showInColumn, bool showInEditor ) +{ + mShowBookmarkInOffsetColumn = showInColumn; + mShowBookmarkInEditor = showInEditor; +} + +int CHexBuffer::writeFile( QFile &file, CProgress &p ) +{ + uint offset = 0; + uint remaining = documentSize(); + + do + { + const uint blockSize = QMIN( 131072 /* == 1024 * 128 */ , remaining ); + const int writeSize = file.writeBlock( data() + offset, blockSize ); + if( writeSize == -1 ) + { + p.finish(); + return( Err_ReadFailed ); + } + offset += blockSize; + remaining -= blockSize; + + if( p.expired() == true ) + { + int errCode = p.step( (float)offset/(float)documentSize() ); + if( errCode == Err_Stop && remaining > 0 ) + { + p.finish(); + return( Err_Success ); + } + } + } + while( remaining > 0 ); + + p.finish(); + mDocumentModified = false; + registerDiskModifyTime( file ); + + return( Err_Success ); +} + + +int CHexBuffer::readFile( QFile &file, const QString &url, CProgress &p ) +{ + if( resize( file.size() + 100 ) == false ) + { + p.finish(); + return( Err_NoMemory ); + } + + if( file.size() > 0 ) + { + mLoadingData = true; + uint offset = 0; + uint remaining = file.size(); + while( remaining > 0 ) + { + const uint blockSize = QMIN( 131072 /* == 1024 * 128 */ , remaining ); + const int readSize = file.readBlock( data() + offset, blockSize ); + if( readSize == -1 ) + { + p.finish(); + mLoadingData = false; + return( Err_ReadFailed ); + } + for( uint i=0; i<blockSize; i++) + { + data()[offset+i] = mEncode[ (unsigned char) data()[offset+i] ]; + } + + offset += blockSize; + remaining -= blockSize; + + if( p.expired() == true ) + { + int errCode = p.step( (float)offset/(float)file.size() ); + if( errCode == Err_Stop && remaining > 0 ) + { + p.finish(); + return( Err_OperationAborted ); + } + } + } + mLoadingData = false; + } + + p.finish(); + + mDocumentModified = false; + setDocumentSize( file.size() ); + registerDiskModifyTime( file ); + setUrl( url ); + computeNumLines(); + mSelect.reset(); + mMark.reset(); + mUndoList.clear(); + mUndoIndex = 0; + + return( Err_Success ); +} + + +int CHexBuffer::insertFile( QFile &file, CProgress &p ) +{ + if( file.size() == 0 ) + { + p.finish(); + return( Err_Success ); + } + + QByteArray array( file.size() ); + if( array.isNull() == true ) + { + p.finish(); + return( Err_NoMemory ); + } + + uint offset = 0; + uint remaining = file.size(); + while( remaining > 0 ) + { + const uint blockSize = QMIN( 131072 /* == 1024 * 128 */ , remaining ); + const int readSize = file.readBlock( array.data() + offset, blockSize ); + if( readSize == -1 ) + { + p.finish(); + return( Err_ReadFailed ); + } + for( uint i=0; i<blockSize; i++) + { + array[offset+i] = mEncode[ (unsigned char) array[offset+i] ]; + } + + offset += blockSize; + remaining -= blockSize; + + if( p.expired() == true ) + { + int errCode = p.step( (float)offset/(float)file.size() ); + if( errCode == Err_Stop && remaining > 0 ) + { + p.finish(); + return( Err_OperationAborted ); + } + } + } + + p.finish(); + + int errCode = inputAtCursor( array, 0 ); + return( errCode ); +} + + +int CHexBuffer::newFile( const QString &url ) +{ + if( resize( 100 ) == 0 ) + { + return( Err_NoMemory ); + } + + mDocumentModified = false; + setDocumentSize( 0 ); + setUrl( url ); + computeNumLines(); + mSelect.reset(); + + return( Err_Success ); +} + + +void CHexBuffer::closeFile( void ) +{ + resize(0); + computeNumLines(); + + mUndoList.clear(); + mUndoIndex = 0; + + setDocumentSize(0); + mDocumentModified = false; + + QString emptyUrl; + setUrl( emptyUrl ); + + mSelect.reset(); + mMark.reset(); + + removeBookmark(-1); // Negative index - All bookmarks +} + + +void CHexBuffer::registerDiskModifyTime( const QFile &file ) +{ + QFileInfo fileInfo( file ); + mDiskModifyTime = fileInfo.lastModified(); +} + + + +void CHexBuffer::setFont( const SDisplayFontInfo &fontInfo ) +{ + mFontInfo = fontInfo; + QFontMetrics fm( mFontInfo.font ); + mFontHeight = fm.height(); + mFontAscent = fm.ascent(); + computeLineWidth(); + + for( int i=0; i < 256; i++ ) + { + mCharValid[i] = QChar(i).isPrint(); + } + + /* + QFontInfo info( mFontInfo.font ); + puts("CHexBuffer mCharValid broken"); + + KCharset charset( info.charSet() ); + for( int i=0; i < 256; i++ ) + { + mCharValid[i] = charset.printable(i); + } + */ +} + + +int CHexBuffer::setEncoding( CConversion::EMode mode, CProgress &p ) +{ + int errCode = mEncode.convert( *this, mode, p ); + if( errCode == Err_Success ) + { + // + // The cursor stores the byte it is "covering", so this information + // must be updated. + // + cursorCompute(); + } + + return( errCode ); +} + + + + +void CHexBuffer::computeLineWidth( void ) +{ + QFontMetrics fm( mFontInfo.font ); + mUnitWidth = fm.width( "M" ); + + if( mLayout.primaryMode == SDisplayLayout::textOnly ) + { + mSplitWidth = 0; + } + else if( mLayout.columnCharSpace == true ) + { + mSplitWidth = mUnitWidth; + } + else + { + mSplitWidth = mLayout.columnSpacing; + } + + setMaximumSize( mMaximumSize ); + + if( mLayout.primaryMode == SDisplayLayout::hexadecimal ) + { + mNumCell = 2; + mCursor.setCellWeight( 4 ); + if( mLayout.primaryUpperCase == true ) + { + printCell = &CHexBuffer::printHexadecimalBigCell; + inputCell = &CHexBuffer::inputHexadecimal; + } + else + { + printCell = &CHexBuffer::printHexadecimalSmallCell; + inputCell = &CHexBuffer::inputHexadecimal; + } + } + else if( mLayout.primaryMode == SDisplayLayout::decimal ) + { + mNumCell = 3; + printCell = &CHexBuffer::printDecimalCell; + inputCell = &CHexBuffer::inputDecimal; + mCursor.setCellWeight( 3 ); + } + else if( mLayout.primaryMode == SDisplayLayout::octal ) + { + mNumCell = 3; + printCell = &CHexBuffer::printOctalCell; + inputCell = &CHexBuffer::inputOctal; + mCursor.setCellWeight( 3 ); + } + else if( mLayout.primaryMode == SDisplayLayout::binary ) + { + mNumCell = 8; + printCell = &CHexBuffer::printBinaryCell; + inputCell = &CHexBuffer::inputBinary; + mCursor.setCellWeight( 1 ); + } + else if( mLayout.primaryMode == SDisplayLayout::textOnly ) + { + mNumCell = 1; + printCell = &CHexBuffer::printAsciiCell; + inputCell = &CHexBuffer::inputAscii; + mCursor.setCellWeight( 8 ); + } + else + { + mNumCell = 2; + mLayout.primaryMode = SDisplayLayout::hexadecimal; + mLayout.primaryUpperCase = false; + printCell = &CHexBuffer::printHexadecimalSmallCell; + inputCell = &CHexBuffer::inputHexadecimal; + mCursor.setCellWeight( 4 ); + } + + // + // 'mPrimaryWidth' is the number of pixels that are needed to display a + // line in the primary field. + // + mPrimaryWidth = mLayout.lineSize * mNumCell * mUnitWidth; + + if( mLayout.columnSpacing != 0 ) + { + int numSplit = mLayout.lineSize / mLayout.columnSize; + numSplit -= mLayout.lineSize % mLayout.columnSize == 0 ? 1 : 0; + mPrimaryWidth += numSplit * mSplitWidth; + } + + // + // 'mSecondaryWidth' is the number of pixels that are needed to display a + // line in the secondary field (there are no spaces). + // + if( mLayout.secondaryMode == SDisplayLayout::hide ) + { + mSecondaryWidth = 0; + } + else + { + mSecondaryWidth = mLayout.lineSize * mUnitWidth; + } + + // + // 'mLineWidth' is the total number of pixels required to display + // offset data, separators, primary and secondary data on a line. + // + mLineWidth = mPrimaryWidth + mSecondaryWidth + mOffsetSize * mUnitWidth; + + // + // The 'mFixedWidth' is the number of pixels of the width that stays the + // same regardless of how many characters that are displayed. + // This entity consists of the edge margins, the inner margins and the + // separators. + // + mFixedWidth = mOffsetSize * mUnitWidth; + + // + // The edge margin is always present in both ends. + // + mLineWidth += mLayout.edgeMarginWidth * 2; + mFixedWidth += mLayout.edgeMarginWidth * 2; + + // + // 'mTextStart1' is the number of pixels from the left edge where the + // primary field starts. + // + mTextStart1 = mLayout.edgeMarginWidth; + if( mLayout.offsetVisible == true ) + { + int width; + if( mLayout.leftSeparatorWidth > 0 ) + { + width = mLayout.separatorMarginWidth * 2 + mLayout.leftSeparatorWidth; + } + else + { + width = (mLayout.separatorMarginWidth * 3) / 2; + } + + mLineWidth += width; + mFixedWidth += width; + mTextStart1 += width + mOffsetSize * mUnitWidth; + } + + // + // 'mTextStart2' is the number of pixels from the left edge where the + // secondary fields start. + // + mTextStart2 = mTextStart1; + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + int width; + if( mLayout.rightSeparatorWidth > 0 ) + { + width = mLayout.separatorMarginWidth * 2 + mLayout.rightSeparatorWidth; + } + else + { + width = (mLayout.separatorMarginWidth * 3) / 2; + } + + mLineWidth += width; + mFixedWidth += width; + mTextStart2 += width + mPrimaryWidth; + } + + setEditMode( mEditMode ); + computeNumLines(); +} + + +void CHexBuffer::computeNumLines( void ) +{ + if( mLayout.lineSize == 0 ) + { + mNumLines = 1; + } + else + { + uint s = mFixedSizeMode == true ? mMaximumSize : documentSize() + 1; + mNumLines = s / mLayout.lineSize + (s % mLayout.lineSize ? 1 : 0); + } +} + + + +void CHexBuffer::drawSelection( QPainter &paint, QColor &color, uint start, + uint stop, int sx ) +{ + if( start >= stop ) { return; } + uint width = stop - start; + + uint addStart, addWidth; + addStart = (start / mLayout.columnSize) * mSplitWidth; + if( width == 0 ) + { + addWidth = 0; + } + else + { + uint g = mLayout.columnSize; + addWidth = (((start % g) + width - 1) / g) * mSplitWidth; + } + + int offset = mTextStart1 - sx; + paint.fillRect( offset + start * mNumCell * mUnitWidth + addStart, + 0, width * mNumCell * mUnitWidth + addWidth, + mFontHeight, color ); + + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + offset = mTextStart2 - sx; + paint.fillRect( offset + start * mUnitWidth, + 0, width * mUnitWidth, + mFontHeight, color ); + } +} + + + + + +void CHexBuffer::drawText( QPainter &paint, uint line, int sx, int x1, int x2 ) +{ + uint fileOffset = line * mLayout.lineSize; + if( documentPresent() == false || mLoadingData == true ) + { + paint.fillRect( x1, 0, x2-x1, lineHeight(), mColor.inactiveBg ); + return; + } + + bool outsideText; + if( size() == 0 || fileOffset > documentSize() || fileOffset >= mMaximumSize) + { + outsideText = true; + } + else + { + outsideText = false; + } + + if( (line+1) % 2 || outsideText == true ) + { + paint.fillRect( x1, 0, x2-x1, lineHeight(), mColor.textBg ); + } + else + { + paint.fillRect( x1, 0, x2-x1, lineHeight(), mColor.secondTextBg ); + } + if( mLayout.horzGridWidth > 0 && outsideText == false ) + { + paint.setPen( mColor.gridFg ); + paint.drawLine( x1, mFontHeight, x2, mFontHeight ); + } + + if( mSelect.inside( fileOffset, mLayout.lineSize ) == true ) + { + uint start = mSelect.start( fileOffset ); + uint stop = mSelect.stop( fileOffset, mLayout.lineSize ); + drawSelection( paint, mColor.selectBg, start, stop, sx ); + } + + // + // A marked area will be displayed "above" a selcted area (given + // the mark background color is different) + // + if( mMark.inside( fileOffset, mLayout.lineSize ) == true ) + { + uint start = mMark.start( fileOffset ); + uint stop = mMark.stop( fileOffset, mLayout.lineSize ); + drawSelection( paint, mColor.markBg, start, stop, sx ); + } + + uint dataSize; + unsigned char *fileData; + if( outsideText == true ) + { + if( size() == 0 ) + { + return; + } + dataSize = 0; + fileData = 0; + } + else + { + dataSize = documentSize() - fileOffset; + if( dataSize > mLayout.lineSize ) { dataSize = mLayout.lineSize; } + fileData = (unsigned char*)&(data()[ fileOffset ]); + } + + // + // Compute the offset area size. We postpose the actual drawing + // until we have drawn any bookmark indicators in the editor areas. + // because we may want to draw an indicator in the offset area as well. + // + int offset = mLayout.edgeMarginWidth - sx; + if( mLayout.offsetVisible == true ) + { + offset += mOffsetSize * mUnitWidth; + if( mLayout.leftSeparatorWidth > 0 ) + { + offset += mLayout.leftSeparatorWidth + mLayout.separatorMarginWidth*2; + } + else + { + offset += (mLayout.separatorMarginWidth * 3) / 2; + } + } + + + #if 0 + int offset = mLayout.edgeMarginWidth - sx; + if( mLayout.offsetVisible == true ) + { + int s0 = mOffsetSize * mUnitWidth; + int s1 = s0 + mLayout.separatorMarginWidth + mLayout.edgeMarginWidth - sx; + if( x1 < s1 && x2 > 0 ) + { + if( outsideText == true ) + { + paint.fillRect( 0, 0, s1, lineHeight(), mColor.offsetBg ); + } + else + { + // + // I want to display the grid here so I cant use lineHeight() + // + paint.fillRect( 0, 0, s1, mFontHeight, mColor.offsetBg ); + } + } + + if( x1 < offset + s0 && x2 >= offset && fileData != 0 ) + { + paint.setPen( mColor.offsetFg ); + THIS_FPTR(printOffset)( mPrintBuf, fileOffset ); + // ## paint.drawText(offset,mFontAscent,&mPrintBuf[mOffsetIndex], + // mOffsetSize); + paint.drawText( offset, mFontAscent, + QString::fromLocal8Bit(&mPrintBuf[mOffsetIndex]), + mOffsetSize ); + } + offset += s0; + + if( mLayout.leftSeparatorWidth > 0 ) + { + offset += mLayout.separatorMarginWidth; + + int s2 = mLayout.leftSeparatorWidth + mLayout.separatorMarginWidth; + if( x1 < offset + s2 && x2 >= offset ) + { + QPen pen( mColor.leftSeparatorFg, mLayout.leftSeparatorWidth ); + paint.setPen( pen ); + int center = offset + mLayout.leftSeparatorWidth/2; + paint.drawLine( center, 0, center, lineHeight() ); + } + offset += s2; + } + else + { + offset += (mLayout.separatorMarginWidth * 3) / 2; + } + } + #endif + + + // + // Draw the primary area + // + int localOffset = offset; + for( uint i = 0; i < dataSize; i++ ) + { + int s = mNumCell * mUnitWidth + + ((i+1) % mLayout.columnSize == 0) * mSplitWidth; + if( x1 < localOffset + s && x2 >= localOffset ) + { + int flag = THIS_FPTR(printCell)( mPrintBuf, fileData[i] ); + if( mSelect.inside( fileOffset+i ) ) + { + paint.setPen( mColor.selectFg ); + } + else if( mMark.inside( fileOffset+i ) ) + { + paint.setPen( mColor.markFg ); + } + else + { + paint.setPen( flag == 0 ? foregroundColor( i ) : mColor.nonPrintFg ); + } + + // ## paint.drawText( localOffset, mFontAscent, mPrintBuf, mNumCell ); + paint.drawText( localOffset, mFontAscent, + QString::fromLocal8Bit(mPrintBuf), mNumCell ); + } + localOffset += s; + + if( mLayout.vertGridWidth > 0 && i+1 < dataSize ) + { + if( (i+1) % mLayout.columnSize == 0 ) + { + paint.setPen( mColor.gridFg ); + int x = localOffset - (mSplitWidth+1) / 2; + paint.drawLine( x, 0, x, mFontHeight ); + } + } + } + + // + // Draw the secondary area + // + offset += mPrimaryWidth; + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + if( mLayout.rightSeparatorWidth > 0 ) + { + offset += mLayout.separatorMarginWidth; + int s = mLayout.separatorMarginWidth + mLayout.rightSeparatorWidth; + if( x1 < offset + s && x2 >= offset ) + { + QPen pen( mColor.rightSeparatorFg, mLayout.rightSeparatorWidth ); + paint.setPen( pen ); + int center = offset + mLayout.rightSeparatorWidth/2; + paint.drawLine( center, 0, center, lineHeight() ); + } + offset += s; + } + else + { + offset += (mLayout.separatorMarginWidth * 3) / 2; + } + + int s = mUnitWidth; + for( uint i = 0; i < dataSize; i++ ) + { + if( x1 < offset + s && x2 >= offset ) + { + int flag = printAsciiCell( mPrintBuf, fileData[i] ); + if( mSelect.inside( fileOffset+i ) ) + { + paint.setPen( mColor.selectFg ); + } + else if( mMark.inside( fileOffset+i ) ) + { + paint.setPen( mColor.markFg ); + } + else + { + paint.setPen( flag == 0 ? mColor.secondaryFg : mColor.nonPrintFg ); + } + + // ## paint.drawText( offset, mFontAscent, mPrintBuf, 1 ); + paint.drawText( offset, mFontAscent, + QString::fromLocal8Bit(mPrintBuf), 1 ); + } + offset += s; + } + } + + // + // Draw the bookmark identifiers on this line (if any). We use the + // bitmask to minimize the number of times we try to draw the bookmarks. + // + int bookmarkPosition = 0; + if( mBookmarkMap.testBit(fileOffset/200) || + mBookmarkMap.testBit((fileOffset+mLayout.lineSize-1)/200 ) ) + { + // Returns a bookmark postion state + bookmarkPosition = drawBookmarks( paint, line, sx ); + } + + // + // Draw the offset area. We have delayed the drawing until now because + // it is possible to draw a bookmark indicator in this area. + // + offset = mLayout.edgeMarginWidth - sx; + if( mLayout.offsetVisible == true ) + { + int s0 = mOffsetSize * mUnitWidth; + int s1 = s0 + mLayout.separatorMarginWidth + mLayout.edgeMarginWidth - sx; + if( x1 < s1 && x2 > 0 ) + { + QColor bg = mShowBookmarkInOffsetColumn && + (bookmarkPosition & BookmarkOnLine) ? + mColor.bookmarkBg : mColor.offsetBg; + if( outsideText == true ) + { + paint.fillRect( 0, 0, s1, lineHeight(), bg ); + } + else + { + // + // I want to display the grid here so I cant use lineHeight() + // + paint.fillRect( 0, 0, s1, mFontHeight, bg ); + } + } + + if( x1 < offset + s0 && x2 >= offset && fileData != 0 ) + { + paint.setPen( mShowBookmarkInOffsetColumn && + bookmarkPosition & BookmarkOnLine ? + mColor.bookmarkFg : mColor.offsetFg ); + THIS_FPTR(printOffset)( mPrintBuf, fileOffset ); + // ## paint.drawText(offset,mFontAscent,&mPrintBuf[mOffsetIndex], + // mOffsetSize); + paint.drawText( offset, mFontAscent, + QString::fromLocal8Bit(&mPrintBuf[mOffsetIndex]), + mOffsetSize ); + } + + offset += s0; + + if( mLayout.leftSeparatorWidth > 0 ) + { + offset += mLayout.separatorMarginWidth; + + int s2 = mLayout.leftSeparatorWidth + mLayout.separatorMarginWidth; + if( x1 < offset + s2 && x2 >= offset ) + { + QPen pen( mColor.leftSeparatorFg, mLayout.leftSeparatorWidth ); + paint.setPen( pen ); + int center = offset + mLayout.leftSeparatorWidth/2; + paint.drawLine( center, 0, center, lineHeight() ); + } + } + } + + + // + // If the cursors are located on the line we have drawn we redraw + // them unless they have been disabled. + // + if( mDisableCursor == false ) + { + if( mCursor.curr.inside( fileOffset, fileOffset + mLayout.lineSize ) ) + { + drawCursor( paint, line, sx, bookmarkPosition & BookmarkOnCursor ); + } + } + +} + + + + +void CHexBuffer::drawText( QPainter &paint, uint line, int x1, int x2, int y, + bool useBlackWhite ) +{ + uint fileOffset = line * mLayout.lineSize; + + bool outsideText; + if( size() == 0 || fileOffset > documentSize() || fileOffset >= mMaximumSize) + { + outsideText = true; + } + else + { + outsideText = false; + } + + if( (line+1) % 2 || outsideText == true ) + { + paint.fillRect( x1, y, x2, lineHeight(), + useBlackWhite == true ? Qt::white : mColor.textBg ); + } + else + { + paint.fillRect( x1, y, x2, lineHeight(), + useBlackWhite == true ? Qt::white : mColor.secondTextBg ); + } + + if( mLayout.horzGridWidth > 0 && outsideText == false ) + { + QPen pen( useBlackWhite == true ? Qt::black : mColor.gridFg, + mLayout.horzGridWidth ); + paint.setPen( pen ); + paint.drawLine( x1, y+mFontHeight, x2+x1, y+mFontHeight ); + } + + uint dataSize; + unsigned char *fileData; + if( outsideText == true ) + { + if( size() == 0 ) + { + return; + } + dataSize = 0; + fileData = 0; + } + else + { + dataSize = documentSize() - fileOffset; + if( dataSize > mLayout.lineSize ) { dataSize = mLayout.lineSize; } + fileData = (unsigned char*)&(data()[ fileOffset ]); + } + + int offset = mLayout.edgeMarginWidth + x1; + + if( mLayout.offsetVisible == true ) + { + int s1 = mOffsetSize * mUnitWidth; + if( fileData != 0 ) + { + paint.setPen( useBlackWhite == true ? Qt::black : mColor.offsetFg ); + THIS_FPTR(printOffset)( mPrintBuf, fileOffset ); + // ## paint.drawText( offset, mFontAscent+y, &mPrintBuf[mOffsetIndex], + // mOffsetSize ); + paint.drawText( offset, mFontAscent+y, + QString::fromLocal8Bit(&mPrintBuf[mOffsetIndex]), + mOffsetSize ); + } + offset += s1; + + if( mLayout.leftSeparatorWidth > 0 ) + { + offset += mLayout.separatorMarginWidth; + + int s2 = mLayout.leftSeparatorWidth + mLayout.separatorMarginWidth; + QPen pen( useBlackWhite == true ? Qt::black : mColor.leftSeparatorFg, + mLayout.leftSeparatorWidth ); + paint.setPen( pen ); + int center = offset + mLayout.leftSeparatorWidth/2; + paint.drawLine( center, y, center, mFontHeight+y ); + offset += s2; + } + else + { + offset += (mLayout.separatorMarginWidth * 3) / 2; + } + } + + int localOffset = offset; + for( uint i = 0; i < dataSize; i++ ) + { + int s = mNumCell * mUnitWidth + + ((i+1) % mLayout.columnSize == 0) * mSplitWidth; + int flag = THIS_FPTR(printCell)( mPrintBuf, fileData[i] ); + if( useBlackWhite == true ) + { + paint.setPen( Qt::black ); + } + else + { + paint.setPen( flag == 0 ? foregroundColor( i ) : mColor.nonPrintFg ); + } + // ## paint.drawText( localOffset, mFontAscent+y, mPrintBuf, mNumCell ); + paint.drawText( localOffset, mFontAscent+y, + QString::fromLocal8Bit(mPrintBuf), mNumCell ); + localOffset += s; + + if( mLayout.vertGridWidth > 0 && i+1 < dataSize ) + { + if( (i+1) % mLayout.columnSize == 0 ) + { + QPen pen( useBlackWhite == true ? Qt::black : mColor.gridFg, + mLayout.vertGridWidth ); + paint.setPen( pen ); + int x = localOffset - (mSplitWidth+1) / 2; + paint.drawLine( x, y, x, y+mFontHeight ); + } + } + + } + + offset += mPrimaryWidth; + + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + if( mLayout.rightSeparatorWidth > 0 ) + { + offset += mLayout.separatorMarginWidth; + int s = mLayout.separatorMarginWidth + mLayout.rightSeparatorWidth; + QPen pen( useBlackWhite == true ? Qt::black : mColor.rightSeparatorFg, + mLayout.rightSeparatorWidth ); + paint.setPen( pen ); + int center = offset + mLayout.rightSeparatorWidth/2; + paint.drawLine( center, y, center, mFontHeight+y ); + offset += s; + } + else + { + offset += (mLayout.separatorMarginWidth * 3) / 2; + } + + + int s = mUnitWidth; + for( uint i = 0; i < dataSize; i++ ) + { + int flag = printAsciiCell( mPrintBuf, fileData[i] ); + if( useBlackWhite == true ) + { + paint.setPen( Qt::black ); + } + else + { + paint.setPen( flag == 0 ? mColor.secondaryFg : mColor.nonPrintFg ); + } + // ## paint.drawText( offset, mFontAscent+y, mPrintBuf, 1 ); + paint.drawText( offset, mFontAscent+y, + QString::fromLocal8Bit(mPrintBuf), 1 ); + offset += s; + } + } + +} + + +int CHexBuffer::headerHeight( QPainter &paint ) +{ + QFont font( paint.font() ); + paint.setFont( KGlobalSettings::generalFont() ); + const QFontMetrics &fm = paint.fontMetrics(); + + int height = fm.height(); + paint.setFont( font ); + return( height ); +} + +int CHexBuffer::headerMargin( QPainter &paint ) +{ + QFont font( paint.font() ); + paint.setFont( KGlobalSettings::generalFont() ); + const QFontMetrics &fm = paint.fontMetrics(); + + int margin = fm.height() / 2; + paint.setFont( font ); + return( margin ); +} + + +void CHexBuffer::drawHeader( QPainter &paint, int sx, int width, int y, + bool isFooter, const SPageHeader &header, + const SPagePosition &position ) +{ + QFont font( paint.font() ); + paint.setFont( KGlobalSettings::generalFont() ); + const QFontMetrics &fm = paint.fontMetrics(); + + paint.fillRect( sx, y, width, fm.height(), Qt::white ); + paint.setPen( Qt::black ); + if( header.line == SPageHeader::SingleLine ) + { + if( isFooter == false ) + { + paint.drawLine( sx, y+fm.height(), sx+width, y+fm.height() ); + } + else + { + paint.drawLine( sx, y, sx+width, y ); + } + } + else if( header.line == SPageHeader::Rectangle ) + { + paint.drawRect( sx, y, width, fm.height() ); + } + + int pos[3] = + { + QPainter::AlignLeft, QPainter::AlignHCenter, QPainter::AlignRight + }; + + QString msg; + for( int i=0; i<3; i++ ) + { + if( header.pos[i] == SPageHeader::DateTime ) + { + QDateTime datetime; + datetime.setTime_t( position.now ); + msg = KGlobal::locale()->formatDateTime(datetime); + } + else if( header.pos[i] == SPageHeader::PageNumber ) + { + msg = i18n("Page %1 of %2") + .arg(KGlobal::locale()->formatNumber(position.curPage, 0)) + .arg(KGlobal::locale()->formatNumber(position.maxPage, 0)); + } + else if( header.pos[i] == SPageHeader::FileName ) + { + msg = mUrl; + } + else + { + continue; + } + + if( 0 && pos[i] == QPainter::AlignRight ) + { + //const QFontMetrics &f = QFontMetrics( KGlobalSettings::generalFont() ); + //QRect r = paint.boundingRect(sx, y, width, fm.height(), pos[i], msg ); + //printf("R: %d, %d, %d, %d\n", r.x(), r.y(), r.width(), r.height() ); + + int x = sx + width - /*r.width();*/ fm.width(msg); + paint.drawText( x, y+fm.height(), msg ); + //printf("paint at %d\n", x ); + } + else + { + paint.drawText( sx, y, width, fm.height(), pos[i], msg ); + } + } + + // + // restore original font. + // + paint.setFont( font ); +} + + + + +int CHexBuffer::drawBookmarks( QPainter &paint, uint line, int startx ) +{ + if( documentPresent() == false || mLoadingData == true ) + { + return( 0 ); + } + + uint start = line*mLayout.lineSize; + uint stop = start+mLayout.lineSize; + QColor bg = mColor.bookmarkBg; + QColor fg = mColor.bookmarkFg; + + int bookmarkPosition = 0; + + for( SCursorOffset *c=mBookmarkList.first(); c!=0; c=mBookmarkList.next() ) + { + if( c->offset >= start && c->offset < stop ) + { + int x = c->offset - start; + int x1 = mTextStart1 + x * mUnitWidth * mNumCell; + x1 += (x / mLayout.columnSize) * mSplitWidth; + int x2 = mTextStart2 + x * mUnitWidth; + + bookmarkPosition |= BookmarkOnLine; + + if( mShowBookmarkInEditor == false ) + { + continue; + } + + uint offset = line*mLayout.lineSize+x; + if( offset == mCursor.curr.offset ) + { + bookmarkPosition |= BookmarkOnCursor; + } + + if( mSelect.inside( offset ) || mMark.inside( offset ) ) + { + paint.fillRect( x1-startx, 2, mUnitWidth*mNumCell, mFontHeight-4, bg ); + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + paint.fillRect( x2-startx, 2, mUnitWidth, mFontHeight-4, bg ); + } + } + else + { + paint.fillRect( x1-startx, 1, mUnitWidth*mNumCell, mFontHeight-2, bg ); + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + paint.fillRect( x2-startx, 1, mUnitWidth, mFontHeight-2, bg ); + } + } + + unsigned char c = (data()[ line*mLayout.lineSize+x]); + + int flag = THIS_FPTR(printCell)( mPrintBuf, c ); + paint.setPen( flag == 0 ? fg : mColor.nonPrintFg ); + // ## paint.drawText( x1-startx, mFontAscent, mPrintBuf, mNumCell ); + paint.drawText( x1-startx, mFontAscent, + QString::fromLocal8Bit(mPrintBuf), mNumCell ); + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + flag = printAsciiCell( mPrintBuf, c ); + paint.setPen( flag == 0 ? fg : mColor.nonPrintFg ); + // ## paint.drawText( x2-startx, mFontAscent, mPrintBuf, 1 ); + paint.drawText( x2-startx, mFontAscent, + QString::fromLocal8Bit(mPrintBuf), 1 ); + } + } + } + + return bookmarkPosition; +} + + + +void CHexBuffer::drawCursor( QPainter &paint, uint line, int startx, + bool onBookmark ) +{ + if( documentPresent() == false || mLoadingData == true ) + { + return; + } + + SCursorSpec &c = mCursor.curr; + + // + // Draw the cursor in primary edit area. + // + QColor bg, fg; + bool useFg; + if( mMark.inside( c.offset ) == true ) + { + bg = mColor.markBg; + fg = mSelect.inside( c.offset ) ? mColor.selectFg : mColor.markFg; + useFg = true; + } + else if( mSelect.inside( c.offset ) == true ) + { + bg = mColor.selectBg; + fg = mColor.selectFg; + useFg = true; + } + else + { + bg = (line+1) % 2 ? mColor.textBg : mColor.secondTextBg; + fg = foregroundColor( c.offset % mLayout.lineSize ); + useFg = false; // Can be true later. + } + + QColor cbg = mColor.cursorBg; + QColor cfg = mColor.cursorFg; + + // + // Fill in the general backround color + // + paint.fillRect( c.x1 - startx, 0, mUnitWidth, mFontHeight, bg ); + if( onBookmark == true ) + { + int w = mUnitWidth * (mNumCell-c.cell); // Rest of cell + if( useFg == true ) + { + paint.fillRect( c.x1-startx, 2, w, mFontHeight-4, mColor.bookmarkBg ); + } + else + { + paint.fillRect( c.x1-startx, 1, w, mFontHeight-2, mColor.bookmarkBg ); + } + } + + // + // Draw the cursor shape + // + bool transparent = false; + if( mActiveEditor == edit_primary ) + { + if( mShowCursor == true ) // Cursor blink on + { + if( c.mPrimaryShape == SCursorSpec::thin ) + { + paint.setPen( cbg ); + int center = c.x1 - startx - 1; + transparent = true; + + if( c.thickState == true ) + { + paint.drawLine( center, 0, center, mFontHeight - 1 ); + paint.drawLine( center+1, 0, center+1, mFontHeight - 1 ); + } + else + { + paint.drawLine( center, 0, center, mFontHeight - 1 ); + paint.drawLine( center-2, 0, center+2, 0 ); + paint.drawLine( center-2, mFontHeight-1, center+2, mFontHeight-1 ); + } + } + else // Solid block shape + { + paint.fillRect( c.x1 - startx, 0, mUnitWidth, mFontHeight, cbg ); + useFg = true; + fg = cfg; + } + } + } + else + { + transparent = true; + paint.setPen( cbg ); + paint.drawRect( c.x1 - startx, 0, mUnitWidth*mNumCell, mFontHeight ); + } + + // + // Draw the text on the cursor position and to the end of the cell. + // + if( c.offset < documentSize() ) + { + int flag = THIS_FPTR(printCell)( mPrintBuf, (unsigned char)c.data ); + if( onBookmark == true ) + { + // Inside bookmark. Draw text with bookmark foreground. + paint.setPen( mColor.bookmarkFg ); + // ## paint.drawText( c.x1-startx, mFontAscent, &mPrintBuf[c.cell], + // mNumCell-c.cell ); + paint.drawText( c.x1-startx, mFontAscent, + QString::fromLocal8Bit(&mPrintBuf[c.cell]), + mNumCell-c.cell ); + } + + if( transparent == false || onBookmark == false ) + { + paint.setPen( flag == 0 || useFg == true ? fg : mColor.nonPrintFg ); + // ## paint.drawText( c.x1 - startx, mFontAscent, &mPrintBuf[c.cell], 1); + paint.drawText( c.x1 - startx, mFontAscent, + QString::fromLocal8Bit(&mPrintBuf[c.cell]), 1 ); + } + } + + // + // Draw the cursor in secodary edit area. + // + if( mLayout.secondaryMode == SDisplayLayout::hide ) + { + return; + } + + + if( mMark.inside( c.offset ) == true ) + { + bg = mColor.markBg; + fg = mSelect.inside( c.offset ) ? mColor.selectFg : mColor.markFg; + useFg = true; + } + else if( mSelect.inside( c.offset ) == true ) + { + bg = mColor.selectBg; + fg = mColor.selectFg; + useFg = true; + } + else + { + bg = (line+1) % 2 ? mColor.textBg : mColor.secondTextBg; + fg = mColor.secondaryFg; + useFg = false; // Can be true later. + } + + + + // + // Fill in the general backround color + // + if( onBookmark == true ) + { + if( useFg == true ) + { + paint.fillRect( c.x2-startx, 2, mUnitWidth, mFontHeight-4, + mColor.bookmarkBg ); + } + else + { + paint.fillRect( c.x2-startx, 1, mUnitWidth, mFontHeight-2, + mColor.bookmarkBg ); + } + } + else + { + paint.fillRect( c.x2 - startx, 0, mUnitWidth, mFontHeight, bg ); + } + + // + // Draw the cursor shape + // + transparent = false; + if( mActiveEditor == edit_secondary ) + { + if( mShowCursor == true ) // Cursor blink on + { + if( c.mSecondaryShape == SCursorSpec::thin ) + { + paint.setPen( cbg ); + int center = c.x2 - startx - 1; + transparent = true; + + if( c.thickState == true ) + { + paint.drawLine( center, 0, center, mFontHeight - 1 ); + paint.drawLine( center+1, 0, center+1, mFontHeight - 1 ); + } + else + { + paint.drawLine( center, 0, center, mFontHeight - 1 ); + paint.drawLine( center-2, 0, center+2, 0 ); + paint.drawLine( center-2, mFontHeight-1, center+2, mFontHeight-1 ); + } + } + else + { + paint.fillRect( c.x2 - startx, 0, mUnitWidth, mFontHeight, cbg ); + useFg = true; + fg = cfg; + } + } + } + else + { + transparent = true; + paint.setPen( cbg ); + paint.drawRect( c.x2 - startx, 0, mUnitWidth, mFontHeight ); + } + + // + // Draw the text on the cursor position and to the end of the cell. + // + if( c.offset < documentSize() ) + { + int flag = printAsciiCell( mPrintBuf, (unsigned char)c.data ); + if( onBookmark == true ) + { + // Inside bookmark. Draw text with bookmark foreground. + paint.setPen( flag == 0 ? mColor.bookmarkFg : mColor.nonPrintFg ); + // ## paint.drawText( c.x2-startx, mFontAscent, mPrintBuf, 1 ); + paint.drawText( c.x2-startx, mFontAscent, + QString::fromLocal8Bit(mPrintBuf), 1 ); + } + if( transparent == false || onBookmark == false ) + { + paint.setPen( flag == 0 || useFg == true ? fg : mColor.nonPrintFg ); + // ## paint.drawText( c.x2 - startx, mFontAscent, mPrintBuf, 1 ); + paint.drawText( c.x2 - startx, mFontAscent, + QString::fromLocal8Bit(mPrintBuf), 1 ); + } + } + +} + + + + +void CHexBuffer::cursorReset( void ) +{ + mCursor.reset(); + cursorCompute(); +} + +void CHexBuffer::cursorCompute( void ) +{ + mCursor.prev = mCursor.curr; + + if( mCursor.next.offset >= documentSize() ) + { + if( documentSize() == 0 ) + { + mCursor.curr.offset = 0; + mCursor.curr.data = 0; + mCursor.curr.cell = 0; + mCursor.curr.maxCell = mNumCell; + + int x = mCursor.curr.offset % mLayout.lineSize; + mCursor.curr.x1 = mTextStart1; + mCursor.curr.x1 += (x * mNumCell + mCursor.curr.cell) * mUnitWidth; + mCursor.curr.x1 += (x / mLayout.columnSize) * mSplitWidth; + mCursor.curr.x2 = mTextStart2 + x * mUnitWidth; + mCursor.curr.y = (mCursor.curr.offset/mLayout.lineSize) * + (mFontHeight+mLayout.horzGridWidth); + return; + + } + if( mFixedSizeMode == true ) + { + uint max = mMaximumSize - 1; + uint off = mCursor.curr.offset % mLayout.lineSize; + uint end = max % mLayout.lineSize; + if( off > end ) + { + uint diff = off - end; + if( max + diff > mLayout.lineSize ) + { + mCursor.next.offset = max + diff - mLayout.lineSize; + } + else + { + mCursor.next.offset = 0; + } + } + else + { + uint diff = end - off; + mCursor.next.offset = diff > max ? max : max - diff; + } + } + else + { + mCursor.next.offset = documentSize(); + } + } + + mCursor.curr.offset = mCursor.next.offset; + mCursor.curr.data = data()[ mCursor.curr.offset ]; + mCursor.curr.cell = mCursor.next.cell; + mCursor.curr.maxCell = mNumCell; + + int x = mCursor.curr.offset % mLayout.lineSize; + + mCursor.curr.x1 = mTextStart1; + mCursor.curr.x1 += (x * mNumCell + mCursor.curr.cell) * mUnitWidth; + mCursor.curr.x1 += (x / mLayout.columnSize) * mSplitWidth; + mCursor.curr.x2 = mTextStart2 + x * mUnitWidth; + mCursor.curr.y = (mCursor.curr.offset/mLayout.lineSize) * + (mFontHeight + mLayout.horzGridWidth); +} + + +bool CHexBuffer::setCursorPosition( int x, int y, bool init, bool cellLevel ) +{ + if( documentPresent() == false ) + { + return( false ); + } + + uint line = y < 0 ? 0 : y / lineHeight(); + uint entry = 0; + int bit = 7; + + if( init == false ) + { + if( mCursor.area() == edit_primary ) + { + int start = mTextStart1; + if( x < start - (int)mLayout.separatorMarginWidth ) + { + return( false ); + } + else + { + int stop = mTextStart1 + mPrimaryWidth + mLayout.separatorMarginWidth; + int width = mNumCell * mUnitWidth; + int space = mSplitWidth; + + for( int position = start, i=0; position < stop; i++ ) + { + if( x <= position + width ) + { + if( cellLevel == true ) + { + while( bit > 0 ) + { + if( x <= position + mUnitWidth ) + { + break; + } + bit -= mCursor.cellWeight(); + position += mUnitWidth; + } + } + break; + } + position += width + (((i+1) % mLayout.columnSize) ? 0 : space); + entry += 1; + } + } + } + else + { + int start = mTextStart2; + if( x < start - (int)mLayout.separatorMarginWidth || + mLayout.secondaryMode == SDisplayLayout::hide ) + { + return( false ); + } + int stop = mTextStart2 + mLayout.lineSize * mUnitWidth; + int width = mUnitWidth * 1; + int space = 0; + + for( int position = start; position < stop; ) + { + if( x <= position + width ) + { + break; + } + position += width + space; + entry += 1; + } + } + } + else + { + int start = mTextStart1; + int stop = start + mPrimaryWidth + mLayout.separatorMarginWidth; + if( x >= start - (int)mLayout.separatorMarginWidth && x <= stop ) + { + int width = mUnitWidth * mNumCell; + int space = mSplitWidth; + + for( int position = start, i=0; position < stop; i++ ) + { + if( x <= position + width ) + { + if( cellLevel == true ) + { + while( bit > 0 ) + { + if( x <= position + mUnitWidth ) + { + break; + } + bit -= mCursor.cellWeight(); + position += mUnitWidth; + } + } + break; + } + position += width + (((i+1) % mLayout.columnSize) ? 0 : space); + entry += 1; + } + + mActiveEditor = edit_primary; + } + else if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + start = mTextStart2; + stop = mTextStart2 + mLayout.lineSize * mUnitWidth + + mLayout.edgeMarginWidth; + if( x >= start - (int)mLayout.separatorMarginWidth && x <= stop ) + { + int width = mUnitWidth * 1; + int space = 0; + + for( int position = start; position < stop; ) + { + if( x <= position + width ) + { + break; + } + position += width + space; + entry += 1; + } + + mActiveEditor = edit_secondary; + } + } + else + { + return( false ); + } + } + + uint offset = line * mLayout.lineSize + entry; + if( offset > documentSize() ) + { + offset = documentSize(); + } + + mCursor.setOffset( offset ); + mCursor.setBit( bit < 0 ? 0 : bit ); + + cursorCompute(); + if( mActiveEditor != mCursor.area() ) + { + mCursor.setArea( mActiveEditor ); + setEditMode( mEditMode ); + } + + return( true ); +} + + + + +bool CHexBuffer::inputAtCursor( QChar c ) +{ + if( documentPresent() == false || mInputMode.noInput() == true ) + { + if( mInputMode.noInput() == true ) { inputSound(); } + return( false ); + } + + if( c.isPrint() == false ) + { + inputSound(); + return( false ); + } + + unsigned char dest; + bool insert; + if( mEditMode == EditReplace || mCursor.curr.cell > 0 ) + { + if( mCursor.curr.offset >= documentSize() ) + { + dest = 0; + insert = true; + } + else + { + dest = (unsigned char)data()[ mCursor.curr.offset ]; + insert = false; + } + } + else + { + dest = 0; + insert = true; + } + + if( insert == true && mInputMode.allowResize == false ) + { + inputSound(); + return( false ); + } + + if( mActiveEditor == edit_primary ) + { + // ## if( THIS_FPTR(inputCell)( &dest, c.latin1(), mCursor.curr.cell ) + //== false ) + if( THIS_FPTR(inputCell)( &dest, QString(c).local8Bit()[0], + mCursor.curr.cell ) == false ) + { + inputSound(); + return( false ); + } + } + else if( mActiveEditor == edit_secondary ) + { + // ## if( inputAscii( &dest, c.latin1(), mCursor.curr.cell ) == false ) + if( !inputAscii( &dest, QString(c).local8Bit()[0], mCursor.curr.cell ) ) + { + inputSound(); + return( false ); + } + } + else + { + return( false ); + } + + recordStart( mCursor ); + recordReplace( mCursor, insert == true ? 0 : 1, (char*)&dest, 1 ); + cursorRight( cursorPrimaryEdit() ); + recordEnd( mCursor ); + + computeNumLines(); + return( true ); +} + + + +int CHexBuffer::inputAtCursor( const QByteArray &buf, uint oldSize ) +{ + if( documentPresent() == false ) + { + return( Err_NoActiveDocument ); + } + if( buf.isNull() == true ) + { + return( Err_EmptyArgument ); + } + + if( mInputMode.noInput() == true ) + { + inputSound(); + return( Err_WriteProtect ); + } + + if( mInputMode.allowResize == false ) + { + inputSound(); + return( Err_NoResize ); + } + + recordStart( mCursor ); + recordReplace( mCursor, oldSize, (char*)&buf[0], buf.size() ); + cursorStep( buf.size(), true, false ); + recordEnd( mCursor ); + + computeNumLines(); + return( Err_Success ); +} + + +bool CHexBuffer::removeAtCursor( bool beforeCursor ) +{ + if( documentPresent() == false ) + { + return( false ); + } + + if( mInputMode.noInput() == true || mInputMode.allowResize == false ) + { + inputSound(); + return( false ); + } + + + if( mSelect.valid() == true ) + { + cutSelection(); + return( true ); + } + + + if( beforeCursor == true ) + { + if( mCursor.curr.offset == 0 ) + { + return( false ); + } + + recordStart( mCursor ); + cursorLeft( false ); + recordReplace( mCursor, 1, 0, 0 ); + recordEnd( mCursor ); + + computeNumLines(); + return( true ); + } + else + { + if( mCursor.curr.offset + 1 > documentSize() ) + { + return( false ); + } + + recordStart( mCursor ); + recordReplace( mCursor, 1, 0, 0 ); + recordEnd( mCursor ); + + computeNumLines(); + return( true ); + } +} + + + +int CHexBuffer::locateRange(const SExportRange &range, uint &start, uint &stop) +{ + if( range.mode == SExportRange::All ) + { + start = 0; + stop = documentSize(); + } + else if( range.mode == SExportRange::Selection ) + { + if( mSelect.valid() == false ) + { + return( Err_NoSelection ); + } + start = mSelect.curr.start; + stop = mSelect.curr.stop; + } + else if( range.mode == SExportRange::Range ) + { + start = range.start; + stop = range.stop; + } + else + { + return( Err_IllegalMode ); + } + + if( start >= stop ) + { + return( Err_IllegalRange ); + } + + return( Err_Success ); +} + + +int CHexBuffer::exportText( const SExportText &ex, CProgress &p ) +{ + uint start, stop; + int errCode = locateRange( ex.range, start, stop ); + if( errCode != Err_Success ) + { + p.finish(); + return( errCode ); + } + + QFile file( ex.destFile ); + if( file.open( IO_WriteOnly ) == false ) + { + p.finish(); + return( Err_OpenWriteFailed ); + } + + uint startLine = calculateLine( start ); + if( startLine >= (uint)numLines() ) + { + startLine = numLines() == 0 ? 0 : numLines() - 1; + } + + uint stopLine = calculateLine( stop ); + if( stopLine >= (uint)numLines() ) + { + stopLine = numLines() == 0 ? 0 : numLines() - 1; + } + + uint totalSize = stopLine - startLine + 1; + uint remaining = stopLine - startLine + 1; + uint bytePerLine = mOffsetSize + 1 + (mNumCell + 2)*mLayout.lineSize + 1; + uint linePerStep = 20; + + QByteArray array( bytePerLine * linePerStep + 1 ); // Line is 0 terminated + if( array.isEmpty() == true ) + { + p.finish(); + return( Err_NoMemory ); + } + + while( remaining > 0 ) + { + uint blockSize = remaining > linePerStep ? linePerStep : remaining; + uint printSize = 0; + + for( uint i = 0; i < blockSize; i++, startLine++ ) + { + printSize += printLine( &array[printSize], startLine ); + } + + int writeSize = file.writeBlock( &array[0], printSize ); + if( writeSize == -1 ) + { + p.finish(); + return( Err_WriteFailed ); + } + + remaining -= blockSize; + if( p.expired() == true ) + { + int errCode = p.step( (float)(totalSize-remaining)/(float)totalSize ); + if( errCode == Err_Stop && remaining > 0 ) + { + p.finish(); + return( Err_OperationAborted ); + } + } + } + + p.finish(); + return( Err_Success ); +} + + + +int CHexBuffer::exportHtml( const SExportHtml &ex, CProgress &p ) +{ + uint start, stop; + int errCode = locateRange( ex.range, start, stop ); + if( errCode != Err_Success ) + { + p.finish(); + return( errCode ); + } + + uint startLine = calculateLine( start ); + if( startLine >= (uint)numLines() ) + { + startLine = numLines() == 0 ? 0 : numLines() - 1; + } + + uint stopLine = calculateLine( stop ); + if( stopLine >= (uint)numLines() ) + { + stopLine = numLines() == 0 ? 0 : numLines() - 1; + } + + uint totalSize = stopLine - startLine + 1; + uint remaining = stopLine - startLine + 1; + + if( ex.linePerPage == 0 ) + { + p.finish(); + return( Err_IllegalArgument ); + } + + uint linePerPage = ex.linePerPage; + uint numFiles = remaining/linePerPage + (remaining%linePerPage ? 1 : 0); + uint fileCount = 0; + + QStringList fileNames, offsets; + QString name, offset; + for( uint i=0; i < numFiles; i++ ) + { + name.sprintf( "%08d.html", i+1 ); + fileNames.append( QString("%1/%2%3").arg(ex.package).arg(ex.prefix). + arg(name)); + } + name.sprintf( "%08d.html", 0 ); + QString tocName =QString("%1/%2%3").arg(ex.package).arg(ex.prefix).arg(name); + + QString linkName; + if( ex.symLink == true ) + { + linkName = QString("%1/%2").arg(ex.package).arg("index.html"); + } + + while( remaining > 0 ) + { + THIS_FPTR(printOffset)( mPrintBuf, startLine*mLayout.lineSize ); + mPrintBuf[mOffsetSize]=0; + offset.sprintf("[%s]", mPrintBuf ); + + uint pageSize = remaining > linePerPage ? linePerPage : remaining; + printHtmlDataPage( tocName, fileNames, fileCount, ex, startLine, pageSize); + + remaining -= pageSize; + startLine += pageSize; + fileCount += 1; + + THIS_FPTR(printOffset)( mPrintBuf, (startLine-1)*mLayout.lineSize ); + mPrintBuf[mOffsetSize]=0; + offset += QString(" %1 [%2]").arg(i18n("to")).arg(mPrintBuf); + offsets.append(offset); + + if( p.expired() == true ) + { + int errCode = p.step( (float)(totalSize-remaining)/(float)totalSize ); + if( errCode == Err_Stop && remaining > 0 ) + { + printHtmlTocPage( tocName, linkName, fileNames, offsets, fileCount ); + p.finish(); + return( Err_OperationAborted ); + } + } + } + + printHtmlTocPage( tocName, linkName, fileNames, offsets, fileCount ); + + p.finish(); + return( Err_Success ); +} + + +int CHexBuffer::exportCArray( const SExportCArray &ex, CProgress &p ) +{ + uint start, stop; + int errCode = locateRange( ex.range, start, stop ); + if( errCode != Err_Success ) + { + p.finish(); + return( errCode ); + } + + QFile file( ex.destFile ); + if( file.open( IO_WriteOnly ) == false ) + { + p.finish(); + return( Err_OpenWriteFailed ); + } + + uint startLine = calculateLine( start ); + if( startLine >= (uint)numLines() ) + { + startLine = numLines() == 0 ? 0 : numLines() - 1; + } + + uint stopLine = calculateLine( stop ); + if( stopLine >= (uint)numLines() ) + { + stopLine = numLines() == 0 ? 0 : numLines() - 1; + } + + uint elementSize = ex.elementSize(); + uint elementOnThisLine = 0; + + QTextStream dest( &file ); + + dest << ex.variableName(stop-start).latin1() << "={" << endl; + for( unsigned int i=start; i<stop; i+=elementSize ) + { + dest << ex.printFormatted( (const char*)&data()[i], stop-i ); + if( i + elementSize < stop ) + { + dest << ","; + } + + if( ++elementOnThisLine >= ex.elementPerLine ) + { + dest << endl; + elementOnThisLine = 0; + } + + if( p.expired() == true ) + { + int errCode = p.step( (float)(i-start)/(float)(stop-start) ); + if( errCode == Err_Stop && (i+elementSize) < stop) + { + p.finish(); + return( Err_OperationAborted ); + } + } + } + dest << "};" << endl; + + p.finish(); + return( Err_Success ); +} + + + + + + + +int CHexBuffer::copySelectedText( QByteArray &array, int columnSegment ) +{ + SExportRange range; + range.mode = SExportRange::Selection; + return( copyText( array, range, columnSegment ) ); +} + + +int CHexBuffer::copyAllText( QByteArray &array ) +{ + SExportRange range; + range.mode = SExportRange::All; + return( copyText( array, range, VisibleColumn ) ); +} + + +int CHexBuffer::copyText( QByteArray &array, const SExportRange &range, + int columnSegment ) +{ + uint start, stop; + int errCode = locateRange( range, start, stop ); + if( errCode != Err_Success ) + { + return( errCode ); + } + + uint startLine = calculateLine( start ); + uint stopLine = calculateLine( stop ); + if( startLine >= (uint)numLines() ) + { + startLine = numLines() == 0 ? 0 : numLines() - 1; + } + if( stopLine >= (uint)numLines() ) + { + stopLine = numLines() == 0 ? 0 : numLines() - 1; + } + + uint bytePerLine = mOffsetSize + 1 + (mNumCell + 2)*mLayout.lineSize + 1; + uint size = (stopLine - startLine + 1)*bytePerLine; + if( array.resize( size+1 ) == false ) + { + return( Err_NoMemory ); + } + + if( columnSegment == VisibleColumn ) + { + columnSegment = PrimaryColumn; // Always visible + if( mLayout.offsetVisible == true ) + { + columnSegment |= OffsetColumn; + } + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + columnSegment |= SecondaryColumn; + } + } + + uint offset = 0; + for( uint i = startLine; i <= stopLine; i++ ) + { + offset += printLine( &array[offset], i, columnSegment ); + } + array[size] = 0; + + return( Err_Success ); +} + + +int CHexBuffer::copySelectedData( QByteArray &array ) +{ + uint start = mSelect.start(); + uint stop = mSelect.stop(); + + if( mSelect.valid() == false || start >= stop ) + { + return( Err_IllegalRange ); + } + + uint size = stop - start; + if( array.resize( size ) == false ) + { + return( Err_NoMemory ); + } + + //unsigned char *src = (unsigned char*)data(); + //char *dst = (char*)array.data(); + + memcpy( &array[0], &data()[start], size ); + return( Err_Success ); +} + + +uint CHexBuffer::numPage( CHexPrinter &printer ) +{ + QPainter paint( &printer ); + paint.setFont( font() ); + + SPageMargin margin = printer.pageMargin(); + SPageSize size = printer.pageUsableSize(); + int headHeight, footHeight, headMargin, footMargin, freeHeight; + + headHeight = footHeight = headMargin = footMargin = 0; + if( printer.pageHeader().enable == true ) + { + headHeight = headerHeight( paint ); + headMargin = headerMargin( paint ); + } + if( printer.pageFooter().enable == true ) + { + footHeight = headerHeight( paint ); + footMargin = headerMargin( paint ); + } + freeHeight = size.height - headHeight - footHeight - headMargin - footMargin; + + float scale = 1.0; + if( (uint)mLineWidth > size.width ) + { + scale = (float)size.width / (float)mLineWidth; + } + uint linePerPage = (uint) ((float)freeHeight/((float)lineHeight()*scale)); + + uint remaining = numLines(); + + return( remaining / linePerPage + (remaining % linePerPage ? 1 : 0) ); +} + + + +int CHexBuffer::print( CHexPrinter &printer, CProgress &p ) +{ + printer.setDocName( mUrl ); + + QPainter paint( &printer ); + paint.setFont( font() ); + + SPageMargin margin = printer.pageMargin(); + SPageSize size = printer.pageUsableSize(); + paint.setClipRect( margin.left, margin.top, size.width, size.height ); + + //printf("%d,%d,%d,%d\n", margin.left, margin.top, size.width, size.height ); + + int headHeight, footHeight, headMargin, footMargin, freeHeight; + + headHeight = footHeight = headMargin = footMargin = 0; + if( printer.pageHeader().enable == true ) + { + headHeight = headerHeight( paint ); + headMargin = headerMargin( paint ); + } + if( printer.pageFooter().enable == true ) + { + footHeight = headerHeight( paint ); + footMargin = headerMargin( paint ); + } + freeHeight = size.height - headHeight - footHeight - headMargin - footMargin; + + float scale = 1.0; + if( (uint)mLineWidth > size.width ) + { + scale = (float)size.width / (float)mLineWidth; + paint.scale( scale, scale ); + } + + uint linePerPage = (uint) ((float)freeHeight/((float)lineHeight()*scale)); + uint sx = (uint) ((float)margin.left/scale); + uint sy = (uint) ((float)(margin.top+headHeight+headMargin)/scale); + + uint remaining = numLines(); + uint line = 0; + + #ifdef PRINTER_TEST + remaining = remaining > linePerPage * 10 ? linePerPage * 10 : remaining; + #endif + + + SPagePosition pageData( time(0), remaining, linePerPage ); + while( remaining > 0 ) + { + uint lineInPage = remaining > linePerPage ? linePerPage : remaining; + uint y = sy; + + // + // Draw header and footer. Reset scaling during that operation. + // + paint.scale( 1.0/scale, 1.0/scale ); + if( printer.pageHeader().enable == true ) + { + drawHeader( paint, margin.left, size.width, margin.top, false, + printer.pageHeader(), pageData ); + } + if( printer.pageFooter().enable == true ) + { + drawHeader( paint, margin.left, size.width, + margin.top+size.height-footHeight, true, + printer.pageFooter(), pageData ); + } + paint.scale( scale, scale ); + + // + // Draw actual data + // + for( uint i=0; i < lineInPage; i++, line++ ) + { + drawText( paint, line, sx, mLineWidth, y, false/*printer.printBlackWhite()*/ ); + y += lineHeight();// - mLayout.horzGridWidth; // FIXME not really nice :) + + if( p.expired() == true ) + { + int errCode = p.step( pageData.current(), pageData.max() ); + if( errCode == Err_Stop ) + { + p.finish(); + return( Err_Success ); // Success here, even if we cancel + } + } + } + + if( p.expired() == true ) + { + int errCode = p.step( pageData.current(), pageData.max() ); + if( errCode == Err_Stop ) + { + p.finish(); + return( Err_Success );// Success here, even if we cancel + } + } + + remaining -= lineInPage; + if( remaining > 0 ) + { + printer.newPage(); + } + + pageData.step(); + } + + p.finish(); + return( Err_Success ); +} + + + + + +uint CHexBuffer::printLine( char *dst, uint line ) +{ + uint offset = line * mLayout.lineSize; + unsigned char *src; + char *start = dst; + + uint dataSize; + if( offset >= documentSize() ) + { + src = 0; + dataSize = 0; + } + else + { + src = (unsigned char*)&data()[ offset ]; + dataSize = documentSize() - offset; + } + + if( mLayout.offsetVisible == true ) + { + THIS_FPTR(printOffset)( dst, offset ); dst += mOffsetSize; + sprintf( dst, " " ); dst += 1; + } + for( uint i=0; i < mLayout.lineSize; i++ ) + { + if( i<dataSize ) + { + THIS_FPTR(printCell)( dst, src[i] ); dst += mNumCell; + } + else + { + memset( dst, ' ', mNumCell ); dst += mNumCell; + } + if( mSplitWidth != 0 ) + { + sprintf( dst, " " ); dst += 1; + } + } + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + for( uint i=0; i < mLayout.lineSize; i++ ) + { + if( i < dataSize ) + { + printAsciiCell( dst, src[i] ); dst += 1; + } + else + { + memset( dst, ' ', 1 ); dst += 1; + } + } + } + sprintf( dst, "\n" ); dst += 1; + return( (uint)(dst-start) ); +} + + +uint CHexBuffer::printLine( char *dst, uint line, int columnSegment ) +{ + uint offset = line * mLayout.lineSize; + unsigned char *src; + char *start = dst; + + uint dataSize; + if( offset >= documentSize() ) + { + src = 0; + dataSize = 0; + } + else + { + src = (unsigned char*)&data()[ offset ]; + dataSize = documentSize() - offset; + } + + if( columnSegment & OffsetColumn ) + { + THIS_FPTR(printOffset)( dst, offset ); dst += mOffsetSize; + sprintf( dst, " " ); dst += 1; + } + + if( columnSegment & PrimaryColumn ) + { + for( uint i=0; i < mLayout.lineSize; i++ ) + { + if( i<dataSize ) + { + THIS_FPTR(printCell)( dst, src[i] ); dst += mNumCell; + } + else + { + memset( dst, ' ', mNumCell ); dst += mNumCell; + } + if( mSplitWidth != 0 ) + { + sprintf( dst, " " ); dst += 1; + } + } + } + + if( columnSegment & SecondaryColumn ) + { + for( uint i=0; i < mLayout.lineSize; i++ ) + { + if( i < dataSize ) + { + printAsciiCell( dst, src[i] ); dst += 1; + } + else + { + memset( dst, ' ', 1 ); dst += 1; + } + } + } + + sprintf( dst, "\n" ); dst += 1; + return( (uint)(dst-start) ); +} + + + + + + + + + + + +bool CHexBuffer::cutSelection( void ) +{ + if( documentPresent() == false || mSelect.size() == 0 ) + { + return( false ); + } + + if( mInputMode.noInput() == true || mInputMode.allowResize == false ) + { + inputSound(); + return( false ); + } + + recordStart( mCursor ); + cursorGoto( mSelect.start(), 0 ); + recordReplace( mCursor, mSelect.size(), 0, 0 ); + recordEnd( mCursor ); + + mSelect.reset(); + + computeNumLines(); + return( true ); +} + + + +bool CHexBuffer::undo( void ) +{ + if( documentPresent() == false || mUndoIndex == 0 || + mInputMode.noInput() == true ) + { + if( mInputMode.noInput() == true ) { inputSound(); } + return( false ); + } + + CHexActionGroup *group = mUndoList.at( mUndoIndex-1 ); + if( group == 0 ) + { + return( false ); + } + + mUndoIndex -= 1; + doActionGroup( group ); + + cursorGoto( group->mStartOffset, group->mStartBit ); + + return( true ); +} + + +bool CHexBuffer::redo( void ) +{ + if( documentPresent() == false || mUndoIndex >= mUndoList.count() || + mInputMode.noInput() == true ) + { + if( mInputMode.noInput() == true ) { inputSound(); } + return( false ); + } + + CHexActionGroup *group = mUndoList.at( mUndoIndex ); + if( group == 0 ) + { + return( false ); + } + + mUndoIndex += 1; + doActionGroup( group ); + + cursorGoto( group->mStopOffset, group->mStopBit ); + + return( true ); +} + + +int CHexBuffer::addBookmark( int position ) +{ + if( documentPresent() == false ) + { + return( Err_NoData ); + } + + if( mBookmarkList.count() >= 9 && position == -1 ) + { + return( Err_ListFull ); + } + + SCursorOffset *co = new SCursorOffset; + if( co == 0 ) + { + fatalSound(); + return( Err_NoMemory ); + } + + co->offset = mCursor.curr.offset; + co->bit = mCursor.bit(); + + if( position == -1 || position > (int)mBookmarkList.count() ) + { + mBookmarkList.append( co ); + } + else + { + mBookmarkList.remove( (uint)position ); + mBookmarkList.insert( (uint)position, co ); + } + + updateBookmarkMap(false); + return( Err_Success ); +} + + +bool CHexBuffer::removeBookmark( int position ) +{ + if( position < 0 ) + { + if( mBookmarkList.count() == 0 ) + { + return( false ); + } + mBookmarkList.clear(); + } + else + { + if( (uint)position >= mBookmarkList.count() ) + { + return( false ); + } + mBookmarkList.remove( position ); + } + + updateBookmarkMap(false); + return( true ); +} + + +void CHexBuffer::updateBookmarkMap( bool resize ) +{ + if( resize == true ) + { + mBookmarkMap.resize( documentSize()/200 + 3 ); + } + mBookmarkMap.fill(0); + + int bookmarkMapSize = mBookmarkMap.size(); + for( SCursorOffset *c=mBookmarkList.first(); c!=0; c=mBookmarkList.next() ) + { + int bookmarkOffset = c->offset / 200; + if( bookmarkOffset < bookmarkMapSize ) + { + // + // Espen 2000-05-16: + // I do this test to avoid some Qt warnings when I have closed + // or reduced the size of the documnet while the (now invalid) + // bookmarks still exist. + // + mBookmarkMap.setBit(bookmarkOffset); + } + } +} + + +int CHexBuffer::findFirst( SSearchControl &sc ) +{ + mMark.reset(); + int errCode = scanData( sc, true ); + return( errCode ); +} + +int CHexBuffer::findNext( SSearchControl &sc ) +{ + sc.fromCursor = true; + int errCode = scanData( sc, false ); + return( errCode ); +} + +int CHexBuffer::findWrap( SSearchControl &sc ) +{ + if( sc.wrapValid == false ) + { + return( Err_NoMatch ); + } + sc.wrapValid = false; + + sc.fromCursor = false; + sc.wrapActive = true; + int errCode = scanData( sc, false ); + sc.fromCursor = true; + return( errCode ); +} + + + + +int CHexBuffer::replaceAll( SSearchControl &sc, bool init ) +{ + if( init == true ) + { + initScanData( sc ); + } + + if( sc.key.isEmpty() == true ) + { + return( Err_EmptyArgument ); + } + + if( documentSize() == 0 ) + { + return( Err_EmptyDocument ); + } + + uint head, tail; + if( sc.inSelection == true ) + { + if( mSelect.valid() == false ) + { + return( Err_NoSelection ); + } + + head = mSelect.start(); + tail = mSelect.stop(); + } + else + { + head = 0; + tail = documentSize(); + } + + uint start, stop; + if( sc.fromCursor == false ) + { + if( sc.wrapActive == true ) + { + start = sc.forward == true ? head : sc.wrapMark; + stop = sc.forward == true ? sc.wrapMark+sc.key.size() : tail; + } + else + { + start = head; + stop = tail; + } + } + else if( sc.forward == true ) + { + start = cursorOffset() < head ? head : cursorOffset(); + stop = sc.wrapActive == true ? sc.wrapMark+sc.key.size() : tail; + } + else + { + start = sc.wrapActive == true ? sc.wrapMark : head; + stop = cursorOffset() > tail ? tail : cursorOffset(); + } + + if( sc.forward == true && start + sc.key.size() > stop ) + { + // + // When searching backwards "stop" is the last offset from where + // we do a memcmp() upward in memory. An overflow for that + // situation is taken care of below. + // + return( Err_NoMatch ); + } + + if( stop + sc.key.size() > tail ) + { + uint diff = stop + sc.key.size() - tail; + stop = stop > diff ? stop - diff : 0; + } + + if( mInputMode.noInput() == true ) + { + inputSound(); + return( Err_WriteProtect ); + } + + recordStart( mCursor ); + uint numReplaced = 0; + + if( sc.forward == true ) + { + for( uint i = start; i <= stop ; ) + { + if( memcmp( &data()[i], sc.key.data(), sc.key.size() ) != 0 ) + { + i++; + } + else + { + cursorGoto( i, 7 ); + recordReplace( mCursor, sc.key.size(), sc.val.data(), sc.val.size() ); + numReplaced += 1; + + if( sc.inSelection == true ) + { + if( sc.key.size() > sc.val.size() ) + { + mSelect.shrink( sc.key.size() - sc.val.size() ); + } + else + { + mSelect.expand( sc.val.size() - sc.key.size() ); + } + } + + if( sc.key.size() > sc.val.size() ) + { + uint diff = sc.key.size() - sc.val.size(); + stop -= QMIN( stop, diff ); + } + else if( sc.key.size() < sc.val.size() ) + { + stop += sc.val.size() - sc.key.size(); + } + + i += sc.val.size(); + cursorStep( sc.val.size(), true, false ); + } + } + } + else + { + for( uint i = stop; i >= start; ) + { + if( memcmp( &data()[i], sc.key.data(), sc.key.size() ) != 0 ) + { + if( i == 0 ) { break; } + i--; + } + else + { + cursorGoto( i, 7 ); + recordReplace( mCursor, sc.key.size(), sc.val.data(), sc.val.size() ); + numReplaced += 1; + + if( sc.inSelection == true ) + { + if( sc.key.size() > sc.val.size() ) + { + mSelect.shrink( sc.key.size() - sc.val.size() ); + } + else + { + mSelect.expand( sc.val.size() - sc.key.size() ); + } + } + + i -= QMIN( i, sc.key.size() ); + if( i == 0 ) { break; } + } + } + } + + recordEnd( mCursor ); + computeNumLines(); + + if( numReplaced == 0 ) + { + return( Err_NoMatch ); + } + + sc.numReplace += numReplaced; + sc.match = true; + mMark.reset(); + + return( Err_Success ); +} + + +int CHexBuffer::replaceMarked( SSearchControl &sc ) +{ + if( documentSize() == 0 ) + { + return( Err_EmptyDocument ); + } + + if( mMark.valid() == false ) + { + return( Err_NoMark ); + } + + bool inSelection; + if( mSelect.valid() == true ) + { + if( mMark.start() >= mSelect.start() && mMark.stop() <= mSelect.stop() ) + { + inSelection = true; + } + else + { + inSelection = false; + } + } + else + { + inSelection = false; + } + + if( mInputMode.noInput() == true ) + { + inputSound(); + return( Err_WriteProtect ); + } + + recordStart( mCursor ); + cursorGoto( mMark.start(), 7 ); + recordReplace( mCursor, mMark.size(), sc.val.data(), sc.val.size() ); + sc.numReplace += 1; + + if( inSelection == true ) + { + if( mMark.size() > sc.val.size() ) + { + mSelect.shrink( mMark.size() - sc.val.size() ); + } + else + { + sc.wrapMark += sc.val.size() - mMark.size(); + mSelect.expand( sc.val.size() - mMark.size() ); + } + } + + + if( sc.wrapActive == false ) + { + if( sc.forward == false ) + { + sc.wrapMark += mMark.size() > sc.val.size() ? + mMark.size() - sc.val.size() : sc.val.size() - mMark.size(); + } + } + + + recordEnd( mCursor ); + computeNumLines(); + + if( sc.forward == true ) + { + // + // We must step over the area we have just altered. This is + // vital if the search key contains a pattern that exists in + // the replace data buffer. + // + cursorStep( sc.val.size(), true, false ); + } + mMark.reset(); + return( Err_Success ); +} + + +#if 0 + +int CHexBuffer::replaceAll( SSearchControl &sc, bool init ) +{ + if( init == true ) + { + initScanData( sc ); + } + + if( sc.key.isEmpty() == true ) + { + return( Err_EmptyArgument ); + } + + if( documentSize() == 0 ) + { + return( Err_EmptyDocument ); + } + + uint head, tail; + if( sc.inSelection == true ) + { + if( mSelect.valid() == false ) + { + return( Err_NoSelection ); + } + + head = mSelect.start(); + tail = mSelect.stop(); + } + else + { + head = 0; + tail = documentSize(); + } + + uint start, stop; + if( sc.fromCursor == false ) + { + if( sc.wrapActive == true ) + { + start = sc.forward == true ? head : sc.wrapMark; + stop = sc.forward == true ? sc.wrapMark : tail; + } + else + { + start = head; + stop = tail; + } + } + else if( sc.forward == true ) + { + start = cursorOffset() < head ? head : cursorOffset(); + stop = sc.wrapActive == true ? sc.wrapMark : tail; + } + else + { + start = sc.wrapActive == true ? sc.wrapMark : head; + stop = cursorOffset() > tail ? tail : cursorOffset(); + } + + + if( start + sc.key.size() > stop ) + { + return( Err_NoMatch ); + } + + if( stop + sc.key.size() > tail ) + { + uint diff = stop + sc.key.size() - tail; + stop = stop > diff ? stop - diff : 0; + } + + if( mInputMode.noInput() == true ) + { + inputSound(); + return( Err_WriteProtect ); + } + + recordStart( mCursor ); + uint numReplaced = 0; + + if( sc.forward == true ) + { + for( uint i = start; i <= stop; i++ ) + { + if( memcmp( &data()[i], sc.key.data(), sc.key.size() ) == 0 ) + { + cursorGoto( i, 7 ); + recordReplace( mCursor, sc.key.size(), sc.val.data(), sc.val.size() ); + numReplaced += 1; + + if( sc.inSelection == true ) + { + if( sc.key.size() > sc.val.size() ) + { + mSelect.shrink( sc.key.size() - sc.val.size() ); + } + else + { + mSelect.expand( sc.val.size() - sc.key.size() ); + } + } + + if( sc.key.size() > sc.key.size() ) + { + uint diff = sc.key.size() - sc.val.size(); + i += diff - 1; + } + else if( sc.key.size() < sc.val.size() ) + { + uint diff = sc.val.size() - sc.key.size(); + stop += diff; + } + else + { + i += sc.val.size() - 1; + } + cursorStep( sc.val.size(), true, false ); + } + } + } + else + { + for( uint i = stop; i >= start; i-- ) + { + if( memcmp( &data()[i], sc.key.data(), sc.key.size() ) == 0 ) + { + cursorGoto( i, 7 ); + recordReplace( mCursor, sc.key.size(), sc.val.data(), sc.val.size() ); + numReplaced += 1; + + if( sc.inSelection == true ) + { + if( sc.key.size() > sc.val.size() ) + { + mSelect.shrink( sc.key.size() - sc.val.size() ); + } + else + { + mSelect.expand( sc.val.size() - sc.key.size() ); + } + } + + } + if( i == 0 ) { break; } + } + } + + recordEnd( mCursor ); + computeNumLines(); + + if( numReplaced == 0 ) + { + return( Err_NoMatch ); + } + + sc.numReplace += numReplaced; + sc.match = true; + mMark.reset(); + + return( Err_Success ); +} + + +int CHexBuffer::replaceMarked( SSearchControl &sc ) +{ + if( documentSize() == 0 ) + { + return( Err_EmptyDocument ); + } + + if( mMark.valid() == false ) + { + return( Err_NoMark ); + } + + bool inSelection; + if( mSelect.valid() == false ) + { + if( mMark.start() >= mSelect.start() && mMark.stop() <= mSelect.stop() ) + { + inSelection = true; + } + else + { + inSelection = false; + } + } + else + { + inSelection = false; + } + + if( mInputMode.noInput() == true ) + { + inputSound(); + return( Err_WriteProtect ); + } + + recordStart( mCursor ); + cursorGoto( mMark.start(), 7 ); + recordReplace( mCursor, mMark.size(), sc.val.data(), sc.val.size() ); + sc.numReplace += 1; + + if( inSelection == true ) + { + if( mMark.size() > sc.val.size() ) + { + mSelect.shrink( mMark.size() - sc.val.size() ); + } + else + { + mSelect.expand( sc.val.size() - mMark.size() ); + } + } + + recordEnd( mCursor ); + computeNumLines(); + mMark.reset(); + + return( Err_Success ); +} + +#endif + + +int CHexBuffer::initScanData( SSearchControl &sc ) +{ + sc.wrapValid = false; + sc.wrapActive = false; + sc.wrapMark = 0; + sc.match = false; + sc.numReplace = 0; + + uint head, tail; + if( sc.inSelection == true ) + { + if( mSelect.valid() == false ) + { + return( Err_NoSelection ); + } + + head = mSelect.start(); + tail = mSelect.stop(); + } + else + { + head = 0; + tail = documentSize(); + } + + if( sc.fromCursor == false ) + { + sc.wrapValid = false; + sc.wrapActive = false; + sc.wrapMark = 0; + } + else if( sc.forward == true ) + { + if( cursorOffset() > tail ) + { + sc.wrapValid = true; + sc.wrapActive = false; + sc.wrapMark = tail; + } + else if( cursorOffset() <= head ) + { + sc.wrapValid = false; + sc.wrapActive = false; + sc.wrapMark = 0; + } + else + { + sc.wrapValid = true; + sc.wrapActive = false; + sc.wrapMark = cursorOffset(); + } + } + else + { + if( cursorOffset() >= tail ) + { + sc.wrapValid = false; + sc.wrapActive = false; + sc.wrapMark = 0; + } + else if( cursorOffset() < head ) + { + sc.wrapValid = true; + sc.wrapActive = false; + sc.wrapMark = head; + } + else + { + sc.wrapValid = true; + sc.wrapActive = false; + sc.wrapMark = cursorOffset(); + } + } + + return( Err_Success ); +} + + + +int CHexBuffer::scanData( SSearchControl &sc, bool init ) +{ + if( init == true ) + { + int errCode = initScanData( sc ); + if( errCode != Err_Success ) + { + return( errCode ); + } + } + + if( sc.key.isEmpty() == true ) + { + return( Err_EmptyArgument ); + } + + if( documentSize() == 0 ) + { + return( Err_EmptyDocument ); + } + + uint head, tail; + if( sc.inSelection == true ) + { + if( mSelect.valid() == false ) + { + return( Err_NoSelection ); + } + + head = mSelect.start(); + tail = mSelect.stop(); + } + else + { + head = 0; + tail = documentSize(); + } + + uint start, stop; + if( sc.fromCursor == false ) + { + if( sc.wrapActive == true ) + { + start = sc.forward == true ? head : sc.wrapMark; + stop = sc.forward == true ? sc.wrapMark+sc.key.size() : tail; + } + else + { + start = head; + stop = tail; + } + } + else if( sc.forward == true ) + { + start = cursorOffset() < head ? head : cursorOffset(); + stop = sc.wrapActive == true ? sc.wrapMark : tail; + } + else + { + start = sc.wrapActive == true ? sc.wrapMark : head; + stop = cursorOffset() > tail ? tail : cursorOffset(); + } + + if( sc.forward == true && start + sc.key.size() > stop ) + { + // + // When searching backwards "stop" is the last offset from where + // we do a memcmp() upward in memory. An overflow for that + // situation is taken care of below. + // + return( stop + sc.key.size() < tail ? Err_WrapBuffer : Err_NoData ); + } + + if( stop + sc.key.size() > tail ) + { + uint diff = stop + sc.key.size() - tail; + stop = stop > diff ? stop - diff : 0; + } + + if( sc.forward == true ) + { + for( uint i = start; i <= stop; i++ ) + { + int result; + if( sc.ignoreCase == true ) + { + result = strncasecmp( &data()[i], sc.key.data(), sc.key.size() ); + } + else + { + result = memcmp( &data()[i], sc.key.data(), sc.key.size() ); + } + if( result == 0 ) + { + if( i != cursorOffset() || mMark.size() != sc.key.size() ) + { + sc.match = true; + cursorGoto( i, 7 ); + markSet( i, sc.key.size() ); + return( Err_Success ); + } + } + } + return( start > head ? Err_WrapBuffer : Err_NoData ); + } + else + { + for( uint i = stop; i >= start; i-- ) + { + int result; + if( sc.ignoreCase == true ) + { + result = strncasecmp( &data()[i], sc.key.data(), sc.key.size() ); + } + else + { + result = memcmp( &data()[i], sc.key.data(), sc.key.size() ); + } + if( result == 0 ) + { + if( i != cursorOffset() || mMark.size() != sc.key.size() ) + { + sc.match = true; + cursorGoto( i, 7 ); + markSet( i, sc.key.size() ); + return( Err_Success ); + } + } + if( i == 0 ) { break; } + } + + return( stop + sc.key.size() <= tail ? Err_WrapBuffer : Err_NoData ); + } +} + + + + + +int CHexBuffer::filter( SFilterControl &fc ) +{ + uint head, tail; + if( fc.inSelection == true ) + { + if( mSelect.valid() == false ) + { + return( Err_NoSelection ); + } + + head = mSelect.start(); + tail = mSelect.stop(); + } + else + { + head = 0; + tail = documentSize(); + } + + uint start, stop; + if( fc.fromCursor == false ) + { + start = head; + stop = tail; + } + else if( fc.forward == true ) + { + start = cursorOffset() < head ? head : cursorOffset(); + stop = tail; + } + else + { + start = head; + stop = cursorOffset() > tail ? tail : cursorOffset(); + } + + if( mInputMode.noInput() == true ) + { + inputSound(); + return( Err_WriteProtect ); + } + + if( start >= stop ) { return( Err_IllegalRange ); } + QByteArray buf( stop - start ); + if( buf.isEmpty() == true ) { return( Err_NoMemory ); } + + int errCode = fc.execute((uchar*)&buf[0],(uchar*)&data()[start],buf.size()); + if( errCode == Err_Success ) + { + recordStart( mCursor ); + cursorGoto( start, 7 ); + recordReplace( mCursor, buf.size(), buf.data(), buf.size() ); + recordEnd( mCursor ); + } + + return( errCode ); +} + + + +int CHexBuffer::collectStrings( CStringCollectControl &sc ) +{ + uint startOffset = 0; + uint start, i; + bool on = false; + + if( sc.minLength < 1 ) { sc.minLength = 1; } + + start = startOffset; + for( i = startOffset; i<documentSize(); i++ ) + { + unsigned char item = data()[i]; + if( isprint( item ) == 0 || item >= 128 ) + { + if( on == true && i-start >= sc.minLength ) + { + QByteArray a( i-start ); + for( uint j=0; j<(i-start); a[j]=data()[start+j], j++ ); + sc.add( start, a ); + } + on = false; + } + else + { + if( on == false ) { start = i; } + on = true; + } + } + + if( on == true && i-start >= sc.minLength ) + { + QByteArray a( i-start ); + for( uint j=0; j<(i-start); a[j]=data()[start+j], j++ ); + sc.add( start, a ); + } + + return( Err_Success ); +} + + +int CHexBuffer::collectStatistic( SStatisticControl &sc, CProgress &p ) +{ + sc.documentSize = documentSize(); + sc.documentName = mUrl; + + for( uint i = 0; i<documentSize(); i++ ) + { + sc.occurrence[ (unsigned char)data()[i] ] += 1; + + // + // The expired() function introduces too much overhead in this case + // so it is only executed every 100'th character + // + if( i % 100 == 0 && p.expired() == true ) + { + int errCode = p.step( (float)i/(float)documentSize() ); + if( errCode == Err_Stop && i+1 < documentSize() ) + { + p.finish(); + return( Err_OperationAborted ); + } + } + } + p.finish(); + return( Err_NoErr ); +} + + +void CHexBuffer::doActionGroup( CHexActionGroup *group ) +{ + if( group == 0 ) + { + return; + } + + CHexAction *action = group->mHexAction; + group->mHexAction = 0; + + while( action != 0 ) + { + doAction( action ); + CHexAction *next = action->mNext; + group->insertAction( action ); + action = next; + } + + computeNumLines(); +} + + +void CHexBuffer::doAction( CHexAction *action ) +{ + if( action->mAction == CHexAction::replace ) + { + doReplace( action, true ); + } +} + + + + + +void CHexBuffer::recordStart( SCursor &cursor ) +{ + // + // Step 1: Remove any undo element that is more recent than the + // current undo index + // + while( mUndoList.count() > mUndoIndex ) + { + mUndoList.removeLast(); + } + + // + // Step 2: Make sure the undo list is no larger than the undo limit. + // We remove the oldest elements in the list. + // + while( mUndoList.count() >= mUndoLimit ) + { + mUndoList.removeFirst(); + mUndoIndex -= 1; + } + + CHexActionGroup *group = new CHexActionGroup( cursor.curr.offset, + cursor.bit() ); + if( group == 0 ) + { + return; + } + + mUndoList.append( group ); + mUndoIndex += 1; +} + + +void CHexBuffer::recordReplace( SCursor &cursor, uint size, char *data1, + uint data1Size ) +{ + CHexAction *hexAction = new CHexAction( CHexAction::replace, + cursor.curr.offset ); + if( hexAction == 0 ) + { + return; + } + + hexAction->mSize = size; + hexAction->mData = data1; + hexAction->mDataSize = data1Size; + + doReplace( hexAction, false ); + mUndoList.getLast()->insertAction( hexAction ); + + if( mCursor.curr.offset < documentSize() ) + { + mCursor.curr.data = data()[ mCursor.curr.offset ]; + } + +} + +void CHexBuffer::recordEnd( SCursor &cursor ) +{ + mUndoList.getLast()->mStopOffset = cursor.curr.offset; + mUndoList.getLast()->mStopBit = cursor.bit(); +} + + +// +// This method is the only place where the doucument data can be changed. +// +void CHexBuffer::doReplace( CHexAction *hexAction, bool removeData ) +{ + uint offset = hexAction->mOffset; + uint oldSize = hexAction->mSize; + char *newData = hexAction->mData; + uint newSize = hexAction->mDataSize; + + hexAction->setData( newSize, &data()[offset], oldSize ); + + // + // Input new data. Resize buffer first if necessary. We always mark the + // data as changed (dirty) when the buffer is resized, otherwise only + // when the new data differ from the current. Nice feature :-) + // + int errCode; + if( newSize > oldSize ) + { + errCode = moveBuffer( offset + newSize - oldSize, offset ); + mDocumentModified = true; + } + else if( newSize < oldSize ) + { + errCode = moveBuffer( offset, offset + oldSize - newSize ); + mDocumentModified = true; + } + else + { + errCode = Err_Success; + if( memcmp( &data()[offset], newData, newSize ) != 0 ) + { + mDocumentModified = true; + } + } + + if( errCode == Err_Success ) + { + memcpy( &data()[offset], newData, newSize ); + } + + // + // Data is removed regardless of success or not. Otherwise we will + // have a mmeory leak. The single reason why the operation above could + // fail is because there was that no more memory that could be + // allocated. + // + if( removeData == true ) + { + delete [] newData; + } + +} + + +bool CHexBuffer::inputDummy( unsigned char *dest, int value, uint cell ) +{ + (void)dest; + (void)value; + (void)cell; + return( false ); +} + + +bool CHexBuffer::inputHexadecimal( unsigned char *dest, int value, uint cell ) +{ + if( value >= '0' && value <= '9' ) + { + value = value - '0'; + } + else if( value >= 'A' && value <= 'F' ) + { + value = value - 'A' + 10; + } + else if( value >= 'a' && value <= 'f' ) + { + value = value - 'a' + 10; + } + else + { + return( false ); + } + + if( cell > 1 ) + { + return( false ); + } + + uint shift = 1 - cell; + *dest = (*dest & ~(0xF<<(shift*4)) ) | (value<<(shift*4)); + return( true ); +} + + +bool CHexBuffer::inputDecimal( unsigned char *dest, int value, uint cell ) +{ + // + // 2000-01-22 Espen Sand + // I do the insertion a bit different here since decimal is special + // with respect to bitwidths. + // + if( value < '0' || value > '9' || cell > 2 ) + { + return( false ); + } + + char buf[4]; + printDecimalCell( buf, *dest ); + buf[cell]=value; + buf[3]=0; + + int tmp = atoi(buf); + if( tmp > 255 ) + { + return( false ); + } + + *dest = tmp; + return( true ); +} + + +bool CHexBuffer::inputOctal( unsigned char *dest, int value, uint cell ) +{ + if( value >= '0' && value <= '7' ) + { + value = value - '0'; + if( cell == 0 && value > 3 ) + { + return( false ); + } + } + else + { + return( false ); + } + + if( cell >= 3 ) + { + return( false ); + } + + uint shift = 2 - cell; + *dest = (*dest & ~(0x7<<(shift*3)) ) | (value<<(shift*3)); + return( true ); +} + + +bool CHexBuffer::inputBinary( unsigned char *dest, int value, uint cell ) +{ + if( value >= '0' && value <= '1' ) + { + value = value - '0'; + } + else + { + return( false ); + } + + if( cell > 7 ) + { + return( false ); + } + + uint shift = 7 - cell; + *dest = (*dest & ~(1<<shift)) | (value<<shift); + return( true ); +} + + + +bool CHexBuffer::inputAscii( unsigned char *dest, int value, uint ) +{ + *dest = value; + return( true ); +} + + + +int CHexBuffer::moveBuffer( uint destOffset, uint srcOffset ) +{ + if( srcOffset > documentSize() || destOffset == srcOffset ) + { + return( Err_Success ); + } + + if( destOffset < srcOffset ) + { + char *dest = &data()[ destOffset ]; + char *src = &data()[ srcOffset ]; + + memmove( dest, src, documentSize() - srcOffset ); + setDocumentSize( documentSize() - (srcOffset - destOffset) ); + return( Err_Success ); + } + else + { + uint s = documentSize() - srcOffset; + if( destOffset + s >= size() ) + { + int errCode = resizeBuffer( destOffset + s ); + if( errCode != Err_Success ) + { + fatalSound(); + return( errCode ); + } + } + else + { + setDocumentSize( documentSize() + (destOffset - srcOffset) ); + } + + char *dest = &data()[ destOffset ]; + char *src = &data()[ srcOffset ]; + + memmove( dest, src, s ); + memset( src, 0, destOffset - srcOffset ); + return( Err_Success ); + } +} + + + + +int CHexBuffer::resizeBuffer( uint newSize ) +{ + if( newSize < documentSize() ) + { + return( Err_Success ); + } + + if( newSize >= size() ) + { + QByteArray tmp; + tmp.duplicate( data(), size() ); + if( tmp.isNull() == true ) + { + return( Err_NoMemory ); + } + + if( fill( '\0', newSize + 100 ) == false ) + { + return( Err_NoMemory ); + } + + memcpy( data(), &tmp[0], tmp.size() ); + } + + setDocumentSize( newSize ); + return( Err_Success ); +} + + +void CHexBuffer::inputSound( void ) +{ + if( mInputErrorSound == true ) + { + KNotifyClient::beep( QObject::tr("Edit operation failed") ); + } +} + + +void CHexBuffer::fatalSound( void ) +{ + if( mFatalErrorSound == true ) + { + KNotifyClient::beep( QObject::tr("Could not allocate memory") ); + } +} + + +int CHexBuffer::printHtmlDataPage( const QString &tocName, + const QStringList &fileNames, uint index, + const SExportHtml &ex, + uint line, uint numLine ) +{ + if( fileNames.count() == 0 ) + { + return( Err_NullArgument ); + } + + if( index >= fileNames.count() ) + { + index = fileNames.count()-1; + } + + QFile file( fileNames[index] ); + if( file.open( IO_WriteOnly ) == false ) + { + return( Err_OperationAborted ); + } + + QTextStream os( &file ); + const QString *next = index+1 >= fileNames.count() ? 0 : &fileNames[index+1]; + const QString *prev = index == 0 ? 0 : &fileNames[index-1]; + const QString *toc = tocName.length() == 0 ? 0 : &tocName; + + printHtmlHeader( os, true ); + if( ex.navigator == true ) + { + printHtmlNavigator( os, next, prev, toc ); + } + + printHtmlCaption( os, ex.topCaption, index+1, fileNames.count() ); + printHtmlTable( os, line, numLine, ex.blackWhite ); + printHtmlCaption( os, ex.bottomCaption, index+1, fileNames.count() ); + + if( ex.navigator == true ) + { + printHtmlNavigator( os, next, prev, toc ); + } + printHtmlHeader( os, false ); + + return( Err_Success ); +} + + +void CHexBuffer::printHtmlTocPage( const QString &tocName, + const QString &linkName, + const QStringList &fileNames, + const QStringList &offsets, + uint numPage ) +{ + if( numPage == 0 || fileNames.count() == 0 ) + { + return; + } + if( numPage >= fileNames.count() ) + { + numPage = fileNames.count() - 1; + } + + QFile file( tocName ); + if( file.open( IO_WriteOnly ) == false ) + { + return; + } + + QTextStream os( &file ); + printHtmlHeader( os, true ); + + os << "<P ALIGN=\"CENTER\">" << endl; + os << "<B><FONT COLOR=BLACK>" << endl; + os << mUrl << endl; + os << "</FONT></B></CAPTION>" << endl; + os << "</P>" << endl; + + os << "<P ALIGN=\"CENTER\"><TT>" << endl; + for( uint i=0; i<=numPage; i++ ) + { + QString n( fileNames[i].right( fileNames[i].length() - + fileNames[i].findRev('/') - 1) ); + os << "<A HREF=\"" << n << "\">" << i18n("Page") << i+1; + os << "</A>"; + os << " " << offsets[i]; + os << "<br>" << endl; + } + os << "</P>" << endl; + + printHtmlHeader( os, false ); + + if( linkName.isEmpty() == false ) + { + // + // Make a symlink. We ignore any error here. I don't consider + // it to be fatal. + // + QString n( tocName.right( tocName.length() - tocName.findRev('/') - 1) ); + symlink( n.latin1(), linkName.latin1() ); + } + +} + + + +void CHexBuffer::printHtmlCaption( QTextStream &os, uint captionType, + uint curPage, uint numPage ) +{ + QString caption; + switch( captionType ) + { + case 0: + return; + break; + + case 1: + caption = mUrl; + break; + + case 2: + caption = mUrl.right( mUrl.length() - mUrl.findRev('/') - 1); + break; + + case 3: + caption = i18n("Page %1 of %2").arg(curPage).arg(numPage); + break; + } + + os << "<P ALIGN=\"CENTER\">" << endl; + os << "<B><FONT COLOR=BLACK>" << endl; + os << caption << endl; + os << "</FONT></B></CAPTION>" << endl; + os << "</P>" << endl; +} + + + +void CHexBuffer::printHtmlNavigator( QTextStream &os, const QString *next, + const QString *prev, const QString *toc ) +{ + os << "<TABLE BORDER=\"0\" CELLSPACING=\"0\" WIDTH=\"100%\">" << endl; + os << "<TR>" << endl; + os << "<TD>" << endl; + if( next == 0 ) + { + os << i18n("Next") << " "; + } + else + { + QString n( next->right( next->length() - next->findRev('/') - 1) ); + os << "<A HREF=\"" << n << "\">" << i18n("Next") << "</A>" << " "; + } + + if( prev == 0 ) + { + os << i18n("Previous") << " "; + } + else + { + QString p( prev->right( prev->length() - prev->findRev('/') - 1) ); + os << "<A HREF=\"" << p << "\">" << i18n("Previous") << "</A>" << " "; + } + + if( toc == 0 ) + { + os << i18n("Contents") << " "; + } + else + { + QString t( toc->right( toc->length() - toc->findRev('/') - 1) ); + os << "<A HREF=\"" << t << "\">" << i18n("Contents"); + os << "</A>" << " "; + } + + os << "</TD>" << endl; + + os << "<TD ALIGN=\"RIGHT\">" << endl; + os << "<A HREF=\"" << "http://home.sol.no/~espensa/khexedit" << "\">"; + os << i18n("Generated by khexedit"); + os << "</A>" << " "; + + os << "</TD>" << endl; + os << "</TR>" << endl << "</TABLE>" << endl; +} + + +int CHexBuffer::printHtmlHeader( QTextStream &os, bool isFront ) +{ + if( isFront == true ) + { + os << "<HTML>" << endl << "<HEAD>" << endl; + os << "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; "; + os << "charset=iso-8859-1\">" << endl; + os << "<META NAME=\"hexdata\" CONTENT=\"khexedit dump\">" << endl; + os << "</HEAD>" << endl << "<BODY>" << endl; + } + else + { + os << "</BODY>" << endl << "</HTML>" << endl; + } + + return( Err_Success ); +} + + +int CHexBuffer::printHtmlTable( QTextStream &os, uint line, uint numLine, + bool bw ) +{ + uint i; + QColor color; + + + int numCol = 1; + if( mLayout.offsetVisible == true ) { numCol += 1; } + if( mLayout.secondaryMode != SDisplayLayout::hide ) { numCol += 1; } + + os << "<TABLE BORDER=1 COLS=" << numCol << " WIDTH=\"100%\" "; + os << "CELLSPACING=0 CELLPADDING=2>" << endl; + if( mLayout.offsetVisible == true ) + { + color = bw == true ? Qt::white : mColor.offsetBg; + os << "<TD BGCOLOR=" << color.name().latin1() << ">" << endl; + os << "<TABLE BORDER=0 COLS=1 WIDTH=\"100%\" "; + os << "CELLSPACING=0 CELLPADDING=2>" << endl; + + color = bw == true ? Qt::black : mColor.offsetFg; + for( i=0; i<numLine; i++ ) + { + os << "<TR><TD><TT><b><FONT COLOR=" << color.name().latin1() << ">"; + THIS_FPTR(printOffset)( mPrintBuf, (line+i)*mLayout.lineSize ); + mPrintBuf[mOffsetSize]=0; + os << mPrintBuf << "</TD></TR>" << endl; + } + os << "</TABLE>" << endl << "</TD>" << endl; + } + + color = bw == true ? Qt::white : mColor.textBg; + os << "<TD BGCOLOR=" << color.name().latin1() << ">" << endl; + os << "<TABLE BORDER=0 COLS=1 WIDTH=\"100%\" "; + os << "CELLSPACING=0 CELLPADDING=2>" << endl; + for( i=0; i<numLine; i++ ) + { + printHtmlLine( os, line+i, true, bw ); + } + os << "</TABLE>" << endl << "</TD>" << endl; + + if( mLayout.secondaryMode != SDisplayLayout::hide ) + { + color = bw == true ? Qt::white : mColor.textBg; + os << "<TD BGCOLOR=" << color.name().latin1() << ">" << endl; + os << "<TABLE BORDER=0 COLS=1 WIDTH=\"100%\" "; + os << "CELLSPACING=0 CELLPADDING=2>" << endl; + for( i=0; i<numLine; i++ ) + { + printHtmlLine( os, line+i, false, bw ); + } + os << "</TABLE>" << endl << "</TD>" << endl; + } + + os << "</TR>" << endl << "</TABLE>" << endl; + return( Err_Success ); +} + + +int CHexBuffer::printHtmlLine( QTextStream &os, uint line, bool isPrimary, + bool bw ) +{ + uint offset = line * mLayout.lineSize; + QColor prevColor; + + QColor color; + if( bw == true ) + { + color = Qt::white; + } + else + { + color = (line+1) % 2 ? mColor.textBg : mColor.secondTextBg; + } + + os << "<TR><TD NOWRAP BGCOLOR=" << color.name().latin1() << "><TT><B>" + << endl; + if( offset >= documentSize() ) + { + os << "<BR></TD></TR>" << endl; + return( Err_Success ); + } + + for( uint i=0; i < mLayout.lineSize; i++ ) + { + if( isPrimary == true ) + { + if( offset+i >= documentSize() ) + { + memset(mPrintBuf, ' ', mNumCell ); + mPrintBuf[mNumCell] = 0; + if( i == 0 ) + { + color = bw == true ? Qt::black : foregroundColor(i); + } + else + { + color = prevColor; + } + } + else + { + unsigned char val = (unsigned char)data()[offset+i]; + if( THIS_FPTR(printCell)( mPrintBuf, val ) == 0 ) + { + color = bw == true ? Qt::black : foregroundColor(i); + } + else + { + color = bw == true ? Qt::black : mColor.nonPrintFg; + } + } + mPrintBuf[mNumCell] = 0; + if( i == 0 ) + { + os << "<FONT COLOR=" << color.name().latin1() << ">"; + } + else if( color != prevColor ) + { + os << "</FONT><FONT COLOR=" << color.name().latin1() << ">"; + } + prevColor = color; + + if( mPrintBuf[0] == '<' ) + { + os << "<"; + } + else + { + os << mPrintBuf; + if( (i+1) % mLayout.columnSize == 0 && (i+1) != mLayout.lineSize ) + { + os << " "; + } + } + } + else + { + if( offset+i >= documentSize() ) + { + memset(mPrintBuf, ' ', 1 ); + if( i == 0 ) + { + color = bw == true ? Qt::black : mColor.secondaryFg; + } + else + { + color = prevColor; + } + } + else + { + unsigned char val = (unsigned char)data()[offset+i]; + if( printAsciiCell( mPrintBuf, val ) == 0 ) + { + color = bw == true ? Qt::black : mColor.secondaryFg; + } + else + { + color = bw == true ? Qt::black : mColor.nonPrintFg; + } + mPrintBuf[1] = 0; + + if( i == 0 ) + { + os << "<FONT COLOR=" << color.name().latin1() << ">"; + } + else if( color != prevColor ) + { + os << "</FONT><FONT COLOR=" << color.name().latin1() << ">"; + } + prevColor = color; + + mPrintBuf[1] = 0; + os << (mPrintBuf[0] == '<' ? "<" : mPrintBuf); + } + + } + } + os << "</TD></TR>" << endl; + return( Err_Success ); +} + + + + + |