summaryrefslogtreecommitdiff
path: root/oox/source/xls/biffinputstream.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/xls/biffinputstream.cxx')
-rw-r--r--oox/source/xls/biffinputstream.cxx631
1 files changed, 631 insertions, 0 deletions
diff --git a/oox/source/xls/biffinputstream.cxx b/oox/source/xls/biffinputstream.cxx
new file mode 100644
index 000000000000..fec36d4fa54d
--- /dev/null
+++ b/oox/source/xls/biffinputstream.cxx
@@ -0,0 +1,631 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org 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 Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include "oox/xls/biffinputstream.hxx"
+#include <algorithm>
+#include <rtl/ustrbuf.hxx>
+
+using ::rtl::OString;
+using ::rtl::OStringToOUString;
+using ::rtl::OUString;
+using ::rtl::OUStringBuffer;
+
+namespace oox {
+namespace xls {
+
+// ============================================================================
+
+namespace prv {
+
+BiffInputRecordBuffer::BiffInputRecordBuffer( BinaryInputStream& rInStrm ) :
+ mrInStrm( rInStrm ),
+ mpCurrentData( 0 ),
+ mnHeaderPos( -1 ),
+ mnBodyPos( 0 ),
+ mnBufferBodyPos( 0 ),
+ mnNextHeaderPos( 0 ),
+ mnRecId( BIFF_ID_UNKNOWN ),
+ mnRecSize( 0 ),
+ mnRecPos( 0 ),
+ mbValidHeader( false )
+{
+ OSL_ENSURE( mrInStrm.isSeekable(), "BiffInputRecordBuffer::BiffInputRecordBuffer - stream must be seekable" );
+ mrInStrm.seekToStart();
+ maOriginalData.reserve( SAL_MAX_UINT16 );
+ maDecodedData.reserve( SAL_MAX_UINT16 );
+ enableDecoder( false ); // updates mpCurrentData
+}
+
+void BiffInputRecordBuffer::restartAt( sal_Int64 nPos )
+{
+ mnHeaderPos = -1;
+ mnBodyPos = mnBufferBodyPos = 0;
+ mnNextHeaderPos = nPos;
+ mnRecId = BIFF_ID_UNKNOWN;
+ mnRecSize = mnRecPos = 0;
+ mbValidHeader = false;
+}
+
+void BiffInputRecordBuffer::setDecoder( const BiffDecoderRef& rxDecoder )
+{
+ mxDecoder = rxDecoder;
+ enableDecoder( true );
+ updateDecoded();
+}
+
+void BiffInputRecordBuffer::enableDecoder( bool bEnable )
+{
+ mpCurrentData = (bEnable && mxDecoder.get() && mxDecoder->isValid()) ? &maDecodedData : &maOriginalData;
+}
+
+bool BiffInputRecordBuffer::startRecord( sal_Int64 nHeaderPos )
+{
+ mbValidHeader = (0 <= nHeaderPos) && (nHeaderPos + 4 <= mrInStrm.getLength());
+ if( mbValidHeader )
+ {
+ mnHeaderPos = nHeaderPos;
+ mrInStrm.seek( nHeaderPos );
+ mrInStrm >> mnRecId >> mnRecSize;
+ mnBodyPos = mrInStrm.tell();
+ mnNextHeaderPos = mnBodyPos + mnRecSize;
+ mbValidHeader = !mrInStrm.isEof() && (mnNextHeaderPos <= mrInStrm.getLength());
+ }
+ if( !mbValidHeader )
+ {
+ mnHeaderPos = mnBodyPos = -1;
+ mnNextHeaderPos = 0;
+ mnRecId = BIFF_ID_UNKNOWN;
+ mnRecSize = 0;
+ }
+ mnRecPos = 0;
+ return mbValidHeader;
+}
+
+bool BiffInputRecordBuffer::startNextRecord()
+{
+ return startRecord( mnNextHeaderPos );
+}
+
+sal_uInt16 BiffInputRecordBuffer::getNextRecId()
+{
+ sal_uInt16 nRecId = BIFF_ID_UNKNOWN;
+ if( mbValidHeader && (mnNextHeaderPos + 4 <= mrInStrm.getLength()) )
+ {
+ mrInStrm.seek( mnNextHeaderPos );
+ mrInStrm >> nRecId;
+ }
+ return nRecId;
+}
+
+void BiffInputRecordBuffer::read( void* opData, sal_uInt16 nBytes )
+{
+ updateBuffer();
+ OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::read - nothing to read" );
+ OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::read - buffer overflow" );
+ memcpy( opData, &(*mpCurrentData)[ mnRecPos ], nBytes );
+ mnRecPos = mnRecPos + nBytes;
+}
+
+void BiffInputRecordBuffer::skip( sal_uInt16 nBytes )
+{
+ OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::skip - nothing to skip" );
+ OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::skip - buffer overflow" );
+ mnRecPos = mnRecPos + nBytes;
+}
+
+void BiffInputRecordBuffer::updateBuffer()
+{
+ OSL_ENSURE( mbValidHeader, "BiffInputRecordBuffer::updateBuffer - invalid access" );
+ if( mnBodyPos != mnBufferBodyPos )
+ {
+ mrInStrm.seek( mnBodyPos );
+ maOriginalData.resize( mnRecSize );
+ if( mnRecSize > 0 )
+ mrInStrm.readMemory( &maOriginalData.front(), static_cast< sal_Int32 >( mnRecSize ) );
+ mnBufferBodyPos = mnBodyPos;
+ updateDecoded();
+ }
+}
+
+void BiffInputRecordBuffer::updateDecoded()
+{
+ if( mxDecoder.get() && mxDecoder->isValid() )
+ {
+ maDecodedData.resize( mnRecSize );
+ if( mnRecSize > 0 )
+ mxDecoder->decode( &maDecodedData.front(), &maOriginalData.front(), mnBodyPos, mnRecSize );
+ }
+}
+
+} // namespace prv
+
+// ============================================================================
+
+BiffInputStream::BiffInputStream( BinaryInputStream& rInStream, bool bContLookup ) :
+ maRecBuffer( rInStream ),
+ mnRecHandle( -1 ),
+ mnRecId( BIFF_ID_UNKNOWN ),
+ mnAltContId( BIFF_ID_UNKNOWN ),
+ mnCurrRecSize( 0 ),
+ mnComplRecSize( 0 ),
+ mbHasComplRec( false ),
+ mbCont( bContLookup )
+{
+ mbEof = true; // EOF will be true if stream is not inside a record
+}
+
+// record control -------------------------------------------------------------
+
+bool BiffInputStream::startNextRecord()
+{
+ bool bValidRec = false;
+ /* #i4266# ignore zero records (id==len==0) (e.g. the application
+ "Crystal Report" writes zero records between other records) */
+ bool bIsZeroRec = false;
+ do
+ {
+ // record header is never encrypted
+ maRecBuffer.enableDecoder( false );
+ // read header of next raw record, returns false at end of stream
+ bValidRec = maRecBuffer.startNextRecord();
+ // ignore record, if identifier and size are zero
+ bIsZeroRec = (maRecBuffer.getRecId() == 0) && (maRecBuffer.getRecSize() == 0);
+ }
+ while( bValidRec && ((mbCont && isContinueId( maRecBuffer.getRecId() )) || bIsZeroRec) );
+
+ // setup other class members
+ setupRecord();
+ return isInRecord();
+}
+
+bool BiffInputStream::startRecordByHandle( sal_Int64 nRecHandle )
+{
+ rewindToRecord( nRecHandle );
+ return startNextRecord();
+}
+
+void BiffInputStream::resetRecord( bool bContLookup, sal_uInt16 nAltContId )
+{
+ if( isInRecord() )
+ {
+ mbCont = bContLookup;
+ mnAltContId = nAltContId;
+ restartRecord( true );
+ maRecBuffer.enableDecoder( true );
+ }
+}
+
+void BiffInputStream::rewindRecord()
+{
+ rewindToRecord( mnRecHandle );
+}
+
+// decoder --------------------------------------------------------------------
+
+void BiffInputStream::setDecoder( const BiffDecoderRef& rxDecoder )
+{
+ maRecBuffer.setDecoder( rxDecoder );
+}
+
+void BiffInputStream::enableDecoder( bool bEnable )
+{
+ maRecBuffer.enableDecoder( bEnable );
+}
+
+// stream/record state and info -----------------------------------------------
+
+sal_uInt16 BiffInputStream::getNextRecId()
+{
+ sal_uInt16 nRecId = BIFF_ID_UNKNOWN;
+ if( isInRecord() )
+ {
+ sal_Int64 nCurrPos = tell(); // save current position in record
+ while( jumpToNextContinue() ) {} // skip following CONTINUE records
+ if( maRecBuffer.startNextRecord() ) // read header of next record
+ nRecId = maRecBuffer.getRecId();
+ seek( nCurrPos ); // restore position, seek() resets old mbValid state
+ }
+ return nRecId;
+}
+
+// BinaryStreamBase interface (seeking) ---------------------------------------
+
+bool BiffInputStream::isSeekable() const
+{
+ return true;
+}
+
+sal_Int64 BiffInputStream::tell() const
+{
+ return mbEof ? -1 : (mnCurrRecSize - maRecBuffer.getRecLeft());
+}
+
+sal_Int64 BiffInputStream::getLength() const
+{
+ if( !mbHasComplRec )
+ const_cast< BiffInputStream* >( this )->calcRecordLength();
+ return mnComplRecSize;
+}
+
+void BiffInputStream::seek( sal_Int64 nRecPos )
+{
+ if( isInRecord() )
+ {
+ if( mbEof || (nRecPos < tell()) )
+ restartRecord( false );
+ if( !mbEof && (nRecPos > tell()) )
+ skip( static_cast< sal_Int32 >( nRecPos - tell() ) );
+ }
+}
+
+sal_Int64 BiffInputStream::tellBase() const
+{
+ return maRecBuffer.getBaseStream().tell();
+}
+
+sal_Int64 BiffInputStream::getBaseLength() const
+{
+ return maRecBuffer.getBaseStream().getLength();
+}
+
+// BinaryInputStream interface (stream read access) ---------------------------
+
+sal_Int32 BiffInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes )
+{
+ sal_Int32 nRet = 0;
+ if( !mbEof )
+ {
+ orData.realloc( ::std::max< sal_Int32 >( nBytes, 0 ) );
+ if( nBytes > 0 )
+ {
+ nRet = readMemory( orData.getArray(), nBytes );
+ if( nRet < nBytes )
+ orData.realloc( nRet );
+ }
+ }
+ return nRet;
+}
+
+sal_Int32 BiffInputStream::readMemory( void* opMem, sal_Int32 nBytes )
+{
+ sal_Int32 nRet = 0;
+ if( !mbEof && opMem && (nBytes > 0) )
+ {
+ sal_uInt8* pnBuffer = reinterpret_cast< sal_uInt8* >( opMem );
+ sal_Int32 nBytesLeft = nBytes;
+
+ while( !mbEof && (nBytesLeft > 0) )
+ {
+ sal_uInt16 nReadSize = getMaxRawReadSize( nBytesLeft );
+ // check nReadSize, stream may already be located at end of a raw record
+ if( nReadSize > 0 )
+ {
+ maRecBuffer.read( pnBuffer, nReadSize );
+ nRet += nReadSize;
+ pnBuffer += nReadSize;
+ nBytesLeft -= nReadSize;
+ }
+ if( nBytesLeft > 0 )
+ jumpToNextContinue();
+ OSL_ENSURE( !mbEof, "BiffInputStream::readMemory - record overread" );
+ }
+ }
+ return nRet;
+}
+
+void BiffInputStream::skip( sal_Int32 nBytes )
+{
+ sal_Int32 nBytesLeft = nBytes;
+ while( !mbEof && (nBytesLeft > 0) )
+ {
+ sal_uInt16 nSkipSize = getMaxRawReadSize( nBytesLeft );
+ // check nSkipSize, stream may already be located at end of a raw record
+ if( nSkipSize > 0 )
+ {
+ maRecBuffer.skip( nSkipSize );
+ nBytesLeft -= nSkipSize;
+ }
+ if( nBytesLeft > 0 )
+ jumpToNextContinue();
+ OSL_ENSURE( !mbEof, "BiffInputStream::skip - record overread" );
+ }
+}
+
+// byte strings ---------------------------------------------------------------
+
+OString BiffInputStream::readByteString( bool b16BitLen, bool bAllowNulChars )
+{
+ sal_Int32 nStrLen = b16BitLen ? readuInt16() : readuInt8();
+ return readCharArray( nStrLen, bAllowNulChars );
+}
+
+OUString BiffInputStream::readByteStringUC( bool b16BitLen, rtl_TextEncoding eTextEnc, bool bAllowNulChars )
+{
+ return OStringToOUString( readByteString( b16BitLen, bAllowNulChars ), eTextEnc );
+}
+
+void BiffInputStream::skipByteString( bool b16BitLen )
+{
+ skip( b16BitLen ? readuInt16() : readuInt8() );
+}
+
+// Unicode strings ------------------------------------------------------------
+
+OUString BiffInputStream::readUniStringChars( sal_uInt16 nChars, bool b16BitChars, bool bAllowNulChars )
+{
+ OUStringBuffer aBuffer;
+ aBuffer.ensureCapacity( nChars );
+
+ /* This function has to react on CONTINUE records to read the repeated
+ flags field, so readUnicodeArray() cannot be used here. */
+ sal_uInt16 nCharsLeft = nChars;
+ while( !mbEof && (nCharsLeft > 0) )
+ {
+ sal_uInt16 nPortionCount = 0;
+ if( b16BitChars )
+ {
+ nPortionCount = ::std::min< sal_uInt16 >( nCharsLeft, maRecBuffer.getRecLeft() / 2 );
+ OSL_ENSURE( (nPortionCount <= nCharsLeft) || ((maRecBuffer.getRecLeft() & 1) == 0),
+ "BiffInputStream::readUniStringChars - missing a byte" );
+ }
+ else
+ {
+ nPortionCount = getMaxRawReadSize( nCharsLeft );
+ }
+
+ // read the character array
+ appendUnicodeArray( aBuffer, nPortionCount, b16BitChars, bAllowNulChars );
+
+ // prepare for next CONTINUE record
+ nCharsLeft = nCharsLeft - nPortionCount;
+ if( nCharsLeft > 0 )
+ jumpToNextStringContinue( b16BitChars );
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString BiffInputStream::readUniStringBody( sal_uInt16 nChars, bool bAllowNulChars )
+{
+ bool b16BitChars;
+ sal_Int32 nAddSize;
+ readUniStringHeader( b16BitChars, nAddSize );
+ OUString aString = readUniStringChars( nChars, b16BitChars, bAllowNulChars );
+ skip( nAddSize );
+ return aString;
+}
+
+OUString BiffInputStream::readUniString( bool bAllowNulChars )
+{
+ return readUniStringBody( readuInt16(), bAllowNulChars );
+}
+
+void BiffInputStream::skipUniStringChars( sal_uInt16 nChars, bool b16BitChars )
+{
+ sal_uInt16 nCharsLeft = nChars;
+ while( !mbEof && (nCharsLeft > 0) )
+ {
+ sal_uInt16 nPortionCount;
+ if( b16BitChars )
+ {
+ nPortionCount = ::std::min< sal_uInt16 >( nCharsLeft, maRecBuffer.getRecLeft() / 2 );
+ OSL_ENSURE( (nPortionCount <= nCharsLeft) || ((maRecBuffer.getRecLeft() & 1) == 0),
+ "BiffInputStream::skipUniStringChars - missing a byte" );
+ skip( 2 * nPortionCount );
+ }
+ else
+ {
+ nPortionCount = getMaxRawReadSize( nCharsLeft );
+ skip( nPortionCount );
+ }
+
+ // prepare for next CONTINUE record
+ nCharsLeft = nCharsLeft - nPortionCount;
+ if( nCharsLeft > 0 )
+ jumpToNextStringContinue( b16BitChars );
+ }
+}
+
+void BiffInputStream::skipUniStringBody( sal_uInt16 nChars )
+{
+ bool b16BitChars;
+ sal_Int32 nAddSize;
+ readUniStringHeader( b16BitChars, nAddSize );
+ skipUniStringChars( nChars, b16BitChars );
+ skip( nAddSize );
+}
+
+void BiffInputStream::skipUniString()
+{
+ skipUniStringBody( readuInt16() );
+}
+
+// private --------------------------------------------------------------------
+
+void BiffInputStream::readAtom( void* opMem, sal_uInt8 nSize )
+{
+ // byte swapping is done in calling BinaryInputStream::readValue() template function
+ if( ensureRawReadSize( nSize ) )
+ maRecBuffer.read( opMem, nSize );
+}
+
+void BiffInputStream::setupRecord()
+{
+ // initialize class members
+ mnRecHandle = maRecBuffer.getRecHeaderPos();
+ mnRecId = maRecBuffer.getRecId();
+ mnAltContId = BIFF_ID_UNKNOWN;
+ mnCurrRecSize = mnComplRecSize = maRecBuffer.getRecSize();
+ mbHasComplRec = !mbCont;
+ mbEof = !isInRecord();
+ // enable decoder in new record
+ enableDecoder( true );
+}
+
+void BiffInputStream::restartRecord( bool bInvalidateRecSize )
+{
+ if( isInRecord() )
+ {
+ maRecBuffer.startRecord( getRecHandle() );
+ mnCurrRecSize = maRecBuffer.getRecSize();
+ if( bInvalidateRecSize )
+ {
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = !mbCont;
+ }
+ mbEof = false;
+ }
+}
+
+void BiffInputStream::rewindToRecord( sal_Int64 nRecHandle )
+{
+ if( nRecHandle >= 0 )
+ {
+ maRecBuffer.restartAt( nRecHandle );
+ mnRecHandle = -1;
+ mbEof = true; // as long as the record is not started
+ }
+}
+
+bool BiffInputStream::isContinueId( sal_uInt16 nRecId ) const
+{
+ return (nRecId == BIFF_ID_CONT) || (nRecId == mnAltContId);
+}
+
+bool BiffInputStream::jumpToNextContinue()
+{
+ mbEof = mbEof || !mbCont || !isContinueId( maRecBuffer.getNextRecId() ) || !maRecBuffer.startNextRecord();
+ if( !mbEof )
+ mnCurrRecSize += maRecBuffer.getRecSize();
+ return !mbEof;
+}
+
+bool BiffInputStream::jumpToNextStringContinue( bool& rb16BitChars )
+{
+ OSL_ENSURE( maRecBuffer.getRecLeft() == 0, "BiffInputStream::jumpToNextStringContinue - unexpected garbage" );
+
+ if( mbCont && (getRemaining() > 0) )
+ {
+ jumpToNextContinue();
+ }
+ else if( mnRecId == BIFF_ID_CONT )
+ {
+ /* CONTINUE handling is off, but we have started reading in a CONTINUE
+ record -> start next CONTINUE for TXO import. We really start a new
+ record here - no chance to return to string origin. */
+ mbEof = mbEof || (maRecBuffer.getNextRecId() != BIFF_ID_CONT) || !maRecBuffer.startNextRecord();
+ if( !mbEof )
+ setupRecord();
+ }
+
+ // trying to read the flags invalidates stream, if no CONTINUE record has been found
+ sal_uInt8 nFlags;
+ readValue( nFlags );
+ rb16BitChars = getFlag( nFlags, BIFF_STRF_16BIT );
+ return !mbEof;
+}
+
+void BiffInputStream::calcRecordLength()
+{
+ sal_Int64 nCurrPos = tell(); // save current position in record
+ while( jumpToNextContinue() ) {} // jumpToNextContinue() adds up mnCurrRecSize
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = true;
+ seek( nCurrPos ); // restore position, seek() resets old mbValid state
+}
+
+bool BiffInputStream::ensureRawReadSize( sal_uInt16 nBytes )
+{
+ if( !mbEof && (nBytes > 0) )
+ {
+ while( !mbEof && (maRecBuffer.getRecLeft() == 0) ) jumpToNextContinue();
+ mbEof = mbEof || (nBytes > maRecBuffer.getRecLeft());
+ OSL_ENSURE( !mbEof, "BiffInputStream::ensureRawReadSize - record overread" );
+ }
+ return !mbEof;
+}
+
+sal_uInt16 BiffInputStream::getMaxRawReadSize( sal_Int32 nBytes ) const
+{
+ return getLimitedValue< sal_uInt16, sal_Int32 >( nBytes, 0, maRecBuffer.getRecLeft() );
+}
+
+void BiffInputStream::appendUnicodeArray( OUStringBuffer& orBuffer, sal_uInt16 nChars, bool b16BitChars, bool bAllowNulChars )
+{
+ orBuffer.ensureCapacity( orBuffer.getLength() + nChars );
+ sal_uInt16 nChar;
+ for( sal_uInt16 nCharIdx = 0; !mbEof && (nCharIdx < nChars); ++nCharIdx )
+ {
+ if( b16BitChars ) readValue( nChar ); else nChar = readuInt8();
+ orBuffer.append( static_cast< sal_Unicode >( (!bAllowNulChars && (nChar == 0)) ? '?' : nChar ) );
+ }
+}
+
+void BiffInputStream::readUniStringHeader( bool& orb16BitChars, sal_Int32& ornAddSize )
+{
+ sal_uInt8 nFlags = readuInt8();
+ OSL_ENSURE( !getFlag( nFlags, BIFF_STRF_UNKNOWN ), "BiffInputStream::readUniStringHeader - unknown flags" );
+ orb16BitChars = getFlag( nFlags, BIFF_STRF_16BIT );
+ sal_uInt16 nFontCount = getFlag( nFlags, BIFF_STRF_RICH ) ? readuInt16() : 0;
+ sal_Int32 nPhoneticSize = getFlag( nFlags, BIFF_STRF_PHONETIC ) ? readInt32() : 0;
+ ornAddSize = 4 * nFontCount + ::std::max< sal_Int32 >( 0, nPhoneticSize );
+}
+
+// ============================================================================
+
+BiffInputStreamPos::BiffInputStreamPos( BiffInputStream& rStrm ) :
+ mrStrm( rStrm ),
+ mnRecHandle( rStrm.getRecHandle() ),
+ mnRecPos( rStrm.tell() )
+{
+}
+
+bool BiffInputStreamPos::restorePosition()
+{
+ bool bValidRec = mrStrm.startRecordByHandle( mnRecHandle );
+ if( bValidRec )
+ mrStrm.seek( mnRecPos );
+ return bValidRec && !mrStrm.isEof();
+}
+
+// ============================================================================
+
+BiffInputStreamPosGuard::BiffInputStreamPosGuard( BiffInputStream& rStrm ) :
+ BiffInputStreamPos( rStrm )
+{
+}
+
+BiffInputStreamPosGuard::~BiffInputStreamPosGuard()
+{
+ restorePosition();
+}
+
+// ============================================================================
+
+} // namespace xls
+} // namespace oox
+