Merge pull request #656 from LogosBible/collection_lock
[mono.git] / mcs / class / corlib / System / Console.cs
index c4ef5943efb963025b30a6f46b57ca50d9486a8e..08f8a4c1da1bfe92ecd879883d42b60b86374e1e 100644 (file)
@@ -7,7 +7,7 @@
 //
 // (C) Ximian, Inc.  http://www.ximian.com
 // (C) 2004,2005 Novell, Inc. (http://www.novell.com)
-//
+// Copyright 2013 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
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+using System.Diagnostics;
 using System.IO;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Security;
 using System.Security.Permissions;
 using System.Text;
 
 namespace System
 {
-       public
-#if NET_2_0
-       static
-#else
-       sealed
-#endif
-       class Console
+       public static partial class Console
        {
+#if !NET_2_1
                private class WindowsConsole
                {
+                       public static bool ctrlHandlerAdded = false;
+                       private delegate bool WindowsCancelHandler (int keyCode);
+                       private static WindowsCancelHandler cancelHandler = new WindowsCancelHandler (DoWindowsConsoleCancelEvent);
+
                        [DllImport ("kernel32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
                        private static extern int GetConsoleCP ();
                        [DllImport ("kernel32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
                        private static extern int GetConsoleOutputCP ();
 
+                       [DllImport ("kernel32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
+                       private static extern bool SetConsoleCtrlHandler (WindowsCancelHandler handler, bool addHandler);
+
+                       // Only call the event handler if Control-C was pressed (code == 0), nothing else
+                       private static bool DoWindowsConsoleCancelEvent (int keyCode)
+                       {
+                               if (keyCode == 0)
+                                       DoConsoleCancelEvent ();
+                               return keyCode == 0;
+                       }
+
                        [MethodImpl (MethodImplOptions.NoInlining)]
                        public static int GetInputCodePage ()
                        {
@@ -63,7 +75,20 @@ namespace System
                        {
                                return GetConsoleOutputCP ();
                        }
+
+                       public static void AddCtrlHandler ()
+                       {
+                               SetConsoleCtrlHandler (cancelHandler, true);
+                               ctrlHandlerAdded = true;
+                       }
+                       
+                       public static void RemoveCtrlHandler ()
+                       {
+                               SetConsoleCtrlHandler (cancelHandler, false);
+                               ctrlHandlerAdded = false;
+                       }
                }
+#endif
 
                internal static TextWriter stdout;
                private static TextWriter stderr;
@@ -71,7 +96,7 @@ namespace System
 
                static Console ()
                {
-#if !NET_2_0 || NET_2_1
+#if NET_2_1
                        Encoding inputEncoding;
                        Encoding outputEncoding;
 #endif
@@ -80,6 +105,10 @@ namespace System
                                //
                                // On Windows, follow the Windows tradition
                                //
+#if NET_2_1
+                               // should never happen since Moonlight does not run on windows
+                               inputEncoding = outputEncoding = Encoding.Default;
+#else                  
                                try {
                                        inputEncoding = Encoding.GetEncoding (WindowsConsole.GetInputCodePage ());
                                        outputEncoding = Encoding.GetEncoding (WindowsConsole.GetOutputCodePage ());
@@ -87,8 +116,9 @@ namespace System
                                } catch {
                                        // FIXME: I18N assemblies are not available when compiling mcs
                                        // Use Latin 1 as it is fast and UTF-8 is never used as console code page
-                                       inputEncoding = outputEncoding = Encoding.GetEncoding (28591);
+                                       inputEncoding = outputEncoding = Encoding.Default;
                                }
+#endif
                        } else {
                                //
                                // On Unix systems (128), do not output the
@@ -104,38 +134,61 @@ namespace System
                                        inputEncoding = outputEncoding = Encoding.Default;
                        }
 
-                       stderr = new UnexceptionalStreamWriter (OpenStandardError (0), outputEncoding); 
-                       ((StreamWriter)stderr).AutoFlush = true;
-                       stderr = TextWriter.Synchronized (stderr, true);
+                       SetupStreams (inputEncoding, outputEncoding);
+               }
 
-#if NET_2_0 && !NET_2_1
+               static void SetupStreams (Encoding inputEncoding, Encoding outputEncoding)
+               {
+#if !NET_2_1
                        if (!Environment.IsRunningOnWindows && ConsoleDriver.IsConsole) {
                                StreamWriter w = new CStreamWriter (OpenStandardOutput (0), outputEncoding);
                                w.AutoFlush = true;
                                stdout = TextWriter.Synchronized (w, true);
+
+                               w = new CStreamWriter (OpenStandardOutput (0), outputEncoding);
+                               w.AutoFlush = true;
+                               stderr = TextWriter.Synchronized (w, true);
+                               
                                stdin = new CStreamReader (OpenStandardInput (0), inputEncoding);
                        } else {
 #endif
+// FULL_AOT_RUNTIME is used (instead of MONOTOUCH) since we only want this code when running on 
+// iOS (simulator or devices) and *not* when running tools (e.g. btouch #12179) that needs to use 
+// the mscorlib.dll shipped with Xamarin.iOS
+#if MONOTOUCH && FULL_AOT_RUNTIME
+                               stdout = new NSLogWriter ();
+#else
                                stdout = new UnexceptionalStreamWriter (OpenStandardOutput (0), outputEncoding);
                                ((StreamWriter)stdout).AutoFlush = true;
+#endif
                                stdout = TextWriter.Synchronized (stdout, true);
+
+#if MONOTOUCH && FULL_AOT_RUNTIME
+                               stderr = new NSLogWriter ();
+#else
+                               stderr = new UnexceptionalStreamWriter (OpenStandardError (0), outputEncoding); 
+                               ((StreamWriter)stderr).AutoFlush = true;
+#endif
+                               stderr = TextWriter.Synchronized (stderr, true);
+
                                stdin = new UnexceptionalStreamReader (OpenStandardInput (0), inputEncoding);
                                stdin = TextReader.Synchronized (stdin);
-#if NET_2_0 && !NET_2_1
+#if !NET_2_1
                        }
 #endif
 
+#if MONODROID
+                       if (LogcatTextWriter.IsRunningOnAndroid ()) {
+                               stdout = TextWriter.Synchronized (new LogcatTextWriter ("mono-stdout", stdout));
+                               stderr = TextWriter.Synchronized (new LogcatTextWriter ("mono-stderr", stderr));
+                       }
+#endif  // MONODROID
+
                        GC.SuppressFinalize (stdout);
                        GC.SuppressFinalize (stderr);
                        GC.SuppressFinalize (stdin);
                }
 
-#if !NET_2_0
-               private Console ()
-               {
-               }
-#endif
-
                public static TextWriter Error {
                        get {
                                return stderr;
@@ -154,6 +207,15 @@ namespace System
                        }
                }
 
+               private static Stream Open (IntPtr handle, FileAccess access, int bufferSize)
+               {
+                       try {
+                               return new FileStream (handle, access, false, bufferSize, false, bufferSize == 0);
+                       } catch (IOException) {
+                               return new NullStream ();
+                       }
+               }
+
                public static Stream OpenStandardError ()
                {
                        return OpenStandardError (0);
@@ -166,11 +228,7 @@ namespace System
                [SecurityPermission (SecurityAction.Assert, UnmanagedCode = true)]
                public static Stream OpenStandardError (int bufferSize)
                {
-                       try {
-                               return new FileStream (MonoIO.ConsoleError, FileAccess.Write, false, bufferSize, false, bufferSize == 0);
-                       } catch (IOException) {
-                               return new NullStream ();
-                       }
+                       return Open (MonoIO.ConsoleError, FileAccess.Write, bufferSize);
                }
 
                public static Stream OpenStandardInput ()
@@ -185,11 +243,7 @@ namespace System
                [SecurityPermission (SecurityAction.Assert, UnmanagedCode = true)]
                public static Stream OpenStandardInput (int bufferSize)
                {
-                       try {
-                               return new FileStream (MonoIO.ConsoleInput, FileAccess.Read, false, bufferSize, false, bufferSize == 0);
-                       } catch (IOException) {
-                               return new NullStream ();
-                       }
+                       return Open (MonoIO.ConsoleInput, FileAccess.Read, bufferSize);
                }
 
                public static Stream OpenStandardOutput ()
@@ -204,11 +258,7 @@ namespace System
                [SecurityPermission (SecurityAction.Assert, UnmanagedCode = true)]
                public static Stream OpenStandardOutput (int bufferSize)
                {
-                       try {
-                               return new FileStream (MonoIO.ConsoleOutput, FileAccess.Write, false, bufferSize, false, bufferSize == 0);
-                       } catch (IOException) {
-                               return new NullStream ();
-                       }
+                       return Open (MonoIO.ConsoleOutput, FileAccess.Write, bufferSize);
                }
 
                [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
@@ -248,9 +298,9 @@ namespace System
                        stdout.Write (value);
                }
 
-               public static void Write (char[] value)
+               public static void Write (char[] buffer)
                {
-                       stdout.Write (value);
+                       stdout.Write (buffer);
                }
 
                public static void Write (decimal value)
@@ -307,7 +357,10 @@ namespace System
 
                public static void Write (string format, params object[] arg)
                {
-                       stdout.Write (format, arg);
+                       if (arg == null)
+                               stdout.Write (format);
+                       else
+                               stdout.Write (format, arg);
                }
 
                public static void Write (char[] buffer, int index, int count)
@@ -325,7 +378,6 @@ namespace System
                        stdout.Write (format, arg0, arg1, arg2);
                }
 
-#if ! BOOTSTRAP_WITH_OLDLIB
                [CLSCompliant (false)]
                public static void Write (string format, object arg0, object arg1, object arg2, object arg3, __arglist)
                {
@@ -344,7 +396,6 @@ namespace System
 
                        stdout.Write (String.Format (format, args));
                }
-#endif
 
                public static void WriteLine ()
                {
@@ -361,9 +412,9 @@ namespace System
                        stdout.WriteLine (value);
                }
 
-               public static void WriteLine (char[] value)
+               public static void WriteLine (char[] buffer)
                {
-                       stdout.WriteLine (value);
+                       stdout.WriteLine (buffer);
                }
 
                public static void WriteLine (decimal value)
@@ -420,7 +471,10 @@ namespace System
 
                public static void WriteLine (string format, params object[] arg)
                {
-                       stdout.WriteLine (format, arg);
+                       if (arg == null)
+                               stdout.WriteLine (format);
+                       else
+                               stdout.WriteLine (format, arg);
                }
 
                public static void WriteLine (char[] buffer, int index, int count)
@@ -438,7 +492,6 @@ namespace System
                        stdout.WriteLine (format, arg0, arg1, arg2);
                }
 
-#if ! BOOTSTRAP_WITH_OLDLIB
                [CLSCompliant (false)]
                public static void WriteLine (string format, object arg0, object arg1, object arg2, object arg3, __arglist)
                {
@@ -457,9 +510,8 @@ namespace System
 
                        stdout.WriteLine (String.Format (format, args));
                }
-#endif
 
-#if NET_2_0 && !NET_2_1
+#if !NET_2_1
                public static int Read ()
                {
                        if ((stdin is CStreamReader) && ConsoleDriver.IsConsole) {
@@ -490,19 +542,25 @@ namespace System
 
 #endif
 
-#if NET_2_0 && !NET_2_1
+#if !NET_2_1
                // FIXME: Console should use these encodings when changed
                static Encoding inputEncoding;
                static Encoding outputEncoding;
 
                public static Encoding InputEncoding {
                        get { return inputEncoding; }
-                       set { inputEncoding = value; }
+                       set {
+                               inputEncoding = value;
+                               SetupStreams (inputEncoding, outputEncoding);
+                       }
                }
 
                public static Encoding OutputEncoding {
                        get { return outputEncoding; }
-                       set { outputEncoding = value; }
+                       set {
+                               outputEncoding = value;
+                               SetupStreams (inputEncoding, outputEncoding);
+                       }
                }
 
                public static ConsoleColor BackgroundColor {
@@ -512,14 +570,17 @@ namespace System
 
                public static int BufferHeight {
                        get { return ConsoleDriver.BufferHeight; }
+                       [MonoLimitation ("Implemented only on Windows")]
                        set { ConsoleDriver.BufferHeight = value; }
                }
 
                public static int BufferWidth {
                        get { return ConsoleDriver.BufferWidth; }
+                       [MonoLimitation ("Implemented only on Windows")]
                        set { ConsoleDriver.BufferWidth = value; }
                }
 
+               [MonoLimitation ("Implemented only on Windows")]
                public static bool CapsLock {
                        get { return ConsoleDriver.CapsLock; }
                }
@@ -561,6 +622,7 @@ namespace System
                        get { return ConsoleDriver.LargestWindowWidth; }
                }
 
+               [MonoLimitation ("Only works on windows")]
                public static bool NumberLock {
                        get { return ConsoleDriver.NumberLock; }
                }
@@ -575,26 +637,50 @@ namespace System
                        set { ConsoleDriver.TreatControlCAsInput = value; }
                }
 
+               [MonoLimitation ("Only works on windows")]
                public static int WindowHeight {
                        get { return ConsoleDriver.WindowHeight; }
                        set { ConsoleDriver.WindowHeight = value; }
                }
 
+               [MonoLimitation ("Only works on windows")]
                public static int WindowLeft {
                        get { return ConsoleDriver.WindowLeft; }
                        set { ConsoleDriver.WindowLeft = value; }
                }
 
+               [MonoLimitation ("Only works on windows")]
                public static int WindowTop {
                        get { return ConsoleDriver.WindowTop; }
                        set { ConsoleDriver.WindowTop = value; }
                }
 
+               [MonoLimitation ("Only works on windows")]
                public static int WindowWidth {
                        get { return ConsoleDriver.WindowWidth; }
                        set { ConsoleDriver.WindowWidth = value; }
                }
 
+#if NET_4_5
+               public static bool IsErrorRedirected {
+                       get {
+                               return ConsoleDriver.IsErrorRedirected;
+                       }
+               }
+
+               public static bool IsOutputRedirected {
+                       get {
+                               return ConsoleDriver.IsOutputRedirected;
+                       }
+               }
+
+               public static bool IsInputRedirected {
+                       get {
+                               return ConsoleDriver.IsInputRedirected;
+                       }
+               }
+#endif
+
                public static void Beep ()
                {
                        Beep (1000, 500);
@@ -616,19 +702,20 @@ namespace System
                        ConsoleDriver.Clear ();
                }
 
-               [MonoTODO ("Not implemented")]
+               [MonoLimitation ("Implemented only on Windows")]
                public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight,
                                                int targetLeft, int targetTop)
                {
-                       throw new NotImplementedException ();
+                       ConsoleDriver.MoveBufferArea (sourceLeft, sourceTop, sourceWidth, sourceHeight, targetLeft, targetTop);
                }
 
-               [MonoTODO("Not implemented")]
+               [MonoLimitation ("Implemented only on Windows")]
                public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight,
                                                int targetLeft, int targetTop, Char sourceChar,
                                                ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
                {
-                       throw new NotImplementedException ();
+                       ConsoleDriver.MoveBufferArea (sourceLeft, sourceTop, sourceWidth, sourceHeight, targetLeft, targetTop,
+                                                       sourceChar, sourceForeColor, sourceBackColor);
                }
 
                public static ConsoleKeyInfo ReadKey ()
@@ -646,6 +733,7 @@ namespace System
                        ConsoleDriver.ResetColor ();
                }
 
+               [MonoLimitation ("Only works on windows")]
                public static void SetBufferSize (int width, int height)
                {
                        ConsoleDriver.SetBufferSize (width, height);
@@ -673,17 +761,33 @@ namespace System
                                        ConsoleDriver.Init ();
 
                                cancel_event += value;
+
+                               if (Environment.IsRunningOnWindows && !WindowsConsole.ctrlHandlerAdded)
+                                       WindowsConsole.AddCtrlHandler();
                        }
                        remove {
                                if (ConsoleDriver.Initialized == false)
                                        ConsoleDriver.Init ();
 
                                cancel_event -= value;
+
+                               if (cancel_event == null && Environment.IsRunningOnWindows)
+                               {
+                                       // Need to remove our hook if there's nothing left in the event
+                                       if (WindowsConsole.ctrlHandlerAdded)
+                                               WindowsConsole.RemoveCtrlHandler();
+                               }
                        }
                }
 
                delegate void InternalCancelHandler ();
-               static InternalCancelHandler cancel_handler = new InternalCancelHandler (DoConsoleCancelEvent);
+               
+#pragma warning disable 414
+               //
+               // Used by console-io.c
+               //
+               static readonly InternalCancelHandler cancel_handler = new InternalCancelHandler (DoConsoleCancelEvent);
+#pragma warning restore 414            
 
                internal static void DoConsoleCancelEvent ()
                {