00001 /******************************************************************************* 00002 00003 @file FileScan.d 00004 00005 Copyright (c) 2004 Kris Bell 00006 00007 This software is provided 'as-is', without any express or implied 00008 warranty. In no event will the authors be held liable for damages 00009 of any kind arising from the use of this software. 00010 00011 Permission is hereby granted to anyone to use this software for any 00012 purpose, including commercial applications, and to alter it and/or 00013 redistribute it freely, subject to the following restrictions: 00014 00015 1. The origin of this software must not be misrepresented; you must 00016 not claim that you wrote the original software. If you use this 00017 software in a product, an acknowledgment within documentation of 00018 said product would be appreciated but is not required. 00019 00020 2. Altered source versions must be plainly marked as such, and must 00021 not be misrepresented as being the original software. 00022 00023 3. This notice may not be removed or altered from any distribution 00024 of the source. 00025 00026 4. Derivative works are permitted, but they must carry this notice 00027 in full and credit the original source. 00028 00029 00030 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00031 00032 00033 @version Initial version; June 2004 00034 00035 @author Chris Sauls 00036 Kris 00037 00038 00039 *******************************************************************************/ 00040 00041 module mango.io.FileScan; 00042 00043 public import mango.io.File, 00044 mango.io.FilePath; 00045 00046 /******************************************************************************* 00047 00048 Recursively scan files and directories, adding filtered files to 00049 an output structure as we go. This can be used to produce a list 00050 of subdirectories and the files contained therein. Usage example: 00051 00052 @code 00053 void files (File file) 00054 { 00055 Stdout (file.getPath) (CR); 00056 } 00057 00058 void dirs (FilePath path) 00059 { 00060 Stdout (path) (CR); 00061 } 00062 00063 FileScan scan = new FileScan; 00064 00065 // find all files with a 'd' extension 00066 scan ((args.length == 2) ? args[1] : ".", "d"); 00067 00068 Stdout ("directories:") (CR); 00069 scan.directories (&dirs); 00070 00071 Stdout (CR) ("files:") (CR); 00072 scan.files (&files); 00073 @endcode 00074 00075 *******************************************************************************/ 00076 00077 class FileScan 00078 { 00079 alias read opCall; 00080 00081 private char[] ext; 00082 private Dependencies deps; 00083 private Filter filter; 00084 00085 typedef bool delegate (FilePath) Filter; 00086 00087 /*********************************************************************** 00088 00089 The list of files and sub-directories found. 00090 00091 ***********************************************************************/ 00092 00093 private struct Dependencies 00094 { 00095 File[] mods; 00096 FilePath[] pkgs; 00097 } 00098 00099 /*********************************************************************** 00100 00101 Read a set of files and directories from the given parent 00102 path, where the files are filtered by the given extension. 00103 00104 ***********************************************************************/ 00105 00106 FileScan read (char[] path, char[] ext) 00107 { 00108 return read (new FilePath(path), ext); 00109 } 00110 00111 /*********************************************************************** 00112 00113 Read a set of files and directories from the given parent 00114 path, where the files are filtered by the given extension. 00115 00116 ***********************************************************************/ 00117 00118 FileScan read (FilePath path, char[] ext) 00119 { 00120 this.ext = ext; 00121 return read (path, &simpleFilter); 00122 } 00123 00124 /*********************************************************************** 00125 00126 Read a set of files and directories from the given parent 00127 path, where the files are filtered by the provided delegate. 00128 00129 ***********************************************************************/ 00130 00131 FileScan read (FilePath path, Filter filter) 00132 { 00133 deps.mods = null; 00134 deps.pkgs = null; 00135 this.filter = filter; 00136 scanFiles (deps, path); 00137 return this; 00138 } 00139 00140 /*********************************************************************** 00141 00142 Visit all the files found in the last scan. The delegate 00143 should return false to terminate early. 00144 00145 ***********************************************************************/ 00146 00147 public FileScan files (void delegate (File) visit) 00148 { 00149 foreach (File file; deps.mods) 00150 visit (file); 00151 return this; 00152 } 00153 00154 /*********************************************************************** 00155 00156 Visit all directories found in the last scan. The delegate 00157 should return false to terminate early. 00158 00159 ***********************************************************************/ 00160 00161 public FileScan directories (void delegate (FilePath) visit) 00162 { 00163 foreach (FilePath path; deps.pkgs) 00164 visit (path); 00165 return this; 00166 } 00167 00168 /*********************************************************************** 00169 00170 Local delegate for filtering files based upon a provided 00171 extension 00172 00173 ***********************************************************************/ 00174 00175 private bool simpleFilter (FilePath fp) 00176 { 00177 char[] sbuf = fp.getExtension; 00178 00179 if (fp.getName[0] != '.') 00180 if (sbuf.length == 0 || sbuf == ext) 00181 return true; 00182 00183 return false; 00184 } 00185 00186 /*********************************************************************** 00187 00188 Recursive routine to locate files and sub-directories. 00189 00190 ***********************************************************************/ 00191 00192 private void scanFiles (inout Dependencies deps, FilePath base) 00193 { 00194 File file = new File (base); 00195 FilePath[] paths = file.toList (filter); 00196 00197 // add packages only if there's something in them 00198 if (paths.length) 00199 deps.pkgs ~= base; 00200 00201 foreach (FilePath x; paths) 00202 { 00203 // combine base path with listed file 00204 FilePath spliced = new FilePath (x.splice (base)); 00205 00206 // recurse if this is a directory ... 00207 file = new File (spliced); 00208 if (file.isDirectory) 00209 scanFiles (deps, spliced); 00210 else 00211 deps.mods ~= file; 00212 } 00213 } 00214 } 00215