Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/wapython
Path: blob/main/core/kernel/src/wasm/posix/termios.ts
1068 views
1
/*
2
3
RANDOM NOTES
4
5
MAJOR TODO: For xterm.js entirely in browser (and MS Windows), we may still
6
have to implement this stuff. Hopefully this will be much easier, since we
7
implemented everything via our posix-node and can observe what's expected
8
by programs.
9
10
Also, for example, one of the flags is
11
12
"ISIG When any of the characters INTR, QUIT, SUSP, or DSUSP are received, generate the corresponding signal."
13
14
and since we are implementing signals and watching characters, of course this is some
15
logic that we would do.
16
17
On a POSIX server, a complete and easy option is to directly call the c library via
18
an extension module, translating flags back and forth between native and wasi,
19
and we do exactly that here.
20
21
Right now on non-POSIX, the following are partially stub functions, but not minimal.
22
23
IMPORTANT! We can't do NOTHING! For example, libedit will
24
randomly not work if we do nothing (which drove me crazy for days)!
25
This is because of the line
26
27
if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0)
28
29
in packages/libedit/build/wasm/src/readline.c. If t.c_lflag doesn't have the ECHO
30
flag, then libedit will be totally broken for interactive use.
31
We set at least that below for fd=0 and intend to do more (TODO!).
32
33
tcflag_t c_iflag; // input modes
34
tcflag_t c_oflag; // output modes
35
tcflag_t c_cflag; // control modes
36
tcflag_t c_lflag; // local modes
37
cc_t c_cc[NCCS]; /; special characters
38
39
I think for us c_lflag is mostly what matters.
40
41
Another key point that is subtle, is we can't just worry about a subset of "official
42
posix flags" and forget about the rest. We have to see what was changed at the
43
wasi level, then modify exactly what is true natively to match that. This makes
44
the code below a bit odd.
45
*/
46
47
import debug from "debug";
48
import constants from "./constants";
49
50
const log = debug("posix:termios");
51
52
const FLAGS = {
53
c_iflag: [
54
"IGNBRK",
55
"BRKINT",
56
"IGNPAR",
57
"PARMRK",
58
"INPCK",
59
"ISTRIP",
60
"INLCR",
61
"IGNCR",
62
"ICRNL",
63
"IXON",
64
"IXANY",
65
"IXOFF",
66
"IMAXBEL",
67
"IUTF8",
68
],
69
c_oflag: ["OPOST", "ONLCR", "OCRNL", "ONOCR", "ONLRET", "OFILL", "OFDEL"],
70
c_cflag: [
71
"CSIZE",
72
"CS5",
73
"CS6",
74
"CS7",
75
"CS8",
76
"CSTOPB",
77
"CREAD",
78
"PARENB",
79
"PARODD",
80
"HUPCL",
81
"CLOCAL",
82
],
83
c_lflag: [
84
"ISIG",
85
"ICANON",
86
"ECHO",
87
"ECHOE",
88
"ECHOK",
89
"ECHONL",
90
"NOFLSH",
91
"TOSTOP",
92
"IEXTEN",
93
],
94
} as const;
95
96
interface Termios {
97
c_iflag: number;
98
c_oflag: number;
99
c_cflag: number;
100
c_lflag: number;
101
}
102
103
export default function termios({ posix, recv, send, wasi, noStdio }) {
104
function termios_set(tioPtr: number, { c_iflag, c_oflag, c_cflag, c_lflag }) {
105
const size = 4;
106
send.u32(tioPtr, c_iflag ?? 0);
107
send.u32(tioPtr + size, c_oflag ?? 0);
108
send.u32(tioPtr + 2 * size, c_cflag ?? 0);
109
send.u32(tioPtr + 3 * size, c_lflag ?? 0);
110
}
111
112
function termios_get(tioPtr: number): {
113
c_iflag: number;
114
c_oflag: number;
115
c_cflag: number;
116
c_lflag: number;
117
} {
118
const size = 4;
119
return {
120
c_iflag: recv.u32(tioPtr),
121
c_oflag: recv.u32(tioPtr + size),
122
c_cflag: recv.u32(tioPtr + 2 * size),
123
c_lflag: recv.u32(tioPtr + 3 * size),
124
};
125
}
126
127
function native_to_wasi(tio_native: Termios): Termios {
128
// now translate the flags from native to WASI
129
const tio_wasi: Termios = {
130
c_iflag: 0,
131
c_oflag: 0,
132
c_cflag: 0,
133
c_lflag: 0,
134
};
135
let s: string[] = [];
136
for (const key in tio_native) {
137
tio_wasi[key] = 0;
138
for (const name of FLAGS[key]) {
139
if (tio_native[key] & posix.constants[name]) {
140
tio_wasi[key] |= constants[name];
141
if (log.enabled) {
142
s.push(name);
143
}
144
}
145
}
146
}
147
if (log.enabled) {
148
s.sort();
149
log("NATIVE: ", s.join(" "));
150
}
151
return tio_wasi;
152
}
153
154
/*
155
// this doesn't end up getting used, so commented out.
156
function wasi_to_native(tio_wasi: Termios): Termios {
157
const tio_native: Termios = {
158
c_iflag: 0,
159
c_oflag: 0,
160
c_cflag: 0,
161
c_lflag: 0,
162
};
163
// let s: string[] = [];
164
for (const key in FLAGS) {
165
tio_native[key] = 0;
166
for (const name of FLAGS[key]) {
167
if (tio_wasi[key] & constants[name]) {
168
tio_native[key] |= posix.constants[name];
169
// s.push(name);
170
}
171
}
172
}
173
// s.sort();
174
//console.log(s.join(" "));
175
//require("fs").appendFileSync("/tmp/log", s + "\n");
176
177
return tio_native;
178
}
179
*/
180
181
return {
182
/*
183
These two functions are critical to applications that use
184
the terminal. They do a huge amount in a traditional POSIX system,
185
e.g., setting baud rates, ICANON mode, echo, etc.
186
187
I think xterm.js is orthogonal to this; it just reflects how the
188
underlying terminal behaves.
189
190
int
191
tcgetattr(int fildes, struct termios *termios_p);
192
193
int
194
tcsetattr(int fildes, int optional_actions,
195
const struct termios *termios_p);
196
197
*/
198
tcgetattr(wasi_fd: number, tioPtr: number): number {
199
const fd = wasi.FD_MAP.get(wasi_fd).real;
200
let tio_wasi: Termios;
201
let tio_native: Termios;
202
if (!noStdio && posix.tcgetattr != null) {
203
tio_native = posix.tcgetattr(fd);
204
// now translate the flags from native to WASI
205
tio_wasi = native_to_wasi(tio_native);
206
} else {
207
tio_native = {} as any; // just for logging below
208
if (fd == 0 || fd == 1) {
209
// I copied this from running it and observing.
210
// NO MATTER what we must c_lflag: constants.ECHO as below, though maybe
211
// having everything is better.
212
tio_wasi = {
213
c_iflag: 27906,
214
c_oflag: 5,
215
c_cflag: 1200,
216
c_lflag: 32827,
217
};
218
// // stdin - do something to avoid total disaster (see comment in header)
219
// tio_wasi = {
220
// c_iflag: 0,
221
// c_oflag: 0,
222
// c_cflag: 0,
223
// c_lflag: constants.ECHO, // at least this is needed or nothing will work.
224
// };
225
} else {
226
tio_wasi = {
227
c_iflag: 0,
228
c_oflag: 0,
229
c_cflag: 0,
230
c_lflag: 0,
231
};
232
}
233
}
234
//console.log("tcgetattr", { wasi_fd, fd, tio_wasi, tio_native });
235
//console.log("GET");
236
//wasi_to_native(tio_wasi);
237
termios_set(tioPtr, tio_wasi);
238
return 0;
239
},
240
241
tcsetattr(
242
wasi_fd: number,
243
_optional_actions: number, // ignored (involves buffering and when change happens)
244
tioPtr: number
245
): number {
246
const fd = wasi.FD_MAP.get(wasi_fd).real;
247
const tio_wasi = termios_get(tioPtr);
248
if (noStdio || posix.tcsetattr == null || posix.tcgetattr == null) {
249
return 0;
250
}
251
const tio_native = posix.tcgetattr(fd);
252
const tio_native_orig = { ...tio_native };
253
const tio_wasi_current = native_to_wasi(tio_native);
254
255
// We change in native **exactly** what they changed, leaving everything
256
// else the same.
257
let somethingChanged = false;
258
for (const key in FLAGS) {
259
for (const name of FLAGS[key]) {
260
if (
261
(tio_wasi[key] & constants[name]) !=
262
(tio_wasi_current[key] & constants[name])
263
) {
264
// name was changed
265
somethingChanged = true;
266
if (tio_wasi[key] & constants[name]) {
267
// set it
268
tio_native[key] |= posix.constants[name];
269
} else {
270
// unset it
271
tio_native[key] &= ~posix.constants[name];
272
}
273
}
274
}
275
}
276
if (!somethingChanged) {
277
log("tcsetattr: nothing changed");
278
return 0;
279
}
280
log("tcsetattr", { fd, tio_native, tio_native_orig });
281
posix.tcsetattr(fd, posix.constants.TCSANOW, tio_native);
282
return 0;
283
},
284
285
// These are stubs for now:
286
287
// int tcdrain(int fildes);
288
tcdrain(): number {
289
log("tcdrain - STUB");
290
return 0;
291
},
292
// int tcflow(int fildes, int action);
293
tcflow(): number {
294
log("tcflow - STUB");
295
return 0;
296
},
297
// int tcflush(int fildes, int action);
298
tcflush(): number {
299
log("tcflush - STUB");
300
return 0;
301
},
302
// int tcsendbreak(int fildes, int duration);
303
tcsendbreak(): number {
304
log("tcsendbreak - STUB");
305
return 0;
306
},
307
};
308
}
309
310