xref: /aoo42x/main/svl/source/filerec/filerec.cxx (revision 6fb30688)
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