xref: /trunk/main/oox/source/xls/biffinputstream.cxx (revision ca5ec200)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "oox/xls/biffinputstream.hxx"
25 
26 #include <algorithm>
27 #include <rtl/ustrbuf.hxx>
28 
29 namespace oox {
30 namespace xls {
31 
32 // ============================================================================
33 
34 using ::rtl::OString;
35 using ::rtl::OStringToOUString;
36 using ::rtl::OUString;
37 using ::rtl::OUStringBuffer;
38 
39 // ============================================================================
40 
41 namespace prv {
42 
BiffInputRecordBuffer(BinaryInputStream & rInStrm)43 BiffInputRecordBuffer::BiffInputRecordBuffer( BinaryInputStream& rInStrm ) :
44     mrInStrm( rInStrm ),
45     mpCurrentData( 0 ),
46     mnHeaderPos( -1 ),
47     mnBodyPos( 0 ),
48     mnBufferBodyPos( 0 ),
49     mnNextHeaderPos( 0 ),
50     mnRecId( BIFF_ID_UNKNOWN ),
51     mnRecSize( 0 ),
52     mnRecPos( 0 ),
53     mbValidHeader( false )
54 {
55     OSL_ENSURE( mrInStrm.isSeekable(), "BiffInputRecordBuffer::BiffInputRecordBuffer - stream must be seekable" );
56     mrInStrm.seekToStart();
57     maOriginalData.reserve( SAL_MAX_UINT16 );
58     maDecodedData.reserve( SAL_MAX_UINT16 );
59     enableDecoder( false );     // updates mpCurrentData
60 }
61 
restartAt(sal_Int64 nPos)62 void BiffInputRecordBuffer::restartAt( sal_Int64 nPos )
63 {
64     mnHeaderPos = -1;
65     mnBodyPos = mnBufferBodyPos = 0;
66     mnNextHeaderPos = nPos;
67     mnRecId = BIFF_ID_UNKNOWN;
68     mnRecSize = mnRecPos = 0;
69     mbValidHeader = false;
70 }
71 
setDecoder(const BiffDecoderRef & rxDecoder)72 void BiffInputRecordBuffer::setDecoder( const BiffDecoderRef& rxDecoder )
73 {
74     mxDecoder = rxDecoder;
75     enableDecoder( true );
76     updateDecoded();
77 }
78 
enableDecoder(bool bEnable)79 void BiffInputRecordBuffer::enableDecoder( bool bEnable )
80 {
81     mpCurrentData = (bEnable && mxDecoder.get() && mxDecoder->isValid()) ? &maDecodedData : &maOriginalData;
82 }
83 
startRecord(sal_Int64 nHeaderPos)84 bool BiffInputRecordBuffer::startRecord( sal_Int64 nHeaderPos )
85 {
86     mbValidHeader = (0 <= nHeaderPos) && (nHeaderPos + 4 <= mrInStrm.size());
87     if( mbValidHeader )
88     {
89         mnHeaderPos = nHeaderPos;
90         mrInStrm.seek( nHeaderPos );
91         mrInStrm >> mnRecId >> mnRecSize;
92         mnBodyPos = mrInStrm.tell();
93         mnNextHeaderPos = mnBodyPos + mnRecSize;
94         mbValidHeader = !mrInStrm.isEof() && (mnNextHeaderPos <= mrInStrm.size());
95     }
96     if( !mbValidHeader )
97     {
98         mnHeaderPos = mnBodyPos = -1;
99         mnNextHeaderPos = 0;
100         mnRecId = BIFF_ID_UNKNOWN;
101         mnRecSize = 0;
102     }
103     mnRecPos = 0;
104     return mbValidHeader;
105 }
106 
startNextRecord()107 bool BiffInputRecordBuffer::startNextRecord()
108 {
109     return startRecord( mnNextHeaderPos );
110 }
111 
getNextRecId()112 sal_uInt16 BiffInputRecordBuffer::getNextRecId()
113 {
114     sal_uInt16 nRecId = BIFF_ID_UNKNOWN;
115     if( mbValidHeader && (mnNextHeaderPos + 4 <= mrInStrm.size()) )
116     {
117         mrInStrm.seek( mnNextHeaderPos );
118         mrInStrm >> nRecId;
119     }
120     return nRecId;
121 }
122 
read(void * opData,sal_uInt16 nBytes)123 void BiffInputRecordBuffer::read( void* opData, sal_uInt16 nBytes )
124 {
125     updateBuffer();
126     OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::read - nothing to read" );
127     OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::read - buffer overflow" );
128     memcpy( opData, &(*mpCurrentData)[ mnRecPos ], nBytes );
129     mnRecPos = mnRecPos + nBytes;
130 }
131 
skip(sal_uInt16 nBytes)132 void BiffInputRecordBuffer::skip( sal_uInt16 nBytes )
133 {
134     OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::skip - nothing to skip" );
135     OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::skip - buffer overflow" );
136     mnRecPos = mnRecPos + nBytes;
137 }
138 
updateBuffer()139 void BiffInputRecordBuffer::updateBuffer()
140 {
141     OSL_ENSURE( mbValidHeader, "BiffInputRecordBuffer::updateBuffer - invalid access" );
142     if( mnBodyPos != mnBufferBodyPos )
143     {
144         mrInStrm.seek( mnBodyPos );
145         maOriginalData.resize( mnRecSize );
146         if( mnRecSize > 0 )
147             mrInStrm.readMemory( &maOriginalData.front(), static_cast< sal_Int32 >( mnRecSize ) );
148         mnBufferBodyPos = mnBodyPos;
149         updateDecoded();
150     }
151 }
152 
updateDecoded()153 void BiffInputRecordBuffer::updateDecoded()
154 {
155     if( mxDecoder.get() && mxDecoder->isValid() )
156     {
157         maDecodedData.resize( mnRecSize );
158         if( mnRecSize > 0 )
159             mxDecoder->decode( &maDecodedData.front(), &maOriginalData.front(), mnBodyPos, mnRecSize );
160     }
161 }
162 
163 } // namespace prv
164 
165 // ============================================================================
166 
BiffInputStream(BinaryInputStream & rInStream,bool bContLookup)167 BiffInputStream::BiffInputStream( BinaryInputStream& rInStream, bool bContLookup ) :
168     BinaryStreamBase( true ),
169     maRecBuffer( rInStream ),
170     mnRecHandle( -1 ),
171     mnRecId( BIFF_ID_UNKNOWN ),
172     mnAltContId( BIFF_ID_UNKNOWN ),
173     mnCurrRecSize( 0 ),
174     mnComplRecSize( 0 ),
175     mbHasComplRec( false ),
176     mbCont( bContLookup )
177 {
178     mbEof = true;   // EOF will be true if stream is not inside a record
179 }
180 
181 // record control -------------------------------------------------------------
182 
startNextRecord()183 bool BiffInputStream::startNextRecord()
184 {
185     bool bValidRec = false;
186     /*  #i4266# ignore zero records (id==len==0) (e.g. the application
187         "Crystal Report" writes zero records between other records) */
188     bool bIsZeroRec = false;
189     do
190     {
191         // record header is never encrypted
192         maRecBuffer.enableDecoder( false );
193         // read header of next raw record, returns false at end of stream
194         bValidRec = maRecBuffer.startNextRecord();
195         // ignore record, if identifier and size are zero
196         bIsZeroRec = (maRecBuffer.getRecId() == 0) && (maRecBuffer.getRecSize() == 0);
197     }
198     while( bValidRec && ((mbCont && isContinueId( maRecBuffer.getRecId() )) || bIsZeroRec) );
199 
200     // setup other class members
201     setupRecord();
202     return isInRecord();
203 }
204 
startRecordByHandle(sal_Int64 nRecHandle)205 bool BiffInputStream::startRecordByHandle( sal_Int64 nRecHandle )
206 {
207     rewindToRecord( nRecHandle );
208     return startNextRecord();
209 }
210 
resetRecord(bool bContLookup,sal_uInt16 nAltContId)211 void BiffInputStream::resetRecord( bool bContLookup, sal_uInt16 nAltContId )
212 {
213     if( isInRecord() )
214     {
215         mbCont = bContLookup;
216         mnAltContId = nAltContId;
217         restartRecord( true );
218         maRecBuffer.enableDecoder( true );
219     }
220 }
221 
rewindRecord()222 void BiffInputStream::rewindRecord()
223 {
224     rewindToRecord( mnRecHandle );
225 }
226 
227 // decoder --------------------------------------------------------------------
228 
setDecoder(const BiffDecoderRef & rxDecoder)229 void BiffInputStream::setDecoder( const BiffDecoderRef& rxDecoder )
230 {
231     maRecBuffer.setDecoder( rxDecoder );
232 }
233 
enableDecoder(bool bEnable)234 void BiffInputStream::enableDecoder( bool bEnable )
235 {
236     maRecBuffer.enableDecoder( bEnable );
237 }
238 
239 // stream/record state and info -----------------------------------------------
240 
getNextRecId()241 sal_uInt16 BiffInputStream::getNextRecId()
242 {
243     sal_uInt16 nRecId = BIFF_ID_UNKNOWN;
244     if( isInRecord() )
245     {
246         sal_Int64 nCurrPos = tell();            // save current position in record
247         while( jumpToNextContinue() ) {}        // skip following CONTINUE records
248         if( maRecBuffer.startNextRecord() )     // read header of next record
249             nRecId = maRecBuffer.getRecId();
250         seek( nCurrPos );                       // restore position, seek() resets old mbValid state
251     }
252     return nRecId;
253 }
254 
255 // BinaryStreamBase interface (seeking) ---------------------------------------
256 
size() const257 sal_Int64 BiffInputStream::size() const
258 {
259     if( !mbHasComplRec )
260         const_cast< BiffInputStream* >( this )->calcRecordLength();
261     return mnComplRecSize;
262 }
263 
tell() const264 sal_Int64 BiffInputStream::tell() const
265 {
266     return mbEof ? -1 : (mnCurrRecSize - maRecBuffer.getRecLeft());
267 }
268 
seek(sal_Int64 nRecPos)269 void BiffInputStream::seek( sal_Int64 nRecPos )
270 {
271     if( isInRecord() )
272     {
273         if( mbEof || (nRecPos < tell()) )
274             restartRecord( false );
275         if( !mbEof && (nRecPos > tell()) )
276             skip( static_cast< sal_Int32 >( nRecPos - tell() ) );
277     }
278 }
279 
close()280 void BiffInputStream::close()
281 {
282 }
283 
tellBase() const284 sal_Int64 BiffInputStream::tellBase() const
285 {
286     return maRecBuffer.getBaseStream().tell();
287 }
288 
sizeBase() const289 sal_Int64 BiffInputStream::sizeBase() const
290 {
291     return maRecBuffer.getBaseStream().size();
292 }
293 
294 // BinaryInputStream interface (stream read access) ---------------------------
295 
readData(StreamDataSequence & orData,sal_Int32 nBytes,size_t nAtomSize)296 sal_Int32 BiffInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes, size_t nAtomSize )
297 {
298     sal_Int32 nRet = 0;
299     if( !mbEof )
300     {
301         orData.realloc( ::std::max< sal_Int32 >( nBytes, 0 ) );
302         if( nBytes > 0 )
303             nRet = readMemory( orData.getArray(), nBytes, nAtomSize );
304     }
305     return nRet;
306 }
307 
readMemory(void * opMem,sal_Int32 nBytes,size_t nAtomSize)308 sal_Int32 BiffInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t nAtomSize )
309 {
310     sal_Int32 nRet = 0;
311     if( !mbEof && opMem && (nBytes > 0) )
312     {
313         sal_uInt8* pnBuffer = reinterpret_cast< sal_uInt8* >( opMem );
314         sal_Int32 nBytesLeft = nBytes;
315 
316         while( !mbEof && (nBytesLeft > 0) )
317         {
318             sal_uInt16 nReadSize = getMaxRawReadSize( nBytesLeft, nAtomSize );
319             // check nReadSize, stream may already be located at end of a raw record
320             if( nReadSize > 0 )
321             {
322                 maRecBuffer.read( pnBuffer, nReadSize );
323                 nRet += nReadSize;
324                 pnBuffer += nReadSize;
325                 nBytesLeft -= nReadSize;
326             }
327             if( nBytesLeft > 0 )
328                 jumpToNextContinue();
329             OSL_ENSURE( !mbEof, "BiffInputStream::readMemory - record overread" );
330         }
331     }
332     return nRet;
333 }
334 
skip(sal_Int32 nBytes,size_t nAtomSize)335 void BiffInputStream::skip( sal_Int32 nBytes, size_t nAtomSize )
336 {
337     sal_Int32 nBytesLeft = nBytes;
338     while( !mbEof && (nBytesLeft > 0) )
339     {
340         sal_uInt16 nSkipSize = getMaxRawReadSize( nBytesLeft, nAtomSize );
341         // check nSkipSize, stream may already be located at end of a raw record
342         if( nSkipSize > 0 )
343         {
344             maRecBuffer.skip( nSkipSize );
345             nBytesLeft -= nSkipSize;
346         }
347         if( nBytesLeft > 0 )
348             jumpToNextContinue();
349         OSL_ENSURE( !mbEof, "BiffInputStream::skip - record overread" );
350     }
351 }
352 
353 // byte strings ---------------------------------------------------------------
354 
readByteString(bool b16BitLen,bool bAllowNulChars)355 OString BiffInputStream::readByteString( bool b16BitLen, bool bAllowNulChars )
356 {
357     sal_Int32 nStrLen = b16BitLen ? readuInt16() : readuInt8();
358     return readCharArray( nStrLen, bAllowNulChars );
359 }
360 
readByteStringUC(bool b16BitLen,rtl_TextEncoding eTextEnc,bool bAllowNulChars)361 OUString BiffInputStream::readByteStringUC( bool b16BitLen, rtl_TextEncoding eTextEnc, bool bAllowNulChars )
362 {
363     return OStringToOUString( readByteString( b16BitLen, bAllowNulChars ), eTextEnc );
364 }
365 
skipByteString(bool b16BitLen)366 void BiffInputStream::skipByteString( bool b16BitLen )
367 {
368     skip( b16BitLen ? readuInt16() : readuInt8() );
369 }
370 
371 // Unicode strings ------------------------------------------------------------
372 
readUniStringChars(sal_uInt16 nChars,bool b16BitChars,bool bAllowNulChars)373 OUString BiffInputStream::readUniStringChars( sal_uInt16 nChars, bool b16BitChars, bool bAllowNulChars )
374 {
375     OUStringBuffer aBuffer;
376     aBuffer.ensureCapacity( nChars );
377 
378     /*  This function has to react on CONTINUE records which repeat the flags
379         field in their first byte and may change the 8bit/16bit character mode,
380         thus a plain call to readCompressedUnicodeArray() cannot be used here. */
381     sal_Int32 nCharsLeft = nChars;
382     while( !mbEof && (nCharsLeft > 0) )
383     {
384         /*  Read the character array from the remaining part of the current raw
385             record. First, calculate the maximum number of characters that can
386             be read without triggering to start a following CONTINUE record. */
387         sal_Int32 nRawChars = b16BitChars ? (getMaxRawReadSize( nCharsLeft * 2, 2 ) / 2) : getMaxRawReadSize( nCharsLeft, 1 );
388         aBuffer.append( readCompressedUnicodeArray( nRawChars, !b16BitChars, bAllowNulChars ) );
389 
390         /*  Prepare for next CONTINUE record. Calling jumpToNextStringContinue()
391             reads the leading byte in the following CONTINUE record and updates
392             the b16BitChars flag. */
393         nCharsLeft -= nRawChars;
394         if( nCharsLeft > 0 )
395             jumpToNextStringContinue( b16BitChars );
396     }
397 
398     return aBuffer.makeStringAndClear();
399 }
400 
readUniStringBody(sal_uInt16 nChars,bool bAllowNulChars)401 OUString BiffInputStream::readUniStringBody( sal_uInt16 nChars, bool bAllowNulChars )
402 {
403     bool b16BitChars;
404     sal_Int32 nAddSize;
405     readUniStringHeader( b16BitChars, nAddSize );
406     OUString aString = readUniStringChars( nChars, b16BitChars, bAllowNulChars );
407     skip( nAddSize );
408     return aString;
409 }
410 
readUniString(bool bAllowNulChars)411 OUString BiffInputStream::readUniString( bool bAllowNulChars )
412 {
413     return readUniStringBody( readuInt16(), bAllowNulChars );
414 }
415 
skipUniStringChars(sal_uInt16 nChars,bool b16BitChars)416 void BiffInputStream::skipUniStringChars( sal_uInt16 nChars, bool b16BitChars )
417 {
418     sal_Int32 nCharsLeft = nChars;
419     while( !mbEof && (nCharsLeft > 0) )
420     {
421         // skip the character array
422         sal_Int32 nSkipSize = b16BitChars ? getMaxRawReadSize( 2 * nCharsLeft, 2 ) : getMaxRawReadSize( nCharsLeft, 1 );
423         skip( nSkipSize );
424 
425         // prepare for next CONTINUE record
426         nCharsLeft -= (b16BitChars ? (nSkipSize / 2) : nSkipSize);
427         if( nCharsLeft > 0 )
428             jumpToNextStringContinue( b16BitChars );
429     }
430 }
431 
skipUniStringBody(sal_uInt16 nChars)432 void BiffInputStream::skipUniStringBody( sal_uInt16 nChars )
433 {
434     bool b16BitChars;
435     sal_Int32 nAddSize;
436     readUniStringHeader( b16BitChars, nAddSize );
437     skipUniStringChars( nChars, b16BitChars );
438     skip( nAddSize );
439 }
440 
skipUniString()441 void BiffInputStream::skipUniString()
442 {
443     skipUniStringBody( readuInt16() );
444 }
445 
446 // private --------------------------------------------------------------------
447 
setupRecord()448 void BiffInputStream::setupRecord()
449 {
450     // initialize class members
451     mnRecHandle = maRecBuffer.getRecHeaderPos();
452     mnRecId = maRecBuffer.getRecId();
453     mnAltContId = BIFF_ID_UNKNOWN;
454     mnCurrRecSize = mnComplRecSize = maRecBuffer.getRecSize();
455     mbHasComplRec = !mbCont;
456     mbEof = !isInRecord();
457     // enable decoder in new record
458     enableDecoder( true );
459 }
460 
restartRecord(bool bInvalidateRecSize)461 void BiffInputStream::restartRecord( bool bInvalidateRecSize )
462 {
463     if( isInRecord() )
464     {
465         maRecBuffer.startRecord( getRecHandle() );
466         mnCurrRecSize = maRecBuffer.getRecSize();
467         if( bInvalidateRecSize )
468         {
469             mnComplRecSize = mnCurrRecSize;
470             mbHasComplRec = !mbCont;
471         }
472         mbEof = false;
473     }
474 }
475 
rewindToRecord(sal_Int64 nRecHandle)476 void BiffInputStream::rewindToRecord( sal_Int64 nRecHandle )
477 {
478     if( nRecHandle >= 0 )
479     {
480         maRecBuffer.restartAt( nRecHandle );
481         mnRecHandle = -1;
482         mbEof = true;   // as long as the record is not started
483     }
484 }
485 
isContinueId(sal_uInt16 nRecId) const486 bool BiffInputStream::isContinueId( sal_uInt16 nRecId ) const
487 {
488     return (nRecId == BIFF_ID_CONT) || (nRecId == mnAltContId);
489 }
490 
jumpToNextContinue()491 bool BiffInputStream::jumpToNextContinue()
492 {
493     mbEof = mbEof || !mbCont || !isContinueId( maRecBuffer.getNextRecId() ) || !maRecBuffer.startNextRecord();
494     if( !mbEof )
495         mnCurrRecSize += maRecBuffer.getRecSize();
496     return !mbEof;
497 }
498 
jumpToNextStringContinue(bool & rb16BitChars)499 bool BiffInputStream::jumpToNextStringContinue( bool& rb16BitChars )
500 {
501     OSL_ENSURE( maRecBuffer.getRecLeft() == 0, "BiffInputStream::jumpToNextStringContinue - alignment error" );
502 
503     if( mbCont && (getRemaining() > 0) )
504     {
505         jumpToNextContinue();
506     }
507     else if( mnRecId == BIFF_ID_CONT )
508     {
509         /*  CONTINUE handling is off, but we have started reading in a CONTINUE
510             record -> start next CONTINUE for TXO import. We really start a new
511             record here - no chance to return to string origin. */
512         mbEof = mbEof || (maRecBuffer.getNextRecId() != BIFF_ID_CONT) || !maRecBuffer.startNextRecord();
513         if( !mbEof )
514             setupRecord();
515     }
516 
517     // trying to read the flags invalidates stream, if no CONTINUE record has been found
518     sal_uInt8 nFlags;
519     readValue( nFlags );
520     rb16BitChars = getFlag( nFlags, BIFF_STRF_16BIT );
521     return !mbEof;
522 }
523 
calcRecordLength()524 void BiffInputStream::calcRecordLength()
525 {
526     sal_Int64 nCurrPos = tell();            // save current position in record
527     while( jumpToNextContinue() ) {}        // jumpToNextContinue() adds up mnCurrRecSize
528     mnComplRecSize = mnCurrRecSize;
529     mbHasComplRec = true;
530     seek( nCurrPos );                       // restore position, seek() resets old mbValid state
531 }
532 
getMaxRawReadSize(sal_Int32 nBytes,size_t nAtomSize) const533 sal_uInt16 BiffInputStream::getMaxRawReadSize( sal_Int32 nBytes, size_t nAtomSize ) const
534 {
535     sal_uInt16 nMaxSize = getLimitedValue< sal_uInt16, sal_Int32 >( nBytes, 0, maRecBuffer.getRecLeft() );
536     if( (0 < nMaxSize) && (nMaxSize < nBytes) && (nAtomSize > 1) )
537     {
538         // check that remaining data in record buffer is a multiple of the passed atom size
539         sal_uInt16 nPadding = static_cast< sal_uInt16 >( nMaxSize % nAtomSize );
540         OSL_ENSURE( nPadding == 0, "BiffInputStream::getMaxRawReadSize - alignment error" );
541         nMaxSize = nMaxSize - nPadding;
542     }
543     return nMaxSize;
544 }
545 
readUniStringHeader(bool & orb16BitChars,sal_Int32 & ornAddSize)546 void BiffInputStream::readUniStringHeader( bool& orb16BitChars, sal_Int32& ornAddSize )
547 {
548     sal_uInt8 nFlags = readuInt8();
549     OSL_ENSURE( !getFlag( nFlags, BIFF_STRF_UNKNOWN ), "BiffInputStream::readUniStringHeader - unknown flags" );
550     orb16BitChars = getFlag( nFlags, BIFF_STRF_16BIT );
551     sal_uInt16 nFontCount = getFlag( nFlags, BIFF_STRF_RICH ) ? readuInt16() : 0;
552     sal_Int32 nPhoneticSize = getFlag( nFlags, BIFF_STRF_PHONETIC ) ? readInt32() : 0;
553     ornAddSize = 4 * nFontCount + ::std::max< sal_Int32 >( 0, nPhoneticSize );
554 }
555 
556 // ============================================================================
557 
BiffInputStreamPos(BiffInputStream & rStrm)558 BiffInputStreamPos::BiffInputStreamPos( BiffInputStream& rStrm ) :
559     mrStrm( rStrm ),
560     mnRecHandle( rStrm.getRecHandle() ),
561     mnRecPos( rStrm.tell() )
562 {
563 }
564 
restorePosition()565 bool BiffInputStreamPos::restorePosition()
566 {
567     bool bValidRec = mrStrm.startRecordByHandle( mnRecHandle );
568     if( bValidRec )
569         mrStrm.seek( mnRecPos );
570     return bValidRec && !mrStrm.isEof();
571 }
572 
573 // ============================================================================
574 
BiffInputStreamPosGuard(BiffInputStream & rStrm)575 BiffInputStreamPosGuard::BiffInputStreamPosGuard( BiffInputStream& rStrm ) :
576     BiffInputStreamPos( rStrm )
577 {
578 }
579 
~BiffInputStreamPosGuard()580 BiffInputStreamPosGuard::~BiffInputStreamPosGuard()
581 {
582     restorePosition();
583 }
584 
585 // ============================================================================
586 
587 } // namespace xls
588 } // namespace oox
589