os/persistentdata/persistentstorage/store/HTOOLS/PFSDUMP.CPP
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     2
// All rights reserved.
sl@0
     3
// This component and the accompanying materials are made available
sl@0
     4
// under the terms of "Eclipse Public License v1.0"
sl@0
     5
// which accompanies this distribution, and is available
sl@0
     6
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     7
//
sl@0
     8
// Initial Contributors:
sl@0
     9
// Nokia Corporation - initial contribution.
sl@0
    10
//
sl@0
    11
// Contributors:
sl@0
    12
//
sl@0
    13
// Description:
sl@0
    14
//
sl@0
    15
sl@0
    16
#include "PFSDUMP.H"
sl@0
    17
sl@0
    18
// entry point
sl@0
    19
sl@0
    20
int main(int argc,char *argv[])
sl@0
    21
	{
sl@0
    22
	if (TheProgram.ProcessCommandLine(argc,argv))
sl@0
    23
		{
sl@0
    24
		TheProgram.ExplainUsage();
sl@0
    25
		return 1;
sl@0
    26
		}
sl@0
    27
	TheProgram.Run();
sl@0
    28
	return 0;
sl@0
    29
	}
sl@0
    30
sl@0
    31
// Class Program
sl@0
    32
sl@0
    33
class Program TheProgram;
sl@0
    34
sl@0
    35
Program::Program()
sl@0
    36
	: iFile(NULL),iOptions(0),iBin(&iBinBuf),iErrors(0)
sl@0
    37
	{
sl@0
    38
	cout << setfill('0');
sl@0
    39
	}
sl@0
    40
sl@0
    41
void Program::Information()
sl@0
    42
	{
sl@0
    43
	if (!Option(quiet))
sl@0
    44
		cout << "\nEPOC PermanentFileStore Dump Utility   Version " << MajorVersion << '.' \
sl@0
    45
			<< setw(2) << MinorVersion << "(build " << setw(3) << setfill('0') << Build \
sl@0
    46
			<< ")\nCopyright (c) Symbian Software Limited 1997-2004. All rights reserved.\n\n" << flush;
sl@0
    47
	cout << hex;
sl@0
    48
	cerr << hex;
sl@0
    49
	}
sl@0
    50
sl@0
    51
void Program::ExplainUsage()
sl@0
    52
	{
sl@0
    53
	Information();
sl@0
    54
	cout << "Usage:  PFSDUMP [options] storefile\n" \
sl@0
    55
			" -v    verbose: dump contents\n" \
sl@0
    56
			" -f    generate a frame dump\n" \
sl@0
    57
			" -r<n> use nth previous store revision\n" \
sl@0
    58
			" -c    format output for comparison (not for frame dumps)\n"
sl@0
    59
			" -q    quiet: suppress logo\n" << flush;
sl@0
    60
	}
sl@0
    61
sl@0
    62
int Program::ProcessCommandLine(int argc,char ** argv)
sl@0
    63
	{
sl@0
    64
	if (argc<2)
sl@0
    65
		return 1;
sl@0
    66
sl@0
    67
	int options=0;
sl@0
    68
	while (--argc>0)
sl@0
    69
		{
sl@0
    70
		istrstream arg(*++argv);
sl@0
    71
		int c=arg.get();
sl@0
    72
		if (c=='/' || c=='-')
sl@0
    73
			{
sl@0
    74
			while ((c=arg.get())!=EOF)
sl@0
    75
				{
sl@0
    76
				switch (c)
sl@0
    77
					{
sl@0
    78
				case 'c': case 'C':
sl@0
    79
					options|=compare;
sl@0
    80
					break;
sl@0
    81
				case 'f': case 'F':
sl@0
    82
					options|=frame;
sl@0
    83
					break;
sl@0
    84
				case 'q': case 'Q':
sl@0
    85
					options|=quiet;
sl@0
    86
					break;
sl@0
    87
				case 'r': case 'R':
sl@0
    88
					arg >> iTocRevision;
sl@0
    89
					if (arg.fail() || iTocRevision<0)
sl@0
    90
						return 1;
sl@0
    91
					break;
sl@0
    92
				case 'v': case 'V':
sl@0
    93
					options|=verbose;
sl@0
    94
					break;
sl@0
    95
				default:			// unrecognised option
sl@0
    96
					return 1;
sl@0
    97
					}
sl@0
    98
				}
sl@0
    99
			}
sl@0
   100
		else if (iFile!=NULL)
sl@0
   101
			return 1;		// two filenames?
sl@0
   102
		else
sl@0
   103
			iFile=arg.str();
sl@0
   104
		}
sl@0
   105
	if (options&frame)
sl@0
   106
		options&=~compare;
sl@0
   107
	iOptions=options;
sl@0
   108
	return iFile==NULL;
sl@0
   109
	}
sl@0
   110
sl@0
   111
void Program::Run()
sl@0
   112
//
sl@0
   113
// The main part of the program
sl@0
   114
//
sl@0
   115
	{
sl@0
   116
	Information();
sl@0
   117
	StoreFile store(iFile);
sl@0
   118
	if (iErrors)
sl@0
   119
		cerr << endl;
sl@0
   120
	if (store.Empty())
sl@0
   121
		{
sl@0
   122
		cout << "Store is empty" << endl;
sl@0
   123
		return;
sl@0
   124
		}
sl@0
   125
	if (Option(frame))
sl@0
   126
		{
sl@0
   127
		store.EmitHeader();
sl@0
   128
		store.EmitFrames();
sl@0
   129
		}
sl@0
   130
	store.EmitToc();
sl@0
   131
	if (!Option(frame))
sl@0
   132
		store.EmitStreams();
sl@0
   133
	}
sl@0
   134
sl@0
   135
void Program::Abort(char const* aMessage)
sl@0
   136
	{
sl@0
   137
	cerr << aMessage << endl;
sl@0
   138
	exit(3);
sl@0
   139
	}
sl@0
   140
sl@0
   141
void Program::Corrupt(char const* aMessage)
sl@0
   142
//
sl@0
   143
// terminate after detecting a fatal corruption error
sl@0
   144
//
sl@0
   145
	{
sl@0
   146
	cerr << "\nfatal error: " << aMessage << "\ncannot continue\n" << flush;
sl@0
   147
	exit(2);
sl@0
   148
	}
sl@0
   149
sl@0
   150
ostream& Program::Error()
sl@0
   151
	{
sl@0
   152
	++TheProgram.iErrors;
sl@0
   153
	return cerr << "error: ";
sl@0
   154
	}
sl@0
   155
sl@0
   156
ostream& Program::Warning()
sl@0
   157
	{
sl@0
   158
	++TheProgram.iErrors;
sl@0
   159
	return cerr << "warning: ";
sl@0
   160
	}
sl@0
   161
sl@0
   162
// Bin stream buffer
sl@0
   163
sl@0
   164
binbuf::binbuf()
sl@0
   165
	: streambuf()
sl@0
   166
	{}
sl@0
   167
sl@0
   168
int binbuf::overflow(int)
sl@0
   169
	{return 0;}
sl@0
   170
sl@0
   171
int binbuf::underflow()
sl@0
   172
	{return EOF;}
sl@0
   173
sl@0
   174
// Class StoreFile
sl@0
   175
sl@0
   176
int StoreFile::OutWidth;
sl@0
   177
sl@0
   178
StoreFile::StoreFile(char const* aFile)
sl@0
   179
	{
sl@0
   180
#ifdef __MSVCDOTNET__
sl@0
   181
	iFile.open(aFile, ios::binary);
sl@0
   182
#else //!__MSVCDOTNET__
sl@0
   183
	iFile.open(aFile, ios::binary | ios::nocreate);
sl@0
   184
#endif //__MSVCDOTNET__
sl@0
   185
	if (!iFile)
sl@0
   186
		Program::Abort("Unable to open file or file does not exist");
sl@0
   187
//
sl@0
   188
	unsigned long uid;
sl@0
   189
	iFile.read((char*)&uid,sizeof(uid));
sl@0
   190
	iFile.seekg(0,ios::end);
sl@0
   191
	int size=iFile.tellg();
sl@0
   192
	if (size<FrameDes::First || uid!=PermanentFileStoreUid)
sl@0
   193
		Program::Abort("Not a permanent file store");
sl@0
   194
//
sl@0
   195
	iSize=size;
sl@0
   196
	if (TheProgram.Option(Program::compare))
sl@0
   197
		OutWidth=7;
sl@0
   198
	else
sl@0
   199
		{
sl@0
   200
		int width=0;
sl@0
   201
		while (size)
sl@0
   202
			{
sl@0
   203
			++width;
sl@0
   204
			size>>=4;
sl@0
   205
			}
sl@0
   206
		OutWidth=width;
sl@0
   207
		}
sl@0
   208
//
sl@0
   209
	cout << "Dumping " << aFile << "\n\n";
sl@0
   210
//
sl@0
   211
	iFile.seekg(Header::Offset,ios::beg);
sl@0
   212
	iFile.read((char*)&iHeader,Header::Size);
sl@0
   213
//
sl@0
   214
	if (Empty())
sl@0
   215
		return;
sl@0
   216
	LoadFrames();
sl@0
   217
	LoadToc();
sl@0
   218
	}
sl@0
   219
sl@0
   220
StoreFile::~StoreFile()
sl@0
   221
	{
sl@0
   222
	iFile.close();
sl@0
   223
	}
sl@0
   224
sl@0
   225
void StoreFile::LoadFrames()
sl@0
   226
	{
sl@0
   227
	FrameDes frame;
sl@0
   228
	int offset=FrameDes::First;
sl@0
   229
	int full=FrameDes::First+FrameDes::Interval;
sl@0
   230
	int diff=FrameDes::First;
sl@0
   231
sl@0
   232
	while (offset-FrameDes::Size<iSize)
sl@0
   233
		{
sl@0
   234
		if (offset==full)
sl@0
   235
			{
sl@0
   236
			full+=FrameDes::Interval;
sl@0
   237
			diff+=FrameDes::Size;
sl@0
   238
			}
sl@0
   239
		if (offset>iSize)
sl@0
   240
			{
sl@0
   241
			Program::Warning() << "incomplete link at " << FramePos(offset-diff) << endl;
sl@0
   242
			break;
sl@0
   243
			}
sl@0
   244
		iFile.seekg(offset-FrameDes::Size,ios::beg);
sl@0
   245
		iFile >> frame;
sl@0
   246
		iFrames.Add(FramePos(offset-diff),frame);
sl@0
   247
		int length=frame.Length();
sl@0
   248
		if (length==0)
sl@0
   249
			{
sl@0
   250
			if (full>iSize && offset>iSize)
sl@0
   251
				Program::Warning() << "incomplete frame at " << FramePos(offset-diff) << endl;
sl@0
   252
			offset=full;
sl@0
   253
			}
sl@0
   254
		else
sl@0
   255
			{
sl@0
   256
			int newoffset=offset+length+FrameDes::Size;
sl@0
   257
			if (newoffset>=full || newoffset-FrameDes::Size>iSize)
sl@0
   258
				{
sl@0
   259
				Program::Error() << "bad link at " << FramePos(offset-diff) << ", skipping to next anchor link" << endl;
sl@0
   260
				offset=full;
sl@0
   261
				}
sl@0
   262
			else
sl@0
   263
				{
sl@0
   264
				offset=newoffset;
sl@0
   265
				if (full-offset<=FrameDes::Size)
sl@0
   266
					offset=full;
sl@0
   267
				}
sl@0
   268
			}
sl@0
   269
		}
sl@0
   270
	iFrames.Complete();
sl@0
   271
	}
sl@0
   272
sl@0
   273
void StoreFile::LoadToc()
sl@0
   274
	{
sl@0
   275
	FramePos toc=iHeader.Toc();
sl@0
   276
	Stream stream(iFrames,toc);
sl@0
   277
	if (!stream.IsGood())
sl@0
   278
		{
sl@0
   279
		Program::Error() << "invalid toc address " << toc << endl;
sl@0
   280
		return;
sl@0
   281
		}
sl@0
   282
	if (stream.Type()!=FrameDes::Toc)
sl@0
   283
		{
sl@0
   284
		Program::Error() << "toc address " << toc << ": refers to non-toc frame"<< endl;
sl@0
   285
		return;
sl@0
   286
		}
sl@0
   287
sl@0
   288
// find the requested store revision
sl@0
   289
	Frames::Iterator f=stream.Frame();
sl@0
   290
	Frames::Iterator const first=iFrames.Begin();
sl@0
   291
	for (int rev=TheProgram.TocRevision();rev;--rev)
sl@0
   292
		{
sl@0
   293
		do	{
sl@0
   294
			if (--f<first)
sl@0
   295
				Program::Abort("Store revision not found");
sl@0
   296
			} while (f->iDes.Type()!=FrameDes::Toc);
sl@0
   297
		}
sl@0
   298
sl@0
   299
	iToc.Load(iFile,iFrames,f,iHeader.GetReloc());
sl@0
   300
sl@0
   301
// verify the Toc stream references
sl@0
   302
	Toc::Iterator const end=iToc.End();
sl@0
   303
	for (Toc::Iterator iter=iToc.Begin();iter<end;++iter)
sl@0
   304
		{
sl@0
   305
		if (iter->iHandle.IsNull())
sl@0
   306
			Program::Error() << "missing entry in toc-delta for index " << (1+iter-iToc.Begin()) << endl;
sl@0
   307
		else if (!iter->iHandle.Avail() && iter->Pos().Pos()>=0)
sl@0
   308
			{
sl@0
   309
			f=iFrames.Find(iter->Pos());
sl@0
   310
			if (f==NULL)
sl@0
   311
				Program::Error() << "invalid stream reference in toc entry " << iter->iHandle << endl;
sl@0
   312
			else if (iter->Pos().Pos()>=toc.Pos())
sl@0
   313
				Program::Error() << "virtual stream reference in toc entry " << iter->iHandle << endl;
sl@0
   314
			else if (f->iDes.Type()!=FrameDes::Data)
sl@0
   315
				Program::Error() << "toc entry " << iter->iHandle << ": refers to non-data frame" << endl;
sl@0
   316
			}
sl@0
   317
		}
sl@0
   318
	}
sl@0
   319
sl@0
   320
void StoreFile::EmitHeader()
sl@0
   321
	{
sl@0
   322
	cout << iHeader;
sl@0
   323
	}
sl@0
   324
sl@0
   325
void StoreFile::EmitFrames()
sl@0
   326
	{
sl@0
   327
	int verbose=TheProgram.Option(Program::verbose);
sl@0
   328
	Frames::Iterator const end=iFrames.End();
sl@0
   329
	for (Frames::Iterator iter=iFrames.Begin();iter<end;++iter)
sl@0
   330
		{
sl@0
   331
		FramePos pos=iter->iPos;
sl@0
   332
		cout << "Frame at " << pos << ": " << iter->iDes << endl;
sl@0
   333
		if (!verbose)
sl@0
   334
			continue;
sl@0
   335
sl@0
   336
	// dump contents
sl@0
   337
		int length=iter->iDes.Length();
sl@0
   338
		if (length==0)
sl@0
   339
			{	// full frame
sl@0
   340
			if (iter+1==end)	// no more
sl@0
   341
				continue;
sl@0
   342
			length=iter[1].iPos.Pos()-pos.Pos();
sl@0
   343
			}
sl@0
   344
		HexDump hex;
sl@0
   345
		iFile.seekg(FileOffset(pos).Offset(),ios::beg);
sl@0
   346
		hex.Dump(iFile,length);
sl@0
   347
		hex.Flush();
sl@0
   348
		cout << endl;
sl@0
   349
		}
sl@0
   350
	cout << endl;
sl@0
   351
	}
sl@0
   352
sl@0
   353
void StoreFile::EmitToc()
sl@0
   354
	{
sl@0
   355
	cout << iToc;
sl@0
   356
	}
sl@0
   357
sl@0
   358
void StoreFile::EmitStreams()
sl@0
   359
	{
sl@0
   360
	int verbose=TheProgram.Option(Program::verbose);
sl@0
   361
	cout << endl;
sl@0
   362
	Toc::Iterator const end=iToc.End();
sl@0
   363
	for (Toc::Iterator iter=iToc.Begin();iter<end;++iter)
sl@0
   364
		{
sl@0
   365
		if (!iter->iHandle.Avail())
sl@0
   366
			{
sl@0
   367
			cout << "Stream " << iter->iHandle;
sl@0
   368
			if (iter->Pos().Pos()==-1)
sl@0
   369
				{
sl@0
   370
				cout << " is empty\n";
sl@0
   371
				continue;
sl@0
   372
				}
sl@0
   373
			if (!TheProgram.Option(Program::compare))
sl@0
   374
				cout << " at " << iter->Pos();
sl@0
   375
			Stream stream(iFrames,iter->Pos());
sl@0
   376
			if (!stream.IsGood() || stream.Type()!=FrameDes::Data)
sl@0
   377
				{
sl@0
   378
				cout << " is invalid\n";
sl@0
   379
				continue;
sl@0
   380
				}
sl@0
   381
			int len=stream.Length();
sl@0
   382
			cout << ", " << dec << len << hex << " byte" << (len==1 ? "\n" : "s\n");
sl@0
   383
			if (!verbose)
sl@0
   384
				continue;
sl@0
   385
sl@0
   386
		// dump contents
sl@0
   387
			HexDump hex;
sl@0
   388
			Frames::Iterator f=stream.Frame();
sl@0
   389
			do
sl@0
   390
				{
sl@0
   391
				FramePos pos=f->iPos;
sl@0
   392
				int len=f++->iDes.Length();
sl@0
   393
				if (len==0)
sl@0
   394
					len=f->iPos.Pos()-pos.Pos();
sl@0
   395
				iFile.seekg(FileOffset(pos).Offset(),ios::beg);
sl@0
   396
				hex.Dump(iFile,len);
sl@0
   397
				} while (f->iDes.Type()==FrameDes::Continuation);
sl@0
   398
			hex.Flush();
sl@0
   399
			cout << endl;
sl@0
   400
			}
sl@0
   401
		}
sl@0
   402
	}
sl@0
   403
sl@0
   404
// Class HexDump
sl@0
   405
sl@0
   406
HexDump::HexDump()
sl@0
   407
	: iPos(0),iByte(0)
sl@0
   408
	{
sl@0
   409
	Reset();
sl@0
   410
	}
sl@0
   411
sl@0
   412
HexDump::~HexDump()
sl@0
   413
	{
sl@0
   414
	Flush();
sl@0
   415
	}
sl@0
   416
sl@0
   417
void HexDump::Reset()
sl@0
   418
	{
sl@0
   419
	memset(iChar,' ',HexInterval+3);
sl@0
   420
	iChar[HexInterval+3]=0;
sl@0
   421
	iPtr=&iChar[2];
sl@0
   422
	}
sl@0
   423
sl@0
   424
void HexDump::Dump(istream& aStream,int aBytes)
sl@0
   425
	{
sl@0
   426
	while (--aBytes>=0 && !aStream.eof())
sl@0
   427
		{
sl@0
   428
		if (iByte==0)
sl@0
   429
			cout << Margin << FileOffset(iPos) << ':';
sl@0
   430
		int c=aStream.get();
sl@0
   431
		cout << ' ' << setw(2) << c;
sl@0
   432
		*iPtr++=char(isprint(c) ? c : '.');
sl@0
   433
		if (++iByte==HexInterval/2)
sl@0
   434
			{
sl@0
   435
			cout << ' ';
sl@0
   436
			++iPtr;
sl@0
   437
			}
sl@0
   438
		else if (iByte==HexInterval)
sl@0
   439
			Flush();
sl@0
   440
		}
sl@0
   441
	}
sl@0
   442
sl@0
   443
void HexDump::Flush()
sl@0
   444
	{
sl@0
   445
	if (iByte)
sl@0
   446
		{
sl@0
   447
		for (int ii=iByte;ii<HexInterval;++ii)
sl@0
   448
			cout << "   ";
sl@0
   449
		if (iByte<HexInterval/2)
sl@0
   450
			cout << ' ';
sl@0
   451
		cout << iChar << endl;
sl@0
   452
		iPos+=iByte;
sl@0
   453
		iByte=0;
sl@0
   454
		Reset();
sl@0
   455
		}
sl@0
   456
	}
sl@0
   457
sl@0
   458
// Toc
sl@0
   459
sl@0
   460
Toc::Toc()
sl@0
   461
	: iPos(0), iRep(NULL), iAvail(0)
sl@0
   462
	{
sl@0
   463
	memset(&iHeader,0,sizeof(iHeader));
sl@0
   464
	}
sl@0
   465
sl@0
   466
Toc::~Toc()
sl@0
   467
	{
sl@0
   468
	free(iRep);
sl@0
   469
	}
sl@0
   470
sl@0
   471
sl@0
   472
void Toc::Base(const char* aPtr,int aCount)
sl@0
   473
	{
sl@0
   474
	Entry* e=iRep;
sl@0
   475
	for (int i=1;i<=aCount;++e,++i)
sl@0
   476
		{
sl@0
   477
		e->iHandle=i;	// set the index part
sl@0
   478
		memcpy((char*)e+Entry::BaseRedundant,aPtr,Entry::BaseSize);
sl@0
   479
		aPtr+=Entry::BaseSize;
sl@0
   480
		}
sl@0
   481
	}
sl@0
   482
sl@0
   483
void Toc::Load(istream& aStream,Frames const& aFrames,Frames::Iterator aFrame,Header::Reloc const* aReloc)
sl@0
   484
	{
sl@0
   485
	iPos = aFrame->iPos;
sl@0
   486
sl@0
   487
	Stream toc1(aFrame);
sl@0
   488
	void* toc = toc1.Load(aStream);
sl@0
   489
	const char* p = reinterpret_cast<char*>(toc);
sl@0
   490
	memcpy(&iHeader,p,Head::Size);
sl@0
   491
	p+=Head::Size;
sl@0
   492
	int n = iHeader.iCount;
sl@0
   493
	if (n < 0)
sl@0
   494
		{
sl@0
   495
		memset(&iHeader,0,Head::Size);
sl@0
   496
		Program::Error() << "corrupt toc" << endl;
sl@0
   497
		return;
sl@0
   498
		}
sl@0
   499
	iRep=static_cast<Entry*>(malloc(n*sizeof(Entry)));
sl@0
   500
	if (iRep==NULL)
sl@0
   501
		Program::Abort("Out of memory");
sl@0
   502
sl@0
   503
	if (iHeader.IsDelta())
sl@0
   504
		{
sl@0
   505
		// verify the delta header
sl@0
   506
		memcpy(&iDelta,p,DeltaHead::Size);
sl@0
   507
		p+=DeltaHead::Size;
sl@0
   508
		int dn = iDelta.iCount;
sl@0
   509
		if (toc1.Length() != Head::Size + DeltaHead::Size + dn * Entry::DeltaSize)
sl@0
   510
			{
sl@0
   511
			memset(&iHeader,0,Head::Size);
sl@0
   512
			Program::Error() << "incomplete toc" << endl;
sl@0
   513
			return;
sl@0
   514
			}
sl@0
   515
		
sl@0
   516
		// find the toc-base
sl@0
   517
		FramePos tocbase(iDelta.iBase + Header::tocoffset);
sl@0
   518
		if (aReloc && aReloc->iHandle.IsTocBase())
sl@0
   519
			tocbase = aReloc->iPos;
sl@0
   520
		Stream toc2(aFrames,tocbase);
sl@0
   521
		if (!toc2.IsGood())
sl@0
   522
			{
sl@0
   523
			memset(&iHeader,0,Head::Size);
sl@0
   524
			Program::Error() << "invalid toc-base address " << tocbase << endl;
sl@0
   525
			return;
sl@0
   526
			}
sl@0
   527
		if (toc2.Type()!=FrameDes::Toc)
sl@0
   528
			{
sl@0
   529
			memset(&iHeader,0,Head::Size);
sl@0
   530
			Program::Error() << "toc-base address " << tocbase << ": refers to non-toc frame"<< endl;
sl@0
   531
			return;
sl@0
   532
			}
sl@0
   533
		
sl@0
   534
		// validate and load the toc-base
sl@0
   535
		void* tocb = toc2.Load(aStream);
sl@0
   536
		const char* p2 = reinterpret_cast<char*>(tocb);
sl@0
   537
		Head headbase;
sl@0
   538
		memcpy(&headbase,p2,Head::Size);
sl@0
   539
		p2+=Head::Size;
sl@0
   540
		if (headbase.IsDelta())
sl@0
   541
			{
sl@0
   542
			memset(&iHeader,0,Head::Size);
sl@0
   543
			Program::Error() << "toc-base is a toc-delta"<< endl;
sl@0
   544
			return;
sl@0
   545
			}
sl@0
   546
		int bn = headbase.iCount;
sl@0
   547
		if (bn > n)
sl@0
   548
			{
sl@0
   549
			memset(&iHeader,0,Head::Size);
sl@0
   550
			Program::Error() << "toc-base is larger than toc"<< endl;
sl@0
   551
			return;
sl@0
   552
			}
sl@0
   553
		Base(p2,bn);
sl@0
   554
		free(tocb);
sl@0
   555
sl@0
   556
		// validate and update with the toc-delta
sl@0
   557
		int last = 0;
sl@0
   558
		while (--dn>=0)
sl@0
   559
			{
sl@0
   560
			Entry e;
sl@0
   561
			memcpy(&e,p,Entry::DeltaSize);
sl@0
   562
			p+=Entry::DeltaSize;
sl@0
   563
			int ix = e.iHandle.Index();
sl@0
   564
			if (ix<=0 || ix > n)
sl@0
   565
				{
sl@0
   566
				memset(&iHeader,0,Head::Size);
sl@0
   567
				Program::Error() << "toc-delta entry " << e.iHandle << " is outside toc"<< endl;
sl@0
   568
				return;
sl@0
   569
				}
sl@0
   570
			if (ix <= last)
sl@0
   571
				{
sl@0
   572
				memset(&iHeader,0,Head::Size);
sl@0
   573
				Program::Error() << "toc-delta entry " << e.iHandle << " is out of order"<< endl;
sl@0
   574
				return;
sl@0
   575
				}
sl@0
   576
			iRep[ix-1] = e;
sl@0
   577
			last = ix;
sl@0
   578
			}
sl@0
   579
		}
sl@0
   580
	else
sl@0
   581
		{
sl@0
   582
		if (toc1.Length() != Head::Size + n * Entry::BaseSize)
sl@0
   583
			{
sl@0
   584
			memset(&iHeader,0,Head::Size);
sl@0
   585
			Program::Error() << "incomplete toc" << endl;
sl@0
   586
			return;
sl@0
   587
			}
sl@0
   588
		Base(p,n);
sl@0
   589
		}
sl@0
   590
	free(toc);
sl@0
   591
sl@0
   592
	// apply the relocation
sl@0
   593
	if (aReloc && !aReloc->iHandle.IsTocBase())
sl@0
   594
		{
sl@0
   595
		int ix=aReloc->iHandle.Index();
sl@0
   596
		if (ix<=0 || ix>n)
sl@0
   597
			Program::Corrupt("invalid index in relocation patch");
sl@0
   598
sl@0
   599
		Entry& e=iRep[ix-1];
sl@0
   600
		if (e.iHandle.Generation()!=aReloc->iHandle.Generation())
sl@0
   601
			Program::Corrupt("incorrect generation in relocation patch");
sl@0
   602
		e.iPos=aReloc->iPos.Pos();
sl@0
   603
		}
sl@0
   604
sl@0
   605
	// count the available entries
sl@0
   606
	int avail=0;
sl@0
   607
	for (int i=0;i<n;++i)
sl@0
   608
		{
sl@0
   609
		if (iRep[i].iHandle.Avail())
sl@0
   610
			++avail;
sl@0
   611
		}
sl@0
   612
	iAvail=avail;
sl@0
   613
sl@0
   614
// verify the available list
sl@0
   615
	Handle link=iHeader.iAvail;
sl@0
   616
	if (!link.IsNull())
sl@0
   617
		{
sl@0
   618
		int ix=link.Index();
sl@0
   619
		if (!link.Avail() || ix<=0 || ix >iHeader.iCount)
sl@0
   620
			{
sl@0
   621
eAvail:		Program::Error() << "corrupt available link in toc header " << link << endl;
sl@0
   622
			return;
sl@0
   623
			}
sl@0
   624
		Entry const* en=&(*this)[ix];
sl@0
   625
		if (en->iHandle!=link)
sl@0
   626
			goto eAvail;
sl@0
   627
		for (;;)
sl@0
   628
			{
sl@0
   629
			if (--avail<0)
sl@0
   630
				{
sl@0
   631
				Program::Error() << "corrupt available list, possible circular reference" << endl;
sl@0
   632
				return;
sl@0
   633
				}
sl@0
   634
			Handle next=en->Link();
sl@0
   635
			if (next.IsNull())
sl@0
   636
				break;
sl@0
   637
			ix=next.Index();
sl@0
   638
			if (!next.Avail() || ix<=0 || ix >iHeader.iCount)
sl@0
   639
				{
sl@0
   640
eLink:			Program::Error() << "corrupt link in toc entry " << link << endl;
sl@0
   641
				return;
sl@0
   642
				}
sl@0
   643
			en=&(*this)[ix];
sl@0
   644
			if (en->iHandle!=next)
sl@0
   645
				goto eLink;
sl@0
   646
			link=next;
sl@0
   647
			}
sl@0
   648
		}
sl@0
   649
	if (avail!=0)
sl@0
   650
		Program::Error() << "corrupt available list: free index leakage" << endl;
sl@0
   651
	}
sl@0
   652
sl@0
   653
ostream& operator<<(ostream& aStream,Toc const& aToc)
sl@0
   654
	{
sl@0
   655
	int all=TheProgram.Option(Program::frame);
sl@0
   656
sl@0
   657
	Toc::Head const& head=aToc.iHeader;
sl@0
   658
	if (TheProgram.Option(Program::compare))
sl@0
   659
		aStream << "Toc: ";
sl@0
   660
	else
sl@0
   661
		aStream << "Toc at " << aToc.iPos << " with ";
sl@0
   662
	aStream << dec << head.iCount << (head.iCount==1 ? " entry: " : " entries: ") \
sl@0
   663
		<< head.iCount-aToc.iAvail  << " allocated, " << aToc.iAvail << " free\n" << hex;
sl@0
   664
	if (!all)
sl@0
   665
		aStream << "root is " << head.Root() << '\n';
sl@0
   666
	else
sl@0
   667
		{
sl@0
   668
		aStream << "first available is " << head.iAvail << '\n';
sl@0
   669
		Toc::Iterator const end=aToc.End();
sl@0
   670
		for (Toc::Iterator iter=aToc.Begin();iter<end;++iter)
sl@0
   671
			{
sl@0
   672
			aStream << (iter->iHandle==head.Root() ? "* " : Margin) << iter->iHandle;
sl@0
   673
			if (iter->iHandle.Avail())
sl@0
   674
				aStream << " free -> " << iter->Link() << '\n';
sl@0
   675
			else if (iter->Pos().Pos()==-1)
sl@0
   676
				aStream << " empty\n";
sl@0
   677
			else
sl@0
   678
				aStream << " alloc at " << iter->Pos() << '\n';
sl@0
   679
			}
sl@0
   680
		}
sl@0
   681
	return aStream << flush;
sl@0
   682
	}
sl@0
   683
sl@0
   684
// Class Stream
sl@0
   685
sl@0
   686
int Stream::Length() const
sl@0
   687
	{
sl@0
   688
	int total=0;
sl@0
   689
	Frames::Iterator f=iFrame;
sl@0
   690
	do	{
sl@0
   691
		int len=f->iDes.Length();
sl@0
   692
		if (len==0)
sl@0
   693
			len=f[1].iPos.Pos()-f[0].iPos.Pos();
sl@0
   694
		total+=len;
sl@0
   695
		} while ((++f)->iDes.Type()==FrameDes::Continuation);
sl@0
   696
	return total;
sl@0
   697
	}
sl@0
   698
sl@0
   699
void* Stream::Load(istream& aStream) const
sl@0
   700
	{
sl@0
   701
	int size = Length();
sl@0
   702
	void* data = malloc(size);
sl@0
   703
	if (data==NULL)
sl@0
   704
		Program::Abort("Out of memory");
sl@0
   705
sl@0
   706
	char* read=reinterpret_cast<char*>(data);
sl@0
   707
	Frames::Iterator f = iFrame;
sl@0
   708
	do
sl@0
   709
		{
sl@0
   710
		FramePos pos=f->iPos;
sl@0
   711
		int len=f++->iDes.Length();
sl@0
   712
		if (len==0)
sl@0
   713
			len=f->iPos.Pos()-pos.Pos();
sl@0
   714
		aStream.seekg(FileOffset(pos).Offset(),ios::beg);
sl@0
   715
		aStream.read(read,len);
sl@0
   716
		read+=len;
sl@0
   717
		} while (f->iDes.Type()==FrameDes::Continuation);
sl@0
   718
sl@0
   719
	return data;
sl@0
   720
	}
sl@0
   721
sl@0
   722
// Class Frames
sl@0
   723
sl@0
   724
Frames::Frames()
sl@0
   725
	: iSize(0),iElements(0),iRep(NULL)
sl@0
   726
	{}
sl@0
   727
sl@0
   728
Frames::~Frames()
sl@0
   729
	{
sl@0
   730
	free(iRep);
sl@0
   731
	}
sl@0
   732
sl@0
   733
void Frames::Add(FramePos aPos,FrameDes aDes)
sl@0
   734
	{
sl@0
   735
	if (iElements==iSize)
sl@0
   736
		{
sl@0
   737
		iSize=iSize==0 ? 128 : iSize+iSize;
sl@0
   738
		void* rep=realloc(iRep,iSize*sizeof(*iRep));
sl@0
   739
		if (rep==NULL)
sl@0
   740
			Program::Abort("Out of memory");
sl@0
   741
		iRep=(Element*)rep;
sl@0
   742
		}
sl@0
   743
	Element& element=iRep[iElements++];
sl@0
   744
	element.iPos=aPos;
sl@0
   745
	element.iDes=aDes;
sl@0
   746
	}
sl@0
   747
sl@0
   748
void Frames::Complete()
sl@0
   749
//
sl@0
   750
// add a terminating entry
sl@0
   751
//
sl@0
   752
	{
sl@0
   753
	Add(0,0);
sl@0
   754
	--iElements;
sl@0
   755
	}
sl@0
   756
sl@0
   757
Frames::Iterator Frames::Find(FramePos aPos) const
sl@0
   758
	{
sl@0
   759
	return (Element const*)bsearch(&aPos,iRep,iElements,sizeof(*iRep),Compare);
sl@0
   760
	}
sl@0
   761
sl@0
   762
int Frames::Compare(void const* aLeft,void const* aRight)
sl@0
   763
	{
sl@0
   764
	int left=static_cast<FramePos const*>(aLeft)->Pos();
sl@0
   765
	int right=static_cast<Element const*>(aRight)->iPos.Pos();
sl@0
   766
	if (left<right)
sl@0
   767
		return -1;
sl@0
   768
	if (left>right)
sl@0
   769
		return 1;
sl@0
   770
	return 0;
sl@0
   771
	}
sl@0
   772
sl@0
   773
// Header
sl@0
   774
sl@0
   775
FramePos Header::Toc() const
sl@0
   776
	{
sl@0
   777
	return tocoffset+(!Dirty() && iToc.iZero==0 ? iToc.iPos : iBackupToc>>backupshift);
sl@0
   778
	}
sl@0
   779
sl@0
   780
Header::Reloc const* Header::GetReloc() const
sl@0
   781
	{
sl@0
   782
	return (Dirty() || iToc.iZero==0) ? NULL : reinterpret_cast<Reloc const*>(&iReloc);
sl@0
   783
	}
sl@0
   784
sl@0
   785
ostream& operator<<(ostream& aStream,Header const& aHeader)
sl@0
   786
	{
sl@0
   787
	aStream << "Header is " << (aHeader.Dirty() ? "dirty" : "clean");
sl@0
   788
	Header::Reloc const* reloc=aHeader.GetReloc();
sl@0
   789
	if (reloc!=NULL)
sl@0
   790
		{
sl@0
   791
		aStream << "\npending relocation of ";
sl@0
   792
		if (reloc->iHandle.IsTocBase())
sl@0
   793
			aStream << "toc-base";
sl@0
   794
		else
sl@0
   795
			aStream << "stream " << StreamId(reloc->iHandle);
sl@0
   796
		aStream << " to " << reloc->iPos;
sl@0
   797
		}
sl@0
   798
	return aStream << "\n\n" << flush;
sl@0
   799
	}
sl@0
   800
sl@0
   801
// FileOffset
sl@0
   802
sl@0
   803
FileOffset::FileOffset(FramePos aPos)
sl@0
   804
// calculate the file offset for a streampos
sl@0
   805
	{
sl@0
   806
	int pos=aPos.Pos();
sl@0
   807
	int n=pos>>FrameDes::FullShift;
sl@0
   808
	pos+=FrameDes::Size*n+FrameDes::First;
sl@0
   809
	iValue=pos;
sl@0
   810
	}
sl@0
   811
sl@0
   812
FileOffset::operator FramePos() const
sl@0
   813
	{
sl@0
   814
	int pos=iValue-FrameDes::First;
sl@0
   815
	int n=pos/FrameDes::Interval;
sl@0
   816
	pos-=n*FrameDes::Size;
sl@0
   817
	return FramePos(pos);
sl@0
   818
	}
sl@0
   819
sl@0
   820
ostream& operator<<(ostream& aStream,FileOffset anOffset)
sl@0
   821
	{
sl@0
   822
	return aStream << setw(StoreFile::OutWidth) << anOffset.iValue;
sl@0
   823
	}
sl@0
   824
sl@0
   825
// Handle
sl@0
   826
sl@0
   827
ostream& operator<<(ostream& aStream,Handle aHandle)
sl@0
   828
	{
sl@0
   829
	if (aHandle.IsNull())
sl@0
   830
		aStream << "Null";
sl@0
   831
	else
sl@0
   832
		aStream << setw(6) << aHandle.Index() << ':' << aHandle.Generation();
sl@0
   833
	return aStream;
sl@0
   834
	}
sl@0
   835
sl@0
   836
// FramePos
sl@0
   837
sl@0
   838
ostream& operator<<(ostream& aStream,FramePos aPos)
sl@0
   839
	{
sl@0
   840
	return aStream << setw(StoreFile::OutWidth) << aPos.iValue << '[' << FileOffset(aPos) << ']';
sl@0
   841
	}
sl@0
   842
sl@0
   843
// FrameDes
sl@0
   844
sl@0
   845
istream& operator>>(istream& aStream,FrameDes& aFrame)
sl@0
   846
	{
sl@0
   847
	return aStream.read((char*)&aFrame,FrameDes::Size);
sl@0
   848
	}
sl@0
   849
sl@0
   850
ostream& operator<<(ostream& aStream,FrameDes aFrame)
sl@0
   851
	{
sl@0
   852
	static char const* FrameType[]={"free","data","toc","continuation"};
sl@0
   853
	aStream << FrameType[aFrame.Type()] << " (";
sl@0
   854
	int length=aFrame.Length();
sl@0
   855
	if (length==0)
sl@0
   856
		aStream << "full";
sl@0
   857
	else
sl@0
   858
		aStream << dec << length << hex;
sl@0
   859
	return aStream << ')';
sl@0
   860
	}
sl@0
   861