1 // NAnt - A .NET build tool
2 // Copyright (C) 2001 Gerry Shaw
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 // Gerry Shaw (gerry_shaw@yahoo.com)
22 "**\*.class" matches all .class files/dirs in a directory tree.
24 "test\a??.java" matches all files/dirs which start with an 'a', then two
25 more characters and then ".java", in a directory called test.
27 "**" matches everything in a directory tree.
29 "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where
30 there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
34 DirectoryScanner scanner = DirectoryScanner();
35 scanner.Includes.Add("**\\*.class");
36 scanner.Exlucdes.Add("modules\\*\\**");
37 scanner.BaseDirectory = "test";
39 foreach (string filename in GetIncludedFiles()) {
40 Console.WriteLine(filename);
44 namespace SourceForge
.NAnt
{
47 using System
.Collections
.Specialized
;
50 using System
.Text
.RegularExpressions
;
52 public class DirectoryScanner
{
54 string _baseDirectory
= Environment
.CurrentDirectory
;
56 // holds the nant patterns
57 StringCollection _includes
= new StringCollection();
58 StringCollection _excludes
= new StringCollection();
60 // holds the nant patterns converted to regular expression patterns
61 StringCollection _includePatterns
= null;
62 StringCollection _excludePatterns
= null;
64 // holds the result from a scan
65 StringCollection _fileNames
= null;
66 StringCollection _directoryNames
= null;
68 public StringCollection Includes
{
69 get { return _includes; }
72 public StringCollection Excludes
{
73 get { return _excludes; }
76 public string BaseDirectory
{
77 get { return _baseDirectory; }
78 set { _baseDirectory = value; }
81 public StringCollection FileNames
{
83 if (_fileNames
== null) {
90 public StringCollection DirectoryNames
{
92 if (_directoryNames
== null) {
95 return _directoryNames
;
100 _includePatterns
= new StringCollection();
101 foreach (string pattern
in Includes
) {
102 _includePatterns
.Add(ToRegexPattern(pattern
));
105 _excludePatterns
= new StringCollection();
106 foreach (string pattern
in Excludes
) {
107 _excludePatterns
.Add(ToRegexPattern(pattern
));
110 _fileNames
= new StringCollection();
111 _directoryNames
= new StringCollection();
113 ScanDirectory(Path
.GetFullPath(BaseDirectory
));
116 void ScanDirectory(string path
) {
117 // get info for the current directory
118 DirectoryInfo currentDirectoryInfo
= new DirectoryInfo(path
);
121 foreach (DirectoryInfo directoryInfo
in currentDirectoryInfo
.GetDirectories()) {
122 ScanDirectory(directoryInfo
.FullName
);
126 foreach (FileInfo fileInfo
in currentDirectoryInfo
.GetFiles()) {
127 string filename
= Path
.Combine(path
, fileInfo
.Name
);
128 if (IsPathIncluded(filename
)) {
129 _fileNames
.Add(filename
);
133 // Check current path last so that delete task will correctly
134 // delete empty directories. This may *seem* like a special case
135 // but it is more like formalizing something in a way that makes
136 // writing the delete task easier :)
137 if (IsPathIncluded(path
)) {
138 _directoryNames
.Add(path
);
142 bool IsPathIncluded(string path
) {
143 bool included
= false;
145 // check path against includes
146 foreach (string pattern
in _includePatterns
) {
147 Match m
= Regex
.Match(path
, pattern
);
154 // check path against excludes
156 foreach (string pattern
in _excludePatterns
) {
157 Match m
= Regex
.Match(path
, pattern
);
168 string ToRegexPattern(string nantPattern
) {
170 StringBuilder pattern
= new StringBuilder(nantPattern
);
172 // NAnt patterns can use either / \ as a directory seperator.
173 // We must replace both of these characters with Path.DirectorySeperatorChar
174 pattern
.Replace('/', Path
.DirectorySeparatorChar
);
175 pattern
.Replace('\\', Path
.DirectorySeparatorChar
);
177 // Patterns MUST be full paths.
178 if (!Path
.IsPathRooted(pattern
.ToString())) {
179 pattern
= new StringBuilder(Path
.Combine(BaseDirectory
, pattern
.ToString()));
182 // The '\' character is a special character in regular expressions
183 // and must be escaped before doing anything else.
184 pattern
.Replace(@"\", @"\\");
186 // Escape the rest of the regular expression special characters.
187 // NOTE: Characters other than . $ ^ { [ ( | ) * + ? \ match themselves.
188 // TODO: Decide if ] and } are missing from this list, the above
189 // list of characters was taking from the .NET SDK docs.
190 pattern
.Replace(".", @"\.");
191 pattern
.Replace("$", @"\$");
192 pattern
.Replace("^", @"\^");
193 pattern
.Replace("{", @"\{");
194 pattern
.Replace("[", @"\[");
195 pattern
.Replace("(", @"\(");
196 pattern
.Replace(")", @"\)");
197 pattern
.Replace("+", @"\+");
199 // Special case directory seperator string under Windows.
200 string seperator
= Path
.DirectorySeparatorChar
.ToString();
201 if (seperator
== @"\") {
205 // Convert NAnt pattern characters to regular expression patterns.
207 // SPECIAL CASE: to match subdirectory OR current directory. If
208 // we don't do this then we can write something like 'src/**/*.cs'
209 // to match all the files ending in .cs in the src directory OR
210 // subdirectories of src.
211 pattern
.Replace(seperator
+ "**", "(" + seperator
+ ".|)|");
213 // | is a place holder for * to prevent it from being replaced in next line
214 pattern
.Replace("**", ".|");
215 pattern
.Replace("*", "[^" + seperator
+ "]*");
216 pattern
.Replace("?", "[^" + seperator
+ "]?");
217 pattern
.Replace('|', '*'); // replace place holder string
219 // Help speed up the search
220 pattern
.Insert(0, '^'); // start of line
221 pattern
.Append('$'); // end of line
223 return pattern
.ToString();