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