|
| 1 | +// Licensed to the .NET Foundation under one or more agreements. |
| 2 | +// The .NET Foundation licenses this file to you under the Apache 2.0 License. |
| 3 | +// See the LICENSE file in the project root for more information. |
| 4 | + |
| 5 | +#nullable enable |
| 6 | + |
| 7 | +using System; |
| 8 | +using System.Collections; |
| 9 | +using System.Numerics; |
| 10 | +using System.Runtime.CompilerServices; |
| 11 | +using System.Runtime.InteropServices; |
| 12 | +using System.Runtime.Versioning; |
| 13 | + |
| 14 | +using Microsoft.Scripting.Runtime; |
| 15 | + |
| 16 | +using IronPython.Runtime; |
| 17 | +using IronPython.Runtime.Operations; |
| 18 | + |
| 19 | +using static IronPython.Modules.PythonIOModule; |
| 20 | + |
| 21 | + |
| 22 | +[assembly: PythonModule("termios", typeof(IronPython.Modules.PythonTermios), PlatformsAttribute.PlatformFamily.Unix)] |
| 23 | +namespace IronPython.Modules; |
| 24 | + |
| 25 | +[SupportedOSPlatform("linux")] |
| 26 | +[SupportedOSPlatform("macos")] |
| 27 | +public static class PythonTermios { |
| 28 | + |
| 29 | + public const string __doc__ = "Stub of termios, just enough to support module tty."; |
| 30 | + // and also prompt_toolkit.terminal.vt100_input |
| 31 | + |
| 32 | +#pragma warning disable IPY01 // Parameter which is marked not nullable does not have the NotNullAttribute |
| 33 | + [SpecialName] |
| 34 | + public static void PerformModuleReload(PythonContext context, PythonDictionary dict) |
| 35 | + => context.EnsureModuleException("termioserror", dict, "error", "termios"); |
| 36 | +#pragma warning restore IPY01 // Parameter which is marked not nullable does not have the NotNullAttribute |
| 37 | + |
| 38 | + |
| 39 | + #region termios IO Control Codes (TIOC*) |
| 40 | + |
| 41 | + public static int TIOCGWINSZ => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x40087468 : 0x5413; |
| 42 | + |
| 43 | + |
| 44 | + #endregion |
| 45 | + |
| 46 | + |
| 47 | + #region Other Public Constants |
| 48 | + // Linux: glibc/bits/termios.h (/usr/include/{x86_64,aarch64}-linux-gnu/) |
| 49 | + // macOS: usr/include/sys/termios.h (/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk) |
| 50 | + |
| 51 | + // iflag |
| 52 | + public const int IGNBRK = 0x0001; // ignore break condition |
| 53 | + public const int BRKINT = 0x0002; // signal interrupt on break |
| 54 | + public const int IGNPAR = 0x0004; // ignore characters with parity errors |
| 55 | + public const int PARMRK = 0x0008; // mark parity and framing errors |
| 56 | + public const int INPCK = 0x0010; // enable input parity check |
| 57 | + public const int ISTRIP = 0x0020; // mask off 8th bit |
| 58 | + public const int INLCR = 0x0040; // map NL into CR on input |
| 59 | + public const int IGNCR = 0x0080; // ignore CR |
| 60 | + public const int ICRNL = 0x0100; // map CR to NL on input |
| 61 | + public static int IXON => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? |
| 62 | + 0x0200 : 0x0400; // enable start output control |
| 63 | + public static int IXOFF => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? |
| 64 | + 0x0400 : 0x1000; // enable stop output control |
| 65 | + public const int IXANY = 0x0800; // any char will restart after stop |
| 66 | + public const int IMAXBEL = 0x2000; // ring bell on input queue full |
| 67 | + public const int IUTF8 = 0x4000; // maintain state for UTF-8 VERASE |
| 68 | + |
| 69 | + |
| 70 | + // oflag |
| 71 | + public const int OPOST = 0x0001; // enable output processing |
| 72 | + public static int ONLCR => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? |
| 73 | + 0x0002 : 0x0004; // map NL to CR-NL |
| 74 | + |
| 75 | + |
| 76 | + // cflag |
| 77 | + public static int CSIZE => CS5 | CS6 | CS7 | CS8; // number of bits per character |
| 78 | + public static int CS5 => 0x0000; // 5 bits per character |
| 79 | + public static int CS6 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0100 : 0x0010; // 6 bits per character |
| 80 | + public static int CS7 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0200 : 0x0020; // 7 bits per character |
| 81 | + public static int CS8 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0300 : 0x0030; // 8 bits per character |
| 82 | + public static int CREAD => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0800 : 0x0080; // enable receiver |
| 83 | + public static int PARENB => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x1000 : 0x0100; // parity enable |
| 84 | + public static int HUPCL => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x4000 : 0x0400; // hang up on last close |
| 85 | + |
| 86 | + [PythonHidden(PlatformID.MacOSX)] |
| 87 | + public static int CBAUD => 0x100f; // mask for baud rate |
| 88 | + [PythonHidden(PlatformID.MacOSX)] |
| 89 | + public static int CBAUDEX => 0x1000; // extra baud speed mask |
| 90 | + |
| 91 | + |
| 92 | + // lflag |
| 93 | + public static uint ECHOKE => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0001u : 0x0800u; // visual erase for line kill |
| 94 | + public static uint ECHOE => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0002u : 0x0010u; // visually erase chars |
| 95 | + public static uint ECHOK => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0004u : 0x0020u; // echo NL after line kill |
| 96 | + public static uint ECHO => 0x0008u; // enable echoing of input characters |
| 97 | + public static uint ECHONL => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0010u : 0x0040u; // echo NL even if ECHO is off |
| 98 | + public static uint ECHOPRT => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0020u : 0x0400u; // visual erase mode for hardcopy |
| 99 | + public static uint ECHOCTL => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0040u : 0x0200u; // echo control characters as ^(Char) |
| 100 | + public static uint ISIG => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0080u : 0x0001u; // enable signals |
| 101 | + public static uint ICANON => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0100u : 0x0002u; // canonical mode |
| 102 | + public static uint IEXTEN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0400u : 0x8000u; // enable extended input character processing |
| 103 | + public static uint TOSTOP => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0040_0000u : 0x0100u; // stop background jobs from output |
| 104 | + public static uint FLUSHO => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0080_0000u : 0x1000u; // output being flushed |
| 105 | + public static uint PENDIN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x2000_0000u : 0x4000u; // retype pending input |
| 106 | + public static uint NOFLSH => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x8000_0000u : 0x0080u; // don't flush after interrupt |
| 107 | + |
| 108 | + |
| 109 | + // when the changes take effect: |
| 110 | + public const int TCSANOW = 0; // change immediately |
| 111 | + public const int TCSADRAIN = 1; // flush output, then change |
| 112 | + public const int TCSAFLUSH = 2; // discard input, flush output, then change |
| 113 | + |
| 114 | + |
| 115 | + // control characters |
| 116 | + public static int VEOF => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0 : 4; |
| 117 | + public static int VEOL => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1 : 11; |
| 118 | + public static int VEOL2 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 2 : 16; |
| 119 | + public static int VERASE => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 3 : 2; |
| 120 | + public static int VWERASE => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 4 : 14; |
| 121 | + public static int VKILL => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 5 : 3; |
| 122 | + public static int VREPRINT => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 6 : 12; |
| 123 | + public static int VINTR => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 8 : 0; |
| 124 | + public static int VQUIT => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 9 : 1; |
| 125 | + public static int VSUSP => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 10 : 10; |
| 126 | + public static int VSTART => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 12 : 8; |
| 127 | + public static int VSTOP => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 13 : 9; |
| 128 | + public static int VLNEXT => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 14 : 15; |
| 129 | + public static int VDISCARD => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 15 : 13; |
| 130 | + public static int VMIN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 16 : 6; |
| 131 | + public static int VTIME => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 17 : 5; |
| 132 | + [PythonHidden(PlatformID.MacOSX)] |
| 133 | + public static int VSWTC => 7; |
| 134 | + public static int NCCS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 20 : 32; |
| 135 | + |
| 136 | + |
| 137 | + // tcflush() uses these |
| 138 | + public static int TCIFLUSH => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1 : 0; // flush input |
| 139 | + public static int TCOFLUSH => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 2 : 1; // flush output |
| 140 | + public static int TCIOFLUSH => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 3 : 2; // flush both |
| 141 | + |
| 142 | + |
| 143 | + // tcflow() uses these |
| 144 | + public static int TCOOFF => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1 : 0; // suspend output |
| 145 | + public static int TCOON => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 2 : 1; // restart output |
| 146 | + public static int TCIOFF => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 3 : 2; // transmit STOP character |
| 147 | + public static int TCION => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 4 : 3; // transmit START character |
| 148 | + |
| 149 | + // baud rates |
| 150 | + public static int B0 => 0; |
| 151 | + public static int B50 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 50 : 1; |
| 152 | + public static int B75 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 75 : 2; |
| 153 | + public static int B110 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 110 : 3; |
| 154 | + public static int B134 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 134 : 4; |
| 155 | + public static int B150 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 150 : 5; |
| 156 | + public static int B200 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 200 : 6; |
| 157 | + public static int B300 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 300 : 7; |
| 158 | + public static int B600 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 600 : 8; |
| 159 | + public static int B1200 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1200 : 9; |
| 160 | + public static int B1800 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1800 : 10; |
| 161 | + public static int B2400 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 2400 : 11; |
| 162 | + public static int B4800 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 4800 : 12; |
| 163 | + public static int B9600 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 9600 : 13; |
| 164 | + public static int B19200 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 19200 : 14; |
| 165 | + public static int B38400 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 38400 : 15; |
| 166 | + public static int B57600 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 57600 : 0x1001; |
| 167 | + public static int B115200 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 115200 : 0x1002; |
| 168 | + public static int B230400 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 230400 : 0x1003; |
| 169 | + // higher baud rates are not defined on macOS |
| 170 | + |
| 171 | + public static object tcgetattr(CodeContext context, int fd) { |
| 172 | + if (fd < 0) throw PythonOps.ValueError("file descriptor cannot be a negative integer ({0})", fd); |
| 173 | + if (fd > 0) throw new NotImplementedException("termios support only for stdin"); |
| 174 | + |
| 175 | + if (context.LanguageContext.SystemStandardIn is not TextIOWrapper stdin) { |
| 176 | + throw new NotImplementedException("termios support only for stdin"); |
| 177 | + } |
| 178 | + if (stdin.closed || !stdin.isatty(context) || stdin.fileno(context) != 0 || Console.IsInputRedirected) { |
| 179 | + throw new NotImplementedException("termios support only for stdin connected to tty"); |
| 180 | + } |
| 181 | + |
| 182 | + var cc = new PythonList(NCCS); |
| 183 | + var specialChars = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? macos__specialChars : linux__specialChars; |
| 184 | + for (int i = 0; i < NCCS; i++) { |
| 185 | + byte c = i < specialChars.Length ? specialChars[i] : (byte)0; |
| 186 | + cc.Add(Bytes.FromByte(c)); |
| 187 | + } |
| 188 | + return PythonList.FromArrayNoCopy([ |
| 189 | + _iflag, |
| 190 | + _oflag, |
| 191 | + _cflag, |
| 192 | + _lflag, |
| 193 | + _ispeed, |
| 194 | + _ospeed, |
| 195 | + cc |
| 196 | + ]); |
| 197 | + } |
| 198 | + |
| 199 | + public static object tcgetattr(CodeContext context, object? file) { |
| 200 | + if (!ReferenceEquals(file, context.LanguageContext.SystemStandardIn)) { |
| 201 | + throw new NotImplementedException("termios support only for stdin"); |
| 202 | + } |
| 203 | + return tcgetattr(context, 0); |
| 204 | + } |
| 205 | + |
| 206 | + |
| 207 | + public static void tcsetattr(CodeContext context, int fd, int when, object? attributes) { |
| 208 | + if (fd != 0) throw new NotImplementedException(); |
| 209 | + |
| 210 | + if (context.LanguageContext.SystemStandardIn is not TextIOWrapper stdin) { |
| 211 | + throw new NotImplementedException("termios support only for stdin"); |
| 212 | + } |
| 213 | + if (stdin.closed || !stdin.isatty(context) || stdin.fileno(context) != 0 || Console.IsInputRedirected) { |
| 214 | + throw new NotImplementedException("termios support only for stdin connected to tty"); |
| 215 | + } |
| 216 | + |
| 217 | + if (attributes is not IList attrs || attrs.Count != 7) { |
| 218 | + throw PythonOps.TypeError("tcsetattr, arg 3: must be 7 element list"); |
| 219 | + } |
| 220 | + |
| 221 | + uint newLflag = attrs[LFlagIdx] switch { |
| 222 | + int i => (uint)i, |
| 223 | + uint ui => ui, |
| 224 | + long l => (uint)l, |
| 225 | + BigInteger bi => (uint)bi, |
| 226 | + Extensible<BigInteger> ebi => (uint)ebi.Value, |
| 227 | + _ => throw PythonOps.TypeErrorForBadInstance("tcsetattr: an integer is required (got type {0})", attrs[LFlagIdx]) |
| 228 | + }; |
| 229 | + |
| 230 | + if (attrs[SpecialCharsIdx] is not IList chars || chars.Count != NCCS) { |
| 231 | + throw PythonOps.TypeError("tcsetattr, atributes[{0}] must be {1} element list", SpecialCharsIdx, NCCS); |
| 232 | + } |
| 233 | + |
| 234 | + var specialChars = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? macos__specialChars : linux__specialChars; |
| 235 | + for (int i = 0; i < chars.Count; i++) { |
| 236 | + object? o = chars[i]; |
| 237 | + int newVal; |
| 238 | + if (o is Bytes b && b.Count == 1) { |
| 239 | + newVal = b[0]; |
| 240 | + } else if (!Converter.TryConvertToInt32(o, out newVal)) { |
| 241 | + throw PythonOps.TypeError("tcsetattr: elements of attributes must be characters or integers"); |
| 242 | + } |
| 243 | + int expected = i < specialChars.Length ? specialChars[i] : 0; |
| 244 | + if (newVal != expected) { |
| 245 | + throw new NotImplementedException("tcsetattr: setting special characters is not supported"); |
| 246 | + } |
| 247 | + } |
| 248 | + |
| 249 | + if (when != TCSANOW) { |
| 250 | + stdin.flush(context); |
| 251 | + } |
| 252 | + |
| 253 | + if ((newLflag & (ECHO | ICANON | IEXTEN | ISIG)) == 0) { |
| 254 | + setraw(context, stdin); |
| 255 | + } else { |
| 256 | + setcbreak(context, stdin); |
| 257 | + } |
| 258 | + } |
| 259 | + |
| 260 | + |
| 261 | + public static void tcsetattr(CodeContext context, object? file, int when, [NotNone] object attributes) { |
| 262 | + if (!ReferenceEquals(file, context.LanguageContext.SystemStandardIn)) { |
| 263 | + throw new NotImplementedException("termios support only for stdin"); |
| 264 | + } |
| 265 | + tcsetattr(context, 0, when, attributes); |
| 266 | + } |
| 267 | + |
| 268 | + #endregion |
| 269 | + |
| 270 | + |
| 271 | + private const int IFlagIdx = 0; |
| 272 | + private const int OFlagIdx = 1; |
| 273 | + private const int CFlagIdx = 2; |
| 274 | + private const int LFlagIdx = 3; |
| 275 | + private const int ISpeedIdx = 4; |
| 276 | + private const int OSpeedIdx = 5; |
| 277 | + private const int SpecialCharsIdx = 6; |
| 278 | + |
| 279 | + private static int _iflag => BRKINT | ICRNL | IXON | IXANY | IMAXBEL | IUTF8; |
| 280 | + private static int _oflag => OPOST | ONLCR; |
| 281 | + private static int _cflag => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? |
| 282 | + CS8 | CREAD | HUPCL |
| 283 | + : CS8 | CREAD | HUPCL | (CBAUD & ~CBAUDEX); |
| 284 | + private static uint _lflag => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? |
| 285 | + ECHOKE | ECHOE | ECHOK | ECHO | ECHOCTL | ISIG | ICANON | IEXTEN | PENDIN |
| 286 | + : ECHOKE | ECHOE | ECHOK | ECHO | ECHOCTL | ISIG | ICANON | IEXTEN; |
| 287 | + private static int _ispeed => B38400; |
| 288 | + private static int _ospeed => B38400; |
| 289 | + |
| 290 | + private static readonly byte[] macos__specialChars = [ |
| 291 | + (byte)0x04, // VEOF ^D |
| 292 | + (byte)0xff, // VEOL |
| 293 | + (byte)0xff, // VEOL2 |
| 294 | + (byte)0x7f, // VERASE DEL |
| 295 | + (byte)0x17, // VWERASE ^W |
| 296 | + (byte)0x15, // VKILL ^U |
| 297 | + (byte)0x12, // VREPRINT ^R |
| 298 | + (byte)0x00, // reserved |
| 299 | + (byte)0x03, // VINTR ^C |
| 300 | + (byte)0x1c, // VQUIT ^\ |
| 301 | + (byte)0x1a, // VSUSP ^Z |
| 302 | + (byte)0x19, // VDSUSP ^Y |
| 303 | + (byte)0x11, // VSTART ^Q |
| 304 | + (byte)0x13, // VSTOP ^S |
| 305 | + (byte)0x16, // VLNEXT ^V |
| 306 | + (byte)0x0f, // VDISCARD ^O |
| 307 | + (byte)0x01, // VMIN |
| 308 | + (byte)0x00, // VTIME |
| 309 | + (byte)0x14, // VSTATUS ^T |
| 310 | + (byte)0x00, // reserved |
| 311 | + ]; |
| 312 | + |
| 313 | + private static readonly byte[] linux__specialChars = [ |
| 314 | + (byte)0x03, // VINTR ^C |
| 315 | + (byte)0x1c, // VQUIT ^\ |
| 316 | + (byte)0x7f, // VERASE DEL |
| 317 | + (byte)0x15, // VKILL ^U |
| 318 | + (byte)0x04, // VEOF ^D |
| 319 | + (byte)0x00, // VTIME |
| 320 | + (byte)0x01, // VMIN |
| 321 | + (byte)0x00, // VSWTC |
| 322 | + (byte)0x11, // VSTART ^Q |
| 323 | + (byte)0x13, // VSTOP ^S |
| 324 | + (byte)0x1a, // VSUSP ^Z |
| 325 | + (byte)0xff, // VEOL |
| 326 | + (byte)0x12, // VREPRINT ^R |
| 327 | + (byte)0x0f, // VDISCARD ^O |
| 328 | + (byte)0x17, // VWERASE ^W |
| 329 | + (byte)0x16, // VLNEXT ^V |
| 330 | + (byte)0xff, // VEOL2 |
| 331 | + // rest are reserved |
| 332 | + ]; |
| 333 | + |
| 334 | + |
| 335 | + private static object? _savedRawStdin; |
| 336 | + |
| 337 | + private static void setraw(CodeContext context, TextIOWrapper stdin) { |
| 338 | + if (_savedRawStdin is null && stdin.buffer is BufferedReader reader) { |
| 339 | + _savedRawStdin = reader.raw; |
| 340 | + reader.raw = new RawConsole(context); |
| 341 | + } |
| 342 | + } |
| 343 | + |
| 344 | + private static void setcbreak(CodeContext context, TextIOWrapper stdin) { |
| 345 | + if (_savedRawStdin is not null |
| 346 | + && stdin.buffer is BufferedReader reader |
| 347 | + && reader.raw is RawConsole) { |
| 348 | + |
| 349 | + reader.raw = _savedRawStdin; |
| 350 | + _savedRawStdin = null; |
| 351 | + } |
| 352 | + } |
| 353 | + |
| 354 | + private class RawConsole : _RawIOBase { |
| 355 | + public RawConsole(CodeContext context) : base(context) { |
| 356 | + } |
| 357 | + |
| 358 | + public override object? read(CodeContext context, object? size=null) { |
| 359 | + int intSize = size switch { |
| 360 | + null => -1, |
| 361 | + int i => i, |
| 362 | + BigInteger bi => (int)bi, |
| 363 | + Extensible<BigInteger> ebi => (int)ebi.Value, |
| 364 | + _ => throw PythonOps.TypeErrorForBadInstance("integer argument expected, got '{0}'", size) |
| 365 | + }; |
| 366 | + if (intSize == 0) return null; |
| 367 | + |
| 368 | + ConsoleKeyInfo info = Console.ReadKey(intercept: true); |
| 369 | + return Bytes.FromByte(unchecked((byte)info.KeyChar)); |
| 370 | + } |
| 371 | + |
| 372 | + public override int fileno(CodeContext context) => 0; |
| 373 | + public override bool isatty(CodeContext context) => true; |
| 374 | + } |
| 375 | + |
| 376 | + private static int ToInt(this object? o) |
| 377 | + => o switch { |
| 378 | + int i => i, |
| 379 | + BigInteger bi => (int)bi, |
| 380 | + Extensible<BigInteger> ebi => (int)ebi.Value, |
| 381 | + _ => throw PythonOps.TypeErrorForBadInstance("an integer is required (got type {0})", o) |
| 382 | + }; |
| 383 | +} |
0 commit comments