**** Merged r40732-r40872 from MCS ****
[mono.git] / mcs / class / corlib / System.IO / Directory.cs
1 // 
2 // System.IO.Directory.cs 
3 //
4 // Authors:
5 //   Jim Richardson  (develop@wtfo-guru.com)
6 //   Miguel de Icaza (miguel@ximian.com)
7 //   Dan Lewis       (dihlewis@yahoo.co.uk)
8 //   Eduardo Garcia  (kiwnix@yahoo.es)
9 //   Ville Palo      (vi64pa@kolumbus.fi)
10 //
11 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
12 // Copyright (C) 2002 Ximian, Inc.
13 // 
14 // Created:        Monday, August 13, 2001 
15 //
16 //------------------------------------------------------------------------------
17
18 //
19 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
20 //
21 // Permission is hereby granted, free of charge, to any person obtaining
22 // a copy of this software and associated documentation files (the
23 // "Software"), to deal in the Software without restriction, including
24 // without limitation the rights to use, copy, modify, merge, publish,
25 // distribute, sublicense, and/or sell copies of the Software, and to
26 // permit persons to whom the Software is furnished to do so, subject to
27 // the following conditions:
28 // 
29 // The above copyright notice and this permission notice shall be
30 // included in all copies or substantial portions of the Software.
31 // 
32 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 //
40
41 using System;
42 using System.Collections;
43 using System.Security;
44 using System.Security.Permissions;
45 using System.Text;
46
47 namespace System.IO
48 {
49         public
50 #if NET_2_0
51         static
52 #else
53         sealed
54 #endif
55         class Directory
56         {
57
58 #if !NET_2_0
59                 private Directory () {}
60 #endif
61                 
62                 public static DirectoryInfo CreateDirectory (string path)
63                 {
64                         if (path == null)
65                                 throw new ArgumentNullException ("path");
66                         
67                         if (path == "")
68                                 throw new ArgumentException ("Path is empty");
69                         
70                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
71                                 throw new ArgumentException ("Path contains invalid chars");
72
73                         if (path.Trim ().Length == 0)
74                                 throw new ArgumentException ("Only blank characters in path");
75                         
76                         // LAMESPEC: with .net 1.0 version this throw NotSupportedException and msdn says so too
77                         // but v1.1 throws ArgumentException.
78                         if (path == ":")
79                                 throw new ArgumentException ("Only ':' In path");
80                         
81                         return CreateDirectoriesInternal (path);
82                 }
83
84                 static DirectoryInfo CreateDirectoriesInternal (string path)
85                 {
86                         DirectoryInfo info = new DirectoryInfo (path);
87                         if (info.Parent != null && !info.Parent.Exists)
88                                  info.Parent.Create ();
89
90                         MonoIOError error;
91                         if (!MonoIO.CreateDirectory (path, out error)) {
92                                 // LAMESPEC: 1.1 and 1.2alpha allow CreateDirectory on a file path.
93                                 // So CreateDirectory ("/tmp/somefile") will succeed if 'somefile' is
94                                 // not a directory. However, 1.0 will throw an exception.
95                                 // We behave like 1.0 here (emulating 1.1-like behavior is just a matter
96                                 // of comparing error to ERROR_FILE_EXISTS, but it's lame to do:
97                                 //    DirectoryInfo di = Directory.CreateDirectory (something);
98                                 // and having di.Exists return false afterwards.
99                                 // I hope we don't break anyone's code, as they should be catching
100                                 // the exception anyway.
101                                 if (error != MonoIOError.ERROR_ALREADY_EXISTS)
102                                         throw MonoIO.GetException (path, error);
103                         }
104
105                         return info;
106                 }
107                 
108                 public static void Delete (string path)
109                 {
110                         if (path == null)
111                                 throw new ArgumentNullException ("path");
112                         
113                         if (path == "")
114                                 throw new ArgumentException ("Path is empty");
115                         
116                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
117                                 throw new ArgumentException ("Path contains invalid chars");
118
119                         if (path.Trim().Length == 0)
120                                 throw new ArgumentException ("Only blank characters in path");
121
122                         if (path == ":")
123                                 throw new NotSupportedException ("Only ':' In path");
124
125                         MonoIOError error;
126                         
127                         if (!MonoIO.RemoveDirectory (path, out error)) {
128                                 /*
129                                  * FIXME:
130                                  * In io-layer/io.c rmdir returns error_file_not_found if directory does not exists.
131                                  * So maybe this could be handled somewhere else?
132                                  */
133                                 if (error == MonoIOError.ERROR_FILE_NOT_FOUND) 
134                                         throw new DirectoryNotFoundException ("Directory '" + path + "' doesnt exists.");
135                                 else
136                                         throw MonoIO.GetException (path, error);
137                         }
138                 }
139
140                 static void RecursiveDelete (string path)
141                 {
142                         foreach (string dir in GetDirectories (path))
143                                 RecursiveDelete (dir);
144
145                         foreach (string file in GetFiles (path))
146                                 File.Delete (file);
147
148                         Directory.Delete (path);
149                 }
150                 
151                 public static void Delete (string path, bool recurse)
152                 {
153                         CheckPathExceptions (path);
154                         
155                         if (recurse == false){
156                                 Delete (path);
157                                 return;
158                         }
159
160                         RecursiveDelete (path);
161                 }
162
163                 public static bool Exists (string path)
164                 {
165                         if (path == null)
166                                 return false;
167                                 
168                         MonoIOError error;
169                         bool exists;
170                         
171                         exists = MonoIO.ExistsDirectory (path, out error);
172                         if (error != MonoIOError.ERROR_SUCCESS &&
173                             error != MonoIOError.ERROR_PATH_NOT_FOUND) {
174                                 throw MonoIO.GetException (path, error);
175                         }
176
177                         return(exists);
178                 }
179
180                 public static DateTime GetLastAccessTime (string path)
181                 {
182                         return File.GetLastAccessTime (path);
183                 }
184                 
185                 public static DateTime GetLastAccessTimeUtc (string path)
186                 {
187                         return GetLastAccessTime (path).ToUniversalTime ();
188                 }
189                       
190                 public static DateTime GetLastWriteTime (string path)
191                 {
192                         return File.GetLastWriteTime (path);
193                 }
194                 
195                 public static DateTime GetLastWriteTimeUtc (string path)
196                 {
197                         return GetLastWriteTime (path).ToUniversalTime ();
198                 }
199
200                 public static DateTime GetCreationTime (string path)
201                 {
202                         return File.GetCreationTime (path);
203                 }
204
205                 public static DateTime GetCreationTimeUtc (string path)
206                 {
207                         return GetCreationTime (path).ToUniversalTime ();
208                 }
209
210                 public static string GetCurrentDirectory ()
211                 {
212                         MonoIOError error;
213                                 
214                         string result = MonoIO.GetCurrentDirectory (out error);
215                         if (error != MonoIOError.ERROR_SUCCESS)
216                                 throw MonoIO.GetException (error);
217
218                         if ((result != null) && (result.Length > 0) && SecurityManager.SecurityEnabled) {
219                                 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, result).Demand ();
220                         }
221                         return result;
222                 }
223                 
224                 public static string [] GetDirectories (string path)
225                 {
226                         return GetDirectories (path, "*");
227                 }
228                 
229                 public static string [] GetDirectories (string path, string pattern)
230                 {
231                         return GetFileSystemEntries (path, pattern, FileAttributes.Directory, FileAttributes.Directory);
232                 }
233                 
234                 public static string GetDirectoryRoot (string path)
235                 {
236                         return new String(Path.DirectorySeparatorChar,1);
237                 }
238                 
239                 public static string [] GetFiles (string path)
240                 {
241                         return GetFiles (path, "*");
242                 }
243                 
244                 public static string [] GetFiles (string path, string pattern)
245                 {
246                         return GetFileSystemEntries (path, pattern, FileAttributes.Directory, 0);
247                 }
248
249                 public static string [] GetFileSystemEntries (string path)
250                 {
251                         return GetFileSystemEntries (path, "*");
252                 }
253
254                 public static string [] GetFileSystemEntries (string path, string pattern)
255                 {
256                         return GetFileSystemEntries (path, pattern, 0, 0);
257                 }
258                 
259                 public static string[] GetLogicalDrives ()
260                 { 
261                         return Environment.GetLogicalDrives ();
262                 }
263
264                 static bool IsRootDirectory (string path)
265                 {
266                         // Unix
267                        if (Path.DirectorySeparatorChar == '/' && path == "/")
268                                return true;
269
270                        // Windows
271                        if (Path.DirectorySeparatorChar == '\\')
272                                if (path.Length == 3 && path.EndsWith (":\\"))
273                                        return true;
274
275                        return false;
276                 }
277
278                 public static DirectoryInfo GetParent (string path)
279                 {
280                         if (path == null)
281                                 throw new ArgumentNullException ();
282                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
283                                 throw new ArgumentException ("Path contains invalid characters");
284                         if (path == "")
285                                 throw new ArgumentException ("The Path do not have a valid format");
286
287                         // return null if the path is the root directory
288                         if (IsRootDirectory (path))
289                                 return null;
290                         
291                         return new DirectoryInfo (Path.GetDirectoryName (path));
292                 }
293
294                 public static void Move (string src, string dest)
295                 {
296                         if (src == null)
297                                 throw new ArgumentNullException ("src");
298
299                         if (dest == null)
300                                 throw new ArgumentNullException ("dest");
301
302                         if (src.Trim () == "" || src.IndexOfAny (Path.InvalidPathChars) != -1)
303                                 throw new ArgumentException ("Invalid source directory name: " + src, "src");
304
305                         if (dest.Trim () == "" || dest.IndexOfAny (Path.InvalidPathChars) != -1)
306                                 throw new ArgumentException ("Invalid target directory name: " + dest, "dest");
307
308                         if (src == dest)
309                                 throw new IOException ("Source directory cannot be same as a target directory.");
310
311                         if (Exists (dest))
312                                 throw new IOException (dest + " already exists.");
313
314                         if (!Exists (src))
315                                 throw new DirectoryNotFoundException (src + " does not exist");
316
317                         MonoIOError error;
318                         if (!MonoIO.MoveFile (src, dest, out error))
319                                 throw MonoIO.GetException (error);
320                 }
321
322                 public static void SetCreationTime (string path, DateTime creation_time)
323                 {
324                         File.SetCreationTime (path, creation_time);
325                 }
326
327                 public static void SetCreationTimeUtc (string path, DateTime creation_time)
328                 {
329                         SetCreationTime (path, creation_time.ToLocalTime ());
330                 }
331
332                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
333                 public static void SetCurrentDirectory (string path)
334                 {
335                         if (path == null)
336                                 throw new ArgumentNullException ("path");
337                         if (path.Trim () == String.Empty)
338                                 throw new ArgumentException ("path string must not be an empty string or whitespace string");
339
340                         MonoIOError error;
341                                 
342                         if (!Exists (path))
343                                 throw new DirectoryNotFoundException ("Directory \"" +
344                                                                         path + "\" not found.");
345
346                         MonoIO.SetCurrentDirectory (path, out error);
347                         if (error != MonoIOError.ERROR_SUCCESS)
348                                 throw MonoIO.GetException (path, error);
349                 }
350
351                 public static void SetLastAccessTime (string path, DateTime last_access_time)
352                 {
353                         File.SetLastAccessTime (path, last_access_time);
354                 }
355
356                 public static void SetLastAccessTimeUtc (string path, DateTime last_access_time)
357                 {
358                         SetLastAccessTime (path, last_access_time.ToLocalTime ());
359                 }
360
361                 public static void SetLastWriteTime (string path, DateTime last_write_time)
362                 {
363                         File.SetLastWriteTime (path, last_write_time);
364                 }
365
366                 public static void SetLastWriteTimeUtc (string path, DateTime last_write_time)
367                 {
368                         SetLastWriteTime (path, last_write_time.ToLocalTime ());
369                 }
370
371                 // private
372                 
373                 private static void CheckPathExceptions (string path)
374                 {
375                         if (path == null)
376                                 throw new System.ArgumentNullException("Path is Null");
377                         if (path == "")
378                                 throw new System.ArgumentException("Path is Empty");
379                         if (path.Trim().Length == 0)
380                                 throw new ArgumentException ("Only blank characters in path");
381                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
382                                 throw new ArgumentException ("Path contains invalid chars");
383                 }
384
385                 private static string [] GetFileSystemEntries (string path, string pattern, FileAttributes mask, FileAttributes attrs)
386                 {
387                         MonoIOStat stat;
388                         IntPtr find;
389
390                         if (path == null || pattern == null)
391                                 throw new ArgumentNullException ();
392
393                         if (pattern == String.Empty)
394                                 return new string [] {};
395                         
396                         if (path.Trim () == "")
397                                 throw new ArgumentException ("The Path does not have a valid format");
398
399                         string wild = Path.Combine (path, pattern);
400                         string wildpath = Path.GetDirectoryName (wild);
401                         if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1)
402                                 throw new ArgumentException ("Path contains invalid characters");
403
404                         if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1) {
405                                 if (path.IndexOfAny (SearchPattern.InvalidChars) == -1)
406                                         throw new ArgumentException ("Path contains invalid characters", "path");
407
408                                 throw new ArgumentException ("Pattern contains invalid characters", "pattern");
409                         }
410
411                         MonoIOError error;
412                         if (!MonoIO.ExistsDirectory (wildpath, out error)) {
413                                 if (error != MonoIOError.ERROR_PATH_NOT_FOUND)
414                                         throw MonoIO.GetException (wildpath, error);
415
416                                 if (wildpath.IndexOfAny (SearchPattern.WildcardChars) == -1)
417                                         throw new DirectoryNotFoundException ("Directory '" + wildpath + "' not found.");
418
419                                 if (path.IndexOfAny (SearchPattern.WildcardChars) == -1)
420                                         throw new ArgumentException ("Pattern is invalid", "pattern");
421
422                                 throw new ArgumentException ("Path is invalid", "path");
423                         }
424
425                         find = MonoIO.FindFirstFile (wild, out stat, out error);
426                         if (find == MonoIO.InvalidHandle) {
427                                 switch (error) {
428                                 case MonoIOError.ERROR_PATH_NOT_FOUND:
429                                         string message = String.Format ("Could not find a part of the path \"{0}\"",
430                                                                         wildpath);
431                                         throw new DirectoryNotFoundException (message);
432                                 case MonoIOError.ERROR_FILE_NOT_FOUND:
433                                 case MonoIOError.ERROR_NO_MORE_FILES:
434                                         return new string [0];
435
436                                 default:
437                                         throw MonoIO.GetException (wildpath, error);
438                                 }
439                         }
440                         
441                         ArrayList entries = new ArrayList ();
442
443                         do {
444                                 if ((stat.Attributes & mask) == attrs)
445                                         entries.Add (Path.Combine (wildpath, stat.Name));
446                         } while (MonoIO.FindNextFile (find, out stat, out error));
447
448                         MonoIO.FindClose (find, out error);
449
450                         return (string []) entries.ToArray (typeof (string));
451                 }
452         }
453 }