bump product version to 5.0.4.1
[LibreOffice.git] / svl / source / filerec / filerec.cxx
blob172f61f1bfc4e5a297afdf69f9c5d7f2b633b368
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <sal/log.hxx>
23 #include <svl/filerec.hxx>
24 #include <osl/endian.h>
27 /* The following macros extract parts from a sal_uInt32 value.
28 These sal_uInt32 values are written out instead of the individual
29 values to reduce the number of calls.
32 #define SFX_REC_PRE(n) ( ((n) & 0x000000FF) )
33 #define SFX_REC_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
34 #define SFX_REC_TYP(n) ( ((n) & 0x000000FF) )
35 #define SFX_REC_VER(n) ( ((n) & 0x0000FF00) >> 8 )
36 #define SFX_REC_TAG(n) ( ((n) & 0xFFFF0000) >> 16 )
38 #define SFX_REC_CONTENT_VER(n) ( ((n) & 0x000000FF) )
39 #define SFX_REC_CONTENT_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
42 /* The following macros combine parts to a sal_uInt32 value.
43 This sal_uInt32 value is written instead of the individual values
44 to reduce the number of calls.
47 static void lclWriteMiniHeader(SvStream *p, sal_uInt32 nPreTag, sal_uInt32 nStartPos, sal_uInt32 nEndPos)
49 (*p).WriteUInt32( sal_uInt32(nPreTag) |
50 sal_uInt32(nEndPos-nStartPos-SFX_REC_HEADERSIZE_MINI) << 8 );
53 static void lclWriteHeader(SvStream *p, sal_uInt32 nRecType, sal_uInt32 nContentTag, sal_uInt32 nContentVer)
55 (*p).WriteUInt32( sal_uInt32(nRecType) |
56 ( sal_uInt32(nContentVer) << 8 ) |
57 ( sal_uInt32(nContentTag) << 16 ) );
60 #define SFX_REC_CONTENT_HEADER(nContentVer,n1StStartPos,nCurStartPos) \
61 ( sal_uInt32(nContentVer) | \
62 sal_uInt32( nCurStartPos - n1StStartPos ) << 8 )
64 /** Close the record; write the header
66 * @param bSeekToEndOfRec
67 * if true (default) the stream is positioned at the end of the record;
68 * if false the stream at the start of the content (so after the header).
70 * This method closes the record. The main function is to write the header.
71 * If the header was written already this method is a no-op.
73 * @return sal_uInt32 != 0: Position im the stream immediately after the record.
74 * If 'bSeekToEndOfRecord==sal_True' this will be equal to the current stream position.
75 * == 0: The header was already written.
77 sal_uInt32 SfxMiniRecordWriter::Close(bool bSeekToEndOfRec)
79 // The header wasn't written yet?
80 if ( !_bHeaderOk )
82 // Write header at the start of the record
83 sal_uInt32 nEndPos = _pStream->Tell();
84 _pStream->Seek( _nStartPos );
85 lclWriteMiniHeader(_pStream, _nPreTag, _nStartPos, nEndPos );
87 // seek to the end of the record or stay where we are
88 if ( bSeekToEndOfRec )
89 _pStream->Seek( nEndPos );
91 // the header has been written NOW
92 _bHeaderOk = true;
93 return nEndPos;
96 // Record was closed already
97 return 0;
101 Internal method for belatedly processsing a header read externally.
102 If the header corresponds to an End-Of-Record tag, an error
103 code is set on the stream and sal_False is returned.
104 But the stream will not be reset to the record start in case of an error.
106 bool SfxMiniRecordReader::SetHeader_Impl( sal_uInt32 nHeader )
108 bool bRet = true;
110 // determine reord end and PreTag from the header
111 _nEofRec = _pStream->Tell() + SFX_REC_OFS(nHeader);
112 _nPreTag = sal::static_int_cast< sal_uInt8 >(SFX_REC_PRE(nHeader));
114 // Errror in cae of End of Record tag
115 if ( _nPreTag == SFX_REC_PRETAG_EOR )
117 _pStream->SetError( ERRCODE_IO_WRONGFORMAT );
118 bRet = true;
120 return bRet;
125 * @param pstream
126 * an \a SvStream, which has an \a SfxMiniRecord at the current position
127 * @param nTag
128 * Pre-Tag of the wanted record
130 * This constructor interprets a 'pStream' from the current position
131 * as a continuous sequence of records that should be parsable by
132 * this group of classes. The first record that is an <SfxMiniRecord>
133 * (possibly an extened-Record> that has the PreTag 'nTag' will be opened
134 * and represented by this instance.
136 * If the end of stream is reached or a record with tag
137 * SFX_REC_PRETAG_EOR is seen before a record with the wanted 'nTag'
138 * tag is found, the created instance is invalid ('IsValid() ==
139 * sal_False'). The ERRCODE_IO_WRONGFORMAT error code will be set on
140 * the stream,and the current position will be unchanged.
142 * If (the wanted tag) 'nTag==SFX_FILEREC_PRETAG_EOR' no attempt is
143 * made to read a record, but 'IsValid()' is set to sal_False immediately.
144 * This gives the possibility to include backward compatible SfxMiniRecords
145 * without 'new' or 'delete'. See <SfxItemSet::Load()>.
147 * Suggested usage:
149 * This constructor allows for adding new record types in a backward
150 * compatible way by writing out a record with a new tag followed
151 * by one with an old tag. In that case previous versions of the program
152 * that do not recognise the new tag will skip the new record
153 * automatically. This does cause a slight run time inefficiency,
154 * compared just starting reading, but if the first record
155 * is the wanted one the difference is just a comparison of 2 bytes.
158 SfxMiniRecordReader::SfxMiniRecordReader(SvStream* pStream, sal_uInt8 nTag)
159 : _pStream(pStream)
160 , _nEofRec(0)
161 , _bSkipped(nTag == SFX_REC_PRETAG_EOR)
163 // ignore if we are looking for SFX_REC_PRETAG_EOR
164 if ( _bSkipped )
166 _nPreTag = nTag;
167 return;
170 // remember StartPos to be able to seek back in case of error
171 sal_uInt32 nStartPos = pStream->Tell();
173 // look for the matching record
174 while(true)
176 // read header
177 SAL_INFO("svl", "SfxFileRec: searching record at " << pStream->Tell());
178 sal_uInt32 nHeader;
179 pStream->ReadUInt32( nHeader );
181 // let the base class extract the header data
182 SetHeader_Impl( nHeader );
184 // handle error, if any
185 if ( pStream->IsEof() )
186 _nPreTag = SFX_REC_PRETAG_EOR;
187 else if ( _nPreTag == SFX_REC_PRETAG_EOR )
188 pStream->SetError( ERRCODE_IO_WRONGFORMAT );
189 else
191 // stop the loop if the right tag is found
192 if ( _nPreTag == nTag )
193 break;
195 // or else skip the record and continue
196 pStream->Seek( _nEofRec );
197 continue;
200 // seek back in case of error
201 pStream->Seek( nStartPos );
202 break;
208 * @param nRecordType for sub classes
209 * @param pStream stream to write the record to
210 * @param nContentTag record type
211 * @param nContentVer record version
213 * internal constructor for sub classes
215 SfxSingleRecordWriter::SfxSingleRecordWriter(sal_uInt8 nRecordType,
216 SvStream* pStream,
217 sal_uInt16 nContentTag,
218 sal_uInt8 nContentVer)
219 : SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT )
221 // write extend header after the SfxMiniRec
222 lclWriteHeader(pStream, nRecordType, nContentTag, nContentVer);
227 * Internal method for reading an SfxMultiRecord header, after
228 * the base class has been initialized and its header has been read.
229 * Set an error code on the stream if needed, but don't seek back
230 * in case of error.
232 inline bool SfxSingleRecordReader::ReadHeader_Impl( sal_uInt16 nTypes )
234 bool bRet;
236 // read header of the base class
237 sal_uInt32 nHeader=0;
238 _pStream->ReadUInt32( nHeader );
239 if ( !SetHeader_Impl( nHeader ) )
240 bRet = false;
241 else
243 // read own header
244 _pStream->ReadUInt32( nHeader );
245 _nRecordVer = sal::static_int_cast< sal_uInt8 >(SFX_REC_VER(nHeader));
246 _nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));
248 // wrong record type?
249 _nRecordType = sal::static_int_cast< sal_uInt8 >(SFX_REC_TYP(nHeader));
250 bRet = 0 != ( nTypes & _nRecordType);
252 return bRet;
258 * @param nTypes arithmetic OR of allowed record types
259 * @param nTag record tag to find
261 * Internal method for reading the header of the first record
262 * that has the tag 'nTag', for which then the type should be
263 * one of the types in 'nTypes'.
265 * If such a record is not found an error code is set, the stream
266 * position is seek-ed back and sal_False is returned.
268 bool SfxSingleRecordReader::FindHeader_Impl(sal_uInt16 nTypes, sal_uInt16 nTag)
270 // remember StartPos to be able to seek back in case of error
271 sal_uInt32 nStartPos = _pStream->Tell();
273 // look for the right record
274 while ( !_pStream->IsEof() )
276 // read header
277 sal_uInt32 nHeader;
278 SAL_INFO("svl", "SfxFileRec: searching record at " << _pStream->Tell());
279 _pStream->ReadUInt32( nHeader );
280 if ( !SetHeader_Impl( nHeader ) )
281 // EOR => abort loop
282 break;
284 // found extended record?
285 if ( _nPreTag == SFX_REC_PRETAG_EXT )
287 // read extended header
288 _pStream->ReadUInt32( nHeader );
289 _nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));
291 // found right record?
292 if ( _nRecordTag == nTag )
294 // record type matches as well?
295 _nRecordType = sal::static_int_cast< sal_uInt8 >(
296 SFX_REC_TYP(nHeader));
297 if ( nTypes & _nRecordType )
298 // ==> found it
299 return true;
301 // error => abort loop
302 break;
306 // else skip
307 if ( !_pStream->IsEof() )
308 _pStream->Seek( _nEofRec );
311 // set error and seek back
312 _pStream->SetError( ERRCODE_IO_WRONGFORMAT );
313 _pStream->Seek( nStartPos );
314 return false;
319 * @param nRecordType sub class record type
320 * @param pStream Stream to write the record to
321 * @param nContentTag Content type
322 * @param nContentVer Content version
324 * Internal method for sub classes
326 SfxMultiFixRecordWriter::SfxMultiFixRecordWriter(sal_uInt8 nRecordType,
327 SvStream* pStream,
328 sal_uInt16 nContentTag,
329 sal_uInt8 nContentVer)
330 : SfxSingleRecordWriter( nRecordType, pStream, nContentTag, nContentVer )
331 , _nContentStartPos(0)
332 , _nContentSize(0)
333 , _nContentCount(0)
335 // space for own header
336 pStream->SeekRel( + SFX_REC_HEADERSIZE_MULTI );
340 * @see SfxMiniRecordWriter
342 sal_uInt32 SfxMultiFixRecordWriter::Close( bool bSeekToEndOfRec )
344 // Header not written yet?
345 if ( !_bHeaderOk )
347 // remember position after header, to be able to seek back to it
348 sal_uInt32 nEndPos = SfxSingleRecordWriter::Close( false );
350 // write extended header after SfxSingleRecord
351 _pStream->WriteUInt16( _nContentCount );
352 _pStream->WriteUInt32( _nContentSize );
354 // seek to end of record or stay after the header
355 if ( bSeekToEndOfRec )
356 _pStream->Seek(nEndPos);
357 return nEndPos;
360 // Record was closed already
361 return 0;
366 * @param nRecordType Record type of the sub class
367 * @param pStream stream to write the record to
368 * @param nRecordTag record base type
369 * @param nRecordVer record base version
371 * Internal constructor for sub classes
373 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter(sal_uInt8 nRecordType,
374 SvStream* pStream,
375 sal_uInt16 nRecordTag,
376 sal_uInt8 nRecordVer)
377 : SfxMultiFixRecordWriter( nRecordType, pStream, nRecordTag, nRecordVer ),
378 _nContentVer( 0 )
384 * @param pStream, stream to write the record to
385 * @param nRecordTag record base type
386 * @param nRecordVer record base version
388 * Starts an SfxMultiVarRecord in \a pStream, for which the size
389 * of the content does not have to be known or identical;
390 * after streaming a record its size will be calculated.
392 * Note:
394 * This method is not inline since too much code was generated
395 * for initializing the <SvULong> members.
397 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter(SvStream* pStream,
398 sal_uInt16 nRecordTag,
399 sal_uInt8 nRecordVer)
400 : SfxMultiFixRecordWriter( SFX_REC_TYPE_VARSIZE,
401 pStream, nRecordTag, nRecordVer ),
402 _nContentVer( 0 )
409 * The destructor of class <SfxMultiVarRecordWriter> closes the
410 * record automatically, in case <SfxMultiVarRecordWriter::Close()>
411 * has not been called explicitly yet.
413 SfxMultiVarRecordWriter::~SfxMultiVarRecordWriter()
415 // close if the header has not been written yet
416 if ( !_bHeaderOk )
417 Close();
422 * Internal method for finishing individual content
424 void SfxMultiVarRecordWriter::FlushContent_Impl()
426 // record the version and position offset of the current content;
427 // the position offset is relative ot the start position of the
428 // first content.
429 assert(_aContentOfs.size() == static_cast<size_t>(_nContentCount)-1);
430 _aContentOfs.resize(_nContentCount-1);
431 _aContentOfs.push_back(
432 SFX_REC_CONTENT_HEADER(_nContentVer,_nStartPos,_nContentStartPos));
436 * @see SfxMultiFixRecordWriter
438 void SfxMultiVarRecordWriter::NewContent()
440 // written Content already?
441 if ( _nContentCount )
442 FlushContent_Impl();
444 // start new Content
445 _nContentStartPos = _pStream->Tell();
446 ++_nContentCount;
450 * @see SfxMiniRecordWriter
452 sal_uInt32 SfxMultiVarRecordWriter::Close( bool bSeekToEndOfRec )
454 // Header not written yet?
455 if ( !_bHeaderOk )
457 // finish content if needed
458 if ( _nContentCount )
459 FlushContent_Impl();
461 // write out content offset table
462 sal_uInt32 nContentOfsPos = _pStream->Tell();
463 //! (loop without braces)
464 for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
465 _pStream->WriteUInt32( _aContentOfs[n] );
467 // skip SfxMultiFixRecordWriter::Close()!
468 sal_uInt32 nEndPos = SfxSingleRecordWriter::Close( false );
470 // write own header
471 _pStream->WriteUInt16( _nContentCount );
472 if ( SFX_REC_TYPE_VARSIZE_RELOC == _nPreTag ||
473 SFX_REC_TYPE_MIXTAGS_RELOC == _nPreTag )
474 _pStream->WriteUInt32( static_cast<sal_uInt32>(nContentOfsPos - ( _pStream->Tell() + sizeof(sal_uInt32) )) );
475 else
476 _pStream->WriteUInt32( nContentOfsPos );
478 // seek to the end of the record or stay where we are
479 if ( bSeekToEndOfRec )
480 _pStream->Seek(nEndPos);
481 return nEndPos;
484 // Record was closed already
485 return 0;
490 * @param nContentTag tag for this content type
491 * @param nContentVer content version
493 * With this method new Content is added to a record and
494 * its tag and version are regorded. This method must be called
495 * to start each content, including the first record.
497 void SfxMultiMixRecordWriter::NewContent(sal_uInt16 nContentTag, sal_uInt8 nContentVer)
499 // Finish the previous record if necessary
500 if ( _nContentCount )
501 FlushContent_Impl();
503 // Write the content tag, and record the version and starting position
504 _nContentStartPos = _pStream->Tell();
505 ++_nContentCount;
506 _pStream->WriteUInt16( nContentTag );
507 _nContentVer = nContentVer;
512 * Internal method for reading an SfxMultiRecord-Headers, after
513 * the base class has been initialized and its header has been read.
514 * If an error occurs an error code is set on the stream, but
515 * the stream position will not be seek-ed back in that case.
517 bool SfxMultiRecordReader::ReadHeader_Impl()
519 // read own header
520 _pStream->ReadUInt16( _nContentCount );
521 _pStream->ReadUInt32( _nContentSize ); // Fix: each on its own, Var|Mix: table position
523 // do we still need to rade a table with Content offsets?
524 if ( _nRecordType != SFX_REC_TYPE_FIXSIZE )
526 // read table from the stream
527 sal_uInt32 nContentPos = _pStream->Tell();
528 if ( _nRecordType == SFX_REC_TYPE_VARSIZE_RELOC ||
529 _nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
530 _pStream->SeekRel( + _nContentSize );
531 else
532 _pStream->Seek( _nContentSize );
533 const size_t nMaxRecords = _pStream->remainingSize() / sizeof(sal_uInt32);
534 if (_nContentCount > nMaxRecords)
536 SAL_WARN("svl", "Parsing error: " << nMaxRecords << " max possible entries, but " <<
537 _nContentCount << " claimed, truncating");
538 _nContentCount = nMaxRecords;
540 _pContentOfs = new sal_uInt32[_nContentCount];
541 memset(_pContentOfs, 0, _nContentCount*sizeof(sal_uInt32));
542 #if defined(OSL_LITENDIAN)
543 _pStream->Read( _pContentOfs, sizeof(sal_uInt32)*_nContentCount );
544 #else
545 // (loop without braces)
546 for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
547 _pStream->ReadUInt32( _pContentOfs[n] );
548 #endif
549 _pStream->Seek( nContentPos );
552 // It was possible to read the error if no error is set on the stream
553 return !_pStream->GetError();
557 SfxMultiRecordReader::SfxMultiRecordReader( SvStream *pStream, sal_uInt16 nTag )
558 : _pContentOfs(0)
559 , _nContentSize(0)
560 , _nContentCount(0)
561 , _nContentNo(0)
562 , _nContentTag( 0 )
563 , _nContentVer( 0 )
565 // remember position in the stream to be able seek back in case of error
566 _nStartPos = pStream->Tell();
568 // look for matching record and initialize base class
569 SfxSingleRecordReader::Construct_Impl( pStream );
570 if ( SfxSingleRecordReader::FindHeader_Impl( SFX_REC_TYPE_FIXSIZE |
571 SFX_REC_TYPE_VARSIZE | SFX_REC_TYPE_VARSIZE_RELOC |
572 SFX_REC_TYPE_MIXTAGS | SFX_REC_TYPE_MIXTAGS_RELOC,
573 nTag ) )
575 // also read own header
576 if ( !ReadHeader_Impl() )
577 // not readable => mark as invalid and reset stream position
578 SetInvalid_Impl( _nStartPos);
583 SfxMultiRecordReader::~SfxMultiRecordReader()
585 delete[] _pContentOfs;
590 * Positions the stream at the start of the next Content, or
591 * for the first call at the start of the first Content in the record,
592 * and reads its header if necessary.
594 * @return sal_False if there is no further Content according to
595 * the record header. Even if sal_True is returned an error can
596 * be set on the stream, for instance if the record finished prematurely
597 * in a broken file.
599 bool SfxMultiRecordReader::GetContent()
601 // more Content available?
602 if ( _nContentNo < _nContentCount )
604 // position the stream at the start of the Content
605 sal_uInt32 nOffset = _nRecordType == SFX_REC_TYPE_FIXSIZE
606 ? _nContentNo * _nContentSize
607 : SFX_REC_CONTENT_OFS(_pContentOfs[_nContentNo]);
608 sal_uInt32 nNewPos = _nStartPos + nOffset;
609 DBG_ASSERT( nNewPos >= _pStream->Tell(), "SfxMultiRecordReader::GetContent() - New position before current, to much data red!" );
611 // #99366#: correct stream pos in every case;
612 // the if clause was added by MT a long time ago,
613 // maybe to 'repair' other corrupt documents; but this
614 // gives errors when writing with 5.1 and reading with current
615 // versions, so we decided to remove the if clause (KA-05/17/2002)
616 // if ( nNewPos > _pStream->Tell() )
617 _pStream->Seek( nNewPos );
619 // Read Content Header if available
620 if ( _nRecordType == SFX_REC_TYPE_MIXTAGS ||
621 _nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
623 _nContentVer = sal::static_int_cast< sal_uInt8 >(
624 SFX_REC_CONTENT_VER(_pContentOfs[_nContentNo]));
625 _pStream->ReadUInt16( _nContentTag );
628 // Increment ContentNo
629 ++_nContentNo;
630 return true;
633 return false;
637 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */