4215deb0fb4f1673f18c9de29b921d9fb254bc2c
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / DirectoryScanner.cs
1 //
2 // DirectoryScanner.cs: Class used by BuildItem.
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 // 
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 #if NET_2_0
29
30 using System;
31 using System.Collections.Generic;
32 using System.IO;
33
34 using Microsoft.Build.Framework;
35 using Microsoft.Build.Utilities;
36
37 namespace Microsoft.Build.BuildEngine {
38         internal class DirectoryScanner {
39                 
40                 DirectoryInfo   baseDirectory;
41                 ITaskItem[]     includes, excludes;
42                 ITaskItem[]     matchedItems;
43
44                 static bool _runningOnWindows;
45                 
46                 static DirectoryScanner ()
47                 {
48                         PlatformID pid = Environment.OSVersion.Platform;
49                         _runningOnWindows =((int) pid != 128 && (int) pid != 4 && (int) pid != 6);
50                 }
51
52                 public DirectoryScanner ()
53                 {
54                 }
55                 
56                 public void Scan ()
57                 {
58                         Dictionary <string, bool> excludedItems;
59                         List <ITaskItem> includedItems;
60                         string[] splitExclude;
61                         
62                         if (includes == null)
63                                 throw new ArgumentNullException ("Includes");
64                         if (baseDirectory == null)
65                                 throw new ArgumentNullException ("BaseDirectory");
66                         
67                         excludedItems = new Dictionary <string, bool> ();
68                         includedItems = new List <ITaskItem> ();
69                         
70                         if (excludes != null)
71                                 foreach (ITaskItem excl in excludes)
72                                         ProcessExclude (excl.ItemSpec, excludedItems);
73
74                         foreach (ITaskItem include_item in includes)
75                                 ProcessInclude (include_item, excludedItems, includedItems);
76
77                         matchedItems = includedItems.ToArray ();
78                 }
79                 
80                 private void ProcessInclude (ITaskItem include_item, Dictionary <string, bool> excludedItems,
81                                 List <ITaskItem> includedItems)
82                 {
83                         string[] separatedPath;
84                         FileInfo[] fileInfo;
85
86                         string name = include_item.ItemSpec;
87                         if (name.IndexOf ('?') == -1 && name.IndexOf ('*') == -1) {
88                                 if (!excludedItems.ContainsKey (Path.GetFullPath(name)))
89                                         includedItems.Add (include_item);
90                         } else {
91                                 if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
92                                         separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
93                                                         StringSplitOptions.RemoveEmptyEntries);
94                                 } else {
95                                         separatedPath = name.Split (new char [] {Path.AltDirectorySeparatorChar},
96                                                         StringSplitOptions.RemoveEmptyEntries);
97                                 }
98                                 if (separatedPath.Length == 1 && separatedPath [0] == String.Empty)
99                                         return;
100
101                                 int offset = 0;
102                                 if (Path.IsPathRooted (name)) {
103                                         baseDirectory = new DirectoryInfo (Path.GetPathRoot (name));
104                                         if (IsRunningOnWindows)
105                                                 // skip the "drive:"
106                                                 offset = 1;
107                                 }
108
109                                 string full_path = Path.GetFullPath (Path.Combine (Environment.CurrentDirectory, include_item.ItemSpec));
110                                 fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
111
112                                 int wildcard_offset = full_path.IndexOf ("**");
113                                 foreach (FileInfo fi in fileInfo) {
114                                         if (!excludedItems.ContainsKey (fi.FullName)) {
115                                                 TaskItem item = new TaskItem (include_item);
116                                                 item.ItemSpec = fi.FullName;
117                                                 if (wildcard_offset >= 0) {
118                                                         string rec_dir = Path.GetDirectoryName (fi.FullName.Substring (wildcard_offset));
119                                                         if (rec_dir.Length > 0)
120                                                                 rec_dir += Path.DirectorySeparatorChar;
121                                                         item.SetMetadata ("RecursiveDir", rec_dir);
122                                                 }
123                                                 includedItems.Add (item);
124                                         }
125                                 }
126                         }
127                 }
128                 
129                 private void ProcessExclude (string name, Dictionary <string, bool> excludedItems)
130                 {
131                         string[] separatedPath;
132                         FileInfo[] fileInfo;
133                         
134                         if (name.IndexOf ('?') == -1 && name.IndexOf ('*') == -1) {
135                                 if (!excludedItems.ContainsKey (Path.GetFullPath (name)))
136                                         excludedItems.Add (Path.GetFullPath (name), true);
137                         } else {
138                                 if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
139                                         separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
140                                                                         StringSplitOptions.RemoveEmptyEntries);
141                                 } else {
142                                         separatedPath = name.Split (new char [] {Path.AltDirectorySeparatorChar},
143                                                                         StringSplitOptions.RemoveEmptyEntries);
144                                 }
145                                 if (separatedPath.Length == 1 && separatedPath [0] == String.Empty)
146                                         return;
147
148                                 int offset = 0;
149                                 if (Path.IsPathRooted (name)) {
150                                         baseDirectory = new DirectoryInfo (Path.GetPathRoot (name));
151                                         if (IsRunningOnWindows)
152                                                 // skip the "drive:"
153                                                 offset = 1;
154                                 }
155                                 fileInfo = ParseIncludeExclude (separatedPath, offset, baseDirectory);
156                                 foreach (FileInfo fi in fileInfo)
157                                         if (!excludedItems.ContainsKey (fi.FullName))
158                                                 excludedItems.Add (fi.FullName, true);
159                         }
160                 }
161                 
162                 private FileInfo[] ParseIncludeExclude (string[] input, int ptr, DirectoryInfo directory)
163                 {
164                         if (input.Length > 1 && ptr == 0 && input [0] == String.Empty)
165                                 ptr++;
166                         if (input.Length == ptr + 1) {
167                                 FileInfo[] fi;
168                                 fi = directory.GetFiles (input [ptr]);
169                                 return fi;
170                         } else {
171                                 DirectoryInfo[] di;
172                                 FileInfo[] fi;
173                                 List <FileInfo> fileInfos = new List <FileInfo> ();
174                                 if (input [ptr] == ".") {
175                                         di = new DirectoryInfo [1];
176                                         di [0] = directory;
177                                 } else if (input [ptr] == "..") {
178                                         di = new DirectoryInfo [1];
179                                         di [0] = directory.Parent;
180                                 } else if (input[ptr] == "**")
181                                 {
182                                         // Read this directory and all subdirectories recursive
183                                         Stack<DirectoryInfo> currentDirectories = new Stack<DirectoryInfo>();                                   
184                                         currentDirectories.Push(directory);
185                                         List<DirectoryInfo> allDirectories = new List<DirectoryInfo>();
186                                         
187                                         while (currentDirectories.Count > 0)
188                                         {
189                                                 DirectoryInfo current = currentDirectories.Pop();
190                                                 allDirectories.Insert (0, current);
191                                                 foreach (DirectoryInfo dir in current.GetDirectories())
192                                                 {
193                                                         currentDirectories.Push(dir);
194                                                 }                                               
195                                         }
196                                         
197                                         // No further directories shall be read
198                                         di = allDirectories.ToArray();                                  
199                                 } else {
200                                         di = directory.GetDirectories (input [ptr]);
201                                 }
202                                 foreach (DirectoryInfo info in di) {
203                                         fi = ParseIncludeExclude (input, ptr + 1, info);
204                                         foreach (FileInfo file in fi)
205                                                 fileInfos.Add (file);
206                                 }
207                                 fi = new FileInfo [fileInfos.Count];
208                                 int i = 0;
209                                 foreach (FileInfo file in fileInfos)
210                                         fi [i++] = file;
211                                 return fi;
212                         }
213                 }
214                 
215                 public DirectoryInfo BaseDirectory {
216                         get { return baseDirectory; }
217                         set { baseDirectory = value; }
218                 }
219                 
220                 public ITaskItem[] Includes {
221                         get { return includes; }
222                         set { includes = value; }
223                 }
224                 
225                 public ITaskItem[] Excludes {
226                         get { return excludes; }
227                         set { excludes = value; }
228                 }
229                 
230                 public ITaskItem[] MatchedItems {
231                         get { return matchedItems; }
232                 }
233                 
234                 static bool IsRunningOnWindows {
235                         get { return _runningOnWindows; }
236                 }
237         }
238 }
239
240 #endif