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