[File.ReadLines] Dispose the StreamReader in the right place.
[mono.git] / mcs / class / corlib / System.IO / File.cs
index 77cdcba15a55c3901fef7a23743c9d8cf2f7ead9..d768e9457c0440d0afc14ecfe129444f87d2ac21 100644 (file)
@@ -10,7 +10,7 @@
 //
 // Copyright 2002 Ximian, Inc. http://www.ximian.com
 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
-// Copyright (C) 2004, 2006 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004, 2006, 2010 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 //
 
 using System;
-using System.Text;
-#if NET_2_0
 using System.Collections.Generic;
+using System.Diagnostics;
+using System.Security;
+using System.Text;
 using System.Runtime.InteropServices;
+
+#if !NET_2_1
 using System.Security.AccessControl;
 #endif
 
 namespace System.IO
 {
-#if NET_2_0
        [ComVisible (true)]
-#endif
-       public
-#if NET_2_0
-       static
-#else
-       sealed
-#endif
-       class File
+       public static class File
        {
-
-#if !NET_2_0
-               private File () {}
-#endif
-
-#if NET_2_0
                public static void AppendAllText (string path, string contents)
                {
                        using (TextWriter w = new StreamWriter (path, true)) {
@@ -72,7 +61,6 @@ namespace System.IO
                                w.Write (contents);
                        }
                }
-#endif
 
                public static StreamWriter AppendText (string path)
                {
@@ -101,6 +89,8 @@ namespace System.IO
                        if (destFileName.Trim ().Length == 0 || destFileName.IndexOfAny (Path.InvalidPathChars) != -1)
                                throw new ArgumentException ("The file name is not valid.");
 
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
                        if (!MonoIO.Exists (sourceFileName, out error))
                                throw new FileNotFoundException (Locale.GetText ("{0} does not exist", sourceFileName), sourceFileName);
                        if ((GetAttributes (sourceFileName) & FileAttributes.Directory) == FileAttributes.Directory)
@@ -125,40 +115,32 @@ namespace System.IO
 
                public static FileStream Create (string path)
                {
-                       return Create (path, 8192, FileOptions.None, null);
+                       return Create (path, 8192);
                }
 
                public static FileStream Create (string path, int bufferSize)
                {
-                       return Create (path, bufferSize, FileOptions.None, null);
+                       return new FileStream (path, FileMode.Create, FileAccess.ReadWrite,
+                               FileShare.None, bufferSize);
                }
 
-#if NET_2_0
-               [MonoTODO ("options not implemented")]
+#if !NET_2_1
+               [MonoLimitation ("FileOptions are ignored")]
                public static FileStream Create (string path, int bufferSize,
                                                 FileOptions options)
                {
                        return Create (path, bufferSize, options, null);
                }
                
-               [MonoTODO ("options and fileSecurity not implemented")]
+               [MonoLimitation ("FileOptions and FileSecurity are ignored")]
                public static FileStream Create (string path, int bufferSize,
                                                 FileOptions options,
                                                 FileSecurity fileSecurity)
-#else
-               private static FileStream Create (string path, int bufferSize,
-                                                 FileOptions options,
-                                                 object fileSecurity)
-#endif
                {
-#if NET_2_0
                        return new FileStream (path, FileMode.Create, FileAccess.ReadWrite,
                                FileShare.None, bufferSize, options);
-#else
-                       return new FileStream (path, FileMode.Create, FileAccess.ReadWrite,
-                               FileShare.None, bufferSize);
-#endif
                }
+#endif
 
                public static StreamWriter CreateText (string path)
                {
@@ -167,10 +149,7 @@ namespace System.IO
 
                public static void Delete (string path)
                {
-                       if (path == null)
-                               throw new ArgumentNullException("path");
-                       if (path.Trim().Length == 0 || path.IndexOfAny(Path.InvalidPathChars) >= 0)
-                               throw new ArgumentException("path");
+                       Path.Validate (path);
                        if (Directory.Exists (path))
                                throw new UnauthorizedAccessException(Locale.GetText ("{0} is a directory", path));
 
@@ -178,6 +157,8 @@ namespace System.IO
                        if (DirName != String.Empty && !Directory.Exists (DirName))
                                throw new DirectoryNotFoundException (Locale.GetText ("Could not find a part of the path \"{0}\".", path));
 
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
                        MonoIOError error;
                        
                        if (!MonoIO.DeleteFile (path, out error)){
@@ -193,16 +174,18 @@ namespace System.IO
                        // any problem with the path or permissions.
                        // Minimizes what information can be
                        // discovered by using this method.
-                       if (path == null || path.Trim().Length == 0
-                           || path.IndexOfAny(Path.InvalidPathChars) >= 0) {
+                       if (String.IsNullOrWhiteSpace (path) || path.IndexOfAny(Path.InvalidPathChars) >= 0)
+                               return false;
+
+                       // on Moonlight this does not throw but returns false
+                       if (!SecurityManager.CheckElevatedPermissions ())
                                return false;
-                       }
 
                        MonoIOError error;
                        return MonoIO.ExistsFile (path, out error);
                }
 
-#if NET_2_0
+#if !NET_2_1
                public static FileSecurity GetAccessControl (string path)
                {
                        throw new NotImplementedException ();
@@ -216,12 +199,8 @@ namespace System.IO
 
                public static FileAttributes GetAttributes (string path)
                {
-                       if (path == null)
-                               throw new ArgumentNullException("path");
-                       if (path.Trim ().Length == 0)
-                               throw new ArgumentException (Locale.GetText ("Path is empty"));
-                       if (path.IndexOfAny (Path.InvalidPathChars) >= 0)
-                               throw new ArgumentException (Locale.GetText ("Path contains invalid chars"));
+                       Path.Validate (path);
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
 
                        MonoIOError error;
                        FileAttributes attrs;
@@ -236,17 +215,14 @@ namespace System.IO
                {
                        MonoIOStat stat;
                        MonoIOError error;
-                       CheckPathExceptions (path);
+                       Path.Validate (path);
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
 
                        if (!MonoIO.GetFileStat (path, out stat, out error)) {
-#if NET_2_0
                                if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
                                        return DefaultLocalFileTime;
                                else
                                        throw new IOException (path);
-#else
-                               throw CreatePartOfPathNotFoundException (path);
-#endif
                        }
                        return DateTime.FromFileTime (stat.CreationTime);
                }
@@ -260,17 +236,14 @@ namespace System.IO
                {
                        MonoIOStat stat;
                        MonoIOError error;
-                       CheckPathExceptions (path);
+                       Path.Validate (path);
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
 
                        if (!MonoIO.GetFileStat (path, out stat, out error)) {
-#if NET_2_0
                                if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
                                        return DefaultLocalFileTime;
                                else
                                        throw new IOException (path);
-#else
-                               throw CreatePartOfPathNotFoundException (path);
-#endif
                        }
                        return DateTime.FromFileTime (stat.LastAccessTime);
                }
@@ -284,17 +257,14 @@ namespace System.IO
                {
                        MonoIOStat stat;
                        MonoIOError error;
-                       CheckPathExceptions (path);
+                       Path.Validate (path);
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
 
                        if (!MonoIO.GetFileStat (path, out stat, out error)) {
-#if NET_2_0
                                if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
                                        return DefaultLocalFileTime;
                                else
                                        throw new IOException (path);
-#else
-                               throw CreatePartOfPathNotFoundException (path);
-#endif
                        }
                        return DateTime.FromFileTime (stat.LastWriteTime);
                }
@@ -306,8 +276,6 @@ namespace System.IO
 
                public static void Move (string sourceFileName, string destFileName)
                {
-                       MonoIOError error;
-
                        if (sourceFileName == null)
                                throw new ArgumentNullException ("sourceFileName");
                        if (destFileName == null)
@@ -320,6 +288,10 @@ namespace System.IO
                                throw new ArgumentException ("An empty file name is not valid.", "destFileName");
                        if (destFileName.Trim ().Length == 0 || destFileName.IndexOfAny (Path.InvalidPathChars) != -1)
                                throw new ArgumentException ("The file name is not valid.");
+
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
+                       MonoIOError error;
                        if (!MonoIO.Exists (sourceFileName, out error))
                                throw new FileNotFoundException (Locale.GetText ("{0} does not exist", sourceFileName), sourceFileName);
 
@@ -332,11 +304,7 @@ namespace System.IO
                        string DirName;
                        DirName = Path.GetDirectoryName (destFileName);
                        if (DirName != String.Empty && !Directory.Exists (DirName))
-#if NET_2_0
                                throw new DirectoryNotFoundException (Locale.GetText ("Could not find a part of the path."));
-#else
-                               throw new DirectoryNotFoundException (Locale.GetText ("Could not find a part of the path '{0}'.", destFileName));
-#endif
 
                        if (!MonoIO.MoveFile (sourceFileName, destFileName, out error)) {
                                if (error == MonoIOError.ERROR_ALREADY_EXISTS)
@@ -379,7 +347,6 @@ namespace System.IO
                        return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
                }
 
-#if NET_2_0
                public static void Replace (string sourceFileName,
                                            string destinationFileName,
                                            string destinationBackupFileName)
@@ -440,7 +407,8 @@ namespace System.IO
                                throw MonoIO.GetException (error);
                        }
                }
-               
+
+#if !NET_2_1
                public static void SetAccessControl (string path,
                                                     FileSecurity fileSecurity)
                {
@@ -452,7 +420,7 @@ namespace System.IO
                                                  FileAttributes fileAttributes)
                {
                        MonoIOError error;
-                       CheckPathExceptions (path);
+                       Path.Validate (path);
 
                        if (!MonoIO.SetFileAttributes (path, fileAttributes, out error))
                                throw MonoIO.GetException (path, error);
@@ -461,7 +429,7 @@ namespace System.IO
                public static void SetCreationTime (string path, DateTime creationTime)
                {
                        MonoIOError error;
-                       CheckPathExceptions (path);
+                       Path.Validate (path);
                        if (!MonoIO.Exists (path, out error))
                                throw MonoIO.GetException (path, error);
                        if (!MonoIO.SetCreationTime (path, creationTime, out error))
@@ -476,7 +444,7 @@ namespace System.IO
                public static void SetLastAccessTime (string path, DateTime lastAccessTime)
                {
                        MonoIOError error;
-                       CheckPathExceptions (path);
+                       Path.Validate (path);
                        if (!MonoIO.Exists (path, out error))
                                throw MonoIO.GetException (path, error);
                        if (!MonoIO.SetLastAccessTime (path, lastAccessTime, out error))
@@ -492,7 +460,7 @@ namespace System.IO
                                                     DateTime lastWriteTime)
                {
                        MonoIOError error;
-                       CheckPathExceptions (path);
+                       Path.Validate (path);
                        if (!MonoIO.Exists (path, out error))
                                throw MonoIO.GetException (path, error);
                        if (!MonoIO.SetLastWriteTime (path, lastWriteTime, out error))
@@ -505,29 +473,6 @@ namespace System.IO
                        SetLastWriteTime (path, lastWriteTimeUtc.ToLocalTime ());
                }
 
-               #region Private
-
-               private static void CheckPathExceptions (string path)
-               {
-                       if (path == null)
-                               throw new System.ArgumentNullException("path");
-                       if (path.Length == 0)
-                               throw new System.ArgumentException(Locale.GetText ("Path is empty"));
-                       if (path.Trim().Length == 0)
-                               throw new ArgumentException (Locale.GetText ("Path is empty"));
-                       if (path.IndexOfAny (Path.InvalidPathChars) != -1)
-                               throw new ArgumentException (Locale.GetText ("Path contains invalid chars"));
-               }
-
-               private static IOException CreatePartOfPathNotFoundException (string path)
-               {
-                       string msg = Locale.GetText ("Part of the path \"{0}\" could not be found.", path);
-                       return new IOException (msg);
-               }
-
-               #endregion
-
-#if NET_2_0
                //
                // The documentation for this method is most likely wrong, it
                // talks about doing a "binary read", but the remarks say
@@ -538,19 +483,22 @@ namespace System.IO
                //
                public static byte [] ReadAllBytes (string path)
                {
-                       using (FileStream s = Open (path, FileMode.Open, FileAccess.Read, FileShare.Read)){
+                       using (FileStream s = OpenRead (path)) {
                                long size = s.Length;
-
-                               //
-                               // Is this worth supporting?
-                               // 
+                               // limited to 2GB according to http://msdn.microsoft.com/en-us/library/system.io.file.readallbytes.aspx
                                if (size > Int32.MaxValue)
-                                       throw new ArgumentException ("Reading more than 4gigs with this call is not supported");
-                               
-                               byte [] result = new byte [s.Length];
-
-                               s.Read (result, 0, (int) size);
-
+                                       throw new IOException ("Reading more than 2GB with this call is not supported");
+
+                               int pos = 0;
+                               int count = (int) size;
+                               byte [] result = new byte [size];
+                               while (count > 0) {
+                                       int n = s.Read (result, pos, count);
+                                       if (n == 0)
+                                               throw new IOException ("Unexpected end of stream");
+                                       pos += n;
+                                       count -= n;
+                               }
                                return result;
                        }
                }
@@ -579,7 +527,9 @@ namespace System.IO
 
                public static string ReadAllText (string path)
                {
-                       return ReadAllText (path, Encoding.UTF8Unmarked);
+                       using (StreamReader sr = new StreamReader (path)) {
+                               return sr.ReadToEnd ();
+                       }
                }
 
                public static string ReadAllText (string path, Encoding encoding)
@@ -658,6 +608,80 @@ namespace System.IO
                        // handling this exception to work properly.
                        throw new NotSupportedException (Locale.GetText ("File encryption isn't supported on any file system."));
                }
+
+#if MOONLIGHT || NET_4_0
+               public static IEnumerable<string> ReadLines (string path)
+               {
+                       return ReadLines (File.OpenText (path));
+               }
+
+               public static IEnumerable<string> ReadLines (string path, Encoding encoding)
+               {
+                       return ReadLines (new StreamReader (path, encoding));
+               }
+
+               // refactored in order to avoid compiler-generated names for Moonlight tools
+               static IEnumerable<string> ReadLines (StreamReader reader)
+               {
+                       using (reader) {
+                               string s;
+                               while ((s = reader.ReadLine ()) != null) {
+                                       yield return s;
+                               }
+                       }
+               }
+
+               public static void AppendAllLines (string path, IEnumerable<string> contents)
+               {
+                       Path.Validate (path);
+
+                       if (contents == null)
+                               return;
+
+                       using (TextWriter w = new StreamWriter (path, true)) {
+                               foreach (var line in contents)
+                                       w.Write (line);
+                       }
+               }
+
+               public static void AppendAllLines (string path, IEnumerable<string> contents, Encoding encoding)
+               {
+                       Path.Validate (path);
+
+                       if (contents == null)
+                               return;
+
+                       using (TextWriter w = new StreamWriter (path, true, encoding)) {
+                               foreach (var line in contents)
+                                       w.Write (line);
+                       }
+               }
+
+               public static void WriteAllLines (string path, IEnumerable<string> contents)
+               {
+                       Path.Validate (path);
+
+                       if (contents == null)
+                               return;
+
+                       using (TextWriter w = new StreamWriter (path, false)) {
+                               foreach (var line in contents)
+                                       w.Write (line);
+                       }
+               }
+
+               public static void WriteAllLines (string path, IEnumerable<string> contents, Encoding encoding)
+               {
+                       Path.Validate (path);
+
+                       if (contents == null)
+                               return;
+
+                       using (TextWriter w = new StreamWriter (path, false, encoding)) {
+                               foreach (var line in contents)
+                                       w.Write (line);
+                       }
+               }
 #endif
        }
 }