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