Merge pull request #216 from ilkerde/master
[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.Security;
39 using System.Text;
40 #if !MOONLIGHT
41 using System.Security.AccessControl;
42 #endif
43
44 namespace System.IO {
45         
46         [Serializable]
47         [ComVisible (true)]
48         public sealed class DirectoryInfo : FileSystemInfo {
49
50                 private string current;
51                 private string parent;
52         
53 #if MOONLIGHT
54                 internal DirectoryInfo ()
55                 {
56                 }
57 #endif
58                 public DirectoryInfo (string path) : this (path, false)
59                 {
60                 }
61
62                 internal DirectoryInfo (string path, bool simpleOriginalPath)
63                 {
64                         CheckPath (path);
65
66                         SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
67
68                         FullPath = Path.GetFullPath (path);
69                         if (simpleOriginalPath)
70                                 OriginalPath = Path.GetFileName (path);
71                         else
72                                 OriginalPath = path;
73
74                         Initialize ();
75                 }
76
77                 private DirectoryInfo (SerializationInfo info, StreamingContext context)
78                         : base (info, context)
79                 {
80                         Initialize ();
81                 }
82
83                 void Initialize ()
84                 {
85                         int len = FullPath.Length - 1;
86                         if ((len > 1) && (FullPath [len] == Path.DirectorySeparatorChar))
87                                 len--;
88                         int last = FullPath.LastIndexOf (Path.DirectorySeparatorChar, len);
89                         if ((last == -1) || ((last == 0) && (len == 0))) {
90                                 current = FullPath;
91                                 parent = null;
92                         } else {
93                                 current = FullPath.Substring (last + 1, len - last);
94                                 if (last == 0 && !Environment.IsRunningOnWindows)
95                                         parent = Path.DirectorySeparatorStr;
96                                 else
97                                         parent = FullPath.Substring (0, last);
98                                 // adjust for drives, i.e. a special case for windows
99                                 if (Environment.IsRunningOnWindows) {
100                                         if ((parent.Length == 2) && (parent [1] == ':') && Char.IsLetter (parent [0]))
101                                                 parent += Path.DirectorySeparatorChar;
102                                 }
103                         }
104                 }
105
106                 // properties
107
108                 public override bool Exists {
109                         get {
110                                 Refresh (false);
111
112                                 if (stat.Attributes == MonoIO.InvalidFileAttributes)
113                                         return false;
114
115                                 if ((stat.Attributes & FileAttributes.Directory) == 0)
116                                         return false;
117
118                                 return true;
119                         }
120                 }
121
122                 public override string Name {
123                         get { return current; }
124                 }
125
126                 public DirectoryInfo Parent {
127                         get {
128                                 if ((parent == null) || (parent.Length == 0))
129                                         return null;
130                                 return new DirectoryInfo (parent);
131                         }
132                 }
133
134                 public DirectoryInfo Root {
135                         get {
136                                 string root = Path.GetPathRoot (FullPath);
137                                 if (root == null)
138                                         return null;
139
140                                 return new DirectoryInfo (root);
141                         }
142                 }
143
144                 // creational methods
145
146                 public void Create ()
147                 {
148                         Directory.CreateDirectory (FullPath);
149                 }
150
151                 public DirectoryInfo CreateSubdirectory (string path)
152                 {
153                         CheckPath (path);
154
155                         path = Path.Combine (FullPath, path);
156                         Directory.CreateDirectory (path);
157                         return new DirectoryInfo (path);
158                 }
159
160                 // directory listing methods
161
162                 public FileInfo [] GetFiles ()
163                 {
164                         return GetFiles ("*");
165                 }
166
167                 public FileInfo [] GetFiles (string searchPattern)
168                 {
169                         if (searchPattern == null)
170                                 throw new ArgumentNullException ("searchPattern");
171
172                         string [] names = Directory.GetFiles (FullPath, searchPattern);
173
174                         FileInfo[] infos = new FileInfo [names.Length];
175                         int i = 0;
176                         foreach (string name in names)
177                                 infos [i++] = new FileInfo (name);
178
179                         return infos;
180                 }
181
182                 public DirectoryInfo [] GetDirectories ()
183                 {
184                         return GetDirectories ("*");
185                 }
186
187                 public DirectoryInfo [] GetDirectories (string searchPattern)
188                 {
189                         if (searchPattern == null)
190                                 throw new ArgumentNullException ("searchPattern");
191
192                         string [] names = Directory.GetDirectories (FullPath, searchPattern);
193
194                         DirectoryInfo[] infos = new DirectoryInfo [names.Length];
195                         int i = 0;
196                         foreach (string name in names)
197                                 infos [i++] = new DirectoryInfo (name);
198
199                         return infos;
200                 }
201
202                 public FileSystemInfo [] GetFileSystemInfos ()
203                 {
204                         return GetFileSystemInfos ("*");
205                 }
206
207                 public FileSystemInfo [] GetFileSystemInfos (string searchPattern)
208                 {
209                         return GetFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
210                 }
211
212 #if NET_4_0
213                 public
214 #endif
215                 FileSystemInfo [] GetFileSystemInfos (string searchPattern, SearchOption searchOption)
216                 {
217                         if (searchPattern == null)
218                                 throw new ArgumentNullException ("searchPattern");
219                         if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
220                                 throw new ArgumentOutOfRangeException ("searchOption", "Must be TopDirectoryOnly or AllDirectories");
221                         if (!Directory.Exists (FullPath))
222                                 throw new IOException ("Invalid directory");
223
224                         List<FileSystemInfo> infos = new List<FileSystemInfo> ();
225                         InternalGetFileSystemInfos (searchPattern, searchOption, infos);
226                         return infos.ToArray ();
227                 }
228
229                 void InternalGetFileSystemInfos (string searchPattern, SearchOption searchOption, List<FileSystemInfo> infos)
230                 {
231                         // UnauthorizedAccessExceptions might happen here and break everything for SearchOption.AllDirectories
232                         string [] dirs = Directory.GetDirectories (FullPath, searchPattern);
233                         string [] files = Directory.GetFiles (FullPath, searchPattern);
234
235                         Array.ForEach<string> (dirs, (dir) => { infos.Add (new DirectoryInfo (dir)); });
236                         Array.ForEach<string> (files, (file) => { infos.Add (new FileInfo (file)); });
237                         if (dirs.Length == 0 || searchOption == SearchOption.TopDirectoryOnly)
238                                 return;
239
240                         foreach (string dir in dirs) {
241                                 DirectoryInfo dinfo = new DirectoryInfo (dir);
242                                 dinfo.InternalGetFileSystemInfos (searchPattern, searchOption, infos);
243                         }
244                 }
245
246                 // directory management methods
247
248                 public override void Delete ()
249                 {
250                         Delete (false);
251                 }
252
253                 public void Delete (bool recursive)
254                 {
255                         Directory.Delete (FullPath, recursive);
256                 }
257
258                 public void MoveTo (string destDirName)
259                 {
260                         if (destDirName == null)
261                                 throw new ArgumentNullException ("destDirName");
262                         if (destDirName.Length == 0)
263                                 throw new ArgumentException ("An empty file name is not valid.", "destDirName");
264
265                         Directory.Move (FullPath, Path.GetFullPath (destDirName));
266                 }
267
268                 public override string ToString ()
269                 {
270                         return OriginalPath;
271                 }
272
273 #if !MOONLIGHT
274                 public DirectoryInfo[] GetDirectories (string searchPattern, SearchOption searchOption)
275                 {
276                         switch (searchOption) {
277                         case SearchOption.TopDirectoryOnly:
278                                 return GetDirectories (searchPattern);
279                         case SearchOption.AllDirectories:
280                                 Queue workq = new Queue(GetDirectories(searchPattern));
281                                 Queue doneq = new Queue();
282                                 while (workq.Count > 0)
283                                         {
284                                                 DirectoryInfo cinfo = (DirectoryInfo) workq.Dequeue();
285                                                 DirectoryInfo[] cinfoDirs = cinfo.GetDirectories(searchPattern);
286                                                 foreach (DirectoryInfo i in cinfoDirs) workq.Enqueue(i);
287                                                 doneq.Enqueue(cinfo);
288                                         }
289
290                                 DirectoryInfo[] infos = new DirectoryInfo[doneq.Count];
291                                 doneq.CopyTo(infos, 0);
292                                 return infos;
293                         default:
294                                 string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", searchOption, "SearchOption");
295                                 throw new ArgumentOutOfRangeException ("searchOption", msg);
296                         }
297                 }       
298
299                 internal int GetFilesSubdirs (ArrayList l, string pattern)
300                 {
301                         int count;
302                         FileInfo [] thisdir = null;
303
304                         try {
305                                 thisdir = GetFiles (pattern);
306                         } catch (System.UnauthorizedAccessException){
307                                 return 0;
308                         }
309                         
310                         count = thisdir.Length;
311                         l.Add (thisdir);
312
313                         foreach (DirectoryInfo subdir in GetDirectories ()){
314                                 count += subdir.GetFilesSubdirs (l, pattern);
315                         }
316                         return count;
317                 }
318                 
319                 public FileInfo[] GetFiles (string searchPattern, SearchOption searchOption)
320                 {
321                         switch (searchOption) {
322                         case SearchOption.TopDirectoryOnly:
323                                 return GetFiles (searchPattern);
324                         case SearchOption.AllDirectories: {
325                                 ArrayList groups = new ArrayList ();
326                                 int count = GetFilesSubdirs (groups, searchPattern);
327                                 int current = 0;
328                                 
329                                 FileInfo [] all = new FileInfo [count];
330                                 foreach (FileInfo [] p in groups){
331                                         p.CopyTo (all, current);
332                                         current += p.Length;
333                                 }
334                                 return all;
335                         }
336                         default:
337                                 string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", searchOption, "SearchOption");
338                                 throw new ArgumentOutOfRangeException ("searchOption", msg);
339                         }
340                 }
341
342                 // access control methods
343
344                 [MonoLimitation ("DirectorySecurity isn't implemented")]
345                 public void Create (DirectorySecurity directorySecurity)
346                 {
347                         if (directorySecurity != null)
348                                 throw new UnauthorizedAccessException ();
349                         Create ();
350                 }
351
352                 [MonoLimitation ("DirectorySecurity isn't implemented")]
353                 public DirectoryInfo CreateSubdirectory (string path, DirectorySecurity directorySecurity)
354                 {
355                         if (directorySecurity != null)
356                                 throw new UnauthorizedAccessException ();
357                         return CreateSubdirectory (path);
358                 }
359
360                 [MonoNotSupported ("DirectorySecurity isn't implemented")]
361                 public DirectorySecurity GetAccessControl ()
362                 {
363                         throw new UnauthorizedAccessException ();
364                 }
365
366                 [MonoNotSupported ("DirectorySecurity isn't implemented")]
367                 public DirectorySecurity GetAccessControl (AccessControlSections includeSections)
368                 {
369                         throw new UnauthorizedAccessException ();
370                 }
371
372                 [MonoLimitation ("DirectorySecurity isn't implemented")]
373                 public void SetAccessControl (DirectorySecurity directorySecurity)
374                 {
375                         if (directorySecurity != null)
376                                 throw new ArgumentNullException ("directorySecurity");
377                         throw new UnauthorizedAccessException ();
378                 }
379 #endif
380
381 #if NET_4_0 || MOONLIGHT || MOBILE
382
383                 public IEnumerable<DirectoryInfo> EnumerateDirectories ()
384                 {
385                         return EnumerateDirectories ("*", SearchOption.TopDirectoryOnly);
386                 }
387
388                 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern)
389                 {
390                         return EnumerateDirectories (searchPattern, SearchOption.TopDirectoryOnly);
391                 }
392
393                 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern, SearchOption searchOption)
394                 {
395                         if (searchPattern == null)
396                                 throw new ArgumentNullException ("searchPattern");
397
398                         return CreateEnumerateDirectoriesIterator (searchPattern, searchOption);
399                 }
400
401                 IEnumerable<DirectoryInfo> CreateEnumerateDirectoriesIterator (string searchPattern, SearchOption searchOption)
402                 {
403                         foreach (string name in Directory.EnumerateDirectories (FullPath, searchPattern, searchOption))
404                                 yield return new DirectoryInfo (name);
405                 }
406
407                 public IEnumerable<FileInfo> EnumerateFiles ()
408                 {
409                         return EnumerateFiles ("*", SearchOption.TopDirectoryOnly);
410                 }
411
412                 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern)
413                 {
414                         return EnumerateFiles (searchPattern, SearchOption.TopDirectoryOnly);
415                 }
416
417                 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern, SearchOption searchOption)
418                 {
419                         if (searchPattern == null)
420                                 throw new ArgumentNullException ("searchPattern");
421
422                         return CreateEnumerateFilesIterator (searchPattern, searchOption);
423                 }
424
425                 IEnumerable<FileInfo> CreateEnumerateFilesIterator (string searchPattern, SearchOption searchOption)
426                 {
427                         foreach (string name in Directory.EnumerateFiles (FullPath, searchPattern, searchOption))
428                                 yield return new FileInfo (name);
429                 }
430
431                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos ()
432                 {
433                         return EnumerateFileSystemInfos ("*", SearchOption.TopDirectoryOnly);
434                 }
435
436                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern)
437                 {
438                         return EnumerateFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
439                 }
440
441                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern, SearchOption searchOption)
442                 {
443                         if (searchPattern == null)
444                                 throw new ArgumentNullException ("searchPattern");
445                         if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
446                                 throw new ArgumentOutOfRangeException ("searchoption");
447
448                         return EnumerateFileSystemInfos (FullPath, searchPattern, searchOption);
449                 }
450
451                 static internal IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string full, string searchPattern, SearchOption searchOption)
452                 {
453                         string path_with_pattern = Path.Combine (full, searchPattern);
454                         IntPtr handle;
455                         MonoIOError error;
456                         FileAttributes rattr;
457                         bool subdirs = searchOption == SearchOption.AllDirectories;
458
459                         Path.Validate (full);
460                         
461                         string s = MonoIO.FindFirst (full, path_with_pattern, out rattr, out error, out handle);
462                         if (s == null)
463                                 yield break;
464                         if (error != 0)
465                                 throw MonoIO.GetException (Path.GetDirectoryName (path_with_pattern), (MonoIOError) error);
466
467                         try {
468                                 if (((rattr & FileAttributes.ReparsePoint) == 0)){
469                                         if ((rattr & FileAttributes.Directory) != 0)
470                                                 yield return new DirectoryInfo (s);
471                                         else
472                                                 yield return new FileInfo (s);
473                                 }
474                                 
475                                 while ((s = MonoIO.FindNext (handle, out rattr, out error)) != null){
476                                         if ((rattr & FileAttributes.ReparsePoint) != 0)
477                                                 continue;
478                                         if ((rattr & FileAttributes.Directory) != 0)
479                                                 yield return new DirectoryInfo (s);
480                                         else
481                                                 yield return new FileInfo (s);
482                                         
483                                         if (((rattr & FileAttributes.Directory) != 0) && subdirs)
484                                                 foreach (FileSystemInfo child in EnumerateFileSystemInfos (s, searchPattern, searchOption))
485                                                         yield return child;
486                                 }
487                         } finally {
488                                 MonoIO.FindClose (handle);
489                         }
490                 }
491                 
492                 
493 #endif
494         }
495 }