1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <sal/log.hxx>
#include <svl/filerec.hxx>
#include <osl/endian.h>
/* The following macros extract parts from a sal_uInt32 value.
These sal_uInt32 values are written out instead of the individual
values to reduce the number of calls.
*/
#define SFX_REC_PRE(n) ( ((n) & 0x000000FF) )
#define SFX_REC_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
#define SFX_REC_TYP(n) ( ((n) & 0x000000FF) )
#define SFX_REC_TAG(n) ( ((n) & 0xFFFF0000) >> 16 )
#define SFX_REC_CONTENT_VER(n) ( ((n) & 0x000000FF) )
#define SFX_REC_CONTENT_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
/* The following macros combine parts to a sal_uInt32 value.
This sal_uInt32 value is written instead of the individual values
to reduce the number of calls.
*/
static void lclWriteMiniHeader(SvStream *p, sal_uInt32 nPreTag, sal_uInt32 nStartPos, sal_uInt32 nEndPos)
{
(*p).WriteUInt32( sal_uInt32(nPreTag) |
sal_uInt32(nEndPos-nStartPos-SFX_REC_HEADERSIZE_MINI) << 8 );
}
static void lclWriteHeader(SvStream *p, sal_uInt32 nRecType, sal_uInt32 nContentTag, sal_uInt32 nContentVer)
{
(*p).WriteUInt32( sal_uInt32(nRecType) |
( sal_uInt32(nContentVer) << 8 ) |
( sal_uInt32(nContentTag) << 16 ) );
}
#define SFX_REC_CONTENT_HEADER(nContentVer,n1StStartPos,nCurStartPos) \
( sal_uInt32(nContentVer) | \
sal_uInt32( nCurStartPos - n1StStartPos ) << 8 )
/** Close the record; write the header
*
* @param bSeekToEndOfRec
* if true (default) the stream is positioned at the end of the record;
* if false the stream at the start of the content (so after the header).
*
* This method closes the record. The main function is to write the header.
* If the header was written already this method is a no-op.
*
* @return sal_uInt32 != 0: Position im the stream immediately after the record.
* If 'bSeekToEndOfRecord==sal_True' this will be equal to the current stream position.
* == 0: The header was already written.
*/
sal_uInt32 SfxMiniRecordWriter::Close(bool bSeekToEndOfRec)
{
// The header wasn't written yet?
if ( !_bHeaderOk )
{
// Write header at the start of the record
sal_uInt32 nEndPos = _pStream->Tell();
_pStream->Seek( _nStartPos );
lclWriteMiniHeader(_pStream, _nPreTag, _nStartPos, nEndPos );
// seek to the end of the record or stay where we are
if ( bSeekToEndOfRec )
_pStream->Seek( nEndPos );
// the header has been written NOW
_bHeaderOk = true;
return nEndPos;
}
// Record was closed already
return 0;
}
/**
Internal method for belatedly processsing a header read externally.
If the header corresponds to an End-Of-Record tag, an error
code is set on the stream and sal_False is returned.
But the stream will not be reset to the record start in case of an error.
*/
bool SfxMiniRecordReader::SetHeader_Impl( sal_uInt32 nHeader )
{
bool bRet = true;
// determine record end and PreTag from the header
_nEofRec = _pStream->Tell() + SFX_REC_OFS(nHeader);
_nPreTag = sal::static_int_cast< sal_uInt8 >(SFX_REC_PRE(nHeader));
// Error in case of End of Record tag
if ( _nPreTag == SFX_REC_PRETAG_EOR )
{
_pStream->SetError( ERRCODE_IO_WRONGFORMAT );
bRet = true;
}
return bRet;
}
/**
*
* @param pstream
* an \a SvStream, which has an \a SfxMiniRecord at the current position
* @param nTag
* Pre-Tag of the wanted record
*
* This constructor interprets a 'pStream' from the current position
* as a continuous sequence of records that should be parsable by
* this group of classes. The first record that is an <SfxMiniRecord>
* (possibly an extended-Record> that has the PreTag 'nTag' will be opened
* and represented by this instance.
*
* If the end of stream is reached or a record with tag
* SFX_REC_PRETAG_EOR is seen before a record with the wanted 'nTag'
* tag is found, the created instance is invalid ('IsValid() ==
* sal_False'). The ERRCODE_IO_WRONGFORMAT error code will be set on
* the stream,and the current position will be unchanged.
*
* If (the wanted tag) 'nTag==SFX_FILEREC_PRETAG_EOR' no attempt is
* made to read a record, but 'IsValid()' is set to sal_False immediately.
* This gives the possibility to include backward compatible SfxMiniRecords
* without 'new' or 'delete'. See <SfxItemSet::Load()>.
*
* Suggested usage:
*
* This constructor allows for adding new record types in a backward
* compatible way by writing out a record with a new tag followed
* by one with an old tag. In that case previous versions of the program
* that do not recognise the new tag will skip the new record
* automatically. This does cause a slight run time inefficiency,
* compared just starting reading, but if the first record
* is the wanted one the difference is just a comparison of 2 bytes.
*/
SfxMiniRecordReader::SfxMiniRecordReader(SvStream* pStream, sal_uInt8 nTag)
: _pStream(pStream)
, _nEofRec(0)
, _bSkipped(nTag == SFX_REC_PRETAG_EOR)
{
// ignore if we are looking for SFX_REC_PRETAG_EOR
if ( _bSkipped )
{
_nPreTag = nTag;
return;
}
// remember StartPos to be able to seek back in case of error
sal_uInt32 nStartPos = pStream->Tell();
// look for the matching record
while(true)
{
// read header
SAL_INFO("svl", "SfxFileRec: searching record at " << pStream->Tell());
sal_uInt32 nHeader;
pStream->ReadUInt32( nHeader );
// let the base class extract the header data
SetHeader_Impl( nHeader );
// handle error, if any
if ( pStream->IsEof() )
_nPreTag = SFX_REC_PRETAG_EOR;
else if ( _nPreTag == SFX_REC_PRETAG_EOR )
pStream->SetError( ERRCODE_IO_WRONGFORMAT );
else
{
// stop the loop if the right tag is found
if ( _nPreTag == nTag )
break;
// or else skip the record and continue
pStream->Seek( _nEofRec );
continue;
}
// seek back in case of error
pStream->Seek( nStartPos );
break;
}
}
/**
*
* @param nRecordType for sub classes
* @param pStream stream to write the record to
* @param nContentTag record type
* @param nContentVer record version
*
* internal constructor for sub classes
*/
SfxSingleRecordWriter::SfxSingleRecordWriter(sal_uInt8 nRecordType,
SvStream* pStream,
sal_uInt16 nContentTag,
sal_uInt8 nContentVer)
: SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT )
{
// write extend header after the SfxMiniRec
lclWriteHeader(pStream, nRecordType, nContentTag, nContentVer);
}
/**
*
* @param nTypes arithmetic OR of allowed record types
* @param nTag record tag to find
*
* Internal method for reading the header of the first record
* that has the tag 'nTag', for which then the type should be
* one of the types in 'nTypes'.
*
* If such a record is not found an error code is set, the stream
* position is seek-ed back and sal_False is returned.
*/
bool SfxSingleRecordReader::FindHeader_Impl(sal_uInt16 nTypes, sal_uInt16 nTag)
{
// remember StartPos to be able to seek back in case of error
sal_uInt32 nStartPos = _pStream->Tell();
// look for the right record
while ( !_pStream->IsEof() )
{
// read header
sal_uInt32 nHeader;
SAL_INFO("svl", "SfxFileRec: searching record at " << _pStream->Tell());
_pStream->ReadUInt32( nHeader );
if ( !SetHeader_Impl( nHeader ) )
// EOR => abort loop
break;
// found extended record?
if ( _nPreTag == SFX_REC_PRETAG_EXT )
{
// read extended header
_pStream->ReadUInt32( nHeader );
_nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));
// found right record?
if ( _nRecordTag == nTag )
{
// record type matches as well?
_nRecordType = sal::static_int_cast< sal_uInt8 >(
SFX_REC_TYP(nHeader));
if ( nTypes & _nRecordType )
// ==> found it
return true;
// error => abort loop
break;
}
}
// else skip
if ( !_pStream->IsEof() )
_pStream->Seek( _nEofRec );
}
// set error and seek back
_pStream->SetError( ERRCODE_IO_WRONGFORMAT );
_pStream->Seek( nStartPos );
return false;
}
/**
*
* @param nRecordType sub class record type
* @param pStream Stream to write the record to
* @param nContentTag Content type
* @param nContentVer Content version
*
* Internal method for sub classes
*/
SfxMultiFixRecordWriter::SfxMultiFixRecordWriter(sal_uInt8 nRecordType,
SvStream* pStream,
sal_uInt16 nContentTag,
sal_uInt8 nContentVer)
: SfxSingleRecordWriter( nRecordType, pStream, nContentTag, nContentVer )
, _nContentStartPos(0)
, _nContentSize(0)
, _nContentCount(0)
{
// space for own header
pStream->SeekRel( + SFX_REC_HEADERSIZE_MULTI );
}
/**
* @see SfxMiniRecordWriter
*/
sal_uInt32 SfxMultiFixRecordWriter::Close()
{
// Header not written yet?
if ( !_bHeaderOk )
{
// remember position after header, to be able to seek back to it
sal_uInt32 nEndPos = SfxSingleRecordWriter::Close();
// write extended header after SfxSingleRecord
_pStream->WriteUInt16( _nContentCount );
_pStream->WriteUInt32( _nContentSize );
// seek to end of record or stay after the header
_pStream->Seek(nEndPos);
return nEndPos;
}
// Record was closed already
return 0;
}
/**
*
* @param nRecordType Record type of the sub class
* @param pStream stream to write the record to
* @param nRecordTag record base type
* @param nRecordVer record base version
*
* Internal constructor for sub classes
*/
SfxMultiVarRecordWriter::SfxMultiVarRecordWriter(sal_uInt8 nRecordType,
SvStream* pStream,
sal_uInt16 nRecordTag)
: SfxMultiFixRecordWriter( nRecordType, pStream, nRecordTag, 0 ),
_nContentVer( 0 )
{
}
/**
*
* @param pStream, stream to write the record to
* @param nRecordTag record base type
* @param nRecordVer record base version
*
* Starts an SfxMultiVarRecord in \a pStream, for which the size
* of the content does not have to be known or identical;
* after streaming a record its size will be calculated.
*
* Note:
*
* This method is not inline since too much code was generated
* for initializing the <SvULong> members.
*/
SfxMultiVarRecordWriter::SfxMultiVarRecordWriter(SvStream* pStream,
sal_uInt16 nRecordTag)
: SfxMultiFixRecordWriter( SFX_REC_TYPE_VARSIZE,
pStream, nRecordTag, 0 ),
_nContentVer( 0 )
{
}
/**
*
* The destructor of class <SfxMultiVarRecordWriter> closes the
* record automatically, in case <SfxMultiVarRecordWriter::Close()>
* has not been called explicitly yet.
*/
SfxMultiVarRecordWriter::~SfxMultiVarRecordWriter()
{
// close if the header has not been written yet
if ( !_bHeaderOk )
Close();
}
/**
*
* Internal method for finishing individual content
*/
void SfxMultiVarRecordWriter::FlushContent_Impl()
{
// record the version and position offset of the current content;
// the position offset is relative ot the start position of the
// first content.
assert(_aContentOfs.size() == static_cast<size_t>(_nContentCount)-1);
_aContentOfs.resize(_nContentCount-1);
_aContentOfs.push_back(
SFX_REC_CONTENT_HEADER(_nContentVer,_nStartPos,_nContentStartPos));
}
/**
* @see SfxMultiFixRecordWriter
*/
void SfxMultiVarRecordWriter::NewContent()
{
// written Content already?
if ( _nContentCount )
FlushContent_Impl();
// start new Content
_nContentStartPos = _pStream->Tell();
++_nContentCount;
}
/**
* @see SfxMiniRecordWriter
*/
sal_uInt32 SfxMultiVarRecordWriter::Close()
{
// Header not written yet?
if ( !_bHeaderOk )
{
// finish content if needed
if ( _nContentCount )
FlushContent_Impl();
// write out content offset table
sal_uInt32 nContentOfsPos = _pStream->Tell();
//! (loop without braces)
for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
_pStream->WriteUInt32( _aContentOfs[n] );
// skip SfxMultiFixRecordWriter::Close()!
sal_uInt32 nEndPos = SfxSingleRecordWriter::Close();
// write own header
_pStream->WriteUInt16( _nContentCount );
if ( SFX_REC_TYPE_VARSIZE_RELOC == _nPreTag ||
SFX_REC_TYPE_MIXTAGS_RELOC == _nPreTag )
_pStream->WriteUInt32( static_cast<sal_uInt32>(nContentOfsPos - ( _pStream->Tell() + sizeof(sal_uInt32) )) );
else
_pStream->WriteUInt32( nContentOfsPos );
// seek to the end of the record or stay where we are
_pStream->Seek(nEndPos);
return nEndPos;
}
// Record was closed already
return 0;
}
/**
*
* @param nContentTag tag for this content type
* @param nContentVer content version
*
* With this method new Content is added to a record and
* its tag and version are recorded. This method must be called
* to start each content, including the first record.
*/
void SfxMultiMixRecordWriter::NewContent(sal_uInt16 nContentTag, sal_uInt8 nContentVer)
{
// Finish the previous record if necessary
if ( _nContentCount )
FlushContent_Impl();
// Write the content tag, and record the version and starting position
_nContentStartPos = _pStream->Tell();
++_nContentCount;
_pStream->WriteUInt16( nContentTag );
_nContentVer = nContentVer;
}
/**
*
* Internal method for reading an SfxMultiRecord-Headers, after
* the base class has been initialized and its header has been read.
* If an error occurs an error code is set on the stream, but
* the stream position will not be seek-ed back in that case.
*/
bool SfxMultiRecordReader::ReadHeader_Impl()
{
// read own header
_pStream->ReadUInt16( _nContentCount );
_pStream->ReadUInt32( _nContentSize ); // Fix: each on its own, Var|Mix: table position
// do we still need to read a table with Content offsets?
if ( _nRecordType != SFX_REC_TYPE_FIXSIZE )
{
// read table from the stream
sal_uInt32 nContentPos = _pStream->Tell();
if ( _nRecordType == SFX_REC_TYPE_VARSIZE_RELOC ||
_nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
_pStream->SeekRel( + _nContentSize );
else
_pStream->Seek( _nContentSize );
const size_t nMaxRecords = _pStream->remainingSize() / sizeof(sal_uInt32);
if (_nContentCount > nMaxRecords)
{
SAL_WARN("svl", "Parsing error: " << nMaxRecords << " max possible entries, but " <<
_nContentCount << " claimed, truncating");
_nContentCount = nMaxRecords;
}
_pContentOfs = new sal_uInt32[_nContentCount];
memset(_pContentOfs, 0, _nContentCount*sizeof(sal_uInt32));
#if defined(OSL_LITENDIAN)
_pStream->Read( _pContentOfs, sizeof(sal_uInt32)*_nContentCount );
#else
// (loop without braces)
for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
_pStream->ReadUInt32( _pContentOfs[n] );
#endif
_pStream->Seek( nContentPos );
}
// It was possible to read the error if no error is set on the stream
return !_pStream->GetError();
}
SfxMultiRecordReader::SfxMultiRecordReader( SvStream *pStream, sal_uInt16 nTag )
: _pContentOfs(nullptr)
, _nContentSize(0)
, _nContentCount(0)
, _nContentNo(0)
, _nContentTag( 0 )
, _nContentVer( 0 )
{
// remember position in the stream to be able seek back in case of error
_nStartPos = pStream->Tell();
// look for matching record and initialize base class
SfxSingleRecordReader::Construct_Impl( pStream );
if ( SfxSingleRecordReader::FindHeader_Impl( SFX_REC_TYPE_FIXSIZE |
SFX_REC_TYPE_VARSIZE | SFX_REC_TYPE_VARSIZE_RELOC |
SFX_REC_TYPE_MIXTAGS | SFX_REC_TYPE_MIXTAGS_RELOC,
nTag ) )
{
// also read own header
if ( !ReadHeader_Impl() )
// not readable => mark as invalid and reset stream position
SetInvalid_Impl( _nStartPos);
}
}
SfxMultiRecordReader::~SfxMultiRecordReader()
{
delete[] _pContentOfs;
}
/**
*
* Positions the stream at the start of the next Content, or
* for the first call at the start of the first Content in the record,
* and reads its header if necessary.
*
* @return sal_False if there is no further Content according to
* the record header. Even if sal_True is returned an error can
* be set on the stream, for instance if the record finished prematurely
* in a broken file.
*/
bool SfxMultiRecordReader::GetContent()
{
// more Content available?
if ( _nContentNo < _nContentCount )
{
// position the stream at the start of the Content
sal_uInt32 nOffset = _nRecordType == SFX_REC_TYPE_FIXSIZE
? _nContentNo * _nContentSize
: SFX_REC_CONTENT_OFS(_pContentOfs[_nContentNo]);
sal_uInt32 nNewPos = _nStartPos + nOffset;
DBG_ASSERT( nNewPos >= _pStream->Tell(), "SfxMultiRecordReader::GetContent() - New position before current, to much data red!" );
// #99366#: correct stream pos in every case;
// the if clause was added by MT a long time ago,
// maybe to 'repair' other corrupt documents; but this
// gives errors when writing with 5.1 and reading with current
// versions, so we decided to remove the if clause (KA-05/17/2002)
// if ( nNewPos > _pStream->Tell() )
_pStream->Seek( nNewPos );
// Read Content Header if available
if ( _nRecordType == SFX_REC_TYPE_MIXTAGS ||
_nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
{
_nContentVer = sal::static_int_cast< sal_uInt8 >(
SFX_REC_CONTENT_VER(_pContentOfs[_nContentNo]));
_pStream->ReadUInt16( _nContentTag );
}
// Increment ContentNo
++_nContentNo;
return true;
}
return false;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|