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

Last change on this file since 19273 was 14993, checked in by tbretz, 7 years ago
Changed Next() in a way that it is not self-referencing anymore.... caused stack overflows with long lists.
File size: 12.4 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(dir);
142
143    TString c;
144    while (!(c=NextD(kTRUE)).IsNull())
145    {
146        // Do not process . and .. entries
147        if (c.EndsWith("/.") || c.EndsWith("/.."))
148            continue;
149
150        // If entry is a directory add it with a lower recursivity
151        if (IsDir(c)==0)
152            rc += AddDirectory(c, filter, recursive-1);
153    }
154    return rc;
155}
156
157// --------------------------------------------------------------------------
158//
159// Add a single file to the iterator
160//
161Int_t MDirIter::AddFile(const char *name)
162{
163    return AddDirectory(gSystem->DirName(name), gSystem->BaseName(name));
164}
165
166// --------------------------------------------------------------------------
167//
168// Adds all entries from iter to this object
169//
170void MDirIter::Add(const MDirIter &iter)
171{
172    TIter NextD(&iter.fList);
173    TObject *o=0;
174    while ((o=NextD()))
175        fList.Add(o->Clone());
176}
177
178Int_t MDirIter::ReadList(const char *name, const TString &path)
179{
180    const char *con = gSystem->ConcatFileName(path.Data(), name);
181    TString fname(path.IsNull() ? name : con);
182    delete [] con;
183
184    gSystem->ExpandPathName(fname);
185
186    ifstream fin(fname);
187    if (!fin)
188    {
189        gLog << err << "Cannot open file " << fname << ": ";
190        gLog << strerror(errno) << " [errno=" << errno << "]" <<endl;
191        return -1;
192    }
193
194    Int_t rc = 0;
195
196    while (1)
197    {
198        TString line;
199        line.ReadLine(fin);
200        if (!fin)
201            break;
202
203        line = line.Strip(TString::kBoth);
204        if (line[0]=='#' || line.IsNull())
205            continue;
206
207        TObjArray &arr = *line.Tokenize(' ');
208
209        for (int i=1; i<arr.GetEntries(); i++)
210            rc += AddDirectory(arr[0]->GetName(), arr[i]->GetName(), -1);
211
212        delete &arr;
213    }
214
215    return true;
216}
217
218// --------------------------------------------------------------------------
219//
220//  Return the pointer to the current directory. If the pointer is NULL
221//  a new directory is opened. If no new directory can be opened NULL is
222//  returned.
223//
224void *MDirIter::Open()
225{
226    // Check whether a directory is already open
227    if (fDirPtr)
228        return fDirPtr;
229
230    // Get Next entry of list
231    fCurrentPath=fNext();
232
233    // Open directory if new entry was found
234    return fCurrentPath ? gSystem->OpenDirectory(fCurrentPath->GetName()) : NULL;
235}
236
237// --------------------------------------------------------------------------
238//
239//  Close directory is opened. Set fDirPtr=NULL
240//
241void MDirIter::Close()
242{
243    if (fDirPtr)
244        gSystem->FreeDirectory(fDirPtr);
245    fDirPtr = NULL;
246}
247
248// --------------------------------------------------------------------------
249//
250//  Returns the concatenation of 'dir' and 'name'
251//
252TString MDirIter::ConcatFileName(const char *dir, const char *name) const
253{
254    return TString(dir)+name;
255}
256
257// --------------------------------------------------------------------------
258//
259// Check whether the given name n matches the filter f.
260//
261// If the filter is encapsulated in ^ and $ it is assumed to be
262// a valid regular expression and TPRegexp is used for filtering.
263//
264// In other cases TRegex is used:
265//
266// As the filter string may contain a + character, we have to replace
267// this filter by a new filter contaning a \+ at all locations where a +
268// was in the original filter.
269//
270// We replace:
271//   .  by  \\.
272//   +  by  \\+
273//   *  by  [^\\/:]*
274//
275// And surround the filter by ^ and $.
276//
277// For more details you can have a look at the template:
278//  TRegexp::MakeWildcard
279//
280Bool_t MDirIter::MatchFilter(const TString &n, TString f) const
281{
282    if (f.IsNull())
283        return kTRUE;
284
285    if (f[0]=='^' && f[f.Length()-1]=='$')
286    {
287        TPRegexp regex(f);
288        return !n(regex).IsNull();
289    }
290
291    f.Prepend("^");
292    f.ReplaceAll(".", "\\.");
293    f.ReplaceAll("+", "\\+");
294    f.ReplaceAll("*", "[^\\/:]*");
295    //f.ReplaceAll("?", "[^\\/:]?");
296    f.Append("$");
297
298    return !n(TRegexp(f)).IsNull();
299}
300
301// --------------------------------------------------------------------------
302//
303// Check whether fqp is a directory.
304// Returns -1 if fqp couldn't be accesed, 0 if it is a directory,
305// 1 otherwise
306//
307Int_t MDirIter::IsDir(const char *fqp) const
308{
309    Long_t t[4];
310    if (gSystem->GetPathInfo(fqp, t, t+1, t+2, t+3))
311        return -1;
312
313    if (t[2]==3)
314        return 0;
315
316    return 1;
317}
318
319// --------------------------------------------------------------------------
320//
321// Check whether the current entry in the directory n is valid or not.
322// Entries must:
323//  - not be . or ..
324//  - match the associated filter
325//  - match the global filter
326//  - not be a directory
327//  - have read permission
328//
329Bool_t MDirIter::CheckEntry(const TString n) const
330{
331    // Check . and ..
332    if (n=="." || n=="..")
333        return kFALSE;
334
335    // Check associated filter
336    if (!MatchFilter(n, fCurrentPath->GetTitle()))
337        return kFALSE;
338
339    // Check global filter
340    if (!MatchFilter(n, fFilter))
341         return kFALSE;
342
343    // Check for file or directory
344    const TString fqp = ConcatFileName(fCurrentPath->GetName(), n);
345    if (IsDir(fqp)<=0)
346        return kFALSE;
347
348    // Check for rread perissions
349    return !gSystem->AccessPathName(fqp, kReadPermission);
350}
351
352// --------------------------------------------------------------------------
353//
354// Reset the iteration and strat from scratch. To do this correctly we have
355// to reset the list of directories to iterate _and_ to close the current
356// directory. When you call Next() the next time the first directory will
357// be reopened again and you'll get the first entry.
358//
359// Do not try to only close the current directory or to reset the directory
360// list only. This might not give the expected result!
361//
362void  MDirIter::Reset()
363{
364    Close();
365    fNext.Reset();
366}
367
368// --------------------------------------------------------------------------
369//
370// Return the Next file in the directory which is valid (see Check())
371// nocheck==1 returns the next entry unchecked
372//
373TString MDirIter::Next(Bool_t nocheck)
374{
375    while (1)
376    {
377        fDirPtr = Open();
378        if (!fDirPtr)
379            break;
380
381        // Get next entry in dir, if existing check validity
382        const char *n = gSystem->GetDirEntry(fDirPtr);
383        if (!n)
384        {
385            // Otherwise close directory and try to get next entry
386            Close();
387            continue;
388        }
389
390        if (nocheck || CheckEntry(n))
391            return ConcatFileName(fCurrentPath->GetName(), n);
392    }
393
394    return "";
395}
396
397// --------------------------------------------------------------------------
398//
399// Print a single entry in the list
400//
401void MDirIter::PrintEntry(const TObject &o) const
402{
403    TString p = o.GetName();
404    TString f = o.GetTitle();
405    cout << p;
406    if (!f.IsNull())
407        cout << " <" << f << ">";
408    cout << endl;
409}
410
411// --------------------------------------------------------------------------
412//
413// Print all scheduled directories. If "all" is specified also all
414// matching entries are printed.
415//
416void MDirIter::Print(const Option_t *opt) const
417{
418    TString s(opt);
419    if (s.Contains("dbg", TString::kIgnoreCase))
420        fList.Print();
421
422    if (!s.Contains("all", TString::kIgnoreCase))
423    {
424        TIter NextD(&fList);
425        TObject *o=NULL;
426        while ((o=NextD()))
427            PrintEntry(*o);
428        return;
429    }
430
431    MDirIter NextD(*this);
432    TString name;
433    TString d;
434    while (!(name=NextD()).IsNull())
435    {
436        const TString p = NextD.fCurrentPath->GetName();
437        if (p!=d)
438        {
439            d=p;
440            PrintEntry(*NextD.fCurrentPath);
441        }
442        cout << " " << name << endl;
443    }
444}
445
446// --------------------------------------------------------------------------
447//
448// Loop over all contents (files). Sort the files alphabetically.
449// Delete the contents of this DirIter and add all sorted files
450// to this DirIter.
451//
452void MDirIter::Sort()
453{
454    MDirIter NextD(*this);
455
456    TList l;
457    l.SetOwner();
458
459    TString name;
460    while (!(name=NextD()).IsNull())
461        l.Add(new TNamed(name.Data(), ""));
462
463    l.Sort();
464
465    fList.Delete();
466    Close();
467    fFilter = "";
468
469    TIter NextN(&l);
470    TObject *o=0;
471    while ((o=NextN()))
472    {
473        TString dir   = o->GetName();
474        TString fname = o->GetName();
475
476        const Int_t last = dir.Last('/');
477        if (last<0)
478            continue;
479
480        dir.Remove(last);
481        fname.Remove(0, last+1);
482
483        AddDirectory(dir, fname);
484    }
485}
Note: See TracBrowser for help on using the repository browser.