Skip to content

Commit f533735

Browse files
committed
termios on Linux
1 parent 6b21fbd commit f533735

File tree

2 files changed

+77
-42
lines changed

2 files changed

+77
-42
lines changed

src/core/IronPython.Modules/termios.cs

Lines changed: 72 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
using static IronPython.Modules.PythonIOModule;
1919

20+
2021
[assembly: PythonModule("termios", typeof(IronPython.Modules.PythonTermios), PlatformsAttribute.PlatformFamily.Unix)]
2122
namespace IronPython.Modules;
2223

@@ -73,6 +74,11 @@ public static class PythonTermios {
7374
public static int PARENB => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x1000 : 0x0100; // parity enable
7475
public static int HUPCL => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x4000 : 0x0400; // hang up on last close
7576

77+
[PythonHidden(PlatformID.MacOSX)]
78+
public static int CBAUD => 0x100f; // mask for baud rate
79+
[PythonHidden(PlatformID.MacOSX)]
80+
public static int CBAUDEX => 0x1000; // extra baud speed mask
81+
7682

7783
// lflag
7884
public static uint ECHOKE => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0001u : 0x0800u; // visual erase for line kill
@@ -87,7 +93,7 @@ public static class PythonTermios {
8793
public static uint IEXTEN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0400u : 0x8000u; // enable extended input character processing
8894
public static uint TOSTOP => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0040_0000u : 0x0100u; // stop background jobs from output
8995
public static uint FLUSHO => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0080_0000u : 0x1000u; // output being flushed
90-
public static uint PENDIN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x2000_0000u : 0x2000u; // XXX retype pending input
96+
public static uint PENDIN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x2000_0000u : 0x4000u; // retype pending input
9197
public static uint NOFLSH => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x8000_0000u : 0x0080u; // don't flush after interrupt
9298

9399

@@ -97,28 +103,26 @@ public static class PythonTermios {
97103
public const int TCSAFLUSH = 2; // discard input, flush output, then change
98104

99105

100-
// control characters - enabled by:
101-
public const int VEOF = 0; // ICANON
102-
public const int VEOL = 1; // ICANON
103-
public const int VEOL2 = 2; // ICANON + IEXTEN
104-
public const int VERASE = 3; // ICANON
105-
public const int VWERASE = 4; // ICANON + IEXTEN
106-
public const int VKILL = 5; // ICANON
107-
public const int VREPRINT = 6; // ICANON + IEXTEN
108-
// 7: spare 1
109-
public const int VINTR = 8; // ISIG
110-
public const int VQUIT = 9; // ISIG
111-
public const int VSUSP = 10; // ISIG
112-
public const int VDSUSP = 11; // ISIG + IEXTEN
113-
public const int VSTART = 12; // IXON, IXOFF
114-
public const int VSTOP = 13; // IXON, IXOFF
115-
public const int VLNEXT = 14; // IEXTEN
116-
public const int VDISCARD = 15; // IEXTEN
117-
public const int VMIN = 16; // !ICANON
118-
public const int VTIME = 17; // !ICANON
119-
public const int VSTATUS = 18; // ICANON + IEXTEN
120-
// 19: spare 2
121-
public const int NCCS = 20; // size of the character array (lowest common value, Linux has more)
106+
// control characters
107+
public static int VEOF => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0 : 4;
108+
public static int VEOL => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1 : 11;
109+
public static int VEOL2 => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 2 : 16;
110+
public static int VERASE => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 3 : 2;
111+
public static int VWERASE => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 4 : 14;
112+
public static int VKILL => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 5 : 3;
113+
public static int VREPRINT => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 6 : 12;
114+
public static int VINTR => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 8 : 0;
115+
public static int VQUIT => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 9 : 1;
116+
public static int VSUSP => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 10 : 10;
117+
public static int VSTART => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 12 : 8;
118+
public static int VSTOP => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 13 : 9;
119+
public static int VLNEXT => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 14 : 15;
120+
public static int VDISCARD => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 15 : 13;
121+
public static int VMIN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 16 : 6;
122+
public static int VTIME => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 17 : 5;
123+
[PythonHidden(PlatformID.MacOSX)]
124+
public static int VSWTC => 7;
125+
public static int NCCS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 20 : 32;
122126

123127

124128
// tcflush() uses these
@@ -149,19 +153,21 @@ public static object tcgetattr(CodeContext context, int fd) {
149153
throw new NotImplementedException("termios support only for stdin connected to tty");
150154
}
151155

152-
var cc = new PythonList(_specialChars.Length);
153-
for (int i = 0; i < _specialChars.Length; i++) {
154-
cc.Add(Bytes.FromByte(_specialChars[i]));
156+
var cc = new PythonList(NCCS);
157+
var specialChars = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? macos__specialChars : linux__specialChars;
158+
for (int i = 0; i < NCCS; i++) {
159+
byte c = i < specialChars.Length ? specialChars[i] : (byte)0;
160+
cc.Add(Bytes.FromByte(c));
155161
}
156-
return PythonList.FromArrayNoCopy(new object[] {
162+
return PythonList.FromArrayNoCopy([
157163
_iflag,
158164
_oflag,
159165
_cflag,
160166
_lflag,
161167
_ispeed,
162168
_ospeed,
163169
cc
164-
});
170+
]);
165171
}
166172

167173
public static object tcgetattr(CodeContext context, object? file) {
@@ -195,10 +201,11 @@ public static void tcsetattr(CodeContext context, int fd, int when, object? attr
195201
_ => throw PythonOps.TypeErrorForBadInstance("tcsetattr: an integer is required (got type {0})", attrs[LFlagIdx])
196202
};
197203

198-
if (attrs[SpecialCharsIdx] is not IList chars || chars.Count != _specialChars.Length) {
199-
throw PythonOps.TypeError("tcsetattr, atributes[{0}] must be {1} element list", SpecialCharsIdx, _specialChars.Length);
204+
if (attrs[SpecialCharsIdx] is not IList chars || chars.Count != NCCS) {
205+
throw PythonOps.TypeError("tcsetattr, atributes[{0}] must be {1} element list", SpecialCharsIdx, NCCS);
200206
}
201207

208+
var specialChars = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? macos__specialChars : linux__specialChars;
202209
for (int i = 0; i < chars.Count; i++) {
203210
object? o = chars[i];
204211
int newVal;
@@ -207,7 +214,8 @@ public static void tcsetattr(CodeContext context, int fd, int when, object? attr
207214
} else if (!Converter.TryConvertToInt32(o, out newVal)) {
208215
throw PythonOps.TypeError("tcsetattr: elements of attributes must be characters or integers");
209216
}
210-
if (newVal != _specialChars[i]) {
217+
int expected = i < specialChars.Length ? specialChars[i] : 0;
218+
if (newVal != expected) {
211219
throw new NotImplementedException("tcsetattr: setting special characters is not supported");
212220
}
213221
}
@@ -244,12 +252,16 @@ public static void tcsetattr(CodeContext context, object? file, int when, [NotNo
244252

245253
private static int _iflag => BRKINT | ICRNL | IXON | IXANY | IMAXBEL | IUTF8;
246254
private static int _oflag => OPOST | ONLCR;
247-
private static int _cflag => CS8 | CREAD | HUPCL;
248-
private static uint _lflag => ECHOKE | ECHOE | ECHOK | ECHO | ECHOCTL | ISIG | ICANON | IEXTEN | PENDIN;
249-
private static int _ispeed = 38400;
250-
private static int _ospeed = 38400;
251-
252-
private static byte[] _specialChars = new byte[NCCS] {
255+
private static int _cflag => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
256+
CS8 | CREAD | HUPCL
257+
: CS8 | CREAD | HUPCL | (CBAUD & ~CBAUDEX);
258+
private static uint _lflag => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
259+
ECHOKE | ECHOE | ECHOK | ECHO | ECHOCTL | ISIG | ICANON | IEXTEN | PENDIN
260+
: ECHOKE | ECHOE | ECHOK | ECHO | ECHOCTL | ISIG | ICANON | IEXTEN;
261+
private static int _ispeed => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 38400 : /* B38400 */ 15;
262+
private static int _ospeed => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 38400 : /* B38400 */ 15;
263+
264+
private static readonly byte[] macos__specialChars = [
253265
(byte)0x04, // VEOF ^D
254266
(byte)0xff, // VEOL
255267
(byte)0xff, // VEOL2
@@ -270,7 +282,29 @@ public static void tcsetattr(CodeContext context, object? file, int when, [NotNo
270282
(byte)0x00, // VTIME
271283
(byte)0x14, // VSTATUS ^T
272284
(byte)0x00, // reserved
273-
};
285+
];
286+
287+
private static readonly byte[] linux__specialChars = [
288+
(byte)0x03, // VINTR ^C
289+
(byte)0x1c, // VQUIT ^\
290+
(byte)0x7f, // VERASE DEL
291+
(byte)0x15, // VKILL ^U
292+
(byte)0x04, // VEOF ^D
293+
(byte)0x00, // VTIME
294+
(byte)0x01, // VMIN
295+
(byte)0x00, // VSWTC
296+
(byte)0x11, // VSTART ^Q
297+
(byte)0x13, // VSTOP ^S
298+
(byte)0x1a, // VSUSP ^Z
299+
(byte)0xff, // VEOL
300+
(byte)0x12, // VREPRINT ^R
301+
(byte)0x0f, // VDISCARD ^O
302+
(byte)0x17, // VWERASE ^W
303+
(byte)0x16, // VLNEXT ^V
304+
(byte)0xff, // VEOL2
305+
// rest are reserved
306+
];
307+
274308

275309
private static object? _savedRawStdin;
276310

tests/suite/modules/io_related/test_termios.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ def test_cflags(self):
5252
if is_osx:
5353
cflag_values = [0x300, 0x0, 0x100, 0x200, 0x300, 0X800, 0x1000, 0x4000]
5454
elif is_linux:
55-
cflag_values = [0x30, 0x0, 0x10, 0x20, 0x30, 0x80, 0x0100, 0x400]
55+
cflags += ["CBAUD", "CBAUDEX"]
56+
cflag_values = [0x30, 0x0, 0x10, 0x20, 0x30, 0x80, 0x0100, 0x400, 0x100f, 0x1000]
5657
self.verify_flags(cflags, cflag_values)
5758

5859

@@ -61,7 +62,7 @@ def test_lflags(self):
6162
if is_osx:
6263
lflag_values = [0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x400, 0x400000, 0x800000, 0x20000000, 0x80000000]
6364
elif is_linux:
64-
lflag_values = [0x800, 0x10, 0x20, 0x8, 0x40, 0x400, 0x200, 0x1, 0x2, 0x8000, 0x100, 0x1000, 0x2000, 0x80]
65+
lflag_values = [0x800, 0x10, 0x20, 0x8, 0x40, 0x400, 0x200, 0x1, 0x2, 0x8000, 0x100, 0x1000, 0x4000, 0x80]
6566
self.verify_flags(lflags, lflag_values)
6667

6768

@@ -94,8 +95,8 @@ def test_cc(self):
9495
cc = ["VEOF", "VEOL", "VEOL2", "VERASE", "VWERASE", "VKILL", "VREPRINT", "VINTR", "VQUIT", "VSUSP", "VSTART", "VSTOP", "VLNEXT", "VDISCARD", "VMIN", "VTIME", "NCCS"]
9596
cc_values = [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 14, 15, 16, 17, 20]
9697
elif is_linux:
97-
cc = ["VEOF", "VEOL", "VEOL2", "VERASE", "VWERASE", "VKILL", "VREPRINT", "VINTR", "VQUIT", "VSUSP", "VDSUSP", "VSTART", "VSTOP", "VLNEXT", "VDISCARD", "VMIN", "VTIME", "NCCS"]
98-
cc_values = [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20]
98+
cc = ["VEOF", "VEOL", "VEOL2", "VERASE", "VWERASE", "VKILL", "VREPRINT", "VINTR", "VQUIT", "VSUSP", "VSTART", "VSTOP", "VLNEXT", "VDISCARD", "VMIN", "VTIME", "VSWTC", "NCCS"]
99+
cc_values = [4, 11, 16, 2, 14, 3, 12, 0, 1, 10, 8, 9, 15, 13, 6, 5, 7, 32]
99100
self.verify_flags(cc, cc_values)
100101

101102

0 commit comments

Comments
 (0)