Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Ticket #587: FileScan.d

File FileScan.d, 8.9 kB (added by flithm, 1 year ago)

Complete FileScan? with patch applied

Line 
1 /*******************************************************************************
2
3         copyright:      Copyright (c) 2004 Kris Bell. All rights reserved
4
5         license:        BSD style: $(LICENSE)
6
7         version:        Jun 2004: Initial release
8         version:        Dec 2006: Pacific release
9
10         author:         Kris
11
12 *******************************************************************************/
13
14 module tango.io.FileScan;
15
16 public import tango.io.FilePath;
17
18 /*******************************************************************************
19
20         Scan files and directories (recursively by default), adding filtered
21         files to an output structure as we go. This can be used to produce a
22         list of subdirectories and the files contained therein. The following
23         example lists all files with suffix ".d" located via the current
24         directory, along with the folders containing them:
25
26     Examples:
27         ---
28         auto scan = new FileScan;
29
30         scan (".", ".d");
31
32         Stdout.formatln ("{0} Folders", scan.folders.length);
33         foreach (folder; scan.folders)
34                  Stdout.formatln ("{0}", folder);
35
36         Stdout.formatln ("\n{0} Files", scan.files.length);
37         foreach (file; scan.files)
38                  Stdout.formatln ("{0}", file);
39         ---
40
41         This is unlikely the most efficient method to scan a vast number of
42         files, but operates in a convenient manner
43         
44         The above code will result in the scan terminating early if an error
45         is encountered along the way (such as permission denied, or a malformed
46         soft-link, etc).  In order to continue in the event of errors use the
47         stopOnError parameter of sweep:
48         ---
49         auto scan = new FileScan;
50
51         scan (".", ".d", true);
52         
53         foreach (idx, file; scan.errors)
54             Stdout.format("Error [{0}]: {1} -- {2}", idx, file.path.toUtf8, file.msg);
55         ---
56 *******************************************************************************/
57
58 class FileScan
59 {      
60         alias sweep opCall;
61
62         uint            total_;
63         FilePath[]      files_,
64                         folders_;
65         FileScanError[] errors_;
66        
67         /***********************************************************************
68
69             Alias for Filter delegate. Accepts a FilePath & a bool as
70             arguments and returns a bool.
71
72             The FilePath argument represents a file found by the scan,
73             and the bool whether the FilePath represents a folder.
74
75             The filter should return true, if matched by the filter. Note
76             that returning false where the path is a folder will result
77             in all files contained being ignored. To always recurse folders,
78             do something like this:
79             ---
80             return (isDir || match (fp.name));
81             ---
82
83         ***********************************************************************/
84
85         alias bool delegate (FilePath, bool) Filter;
86
87         /***********************************************************************
88
89                 Return the number of files found in the last scan
90
91         ***********************************************************************/
92
93         public uint inspected ()
94         {
95                 return total_;
96         }
97
98         /***********************************************************************
99
100                 Return all the files found in the last scan
101
102         ***********************************************************************/
103
104         public FilePath[] files ()
105         {
106                 return files_;
107         }
108
109         /***********************************************************************
110         
111                 Return all directories found in the last scan
112
113         ***********************************************************************/
114
115         public FilePath[] folders ()
116         {
117                 return folders_;
118         }
119
120         /***********************************************************************
121         
122                 Return all errors found in the last scan
123
124         ***********************************************************************/
125
126         public FileScanError[] errors ()
127         {
128                 return errors_;
129         }
130        
131         /***********************************************************************
132
133                 Sweep a set of files and directories from the given parent
134                 path, where the files are filtered by the given suffix
135                 
136                 Params:  path =         The path to sweep
137                          match =        The pattern to match while sweeping
138                          stopOnError =  If true sweep will throw an exception
139                                         on encountering an error
140                          recurse =  Do a recursive scan if true
141         
142         ***********************************************************************/
143        
144         FileScan sweep (char[] path, char[] match, bool stopOnError = true, bool recurse = true)
145         {
146                 return sweep (path, (FilePath fp, bool isDir)
147                              {return isDir || match == "" || fp.suffix == match;}, stopOnError, recurse);
148         }
149
150         /***********************************************************************
151
152                 Sweep a set of files and directories from the given parent
153                 path, where the files are filtered by the provided delegate
154
155                 Params:  path =         The path to sweep
156                          match =        The pattern to match while sweeping
157                          stopOnError =  If true sweep will throw an exception
158                                         on encountering an error
159                          recurse =  Do a recursive scan if true
160         
161         ***********************************************************************/
162        
163         FileScan sweep (char[] path, Filter filter, bool stopOnError = true, bool recurse = true)
164         {
165                 total_ = 0;
166                 files_ = folders_ = null;
167                 errors_ = null;
168                 return scan (new FilePath(path), filter, stopOnError, recurse);
169         }
170
171         /***********************************************************************
172
173                 Internal routine to locate files and sub-directories. We
174                 skip folders composed only of '.' chars.
175
176         ***********************************************************************/
177
178         private FileScan scan (FilePath folder, Filter filter, bool stopOnError, bool recurse)
179         {
180                 FilePath[] paths;
181
182                 void add (char[] prefix, char[] name, bool isDir)
183                 {
184                         char[512] tmp;
185
186                         int len = prefix.length + name.length;
187                         assert (len < tmp.length);
188                         ++total_;
189
190                         // construct full pathname
191                         tmp[0..prefix.length] = prefix;
192                         tmp[prefix.length..len] = name;
193
194                         auto p = new FilePath (tmp[0 .. len], isDir);
195
196                         // test this entry for inclusion
197                         if (filter (p, isDir))
198                            {
199                            // skip dirs composed only of '.'
200                            if (name.length > 3 || name != "..."[0 .. name.length])
201                                paths ~= p;                                                   
202                            }
203                         else
204                            delete p;
205                 }
206
207         try {
208                     folder.toList (&add);
209                 } catch (Exception e) {
210                     if (stopOnError)
211                         throw e;
212                     else {
213                         errors_ ~= new FileScanError(folder, e.msg);
214                         return this;
215                     }
216                 }
217                 auto count = files_.length;
218                
219                 foreach (path; paths)
220                          if (path.isDir)
221                              if (recurse)
222                                   scan (path, filter, stopOnError, recurse);
223                              else
224                                   folders_ ~= path;
225                          else
226                             files_ ~= path;
227                
228                 // add packages only if there's something in them
229                 if (files_.length > count)
230                     folders_ ~= folder;
231
232                 return this;
233         }
234 }
235
236 /*******************************************************************************
237
238     Hold information about errors encountered during the scan
239     
240     Params: path =      path the triggered the errors
241         msg =       reason behind the error
242
243 *******************************************************************************/
244
245 class FileScanError {
246     FilePath    path;
247     char []     msg;
248    
249     this(FilePath Path, char [] Msg) {
250         path = Path;
251         msg = Msg;
252     }
253 }