source: trunk/Mars/mbase/MDirIter.cc@ 20095

Last change on this file since 20095 was 19720, checked in by tbretz, 5 years ago
Added a constructor which allows to instantiate MDIrIter with just one argument
File size: 12.9 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 6/2003 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2008
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MDirIter
28//
29// Iterator for files in several directories (with filters)
30//
31// Use this class if you want to get all filenames in a directory
32// one-by-one.
33//
34// You can specify more than one directory (also recursivly) and you
35// can use filters (eg. *.root)
36//
37// Here is an example which will print all *.root files in the current
38// directory and all its subdirectories and all Gamma*.root files in
39// the directory ../data.
40//
41// ------------------------------------------------------------------------
42//
43// // Instatiate the iterator
44// MDirIter Next();
45//
46// // Add the current directory (for *.root files) and recursive
47// // directories with the same filter
48// Next.AddDirectory(".", "*.root", kTRUE);
49// // Add the directory ../data, too (filter only Gamma*.root files)
50// Next.AddDirectory("../data", "Gamma*.root");
51//
52// TString name;
53// while (!(name=Next()).IsNull())
54// cout << name << endl;
55//
56// ------------------------------------------------------------------------
57//
58// WARNING: If you specify relative directories (like in the example) the
59// result may depend on the current working directory! Better use
60// absolute paths.
61//
62/////////////////////////////////////////////////////////////////////////////
63#include "MDirIter.h"
64
65#include <fstream>
66#include <iostream>
67
68#include <TList.h>
69#include <TNamed.h>
70#include <TRegexp.h>
71#include <TPRegexp.h>
72#include <TSystem.h>
73
74#include "MLog.h"
75#include "MLogManip.h"
76
77ClassImp(MDirIter);
78
79using namespace std;
80
81// --------------------------------------------------------------------------
82//
83// Add a directory, eg dir="../data"
84// Using a filter (wildcards) will only return files matching this filter.
85// recursive is the number of recursive directories (use 0 for none and -1
86// for all)
87// Returns the number of directories added.
88// If a directory is added using a filter and the directory is already
89// existing without a filter the filter is replaced.
90// If any directory to be added is already existing with a different
91// filter a new entry is created, eg:
92// already existing: ../data <*.root>
93// new entry: ../data <G*>
94// The filters are or'ed.
95//
96Int_t MDirIter::AddDirectory(const char *d, const char *filter, Int_t recursive)
97{
98 TString dir(d);
99
100 // Sanity check
101 if (dir.IsNull())
102 return 0;
103
104#if ROOT_VERSION_CODE < ROOT_VERSION(3,05,05)
105 if (dir[dir.Length()-1]!='/')
106 dir += '/';
107#else
108 if (!dir.EndsWith("/"))
109 dir += '/';
110#endif
111 gSystem->ExpandPathName(dir);
112
113 // Try to find dir in the list of existing dirs
114 TObject *o = fList.FindObject(dir);
115 if (o)
116 {
117 const TString t(o->GetTitle());
118
119 // Check whether the existing dir has an associated filter
120 if (t.IsNull())
121 {
122 // Replace old filter by new one
123 ((TNamed*)o)->SetTitle(filter);
124 return 0;
125 }
126
127 // If the filters are the same no action is taken
128 if (t==filter)
129 return 0;
130 }
131
132 fList.Add(new TNamed((const char*)dir, filter ? filter : ""));
133
134 // No recuresive directories, return
135 if (recursive==0)
136 return 1;
137
138 Int_t rc = 1;
139
140 // Create an iterator to iterate over all entries in the directory
141 MDirIter NextD;
142 NextD.AddDirectory(dir);
143
144 TString c;
145 while (!(c=NextD(kTRUE)).IsNull())
146 {
147 // Do not process . and .. entries
148 if (c.EndsWith("/.") || c.EndsWith("/.."))
149 continue;
150
151 // If entry is a directory add it with a lower recursivity
152 if (IsDir(c)==0)
153 rc += AddDirectory(c, filter, recursive-1);
154 }
155 return rc;
156}
157
158// --------------------------------------------------------------------------
159//
160// Add a single file to the iterator
161//
162Int_t MDirIter::AddFile(const char *name, Int_t rec)
163{
164 return AddDirectory(gSystem->DirName(name), gSystem->BaseName(name));
165}
166
167// --------------------------------------------------------------------------
168//
169// Adds all entries from iter to this object
170//
171void MDirIter::Add(const MDirIter &iter)
172{
173 TIter NextD(&iter.fList);
174 TObject *o=0;
175 while ((o=NextD()))
176 fList.Add(o->Clone());
177}
178
179Int_t MDirIter::ReadList(const char *name, const TString &path)
180{
181 const char *con = gSystem->ConcatFileName(path.Data(), name);
182 TString fname(path.IsNull() ? name : con);
183 delete [] con;
184
185 gSystem->ExpandPathName(fname);
186
187 ifstream fin(fname);
188 if (!fin)
189 {
190 gLog << err << "Cannot open file " << fname << ": ";
191 gLog << strerror(errno) << " [errno=" << errno << "]" <<endl;
192 return -1;
193 }
194
195 Int_t rc = 0;
196
197 while (1)
198 {
199 TString line;
200 line.ReadLine(fin);
201 if (!fin)
202 break;
203
204 line = line.Strip(TString::kBoth);
205 if (line[0]=='#' || line.IsNull())
206 continue;
207
208 TObjArray &arr = *line.Tokenize(' ');
209
210 if (arr.GetEntries()==1)
211 {
212 // FIXME: Check for wildcards
213 const TString file = arr[0]->GetName();
214 const Ssiz_t p = file.Last('/');
215
216 if (p<=0)
217 rc += AddDirectory(".", arr[0]->GetName(), 0);
218 else
219 rc += AddDirectory(TString(file(0, p)), file.Data()+p+1, 0);
220 }
221 else
222 {
223 for (int i=1; i<arr.GetEntries(); i++)
224 rc += AddDirectory(arr[0]->GetName(), arr[i]->GetName(), -1);
225 }
226
227 delete &arr;
228 }
229
230 return true;
231}
232
233// --------------------------------------------------------------------------
234//
235// Return the pointer to the current directory. If the pointer is NULL
236// a new directory is opened. If no new directory can be opened NULL is
237// returned.
238//
239void *MDirIter::Open()
240{
241 // Check whether a directory is already open
242 if (fDirPtr)
243 return fDirPtr;
244
245 // Get Next entry of list
246 fCurrentPath=fNext();
247
248 // Open directory if new entry was found
249 return fCurrentPath ? gSystem->OpenDirectory(fCurrentPath->GetName()) : NULL;
250}
251
252// --------------------------------------------------------------------------
253//
254// Close directory is opened. Set fDirPtr=NULL
255//
256void MDirIter::Close()
257{
258 if (fDirPtr)
259 gSystem->FreeDirectory(fDirPtr);
260 fDirPtr = NULL;
261}
262
263// --------------------------------------------------------------------------
264//
265// Returns the concatenation of 'dir' and 'name'
266//
267TString MDirIter::ConcatFileName(const char *dir, const char *name) const
268{
269 return TString(dir)+name;
270}
271
272// --------------------------------------------------------------------------
273//
274// Check whether the given name n matches the filter f.
275//
276// If the filter is encapsulated in ^ and $ it is assumed to be
277// a valid regular expression and TPRegexp is used for filtering.
278//
279// In other cases TRegex is used:
280//
281// As the filter string may contain a + character, we have to replace
282// this filter by a new filter contaning a \+ at all locations where a +
283// was in the original filter.
284//
285// We replace:
286// . by \\.
287// + by \\+
288// * by [^\\/:]*
289//
290// And surround the filter by ^ and $.
291//
292// For more details you can have a look at the template:
293// TRegexp::MakeWildcard
294//
295Bool_t MDirIter::MatchFilter(const TString &n, TString f) const
296{
297 if (f.IsNull())
298 return kTRUE;
299
300 if (f[0]=='^' && f[f.Length()-1]=='$')
301 {
302 TPRegexp regex(f);
303 return !n(regex).IsNull();
304 }
305
306 f.Prepend("^");
307 f.ReplaceAll(".", "\\.");
308 f.ReplaceAll("+", "\\+");
309 f.ReplaceAll("*", "[^\\/:]*");
310 //f.ReplaceAll("?", "[^\\/:]?");
311 f.Append("$");
312
313 return !n(TRegexp(f)).IsNull();
314}
315
316// --------------------------------------------------------------------------
317//
318// Check whether fqp is a directory.
319// Returns -1 if fqp couldn't be accesed, 0 if it is a directory,
320// 1 otherwise
321//
322Int_t MDirIter::IsDir(const char *fqp) const
323{
324 Long_t t[4];
325 if (gSystem->GetPathInfo(fqp, t, t+1, t+2, t+3))
326 return -1;
327
328 if (t[2]==3)
329 return 0;
330
331 return 1;
332}
333
334// --------------------------------------------------------------------------
335//
336// Check whether the current entry in the directory n is valid or not.
337// Entries must:
338// - not be . or ..
339// - match the associated filter
340// - match the global filter
341// - not be a directory
342// - have read permission
343//
344Bool_t MDirIter::CheckEntry(const TString n) const
345{
346 // Check . and ..
347 if (n=="." || n=="..")
348 return kFALSE;
349
350 // Check associated filter
351 if (!MatchFilter(n, fCurrentPath->GetTitle()))
352 return kFALSE;
353
354 // Check global filter
355 if (!MatchFilter(n, fFilter))
356 return kFALSE;
357
358 // Check for file or directory
359 const TString fqp = ConcatFileName(fCurrentPath->GetName(), n);
360 if (IsDir(fqp)<=0)
361 return kFALSE;
362
363 // Check for rread perissions
364 return !gSystem->AccessPathName(fqp, kReadPermission);
365}
366
367// --------------------------------------------------------------------------
368//
369// Reset the iteration and strat from scratch. To do this correctly we have
370// to reset the list of directories to iterate _and_ to close the current
371// directory. When you call Next() the next time the first directory will
372// be reopened again and you'll get the first entry.
373//
374// Do not try to only close the current directory or to reset the directory
375// list only. This might not give the expected result!
376//
377void MDirIter::Reset()
378{
379 Close();
380 fNext.Reset();
381}
382
383// --------------------------------------------------------------------------
384//
385// Return the Next file in the directory which is valid (see Check())
386// nocheck==1 returns the next entry unchecked
387//
388TString MDirIter::Next(Bool_t nocheck)
389{
390 while (1)
391 {
392 fDirPtr = Open();
393 if (!fDirPtr)
394 break;
395
396 // Get next entry in dir, if existing check validity
397 const char *n = gSystem->GetDirEntry(fDirPtr);
398 if (!n)
399 {
400 // Otherwise close directory and try to get next entry
401 Close();
402 continue;
403 }
404
405 if (nocheck || CheckEntry(n))
406 return ConcatFileName(fCurrentPath->GetName(), n);
407 }
408
409 return "";
410}
411
412// --------------------------------------------------------------------------
413//
414// Print a single entry in the list
415//
416void MDirIter::PrintEntry(const TObject &o) const
417{
418 TString p = o.GetName();
419 TString f = o.GetTitle();
420 cout << p;
421 if (!f.IsNull())
422 cout << " <" << f << ">";
423 cout << endl;
424}
425
426// --------------------------------------------------------------------------
427//
428// Print all scheduled directories. If "all" is specified also all
429// matching entries are printed.
430//
431void MDirIter::Print(const Option_t *opt) const
432{
433 TString s(opt);
434 if (s.Contains("dbg", TString::kIgnoreCase))
435 fList.Print();
436
437 if (!s.Contains("all", TString::kIgnoreCase))
438 {
439 TIter NextD(&fList);
440 TObject *o=NULL;
441 while ((o=NextD()))
442 PrintEntry(*o);
443 return;
444 }
445
446 MDirIter NextD(*this);
447 TString name;
448 TString d;
449 while (!(name=NextD()).IsNull())
450 {
451 const TString p = NextD.fCurrentPath->GetName();
452 if (p!=d)
453 {
454 d=p;
455 PrintEntry(*NextD.fCurrentPath);
456 }
457 cout << " " << name << endl;
458 }
459}
460
461// --------------------------------------------------------------------------
462//
463// Loop over all contents (files). Sort the files alphabetically.
464// Delete the contents of this DirIter and add all sorted files
465// to this DirIter.
466//
467void MDirIter::Sort()
468{
469 MDirIter NextD(*this);
470
471 TList l;
472 l.SetOwner();
473
474 TString name;
475 while (!(name=NextD()).IsNull())
476 l.Add(new TNamed(name.Data(), ""));
477
478 l.Sort();
479
480 fList.Delete();
481 Close();
482 fFilter = "";
483
484 TIter NextN(&l);
485 TObject *o=0;
486 while ((o=NextN()))
487 {
488 TString dir = o->GetName();
489 TString fname = o->GetName();
490
491 const Int_t last = dir.Last('/');
492 if (last<0)
493 continue;
494
495 dir.Remove(last);
496 fname.Remove(0, last+1);
497
498 AddDirectory(dir, fname);
499 }
500}
Note: See TracBrowser for help on using the repository browser.