/* ======================================================================== *\ ! ! * ! * This file is part of MARS, the MAGIC Analysis and Reconstruction ! * Software. It is distributed to you in the hope that it can be a useful ! * and timesaving tool in analysing Data of imaging Cerenkov telescopes. ! * It is distributed WITHOUT ANY WARRANTY. ! * ! * Permission to use, copy, modify and distribute this software and its ! * documentation for any purpose is hereby granted without fee, ! * provided that the above copyright notice appear in all copies and ! * that both that copyright notice and this permission notice appear ! * in supporting documentation. It is provided "as is" without express ! * or implied warranty. ! * ! ! ! Author(s): Thomas Bretz, 6/2003 ! ! Copyright: MAGIC Software Development, 2000-2003 ! ! \* ======================================================================== */ ///////////////////////////////////////////////////////////////////////////// // // MDirIter // // Iterator for files in several directories (with filters) // // Use this class if you want to get all filenames in a directory // one-by-one. // // You can specify more than one directory (also recursivly) and you // can use filters (eg. *.root) // // Here is an example which will print all *.root files in the current // directory and all its subdirectories and all Gamma*.root files in // the directory ../data. // // ------------------------------------------------------------------------ // // // Instatiate the iterator // MDirIter Next(); // // // Add the current directory (for *.root files) and recursive // // directories with the same filter // Next.AddDirectory(".", "*.root", kTRUE); // // Add the directory ../data, too (filter only Gamma*.root files) // Next.AddDirectory("../data", "Gamma*.root"); // // TString name; // while (!(name=Next()).IsNull()) // cout << name << endl; // // ------------------------------------------------------------------------ // // WARNING: If you specify relative directories (like in the example) the // result may depend on the current working directory! Better use // absolute paths. // ///////////////////////////////////////////////////////////////////////////// #include "MDirIter.h" #include #include #include #include ClassImp(MDirIter); using namespace std; // -------------------------------------------------------------------------- // // Add a directory, eg dir="../data" // Using a filter (wildcards) will only return files matching this filter. // recursive is the number of recursive directories (use 0 for none and -1 // for all) // Returns the number of directories added. // If a directory is added using a filter and the directory is already // existing without a filter the filter is replaced. // If any directory to be added is already existing with a different // filter a new entry is created, eg: // already existing: ../data <*.root> // new entry: ../data // The filters are or'ed. // Int_t MDirIter::AddDirectory(const char *d, const char *filter, Int_t recursive) { TString dir(d); // Sanity check if (dir.IsNull()) return 0; #if ROOT_VERSION_CODE < ROOT_VERSION(3,05,05) if (dir[dir.Length()-1]!='/') dir += '/'; #else if (!dir.EndsWith("/")) dir += '/'; #endif gSystem->ExpandPathName(dir); // Try to find dir in the list of existing dirs TObject *o = fList.FindObject(dir); if (o) { const TString t(o->GetTitle()); // Check whether the existing dir has an associated filter if (t.IsNull()) { // Replace old filter by new one ((TNamed*)o)->SetTitle(filter); return 0; } // If the filters are the same no action is taken if (t==filter) return 0; } fList.Add(new TNamed((const char*)dir, filter ? filter : "")); // No recuresive directories, return if (recursive==0) return 1; Int_t rc = 1; // Create an iterator to iterate over all entries in the directory MDirIter NextD(dir); TString c; while (!(c=NextD(kTRUE)).IsNull()) { // Do not process . and .. entries if (c.EndsWith("/.") || c.EndsWith("/..")) continue; // If entry is a directory add it with a lower recursivity if (IsDir(c)==0) rc += AddDirectory(c, filter, recursive-1); } return rc; } // -------------------------------------------------------------------------- // // Adds all entries from iter to this object // void MDirIter::Add(const MDirIter &iter) { TIter NextD(&iter.fList); TObject *o=0; while ((o=NextD())) fList.Add(o->Clone()); } // -------------------------------------------------------------------------- // // Return the pointer to the current directory. If the pointer is NULL // a new directory is opened. If no new directory can be opened NULL is // returned. // void *MDirIter::Open() { // Check whether a directory is already open if (fDirPtr) return fDirPtr; // Get Next entry of list fCurrentPath=fNext(); // Open directory if new entry was found return fCurrentPath ? gSystem->OpenDirectory(fCurrentPath->GetName()) : NULL; } // -------------------------------------------------------------------------- // // Close directory is opened. Set fDirPtr=NULL // void MDirIter::Close() { if (fDirPtr) gSystem->FreeDirectory(fDirPtr); fDirPtr = NULL; } // -------------------------------------------------------------------------- // // Returns the concatenation of 'dir' and 'name' // TString MDirIter::ConcatFileName(const char *dir, const char *name) const { return TString(dir)+name; } // -------------------------------------------------------------------------- // // As the filter string may contain a + character, we have to replace // this filter by a new filter contaning a \+ at all locations where a + // was in the original filter. // // We replace: // . by \\. // + by \\+ // * by [^\\/:]* // ? by . // // And surround the filter by ^ and $. // // For more details you can have a look at the template: // TRegexp::MakeWildcard // const TRegexp MDirIter::MakeRegexp(TString n) const { n.Prepend("^"); n.ReplaceAll(".", "\\."); n.ReplaceAll("+", "\\+"); n.ReplaceAll("*", "[^\\/:]*"); n.Append("$"); return TRegexp(n, kFALSE); } // -------------------------------------------------------------------------- // // Check whether the given name n matches the filter f. // Filters are of the form TRegexp(f, kTRUE) // Bool_t MDirIter::MatchFilter(const TString &n, const TString &f) const { return f.IsNull() || !n(MakeRegexp(f)).IsNull(); } // -------------------------------------------------------------------------- // // Check whether fqp is a directory. // Returns -1 if fqp couldn't be accesed, 0 if it is a directory, // 1 otherwise // Int_t MDirIter::IsDir(const char *fqp) const { Long_t t[4]; if (gSystem->GetPathInfo(fqp, t, t+1, t+2, t+3)) return -1; if (t[2]==3) return 0; return 1; } // -------------------------------------------------------------------------- // // Check whether the current entry in the directory n is valid or not. // Entries must: // - not be . or .. // - match the associated filter // - match the global filter // - not be a directory // - have read permission // Bool_t MDirIter::CheckEntry(const TString n) const { // Check . and .. if (n=="." || n=="..") return kFALSE; // Check associated filter if (!MatchFilter(n, fCurrentPath->GetTitle())) return kFALSE; // Check global filter if (!MatchFilter(n, fFilter)) return kFALSE; // Check for file or directory const TString fqp = ConcatFileName(fCurrentPath->GetName(), n); if (IsDir(fqp)<=0) return kFALSE; // Check for rread perissions return !gSystem->AccessPathName(fqp, kReadPermission); } // -------------------------------------------------------------------------- // // Reset the iteration and strat from scratch. To do this correctly we have // to reset the list of directories to iterate _and_ to close the current // directory. When you call Next() the next time the first directory will // be reopened again and you'll get the first entry. // // Do not try to only close the current directory or to reset the directory // list only. This might not give the expected result! // void MDirIter::Reset() { Close(); fNext.Reset(); } // -------------------------------------------------------------------------- // // Return the Next file in the directory which is valid (see Check()) // nocheck==1 returns the next entry unchecked // TString MDirIter::Next(Bool_t nocheck) { fDirPtr = Open(); if (!fDirPtr) return ""; // Get next entry in dir, if existing check validity const char *n = gSystem->GetDirEntry(fDirPtr); if (n) return nocheck || CheckEntry(n) ? ConcatFileName(fCurrentPath->GetName(), n) : Next(); // Otherwise close directory and try to get next entry Close(); return Next(); } // -------------------------------------------------------------------------- // // Print a single entry in the list // void MDirIter::PrintEntry(const TObject &o) const { TString p = o.GetName(); TString f = o.GetTitle(); cout << p; if (!f.IsNull()) cout << " <" << f << ">"; cout << endl; } // -------------------------------------------------------------------------- // // Print all scheduled directories. If "all" is specified also all // matching entries are printed. // void MDirIter::Print(const Option_t *opt) const { TString s(opt); if (s.Contains("dbg", TString::kIgnoreCase)) fList.Print(); if (!s.Contains("all", TString::kIgnoreCase)) { TIter NextD(&fList); TObject *o=NULL; while ((o=NextD())) PrintEntry(*o); return; } MDirIter NextD(*this); TString name; TString d; while (!(name=NextD()).IsNull()) { const TString p = NextD.fCurrentPath->GetName(); if (p!=d) { d=p; PrintEntry(*NextD.fCurrentPath); } cout << " " << name << endl; } } // -------------------------------------------------------------------------- // // Loop over all contents (files). Sort the files alphabetically. // Delete the contents of this DirIter and add all sorted files // to this DirIter. // void MDirIter::Sort() { MDirIter NextD(*this); TList l; l.SetOwner(); TString name; while (!(name=NextD()).IsNull()) l.Add(new TNamed(name.Data(), "")); l.Sort(); fList.Delete(); Close(); fFilter = ""; TIter NextN(&l); TObject *o=0; while ((o=NextN())) { TString dir = o->GetName(); TString fname = o->GetName(); const Int_t last = dir.Last('/'); if (last<0) continue; dir.Remove(last); fname.Remove(0, last+1); AddDirectory(dir, fname); } }