sl@0: // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: #include "PFSDUMP.H" sl@0: sl@0: // entry point sl@0: sl@0: int main(int argc,char *argv[]) sl@0: { sl@0: if (TheProgram.ProcessCommandLine(argc,argv)) sl@0: { sl@0: TheProgram.ExplainUsage(); sl@0: return 1; sl@0: } sl@0: TheProgram.Run(); sl@0: return 0; sl@0: } sl@0: sl@0: // Class Program sl@0: sl@0: class Program TheProgram; sl@0: sl@0: Program::Program() sl@0: : iFile(NULL),iOptions(0),iBin(&iBinBuf),iErrors(0) sl@0: { sl@0: cout << setfill('0'); sl@0: } sl@0: sl@0: void Program::Information() sl@0: { sl@0: if (!Option(quiet)) sl@0: cout << "\nEPOC PermanentFileStore Dump Utility Version " << MajorVersion << '.' \ sl@0: << setw(2) << MinorVersion << "(build " << setw(3) << setfill('0') << Build \ sl@0: << ")\nCopyright (c) Symbian Software Limited 1997-2004. All rights reserved.\n\n" << flush; sl@0: cout << hex; sl@0: cerr << hex; sl@0: } sl@0: sl@0: void Program::ExplainUsage() sl@0: { sl@0: Information(); sl@0: cout << "Usage: PFSDUMP [options] storefile\n" \ sl@0: " -v verbose: dump contents\n" \ sl@0: " -f generate a frame dump\n" \ sl@0: " -r use nth previous store revision\n" \ sl@0: " -c format output for comparison (not for frame dumps)\n" sl@0: " -q quiet: suppress logo\n" << flush; sl@0: } sl@0: sl@0: int Program::ProcessCommandLine(int argc,char ** argv) sl@0: { sl@0: if (argc<2) sl@0: return 1; sl@0: sl@0: int options=0; sl@0: while (--argc>0) sl@0: { sl@0: istrstream arg(*++argv); sl@0: int c=arg.get(); sl@0: if (c=='/' || c=='-') sl@0: { sl@0: while ((c=arg.get())!=EOF) sl@0: { sl@0: switch (c) sl@0: { sl@0: case 'c': case 'C': sl@0: options|=compare; sl@0: break; sl@0: case 'f': case 'F': sl@0: options|=frame; sl@0: break; sl@0: case 'q': case 'Q': sl@0: options|=quiet; sl@0: break; sl@0: case 'r': case 'R': sl@0: arg >> iTocRevision; sl@0: if (arg.fail() || iTocRevision<0) sl@0: return 1; sl@0: break; sl@0: case 'v': case 'V': sl@0: options|=verbose; sl@0: break; sl@0: default: // unrecognised option sl@0: return 1; sl@0: } sl@0: } sl@0: } sl@0: else if (iFile!=NULL) sl@0: return 1; // two filenames? sl@0: else sl@0: iFile=arg.str(); sl@0: } sl@0: if (options&frame) sl@0: options&=~compare; sl@0: iOptions=options; sl@0: return iFile==NULL; sl@0: } sl@0: sl@0: void Program::Run() sl@0: // sl@0: // The main part of the program sl@0: // sl@0: { sl@0: Information(); sl@0: StoreFile store(iFile); sl@0: if (iErrors) sl@0: cerr << endl; sl@0: if (store.Empty()) sl@0: { sl@0: cout << "Store is empty" << endl; sl@0: return; sl@0: } sl@0: if (Option(frame)) sl@0: { sl@0: store.EmitHeader(); sl@0: store.EmitFrames(); sl@0: } sl@0: store.EmitToc(); sl@0: if (!Option(frame)) sl@0: store.EmitStreams(); sl@0: } sl@0: sl@0: void Program::Abort(char const* aMessage) sl@0: { sl@0: cerr << aMessage << endl; sl@0: exit(3); sl@0: } sl@0: sl@0: void Program::Corrupt(char const* aMessage) sl@0: // sl@0: // terminate after detecting a fatal corruption error sl@0: // sl@0: { sl@0: cerr << "\nfatal error: " << aMessage << "\ncannot continue\n" << flush; sl@0: exit(2); sl@0: } sl@0: sl@0: ostream& Program::Error() sl@0: { sl@0: ++TheProgram.iErrors; sl@0: return cerr << "error: "; sl@0: } sl@0: sl@0: ostream& Program::Warning() sl@0: { sl@0: ++TheProgram.iErrors; sl@0: return cerr << "warning: "; sl@0: } sl@0: sl@0: // Bin stream buffer sl@0: sl@0: binbuf::binbuf() sl@0: : streambuf() sl@0: {} sl@0: sl@0: int binbuf::overflow(int) sl@0: {return 0;} sl@0: sl@0: int binbuf::underflow() sl@0: {return EOF;} sl@0: sl@0: // Class StoreFile sl@0: sl@0: int StoreFile::OutWidth; sl@0: sl@0: StoreFile::StoreFile(char const* aFile) sl@0: { sl@0: #ifdef __MSVCDOTNET__ sl@0: iFile.open(aFile, ios::binary); sl@0: #else //!__MSVCDOTNET__ sl@0: iFile.open(aFile, ios::binary | ios::nocreate); sl@0: #endif //__MSVCDOTNET__ sl@0: if (!iFile) sl@0: Program::Abort("Unable to open file or file does not exist"); sl@0: // sl@0: unsigned long uid; sl@0: iFile.read((char*)&uid,sizeof(uid)); sl@0: iFile.seekg(0,ios::end); sl@0: int size=iFile.tellg(); sl@0: if (size>=4; sl@0: } sl@0: OutWidth=width; sl@0: } sl@0: // sl@0: cout << "Dumping " << aFile << "\n\n"; sl@0: // sl@0: iFile.seekg(Header::Offset,ios::beg); sl@0: iFile.read((char*)&iHeader,Header::Size); sl@0: // sl@0: if (Empty()) sl@0: return; sl@0: LoadFrames(); sl@0: LoadToc(); sl@0: } sl@0: sl@0: StoreFile::~StoreFile() sl@0: { sl@0: iFile.close(); sl@0: } sl@0: sl@0: void StoreFile::LoadFrames() sl@0: { sl@0: FrameDes frame; sl@0: int offset=FrameDes::First; sl@0: int full=FrameDes::First+FrameDes::Interval; sl@0: int diff=FrameDes::First; sl@0: sl@0: while (offset-FrameDes::SizeiSize) sl@0: { sl@0: Program::Warning() << "incomplete link at " << FramePos(offset-diff) << endl; sl@0: break; sl@0: } sl@0: iFile.seekg(offset-FrameDes::Size,ios::beg); sl@0: iFile >> frame; sl@0: iFrames.Add(FramePos(offset-diff),frame); sl@0: int length=frame.Length(); sl@0: if (length==0) sl@0: { sl@0: if (full>iSize && offset>iSize) sl@0: Program::Warning() << "incomplete frame at " << FramePos(offset-diff) << endl; sl@0: offset=full; sl@0: } sl@0: else sl@0: { sl@0: int newoffset=offset+length+FrameDes::Size; sl@0: if (newoffset>=full || newoffset-FrameDes::Size>iSize) sl@0: { sl@0: Program::Error() << "bad link at " << FramePos(offset-diff) << ", skipping to next anchor link" << endl; sl@0: offset=full; sl@0: } sl@0: else sl@0: { sl@0: offset=newoffset; sl@0: if (full-offset<=FrameDes::Size) sl@0: offset=full; sl@0: } sl@0: } sl@0: } sl@0: iFrames.Complete(); sl@0: } sl@0: sl@0: void StoreFile::LoadToc() sl@0: { sl@0: FramePos toc=iHeader.Toc(); sl@0: Stream stream(iFrames,toc); sl@0: if (!stream.IsGood()) sl@0: { sl@0: Program::Error() << "invalid toc address " << toc << endl; sl@0: return; sl@0: } sl@0: if (stream.Type()!=FrameDes::Toc) sl@0: { sl@0: Program::Error() << "toc address " << toc << ": refers to non-toc frame"<< endl; sl@0: return; sl@0: } sl@0: sl@0: // find the requested store revision sl@0: Frames::Iterator f=stream.Frame(); sl@0: Frames::Iterator const first=iFrames.Begin(); sl@0: for (int rev=TheProgram.TocRevision();rev;--rev) sl@0: { sl@0: do { sl@0: if (--fiDes.Type()!=FrameDes::Toc); sl@0: } sl@0: sl@0: iToc.Load(iFile,iFrames,f,iHeader.GetReloc()); sl@0: sl@0: // verify the Toc stream references sl@0: Toc::Iterator const end=iToc.End(); sl@0: for (Toc::Iterator iter=iToc.Begin();iteriHandle.IsNull()) sl@0: Program::Error() << "missing entry in toc-delta for index " << (1+iter-iToc.Begin()) << endl; sl@0: else if (!iter->iHandle.Avail() && iter->Pos().Pos()>=0) sl@0: { sl@0: f=iFrames.Find(iter->Pos()); sl@0: if (f==NULL) sl@0: Program::Error() << "invalid stream reference in toc entry " << iter->iHandle << endl; sl@0: else if (iter->Pos().Pos()>=toc.Pos()) sl@0: Program::Error() << "virtual stream reference in toc entry " << iter->iHandle << endl; sl@0: else if (f->iDes.Type()!=FrameDes::Data) sl@0: Program::Error() << "toc entry " << iter->iHandle << ": refers to non-data frame" << endl; sl@0: } sl@0: } sl@0: } sl@0: sl@0: void StoreFile::EmitHeader() sl@0: { sl@0: cout << iHeader; sl@0: } sl@0: sl@0: void StoreFile::EmitFrames() sl@0: { sl@0: int verbose=TheProgram.Option(Program::verbose); sl@0: Frames::Iterator const end=iFrames.End(); sl@0: for (Frames::Iterator iter=iFrames.Begin();iteriPos; sl@0: cout << "Frame at " << pos << ": " << iter->iDes << endl; sl@0: if (!verbose) sl@0: continue; sl@0: sl@0: // dump contents sl@0: int length=iter->iDes.Length(); sl@0: if (length==0) sl@0: { // full frame sl@0: if (iter+1==end) // no more sl@0: continue; sl@0: length=iter[1].iPos.Pos()-pos.Pos(); sl@0: } sl@0: HexDump hex; sl@0: iFile.seekg(FileOffset(pos).Offset(),ios::beg); sl@0: hex.Dump(iFile,length); sl@0: hex.Flush(); sl@0: cout << endl; sl@0: } sl@0: cout << endl; sl@0: } sl@0: sl@0: void StoreFile::EmitToc() sl@0: { sl@0: cout << iToc; sl@0: } sl@0: sl@0: void StoreFile::EmitStreams() sl@0: { sl@0: int verbose=TheProgram.Option(Program::verbose); sl@0: cout << endl; sl@0: Toc::Iterator const end=iToc.End(); sl@0: for (Toc::Iterator iter=iToc.Begin();iteriHandle.Avail()) sl@0: { sl@0: cout << "Stream " << iter->iHandle; sl@0: if (iter->Pos().Pos()==-1) sl@0: { sl@0: cout << " is empty\n"; sl@0: continue; sl@0: } sl@0: if (!TheProgram.Option(Program::compare)) sl@0: cout << " at " << iter->Pos(); sl@0: Stream stream(iFrames,iter->Pos()); sl@0: if (!stream.IsGood() || stream.Type()!=FrameDes::Data) sl@0: { sl@0: cout << " is invalid\n"; sl@0: continue; sl@0: } sl@0: int len=stream.Length(); sl@0: cout << ", " << dec << len << hex << " byte" << (len==1 ? "\n" : "s\n"); sl@0: if (!verbose) sl@0: continue; sl@0: sl@0: // dump contents sl@0: HexDump hex; sl@0: Frames::Iterator f=stream.Frame(); sl@0: do sl@0: { sl@0: FramePos pos=f->iPos; sl@0: int len=f++->iDes.Length(); sl@0: if (len==0) sl@0: len=f->iPos.Pos()-pos.Pos(); sl@0: iFile.seekg(FileOffset(pos).Offset(),ios::beg); sl@0: hex.Dump(iFile,len); sl@0: } while (f->iDes.Type()==FrameDes::Continuation); sl@0: hex.Flush(); sl@0: cout << endl; sl@0: } sl@0: } sl@0: } sl@0: sl@0: // Class HexDump sl@0: sl@0: HexDump::HexDump() sl@0: : iPos(0),iByte(0) sl@0: { sl@0: Reset(); sl@0: } sl@0: sl@0: HexDump::~HexDump() sl@0: { sl@0: Flush(); sl@0: } sl@0: sl@0: void HexDump::Reset() sl@0: { sl@0: memset(iChar,' ',HexInterval+3); sl@0: iChar[HexInterval+3]=0; sl@0: iPtr=&iChar[2]; sl@0: } sl@0: sl@0: void HexDump::Dump(istream& aStream,int aBytes) sl@0: { sl@0: while (--aBytes>=0 && !aStream.eof()) sl@0: { sl@0: if (iByte==0) sl@0: cout << Margin << FileOffset(iPos) << ':'; sl@0: int c=aStream.get(); sl@0: cout << ' ' << setw(2) << c; sl@0: *iPtr++=char(isprint(c) ? c : '.'); sl@0: if (++iByte==HexInterval/2) sl@0: { sl@0: cout << ' '; sl@0: ++iPtr; sl@0: } sl@0: else if (iByte==HexInterval) sl@0: Flush(); sl@0: } sl@0: } sl@0: sl@0: void HexDump::Flush() sl@0: { sl@0: if (iByte) sl@0: { sl@0: for (int ii=iByte;iiiHandle=i; // set the index part sl@0: memcpy((char*)e+Entry::BaseRedundant,aPtr,Entry::BaseSize); sl@0: aPtr+=Entry::BaseSize; sl@0: } sl@0: } sl@0: sl@0: void Toc::Load(istream& aStream,Frames const& aFrames,Frames::Iterator aFrame,Header::Reloc const* aReloc) sl@0: { sl@0: iPos = aFrame->iPos; sl@0: sl@0: Stream toc1(aFrame); sl@0: void* toc = toc1.Load(aStream); sl@0: const char* p = reinterpret_cast(toc); sl@0: memcpy(&iHeader,p,Head::Size); sl@0: p+=Head::Size; sl@0: int n = iHeader.iCount; sl@0: if (n < 0) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "corrupt toc" << endl; sl@0: return; sl@0: } sl@0: iRep=static_cast(malloc(n*sizeof(Entry))); sl@0: if (iRep==NULL) sl@0: Program::Abort("Out of memory"); sl@0: sl@0: if (iHeader.IsDelta()) sl@0: { sl@0: // verify the delta header sl@0: memcpy(&iDelta,p,DeltaHead::Size); sl@0: p+=DeltaHead::Size; sl@0: int dn = iDelta.iCount; sl@0: if (toc1.Length() != Head::Size + DeltaHead::Size + dn * Entry::DeltaSize) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "incomplete toc" << endl; sl@0: return; sl@0: } sl@0: sl@0: // find the toc-base sl@0: FramePos tocbase(iDelta.iBase + Header::tocoffset); sl@0: if (aReloc && aReloc->iHandle.IsTocBase()) sl@0: tocbase = aReloc->iPos; sl@0: Stream toc2(aFrames,tocbase); sl@0: if (!toc2.IsGood()) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "invalid toc-base address " << tocbase << endl; sl@0: return; sl@0: } sl@0: if (toc2.Type()!=FrameDes::Toc) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "toc-base address " << tocbase << ": refers to non-toc frame"<< endl; sl@0: return; sl@0: } sl@0: sl@0: // validate and load the toc-base sl@0: void* tocb = toc2.Load(aStream); sl@0: const char* p2 = reinterpret_cast(tocb); sl@0: Head headbase; sl@0: memcpy(&headbase,p2,Head::Size); sl@0: p2+=Head::Size; sl@0: if (headbase.IsDelta()) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "toc-base is a toc-delta"<< endl; sl@0: return; sl@0: } sl@0: int bn = headbase.iCount; sl@0: if (bn > n) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "toc-base is larger than toc"<< endl; sl@0: return; sl@0: } sl@0: Base(p2,bn); sl@0: free(tocb); sl@0: sl@0: // validate and update with the toc-delta sl@0: int last = 0; sl@0: while (--dn>=0) sl@0: { sl@0: Entry e; sl@0: memcpy(&e,p,Entry::DeltaSize); sl@0: p+=Entry::DeltaSize; sl@0: int ix = e.iHandle.Index(); sl@0: if (ix<=0 || ix > n) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "toc-delta entry " << e.iHandle << " is outside toc"<< endl; sl@0: return; sl@0: } sl@0: if (ix <= last) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "toc-delta entry " << e.iHandle << " is out of order"<< endl; sl@0: return; sl@0: } sl@0: iRep[ix-1] = e; sl@0: last = ix; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: if (toc1.Length() != Head::Size + n * Entry::BaseSize) sl@0: { sl@0: memset(&iHeader,0,Head::Size); sl@0: Program::Error() << "incomplete toc" << endl; sl@0: return; sl@0: } sl@0: Base(p,n); sl@0: } sl@0: free(toc); sl@0: sl@0: // apply the relocation sl@0: if (aReloc && !aReloc->iHandle.IsTocBase()) sl@0: { sl@0: int ix=aReloc->iHandle.Index(); sl@0: if (ix<=0 || ix>n) sl@0: Program::Corrupt("invalid index in relocation patch"); sl@0: sl@0: Entry& e=iRep[ix-1]; sl@0: if (e.iHandle.Generation()!=aReloc->iHandle.Generation()) sl@0: Program::Corrupt("incorrect generation in relocation patch"); sl@0: e.iPos=aReloc->iPos.Pos(); sl@0: } sl@0: sl@0: // count the available entries sl@0: int avail=0; sl@0: for (int i=0;iiHeader.iCount) sl@0: { sl@0: eAvail: Program::Error() << "corrupt available link in toc header " << link << endl; sl@0: return; sl@0: } sl@0: Entry const* en=&(*this)[ix]; sl@0: if (en->iHandle!=link) sl@0: goto eAvail; sl@0: for (;;) sl@0: { sl@0: if (--avail<0) sl@0: { sl@0: Program::Error() << "corrupt available list, possible circular reference" << endl; sl@0: return; sl@0: } sl@0: Handle next=en->Link(); sl@0: if (next.IsNull()) sl@0: break; sl@0: ix=next.Index(); sl@0: if (!next.Avail() || ix<=0 || ix >iHeader.iCount) sl@0: { sl@0: eLink: Program::Error() << "corrupt link in toc entry " << link << endl; sl@0: return; sl@0: } sl@0: en=&(*this)[ix]; sl@0: if (en->iHandle!=next) sl@0: goto eLink; sl@0: link=next; sl@0: } sl@0: } sl@0: if (avail!=0) sl@0: Program::Error() << "corrupt available list: free index leakage" << endl; sl@0: } sl@0: sl@0: ostream& operator<<(ostream& aStream,Toc const& aToc) sl@0: { sl@0: int all=TheProgram.Option(Program::frame); sl@0: sl@0: Toc::Head const& head=aToc.iHeader; sl@0: if (TheProgram.Option(Program::compare)) sl@0: aStream << "Toc: "; sl@0: else sl@0: aStream << "Toc at " << aToc.iPos << " with "; sl@0: aStream << dec << head.iCount << (head.iCount==1 ? " entry: " : " entries: ") \ sl@0: << head.iCount-aToc.iAvail << " allocated, " << aToc.iAvail << " free\n" << hex; sl@0: if (!all) sl@0: aStream << "root is " << head.Root() << '\n'; sl@0: else sl@0: { sl@0: aStream << "first available is " << head.iAvail << '\n'; sl@0: Toc::Iterator const end=aToc.End(); sl@0: for (Toc::Iterator iter=aToc.Begin();iteriHandle==head.Root() ? "* " : Margin) << iter->iHandle; sl@0: if (iter->iHandle.Avail()) sl@0: aStream << " free -> " << iter->Link() << '\n'; sl@0: else if (iter->Pos().Pos()==-1) sl@0: aStream << " empty\n"; sl@0: else sl@0: aStream << " alloc at " << iter->Pos() << '\n'; sl@0: } sl@0: } sl@0: return aStream << flush; sl@0: } sl@0: sl@0: // Class Stream sl@0: sl@0: int Stream::Length() const sl@0: { sl@0: int total=0; sl@0: Frames::Iterator f=iFrame; sl@0: do { sl@0: int len=f->iDes.Length(); sl@0: if (len==0) sl@0: len=f[1].iPos.Pos()-f[0].iPos.Pos(); sl@0: total+=len; sl@0: } while ((++f)->iDes.Type()==FrameDes::Continuation); sl@0: return total; sl@0: } sl@0: sl@0: void* Stream::Load(istream& aStream) const sl@0: { sl@0: int size = Length(); sl@0: void* data = malloc(size); sl@0: if (data==NULL) sl@0: Program::Abort("Out of memory"); sl@0: sl@0: char* read=reinterpret_cast(data); sl@0: Frames::Iterator f = iFrame; sl@0: do sl@0: { sl@0: FramePos pos=f->iPos; sl@0: int len=f++->iDes.Length(); sl@0: if (len==0) sl@0: len=f->iPos.Pos()-pos.Pos(); sl@0: aStream.seekg(FileOffset(pos).Offset(),ios::beg); sl@0: aStream.read(read,len); sl@0: read+=len; sl@0: } while (f->iDes.Type()==FrameDes::Continuation); sl@0: sl@0: return data; sl@0: } sl@0: sl@0: // Class Frames sl@0: sl@0: Frames::Frames() sl@0: : iSize(0),iElements(0),iRep(NULL) sl@0: {} sl@0: sl@0: Frames::~Frames() sl@0: { sl@0: free(iRep); sl@0: } sl@0: sl@0: void Frames::Add(FramePos aPos,FrameDes aDes) sl@0: { sl@0: if (iElements==iSize) sl@0: { sl@0: iSize=iSize==0 ? 128 : iSize+iSize; sl@0: void* rep=realloc(iRep,iSize*sizeof(*iRep)); sl@0: if (rep==NULL) sl@0: Program::Abort("Out of memory"); sl@0: iRep=(Element*)rep; sl@0: } sl@0: Element& element=iRep[iElements++]; sl@0: element.iPos=aPos; sl@0: element.iDes=aDes; sl@0: } sl@0: sl@0: void Frames::Complete() sl@0: // sl@0: // add a terminating entry sl@0: // sl@0: { sl@0: Add(0,0); sl@0: --iElements; sl@0: } sl@0: sl@0: Frames::Iterator Frames::Find(FramePos aPos) const sl@0: { sl@0: return (Element const*)bsearch(&aPos,iRep,iElements,sizeof(*iRep),Compare); sl@0: } sl@0: sl@0: int Frames::Compare(void const* aLeft,void const* aRight) sl@0: { sl@0: int left=static_cast(aLeft)->Pos(); sl@0: int right=static_cast(aRight)->iPos.Pos(); sl@0: if (leftright) sl@0: return 1; sl@0: return 0; sl@0: } sl@0: sl@0: // Header sl@0: sl@0: FramePos Header::Toc() const sl@0: { sl@0: return tocoffset+(!Dirty() && iToc.iZero==0 ? iToc.iPos : iBackupToc>>backupshift); sl@0: } sl@0: sl@0: Header::Reloc const* Header::GetReloc() const sl@0: { sl@0: return (Dirty() || iToc.iZero==0) ? NULL : reinterpret_cast(&iReloc); sl@0: } sl@0: sl@0: ostream& operator<<(ostream& aStream,Header const& aHeader) sl@0: { sl@0: aStream << "Header is " << (aHeader.Dirty() ? "dirty" : "clean"); sl@0: Header::Reloc const* reloc=aHeader.GetReloc(); sl@0: if (reloc!=NULL) sl@0: { sl@0: aStream << "\npending relocation of "; sl@0: if (reloc->iHandle.IsTocBase()) sl@0: aStream << "toc-base"; sl@0: else sl@0: aStream << "stream " << StreamId(reloc->iHandle); sl@0: aStream << " to " << reloc->iPos; sl@0: } sl@0: return aStream << "\n\n" << flush; sl@0: } sl@0: sl@0: // FileOffset sl@0: sl@0: FileOffset::FileOffset(FramePos aPos) sl@0: // calculate the file offset for a streampos sl@0: { sl@0: int pos=aPos.Pos(); sl@0: int n=pos>>FrameDes::FullShift; sl@0: pos+=FrameDes::Size*n+FrameDes::First; sl@0: iValue=pos; sl@0: } sl@0: sl@0: FileOffset::operator FramePos() const sl@0: { sl@0: int pos=iValue-FrameDes::First; sl@0: int n=pos/FrameDes::Interval; sl@0: pos-=n*FrameDes::Size; sl@0: return FramePos(pos); sl@0: } sl@0: sl@0: ostream& operator<<(ostream& aStream,FileOffset anOffset) sl@0: { sl@0: return aStream << setw(StoreFile::OutWidth) << anOffset.iValue; sl@0: } sl@0: sl@0: // Handle sl@0: sl@0: ostream& operator<<(ostream& aStream,Handle aHandle) sl@0: { sl@0: if (aHandle.IsNull()) sl@0: aStream << "Null"; sl@0: else sl@0: aStream << setw(6) << aHandle.Index() << ':' << aHandle.Generation(); sl@0: return aStream; sl@0: } sl@0: sl@0: // FramePos sl@0: sl@0: ostream& operator<<(ostream& aStream,FramePos aPos) sl@0: { sl@0: return aStream << setw(StoreFile::OutWidth) << aPos.iValue << '[' << FileOffset(aPos) << ']'; sl@0: } sl@0: sl@0: // FrameDes sl@0: sl@0: istream& operator>>(istream& aStream,FrameDes& aFrame) sl@0: { sl@0: return aStream.read((char*)&aFrame,FrameDes::Size); sl@0: } sl@0: sl@0: ostream& operator<<(ostream& aStream,FrameDes aFrame) sl@0: { sl@0: static char const* FrameType[]={"free","data","toc","continuation"}; sl@0: aStream << FrameType[aFrame.Type()] << " ("; sl@0: int length=aFrame.Length(); sl@0: if (length==0) sl@0: aStream << "full"; sl@0: else sl@0: aStream << dec << length << hex; sl@0: return aStream << ')'; sl@0: } sl@0: