xref: /trunk/main/svl/source/filerec/filerec.cxx (revision 3a7cf181c55416e69e525ddc0b38c22235ec1569)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svl.hxx"
30 #include <svl/filerec.hxx>
31 #include <osl/endian.h>
32 
33 //========================================================================
34 
35 SV_IMPL_VARARR( SfxUINT32s, sal_uInt32 );
36 
37 //========================================================================
38 
39 /*  Die folgenden Makros extrahieren Teilbereiche aus einem sal_uInt32 Wert.
40     Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
41     um Calls zu sparen.
42 */
43 
44 #define SFX_REC_PRE(n) ( ((n) & 0x000000FF) )
45 #define SFX_REC_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
46 #define SFX_REC_TYP(n) ( ((n) & 0x000000FF) )
47 #define SFX_REC_VER(n) ( ((n) & 0x0000FF00) >> 8 )
48 #define SFX_REC_TAG(n) ( ((n) & 0xFFFF0000) >> 16 )
49 
50 #define SFX_REC_CONTENT_VER(n) ( ((n) & 0x000000FF) )
51 #define SFX_REC_CONTENT_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
52 
53 //-------------------------------------------------------------------------
54 
55 /*  Die folgenden Makros setzen Teilbereiche zu einem sal_uInt32 Wert zusammen.
56     Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
57     um Calls zu sparen.
58 */
59 
60 #define SFX_REC_MINI_HEADER(nPreTag,nStartPos,nEndPos) \
61                     ( sal_uInt32(nPreTag) | \
62                       sal_uInt32(nEndPos-nStartPos-SFX_REC_HEADERSIZE_MINI) << 8 )
63 
64 #define SFX_REC_HEADER(nRecType,nContentTag,nContentVer) \
65                     ( sal_uInt32(nRecType) | \
66                       ( sal_uInt32(nContentVer) << 8 ) | \
67                       ( sal_uInt32(nContentTag) << 16 ) )
68 
69 #define SFX_REC_CONTENT_HEADER(nContentVer,n1StStartPos,nCurStartPos) \
70                     ( sal_uInt32(nContentVer) | \
71                       sal_uInt32( nCurStartPos - n1StStartPos ) << 8 )
72 
73 //=========================================================================
74 
75 sal_uInt32 SfxMiniRecordWriter::Close
76 (
77     FASTBOOL    bSeekToEndOfRec     /*  sal_True (default)
78                                         Der Stream wird an das Ende des Records
79                                         positioniert.
80 
81                                         sal_False
82                                         Der Stream wird an den Anfang des
83                                         Contents (also hinter den Header)
84                                         positioniert.
85                                     */
86 )
87 
88 /*  [Beschreibung]
89 
90     Diese Methode schlie\st den Record. Dabei wird haupts"achlich der
91     Header geschrieben.
92 
93     Wurde der Header bereits geschrieben, hat der Aufruf keine Wirkung.
94 
95 
96     [R"uckgabewert]
97 
98     sal_uInt32      != 0
99                 Position im Stream, die direkt hinter dem Record liegt.
100                 'bSeekToEndOfRecord==sal_True'
101                 => R"uckgabewert == aktuelle Stream-Position nach Aufruf
102 
103                 == 0
104                 Der Header war bereits geschrieben worden.
105 */
106 
107 {
108     // wurde der Header noch nicht geschrieben?
109     if ( !_bHeaderOk )
110     {
111         // Header an den Anfang des Records schreiben
112         sal_uInt32 nEndPos = _pStream->Tell();
113         _pStream->Seek( _nStartPos );
114         *_pStream << SFX_REC_MINI_HEADER( _nPreTag, _nStartPos, nEndPos );
115 
116         // je nachdem ans Ende des Records seeken oder hinter Header bleiben
117         if ( bSeekToEndOfRec )
118             _pStream->Seek( nEndPos );
119 
120         // Header wurde JETZT geschrieben
121         _bHeaderOk = sal_True;
122         return nEndPos;
123     }
124 #ifdef DBG_UTIL
125     // mu\s Fix-Size-Record gepr"uft werden?
126     else if ( SFX_BOOL_DONTCARE == _bHeaderOk )
127     {
128         // Header auslesen, um Soll-Gr"o\se zu bestimmen
129         sal_uInt32 nEndPos = _pStream->Tell();
130         _pStream->Seek( _nStartPos );
131         sal_uInt32 nHeader;
132         *_pStream >> nHeader;
133         _pStream->Seek( nEndPos );
134 
135         // Soll-Gr"o\se mit Ist-Gr"o\se vergleichen
136         DBG_ASSERT( nEndPos - SFX_REC_OFS(nHeader) == _nStartPos + sizeof(sal_uInt32),
137                     "fixed record size incorrect" );
138         DbgOutf( "SfxFileRec: written record until %ul", nEndPos );
139     }
140 #endif
141 
142     // Record war bereits geschlossen
143     return 0;
144 }
145 
146 //=========================================================================
147 
148 sal_uInt16 SfxMiniRecordReader::ScanRecordType
149 (
150     SvStream*   pStream         /*  <SvStream> an dessen aktueller Position
151                                     ein Record liegt, dessen Typ erkannt werden
152                                     soll.
153                                 */
154 )
155 
156 /*  [Beschreibung]
157 
158     Mit dieser statischen Methode kann ermittelt werden, ob sich an der
159     aktuellen Position in einem Stream ein Record befindet, und der Typ
160     des Records kann ermittelt werden.
161 
162     Die Position im Stream ist nach dem Aufruf aufver"andert.
163 
164 
165     [Anmerkung]
166 
167     Die Record-Typen k"onnen zwar (abgesehen vom Drawing-Enginge-Record)
168     untereinander eindeutig erkannt werden, es besteht jedoch die Gefahr
169     der Verwechslung von Records mit normalen Daten. File-Formate sollten
170     darauf R"ucksicht nehmen. Handelt es sich um keinen Record, wird
171     am wahrscheinlichsten SFX_REC_TYPE_MINI zur"uckgeliefert, da dieser
172     Typ sich aufgrund seines sparsam kurzen Headers durch die k"urzeste
173     Kennung auszeichnet.
174 
175 
176     [R"uckgabewert]
177 
178     sal_uInt16                          SFX_REC_TYPE_EOR
179                                     An der aktuellen Position des Streams
180                                     steht eine End-Of-Records-Kennung.
181 
182                                     SFX_REC_TYPE_MINI
183                                     Es handelt sich um einen SW3 kompatiblen
184                                     Mini-Record, dessen einzige Kennung sein
185                                     'Mini-Tag' ist.
186 
187                                     SFX_REC_TYPE_SINGLE
188                                     Es handelt sich um einen Extended-Record
189                                     mit einem einzigen Content, der durch eine
190                                     Version und ein Tag n"aher gekennzeichnet
191                                     ist.
192 
193                                     SFX_REC_TYPE_FIXSIZE
194                                     Es handelt sich um einen Extended-Record
195                                     mit mehreren Contents gleicher Gr"o\se,
196                                     die gemeinsam durch eine einzige Version
197                                     und ein einziges gemeinsames Tag n"aher
198                                     gekennzeichnet sind.
199 
200                                     SFX_REC_TYPE_VARSIZE
201                                     Es handelt sich um einen Extended-Record
202                                     mit mehreren Contents variabler Gr"o\se,
203                                     die gemeinsam durch eine einzige Version
204                                     und ein einziges gemeinsames Tag n"aher
205                                     gekennzeichnet sind.
206 
207                                     SFX_REC_TYPE_MIXTAGS
208                                     Es handelt sich um einen Extended-Record
209                                     mit mehreren Contents variabler Gr"o\se,
210                                     die jeweils durch ein eignes Tag und
211                                     eine eigene Versions-Nummer n"aher
212                                     gekennzeichnet sind.
213 
214                                     SFX_REC_TYPE_DRAWENG
215                                     Es handelt sich wahrscheinlich um einen
216                                     Drawing-Engine-Record. Dieser Record-Typ
217                                     kann von den Klassen dieser Gruppe nicht
218                                     interpretiert werden.
219 */
220 
221 {
222     // die ersten 4 Bytes als Mini-Header lesen
223     sal_uInt32 nHeader;
224     *pStream >> nHeader;
225 
226     // k"onnte es sich um einen extended-Record handeln?
227     sal_uInt16 nPreTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_PRE(nHeader));
228     if ( SFX_REC_PRETAG_EXT == nPreTag )
229     {
230         // die n"achsten 4 Bytes als extended-Header lesen
231         *pStream >> nHeader;
232 
233         // Stream-Position restaurieren
234         pStream->SeekRel(-8);
235 
236         // liegt eine g"ultige Record-Kennung vor?
237         sal_uInt16 nType = sal::static_int_cast< sal_uInt16 >(SFX_REC_TYP(nHeader));
238         if ( nType >= SFX_REC_TYPE_FIRST && nType <= SFX_REC_TYPE_LAST )
239             // entsprechenden extended-Record-Typ zur"uckliefern
240             return nType;
241 
242         // sonst ist der Record-Typ unbekannt
243         return SFX_REC_TYPE_NONE;
244     }
245 
246     // Stream-Position restaurieren
247     pStream->SeekRel(-4);
248 
249     // liegt eine End-Of-Record-Kennung vor?
250     if ( SFX_REC_PRETAG_EOR == nPreTag )
251         return nPreTag;
252 
253     // liegt ein Drawin-Engine-Record vor?
254     if ( nHeader == sal_uInt32(*"DRMD") || nHeader == sal_uInt32(*"DRVW") )
255         return SFX_REC_TYPE_DRAWENG;
256 
257     // alle anderen sind grunds"atzlich g"ultige Mini-Records
258     return SFX_REC_TYPE_MINI;
259 }
260 
261 //-------------------------------------------------------------------------
262 
263 FASTBOOL SfxMiniRecordReader::SetHeader_Impl( sal_uInt32 nHeader )
264 
265 /*  [Beschreibung]
266 
267     Interne Methode zum nachtr"aglichen Verarbeiten eines extern gelesenen
268     Headers. Falls der Header eine End-Of-Records-Kennung darstellt,
269     wird am Stream ein Errorcode gesetzt und sal_False zur"uckgeliefert. Im
270     Fehlerfall wird der Stream jedoch nicht auf den Record-Anfang zur"uck-
271     gesetzt.
272 */
273 
274 {
275     FASTBOOL bRet = sal_True;
276 
277     // Record-Ende und Pre-Tag aus dem Header ermitteln
278     _nEofRec = _pStream->Tell() + SFX_REC_OFS(nHeader);
279     _nPreTag = sal::static_int_cast< sal_uInt8 >(SFX_REC_PRE(nHeader));
280 
281     // wenn End-Of-Record-Kennung, dann Fehler
282     if ( _nPreTag == SFX_REC_PRETAG_EOR )
283     {
284         _pStream->SetError( ERRCODE_IO_WRONGFORMAT );
285         bRet = sal_False;
286     }
287     return bRet;
288 }
289 
290 //-------------------------------------------------------------------------
291 
292 SfxMiniRecordReader::SfxMiniRecordReader
293 (
294     SvStream*       pStream         /*  <SvStream>, an dessen aktueller
295                                         Position sich ein <SfxMiniRecord>
296                                         befindet.
297                                     */
298 )
299 
300 /*  [Beschreibung]
301 
302     Dieser Ctor liest den Header eines <SfxMiniRecord> ab der aktuellen
303     Position von 'pStream'. Da grunds"atzlich fast 4-Byte Kombination ein
304     g"ultiger SfxMiniRecord-Header ist, bleiben die einzig m"oglichen
305     Fehler der EOF-Status des Streams, und ein SFX_REC_PRETAG_EOR
306     als Pre-Tag. Ein entsprechender Error-Code (ERRCODE_IO_EOF bzw.
307     ERRCODE_IO_WRONGFORMAT) ist dann am Stream gesetzt, dessen Position
308     dann au\serdem unver"andert ist.
309 */
310 
311 :   _pStream( pStream ),
312     _bSkipped( sal_False )
313 {
314     // Header einlesen
315     sal_uInt32 nStartPos = pStream->Tell(); // um im Fehlerfall zur"uck zu-seeken
316     DBG( DbgOutf( "SfxFileRec: reading record at %ul", nStartPos ) );
317     sal_uInt32 nHeader;
318     *pStream >> nHeader;
319 
320     // Headerdaten extrahieren
321     SetHeader_Impl( nHeader );
322 
323     // Fehlerbehandlung
324     if ( pStream->IsEof() )
325         _nPreTag = SFX_REC_PRETAG_EOR;
326     else if ( _nPreTag == SFX_REC_PRETAG_EOR )
327         pStream->SetError( ERRCODE_IO_WRONGFORMAT );
328     if ( !IsValid() )
329         pStream->Seek( nStartPos );
330 }
331 
332 //-------------------------------------------------------------------------
333 
334 SfxMiniRecordReader::SfxMiniRecordReader
335 (
336     SvStream*       pStream,        /*  <SvStream>, an dessen aktueller
337                                         Position sich ein <SfxMiniRecord>
338                                         befindet.
339                                     */
340     sal_uInt8           nTag            //  Pre-Tag des gew"unschten Records
341 )
342 
343 /*  [Beschreibung]
344 
345     Dieser Ctor interpretiert 'pStream' ab der aktuellen Position als
346     eine l"uckenlose Folge von, von dieser Klassen-Gruppe interpretierbaren,
347     Records. Der in dieser Folge erste als <SfxMiniRecord> interpretierbare
348     (also ggf. auch ein extended-Record) mit dem PreTag 'nTag' wird ge"offnet
349     und durch diese Instanz repr"asentiert.
350 
351     Wird das Ende des Streams oder die Kennung SFX_REC_PRETAG_EOR
352     erreicht, bevor ein Record mit dem ge"unschten Pre-Tag gefunden wird,
353     ist die erzeugte Instanz ung"ultig ('IsValid() == sal_False'). Ein ent-
354     sprechender Error-Code (ERRCODE_IO_EOF bzw. ERRCODE_IO_WRONGFORMAT)
355     ist dann am Stream gesetzt, dessen Position ist dann au\serdem unver-
356     "andert.
357 
358     Bei 'nTag==SFX_FILEREC_PRETAG_EOR' wird nicht versucht, einen Record
359     zu lesen, es wird sofort 'IsValid()' auf sal_False gesetzt und kein Error-Code
360     am Stream gesetzt. Dies ist dauzu gedacht, ohne 'new' und 'delete'
361     abw"rtskompatibel SfxMiniRecords einbauen zu k"onnen. Siehe dazu
362     <SfxItemSet::Load()>.
363 
364 
365     [Anwendungsvorschlag]
366 
367     Wird dieser Ctor in einer bereits ausgelieferten Programmversion
368     verwendet, k"onnen in das File-Format jeweils davor kompatibel neue
369     Records mit einer anderen Kennung eingef"ugt werden. Diese werden
370     schlie\slich automatisch "uberlesen. Erkauft wird diese M"oglichkeit
371     allerdings mit etwas schlechterem Laufzeitverhalten im Vergleich mit
372     direktem 'drauf-los-lesen', der sich jedoch auf einen Vergleich zweier
373     Bytes reduziert, falls der gesuchte Record der erste in der Folge ist.
374 */
375 
376 :   _pStream( pStream ),
377     _bSkipped( nTag == SFX_REC_PRETAG_EOR )
378 {
379     // ggf. ignorieren (s.o.)
380     if ( _bSkipped )
381     {
382         _nPreTag = nTag;
383         return;
384     }
385 
386     // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
387     sal_uInt32 nStartPos = pStream->Tell();
388 
389     // passenden Record suchen
390     while(sal_True)
391     {
392         // Header lesen
393         DBG( DbgOutf( "SfxFileRec: searching record at %ul", pStream->Tell() ) );
394         sal_uInt32 nHeader;
395         *pStream >> nHeader;
396 
397         // Headerdaten von Basisklasse extrahieren lassen
398         SetHeader_Impl( nHeader );
399 
400         // ggf. Fehler behandeln
401         if ( pStream->IsEof() )
402             _nPreTag = SFX_REC_PRETAG_EOR;
403         else if ( _nPreTag == SFX_REC_PRETAG_EOR )
404             pStream->SetError( ERRCODE_IO_WRONGFORMAT );
405         else
406         {
407             // wenn gefunden, dann Schleife abbrechen
408             if ( _nPreTag == nTag )
409                 break;
410 
411             // sonst skippen und weitersuchen
412             pStream->Seek( _nEofRec );
413             continue;
414         }
415 
416         // Fehler => zur"uck-seeken
417         pStream->Seek( nStartPos );
418         break;
419     }
420 }
421 
422 //=========================================================================
423 
424 SfxSingleRecordWriter::SfxSingleRecordWriter
425 (
426     sal_uInt8           nRecordType,    // f"ur Subklassen
427     SvStream*       pStream,        // Stream, in dem der Record angelegt wird
428     sal_uInt16          nContentTag,    // Inhalts-Art-Kennung
429     sal_uInt8           nContentVer     // Inhalts-Versions-Kennung
430 )
431 
432 /*  [Beschreibung]
433 
434     Interner Ctor f"ur Subklassen.
435 */
436 
437 :   SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT )
438 {
439     // Erweiterten Header hiner den des SfxMiniRec schreiben
440     *pStream << SFX_REC_HEADER(nRecordType, nContentTag, nContentVer);
441 }
442 
443 //-------------------------------------------------------------------------
444 
445 SfxSingleRecordWriter::SfxSingleRecordWriter
446 (
447     SvStream*       pStream,        // Stream, in dem der Record angelegt wird
448     sal_uInt16          nContentTag,    // Inhalts-Art-Kennung
449     sal_uInt8           nContentVer     // Inhalts-Versions-Kennung
450 )
451 
452 /*  [Beschreibung]
453 
454     Legt in 'pStream' einen 'SfxSingleRecord' an, dessen Content-Gr"o\se
455     nicht bekannt ist, sondern nach dam Streamen des Contents errechnet
456     werden soll.
457 */
458 
459 :   SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT )
460 {
461     // Erweiterten Header hiner den des SfxMiniRec schreiben
462     *pStream << SFX_REC_HEADER( SFX_REC_TYPE_SINGLE, nContentTag, nContentVer);
463 }
464 
465 //-------------------------------------------------------------------------
466 
467 SfxSingleRecordWriter::SfxSingleRecordWriter
468 (
469     SvStream*       pStream,        // Stream, in dem der Record angelegt wird
470     sal_uInt16          nContentTag,    // Inhalts-Art-Kennung
471     sal_uInt8           nContentVer,    // Inhalts-Versions-Kennung
472     sal_uInt32          nContentSize    // Gr"o\se des Inhalts in Bytes
473 )
474 
475 /*  [Beschreibung]
476 
477     Legt in 'pStream' einen 'SfxSingleRecord' an, dessen Content-Gr"o\se
478     von vornherein bekannt ist.
479 */
480 
481 :   SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT,
482                          nContentSize + SFX_REC_HEADERSIZE_SINGLE )
483 {
484     // Erweiterten Header hinter den des SfxMiniRec schreiben
485     *pStream << SFX_REC_HEADER( SFX_REC_TYPE_SINGLE, nContentTag, nContentVer);
486 }
487 
488 //=========================================================================
489 
490 inline FASTBOOL SfxSingleRecordReader::ReadHeader_Impl( sal_uInt16 nTypes )
491 
492 /*  [Beschreibung]
493 
494     Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
495     die Basisklasse bereits initialisiert und deren Header gelesen ist.
496     Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
497     nicht zur"uckge-seekt.
498 */
499 
500 {
501     FASTBOOL bRet;
502 
503     // Basisklassen-Header einlesen
504     sal_uInt32 nHeader=0;
505     *_pStream >> nHeader;
506     if ( !SetHeader_Impl( nHeader ) )
507         bRet = sal_False;
508     else
509     {
510         // eigenen Header einlesen
511         *_pStream >> nHeader;
512         _nRecordVer = sal::static_int_cast< sal_uInt8 >(SFX_REC_VER(nHeader));
513         _nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));
514 
515         // falscher Record-Typ?
516         _nRecordType = sal::static_int_cast< sal_uInt8 >(SFX_REC_TYP(nHeader));
517         bRet = 0 != ( nTypes & _nRecordType);
518     }
519     return bRet;
520 }
521 
522 //-------------------------------------------------------------------------
523 
524 SfxSingleRecordReader::SfxSingleRecordReader( SvStream *pStream )
525 :   SfxMiniRecordReader()
526 {
527     // Startposition merken, um im Fehlerfall zur"uck-seeken zu k"onnen
528     #ifdef DBG_UTIL
529     sal_uInt32 nStartPos = pStream->Tell();
530     DBG( DbgOutf( "SfxFileRec: reading record at %ul", nStartPos ) );
531     #endif
532 
533     // Basisklasse initialisieren (nicht via Ctor, da der nur MiniRecs akzept.)
534     Construct_Impl( pStream );
535 
536     // nur Header mit korrektem Record-Type akzeptieren
537     if ( !ReadHeader_Impl( SFX_REC_TYPE_SINGLE ) )
538     {
539         // Error-Code setzen und zur"uck-seeken
540         pStream->SeekRel( - SFX_REC_HEADERSIZE_SINGLE );
541         pStream->SetError( ERRCODE_IO_WRONGFORMAT );
542     }
543 }
544 
545 //-------------------------------------------------------------------------
546 
547 SfxSingleRecordReader::SfxSingleRecordReader( SvStream *pStream, sal_uInt16 nTag )
548 {
549     // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
550     sal_uInt32 nStartPos = pStream->Tell();
551 
552     // richtigen Record suchen, ggf. Error-Code setzen und zur"uck-seeken
553     Construct_Impl( pStream );
554     if ( !FindHeader_Impl( SFX_REC_TYPE_SINGLE, nTag ) )
555     {
556         // Error-Code setzen und zur"uck-seeken
557         pStream->Seek( nStartPos );
558         pStream->SetError( ERRCODE_IO_WRONGFORMAT );
559     }
560 }
561 
562 //-------------------------------------------------------------------------
563 
564 FASTBOOL SfxSingleRecordReader::FindHeader_Impl
565 (
566     sal_uInt16      nTypes,     // arithm. Veroderung erlaubter Record-Typen
567     sal_uInt16      nTag        // zu findende Record-Art-Kennung
568 )
569 
570 /*  [Beschreibung]
571 
572     Interne Methode zum lesen des Headers des ersten Record, der einem
573     der Typen in 'nTypes' entspricht und mit der Art-Kennung 'nTag'
574     gekennzeichnet ist.
575 
576     Kann ein solcher Record nicht gefunden werden, wird am Stream ein
577     Errorcode gesetzt, zur"uck-geseekt und sal_False zur"uckgeliefert.
578 */
579 
580 {
581     // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
582     sal_uInt32 nStartPos = _pStream->Tell();
583 
584     // richtigen Record suchen
585     while ( !_pStream->IsEof() )
586     {
587         // Header lesen
588         sal_uInt32 nHeader;
589         DBG( DbgOutf( "SfxFileRec: searching record at %ul", _pStream->Tell() ) );
590         *_pStream >> nHeader;
591         if ( !SetHeader_Impl( nHeader ) )
592             // EOR => Such-Schleife abbreichen
593             break;
594 
595         // Extended Record gefunden?
596         if ( _nPreTag == SFX_REC_PRETAG_EXT )
597         {
598             // Extended Header lesen
599             *_pStream >> nHeader;
600             _nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));
601 
602             // richtigen Record gefunden?
603             if ( _nRecordTag == nTag )
604             {
605                 // gefundener Record-Typ passend?
606                 _nRecordType = sal::static_int_cast< sal_uInt8 >(
607                     SFX_REC_TYP(nHeader));
608                 if ( nTypes & _nRecordType )
609                     // ==> gefunden
610                     return sal_True;
611 
612                 // error => Such-Schleife abbrechen
613                 break;
614             }
615         }
616 
617         // sonst skippen
618         if ( !_pStream->IsEof() )
619             _pStream->Seek( _nEofRec );
620     }
621 
622     // Fehler setzen und zur"uck-seeken
623     _pStream->SetError( ERRCODE_IO_WRONGFORMAT );
624     _pStream->Seek( nStartPos );
625     return sal_False;
626 }
627 
628 //=========================================================================
629 
630 SfxMultiFixRecordWriter::SfxMultiFixRecordWriter
631 (
632     sal_uInt8           nRecordType,    // Subklassen Record-Kennung
633     SvStream*       pStream,        // Stream, in dem der Record angelegt wird
634     sal_uInt16          nContentTag,    // Content-Art-Kennung
635     sal_uInt8           nContentVer,    // Content-Versions-Kennung
636     sal_uInt32                          // Gr"o\se jedes einzelnen Contents in Bytes
637 )
638 
639 /*  [Beschreibung]
640 
641     Interne Methode f"ur Subklassen.
642 */
643 
644 :   SfxSingleRecordWriter( nRecordType, pStream, nContentTag, nContentVer ),
645     _nContentCount( 0 )
646 {
647     // Platz f"ur eigenen Header
648     pStream->SeekRel( + SFX_REC_HEADERSIZE_MULTI );
649 }
650 
651 //------------------------------------------------------------------------
652 
653 SfxMultiFixRecordWriter::SfxMultiFixRecordWriter
654 (
655     SvStream*       pStream,        // Stream, in dem der Record angelegt wird
656     sal_uInt16          nContentTag,    // Content-Art-Kennung
657     sal_uInt8           nContentVer,    // Content-Versions-Kennung
658     sal_uInt32                          // Gr"o\se jedes einzelnen Contents in Bytes
659 )
660 
661 /*  [Beschreibung]
662 
663     Legt in 'pStream' einen 'SfxMultiFixRecord' an, dessen Content-Gr"o\se
664     konstant und von vornherein bekannt ist.
665 */
666 
667 :   SfxSingleRecordWriter( SFX_REC_TYPE_FIXSIZE,
668                            pStream, nContentTag, nContentVer ),
669     _nContentCount( 0 )
670 {
671     // Platz f"ur eigenen Header
672     pStream->SeekRel( + SFX_REC_HEADERSIZE_MULTI );
673 }
674 
675 //------------------------------------------------------------------------
676 
677 sal_uInt32 SfxMultiFixRecordWriter::Close( FASTBOOL bSeekToEndOfRec )
678 
679 //  siehe <SfxMiniRecordWriter>
680 
681 {
682     // Header noch nicht geschrieben?
683     if ( !_bHeaderOk )
684     {
685         // Position hinter Record merken, um sie restaurieren zu k"onnen
686         sal_uInt32 nEndPos = SfxSingleRecordWriter::Close( sal_False );
687 
688         // gegen"uber SfxSingleRecord erweiterten Header schreiben
689         *_pStream << _nContentCount;
690         *_pStream << _nContentSize;
691 
692         // je nachdem ans Ende des Records seeken oder hinter Header bleiben
693         if ( bSeekToEndOfRec )
694             _pStream->Seek(nEndPos);
695         return nEndPos;
696     }
697 
698     // Record war bereits geschlossen
699     return 0;
700 }
701 
702 //=========================================================================
703 
704 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
705 (
706     sal_uInt8           nRecordType,    // Record-Kennung der Subklasse
707     SvStream*       pStream,        // Stream, in dem der Record angelegt wird
708     sal_uInt16          nRecordTag,     // Gesamt-Art-Kennung
709     sal_uInt8           nRecordVer      // Gesamt-Versions-Kennung
710 )
711 
712 /*  [Beschreibung]
713 
714     Interner Ctor f"ur Subklassen.
715 */
716 
717 :   SfxMultiFixRecordWriter( nRecordType, pStream, nRecordTag, nRecordVer, 0 ),
718     _nContentVer( 0 )
719 {
720 }
721 
722 //-------------------------------------------------------------------------
723 
724 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
725 (
726     SvStream*       pStream,        // Stream, in dem der Record angelegt wird
727     sal_uInt16          nRecordTag,     // Gesamt-Art-Kennung
728     sal_uInt8           nRecordVer      // Gesamt-Versions-Kennung
729 )
730 
731 /*  [Beschreibung]
732 
733     Legt in 'pStream' einen 'SfxMultiVarRecord' an, dessen Content-Gr"o\sen
734     weder bekannt sind noch identisch sein m"ussen, sondern jeweils nach dem
735     Streamen jedes einzelnen Contents errechnet werden sollen.
736 
737 
738     [Anmerkung]
739 
740     Diese Methode ist nicht inline, da f"ur die Initialisierung eines
741     <SvULongs>-Members zu viel Code generiert werden w"urde.
742 */
743 
744 :   SfxMultiFixRecordWriter( SFX_REC_TYPE_VARSIZE,
745                              pStream, nRecordTag, nRecordVer, 0 ),
746     _nContentVer( 0 )
747 {
748 }
749 
750 //-------------------------------------------------------------------------
751 
752 SfxMultiVarRecordWriter::~SfxMultiVarRecordWriter()
753 
754 /*  [Beschreibung]
755 
756     Der Dtor der Klasse <SfxMultiVarRecordWriter> schlie\st den Record
757     automatisch, falls <SfxMultiVarRecordWriter::Close()> nicht bereits
758     explizit gerufen wurde.
759 */
760 
761 {
762     // wurde der Header noch nicht geschrieben oder mu\s er gepr"uft werden
763     if ( !_bHeaderOk )
764         Close();
765 }
766 
767 //-------------------------------------------------------------------------
768 
769 void SfxMultiVarRecordWriter::FlushContent_Impl()
770 
771 /*  [Beschreibung]
772 
773     Interne Methode zum Abschlie\sen eines einzelnen Contents.
774 */
775 
776 {
777     // Versions-Kennung und Positions-Offset des aktuellen Contents merken;
778     // das Positions-Offset ist relativ zur Startposition des ersten Contents
779     _aContentOfs.Insert(
780             SFX_REC_CONTENT_HEADER(_nContentVer,_nStartPos,_nContentStartPos),
781             _nContentCount-1 );
782 }
783 
784 //-------------------------------------------------------------------------
785 
786 void SfxMultiVarRecordWriter::NewContent()
787 
788 // siehe <SfxMultiFixRecordWriter>
789 
790 {
791     // schon ein Content geschrieben?
792     if ( _nContentCount )
793         FlushContent_Impl();
794 
795     // neuen Content beginnen
796     _nContentStartPos = _pStream->Tell();
797     ++_nContentCount;
798 }
799 
800 //-------------------------------------------------------------------------
801 
802 sal_uInt32 SfxMultiVarRecordWriter::Close( FASTBOOL bSeekToEndOfRec )
803 
804 // siehe <SfxMiniRecordWriter>
805 
806 {
807     // Header noch nicht geschrieben?
808     if ( !_bHeaderOk )
809     {
810         // ggf. letzten Content abschlie\sen
811         if ( _nContentCount )
812             FlushContent_Impl();
813 
814         // Content-Offset-Tabelle schreiben
815         sal_uInt32 nContentOfsPos = _pStream->Tell();
816         //! darf man das so einr"ucken?
817         #if defined(OSL_LITENDIAN)
818             _pStream->Write( _aContentOfs.GetData(),
819                              sizeof(sal_uInt32)*_nContentCount );
820         #else
821             for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
822                 *_pStream << sal_uInt32(_aContentOfs[n]);
823         #endif
824 
825         // SfxMultiFixRecordWriter::Close() "uberspringen!
826         sal_uInt32 nEndPos = SfxSingleRecordWriter::Close( sal_False );
827 
828         // eigenen Header schreiben
829         *_pStream << _nContentCount;
830         if ( SFX_REC_TYPE_VARSIZE_RELOC == _nPreTag ||
831              SFX_REC_TYPE_MIXTAGS_RELOC == _nPreTag )
832             *_pStream << static_cast<sal_uInt32>(nContentOfsPos - ( _pStream->Tell() + sizeof(sal_uInt32) ));
833         else
834             *_pStream << nContentOfsPos;
835 
836         // ans Ende des Records seeken bzw. am Ende des Headers bleiben
837         if ( bSeekToEndOfRec )
838              _pStream->Seek(nEndPos);
839         return nEndPos;
840     }
841 
842     // Record war bereits vorher geschlossen
843     return 0;
844 }
845 
846 //=========================================================================
847 
848 void SfxMultiMixRecordWriter::NewContent
849 (
850     sal_uInt16      nContentTag,    // Kennung f"ur die Art des Contents
851     sal_uInt8       nContentVer     // Kennung f"ur die Version des Contents
852 )
853 
854 /*  [Beschreibung]
855 
856     Mit dieser Methode wird in den Record ein neuer Content eingef"ugt
857     und dessen Content-Tag sowie dessen Content-Version angegeben. Jeder,
858     auch der 1. Record mu\s durch Aufruf dieser Methode eingeleitet werden.
859 */
860 
861 {
862     // ggf. vorherigen Record abschlie\sen
863     if ( _nContentCount )
864         FlushContent_Impl();
865 
866     // Tag vor den Content schreiben, Version und Startposition merken
867     _nContentStartPos = _pStream->Tell();
868     ++_nContentCount;
869     *_pStream << nContentTag;
870     _nContentVer = nContentVer;
871 }
872 
873 //=========================================================================
874 
875 FASTBOOL SfxMultiRecordReader::ReadHeader_Impl()
876 
877 /*  [Beschreibung]
878 
879     Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
880     die Basisklasse bereits initialisiert und deren Header gelesen ist.
881     Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
882     nicht zur"uckge-seekt.
883 */
884 
885 {
886     // eigenen Header lesen
887     *_pStream >> _nContentCount;
888     *_pStream >> _nContentSize; // Fix: jedes einzelnen, Var|Mix: Tabellen-Pos.
889 
890     // mu\s noch eine Tabelle mit Content-Offsets geladen werden?
891     if ( _nRecordType != SFX_REC_TYPE_FIXSIZE )
892     {
893         // Tabelle aus dem Stream einlesen
894         sal_uInt32 nContentPos = _pStream->Tell();
895         if ( _nRecordType == SFX_REC_TYPE_VARSIZE_RELOC ||
896              _nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
897             _pStream->SeekRel( + _nContentSize );
898         else
899             _pStream->Seek( _nContentSize );
900         _pContentOfs = new sal_uInt32[_nContentCount];
901         memset(_pContentOfs, 0, _nContentCount*sizeof(sal_uInt32));
902     //! darf man jetzt so einr"ucken
903         #if defined(OSL_LITENDIAN)
904             _pStream->Read( _pContentOfs, sizeof(sal_uInt32)*_nContentCount );
905         #else
906             for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
907                 *_pStream >> _pContentOfs[n];
908         #endif
909         _pStream->Seek( nContentPos );
910     }
911 
912     // Header konnte gelesen werden, wenn am Stream kein Error gesetzt ist
913     return !_pStream->GetError();
914 }
915 
916 //-------------------------------------------------------------------------
917 
918 SfxMultiRecordReader::SfxMultiRecordReader( SvStream *pStream )
919     : _pContentOfs(0)
920     , _nContentSize(0)
921     , _nContentCount(0)
922     , _nContentNo(0)
923 {
924     // Position im Stream merken, um im Fehlerfall zur"uck-seeken zu k"onnen
925     _nStartPos = pStream->Tell();
926 
927     // Basisklasse konstruieren (normaler Ctor w"urde nur SingleRecs lesen)
928     SfxSingleRecordReader::Construct_Impl( pStream );
929 
930     // Header der Basisklasse lesen
931     if ( !SfxSingleRecordReader::ReadHeader_Impl( SFX_REC_TYPE_FIXSIZE |
932                 SFX_REC_TYPE_VARSIZE | SFX_REC_TYPE_VARSIZE_RELOC |
933                 SFX_REC_TYPE_MIXTAGS | SFX_REC_TYPE_MIXTAGS_RELOC ) ||
934          !ReadHeader_Impl() )
935         // als ung"ultig markieren und zur"uck-seeken
936         SetInvalid_Impl( _nStartPos );
937 }
938 
939 //-------------------------------------------------------------------------
940 
941 SfxMultiRecordReader::SfxMultiRecordReader( SvStream *pStream, sal_uInt16 nTag )
942 :   _nContentNo(0)
943 {
944     // Position im Stream merken, um im Fehlerfall zur"uck-seeken zu k"onnen
945     _nStartPos = pStream->Tell();
946 
947     // passenden Record suchen und Basisklasse initialisieren
948     SfxSingleRecordReader::Construct_Impl( pStream );
949     if ( SfxSingleRecordReader::FindHeader_Impl( SFX_REC_TYPE_FIXSIZE |
950             SFX_REC_TYPE_VARSIZE | SFX_REC_TYPE_VARSIZE_RELOC |
951             SFX_REC_TYPE_MIXTAGS | SFX_REC_TYPE_MIXTAGS_RELOC,
952             nTag ) )
953     {
954         // eigenen Header dazu-lesen
955         if ( !ReadHeader_Impl() )
956             // nicht lesbar => als ung"ultig markieren und zur"uck-seeken
957             SetInvalid_Impl( _nStartPos);
958     }
959 }
960 
961 //-------------------------------------------------------------------------
962 
963 SfxMultiRecordReader::~SfxMultiRecordReader()
964 {
965     delete[] _pContentOfs;
966 }
967 
968 //-------------------------------------------------------------------------
969 
970 FASTBOOL SfxMultiRecordReader::GetContent()
971 
972 /*  [Beschreibung]
973 
974     Positioniert den Stream an den Anfang des n"chsten bzw. beim 1. Aufruf
975     auf den Anfang des ersten Contents im Record und liest ggf. dessen
976     Header ein.
977 
978     Liegt laut Record-Header kein Content mehr vor, wird sal_False zur"uck-
979     gegeben. Trotz einem sal_True-Returnwert kann am Stream ein Fehlercode
980     gesetzt sein, z.B. falls er unvorhergesehenerweise (kaputtes File)
981     zuende ist.
982 */
983 
984 {
985     // noch ein Content vorhanden?
986     if ( _nContentNo < _nContentCount )
987     {
988         // den Stream an den Anfang des Contents positionieren
989         sal_uInt32 nOffset = _nRecordType == SFX_REC_TYPE_FIXSIZE
990                     ? _nContentNo * _nContentSize
991                     : SFX_REC_CONTENT_OFS(_pContentOfs[_nContentNo]);
992         sal_uInt32 nNewPos = _nStartPos + nOffset;
993         DBG_ASSERT( nNewPos >= _pStream->Tell(), "SfxMultiRecordReader::GetContent() - New position before current, to much data red!" );
994 
995         // #99366#: correct stream pos in every case;
996         // the if clause was added by MT  a long time ago,
997         // maybe to 'repair' other corrupt documents; but this
998         // gives errors when writing with 5.1 and reading with current
999         // versions, so we decided to remove the if clause (KA-05/17/2002)
1000         // if ( nNewPos > _pStream->Tell() )
1001         _pStream->Seek( nNewPos );
1002 
1003         // ggf. Content-Header lesen
1004         if ( _nRecordType == SFX_REC_TYPE_MIXTAGS ||
1005              _nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
1006         {
1007             _nContentVer = sal::static_int_cast< sal_uInt8 >(
1008                 SFX_REC_CONTENT_VER(_pContentOfs[_nContentNo]));
1009             *_pStream >> _nContentTag;
1010         }
1011 
1012         // ContentNo weiterz"ahlen
1013         ++_nContentNo;
1014         return sal_True;
1015     }
1016 
1017     return sal_False;
1018 }
1019 
1020 
1021