/*
<img src="gif/fildir.gif">
*/
//End_Html
#include "Riostream.h"
#include "Strlen.h"
#include "TDirectoryFile.h"
#include "TFile.h"
#include "TBufferFile.h"
#include "TMapFile.h"
#include "TClassTable.h"
#include "TInterpreter.h"
#include "THashList.h"
#include "TBrowser.h"
#include "TFree.h"
#include "TKey.h"
#include "TStreamerInfo.h"
#include "TROOT.h"
#include "TError.h"
#include "Bytes.h"
#include "TClass.h"
#include "TRegexp.h"
#include "TSystem.h"
#include "TStreamerElement.h"
#include "TProcessUUID.h"
#include "TVirtualMutex.h"
const UInt_t kIsBigFile = BIT(16);
const Int_t kMaxLen = 2048;
ClassImp(TDirectoryFile)
TDirectoryFile::TDirectoryFile() : TDirectory()
, fModified(kFALSE), fWritable(kFALSE), fNbytesKeys(0), fNbytesName(0)
, fBufferSize(0), fSeekDir(0), fSeekParent(0), fSeekKeys(0)
, fFile(0), fKeys(0)
{
}
TDirectoryFile::TDirectoryFile(const char *name, const char *title, Option_t *classname, TDirectory* initMotherDir)
: TDirectory()
, fModified(kFALSE), fWritable(kFALSE), fNbytesKeys(0), fNbytesName(0)
, fBufferSize(0), fSeekDir(0), fSeekParent(0), fSeekKeys(0)
, fFile(0), fKeys(0)
{
fName = name;
fTitle = title;
if (initMotherDir==0) initMotherDir = gDirectory;
if (strchr(name,'/')) {
::Error("TDirectoryFile","directory name (%s) cannot contain a slash", name);
gDirectory = 0;
return;
}
if (strlen(GetName()) == 0) {
::Error("TDirectoryFile","directory name cannot be \"\"");
gDirectory = 0;
return;
}
Build(initMotherDir ? initMotherDir->GetFile() : 0, initMotherDir);
TDirectory* motherdir = GetMotherDir();
TFile* f = GetFile();
if ((motherdir==0) || (f==0)) return;
if (!f->IsWritable()) return;
if (motherdir->GetKey(name)) {
Error("TDirectoryFile","An object with name %s exists already", name);
return;
}
TClass *cl = 0;
if (strlen(classname) != 0) {
cl = TClass::GetClass(classname);
if (!cl) {
Error("TDirectoryFile","Invalid class name: %s",classname);
return;
}
} else {
cl = IsA();
}
fBufferSize = 0;
fWritable = kTRUE;
Init(cl);
fModified = kFALSE;
R__LOCKGUARD2(gROOTMutex);
gROOT->GetUUIDs()->AddUUID(fUUID,this);
}
void TDirectoryFile::Init(TClass *cl)
{
TFile* f = GetFile();
if (f->IsBinary()) {
if (cl==0) {
cl = IsA();
}
TDirectory* motherdir = GetMotherDir();
fSeekParent = f->GetSeekDir();
Int_t nbytes = TDirectoryFile::Sizeof();
TKey *key = new TKey(fName,fTitle,cl,nbytes,motherdir);
fNbytesName = key->GetKeylen();
fSeekDir = key->GetSeekKey();
if (fSeekDir == 0) return;
char *buffer = key->GetBuffer();
TDirectoryFile::FillBuffer(buffer);
Int_t cycle = motherdir ? motherdir->AppendKey(key) : 0;
key->WriteFile(cycle);
} else {
fSeekParent = 0;
fNbytesName = 0;
fSeekDir = f->DirCreateEntry(this);
if (fSeekDir == 0) return;
}
}
TDirectoryFile::TDirectoryFile(const TDirectoryFile & directory) : TDirectory(directory)
, fModified(kFALSE), fWritable(kFALSE), fNbytesKeys(0), fNbytesName(0)
, fBufferSize(0), fSeekDir(0), fSeekParent(0), fSeekKeys(0)
, fFile(0), fKeys(0)
{
((TDirectoryFile&)directory).Copy(*this);
}
TDirectoryFile::~TDirectoryFile()
{
if (fKeys) {
fKeys->Delete("slow");
SafeDelete(fKeys);
}
CleanTargets();
if (fList) {
fList->Delete("slow");
SafeDelete(fList);
}
if (gDebug) {
Info("~TDirectoryFile", "dtor called for %s", GetName());
}
}
void TDirectoryFile::Append(TObject *obj, Bool_t replace )
{
if (obj == 0 || fList == 0) return;
TDirectory::Append(obj,replace);
if (!fMother) return;
if (fMother->IsA() == TMapFile::Class()) {
TMapFile *mfile = (TMapFile*)fMother;
mfile->Add(obj);
}
}
Int_t TDirectoryFile::AppendKey(TKey *key)
{
fModified = kTRUE;
key->SetMotherDir(this);
TKey *oldkey = (TKey*)fKeys->FindObject(key->GetName());
if (!oldkey) {
fKeys->Add(key);
return 1;
}
TObjLink *lnk = fKeys->FirstLink();
while (lnk) {
oldkey = (TKey*)lnk->GetObject();
if (!strcmp(oldkey->GetName(), key->GetName()))
break;
lnk = lnk->Next();
}
fKeys->AddBefore(lnk, key);
return oldkey->GetCycle() + 1;
}
void TDirectoryFile::Browse(TBrowser *b)
{
TString name;
if (b) {
TObject *obj = 0;
TIter nextin(fList);
TKey *key = 0, *keyo = 0;
TIter next(fKeys);
cd();
while ((obj = nextin())) {
if (fKeys->FindObject(obj->GetName())) continue;
b->Add(obj, obj->GetName());
}
while ((key = (TKey *) next())) {
int skip = 0;
if (!keyo || (keyo && strcmp(keyo->GetName(), key->GetName()))) {
skip = 0;
obj = fList->FindObject(key->GetName());
if (obj) {
b->Add(obj, obj->GetName());
if (obj->IsFolder() && !obj->InheritsFrom("TTree"))
skip = 1;
}
}
if (!skip) {
name.Form("%s;%d", key->GetName(), key->GetCycle());
b->Add(key, name);
}
keyo = key;
}
}
}
void TDirectoryFile::Build(TFile* motherFile, TDirectory* motherDir)
{
if (motherDir && strlen(GetName()) != 0) motherDir->Append(this);
fModified = kTRUE;
fWritable = kFALSE;
fDatimeC.Set();
fDatimeM.Set();
fNbytesKeys = 0;
fSeekDir = 0;
fSeekParent = 0;
fSeekKeys = 0;
fList = new THashList(100,50);
fKeys = new THashList(100,50);
fMother = motherDir;
fFile = motherFile ? motherFile : TFile::CurrentFile();
SetBit(kCanDelete);
}
Bool_t TDirectoryFile::cd(const char *path)
{
Bool_t ok = TDirectory::cd(path);
if (ok) TFile::CurrentFile() = fFile;
return ok;
}
void TDirectoryFile::CleanTargets()
{
if (gFile == this) {
gFile = 0;
}
TDirectory::CleanTargets();
}
TObject *TDirectoryFile::CloneObject(const TObject *obj, Bool_t autoadd )
{
char *pobj = (char*)obj->IsA()->New();
if (!pobj) return 0;
Int_t baseOffset = obj->IsA()->GetBaseClassOffset(TObject::Class());
if (baseOffset==-1) {
Fatal("CloneObject","Incorrect detection of the inheritance from TObject for class %s.\n",
obj->IsA()->GetName());
}
TObject *newobj = (TObject*)(pobj+baseOffset);
{
TFile *filsav = gFile;
gFile = 0;
const Int_t bufsize = 10000;
TBufferFile buffer(TBuffer::kWrite,bufsize);
buffer.MapObject(obj);
{
Bool_t isRef = obj->TestBit(kIsReferenced);
((TObject*)obj)->ResetBit(kIsReferenced);
((TObject*)obj)->Streamer(buffer);
if (isRef) ((TObject*)obj)->SetBit(kIsReferenced);
}
buffer.SetReadMode();
buffer.ResetMap();
buffer.SetBufferOffset(0);
buffer.MapObject(newobj);
newobj->Streamer(buffer);
newobj->ResetBit(kIsReferenced);
newobj->ResetBit(kCanDelete);
gFile = filsav;
}
if (autoadd) {
ROOT::DirAutoAdd_t func = obj->IsA()->GetDirectoryAutoAdd();
if (func) {
func(newobj,this);
}
}
return newobj;
}
TObject *TDirectoryFile::FindObjectAnyFile(const char *name) const
{
TFile *f;
R__LOCKGUARD2(gROOTMutex);
TIter next(gROOT->GetListOfFiles());
while ((f = (TFile*)next())) {
TObject *obj = f->GetList()->FindObject(name);
if (obj) return obj;
}
return 0;
}
TDirectory *TDirectoryFile::GetDirectory(const char *apath,
Bool_t printError, const char *funcname)
{
Int_t nch = 0;
if (apath) nch = strlen(apath);
if (!nch) {
return this;
}
if (funcname==0 || strlen(funcname)==0) funcname = "GetDirectory";
TDirectory *result = this;
char *path = new char[nch+1]; path[0] = 0;
if (nch) strlcpy(path,apath,nch+1);
char *s = (char*)strchr(path, ':');
if (s) {
*s = '\0';
R__LOCKGUARD2(gROOTMutex);
TDirectory *f = (TDirectory *)gROOT->GetListOfFiles()->FindObject(path);
if (!f && !strcmp(gROOT->GetName(), path)) f = gROOT;
if (s) *s = ':';
if (f) {
result = f;
if (s && *(s+1)) result = f->GetDirectory(s+1,printError,funcname);
delete [] path; return result;
} else {
if (printError) Error(funcname, "No such file %s", path);
delete [] path; return 0;
}
}
if (path[0] == '/') {
TDirectory *td = fFile;
if (!fFile) td = gROOT;
result = td->GetDirectory(path+1,printError,funcname);
delete [] path; return result;
}
TDirectoryFile *obj;
char *slash = (char*)strchr(path,'/');
if (!slash) {
if (!strcmp(path, "..")) {
result = GetMotherDir();
delete [] path; return result;
}
GetObject(path,obj);
if (!obj) {
if (printError) Error(funcname,"Unknown directory %s", path);
delete [] path; return 0;
}
delete [] path; return obj;
}
TString subdir(path);
slash = (char*)strchr(subdir.Data(),'/');
*slash = 0;
if (!strcmp(subdir, "..")) {
TDirectory* mom = GetMotherDir();
if (mom)
result = mom->GetDirectory(slash+1,printError,funcname);
delete [] path; return result;
}
GetObject(subdir,obj);
if (!obj) {
if (printError) Error(funcname,"Unknown directory %s", subdir.Data());
delete [] path; return 0;
}
result = ((TDirectory*)obj)->GetDirectory(slash+1,printError,funcname);
delete [] path; return result;
}
void TDirectoryFile::Close(Option_t *)
{
if (!fList || !fSeekDir) {
return;
}
Save();
Bool_t fast = kTRUE;
TObjLink *lnk = fList->FirstLink();
while (lnk) {
if (lnk->GetObject()->IsA() == TDirectoryFile::Class()) {fast = kFALSE;break;}
lnk = lnk->Next();
}
if (fast) fList->Delete();
else fList->Delete("slow");
if (fKeys) {
fKeys->Delete("slow");
}
CleanTargets();
}
void TDirectoryFile::Delete(const char *namecycle)
{
if (gDebug)
Info("Delete","Call for this = %s namecycle = %s",
GetName(), (namecycle ? namecycle : "null"));
TDirectory::TContext ctxt(gDirectory, this);
Short_t cycle;
char name[kMaxLen];
DecodeNameCycle(namecycle, name, cycle, kMaxLen);
Int_t deleteall = 0;
Int_t deletetree = 0;
if(strcmp(name,"*") == 0) deleteall = 1;
if(strcmp(name,"*T") == 0){ deleteall = 1; deletetree = 1;}
if(strcmp(name,"T*") == 0){ deleteall = 1; deletetree = 1;}
if(namecycle==0 || strlen(namecycle) == 0){ deleteall = 1; deletetree = 1;}
TRegexp re(name,kTRUE);
TString s;
Int_t deleteOK = 0;
if (cycle >= 9999 ) {
TNamed *idcur;
TIter next(fList);
while ((idcur = (TNamed *) next())) {
deleteOK = 0;
s = idcur->GetName();
if (deleteall || s.Index(re) != kNPOS) {
deleteOK = 1;
if (idcur->IsA() == TDirectoryFile::Class()) {
deleteOK = 2;
if (!deletetree && deleteall) deleteOK = 0;
}
}
if (deleteOK != 0) {
fList->Remove(idcur);
if (deleteOK==2) {
if (deletetree)
((TDirectory*) idcur)->ReadAll("dirs");
idcur->Delete(deletetree ? "T*;*" : "*");
delete idcur;
} else
idcur->Delete(name);
}
}
}
if (cycle != 9999 ) {
if (IsWritable()) {
TKey *key;
TIter nextkey(GetListOfKeys());
while ((key = (TKey *) nextkey())) {
deleteOK = 0;
s = key->GetName();
if (deleteall || s.Index(re) != kNPOS) {
if (cycle == key->GetCycle()) deleteOK = 1;
if (cycle > 9999) deleteOK = 1;
if (strstr(key->GetClassName(),"TDirectory")) {
deleteOK = 2;
if (!deletetree && deleteall) deleteOK = 0;
if (cycle == key->GetCycle()) deleteOK = 2;
}
}
if (deleteOK) {
if (deleteOK==2) {
TDirectory* dir = GetDirectory(key->GetName(), kTRUE, "Delete");
if (dir!=0) {
dir->Delete("T*;*");
fList->Remove(dir);
delete dir;
}
}
key->Delete();
fKeys->Remove(key);
fModified = kTRUE;
delete key;
}
}
TFile* f = GetFile();
if (fModified && (f!=0)) {
WriteKeys();
WriteDirHeader();
f->WriteFree();
f->WriteHeader();
}
}
}
}
void TDirectoryFile::FillBuffer(char *&buffer)
{
Version_t version = TDirectoryFile::Class_Version();
if (fSeekKeys > TFile::kStartBigFile) version += 1000;
tobuf(buffer, version);
fDatimeC.FillBuffer(buffer);
fDatimeM.FillBuffer(buffer);
tobuf(buffer, fNbytesKeys);
tobuf(buffer, fNbytesName);
if (version > 1000) {
tobuf(buffer, fSeekDir);
tobuf(buffer, fSeekParent);
tobuf(buffer, fSeekKeys);
} else {
tobuf(buffer, (Int_t)fSeekDir);
tobuf(buffer, (Int_t)fSeekParent);
tobuf(buffer, (Int_t)fSeekKeys);
}
fUUID.FillBuffer(buffer);
if (fFile && fFile->GetVersion() < 40000) return;
if (version <=1000) for (Int_t i=0;i<3;i++) tobuf(buffer,Int_t(0));
}
TKey *TDirectoryFile::FindKey(const char *keyname) const
{
Short_t cycle;
char name[kMaxLen];
DecodeNameCycle(keyname, name, cycle, kMaxLen);
return GetKey(name,cycle);
}
TKey *TDirectoryFile::FindKeyAny(const char *keyname) const
{
TDirectory *dirsav = gDirectory;
Short_t cycle;
char name[kMaxLen];
DecodeNameCycle(keyname, name, cycle, kMaxLen);
TIter next(GetListOfKeys());
TKey *key;
while ((key = (TKey *) next())) {
if (!strcmp(name, key->GetName()))
if ((cycle == 9999) || (cycle >= key->GetCycle())) {
((TDirectory*)this)->cd();
return key;
}
}
next.Reset();
while ((key = (TKey *) next())) {
if (strstr(key->GetClassName(),"TDirectory")) {
TDirectory* subdir =
((TDirectory*)this)->GetDirectory(key->GetName(), kTRUE, "FindKeyAny");
TKey *k = (subdir!=0) ? subdir->FindKeyAny(keyname) : 0;
if (k) return k;
}
}
if (dirsav) dirsav->cd();
return 0;
}
TObject *TDirectoryFile::FindObjectAny(const char *aname) const
{
TObject *obj = TDirectory::FindObjectAny(aname);
if (obj) return obj;
TDirectory *dirsav = gDirectory;
Short_t cycle;
char name[kMaxLen];
DecodeNameCycle(aname, name, cycle, kMaxLen);
TIter next(GetListOfKeys());
TKey *key;
while ((key = (TKey *) next())) {
if (!strcmp(name, key->GetName())) {
if (cycle == 9999) return key->ReadObj();
if (cycle >= key->GetCycle()) return key->ReadObj();
}
}
next.Reset();
while ((key = (TKey *) next())) {
if (strstr(key->GetClassName(),"TDirectory")) {
TDirectory* subdir =
((TDirectory*)this)->GetDirectory(key->GetName(), kTRUE, "FindKeyAny");
TKey *k = subdir==0 ? 0 : subdir->FindKeyAny(aname);
if (k) { if (dirsav) dirsav->cd(); return k->ReadObj();}
}
}
if (dirsav) dirsav->cd();
return 0;
}
TObject *TDirectoryFile::Get(const char *namecycle)
{
Short_t cycle;
char name[kMaxLen];
DecodeNameCycle(namecycle, name, cycle, kMaxLen);
Int_t nch = strlen(name);
for (Int_t i = nch-1; i > 0; i--) {
if (name[i] == '/') {
name[i] = 0;
TDirectory* dirToSearch=GetDirectory(name);
const char *subnamecycle = namecycle + i + 1;
name[i] = '/';
return dirToSearch?dirToSearch->Get(subnamecycle):0;
}
}
const char *namobj = name;
TObject *idcur = fList ? fList->FindObject(namobj) : nullptr;
if (idcur) {
if (idcur==this && strlen(namobj)!=0) {
idcur = 0;
} else if (cycle == 9999) {
return idcur;
} else {
if (idcur->InheritsFrom(TCollection::Class()))
idcur->Delete();
delete idcur;
idcur = 0;
}
}
TKey *key;
TIter nextkey(GetListOfKeys());
while ((key = (TKey *) nextkey())) {
if (strcmp(namobj,key->GetName()) == 0) {
if ((cycle == 9999) || (cycle == key->GetCycle())) {
TDirectory::TContext ctxt(this);
idcur = key->ReadObj();
break;
}
}
}
return idcur;
}
void *TDirectoryFile::GetObjectUnchecked(const char *namecycle)
{
return GetObjectChecked(namecycle,(TClass*)0);
}
void *TDirectoryFile::GetObjectChecked(const char *namecycle, const char* classname)
{
return GetObjectChecked(namecycle,TClass::GetClass(classname));
}
void *TDirectoryFile::GetObjectChecked(const char *namecycle, const TClass* expectedClass)
{
Short_t cycle;
char name[kMaxLen];
DecodeNameCycle(namecycle, name, cycle, kMaxLen);
Int_t nch = strlen(name);
for (Int_t i = nch-1; i > 0; i--) {
if (name[i] == '/') {
name[i] = 0;
TDirectory* dirToSearch=GetDirectory(name);
const char *subnamecycle = namecycle + i + 1;
name[i] = '/';
if (dirToSearch) {
return dirToSearch->GetObjectChecked(subnamecycle, expectedClass);
} else {
return 0;
}
}
}
const char *namobj = name;
if (expectedClass==0 || expectedClass->InheritsFrom(TObject::Class())) {
TObject *objcur = fList ? fList->FindObject(namobj) : 0;
if (objcur) {
if (objcur==this && strlen(namobj)!=0) {
objcur = 0;
} else if (cycle == 9999) {
if (expectedClass && objcur->IsA()->GetBaseClassOffset(expectedClass) == -1) return 0;
else return objcur;
} else {
if (objcur->InheritsFrom(TCollection::Class()))
objcur->Delete();
delete objcur;
objcur = 0;
}
}
}
void *idcur = 0;
TKey *key;
TIter nextkey(GetListOfKeys());
while ((key = (TKey *) nextkey())) {
if (strcmp(namobj,key->GetName()) == 0) {
if ((cycle == 9999) || (cycle == key->GetCycle())) {
TDirectory::TContext ctxt(this);
idcur = key->ReadObjectAny(expectedClass);
break;
}
}
}
return idcur;
}
Int_t TDirectoryFile::GetBufferSize() const
{
if (fBufferSize <= 0) return fFile->GetBestBuffer();
else return fBufferSize;
}
TKey *TDirectoryFile::GetKey(const char *name, Short_t cycle) const
{
TIter next( ((THashList *)(GetListOfKeys()))->GetListForObject(name) );
TKey *key;
while (( key = (TKey *)next() )) {
if (!strcmp(name, key->GetName())) {
if ((cycle == 9999) || (cycle >= key->GetCycle()))
return key;
}
}
return 0;
}
void TDirectoryFile::ls(Option_t *option) const
{
TROOT::IndentLevel();
cout <<ClassName()<<"*\t\t"<<GetName()<<"\t"<<GetTitle()<<endl;
TROOT::IncreaseDirLevel();
TString opta = option;
TString opt = opta.Strip(TString::kBoth);
Bool_t memobj = kTRUE;
Bool_t diskobj = kTRUE;
TString reg = "*";
if (opt.BeginsWith("-m")) {
diskobj = kFALSE;
if (opt.Length() > 2)
reg = opt(2,opt.Length());
} else if (opt.BeginsWith("-d")) {
memobj = kFALSE;
if (opt.Length() > 2)
reg = opt(2,opt.Length());
} else if (!opt.IsNull())
reg = opt;
TRegexp re(reg, kTRUE);
if (memobj) {
TObject *obj;
TIter nextobj(fList);
while ((obj = (TObject *) nextobj())) {
TString s = obj->GetName();
if (s.Index(re) == kNPOS) continue;
obj->ls(option);
}
}
if (diskobj) {
TKey *key;
TIter next(GetListOfKeys());
while ((key = (TKey *) next())) {
TString s = key->GetName();
if (s.Index(re) == kNPOS) continue;
key->ls();
}
}
TROOT::DecreaseDirLevel();
}
TFile *TDirectoryFile::OpenFile(const char *name, Option_t *option,const char *ftitle, Int_t compress, Int_t netopt)
{
return TFile::Open(name,option,ftitle,compress,netopt);
}
TDirectory *TDirectoryFile::mkdir(const char *name, const char *title)
{
if (!name || !title || !strlen(name)) return 0;
if (!strlen(title)) title = name;
if (GetKey(name)) {
Error("mkdir","An object with name %s exists already",name);
return 0;
}
TDirectoryFile *newdir = 0;
if (const char *slash = strchr(name,'/')) {
Long_t size = Long_t(slash-name);
char *workname = new char[size+1];
strncpy(workname, name, size);
workname[size] = 0;
TDirectoryFile *tmpdir;
GetObject(workname,tmpdir);
if (!tmpdir) {
tmpdir = (TDirectoryFile*)mkdir(workname,title);
if (!tmpdir) return 0;
}
if (!newdir) newdir = tmpdir;
tmpdir->mkdir(slash+1);
delete[] workname;
return newdir;
}
TDirectory::TContext ctxt(this);
newdir = new TDirectoryFile(name, title, "", this);
return newdir;
}
void TDirectoryFile::Purge(Short_t)
{
if (!IsWritable()) return;
TDirectory::TContext ctxt(this);
TKey *key;
TIter prev(GetListOfKeys(), kIterBackward);
while ((key = (TKey*)prev())) {
TKey *keyprev = (TKey*)GetListOfKeys()->Before(key);
if (!keyprev) break;
if (key->GetKeep() == 0) {
if (strcmp(key->GetName(), keyprev->GetName()) == 0) {
key->Delete();
delete key;
}
}
}
TFile* f = GetFile();
if (fModified && (f!=0)) {
WriteKeys();
WriteDirHeader();
f->WriteFree();
f->WriteHeader();
}
}
void TDirectoryFile::ReadAll(Option_t* opt)
{
TDirectory::TContext ctxt(this);
TKey *key;
TIter next(GetListOfKeys());
Bool_t readdirs = ((opt!=0) && ((strcmp(opt,"dirs")==0) || (strcmp(opt,"dirs*")==0)));
if (readdirs)
while ((key = (TKey *) next())) {
if (strstr(key->GetClassName(),"TDirectory")==0) continue;
TDirectory *dir = GetDirectory(key->GetName(), kTRUE, "ReadAll");
if ((dir!=0) && (strcmp(opt,"dirs*")==0)) dir->ReadAll("dirs*");
}
else
while ((key = (TKey *) next())) {
TObject *thing = GetList()->FindObject(key->GetName());
if (thing) { delete thing; }
key->ReadObj();
}
}
Int_t TDirectoryFile::ReadKeys(Bool_t forceRead)
{
if (fFile==0) return 0;
if (!fFile->IsBinary())
return fFile->DirReadKeys(this);
TDirectory::TContext ctxt(this);
char *buffer;
if (forceRead) {
fKeys->Delete();
Int_t nbytes = fNbytesName + TDirectoryFile::Sizeof();
char *header = new char[nbytes];
buffer = header;
fFile->Seek(fSeekDir);
if ( fFile->ReadBuffer(buffer,nbytes) ) {
delete [] header;
return 0;
}
buffer += fNbytesName;
Version_t versiondir;
frombuf(buffer,&versiondir);
fDatimeC.ReadBuffer(buffer);
fDatimeM.ReadBuffer(buffer);
frombuf(buffer, &fNbytesKeys);
frombuf(buffer, &fNbytesName);
if (versiondir > 1000) {
frombuf(buffer, &fSeekDir);
frombuf(buffer, &fSeekParent);
frombuf(buffer, &fSeekKeys);
} else {
Int_t sdir,sparent,skeys;
frombuf(buffer, &sdir); fSeekDir = (Long64_t)sdir;
frombuf(buffer, &sparent); fSeekParent = (Long64_t)sparent;
frombuf(buffer, &skeys); fSeekKeys = (Long64_t)skeys;
}
delete [] header;
}
Int_t nkeys = 0;
Long64_t fsize = fFile->GetSize();
if ( fSeekKeys > 0) {
TKey *headerkey = new TKey(fSeekKeys, fNbytesKeys, this);
headerkey->ReadFile();
buffer = headerkey->GetBuffer();
headerkey->ReadKeyBuffer(buffer);
TKey *key;
frombuf(buffer, &nkeys);
for (Int_t i = 0; i < nkeys; i++) {
key = new TKey(this);
key->ReadKeyBuffer(buffer);
if (key->GetSeekKey() < 64 || key->GetSeekKey() > fsize) {
Error("ReadKeys","reading illegal key, exiting after %d keys",i);
fKeys->Remove(key);
nkeys = i;
break;
}
if (key->GetSeekPdir() < 64 || key->GetSeekPdir() > fsize) {
Error("ReadKeys","reading illegal key, exiting after %d keys",i);
fKeys->Remove(key);
nkeys = i;
break;
}
fKeys->Add(key);
}
delete headerkey;
}
return nkeys;
}
Int_t TDirectoryFile::ReadTObject(TObject *obj, const char *keyname)
{
if (!fFile) { Error("Read","No file open"); return 0; }
TKey *key = 0;
TIter nextkey(GetListOfKeys());
while ((key = (TKey *) nextkey())) {
if (strcmp(keyname,key->GetName()) == 0) {
return key->Read(obj);
}
}
Error("Read","Key not found");
return 0;
}
void TDirectoryFile::ResetAfterMerge(TFileMergeInfo *info)
{
fModified = kFALSE;
fDatimeC.Set();
fDatimeM.Set();
fNbytesKeys = 0;
fNbytesName = 0;
fSeekDir = 0;
fSeekParent = 0;
fSeekKeys = 0;
TKey *key = (TKey*)fKeys->FindObject(fName);
TClass *cl = IsA();
if (key) {
cl = TClass::GetClass(key->GetClassName());
}
if (fKeys) {
fKeys->Delete("slow");
}
Init(cl);
TIter next(GetList());
TObject *idcur;
while ((idcur = next())) {
if (idcur->IsA() == TDirectoryFile::Class()) {
((TDirectoryFile*)idcur)->ResetAfterMerge(info);
}
}
}
void TDirectoryFile::rmdir(const char *name)
{
if ((name==0) || (*name==0)) return;
TString mask(name);
mask+=";*";
Delete(mask);
}
void TDirectoryFile::Save()
{
TDirectory::TContext ctxt(this);
SaveSelf();
if (fList) {
TObject *idcur;
TIter next(fList);
while ((idcur = next())) {
if (idcur->InheritsFrom(TDirectoryFile::Class())) {
TDirectoryFile *dir = (TDirectoryFile*)idcur;
dir->Save();
}
}
}
}
Int_t TDirectoryFile::SaveObjectAs(const TObject *obj, const char *filename, Option_t *option) const
{
if (!obj) return 0;
TDirectory *dirsav = gDirectory;
TString fname = filename;
if (!filename || strlen(filename) == 0) {
fname.Form("%s.root",obj->GetName());
}
TFile *local = TFile::Open(fname.Data(),"recreate");
if (!local) return 0;
Int_t nbytes = obj->Write();
delete local;
if (dirsav) dirsav->cd();
TString opt = option;
opt.ToLower();
if (!opt.Contains("q")) {
if (!gSystem->AccessPathName(fname.Data())) obj->Info("SaveAs", "ROOT file %s has been created", fname.Data());
}
return nbytes;
}
void TDirectoryFile::SaveSelf(Bool_t force)
{
if (IsWritable() && (fModified || force) && fFile) {
Bool_t dowrite = kTRUE;
if (fFile->GetListOfFree())
dowrite = fFile->GetListOfFree()->First() != 0;
if (dowrite) {
TDirectory *dirsav = gDirectory;
if (dirsav != this) cd();
WriteKeys();
WriteDirHeader();
if (dirsav && dirsav != this) dirsav->cd();
}
}
}
void TDirectoryFile::SetBufferSize(Int_t bufsize)
{
fBufferSize = bufsize;
}
void TDirectoryFile::SetTRefAction(TObject *ref, TObject *parent)
{
Int_t offset = (char*)ref - (char*)parent;
TClass *cl = parent->IsA();
cl->BuildRealData(parent);
TStreamerInfo *info = (TStreamerInfo*)cl->GetStreamerInfo();
TIter next(info->GetElements());
TStreamerElement *element;
while((element = (TStreamerElement*)next())) {
if (element->GetOffset() != offset) continue;
Int_t execid = element->GetExecID();
if (execid > 0) ref->SetBit(execid << 8);
return;
}
}
void TDirectoryFile::SetWritable(Bool_t writable)
{
TDirectory::TContext ctxt(this);
fWritable = writable;
if (fList) {
TObject *idcur;
TIter next(fList);
while ((idcur = next())) {
if (idcur->InheritsFrom(TDirectoryFile::Class())) {
TDirectoryFile *dir = (TDirectoryFile*)idcur;
dir->SetWritable(writable);
}
}
}
}
Int_t TDirectoryFile::Sizeof() const
{
Int_t nbytes = 22;
nbytes += fDatimeC.Sizeof();
nbytes += fDatimeM.Sizeof();
nbytes += fUUID.Sizeof();
if (fFile && fFile->GetVersion() >= 40000) nbytes += 12;
return nbytes;
}
void TDirectoryFile::Streamer(TBuffer &b)
{
Version_t v,version;
if (b.IsReading()) {
Build((TFile*)b.GetParent(), 0);
if (fFile && fFile->IsWritable()) fWritable = kTRUE;
if (fFile && !fFile->IsBinary()) {
Version_t R__v = b.ReadVersion(0, 0);
TClass* dirclass = (R__v < 5) ? TDirectory::Class() : TDirectoryFile::Class();
b.ClassBegin(dirclass, R__v);
TString sbuf;
b.ClassMember("CreateTime","TString");
sbuf.Streamer(b);
TDatime timeC(sbuf.Data());
fDatimeC = timeC;
b.ClassMember("ModifyTime","TString");
sbuf.Streamer(b);
TDatime timeM(sbuf.Data());
fDatimeM = timeM;
b.ClassMember("UUID","TString");
sbuf.Streamer(b);
TUUID id(sbuf.Data());
fUUID = id;
b.ClassEnd(dirclass);
fSeekKeys = 0;
} else {
b >> version;
fDatimeC.Streamer(b);
fDatimeM.Streamer(b);
b >> fNbytesKeys;
b >> fNbytesName;
if (version > 1000) {
SetBit(kIsBigFile);
b >> fSeekDir;
b >> fSeekParent;
b >> fSeekKeys;
} else {
Int_t sdir,sparent,skeys;
b >> sdir; fSeekDir = (Long64_t)sdir;
b >> sparent; fSeekParent = (Long64_t)sparent;
b >> skeys; fSeekKeys = (Long64_t)skeys;
}
v = version%1000;
if (v == 2) {
fUUID.StreamerV1(b);
} else if (v > 2) {
fUUID.Streamer(b);
}
}
R__LOCKGUARD2(gROOTMutex);
gROOT->GetUUIDs()->AddUUID(fUUID,this);
if (fSeekKeys) ReadKeys();
} else {
if (fFile && !fFile->IsBinary()) {
b.WriteVersion(TDirectoryFile::Class());
TString sbuf;
b.ClassBegin(TDirectoryFile::Class());
b.ClassMember("CreateTime","TString");
sbuf = fDatimeC.AsSQLString();
sbuf.Streamer(b);
b.ClassMember("ModifyTime","TString");
fDatimeM.Set();
sbuf = fDatimeM.AsSQLString();
sbuf.Streamer(b);
b.ClassMember("UUID","TString");
sbuf = fUUID.AsString();
sbuf.Streamer(b);
b.ClassEnd(TDirectoryFile::Class());
} else {
version = TDirectoryFile::Class_Version();
if (fFile && fFile->GetEND() > TFile::kStartBigFile) version += 1000;
b << version;
fDatimeC.Streamer(b);
fDatimeM.Streamer(b);
b << fNbytesKeys;
b << fNbytesName;
if (version > 1000) {
b << fSeekDir;
b << fSeekParent;
b << fSeekKeys;
} else {
b << (Int_t)fSeekDir;
b << (Int_t)fSeekParent;
b << (Int_t)fSeekKeys;
}
fUUID.Streamer(b);
if (version <=1000) for (Int_t i=0;i<3;i++) b << Int_t(0);
}
}
}
Int_t TDirectoryFile::Write(const char *, Int_t opt, Int_t bufsize)
{
if (!IsWritable()) return 0;
TDirectory::TContext ctxt(this);
TIter next(fList);
TObject *obj;
Int_t nbytes = 0;
while ((obj=next())) {
nbytes += obj->Write(0,opt,bufsize);
}
SaveSelf(kTRUE);
return nbytes;
}
Int_t TDirectoryFile::Write(const char *n, Int_t opt, Int_t bufsize) const
{
Error("Write const","A const TDirectory object should not be saved. We try to proceed anyway.");
return const_cast<TDirectoryFile*>(this)->Write(n, opt, bufsize);
}
Int_t TDirectoryFile::WriteTObject(const TObject *obj, const char *name, Option_t *option, Int_t bufsize)
{
TDirectory::TContext ctxt(this);
if (fFile==0) {
const char *objname = "no name specified";
if (name) objname = name;
else if (obj) objname = obj->GetName();
Error("WriteTObject","The current directory (%s) is not associated with a file. The object (%s) has not been written.",GetName(),objname);
return 0;
}
if (!fFile->IsWritable()) {
if (!fFile->TestBit(TFile::kWriteError)) {
Error("WriteTObject","Directory %s is not writable", fFile->GetName());
}
return 0;
}
if (!obj) return 0;
TString opt = option;
opt.ToLower();
TKey *key=0, *oldkey=0;
Int_t bsize = GetBufferSize();
if (bufsize > 0) bsize = bufsize;
const char *oname;
if (name && *name)
oname = name;
else
oname = obj->GetName();
Int_t nch = strlen(oname);
char *newName = 0;
if (nch && oname[nch-1] == ' ') {
newName = new char[nch+1];
strlcpy(newName,oname,nch+1);
for (Int_t i=0;i<nch;i++) {
if (newName[nch-i-1] != ' ') break;
newName[nch-i-1] = 0;
}
oname = newName;
}
if (opt.Contains("overwrite")) {
key = GetKey(oname);
if (key) {
key->Delete();
delete key;
}
}
if (opt.Contains("writedelete")) {
oldkey = GetKey(oname);
}
key = fFile->CreateKey(this, obj, oname, bsize);
if (newName) delete [] newName;
if (!key->GetSeekKey()) {
fKeys->Remove(key);
delete key;
if (bufsize) fFile->SetBufferSize(bufsize);
return 0;
}
fFile->SumBuffer(key->GetObjlen());
Int_t nbytes = key->WriteFile(0);
if (fFile->TestBit(TFile::kWriteError)) {
if (bufsize) fFile->SetBufferSize(bufsize);
return 0;
}
if (oldkey) {
oldkey->Delete();
delete oldkey;
}
if (bufsize) fFile->SetBufferSize(bufsize);
return nbytes;
}
Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option, Int_t bufsize)
{
TClass *cl = TClass::GetClass(classname);
if (cl == 0) {
TObject *info_obj = *(TObject**)obj;
TVirtualStreamerInfo *info = dynamic_cast<TVirtualStreamerInfo*>(info_obj);
if (info == 0) {
Error("WriteObjectAny","Unknown class: %s",classname);
return 0;
} else {
cl = info->GetClass();
}
}
return WriteObjectAny(obj,cl,name,option,bufsize);
}
Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option, Int_t bufsize)
{
TDirectory::TContext ctxt(this);
if (fFile==0) return 0;
if (!fFile->IsWritable()) {
if (!fFile->TestBit(TFile::kWriteError)) {
Error("WriteObject","File %s is not writable", fFile->GetName());
}
return 0;
}
if (!obj || !cl) return 0;
TKey *key, *oldkey=0;
Int_t bsize = GetBufferSize();
if (bufsize > 0) bsize = bufsize;
TString opt = option;
opt.ToLower();
const char *oname;
if (name && *name)
oname = name;
else
oname = cl->GetName();
Int_t nch = strlen(oname);
char *newName = 0;
if (nch && oname[nch-1] == ' ') {
newName = new char[nch+1];
strlcpy(newName,oname,nch+1);
for (Int_t i=0;i<nch;i++) {
if (newName[nch-i-1] != ' ') break;
newName[nch-i-1] = 0;
}
oname = newName;
}
if (opt.Contains("overwrite")) {
key = GetKey(oname);
if (key) {
key->Delete();
delete key;
}
}
if (opt.Contains("writedelete")) {
oldkey = GetKey(oname);
}
key = fFile->CreateKey(this, obj, cl, oname, bsize);
if (newName) delete [] newName;
if (!key->GetSeekKey()) {
fKeys->Remove(key);
delete key;
return 0;
}
fFile->SumBuffer(key->GetObjlen());
Int_t nbytes = key->WriteFile(0);
if (fFile->TestBit(TFile::kWriteError)) return 0;
if (oldkey) {
oldkey->Delete();
delete oldkey;
}
return nbytes;
}
void TDirectoryFile::WriteDirHeader()
{
TFile* f = GetFile();
if (f==0) return;
if (!f->IsBinary()) {
fDatimeM.Set();
f->DirWriteHeader(this);
return;
}
Int_t nbytes = TDirectoryFile::Sizeof();
char * header = new char[nbytes];
char * buffer = header;
fDatimeM.Set();
TDirectoryFile::FillBuffer(buffer);
Long64_t pointer = fSeekDir + fNbytesName;
fModified = kFALSE;
f->Seek(pointer);
f->WriteBuffer(header, nbytes);
if (f->MustFlush()) f->Flush();
delete [] header;
}
void TDirectoryFile::WriteKeys()
{
TFile* f = GetFile();
if (f==0) return;
if (!f->IsBinary()) {
f->DirWriteKeys(this);
return;
}
if (fSeekKeys != 0) {
f->MakeFree(fSeekKeys, fSeekKeys + fNbytesKeys -1);
}
TIter next(fKeys);
TKey *key;
Int_t nkeys = fKeys->GetSize();
Int_t nbytes = sizeof nkeys;
if (f->GetEND() > TFile::kStartBigFile) nbytes += 8;
while ((key = (TKey*)next())) {
nbytes += key->Sizeof();
}
TKey *headerkey = new TKey(fName,fTitle,IsA(),nbytes,this);
if (headerkey->GetSeekKey() == 0) {
delete headerkey;
return;
}
char *buffer = headerkey->GetBuffer();
next.Reset();
tobuf(buffer, nkeys);
while ((key = (TKey*)next())) {
key->FillBuffer(buffer);
}
fSeekKeys = headerkey->GetSeekKey();
fNbytesKeys = headerkey->GetNbytes();
headerkey->WriteFile();
delete headerkey;
}