New test.
[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.Collections;
42 using System.Security;
43 using System.Security.Permissions;
44 using System.Text;
45
46 namespace System.IO
47 {
48         public
49 #if NET_2_0
50         static
51 #else
52         sealed
53 #endif
54         class Directory
55         {
56
57 #if !NET_2_0
58                 private Directory () {}
59 #endif
60                 
61                 public static DirectoryInfo CreateDirectory (string path)
62                 {
63                         if (path == null)
64                                 throw new ArgumentNullException ("path");
65                         
66                         if (path == "")
67                                 throw new ArgumentException ("Path is empty");
68                         
69                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
70                                 throw new ArgumentException ("Path contains invalid chars");
71
72                         if (path.Trim ().Length == 0)
73                                 throw new ArgumentException ("Only blank characters in path");
74
75 #if NET_2_0
76                         if (File.Exists(path))
77                                 throw new IOException ("Cannot create " + path + " because a file with the same name already exists.");
78 #endif
79                         
80                         // LAMESPEC: with .net 1.0 version this throw NotSupportedException and msdn says so too
81                         // but v1.1 throws ArgumentException.
82                         if (path == ":")
83                                 throw new ArgumentException ("Only ':' In path");
84                         
85                         return CreateDirectoriesInternal (path);
86                 }
87
88                 static DirectoryInfo CreateDirectoriesInternal (string path)
89                 {
90                         if (SecurityManager.SecurityEnabled) {
91                                 new FileIOPermission (FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, path).Demand ();
92                         }
93
94                         DirectoryInfo info = new DirectoryInfo (path);
95                         if (info.Parent != null && !info.Parent.Exists)
96                                  info.Parent.Create ();
97
98                         MonoIOError error;
99                         if (!MonoIO.CreateDirectory (path, out error)) {
100                                 // LAMESPEC: 1.1 and 1.2alpha allow CreateDirectory on a file path.
101                                 // So CreateDirectory ("/tmp/somefile") will succeed if 'somefile' is
102                                 // not a directory. However, 1.0 will throw an exception.
103                                 // We behave like 1.0 here (emulating 1.1-like behavior is just a matter
104                                 // of comparing error to ERROR_FILE_EXISTS, but it's lame to do:
105                                 //    DirectoryInfo di = Directory.CreateDirectory (something);
106                                 // and having di.Exists return false afterwards.
107                                 // I hope we don't break anyone's code, as they should be catching
108                                 // the exception anyway.
109                                 if (error != MonoIOError.ERROR_ALREADY_EXISTS &&
110                                     error != MonoIOError.ERROR_FILE_EXISTS)
111                                         throw MonoIO.GetException (path, error);
112                         }
113
114                         return info;
115                 }
116                 
117                 public static void Delete (string path)
118                 {
119                         if (path == null)
120                                 throw new ArgumentNullException ("path");
121                         
122                         if (path == "")
123                                 throw new ArgumentException ("Path is empty");
124                         
125                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
126                                 throw new ArgumentException ("Path contains invalid chars");
127
128                         if (path.Trim().Length == 0)
129                                 throw new ArgumentException ("Only blank characters in path");
130
131                         if (path == ":")
132                                 throw new NotSupportedException ("Only ':' In path");
133
134                         MonoIOError error;
135                         bool success;
136                         
137                         if (MonoIO.ExistsSymlink (path, out error)) {
138                                 /* RemoveDirectory maps to rmdir()
139                                  * which fails on symlinks (ENOTDIR)
140                                  */
141                                 success = MonoIO.DeleteFile (path, out error);
142                         } else {
143                                 success = MonoIO.RemoveDirectory (path,
144                                                                   out error);
145                         }
146                         
147                         if (!success) {
148                                 /*
149                                  * FIXME:
150                                  * In io-layer/io.c rmdir returns error_file_not_found if directory does not exists.
151                                  * So maybe this could be handled somewhere else?
152                                  */
153                                 if (error == MonoIOError.ERROR_FILE_NOT_FOUND) 
154                                         throw new DirectoryNotFoundException ("Directory '" + path + "' doesnt exists.");
155                                 else
156                                         throw MonoIO.GetException (path, error);
157                         }
158                 }
159
160                 static void RecursiveDelete (string path)
161                 {
162                         MonoIOError error;
163                         
164                         foreach (string dir in GetDirectories (path)) {
165                                 if (MonoIO.ExistsSymlink (dir, out error)) {
166                                         MonoIO.DeleteFile (dir, out error);
167                                 } else {
168                                         RecursiveDelete (dir);
169                                 }
170                         }
171
172                         foreach (string file in GetFiles (path))
173                                 File.Delete (file);
174
175                         Directory.Delete (path);
176                 }
177                 
178                 public static void Delete (string path, bool recurse)
179                 {
180                         CheckPathExceptions (path);
181                         
182                         if (recurse == false){
183                                 Delete (path);
184                                 return;
185                         }
186
187                         RecursiveDelete (path);
188                 }
189
190                 public static bool Exists (string path)
191                 {
192                         if (path == null)
193                                 return false;
194                                 
195                         MonoIOError error;
196                         bool exists;
197                         
198                         exists = MonoIO.ExistsDirectory (path, out error);
199                         if (error != MonoIOError.ERROR_SUCCESS &&
200                             error != MonoIOError.ERROR_PATH_NOT_FOUND &&
201                             error != MonoIOError.ERROR_INVALID_HANDLE &&
202                             error != MonoIOError.ERROR_ACCESS_DENIED) {
203
204                                 // INVALID_HANDLE might happen if the file is moved
205                                 // while testing for the existence, a kernel issue
206                                 // according to Larry Ewing.
207                                 
208                                 throw MonoIO.GetException (path, error);
209                         }
210
211                         return(exists);
212                 }
213
214                 public static DateTime GetLastAccessTime (string path)
215                 {
216                         return File.GetLastAccessTime (path);
217                 }
218                 
219                 public static DateTime GetLastAccessTimeUtc (string path)
220                 {
221                         return GetLastAccessTime (path).ToUniversalTime ();
222                 }
223                       
224                 public static DateTime GetLastWriteTime (string path)
225                 {
226                         return File.GetLastWriteTime (path);
227                 }
228                 
229                 public static DateTime GetLastWriteTimeUtc (string path)
230                 {
231                         return GetLastWriteTime (path).ToUniversalTime ();
232                 }
233
234                 public static DateTime GetCreationTime (string path)
235                 {
236                         return File.GetCreationTime (path);
237                 }
238
239                 public static DateTime GetCreationTimeUtc (string path)
240                 {
241                         return GetCreationTime (path).ToUniversalTime ();
242                 }
243
244                 public static string GetCurrentDirectory ()
245                 {
246                         MonoIOError error;
247                                 
248                         string result = MonoIO.GetCurrentDirectory (out error);
249                         if (error != MonoIOError.ERROR_SUCCESS)
250                                 throw MonoIO.GetException (error);
251
252                         if ((result != null) && (result.Length > 0) && SecurityManager.SecurityEnabled) {
253                                 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, result).Demand ();
254                         }
255                         return result;
256                 }
257                 
258                 public static string [] GetDirectories (string path)
259                 {
260                         return GetDirectories (path, "*");
261                 }
262                 
263                 public static string [] GetDirectories (string path, string pattern)
264                 {
265                         return GetFileSystemEntries (path, pattern, FileAttributes.Directory, FileAttributes.Directory);
266                 }
267                 
268 #if NET_2_0
269                 public static string [] GetDirectories (string path, string pattern, SearchOption option)
270                 {
271                         if (option == SearchOption.TopDirectoryOnly)
272                                 return GetDirectories (path, pattern);
273                         ArrayList all = new ArrayList ();
274                         GetDirectoriesRecurse (path, pattern, all);
275                         return (string []) all.ToArray (typeof (string));
276                 }
277                 
278                 static void GetDirectoriesRecurse (string path, string pattern, ArrayList all)
279                 {
280                         all.AddRange (GetDirectories (path, pattern));
281                         foreach (string dir in GetDirectories (path))
282                                 GetDirectoriesRecurse (dir, pattern, all);
283                 }
284 #endif
285
286                 public static string GetDirectoryRoot (string path)
287                 {
288                         return new String(Path.DirectorySeparatorChar,1);
289                 }
290                 
291                 public static string [] GetFiles (string path)
292                 {
293                         return GetFiles (path, "*");
294                 }
295                 
296                 public static string [] GetFiles (string path, string pattern)
297                 {
298                         return GetFileSystemEntries (path, pattern, FileAttributes.Directory, 0);
299                 }
300
301 #if NET_2_0
302                 public static string[] GetFiles (string path, string searchPattern, SearchOption searchOption)
303                 {
304                         if (searchOption == SearchOption.TopDirectoryOnly)
305                                 return GetFiles (path, searchPattern);
306                         ArrayList all = new ArrayList ();
307                         GetFilesRecurse (path, searchPattern, all);
308                         return (string []) all.ToArray (typeof (string));
309                 }
310                 
311                 static void GetFilesRecurse (string path, string pattern, ArrayList all)
312                 {
313                         all.AddRange (GetFiles (path, pattern));
314                         foreach (string dir in GetDirectories (path))
315                                 GetFilesRecurse (dir, pattern, all);
316                 }
317 #endif
318
319                 public static string [] GetFileSystemEntries (string path)
320                 {
321                         return GetFileSystemEntries (path, "*");
322                 }
323
324                 public static string [] GetFileSystemEntries (string path, string pattern)
325                 {
326                         return GetFileSystemEntries (path, pattern, 0, 0);
327                 }
328                 
329                 public static string[] GetLogicalDrives ()
330                 { 
331                         return Environment.GetLogicalDrives ();
332                 }
333
334                 static bool IsRootDirectory (string path)
335                 {
336                         // Unix
337                        if (Path.DirectorySeparatorChar == '/' && path == "/")
338                                return true;
339
340                        // Windows
341                        if (Path.DirectorySeparatorChar == '\\')
342                                if (path.Length == 3 && path.EndsWith (":\\"))
343                                        return true;
344
345                        return false;
346                 }
347
348                 public static DirectoryInfo GetParent (string path)
349                 {
350                         if (path == null)
351                                 throw new ArgumentNullException ();
352                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
353                                 throw new ArgumentException ("Path contains invalid characters");
354                         if (path == "")
355                                 throw new ArgumentException ("The Path do not have a valid format");
356
357                         // return null if the path is the root directory
358                         if (IsRootDirectory (path))
359                                 return null;
360
361                         string parent_name = Path.GetDirectoryName (path);
362                         if (parent_name == "")
363                                 parent_name = GetCurrentDirectory();
364
365                         return new DirectoryInfo (parent_name);
366                 }
367
368                 public static void Move (string src, string dest)
369                 {
370                         if (src == null)
371                                 throw new ArgumentNullException ("src");
372
373                         if (dest == null)
374                                 throw new ArgumentNullException ("dest");
375
376                         if (src.Trim () == "" || src.IndexOfAny (Path.InvalidPathChars) != -1)
377                                 throw new ArgumentException ("Invalid source directory name: " + src, "src");
378
379                         if (dest.Trim () == "" || dest.IndexOfAny (Path.InvalidPathChars) != -1)
380                                 throw new ArgumentException ("Invalid target directory name: " + dest, "dest");
381
382                         if (src == dest)
383                                 throw new IOException ("Source directory cannot be same as a target directory.");
384
385                         if (Exists (dest))
386                                 throw new IOException (dest + " already exists.");
387
388                         if (!Exists (src))
389                                 throw new DirectoryNotFoundException (src + " does not exist");
390
391                         MonoIOError error;
392                         if (!MonoIO.MoveFile (src, dest, out error))
393                                 throw MonoIO.GetException (error);
394                 }
395
396                 public static void SetCreationTime (string path, DateTime creation_time)
397                 {
398                         File.SetCreationTime (path, creation_time);
399                 }
400
401                 public static void SetCreationTimeUtc (string path, DateTime creation_time)
402                 {
403                         SetCreationTime (path, creation_time.ToLocalTime ());
404                 }
405
406                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
407                 public static void SetCurrentDirectory (string path)
408                 {
409                         if (path == null)
410                                 throw new ArgumentNullException ("path");
411                         if (path.Trim () == String.Empty)
412                                 throw new ArgumentException ("path string must not be an empty string or whitespace string");
413
414                         MonoIOError error;
415                                 
416                         if (!Exists (path))
417                                 throw new DirectoryNotFoundException ("Directory \"" +
418                                                                         path + "\" not found.");
419
420                         MonoIO.SetCurrentDirectory (path, out error);
421                         if (error != MonoIOError.ERROR_SUCCESS)
422                                 throw MonoIO.GetException (path, error);
423                 }
424
425                 public static void SetLastAccessTime (string path, DateTime last_access_time)
426                 {
427                         File.SetLastAccessTime (path, last_access_time);
428                 }
429
430                 public static void SetLastAccessTimeUtc (string path, DateTime last_access_time)
431                 {
432                         SetLastAccessTime (path, last_access_time.ToLocalTime ());
433                 }
434
435                 public static void SetLastWriteTime (string path, DateTime last_write_time)
436                 {
437                         File.SetLastWriteTime (path, last_write_time);
438                 }
439
440                 public static void SetLastWriteTimeUtc (string path, DateTime last_write_time)
441                 {
442                         SetLastWriteTime (path, last_write_time.ToLocalTime ());
443                 }
444
445                 // private
446                 
447                 private static void CheckPathExceptions (string path)
448                 {
449                         if (path == null)
450                                 throw new System.ArgumentNullException("Path is Null");
451                         if (path == "")
452                                 throw new System.ArgumentException("Path is Empty");
453                         if (path.Trim().Length == 0)
454                                 throw new ArgumentException ("Only blank characters in path");
455                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
456                                 throw new ArgumentException ("Path contains invalid chars");
457                 }
458
459                 private static string [] GetFileSystemEntries (string path, string pattern, FileAttributes mask, FileAttributes attrs)
460                 {
461                         if (path == null || pattern == null)
462                                 throw new ArgumentNullException ();
463
464                         if (pattern == String.Empty)
465                                 return new string [] {};
466                         
467                         if (path.Trim () == "")
468                                 throw new ArgumentException ("The Path does not have a valid format");
469
470                         string wild = Path.Combine (path, pattern);
471                         string wildpath = Path.GetDirectoryName (wild);
472                         if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1)
473                                 throw new ArgumentException ("Path contains invalid characters");
474
475                         if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1) {
476                                 if (path.IndexOfAny (SearchPattern.InvalidChars) == -1)
477                                         throw new ArgumentException ("Path contains invalid characters", "path");
478
479                                 throw new ArgumentException ("Pattern contains invalid characters", "pattern");
480                         }
481
482                         MonoIOError error;
483                         if (!MonoIO.ExistsDirectory (wildpath, out error)) {
484                                 if (error == MonoIOError.ERROR_SUCCESS) {
485                                         MonoIOError file_error;
486                                         if (MonoIO.ExistsFile (wildpath, out file_error)) {
487                                                 return new string [] { wildpath };
488                                         }
489                                 }
490
491                                 if (error != MonoIOError.ERROR_PATH_NOT_FOUND)
492                                         throw MonoIO.GetException (wildpath, error);
493
494                                 if (wildpath.IndexOfAny (SearchPattern.WildcardChars) == -1)
495                                         throw new DirectoryNotFoundException ("Directory '" + wildpath + "' not found.");
496
497                                 if (path.IndexOfAny (SearchPattern.WildcardChars) == -1)
498                                         throw new ArgumentException ("Pattern is invalid", "pattern");
499
500                                 throw new ArgumentException ("Path is invalid", "path");
501                         }
502
503                         string [] result = MonoIO.GetFileSystemEntries (wildpath, pattern, (int) attrs, (int) mask, out error);
504                         if (error != 0)
505                                 throw MonoIO.GetException (wildpath, error);
506
507                         return result;
508                 }
509         }
510 }
511