[w32file] Move MonoIO.Find{First,Next,Close} to managed
[mono.git] / mcs / class / corlib / System.IO / DirectoryInfo.cs
index 25be8be02915cda5d8116e9258e1a89453ffe6a1..1cf54bf5b2bbf408cea543afcdd4ae1994f922bf 100644 (file)
@@ -1,17 +1,17 @@
-// \r
-// System.IO.DirectoryInfo.cs \r
-//\r
-// Author:\r
-//   Miguel de Icaza, miguel@ximian.com\r
-//   Jim Richardson, develop@wtfo-guru.com\r
-//   Dan Lewis, dihlewis@yahoo.co.uk\r
-//\r
-// Copyright (C) 2002 Ximian, Inc.\r
-// Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved\r
-// \r
-
+// 
+// System.IO.DirectoryInfo.cs 
+//
+// Authors:
+//   Miguel de Icaza, miguel@ximian.com
+//   Jim Richardson, develop@wtfo-guru.com
+//   Dan Lewis, dihlewis@yahoo.co.uk
+//   Sebastien Pouliot  <sebastien@ximian.com>
+//   Marek Safar  <marek.safar@gmail.com>
 //
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2002 Ximian, Inc.
+// Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2014 Xamarin, Inc (http://www.xamarin.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-\r
-using System;\r
-using System.Collections;\r
-\r
-namespace System.IO {\r
-       \r
-       [Serializable]\r
-       public sealed class DirectoryInfo : FileSystemInfo {\r
-       \r
-               public DirectoryInfo (string path) {\r
-                       CheckPath (path);\r
-               \r
-                       OriginalPath = path;\r
-                       FullPath = Path.GetFullPath (path);\r
-               }\r
-\r
-               // properties\r
-\r
-               public override bool Exists {\r
-                       get {\r
-                               Refresh (false);\r
-\r
-                               if (stat.Attributes == MonoIO.InvalidFileAttributes)\r
-                                       return false;\r
-\r
-                               if ((stat.Attributes & FileAttributes.Directory) == 0)\r
-                                       return false;\r
-\r
-                               return true;\r
-                       }\r
-               }\r
-\r
-               public override string Name {\r
-                       get {\r
-                               string result = Path.GetFileName (FullPath);\r
-                               if (result == null || result == "")\r
-                                       return FullPath;\r
-                               return result;\r
-                       }\r
-               }\r
-\r
-               public DirectoryInfo Parent {\r
-                       get {\r
-                               string dirname = Path.GetDirectoryName (FullPath);\r
-                               if (dirname == null)\r
-                                       return null;\r
-\r
-                               return new DirectoryInfo (dirname);\r
-                       }\r
-               }\r
-\r
-               public DirectoryInfo Root {\r
-                       get {\r
-                               string root = Path.GetPathRoot (FullPath);\r
-                               if (root == null)\r
-                                       return null;\r
-\r
-                               return new DirectoryInfo (root);\r
-                       }\r
-               }\r
-\r
-               // creational methods\r
-\r
-               public void Create () {\r
-                       Directory.CreateDirectory (FullPath);\r
-               }\r
-\r
-               public DirectoryInfo CreateSubdirectory (string name) {
-                       CheckPath (name);
-                       \r
-                       string path = Path.Combine (FullPath, name);\r
-                       Directory.CreateDirectory (path);\r
-\r
-                       return new DirectoryInfo (path);\r
-               }\r
-\r
-               // directory listing methods\r
-\r
-               public FileInfo [] GetFiles () {\r
-                       return GetFiles ("*");\r
-               }\r
-\r
-               public FileInfo [] GetFiles (string pattern) {\r
-                       string [] names = Directory.GetFiles (FullPath, pattern);\r
-\r
-                       ArrayList infos = new ArrayList ();\r
-                       foreach (string name in names)\r
-                               infos.Add (new FileInfo (name));\r
-\r
-                       return (FileInfo []) infos.ToArray (typeof (FileInfo));\r
-               }\r
-\r
-               public DirectoryInfo [] GetDirectories () {\r
-                       return GetDirectories ("*");\r
-               }\r
-\r
-               public DirectoryInfo [] GetDirectories (string pattern) {\r
-                       string [] names = Directory.GetDirectories (FullPath, pattern);\r
-\r
-                       ArrayList infos = new ArrayList ();\r
-                       foreach (string name in names)\r
-                               infos.Add (new DirectoryInfo (name));\r
-\r
-                       return (DirectoryInfo []) infos.ToArray (typeof (DirectoryInfo));\r
-               }\r
-\r
-               public FileSystemInfo [] GetFileSystemInfos () {\r
-                       return GetFileSystemInfos ("*");\r
-               }\r
-\r
-               public FileSystemInfo [] GetFileSystemInfos (string pattern) {\r
-                       ArrayList infos = new ArrayList ();\r
-                       infos.AddRange (GetDirectories (pattern));\r
-                       infos.AddRange (GetFiles (pattern));\r
-\r
-                       return (FileSystemInfo []) infos.ToArray (typeof (FileSystemInfo));\r
-               }\r
-\r
-               // directory management methods\r
-\r
-               public override void Delete () {\r
-                       Delete (false);\r
-               }\r
-\r
-               public void Delete (bool recurse) {\r
-                       Directory.Delete (FullPath, recurse);\r
-               }\r
-\r
-               public void MoveTo (string dest) {\r
-                       Directory.Move (FullPath, Path.GetFullPath (dest));\r
-               }\r
-\r
-               public override string ToString () {\r
-                       return OriginalPath;\r
-               }\r
-       }\r
-}\r
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Security;
+using System.Text;
+using System.Security.AccessControl;
+
+using Microsoft.Win32.SafeHandles;
+
+namespace System.IO {
+       
+       [Serializable]
+       [ComVisible (true)]
+       public sealed class DirectoryInfo : FileSystemInfo {
+
+               private string current;
+               private string parent;
+       
+               public DirectoryInfo (string path) : this (path, false)
+               {
+               }
+
+               internal DirectoryInfo (string path, bool simpleOriginalPath)
+               {
+                       CheckPath (path);
+
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
+                       FullPath = Path.GetFullPath (path);
+                       if (simpleOriginalPath)
+                               OriginalPath = Path.GetFileName (FullPath);
+                       else
+                               OriginalPath = path;
+
+                       Initialize ();
+               }
+
+               private DirectoryInfo (SerializationInfo info, StreamingContext context)
+                       : base (info, context)
+               {
+                       Initialize ();
+               }
+
+               void Initialize ()
+               {
+                       int len = FullPath.Length - 1;
+                       if ((len > 1) && (FullPath [len] == Path.DirectorySeparatorChar))
+                               len--;
+                       int last = FullPath.LastIndexOf (Path.DirectorySeparatorChar, len);
+                       if ((last == -1) || ((last == 0) && (len == 0))) {
+                               current = FullPath;
+                               parent = null;
+                       } else {
+                               current = FullPath.Substring (last + 1, len - last);
+                               if (last == 0 && !Environment.IsRunningOnWindows)
+                                       parent = Path.DirectorySeparatorStr;
+                               else
+                                       parent = FullPath.Substring (0, last);
+                               // adjust for drives, i.e. a special case for windows
+                               if (Environment.IsRunningOnWindows) {
+                                       if ((parent.Length == 2) && (parent [1] == ':') && Char.IsLetter (parent [0]))
+                                               parent += Path.DirectorySeparatorChar;
+                               }
+                       }
+               }
+
+               // properties
+
+               public override bool Exists {
+                       get {
+                               if (_dataInitialised == -1)
+                                       Refresh ();
+
+                               if (_data.fileAttributes == MonoIO.InvalidFileAttributes)
+                                       return false;
+
+                               if ((_data.fileAttributes & FileAttributes.Directory) == 0)
+                                       return false;
+
+                               return true;
+                       }
+               }
+
+               public override string Name {
+                       get { return current; }
+               }
+
+               public DirectoryInfo Parent {
+                       get {
+                               if ((parent == null) || (parent.Length == 0))
+                                       return null;
+                               return new DirectoryInfo (parent);
+                       }
+               }
+
+               public DirectoryInfo Root {
+                       get {
+                               string root = Path.GetPathRoot (FullPath);
+                               if (root == null)
+                                       return null;
+
+                               return new DirectoryInfo (root);
+                       }
+               }
+
+               // creational methods
+
+               public void Create ()
+               {
+                       Directory.CreateDirectory (FullPath);
+               }
+
+               public DirectoryInfo CreateSubdirectory (string path)
+               {
+                       CheckPath (path);
+
+                       path = Path.Combine (FullPath, path);
+                       Directory.CreateDirectory (path);
+                       return new DirectoryInfo (path);
+               }
+
+               // directory listing methods
+
+               public FileInfo [] GetFiles ()
+               {
+                       return GetFiles ("*");
+               }
+
+               public FileInfo [] GetFiles (string searchPattern)
+               {
+                       if (searchPattern == null)
+                               throw new ArgumentNullException ("searchPattern");
+
+                       string [] names = Directory.GetFiles (FullPath, searchPattern);
+
+                       FileInfo[] infos = new FileInfo [names.Length];
+                       int i = 0;
+                       foreach (string name in names)
+                               infos [i++] = new FileInfo (name);
+
+                       return infos;
+               }
+
+               public DirectoryInfo [] GetDirectories ()
+               {
+                       return GetDirectories ("*");
+               }
+
+               public DirectoryInfo [] GetDirectories (string searchPattern)
+               {
+                       if (searchPattern == null)
+                               throw new ArgumentNullException ("searchPattern");
+
+                       string [] names = Directory.GetDirectories (FullPath, searchPattern);
+
+                       DirectoryInfo[] infos = new DirectoryInfo [names.Length];
+                       int i = 0;
+                       foreach (string name in names)
+                               infos [i++] = new DirectoryInfo (name);
+
+                       return infos;
+               }
+
+               public FileSystemInfo [] GetFileSystemInfos ()
+               {
+                       return GetFileSystemInfos ("*");
+               }
+
+               public FileSystemInfo [] GetFileSystemInfos (string searchPattern)
+               {
+                       return GetFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
+               }
+
+               public
+               FileSystemInfo [] GetFileSystemInfos (string searchPattern, SearchOption searchOption)
+               {
+                       if (searchPattern == null)
+                               throw new ArgumentNullException ("searchPattern");
+                       if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
+                               throw new ArgumentOutOfRangeException ("searchOption", "Must be TopDirectoryOnly or AllDirectories");
+                       if (!Directory.Exists (FullPath))
+                               throw new IOException ("Invalid directory");
+
+                       List<FileSystemInfo> infos = new List<FileSystemInfo> ();
+                       InternalGetFileSystemInfos (searchPattern, searchOption, infos);
+                       return infos.ToArray ();
+               }
+
+               void InternalGetFileSystemInfos (string searchPattern, SearchOption searchOption, List<FileSystemInfo> infos)
+               {
+                       // UnauthorizedAccessExceptions might happen here and break everything for SearchOption.AllDirectories
+                       string [] dirs = Directory.GetDirectories (FullPath, searchPattern);
+                       string [] files = Directory.GetFiles (FullPath, searchPattern);
+
+                       Array.ForEach<string> (dirs, (dir) => { infos.Add (new DirectoryInfo (dir)); });
+                       Array.ForEach<string> (files, (file) => { infos.Add (new FileInfo (file)); });
+                       if (dirs.Length == 0 || searchOption == SearchOption.TopDirectoryOnly)
+                               return;
+
+                       foreach (string dir in dirs) {
+                               DirectoryInfo dinfo = new DirectoryInfo (dir);
+                               dinfo.InternalGetFileSystemInfos (searchPattern, searchOption, infos);
+                       }
+               }
+
+               // directory management methods
+
+               public override void Delete ()
+               {
+                       Delete (false);
+               }
+
+               public void Delete (bool recursive)
+               {
+                       Directory.Delete (FullPath, recursive);
+               }
+
+               public void MoveTo (string destDirName)
+               {
+                       if (destDirName == null)
+                               throw new ArgumentNullException ("destDirName");
+                       if (destDirName.Length == 0)
+                               throw new ArgumentException ("An empty file name is not valid.", "destDirName");
+
+                       Directory.Move (FullPath, Path.GetFullPath (destDirName));
+                       FullPath = OriginalPath = destDirName;
+                       Initialize ();
+               }
+
+               public override string ToString ()
+               {
+                       return OriginalPath;
+               }
+
+               public DirectoryInfo[] GetDirectories (string searchPattern, SearchOption searchOption)
+               {
+                   //NULL-check of searchPattern is done in Directory.GetDirectories
+                       string [] names = Directory.GetDirectories (FullPath, searchPattern, searchOption);
+                       //Convert the names to DirectoryInfo instances
+                       DirectoryInfo[] infos = new DirectoryInfo [names.Length];
+                       for (int i = 0; i<names.Length; ++i){
+                               string name = names[i];
+                               infos [i] = new DirectoryInfo (name);
+                       }
+                       return infos;
+               }       
+
+               internal int GetFilesSubdirs (ArrayList l, string pattern)
+               {
+                       int count;
+                       FileInfo [] thisdir = null;
+
+                       try {
+                               thisdir = GetFiles (pattern);
+                       } catch (System.UnauthorizedAccessException){
+                               return 0;
+                       }
+                       
+                       count = thisdir.Length;
+                       l.Add (thisdir);
+
+                       foreach (DirectoryInfo subdir in GetDirectories ()){
+                               count += subdir.GetFilesSubdirs (l, pattern);
+                       }
+                       return count;
+               }
+               
+               public FileInfo[] GetFiles (string searchPattern, SearchOption searchOption)
+               {
+                       switch (searchOption) {
+                       case SearchOption.TopDirectoryOnly:
+                               return GetFiles (searchPattern);
+                       case SearchOption.AllDirectories: {
+                               ArrayList groups = new ArrayList ();
+                               int count = GetFilesSubdirs (groups, searchPattern);
+                               int current = 0;
+                               
+                               FileInfo [] all = new FileInfo [count];
+                               foreach (FileInfo [] p in groups){
+                                       p.CopyTo (all, current);
+                                       current += p.Length;
+                               }
+                               return all;
+                       }
+                       default:
+                               string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", searchOption, "SearchOption");
+                               throw new ArgumentOutOfRangeException ("searchOption", msg);
+                       }
+               }
+
+               // access control methods
+
+               [MonoLimitation ("DirectorySecurity isn't implemented")]
+               public void Create (DirectorySecurity directorySecurity)
+               {
+                       if (directorySecurity != null)
+                               throw new UnauthorizedAccessException ();
+                       Create ();
+               }
+
+               [MonoLimitation ("DirectorySecurity isn't implemented")]
+               public DirectoryInfo CreateSubdirectory (string path, DirectorySecurity directorySecurity)
+               {
+                       if (directorySecurity != null)
+                               throw new UnauthorizedAccessException ();
+                       return CreateSubdirectory (path);
+               }
+
+               public DirectorySecurity GetAccessControl ()
+               {
+                       return Directory.GetAccessControl (FullPath);
+               }
+
+               public DirectorySecurity GetAccessControl (AccessControlSections includeSections)
+               {
+                       return Directory.GetAccessControl (FullPath, includeSections);
+               }
+
+               public void SetAccessControl (DirectorySecurity directorySecurity)
+               {
+                       Directory.SetAccessControl (FullPath, directorySecurity);
+               }
+
+
+               public IEnumerable<DirectoryInfo> EnumerateDirectories ()
+               {
+                       return EnumerateDirectories ("*", SearchOption.TopDirectoryOnly);
+               }
+
+               public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern)
+               {
+                       return EnumerateDirectories (searchPattern, SearchOption.TopDirectoryOnly);
+               }
+
+               public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern, SearchOption searchOption)
+               {
+                       if (searchPattern == null)
+                               throw new ArgumentNullException ("searchPattern");
+
+                       return CreateEnumerateDirectoriesIterator (searchPattern, searchOption);
+               }
+
+               IEnumerable<DirectoryInfo> CreateEnumerateDirectoriesIterator (string searchPattern, SearchOption searchOption)
+               {
+                       foreach (string name in Directory.EnumerateDirectories (FullPath, searchPattern, searchOption))
+                               yield return new DirectoryInfo (name);
+               }
+
+               public IEnumerable<FileInfo> EnumerateFiles ()
+               {
+                       return EnumerateFiles ("*", SearchOption.TopDirectoryOnly);
+               }
+
+               public IEnumerable<FileInfo> EnumerateFiles (string searchPattern)
+               {
+                       return EnumerateFiles (searchPattern, SearchOption.TopDirectoryOnly);
+               }
+
+               public IEnumerable<FileInfo> EnumerateFiles (string searchPattern, SearchOption searchOption)
+               {
+                       if (searchPattern == null)
+                               throw new ArgumentNullException ("searchPattern");
+
+                       return CreateEnumerateFilesIterator (searchPattern, searchOption);
+               }
+
+               IEnumerable<FileInfo> CreateEnumerateFilesIterator (string searchPattern, SearchOption searchOption)
+               {
+                       foreach (string name in Directory.EnumerateFiles (FullPath, searchPattern, searchOption))
+                               yield return new FileInfo (name);
+               }
+
+               public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos ()
+               {
+                       return EnumerateFileSystemInfos ("*", SearchOption.TopDirectoryOnly);
+               }
+
+               public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern)
+               {
+                       return EnumerateFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
+               }
+
+               public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern, SearchOption searchOption)
+               {
+                       if (searchPattern == null)
+                               throw new ArgumentNullException ("searchPattern");
+                       if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
+                               throw new ArgumentOutOfRangeException ("searchoption");
+
+                       return EnumerateFileSystemInfos (FullPath, searchPattern, searchOption);
+               }
+
+               static internal IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string basePath, string searchPattern, SearchOption searchOption)
+               {
+                       Path.Validate (basePath);
+
+                       SafeFindHandle findHandle = null;
+
+                       try {
+                               string filePath;
+                               int nativeAttrs;
+
+                               string basePathWithPattern = Path.Combine (basePath, searchPattern);
+
+                               int nativeError;
+                               try {} finally {
+                                       findHandle = new SafeFindHandle (MonoIO.FindFirstFile (basePathWithPattern, out filePath, out nativeAttrs, out nativeError));
+                               }
+
+                               if (findHandle.IsInvalid) {
+                                       MonoIOError error = (MonoIOError) nativeError;
+                                       if (error != MonoIOError.ERROR_FILE_NOT_FOUND)
+                                               throw MonoIO.GetException (Path.GetDirectoryName (basePathWithPattern), error);
+
+                                       yield break;
+                               }
+
+                               do {
+                                       if (filePath == null)
+                                               yield break;
+
+                                       if (filePath == "." || filePath == "..")
+                                               continue;
+
+                                       FileAttributes attrs = (FileAttributes) nativeAttrs;
+
+                                       string fullPath = Path.Combine (basePath, filePath);
+
+                                       if ((attrs & FileAttributes.ReparsePoint) == 0) {
+                                               if ((attrs & FileAttributes.Directory) != 0)
+                                                       yield return new DirectoryInfo (fullPath);
+                                               else
+                                                       yield return new FileInfo (fullPath);
+                                       }
+
+                                       if ((attrs & FileAttributes.Directory) != 0 && searchOption == SearchOption.AllDirectories) {
+                                               foreach (FileSystemInfo child in EnumerateFileSystemInfos (fullPath, searchPattern, searchOption))
+                                                       yield return child;
+                                       }
+                               } while (MonoIO.FindNextFile (findHandle.DangerousGetHandle (), out filePath, out nativeAttrs, out int _));
+                       } finally {
+                               if (findHandle != null)
+                                       findHandle.Dispose ();
+                       }
+               }
+               
+               internal void CheckPath (string path)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException ("path");
+                       if (path.Length == 0)
+                               throw new ArgumentException ("An empty file name is not valid.");
+                       if (path.IndexOfAny (Path.InvalidPathChars) != -1)
+                               throw new ArgumentException ("Illegal characters in path.");
+                       if (Environment.IsRunningOnWindows) {
+                               int idx = path.IndexOf (':');
+                               if (idx >= 0 && idx != 1)
+                                       throw new ArgumentException ("path");
+                       }
+               }
+       }
+}