using System; using System.IO; using System.Text; using System.Threading; using AspClassic.Scripting.Utils; namespace AspClassic.Scripting.Runtime; public sealed class SharedIO { private sealed class StreamProxy : Stream { private readonly ConsoleStreamType _type; private readonly SharedIO _io; public override bool CanRead => _type == ConsoleStreamType.Input; public override bool CanSeek => false; public override bool CanWrite => !CanRead; public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } public StreamProxy(SharedIO io, ConsoleStreamType type) { _io = io; _type = type; } public override void Flush() { _io.GetStream(_type).Flush(); } public override int Read(byte[] buffer, int offset, int count) { return _io.GetStream(_type).Read(buffer, offset, count); } public override void Write(byte[] buffer, int offset, int count) { _io.GetStream(_type).Write(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } } private readonly object _mutex = new object(); private Stream _inputStream; private Stream _outputStream; private Stream _errorStream; private TextReader _inputReader; private TextWriter _outputWriter; private TextWriter _errorWriter; private Encoding _inputEncoding; public Stream InputStream { get { InitializeInput(); return _inputStream; } } public Stream OutputStream { get { InitializeOutput(); return _outputStream; } } public Stream ErrorStream { get { InitializeErrorOutput(); return _errorStream; } } public TextReader InputReader { get { InitializeInput(); return _inputReader; } } public TextWriter OutputWriter { get { InitializeOutput(); return _outputWriter; } } public TextWriter ErrorWriter { get { InitializeErrorOutput(); return _errorWriter; } } public Encoding InputEncoding { get { InitializeInput(); return _inputEncoding; } } public Encoding OutputEncoding { get { InitializeOutput(); return _outputWriter.Encoding; } } public Encoding ErrorEncoding { get { InitializeErrorOutput(); return _errorWriter.Encoding; } } internal SharedIO() { } private void InitializeInput() { if (_inputStream != null) { return; } lock (_mutex) { if (_inputStream == null) { _inputStream = ConsoleInputStream.Instance; _inputEncoding = Console.InputEncoding; _inputReader = Console.In; } } } private void InitializeOutput() { if (_outputStream != null) { return; } lock (_mutex) { if (_outputStream == null) { _outputStream = Console.OpenStandardOutput(); _outputWriter = Console.Out; } } } private void InitializeErrorOutput() { if (_errorStream == null) { Stream value = Console.OpenStandardError(); Interlocked.CompareExchange(ref _errorStream, value, null); Interlocked.CompareExchange(ref _errorWriter, Console.Error, null); } } public void SetOutput(Stream stream, TextWriter writer) { lock (_mutex) { _outputStream = stream; _outputWriter = writer; } } public void SetErrorOutput(Stream stream, TextWriter writer) { lock (_mutex) { _errorStream = stream; _errorWriter = writer; } } public void SetInput(Stream stream, TextReader reader, Encoding encoding) { lock (_mutex) { _inputStream = stream; _inputReader = reader; _inputEncoding = encoding; } } public void RedirectToConsole() { lock (_mutex) { _inputEncoding = null; _inputStream = null; _outputStream = null; _errorStream = null; _inputReader = null; _outputWriter = null; _errorWriter = null; } } public Stream GetStream(ConsoleStreamType type) { return type switch { ConsoleStreamType.Input => InputStream, ConsoleStreamType.Output => OutputStream, ConsoleStreamType.ErrorOutput => ErrorStream, _ => throw Error.InvalidStreamType(type), }; } public TextWriter GetWriter(ConsoleStreamType type) { return type switch { ConsoleStreamType.Output => OutputWriter, ConsoleStreamType.ErrorOutput => ErrorWriter, _ => throw Error.InvalidStreamType(type), }; } public Encoding GetEncoding(ConsoleStreamType type) { return type switch { ConsoleStreamType.Input => InputEncoding, ConsoleStreamType.Output => OutputEncoding, ConsoleStreamType.ErrorOutput => ErrorEncoding, _ => throw Error.InvalidStreamType(type), }; } public TextReader GetReader(out Encoding encoding) { lock (_mutex) { TextReader inputReader = InputReader; encoding = InputEncoding; return inputReader; } } public Stream GetStreamProxy(ConsoleStreamType type) { return new StreamProxy(this, type); } }