#include "hdiskspace.h"
#include "TRegexp.h"
#include "TDatime.h"
#include "TStyle.h"
#include "TH2D.h"
#include "TLegend.h"
#include <algorithm>
#include <ctype.h>
#include "TStreamer.h"
#include "TDirectory.h"
ClassImp(HFileSys)
ClassImp(HDiskFile)
ClassImp(HDiskDir)
ClassImp(HDiskCatalog)
ClassImp(HDiskStat)
HDiskCatalog* gHDiskCatalog;
ULong64_t          HFileSys::unitK        = 1024L;
ULong64_t          HFileSys::unitM        = 1024L*1024L;
ULong64_t          HFileSys::unitG        = 1024L*1024L*1024L;
ULong64_t          HFileSys::unitT        = 1024L*1024L*1024L*1024L;
ULong64_t          HFileSys::SmallerLimit = 1024L*1024L;   
map<uid_t,TString> HFileSys::musers  =  initUsers();
map<uid_t,TString> HFileSys::mgroups =  initGroups();
void HFileSys::getUsers(map<uid_t,TString>& musers)
{
    
    musers.clear();
    setpwent();
    struct passwd* entry = 0;
    while( (entry = getpwent() ) != 0){
	if(musers.find(entry->pw_uid) == musers.end()) musers[entry->pw_uid] = entry->pw_name;    }
    endpwent();
}
void HFileSys::getGroups(map<gid_t,TString>& mgroups)
{
    
    mgroups.clear();
    setgrent();
    struct group* entry =0;
    while( (entry = getgrent() ) != 0){
	if(mgroups.find(entry->gr_gid) == mgroups.end()) mgroups[entry->gr_gid] = entry->gr_name;
    }
    endgrent();
}
map<uid_t,TString> HFileSys::initUsers()
{
    
    map<uid_t,TString> m;
    return m;
}
map<gid_t,TString> HFileSys::initGroups()
{
    
    map<gid_t,TString> m;
    return m;
}
void HFileSys::fillSysInfo()
{
    
    
    getUsers (musers);
    getGroups(mgroups);
}
Bool_t HFileSys::getUser(uid_t uid,TString& name)
{
    
    
    
    name = Form("unknown_%i",uid);
    map<uid_t,TString>::iterator it = musers.find(uid);
    if(it!=musers.end()) { name = it->second; return kTRUE;}
    return kFALSE;
}
Bool_t HFileSys::getGroup(gid_t gid,TString& name)
{
    
    
    
    name = Form("unknown_%i",gid);
    map<gid_t,TString>::iterator it = mgroups.find(gid);
    if(it!=mgroups.end()) { name = it->second; return kTRUE;}
    return kFALSE;
}
void HFileSys::getModTime(time_t lastmod,TString& modtime)
{
    
    
    modtime = ctime(&lastmod);
    modtime.ReplaceAll('\n',"");
}
void HFileSys::getFileSize(ULong64_t size, TString& out)
{
    
    out="";
    Float_t c = unitK;   
    TString s = "K";
    if (size>unitM && size <= unitG){
	c=unitM;
        s = "M";
    } else if (size>unitG){
	c=unitG;
	s="G";
	if(size>unitT){
	    c=unitT;
	    s="T";
	}
    }
    Float_t val=size/c;
    if     (s=="K")   out= Form("%9.0fK",val);
    else if(s=="M")   out= Form("%9.3fM",val);
    else if(s=="G")   out= Form("%9.3fG",val);
    else              out= Form("%9.3fT",val);
}
void HFileSys::lsFiles(TString directory, vector<TString>& fileList,Bool_t clear,Bool_t fullpath)
{
    
    
    if(clear)fileList.clear();
    DIR *dir;
    struct dirent *ent = NULL;
    if ((dir=opendir(directory.Data())) != NULL)   
    {
	while((ent=readdir(dir)) != NULL) { 
	    if(ent->d_type == DT_REG) {
		if(!fullpath)fileList.push_back(ent->d_name); 
                else         fileList.push_back(Form("%s/%s",directory.Data(),ent->d_name));
	    }
	}
	closedir(dir);
    }
}
void  HFileSys::lsFilesRecursive(TString directory, vector<TString>&fullList)
{
    
    
    if(directory.EndsWith("/")) directory.Replace(directory.Length()-1,1,"");
    vector<TString> fileList;
    lsDirectory(directory,fileList);  
    lsFiles(directory,fullList,kFALSE,kTRUE);
    for(vector<TString>::iterator i = fileList.begin(); i != fileList.end(); ++i) 
    {
	if (strcmp((*i).Data(), ".") &&     
	    strcmp((*i).Data(), ".."))
	{
	    stringstream fullname;
	    fullname << directory << "/" << (*i);
	    lsFilesRecursive(fullname.str(), fullList);
	}
    }
}
void HFileSys::lsDirectory(TString directory, vector<TString>& fileList)
{
    
    
    fileList.clear();
    DIR *dir;
    struct dirent *ent=NULL;
    if ((dir=opendir(directory.Data())) != NULL) 
    {
	while((ent=readdir(dir)) != NULL) { 
	    if (strcmp(ent->d_name, ".") &&     
		strcmp(ent->d_name, ".."))
	    {
		if(ent->d_type == DT_DIR)fileList.push_back(ent->d_name); 
	    }
	}
	closedir(dir); 
    }
}
void  HFileSys::lsDirectoryRecursive(TString directory, vector<TString>&fullList)
{
    
    
    vector<TString> fileList;
    lsDirectory(directory,fileList);  
    for(vector<TString>::iterator i = fileList.begin(); i != fileList.end(); ++i) 
    {
	if (strcmp((*i).Data(), ".") &&     
	    strcmp((*i).Data(), ".."))
	{
	    stringstream fullname;
	    fullname << directory << "/" << (*i);
	    fullList.push_back(fullname.str());
	    lsDirectoryRecursive(fullname.str(), fullList);
	}
    }
}
HDiskFile::HDiskFile(TString name)
{
    SetName(name.Data());
    size   = 0;
    group  = 0;
    owner  = 0;
    lastmod= 0;
}
HDiskFile::~HDiskFile()
{
}
void HDiskFile::print(Int_t sp)
{
    
    
    TString modtime ;
    TString sowner;
    TString sgroup;
    TString fsize;
    HFileSys::getModTime (lastmod,modtime);
    HFileSys::getUser    (owner,sowner);
    HFileSys::getGroup   (group,sgroup);
    HFileSys::getFileSize(size,fsize);
    if(sp==0){
	cout<<setw(8)                     <<fsize.Data()
	    <<" "<<modtime.Data()
	    <<" "<<setw(15)<<sowner
	    <<" "<<setw(15)<<sgroup
	    <<" "<<GetName()
	    <<endl;
    } else {
	cout<<setw(8)<<std::string(sp,' ')<<fsize.Data()
	    <<" "<<modtime.Data()
	    <<" "<<setw(15)<<sowner
	    <<" "<<setw(15)<<sgroup
	    <<" "<<GetName()
	    <<endl;
    }
}
HDiskDir::HDiskDir(TString name)
{
    SetName(name.Data());
    size                    = 0;
    group                   = 0;
    owner                   = 0;
    lastmod                 = 0;
    nFilesSmallerLimit      = 0;
    nFilesSmallerLimitTotal = 0;
    nFilesTotal             = 0;
    level                   = 0;
    nEmptyDirs              = 0;
    nDirsTotal              = 0;
    nEmptyDirsTotal         = 0;
    
    if(gHDiskCatalog) {
	name.ReplaceAll(gHDiskCatalog->getDiskName().Data(),"");
	level = name.CountChar('/');
    }
}
HDiskDir::~HDiskDir()
{
}
void HDiskDir::update()
{
    
    
    nFilesSmallerLimitTotal = 0;
    nFilesTotal             = 0;
    size                    = 0;
    nEmptyDirsTotal         = 0;
    nDirsTotal              = 0;
    updateRecursive(this,this);
}
void HDiskDir::update(HDiskDir* mother)
{
    
    mother->addNSmallFilesTotal(nFilesSmallerLimit);
    mother->addNFilesTotal(files.size());
    mother->addNDirsTotal(dirs.size());
    mother->addNEmptyDirsTotal(nEmptyDirs);
    for(UInt_t i=0;i<files.size(); ++i){ 
       mother->addSize(files[i].getSize());
    }
}
void  HDiskDir::updateRecursive(HDiskDir* daughter,HDiskDir* mother)
{
    
    if(!gHDiskCatalog) {
	cout<<"ERROR: HDiskDir::updateRecursive() : gHDiskCatatlog==NULL! "<<endl;
	return ;
    }
    daughter->update(mother);
    for(UInt_t i=0;i<daughter->getDirs().size(); ++i) 
    {
	HDiskDir* d = (HDiskDir*)gHDiskCatalog->getList()->At(daughter->getDirs()[i]);
	if(!d) {
	    cout<<"ERROR: HDiskDir::updateRecursive() : index wrong, retrived NULL! "<<endl;
	    return ;
	}
	updateRecursive(d,mother);
    }
}
void HDiskDir::print(Int_t sp, TString base)
{
    
    
    
    TString modtime ;
    TString sowner;
    TString sgroup;
    TString fsize;
    TString name = GetName();
    if(base != "") name.ReplaceAll(base.Data(),"");
    HFileSys::getModTime (lastmod,modtime);
    HFileSys::getUser    (owner,sowner);
    HFileSys::getGroup   (group,sgroup);
    HFileSys::getFileSize(size,fsize) ;
    if(sp==0){
	cout                     <<setw(8)<<fsize.Data()
	    <<" "<<modtime.Data()
	    <<" "<<setw(12)<<sowner
	    <<" "<<setw(10)<<sgroup
	    <<" "<<setw(9)<<nFilesTotal
	    <<" "<<setw(9)<<nFilesSmallerLimitTotal
	    <<" "<<setw(9)<<nDirsTotal
	    <<" "<<setw(9)<<nEmptyDirsTotal
	    <<" "<<name
	    <<endl;
    } else {
	cout<<std::string(sp,'-')<<setw(8)<<fsize.Data()
	    <<" "<<modtime.Data()
	    <<" "<<setw(12)<<sowner
	    <<" "<<setw(10)<<sgroup
	    <<" "<<setw(9)<<nFilesTotal
	    <<" "<<setw(9)<<nFilesSmallerLimitTotal
	    <<" "<<setw(9)<<nDirsTotal
	    <<" "<<setw(9)<<nEmptyDirsTotal
	    <<" "<<name
	    <<endl;
    }
}
void HDiskDir::printFiles(Int_t sp)
{
    
    for(UInt_t i = 0; i < files.size(); i++){
	files[i].print(sp);
    }
}
void HDiskDir::printDirs(Int_t sp, TString base)
{
    
    if(!gHDiskCatalog) {
	cout<<"ERROR: HDiskDir::printDirs() : gHDiskCatatlog==NULL! "<<endl;
	return ;
    }
    for(UInt_t i = 0; i < dirs.size(); i++){
	HDiskDir* d = (HDiskDir*)gHDiskCatalog->getList()->At(dirs[i]);
	if(!d) {
	    cout<<"ERROR: HDiskDir::printDirs() : index wrong, retrived NULL! "<<endl;
	    continue ;
	}
	d->print(sp,base);
    }
}
Bool_t HDiskDir::scan(UInt_t index)
{
    
    
    
    Bool_t problem = kFALSE;
    files.clear();
    struct stat status;
    if(stat(GetName(),&status) == 0)
    {
	lastmod            = status.st_mtime;
	owner              = status.st_uid;
	group              = status.st_gid;
	size               = status.st_size;
	nFilesSmallerLimit = 0;
	vector<TString> fileList;
	HFileSys::lsFiles(GetName(),fileList);
	TString fullname;
	for(UInt_t i = 0; i < fileList.size(); i++){
	    fullname = Form("%s/%s",GetName() ,fileList[i].Data());
	    if(stat(fullname.Data() ,&status) == 0)
	    {
		HDiskFile newfile(fileList[i]);
		newfile.setSize   (status.st_size);
		newfile.setGroup  (status.st_gid);
		newfile.setOwner  (status.st_uid);
		newfile.setLastMod(status.st_mtime);
                newfile.setDir(index);
		if(newfile.isToSmall()) nFilesSmallerLimit++;
		files.push_back(newfile);
	    } else {
		cout<<"ERROR : HDiskDir:scan() : Could not stat file = "<<fileList[i].Data()<<endl;
		problem=kTRUE;
	    }
	}
    } else {
	cout<<"ERROR : HDiskDir:scan() : Could not stat dir = "<<GetName()<<endl;
	problem = kTRUE;
    }
    return problem;
}
Bool_t HDiskDir::scan(struct stat& status, UInt_t index,vector<TString>& dirList)
{
    
    
    
    Bool_t problem = kFALSE;
    files.clear();
    dirList.clear();
    lastmod            = status.st_mtime;
    owner              = status.st_uid;
    group              = status.st_gid;
    size               = status.st_size;
    nFilesSmallerLimit = 0;
    TString fullname;
    DIR *dir;
    struct dirent *ent = NULL;
    if ((dir=opendir(GetName())) != NULL)   
    {
	while((ent=readdir(dir)) != NULL) { 
	    if(ent->d_type == DT_DIR) {
		if (strcmp(ent->d_name, ".") &&     
		    strcmp(ent->d_name, "..")){
		    dirList.push_back(Form("%s/%s",GetName() ,ent->d_name)); 
		}
	    }
	    else if(ent->d_type == DT_REG)
	    {
		fullname = Form("%s/%s",GetName() ,ent->d_name);
		if(stat(fullname.Data() ,&status) == 0)
		{
		    HDiskFile newfile(ent->d_name);
		    newfile.setSize   (status.st_size);
		    newfile.setGroup  (status.st_gid);
		    newfile.setOwner  (status.st_uid);
		    newfile.setLastMod(status.st_mtime);
		    newfile.setDir(index);
		    if(newfile.isToSmall()) nFilesSmallerLimit++;
		    files.push_back(newfile);
		} else {
		    cout<<"ERROR : HDiskDir:scan() : Could not stat file = "<<ent->d_name<<endl;
		    problem=kTRUE;
		}
	    }
	}
	closedir(dir);
    }
    return problem;
}
void  HDiskDir::setDirToFiles(UInt_t index)
{
    for(UInt_t i = 0; i < files.size(); i++){
         files[i].setDir(index);
    }
}
Bool_t HDiskDir::isDaughter(HDiskDir* mother)
{
    
    
    if(!mother) return kFALSE;
    if(strncmp(GetName(),mother->GetName(),strlen(mother->GetName())) == 0) return kTRUE;
    else return kFALSE;
}
HDiskCatalog::HDiskCatalog(TString name)
{
    diskname=name;
    if(diskname.EndsWith("/")) {
	diskname.Replace(diskname.Length()-1,1,"");
    }
    list = new TObjArray();
    HFileSys::fillSysInfo();
    musers  = HFileSys::getUserMap();
    mgroups = HFileSys::getGroupMap();
    nSplit = 0;
    setCurrentCatalog();
}
HDiskCatalog::~HDiskCatalog()
{
    for(Int_t i=0;i<list->GetEntries();i++){
	HDiskDir* d = (HDiskDir*)list->At(i);
	delete d;
    }
    delete list;
    list=0;
}
Bool_t HDiskCatalog::cmpName(HDiskDir* a,HDiskDir* b)
{
    
    
    TString usera;  HFileSys::getUser((uid_t)a->getOwner(),usera);
    TString userb;  HFileSys::getUser((uid_t)b->getOwner(),userb);
    string first (usera.Data());
    string second(userb.Data());
    UInt_t i = 0;
    while ( (i<first.length()) && (i<second.length()) )
    {
	if      (tolower(first[i]) < tolower(second[i])) return kTRUE;
	else if (tolower(first[i]) > tolower(second[i])) return kFALSE;
	++i;
    }
    return ( first.length() < second.length() );
}
Bool_t HDiskCatalog::cmpSize(HDiskDir* a,HDiskDir* b)
{
    
    return ( a->getSize() > b->getSize() );
}
Bool_t HDiskCatalog::cmpNFiles(HDiskDir* a,HDiskDir* b)
{
    
    return ( a->getNFilesTotal() > b->getNFilesTotal() );
}
Bool_t HDiskCatalog::cmpNSmallFiles(HDiskDir* a,HDiskDir* b)
{
    
    return ( a->getNSmallFilesTotal() > b->getNSmallFilesTotal() );
}
Bool_t HDiskCatalog::cmpNFilesRatio(HDiskDir* a,HDiskDir* b)
{
    
    Float_t ratioa = 0;
    Float_t ratiob = 0;
    if(a->getNFilesTotal()>0) ratioa= a->getNSmallFilesTotal()/(Float_t)a->getNFilesTotal();
    if(b->getNFilesTotal()>0) ratiob= b->getNSmallFilesTotal()/(Float_t)b->getNFilesTotal();
    return ( ratioa > ratiob );
}
HDiskDir* HDiskCatalog::getDir(TString name, Int_t* ind)
{
    
    
    Int_t i = getDirMap(name);
    if(i>-1){
	if(ind!=0) *ind = i ;
	return (HDiskDir*) list->At(i);
    } else return NULL;
}
HDiskDir* HDiskCatalog::getDir(TString name,TObjArray* listtmp,Int_t* ind)
{
    
    
    for(Int_t i = 0; i < listtmp->GetEntries(); i++){
	HDiskDir* dir = (HDiskDir*) listtmp->At(i);
	if(strcmp(dir->GetName(),name.Data()) == 0 ) {
	    if(ind!=0) *ind = i ;
	    return dir;
	}
    }
    return NULL;
}
void HDiskCatalog::print(UChar_t maxlevel)
{
    
    for(Int_t i=0; i< list->GetEntries();i++){
	HDiskDir* dir = ((HDiskDir*)list->At(i));
        if(dir->getLevel() > maxlevel) continue;
	dir->print();
    }
    printExecution();
}
void HDiskCatalog::loopDirectory(TString directory, vector<TString>& dirList)
{
    
    
    dirList.clear();
    if(stat(directory.Data(),&status)==0)
    {
	HDiskDir* mydir  = 0;
	HDiskDir* olddir = getDirMapOld(directory.Data());
	Bool_t changed = kFALSE;
	if(olddir && olddir->getLastMod() != status.st_mtime) changed = kTRUE;
        Int_t index = -1;
	
	
	if(changed || !olddir) {
	    mydir = new HDiskDir(directory.Data());
	    flisttmp->AddLast(mydir);
	    index = flisttmp->GetEntries() -1;
	    mydir->scan(status,index,dirList);
	    cout<<"new : "<<setw(8)<<mydir->getNFiles()<<" files "<<mydir->GetName()<<endl;
	    fnfiles      += mydir->getNFiles();
	    fnfilestotal += mydir->getNFiles();
	    fndirs       ++;
	    addDir(mydir->GetName(),index);
	}
	
	
	
	if (olddir && !changed){   
	    mydir  = new HDiskDir(*olddir);
	    flisttmp->AddLast(mydir);
            index = flisttmp->GetEntries() -1;
	    mydir->setDirToFiles(index);
	    vector<Int_t>& dirs = mydir->getDirs();
	    for(UInt_t i = 0; i < dirs.size(); i++){
		HDiskDir* d = (HDiskDir*)list->At(dirs[i]);
                if(d) dirList.push_back(d->GetName());
                else  cout<<"ERROR : HDiskCatalog:loopDirectory() : Could not dir from old index "<<mydir->GetName()<<endl;
	    }
	    mydir->resetDirs(); 
	    
	    fnfiles      += mydir->getNFiles();
	    fnfilestotal += mydir->getNFiles();
	    fndirs       ++;
	    addDir(mydir->GetName(),index);
	}
	
	
	
	HDiskDir* motherdir = 0 ;
	TString mothername  = gSystem->DirName(mydir->GetName());
	Int_t ind = getDirMap(mothername);
	if(ind >-1){
	    motherdir = (HDiskDir*)flisttmp->At(ind);
	}
	if(motherdir) {
	    motherdir->addDir(index);
	    mydir    ->setMother(ind);
	} else {
	    cout<<"ERROR : HDiskCatalog:loopDirectory() : Could not find mother dir of "<<mydir->GetName()<<" ("<<mothername<<" ?)"<<endl;
	}
	
    } else {
	
	cout<<"ERROR : HDiskCatalog:loopDirectory() : Could not stat dir "<<directory<<endl;
    }
}
void  HDiskCatalog::loopDirectoryRecursive(TString directory)
{
    
    
    vector<TString> dirList;
    loopDirectory(directory,dirList);  
    for(UInt_t i = 0; i < dirList.size(); ++i) 
    {
	loopDirectoryRecursive(dirList[i]);
    }
}
Bool_t HDiskCatalog::scan()
{
    
    
    lastScanStart = time(&lastScanStart);
    TString mytime;
    HFileSys::getModTime(lastScanStart,mytime);
    cout<<"HDiskCatalog:scan() : disk is "<<diskname<<endl;
    cout<<"HDiskCatalog:scan() : start scannning  at "<<mytime<<endl;
    setCurrentCatalog();
    flisttmp = new TObjArray() ;
    mDirToInd.clear();
    
    
    mDirOld.clear();
    for(Int_t i = 0; i < list->GetEntries(); i++){
	HDiskDir* dir = (HDiskDir*)list->At(i);
        mDirOld[dir->GetName()] = dir;
    }
    
    vector<TString>listofentries;
    vector<TString>listsubdirs;
    Int_t index        = -1;
    fnfilestotal       =  0;
    HDiskDir* base     =  0;
    
    
    if(stat(diskname.Data(),&status) == 0)
    {
	base = new HDiskDir(diskname.Data());
	flisttmp->AddLast(base);
	index = flisttmp->GetEntries()-1;
	base->scan(index);
	fnfilestotal += base->getNFiles();
        addDir(base->GetName(),index);
    } else {
	cout<<"ERROR : HDiskCatalog:scan() : Could not stat dir = "<<diskname.Data()<<endl;
        return kFALSE;
    }
    
    
    
    HFileSys::lsDirectory(diskname,listsubdirs);
    for(UInt_t j = 0; j < listsubdirs.size(); j++)
    {
	fnfiles= 0;
        fndirs = 0;
	TString actdir=Form("%s/%s",diskname.Data(),listsubdirs[j].Data());
	
        
	if(stat(actdir.Data(),&status) == 0)
	{
	    HDiskDir* level1 = new HDiskDir(actdir);
	    flisttmp->AddLast(level1);
	    index = flisttmp->GetEntries()-1;
            level1->scan(index);
	    base  ->addDir(index);
	    level1->setMother(0);
	    fnfilestotal +=level1->getNFiles();
	    fnfiles      +=level1->getNFiles();
	    fndirs      ++;
	    addDir(level1->GetName(),index);
 	} else {
	    cout<<"ERROR : HDiskCatalog:scan() : Could not stat dir = "<<diskname.Data()<<endl;
	    return kFALSE;
	}
	
	
        
	vector<TString>listofentries;
	HFileSys::lsDirectory(actdir, listofentries);
	cout<<"Scanning list of directories for "<<actdir<<endl;
	for(UInt_t i = 0; i < listofentries.size(); i++)
	{
	    loopDirectoryRecursive(Form("%s/%s",actdir.Data(),listofentries[i].Data()));
	}
	cout<<"Scanned "<<setw(8)<<fndirs<<" directories with "<<setw(8)<<fnfiles<<" files"<<endl;
    } 
    
    cout<<"Total Scanned "<<setw(8)<<index<<" directories with "<<setw(8)<<fnfilestotal<<" files"<<endl;
    
    
    for(Int_t i = 0; i < list->GetEntries(); i++){
	HDiskDir* dir = (HDiskDir*)list->At(i);
	delete dir;
    }
    delete list;
    list = flisttmp;
    
    
    
    
    cout<<"Updating list of directories "<<endl;
    for(Int_t i = 0; i < list->GetEntries(); i++){  
	HDiskDir* dir = (HDiskDir*)list->At(i);
	vector<Int_t>& dirs = dir->getDirs();
        UInt_t n = 0;
	for(UInt_t j = 0;j < dirs.size(); j++){
	    if( ((HDiskDir*)list->At(dirs[j]))->isEmpty()) n++;
	}
        dir->setNEmptyDirs(n);
    }
    for(Int_t i = 0; i < list->GetEntries(); i++){  
	HDiskDir* dir = (HDiskDir*)list->At(i);
	dir->update();
    }
    
    lastScanStop = time(&lastScanStop);
    printExecution();
    return kTRUE;
}
void  HDiskCatalog::updateDirIndices()
{
    
    
    TString mothername;
    Int_t indexMoth;
    for(Int_t i = 0; i < list->GetEntries(); i++){
	HDiskDir* dir = (HDiskDir*)list->At(i);
	mothername = gSystem->DirName(dir->GetName());
	HDiskDir* dmother =  getDir(mothername,&indexMoth);
        if(dmother) dmother->addDir(i);
    }
}
Int_t HDiskCatalog::addDir(HDiskDir* dir)
{
    
    
    Int_t   index        = -1;
    Int_t   indexMoth    = -1;
    TString  mothname = gSystem->DirName(dir->GetName());
    HDiskDir* dmother =  getDir(mothname,&indexMoth);
    HDiskDir* mydir  = new HDiskDir(*dir);
    list->AddLast(mydir);
    index = list->GetEntries()-1;
    mydir->setDirToFiles(index);     
    mydir->setLevel(mydir->getLevel()+1);
    addDir(mydir->GetName(),index);  
    mydir->getDirs().clear();        
    if(dmother)
    {
	if(indexMoth == 0)
	{
	    
	    
	    
	    dmother->addSize(mydir->getSize());
	    dmother->addNFilesTotal     (dir->getNFilesTotal());
	    dmother->addNSmallFilesTotal(dir->getNSmallFilesTotal());
	    dmother->addNDirsTotal      (dir->getNDirsTotal()+1);  
	    dmother->addNEmptyDirsTotal (dir->getNEmptyDirsTotal());
	}
    }
    return index;
}
void HDiskCatalog::getDaughterDirs(HDiskDir* dir, vector<HDiskDir*>& daughters)
{
    
    daughters.clear();
    for(UInt_t i = 0; i < dir->getNDirs(); i++){
	HDiskDir* d = (HDiskDir*)list->At(dir->getDirs()[i]);
	if(d) {
	    daughters.push_back(d);
	}
    }
}
void HDiskCatalog::getDaughterDirsRecursive(HDiskDir* dir, vector<HDiskDir*>& list)
{
    
    vector<HDiskDir*> daughters;
    getDaughterDirs(dir,daughters);
    for(UInt_t i = 0; i < daughters.size(); i++)
    {
	HDiskDir* d = daughters[i];
        list.push_back(d);
	getDaughterDirsRecursive(d,list);
    }
}
void HDiskCatalog::sortDirs(HDiskDir* dir,vector<HDiskDir*>& daughters,TString option)
{
    
    
    
    
    
    
    getDaughterDirs(dir,daughters);
    if(daughters.size()==0) return;
    if(option=="name")        { sort(daughters.begin(),daughters.end(),cmpName);       }
    if(option=="size")        { sort(daughters.begin(),daughters.end(),cmpSize);       }
    if(option=="nfiles")      { sort(daughters.begin(),daughters.end(),cmpNFiles);     }
    if(option=="nsmallfiles") { sort(daughters.begin(),daughters.end(),cmpNSmallFiles);}
    if(option=="filesratio")  { sort(daughters.begin(),daughters.end(),cmpNFilesRatio);}
}
void HDiskCatalog::sortDirsRecursive(HDiskDir* dir, vector<HDiskDir*>& dlist,TString option)
{
    
    
    
    
    
    
    vector<HDiskDir*> daughters;
    sortDirs(dir,daughters,option);
    for(UInt_t i = 0; i < daughters.size(); i ++) {
	HDiskDir* d = daughters[i];
	dlist.push_back(d);
        sortDirsRecursive(d,dlist,option);
    }
}
void HDiskCatalog::printDisk(UChar_t maxlevel,TString option ,Int_t nfill,TString filler)
{
    
    
    
    
    
    
    
    
    
    TString replace = diskname ;
    replace +="/";
    Int_t maxreplace = 36;
    if(nfill==0) {
	if(replace.Length()<maxreplace) maxreplace = replace.Length();
	cout<<replace<<std::string(10+26-maxreplace,' ')
	    <<setw(8)<<""
	    <<" "<<""
	    <<" "<<setw(12)<<"uid"
	    <<" "<<setw(10)<<"gid"
	    <<" "<<setw(9)<<"files"
	    <<" "<<setw(9)<<"small f"
	    <<" "<<setw(9)<<"dirs"
	    <<" "<<setw(9)<<"empty d"
	    <<endl;
    } else {
	if(replace.Length()<maxlevel*nfill+26) maxreplace = replace.Length();
        else                                   maxreplace = maxlevel*nfill+26;
	cout<<replace<<std::string(maxlevel*nfill+26-maxreplace,' ')
	    <<setw(8)<<""
	    <<" "<<""
	    <<" "<<setw(12)<<"uid"
	    <<" "<<setw(10)<<"gid"
	    <<" "<<setw(9)<<"files"
	    <<" "<<setw(9)<<"small f"
	    <<" "<<setw(9)<<"dirs"
	    <<" "<<setw(9)<<"empty d"
	    <<endl;
    }
    HDiskDir* base = (HDiskDir*) list->At(0);
    vector<HDiskDir*> level1;
    vector<HDiskDir*> full;
    full.push_back(base);
    if(base){
	for(UInt_t i = 0; i < base->getNDirs(); i ++) {
            HDiskDir* lvl1 = (HDiskDir*) list->At(base->getDirs()[i]);
	    if(lvl1){
		level1.push_back(lvl1);
	    }
	}
    }
    if(option=="name")        { sort(level1.begin(),level1.end(),cmpName);       }
    if(option=="size")        { sort(level1.begin(),level1.end(),cmpSize);       }
    if(option=="nfiles")      { sort(level1.begin(),level1.end(),cmpNFiles);     }
    if(option=="nsmallfiles") { sort(level1.begin(),level1.end(),cmpNSmallFiles);}
    for(UInt_t i = 0; i < level1.size(); i ++) {
	full.push_back(level1[i]);
	sortDirsRecursive(level1[i],full,option);
    }
    for(UInt_t i=0;i<full.size();i++)
    {
	HDiskDir* dir = full[i];
	if(dir->getLevel() > maxlevel) continue;
	TString dirname = dir->GetName();
	dirname.ReplaceAll(replace.Data(),"");
	TString modtime ;
	TString sowner;
	TString sgroup;
	TString fsize;
	HFileSys::getModTime (dir->getLastMod(),modtime);
	HFileSys::getUser    (dir->getOwner(),sowner);
	HFileSys::getGroup   (dir->getGroup(),sgroup);
	HFileSys::getFileSize(dir->getSize(),fsize);
	if(nfill==0) {
	    cout<<std::string(10,' ')
		<<setw(8)<<fsize.Data()
		<<" "<<modtime.Data()
		<<" "<<setw(12)<<sowner
		<<" "<<setw(10)<<sgroup
		<<" "<<setw(9)<<dir->getNFilesTotal()
		<<" "<<setw(9)<<dir->getNSmallFilesTotal()
		<<" "<<setw(9)<<dir->getNDirsTotal()
		<<" "<<setw(9)<<dir->getNEmptyDirsTotal()
		<<" "<<dirname
		<<endl;
	} else {
	    cout<<std::string(dir->getLevel()*nfill,*filler.Data())
		<<setw(8)<<fsize.Data()
		<<std::string((maxlevel-dir->getLevel())*nfill,' ')
		<<" "<<modtime.Data()
		<<" "<<setw(12)<<sowner
		<<" "<<setw(10)<<sgroup
		<<" "<<setw(9)<<dir->getNFilesTotal()
		<<" "<<setw(9)<<dir->getNSmallFilesTotal()
		<<" "<<setw(9)<<dir->getNDirsTotal()
		<<" "<<setw(9)<<dir->getNEmptyDirsTotal()
		<<" "<<dirname<<endl;
	}
    }
    printExecution();
}
void HDiskCatalog::printExecution()
{
    
    TString mytime;
    HFileSys::getModTime(lastScanStart,mytime);
    cout<<"scan started  at "<<mytime<<endl;
    HFileSys::getModTime(lastScanStop,mytime);
    cout<<"scan finished at "<<mytime<<" ("<<(lastScanStop-lastScanStart)/60.<<" minutes)"<<endl;
}
UInt_t HDiskCatalog::filterDirs(TString regexp,vector<HDiskDir*>& dirs,TString range1S,TString range2S,Long64_t size1)
{
    
    
    
    
   ULong64_t range1=0;
   ULong64_t range2=kMaxULong64-1;;
   TDatime damin;
   TDatime damax;
   struct tm t_min;
   struct tm t_max;
   ULong64_t size = abs(size1);
   if(range1S.CompareTo("")!=0) {
       damin.Set(range1S.Data());
       range1 = damin.Convert();
       localtime_r((time_t*)(&range1),&t_min);
   }
   if(range2S.CompareTo("")!=0) {
       damax.Set(range2S.Data());
       range2 = damax.Convert();
       localtime_r((time_t*)(&range2),&t_max);
   }
   TRegexp expr(regexp);
   dirs.clear();
   for(Int_t i=0;i< list->GetEntries();i++){
       HDiskDir* dir = (HDiskDir*)list->At(i);
       TString name = dir->GetName();
       if((ULong64_t)dir->getLastMod() < range1 || (ULong64_t)dir->getLastMod() > range2 ) continue;
       if     (size1 > 0 && dir->getSize()<size) continue;
       else if(size1 < 0 && dir->getSize()>size) continue;
       if(name(expr) != "") dirs.push_back(dir);
   }
   return dirs.size();
}
UInt_t HDiskCatalog::filterFiles(TString regexpdir,TString regexpfile,vector<HDiskFile*>& files,TString range1S,TString range2S,Long64_t size1)
{
    
    
    
    
    
   files.clear();
   ULong64_t range1=0;
   ULong64_t range2=kMaxULong64-1;
   ULong64_t size = abs(size1);
   TDatime damin;
   TDatime damax;
   struct tm t_min;
   struct tm t_max;
   if(range1S.CompareTo("")!=0) {
       damin.Set(range1S.Data());
       range1 = damin.Convert();
       localtime_r((time_t*)(&range1),&t_min);
   }
   if(range2S.CompareTo("")!=0) {
       damax.Set(range2S.Data());
       range2 = damax.Convert();
       localtime_r((time_t*)(&range2),&t_max);
   }
    TRegexp exprdir (regexpdir);
    TRegexp exprfile(regexpfile);
    TString modtime;
    TString sowner;
    TString sgroup;
    TString fsize;
    TString name;
    TString fname;
    for(Int_t i=0;i< list->GetEntries();i++){
	HDiskDir* dir = (HDiskDir*)list->At(i);
	name = dir->GetName();
	if(name(exprdir) != "") {
	    for(UInt_t j=0;j<dir->getNFiles();j++){
		HDiskFile& f = dir->getFiles()[j];
		fname = f.GetName();
		if((ULong64_t)f.getLastMod() < range1 || (ULong64_t)f.getLastMod() > range2 ) continue;
		if     (size1 > 0 && f.getSize()<size) continue;
		else if(size1 < 0 && f.getSize()>size) continue;
		if(fname(exprfile) != "") {
                   files.push_back(&f);
		}
	    }
	}
    }
    return files.size();
}
void HDiskCatalog::printDirs(TString regexp,TString range1S,TString range2S,Long64_t size)
{
    
    
    
    
   vector<HDiskDir*> dirs;
   filterDirs(regexp,dirs,range1S,range2S,size);
   for(UInt_t i=0;i < dirs.size();i++){
       HDiskDir* dir = dirs[i];
       dir->print();
   }
}
void HDiskCatalog::printFiles(TString regexpdir,TString regexpfile,TString range1S,TString range2S,Long64_t size)
{
    
    
    
    
    
    vector<HDiskFile*> files;
    filterFiles(regexpdir,regexpfile,files,range1S,range2S,size);
    TString modtime;
    TString sowner;
    TString sgroup;
    TString fsize;
    for(UInt_t i=0;i < files.size();i++){
	HDiskFile* f = files[i];
	HFileSys::getModTime (f->getLastMod(),modtime);
	HFileSys::getUser    (f->getOwner(),sowner);
	HFileSys::getGroup   (f->getGroup(),sgroup);
	HFileSys::getFileSize(f->getSize(),fsize);
        HDiskDir* dir = (HDiskDir*)list->At(f->getDir());
	cout<<setw(8) <<fsize.Data()
	    <<" "<<modtime.Data()
	    <<" "<<setw(15)<<sowner
	    <<" "<<setw(15)<<sgroup
	    <<" "<<dir->GetName()
	    <<"/"<<f->GetName()<<endl;
    }
}
Bool_t HDiskCatalog::getUser(uid_t uid,TString& name)
{
    
    
    
    name = Form("unknown_%i",uid);
    map<uid_t,TString>::iterator it = musers.find(uid);
    if(it!=musers.end()) { name = it->second; return kTRUE;}
    return kFALSE;
}
Bool_t HDiskCatalog::getGroup(gid_t gid,TString& name)
{
    
    
    
    name = Form("unknown_%i",gid);
    map<gid_t,TString>::iterator it = mgroups.find(gid);
    if(it!=mgroups.end()) { name = it->second; return kTRUE;}
    return kFALSE;
}
void HDiskCatalog::Streamer(TBuffer &R__b)
{
   
   UInt_t R__s, R__c;
   if (R__b.IsReading()) {
      Version_t R__v = R__b.ReadVersion(&R__s, &R__c); if (R__v) { }
      TNamed::Streamer(R__b);
      diskname.Streamer(R__b);
      
      {
         map<uid_t,TString> &R__stl =  musers;
         R__stl.clear();
         int R__i, R__n;
         R__b >> R__n;
         for (R__i = 0; R__i < R__n; R__i++) {
            unsigned int R__t;
            R__b >> R__t;
            TString R__t2;
            R__t2.Streamer(R__b);
            typedef unsigned int Value_t;
            std::pair<Value_t const, TString > R__t3(R__t,R__t2);
            R__stl.insert(R__t3);
         }
      }
      {
         map<gid_t,TString> &R__stl =  mgroups;
         R__stl.clear();
         int R__i, R__n;
         R__b >> R__n;
         for (R__i = 0; R__i < R__n; R__i++) {
            unsigned int R__t;
            R__b >> R__t;
            TString R__t2;
            R__t2.Streamer(R__b);
            typedef unsigned int Value_t;
            std::pair<Value_t const, TString > R__t3(R__t,R__t2);
            R__stl.insert(R__t3);
         }
      }
      R__b >> lastScanStart;
      R__b >> lastScanStop;
      {
         map<TString,Int_t> &R__stl =  mDirToInd;
         R__stl.clear();
         int R__i, R__n;
         R__b >> R__n;
         for (R__i = 0; R__i < R__n; R__i++) {
            TString R__t;
            R__t.Streamer(R__b);
            int R__t2;
            R__b >> R__t2;
            typedef TString Value_t;
            std::pair<Value_t const, int > R__t3(R__t,R__t2);
            R__stl.insert(R__t3);
         }
      }
      
      
      R__b >> nSplit;
      for(Int_t i = 0; i < nSplit; i++)
      {
	  TObjArray* a = (TObjArray*) gDirectory->Get(Form("aList_%i",i));
	  if(a){
	      for(Int_t j = 0 ; j < a->GetEntries(); j++ ){
		  HDiskDir* d = (HDiskDir*)a->At(j);
		  list ->AddLast(d);
	      }
	      delete a;
	  } else {
	      Error("Streamer()","Could not retrieve TObjArray aList_%i",i);
	  }
      }
      
      R__b.CheckByteCount(R__s, R__c, HDiskCatalog::IsA());
   } else {
      R__c = R__b.WriteVersion(HDiskCatalog::IsA(), kTRUE);
      TNamed::Streamer(R__b);
      diskname.Streamer(R__b);
      
      {
         map<uid_t,TString> &R__stl =  musers;
         int R__n=(true) ? int(R__stl.size()) : 0;
         R__b << R__n;
         if(R__n) {
            map<uid_t,TString>::iterator R__k;
            for (R__k = R__stl.begin(); R__k != R__stl.end(); ++R__k) {
            R__b << ((*R__k).first );
            ((TString&)((*R__k).second)).Streamer(R__b);
            }
         }
      }
      {
         map<gid_t,TString> &R__stl =  mgroups;
         int R__n=(true) ? int(R__stl.size()) : 0;
         R__b << R__n;
         if(R__n) {
            map<gid_t,TString>::iterator R__k;
            for (R__k = R__stl.begin(); R__k != R__stl.end(); ++R__k) {
            R__b << ((*R__k).first );
            ((TString&)((*R__k).second)).Streamer(R__b);
            }
         }
      }
      R__b << lastScanStart;
      R__b << lastScanStop;
      {
         map<TString,Int_t> &R__stl =  mDirToInd;
         int R__n=(true) ? int(R__stl.size()) : 0;
         R__b << R__n;
         if(R__n) {
            map<TString,Int_t>::iterator R__k;
            for (R__k = R__stl.begin(); R__k != R__stl.end(); ++R__k) {
            ((TString&)((*R__k).first )).Streamer(R__b);
            R__b << ((*R__k).second);
            }
         }
      }
      
      
      nSplit       = 0;
      Int_t nPart  = 1000000;
      Int_t nF     = 0;
      TObjArray* a = new TObjArray();
      for(Int_t i = 0 ; i < list->GetEntries(); i++)
      {
	  HDiskDir* d = (HDiskDir*)list->At(i);
	  Int_t n = d->getNFiles();
	  if(nF+n >= nPart){
	      a->SetOwner(kFALSE);
	      a->Write(Form("aList_%i",nSplit),TObject::kSingleKey);
	      delete a;
	      a = new TObjArray();
              nF = 0;
	      nSplit++;
	  }
	  a ->AddLast(d);
          nF += n;
      }
      a->SetOwner(kFALSE);
      a->Write(Form("aList_%i",nSplit),TObject::kSingleKey);
      delete a;
      nSplit++;
      
      R__b << nSplit;
      R__b.SetByteCount(R__c, kTRUE);
   }
}
HDiskStat::HDiskStat(TString name)
{
    diskname = name;
    Int_t colors[]=
    {
	kRed  ,kMagenta  ,kBlue  ,kCyan  ,kGreen  ,kOrange,
	kRed-6,kMagenta-6,kBlue-6,kCyan-6,kGreen-6,kOrange-6,
	kRed-7,kMagenta-7,kBlue-7,kCyan-7,kGreen-7,kOrange-7,
	kRed-2,kMagenta-2,kBlue-2,kCyan-2,kGreen-2,kOrange-2,kBlack
    };
   Int_t markers[]={
       20,21,22,23,24,25,26,27,28
   };
   Int_t styles[]={
       1,2
   };
   vcolors .assign(colors ,colors + sizeof(colors)  /sizeof(Int_t));
   vmarkers.assign(markers,markers + sizeof(markers)/sizeof(Int_t));
   vstyles .assign(styles ,styles  + sizeof(styles) /sizeof(Int_t));
   unit =  HFileSys::getUnitG() ;
}
HDiskStat::~HDiskStat()
{
    ;
}
Int_t HDiskStat::evalOpt(TString opt)
{
    Int_t index =-1;
    if     (opt=="size")        index = 0;
    else if(opt=="nfiles")      index = 1;
    else if(opt=="nsmallfiles") index = 2;
    else {
	cout<<"ERROR: HDiskStat::evalOpt(): Unknown option ="<<opt<<endl;
    }
    return index;
}
Bool_t HDiskStat::findMinMaxGraph(Double_t& xmin,Double_t& xmax,Double_t& ymin,Double_t& ymax,TGraph* g)
{
    xmax = 0;
    xmin = 0;
    ymax = 0;
    ymin = 0;
    Double_t x,y;
    for(Int_t i=0; i<g->GetN();i++){
	g->GetPoint(i,x,y);
	if(x<xmin || xmin==0) xmin = x;
	if(x>xmax || xmax==0) xmax = x;
	if(y<ymin || ymin==0) ymin = y;
	if(y>ymax || ymax==0) ymax = y;
    }
    return kTRUE;
}
Bool_t HDiskStat::findMinMaxAll(Double_t& xmin,Double_t& xmax,Double_t& ymin,Double_t& ymax,vector<TGraph*>& vg)
{
    xmax = 0;
    xmin = 0;
    ymax = 0;
    ymin = 0;
    TGraph*g  = 0;
    Double_t x1,x2,y1,y2;
    for(UInt_t i=0; i<vg.size(); i++ ) {
	g  = vg[i];
	findMinMaxGraph(x1,x2,y1,y2,g);
	if(x1<xmin || xmin==0) xmin = x1;
	if(x2>xmax || xmax==0) xmax = x2;
	if(y1<ymin || ymin==0) ymin = y1;
	if(y2>ymax || ymax==0) ymax = y2;
    }
    if(ymin==ymax && ymin!=0)   ymin=0;
    if(ymin==ymax && ymin==0) { ymin=0; ymax = 10;}
    return kTRUE;
}
Bool_t HDiskStat::findMinMaxAll(Double_t& xmin,Double_t& xmax,Double_t& ymin,Double_t& ymax,TString opt)
{
    Int_t index = 0 ;
    xmax = 0;
    xmin = 0;
    ymax = 0;
    ymin = 0;
    index=evalOpt(opt);
    if(index < 0 ) return kFALSE;
    TGraph*g  = 0;
    Double_t x1,x2,y1,y2;
    for(map<TString,vector<TGraph> >::iterator iter = mDirToVal.begin(); iter != mDirToVal.end(); ++iter ) {
	g  = &(iter->second[index]);
	findMinMaxGraph(x1,x2,y1,y2,g);
	if(x1<xmin || xmin==0) xmin = x1;
	if(x2>xmax || xmax==0) xmax = x2;
	if(y1<ymin || ymin==0) ymin = y1;
	if(y2>ymax || ymax==0) ymax = y2;
    }
    if(ymin==ymax && ymin!=0)   ymin=0;
    if(ymin==ymax && ymin==0) { ymin=0; ymax = 10;}
    return kTRUE;
}
TGraph* HDiskStat::getDir(TString dirname,TString opt)
{
    Int_t index = evalOpt(opt);
    if(index<0) return NULL;
    map<TString, vector<TGraph> >::iterator it = mDirToVal.find(dirname);
    if(it != mDirToVal.end()) {
	return &it->second[index];
    } else return NULL;
}
vector<TGraph>* HDiskStat::getDirVec(TString dirname)
{
    map<TString, vector<TGraph> >::iterator it = mDirToVal.find(dirname);
    if(it != mDirToVal.end()) {
	return &(it->second);
    }
    else return NULL;
}
void HDiskStat::addEntry(time_t scanstart,HDiskDir* dir)
{
    map<TString, vector<TGraph> >::iterator it = mDirToVal.find(dir->GetName());
    if(it != mDirToVal.end()) { 
	it->second[0].SetPoint(it->second[0].GetN(),scanstart,dir->getSize());
        it->second[1].SetPoint(it->second[1].GetN(),scanstart,dir->getNFilesTotal());
        it->second[2].SetPoint(it->second[2].GetN(),scanstart,dir->getNSmallFilesTotal());
    } else { 
	vector<TGraph> v;
	TGraph gsize;
	gsize.SetName (Form("%s_size",dir->GetName()));
	gsize.SetPoint(gsize.GetN(),scanstart,dir->getSize());
	gsize.SetLineColor  (vcolors [(mDirToVal.size()+1)%vcolors .size()]);
	gsize.SetMarkerColor(vcolors [(mDirToVal.size()+1)%vcolors .size()]);
	gsize.SetMarkerStyle(vmarkers[(mDirToVal.size()+1)%vmarkers.size()]);
	gsize.SetLineStyle  (vstyles [(mDirToVal.size()+1)%vstyles .size()]);
	v.push_back(gsize);
	TGraph gnfiles;
	gnfiles.SetName (Form("%s_nfiles",dir->GetName()));
	gnfiles.SetPoint(gnfiles.GetN(),scanstart,dir->getNFilesTotal());
	gnfiles.SetLineColor  (vcolors [(mDirToVal.size()+1)%vcolors .size()]);
	gnfiles.SetMarkerColor(vcolors [(mDirToVal.size()+1)%vcolors .size()]);
	gnfiles.SetMarkerStyle(vmarkers[(mDirToVal.size()+1)%vmarkers.size()]);
	gnfiles.SetLineStyle  (vstyles [(mDirToVal.size()+1)%vstyles .size()]);
        v.push_back(gnfiles);
	TGraph gnsmallfiles;
	gnsmallfiles.SetName (Form("%s_nsmallfiles",dir->GetName()));
	gnsmallfiles.SetPoint(gnsmallfiles.GetN(),scanstart,dir->getNSmallFilesTotal());
	gnsmallfiles.SetLineColor  (vcolors [(mDirToVal.size()+1)%vcolors .size()]);
	gnsmallfiles.SetMarkerColor(vcolors [(mDirToVal.size()+1)%vcolors .size()]);
	gnsmallfiles.SetMarkerStyle(vmarkers[(mDirToVal.size()+1)%vmarkers.size()]);
	gnsmallfiles.SetLineStyle  (vstyles [(mDirToVal.size()+1)%vstyles .size()]);
	v.push_back(gnsmallfiles);
        mDirToVal[dir->GetName()] = v;
    }
}
TCanvas* HDiskStat::draw(TString opt,UInt_t lastDays,ULong64_t u,TString select)
{
    
    
    Int_t index = evalOpt(opt);
    if (index < 0) return NULL;
    gStyle->SetOptStat(0);
    gStyle->SetPadTopMargin(0.08);
    gStyle->SetPadBottomMargin(0.2);
    gStyle->SetPadRightMargin(0.18);
    gStyle->SetPadLeftMargin(0.12);
    TString timeformat="#splitline{%d}{%b}";
    ULong64_t    U = unit;
    if( u != 0 ) U = u;
    if(opt != "size"  ) U = 1;
    time_t tnow=0;
    tnow=time(&tnow);
    TString timeStamp;
    HFileSys::getModTime(tnow,timeStamp);
    time_t tlowlimit = tnow - lastDays*86400L;   
    Double_t dtlow   = (Double_t)tlowlimit;
    
    
    Double_t xmax = 0;
    Double_t xmin = 0;
    Double_t ymax = 0;
    Double_t ymin = 0;
    findMinMaxAll(xmin,xmax,ymin,ymax,opt);   
    ymax/=U;                                  
    ymin/=U;
    
    if(xmin > dtlow) {  
	dtlow     = xmin;
        tlowlimit = (time_t) xmin;
    }
    
    
    
    vector<TGraph*> vg;
    Double_t x,y;
    for (map<TString,vector<TGraph> >::iterator it = mDirToVal.begin(); it != mDirToVal.end(); ++it) {
        TGraph& gin= it->second[index];
	TString name=it->first;
	name.ReplaceAll(diskname.Data(),"");
	if(select != "mother") {
          if(name == "") continue;  
	} else {
            if(name != "") continue;  
	    name=gSystem->BaseName(it->first.Data()); 
	}
	TGraph* g = new TGraph();
	g->SetName(name.Data());
	g->SetLineColor(gin.GetLineColor());
	g->SetLineStyle(gin.GetLineStyle());
	g->SetLineWidth(gin.GetLineWidth());
	g->SetMarkerStyle(gin.GetMarkerStyle());
	g->SetMarkerColor(gin.GetMarkerColor());
	g->SetMarkerSize(gin.GetMarkerSize());
       
        Int_t ct=0;
	for(Int_t i=0;i<gin.GetN();i++){
	    gin.GetPoint(i,x,y);
	    if(x>=dtlow){
		g->SetPoint(ct,x,y/U);
		ct++;
	    }
	}
	if(g->GetN() > 0){             
	    Double_t x1 = 0;
	    Double_t x2 = 0;
	    Double_t y1 = 0;
	    Double_t y2 = 0;
            findMinMaxGraph(x1,x2,y1,y2,g);
	    if(y2>0) vg.push_back(g);  
            else     delete g;
	} else     delete g;
    }
    
    findMinMaxAll(xmin,xmax,ymin,ymax,vg); 
    
    
    TCanvas* c = new TCanvas(Form("cuser_%s_%s",opt.Data(),select.Data()),"user stat",1000,800);
    gPad->SetGridy();
    gPad->SetGridx();
    TH2D* h = new TH2D(Form("hdir_%s_%s",opt.Data(),select.Data()),Form("%s : %s ",diskname.Data(),timeStamp.Data()),1000,xmin,xmax,10,ymin == 0 ? 0 : ymin*.9,ymax*1.1);
    h->GetXaxis()->SetTimeDisplay(1);
    h->GetXaxis()->SetTimeFormat(timeformat.Data());
    h->GetXaxis()->SetLabelSize(0.02);
    h->GetXaxis()->LabelsOption("v");
    h->GetXaxis()->SetTickLength(0);
    if     (opt == "size"  )      {
	if(U==HFileSys::getUnitK() ) h->SetYTitle("diskspace [KB]");
	if(U==HFileSys::getUnitM() ) h->SetYTitle("diskspace [MB]");
        if(U==HFileSys::getUnitG() ) h->SetYTitle("diskspace [GB]");
        if(U==HFileSys::getUnitT() ) h->SetYTitle("diskspace [TB]");
    }
    else if(opt == "nfiles")      h->SetYTitle("number of files");
    else if(opt == "nsmallfiles") h->SetYTitle("number of small files");
    h->GetYaxis()->SetTitleOffset(1.7);
    h->Draw();
    TLegend* l = new TLegend(0.83,0.97,0.995,0.03,"","brNDC");
    for(UInt_t i=0;i<vg.size();i++){
        TGraph* g = vg[i];
	l->AddEntry(g,g->GetName(),"lp");
    }
    l->Draw();
    for(UInt_t i=0;i<vg.size();i++){
        TGraph* g = vg[i];
	g->Draw("L");
	g->Draw("Psame");
    }
    
    return c;
}