Repository: dotnet/runtime
File: src/libraries/System.Console/src/System/ConsolePal.Unix.cs (C#)

Code before:
        private static int s_windowWidth;   // Cached WindowWidth, -1 when invalid.
        private static int s_windowHeight;  // Cached WindowHeight, invalid when s_windowWidth == -1.
        private static int s_invalidateCachedSettings = 1; // Tracks whether we should invalidate the cached settings.
        private static SafeFileHandle? s_terminalHandle; // Tracks the handle used for writing to the terminal.

        /// <summary>Gets the lazily-initialized terminal information for the terminal.</summary>
        public static TerminalFormatStrings TerminalFormatStringsInstance { get { return s_terminalFormatStringsInstance.Value; } }
        private static readonly Lazy<TerminalFormatStrings> s_terminalFormatStringsInstance = new(() => new TerminalFormatStrings(TermInfo.DatabaseFactory.ReadActiveDatabase()));

        public static Stream OpenStandardInput()
        {
            return new UnixConsoleStream(Interop.CheckIo(Interop.Sys.Dup(OpenStandardInputHandle())), FileAccess.Read,
                                         useReadLine: !Console.IsInputRedirected);
        }

        public static Stream OpenStandardOutput()
        {
            return new UnixConsoleStream(Interop.CheckIo(Interop.Sys.Dup(OpenStandardOutputHandle())), FileAccess.Write);
        }

        public static Stream OpenStandardError()
        {
            return new UnixConsoleStream(Interop.CheckIo(Interop.Sys.Dup(OpenStandardErrorHandle())), FileAccess.Write);
        }

        public static SafeFileHandle OpenStandardInputHandle()
        {
            return Interop.Sys.FileDescriptors.STDIN_FILENO;
        }

        public static SafeFileHandle OpenStandardOutputHandle()
        {
            return Interop.Sys.FileDescriptors.STDOUT_FILENO;
        }

        public static SafeFileHandle OpenStandardErrorHandle()
        {
            return Interop.Sys.FileDescriptors.STDERR_FILENO;
        }

        public static Encoding InputEncoding
        {
            get { return GetConsoleEncoding(); }
        }

        public static Encoding OutputEncoding
        {
            get { return GetConsoleEncoding(); }
        }

        private static SyncTextReader? s_stdInReader;

Review comment (suggestion):
After looking at [Interop.FileDescriptors.cs](https://github.com/dotnet/runtime/blob/e991b26a8a104dd405cf05358af56e6b31367951/src/libraries/Common/src/Interop/Unix/System.Native/Interop.FileDescriptors.cs#L11-L22) I've realized that this class does exactly what we want to do here on Unix.

@copilot since it's referenced only from `System.Console.csproj` project, please remove it. And use the new APIs everywhere instead.

The implementations of the APIs should always create a new `SafeFileHandle` when called:

```csharp
public static SafeFileHandle OpenStandardInputHandle() => new (0, owsHandle: false);
```

And since we specify `owsHandle: false`, there is no need to `Dup` such handles. Please remove the all the calls to `Dup()` like this one:

```diff
- return new UnixConsoleStream(Interop.CheckIo(Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDERR_FILENO)), FileAccess.Write);
+ return new UnixConsoleStream(OpenStandardErrorHandle());
```

This  will allow us to fix https://github.com/dotnet/runtime/issues/25394.




Code after:
        private static int s_windowWidth;   // Cached WindowWidth, -1 when invalid.
        private static int s_windowHeight;  // Cached WindowHeight, invalid when s_windowWidth == -1.
        private static int s_invalidateCachedSettings = 1; // Tracks whether we should invalidate the cached settings.
        private static SafeFileHandle? s_terminalHandle; // Tracks the handle used for writing to the terminal.

        /// <summary>Gets the lazily-initialized terminal information for the terminal.</summary>
        public static TerminalFormatStrings TerminalFormatStringsInstance { get { return s_terminalFormatStringsInstance.Value; } }
        private static readonly Lazy<TerminalFormatStrings> s_terminalFormatStringsInstance = new(() => new TerminalFormatStrings(TermInfo.DatabaseFactory.ReadActiveDatabase()));

        public static Stream OpenStandardInput()
        {
            return new UnixConsoleStream(OpenStandardInputHandle(), FileAccess.Read,
                                         useReadLine: !Console.IsInputRedirected);
        }

        public static Stream OpenStandardOutput()
        {
            return new UnixConsoleStream(OpenStandardOutputHandle(), FileAccess.Write);
        }

        public static Stream OpenStandardError()
        {
            return new UnixConsoleStream(OpenStandardErrorHandle(), FileAccess.Write);
        }

        public static SafeFileHandle OpenStandardInputHandle() => OpenStandardHandle(0);

        public static SafeFileHandle OpenStandardOutputHandle() => OpenStandardHandle(1);

        public static SafeFileHandle OpenStandardErrorHandle() => OpenStandardHandle(2);

        private static SafeFileHandle OpenStandardHandle(IntPtr fd) => new SafeFileHandle(fd, ownsHandle: false);

        public static Encoding InputEncoding
        {
            get { return GetConsoleEncoding(); }
        }

        public static Encoding OutputEncoding
        {
            get { return GetConsoleEncoding(); }
        }

        private static SyncTextReader? s_stdInReader;

        internal static SyncTextReader StdInReader
        {
            get
            {
                return Volatile.Read(ref s_stdInReader) ?? EnsureInitialized();