New tests.
[mono.git] / mcs / class / corlib / System.IO / DirectoryInfo.cs
1 // 
2 // System.IO.DirectoryInfo.cs 
3 //
4 // Authors:
5 //   Miguel de Icaza, miguel@ximian.com
6 //   Jim Richardson, develop@wtfo-guru.com
7 //   Dan Lewis, dihlewis@yahoo.co.uk
8 //   Sebastien Pouliot  <sebastien@ximian.com>
9 //
10 // Copyright (C) 2002 Ximian, Inc.
11 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
12 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Runtime.InteropServices;
37 using System.Runtime.Serialization;
38 using System.Text;
39 #if !MOONLIGHT
40 using System.Security.AccessControl;
41 #endif
42
43 namespace System.IO {
44         
45         [Serializable]
46         [ComVisible (true)]
47         public sealed class DirectoryInfo : FileSystemInfo {
48
49                 private string current;
50                 private string parent;
51         
52                 public DirectoryInfo (string path) : this (path, false)
53                 {
54                 }
55
56                 internal DirectoryInfo (string path, bool simpleOriginalPath)
57                 {
58                         CheckPath (path);
59
60                         FullPath = Path.GetFullPath (path);
61                         if (simpleOriginalPath)
62                                 OriginalPath = Path.GetFileName (path);
63                         else
64                                 OriginalPath = path;
65
66                         Initialize ();
67                 }
68
69                 private DirectoryInfo (SerializationInfo info, StreamingContext context)
70                         : base (info, context)
71                 {
72                         Initialize ();
73                 }
74
75                 void Initialize ()
76                 {
77                         int len = FullPath.Length - 1;
78                         if ((len > 1) && (FullPath [len] == Path.DirectorySeparatorChar))
79                                 len--;
80                         int last = FullPath.LastIndexOf (Path.DirectorySeparatorChar, len);
81                         if ((last == -1) || ((last == 0) && (len == 0))) {
82                                 current = FullPath;
83                                 parent = null;
84                         } else {
85                                 current = FullPath.Substring (last + 1, len - last);
86                                 if (last == 0 && !Environment.IsRunningOnWindows)
87                                         parent = Path.DirectorySeparatorStr;
88                                 else
89                                         parent = FullPath.Substring (0, last);
90                                 // adjust for drives, i.e. a special case for windows
91                                 if (Environment.IsRunningOnWindows) {
92                                         if ((parent.Length == 2) && (parent [1] == ':') && Char.IsLetter (parent [0]))
93                                                 parent += Path.DirectorySeparatorChar;
94                                 }
95                         }
96                 }
97
98                 // properties
99
100                 public override bool Exists {
101                         get {
102                                 Refresh (false);
103
104                                 if (stat.Attributes == MonoIO.InvalidFileAttributes)
105                                         return false;
106
107                                 if ((stat.Attributes & FileAttributes.Directory) == 0)
108                                         return false;
109
110                                 return true;
111                         }
112                 }
113
114                 public override string Name {
115                         get { return current; }
116                 }
117
118                 public DirectoryInfo Parent {
119                         get {
120                                 if ((parent == null) || (parent.Length == 0))
121                                         return null;
122                                 return new DirectoryInfo (parent);
123                         }
124                 }
125
126                 public DirectoryInfo Root {
127                         get {
128                                 string root = Path.GetPathRoot (FullPath);
129                                 if (root == null)
130                                         return null;
131
132                                 return new DirectoryInfo (root);
133                         }
134                 }
135
136                 // creational methods
137
138                 public void Create ()
139                 {
140                         Directory.CreateDirectory (FullPath);
141                 }
142
143                 public DirectoryInfo CreateSubdirectory (string path)
144                 {
145                         CheckPath (path);
146
147                         path = Path.Combine (FullPath, path);
148                         Directory.CreateDirectory (path);
149                         return new DirectoryInfo (path);
150                 }
151
152                 // directory listing methods
153
154                 public FileInfo [] GetFiles ()
155                 {
156                         return GetFiles ("*");
157                 }
158
159                 public FileInfo [] GetFiles (string searchPattern)
160                 {
161                         if (searchPattern == null)
162                                 throw new ArgumentNullException ("searchPattern");
163
164                         string [] names = Directory.GetFiles (FullPath, searchPattern);
165
166                         FileInfo[] infos = new FileInfo [names.Length];
167                         int i = 0;
168                         foreach (string name in names)
169                                 infos [i++] = new FileInfo (name);
170
171                         return infos;
172                 }
173
174                 public DirectoryInfo [] GetDirectories ()
175                 {
176                         return GetDirectories ("*");
177                 }
178
179                 public DirectoryInfo [] GetDirectories (string searchPattern)
180                 {
181                         if (searchPattern == null)
182                                 throw new ArgumentNullException ("searchPattern");
183
184                         string [] names = Directory.GetDirectories (FullPath, searchPattern);
185
186                         DirectoryInfo[] infos = new DirectoryInfo [names.Length];
187                         int i = 0;
188                         foreach (string name in names)
189                                 infos [i++] = new DirectoryInfo (name);
190
191                         return infos;
192                 }
193
194                 public FileSystemInfo [] GetFileSystemInfos ()
195                 {
196                         return GetFileSystemInfos ("*");
197                 }
198
199                 public FileSystemInfo [] GetFileSystemInfos (string searchPattern)
200                 {
201                         return GetFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
202                 }
203
204 #if NET_4_0
205                 public
206 #endif
207                 FileSystemInfo [] GetFileSystemInfos (string searchPattern, SearchOption searchOption)
208                 {
209                         if (searchPattern == null)
210                                 throw new ArgumentNullException ("searchPattern");
211                         if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
212                                 throw new ArgumentOutOfRangeException ("searchOption", "Must be TopDirectoryOnly or AllDirectories");
213                         if (!Directory.Exists (FullPath))
214                                 throw new IOException ("Invalid directory");
215
216                         List<FileSystemInfo> infos = new List<FileSystemInfo> ();
217                         InternalGetFileSystemInfos (searchPattern, searchOption, infos);
218                         return infos.ToArray ();
219                 }
220
221                 void InternalGetFileSystemInfos (string searchPattern, SearchOption searchOption, List<FileSystemInfo> infos)
222                 {
223                         // UnauthorizedAccessExceptions might happen here and break everything for SearchOption.AllDirectories
224                         string [] dirs = Directory.GetDirectories (FullPath, searchPattern);
225                         string [] files = Directory.GetFiles (FullPath, searchPattern);
226
227                         Array.ForEach<string> (dirs, (dir) => { infos.Add (new DirectoryInfo (dir)); });
228                         Array.ForEach<string> (files, (file) => { infos.Add (new FileInfo (file)); });
229                         if (dirs.Length == 0 || searchOption == SearchOption.TopDirectoryOnly)
230                                 return;
231
232                         foreach (string dir in dirs) {
233                                 DirectoryInfo dinfo = new DirectoryInfo (dir);
234                                 dinfo.InternalGetFileSystemInfos (searchPattern, searchOption, infos);
235                         }
236                 }
237
238                 // directory management methods
239
240                 public override void Delete ()
241                 {
242                         Delete (false);
243                 }
244
245                 public void Delete (bool recursive)
246                 {
247                         Directory.Delete (FullPath, recursive);
248                 }
249
250                 public void MoveTo (string destDirName)
251                 {
252                         if (destDirName == null)
253                                 throw new ArgumentNullException ("destDirName");
254                         if (destDirName.Length == 0)
255                                 throw new ArgumentException ("An empty file name is not valid.", "destDirName");
256
257                         Directory.Move (FullPath, Path.GetFullPath (destDirName));
258                 }
259
260                 public override string ToString ()
261                 {
262                         return OriginalPath;
263                 }
264
265 #if !MOONLIGHT
266                 public DirectoryInfo[] GetDirectories (string searchPattern, SearchOption searchOption)
267                 {
268                         switch (searchOption) {
269                         case SearchOption.TopDirectoryOnly:
270                                 return GetDirectories (searchPattern);
271                         case SearchOption.AllDirectories:
272                                 Queue workq = new Queue(GetDirectories(searchPattern));
273                                 Queue doneq = new Queue();
274                                 while (workq.Count > 0)
275                                         {
276                                                 DirectoryInfo cinfo = (DirectoryInfo) workq.Dequeue();
277                                                 DirectoryInfo[] cinfoDirs = cinfo.GetDirectories(searchPattern);
278                                                 foreach (DirectoryInfo i in cinfoDirs) workq.Enqueue(i);
279                                                 doneq.Enqueue(cinfo);
280                                         }
281
282                                 DirectoryInfo[] infos = new DirectoryInfo[doneq.Count];
283                                 doneq.CopyTo(infos, 0);
284                                 return infos;
285                         default:
286                                 string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", searchOption, "SearchOption");
287                                 throw new ArgumentOutOfRangeException ("searchOption", msg);
288                         }
289                 }       
290
291                 internal int GetFilesSubdirs (ArrayList l, string pattern)
292                 {
293                         int count;
294                         FileInfo [] thisdir = null;
295
296                         try {
297                                 thisdir = GetFiles (pattern);
298                         } catch (System.UnauthorizedAccessException){
299                                 return 0;
300                         }
301                         
302                         count = thisdir.Length;
303                         l.Add (thisdir);
304
305                         foreach (DirectoryInfo subdir in GetDirectories ()){
306                                 count += subdir.GetFilesSubdirs (l, pattern);
307                         }
308                         return count;
309                 }
310                 
311                 public FileInfo[] GetFiles (string searchPattern, SearchOption searchOption)
312                 {
313                         switch (searchOption) {
314                         case SearchOption.TopDirectoryOnly:
315                                 return GetFiles (searchPattern);
316                         case SearchOption.AllDirectories: {
317                                 ArrayList groups = new ArrayList ();
318                                 int count = GetFilesSubdirs (groups, searchPattern);
319                                 int current = 0;
320                                 
321                                 FileInfo [] all = new FileInfo [count];
322                                 foreach (FileInfo [] p in groups){
323                                         p.CopyTo (all, current);
324                                         current += p.Length;
325                                 }
326                                 return all;
327                         }
328                         default:
329                                 string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", searchOption, "SearchOption");
330                                 throw new ArgumentOutOfRangeException ("searchOption", msg);
331                         }
332                 }
333
334                 // access control methods
335
336                 [MonoLimitation ("DirectorySecurity isn't implemented")]
337                 public void Create (DirectorySecurity directorySecurity)
338                 {
339                         if (directorySecurity != null)
340                                 throw new UnauthorizedAccessException ();
341                         Create ();
342                 }
343
344                 [MonoLimitation ("DirectorySecurity isn't implemented")]
345                 public DirectoryInfo CreateSubdirectory (string path, DirectorySecurity directorySecurity)
346                 {
347                         if (directorySecurity != null)
348                                 throw new UnauthorizedAccessException ();
349                         return CreateSubdirectory (path);
350                 }
351
352                 [MonoNotSupported ("DirectorySecurity isn't implemented")]
353                 public DirectorySecurity GetAccessControl ()
354                 {
355                         throw new UnauthorizedAccessException ();
356                 }
357
358                 [MonoNotSupported ("DirectorySecurity isn't implemented")]
359                 public DirectorySecurity GetAccessControl (AccessControlSections includeSections)
360                 {
361                         throw new UnauthorizedAccessException ();
362                 }
363
364                 [MonoLimitation ("DirectorySecurity isn't implemented")]
365                 public void SetAccessControl (DirectorySecurity directorySecurity)
366                 {
367                         if (directorySecurity != null)
368                                 throw new ArgumentNullException ("directorySecurity");
369                         throw new UnauthorizedAccessException ();
370                 }
371 #endif
372
373 #if NET_4_0 || MOONLIGHT
374
375                 public IEnumerable<DirectoryInfo> EnumerateDirectories ()
376                 {
377                         return EnumerateDirectories ("*", SearchOption.TopDirectoryOnly);
378                 }
379
380                 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern)
381                 {
382                         return EnumerateDirectories (searchPattern, SearchOption.TopDirectoryOnly);
383                 }
384
385                 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern, SearchOption searchOption)
386                 {
387                         foreach (string name in Directory.EnumerateDirectories (FullPath, searchPattern, searchOption))
388                                 yield return new DirectoryInfo (name);
389                 }
390
391                 public IEnumerable<FileInfo> EnumerateFiles ()
392                 {
393                         return EnumerateFiles ("*", SearchOption.TopDirectoryOnly);
394                 }
395
396                 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern)
397                 {
398                         return EnumerateFiles (searchPattern, SearchOption.TopDirectoryOnly);
399                 }
400
401                 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern, SearchOption searchOption)
402                 {
403                         foreach (string name in Directory.EnumerateFiles (FullPath, searchPattern, searchOption))
404                                 yield return new FileInfo (name);
405                 }
406
407                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos ()
408                 {
409                         return EnumerateFileSystemInfos ("*", SearchOption.TopDirectoryOnly);
410                 }
411
412                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern)
413                 {
414                         return EnumerateFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
415                 }
416
417                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern, SearchOption searchOption)
418                 {
419                         if (searchPattern == null)
420                                 throw new ArgumentNullException ("searchPattern");
421                         if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
422                                 throw new ArgumentOutOfRangeException ("searchoption");
423
424                         return EnumerateFileSystemInfos (FullPath, searchPattern, searchOption);
425                 }
426
427                 static internal IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string full, string searchPattern, SearchOption searchOption)
428                 {
429                         string path_with_pattern = Path.Combine (full, searchPattern);
430                         IntPtr handle;
431                         MonoIOError error;
432                         FileAttributes rattr;
433                         bool subdirs = searchOption == SearchOption.AllDirectories;
434                         
435                         string s = MonoIO.FindFirst (full, path_with_pattern, out rattr, out error, out handle);
436                         if (s == null)
437                                 yield break;
438                         if (error != 0)
439                                 throw MonoIO.GetException (Path.GetDirectoryName (path_with_pattern), (MonoIOError) error);
440
441                         try {
442                                 if (((rattr & FileAttributes.ReparsePoint) == 0)){
443                                         if ((rattr & FileAttributes.Directory) != 0)
444                                                 yield return new DirectoryInfo (s);
445                                         else
446                                                 yield return new FileInfo (s);
447                                 }
448                                 
449                                 while ((s = MonoIO.FindNext (handle, out rattr, out error)) != null){
450                                         if ((rattr & FileAttributes.ReparsePoint) != 0)
451                                                 continue;
452                                         if ((rattr & FileAttributes.Directory) != 0)
453                                                 yield return new DirectoryInfo (s);
454                                         else
455                                                 yield return new FileInfo (s);
456                                         
457                                         if (((rattr & FileAttributes.Directory) != 0) && subdirs)
458                                                 foreach (FileSystemInfo child in EnumerateFileSystemInfos (s, searchPattern, searchOption))
459                                                         yield return child;
460                                 }
461                         } finally {
462                                 MonoIO.FindClose (handle);
463                         }
464                 }
465                 
466                 
467 #endif
468         }
469 }