Skip to content

Commit 58152e9

Browse files
authored
Overhaul the termios API. (#615)
Instead of defining `Termios` as a plain alias for the libc `termios` type, define our own `Termios` struct with a libc-compatible layout, so that we can use `bitflags` flags types, and have better overall ergonomics. This encapsulates the `termios` vs. `termios2` difference on Linux, and supports arbitrary I/O speeds on both Linux and BSDs with a consistent interface. This is a little higher-level than rustix typically aims for, but it doesn't incur extra syscalls or even extra `Termios` struct copies, and the things it hides are both awkward and uninteresting, such as the whole termios vs. termios2 split which only happens on Linux, where you have to use termios2 to support arbitrary speeds but you can't use termios2 with `cfmakeraw` etc., and also `c_ispeed`/`c_ospeed` work differently in termios2. And PowerPC lacks termios2 but has a termios that acts like termios2. And MIPS adds `TCSETS` to its `TCSANOW` etc. values.
1 parent 3c69a8d commit 58152e9

File tree

20 files changed

+1905
-1800
lines changed

20 files changed

+1905
-1800
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ once_cell = { version = "1.5.2", optional = true }
3838
# libc backend can be selected via adding `--cfg=rustix_use_libc` to
3939
# `RUSTFLAGS` or enabling the `use-libc` cargo feature.
4040
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "powerpc64", target_arch = "riscv64", target_arch = "mips", target_arch = "mips64", target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies]
41-
linux-raw-sys = { version = "0.4.2", default-features = false, features = ["general", "errno", "ioctl", "no_std"] }
41+
linux-raw-sys = { version = "0.4.3", default-features = false, features = ["general", "errno", "ioctl", "no_std"] }
4242
libc_errno = { package = "errno", version = "0.3.1", default-features = false, optional = true }
4343
libc = { version = "0.2.144", features = ["extra_traits"], optional = true }
4444

@@ -55,7 +55,7 @@ libc = { version = "0.2.144", features = ["extra_traits"] }
5555
# Some syscalls do not have libc wrappers, such as in `io_uring`. For these,
5656
# the libc backend uses the linux-raw-sys ABI and `libc::syscall`.
5757
[target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "powerpc64", target_arch = "riscv64", target_arch = "mips", target_arch = "mips64", target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies]
58-
linux-raw-sys = { version = "0.4.2", default-features = false, features = ["general", "ioctl", "no_std"] }
58+
linux-raw-sys = { version = "0.4.3", default-features = false, features = ["general", "ioctl", "no_std"] }
5959

6060
# For the libc backend on Windows, use the Winsock2 API in windows-sys.
6161
[target.'cfg(windows)'.dependencies.windows-sys]

examples/stdio.rs

Lines changed: 79 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ fn main() -> io::Result<()> {
3636
#[cfg(all(not(windows), feature = "stdio"))]
3737
fn show<Fd: AsFd>(fd: Fd) -> io::Result<()> {
3838
let fd = fd.as_fd();
39-
println!(" - ready: {:?}", rustix::io::ioctl_fionread(fd)?);
39+
println!(" - ready bytes: {:?}", rustix::io::ioctl_fionread(fd)?);
4040

4141
#[cfg(feature = "termios")]
4242
if isatty(fd) {
@@ -58,57 +58,53 @@ fn show<Fd: AsFd>(fd: Fd) -> io::Result<()> {
5858
use rustix::termios::*;
5959
let term = tcgetattr(fd)?;
6060

61-
if let Some(speed) = speed_value(cfgetispeed(&term)) {
62-
println!(" - ispeed: {}", speed);
63-
}
64-
if let Some(speed) = speed_value(cfgetospeed(&term)) {
65-
println!(" - ospeed: {}", speed);
66-
}
61+
println!(" - input_speed: {}", term.input_speed());
62+
println!(" - output_speed: {}", term.output_speed());
6763

6864
print!(" - in flags:");
69-
if (term.c_iflag & IGNBRK) != 0 {
65+
if term.input_modes.contains(InputModes::IGNBRK) {
7066
print!(" IGNBRK");
7167
}
72-
if (term.c_iflag & BRKINT) != 0 {
68+
if term.input_modes.contains(InputModes::BRKINT) {
7369
print!(" BRKINT");
7470
}
75-
if (term.c_iflag & IGNPAR) != 0 {
71+
if term.input_modes.contains(InputModes::IGNPAR) {
7672
print!(" IGNPAR");
7773
}
78-
if (term.c_iflag & PARMRK) != 0 {
74+
if term.input_modes.contains(InputModes::PARMRK) {
7975
print!(" PARMRK");
8076
}
81-
if (term.c_iflag & INPCK) != 0 {
77+
if term.input_modes.contains(InputModes::INPCK) {
8278
print!(" INPCK");
8379
}
84-
if (term.c_iflag & ISTRIP) != 0 {
80+
if term.input_modes.contains(InputModes::ISTRIP) {
8581
print!(" ISTRIP");
8682
}
87-
if (term.c_iflag & INLCR) != 0 {
83+
if term.input_modes.contains(InputModes::INLCR) {
8884
print!(" INLCR");
8985
}
90-
if (term.c_iflag & IGNCR) != 0 {
86+
if term.input_modes.contains(InputModes::IGNCR) {
9187
print!(" IGNCR");
9288
}
93-
if (term.c_iflag & ICRNL) != 0 {
89+
if term.input_modes.contains(InputModes::ICRNL) {
9490
print!(" ICRNL");
9591
}
96-
#[cfg(any(linux_raw, all(libc, any(solarish, target_os = "haiku"))))]
97-
if (term.c_iflag & IUCLC) != 0 {
92+
#[cfg(any(linux_kernel, solarish, target_os = "haiku"))]
93+
if term.input_modes.contains(InputModes::IUCLC) {
9894
print!(" IUCLC");
9995
}
100-
if (term.c_iflag & IXON) != 0 {
96+
if term.input_modes.contains(InputModes::IXON) {
10197
print!(" IXON");
10298
}
10399
#[cfg(not(target_os = "redox"))]
104-
if (term.c_iflag & IXANY) != 0 {
100+
if term.input_modes.contains(InputModes::IXANY) {
105101
print!(" IXANY");
106102
}
107-
if (term.c_iflag & IXOFF) != 0 {
103+
if term.input_modes.contains(InputModes::IXOFF) {
108104
print!(" IXOFF");
109105
}
110106
#[cfg(not(any(target_os = "haiku", target_os = "redox")))]
111-
if (term.c_iflag & IMAXBEL) != 0 {
107+
if term.input_modes.contains(InputModes::IMAXBEL) {
112108
print!(" IMAXBEL");
113109
}
114110
#[cfg(not(any(
@@ -120,13 +116,13 @@ fn show<Fd: AsFd>(fd: Fd) -> io::Result<()> {
120116
target_os = "haiku",
121117
target_os = "redox",
122118
)))]
123-
if (term.c_iflag & IUTF8) != 0 {
119+
if term.input_modes.contains(InputModes::IUTF8) {
124120
print!(" IUTF8");
125121
}
126122
println!();
127123

128124
print!(" - out flags:");
129-
if (term.c_oflag & OPOST) != 0 {
125+
if term.output_modes.contains(OutputModes::OPOST) {
130126
print!(" OPOST");
131127
}
132128
#[cfg(not(any(
@@ -136,200 +132,180 @@ fn show<Fd: AsFd>(fd: Fd) -> io::Result<()> {
136132
target_os = "netbsd",
137133
target_os = "redox"
138134
)))]
139-
if (term.c_oflag & OLCUC) != 0 {
135+
if term.output_modes.contains(OutputModes::OLCUC) {
140136
print!(" OLCUC");
141137
}
142-
if (term.c_oflag & ONLCR) != 0 {
138+
if term.output_modes.contains(OutputModes::ONLCR) {
143139
print!(" ONLCR");
144140
}
145-
if (term.c_oflag & OCRNL) != 0 {
141+
if term.output_modes.contains(OutputModes::OCRNL) {
146142
print!(" OCRNL");
147143
}
148-
if (term.c_oflag & ONOCR) != 0 {
144+
if term.output_modes.contains(OutputModes::ONOCR) {
149145
print!(" ONOCR");
150146
}
151-
if (term.c_oflag & ONLRET) != 0 {
147+
if term.output_modes.contains(OutputModes::ONLRET) {
152148
print!(" ONLRET");
153149
}
154150
#[cfg(not(bsd))]
155-
if (term.c_oflag & OFILL) != 0 {
151+
if term.output_modes.contains(OutputModes::OFILL) {
156152
print!(" OFILL");
157153
}
158154
#[cfg(not(bsd))]
159-
if (term.c_oflag & OFDEL) != 0 {
155+
if term.output_modes.contains(OutputModes::OFDEL) {
160156
print!(" OFDEL");
161157
}
162158
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
163-
if (term.c_oflag & NLDLY) != 0 {
159+
if term.output_modes.contains(OutputModes::NLDLY) {
164160
print!(" NLDLY");
165161
}
166162
#[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "redox")))]
167-
if (term.c_oflag & CRDLY) != 0 {
163+
if term.output_modes.contains(OutputModes::CRDLY) {
168164
print!(" CRDLY");
169165
}
170166
#[cfg(not(any(netbsdlike, solarish, target_os = "dragonfly", target_os = "redox")))]
171-
if (term.c_oflag & TABDLY) != 0 {
167+
if term.output_modes.contains(OutputModes::TABDLY) {
172168
print!(" TABDLY");
173169
}
174170
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
175-
if (term.c_oflag & BSDLY) != 0 {
171+
if term.output_modes.contains(OutputModes::BSDLY) {
176172
print!(" BSDLY");
177173
}
178-
#[cfg(not(any(all(libc, target_env = "musl"), bsd, solarish, target_os = "redox")))]
179-
if (term.c_oflag & VTDLY) != 0 {
174+
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
175+
if term.output_modes.contains(OutputModes::VTDLY) {
180176
print!(" VTDLY");
181177
}
182-
#[cfg(not(any(all(libc, target_env = "musl"), bsd, solarish, target_os = "redox")))]
183-
if (term.c_oflag & FFDLY) != 0 {
178+
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
179+
if term.output_modes.contains(OutputModes::FFDLY) {
184180
print!(" FFDLY");
185181
}
186182
println!();
187183

188184
print!(" - control flags:");
189-
#[cfg(not(any(bsd, target_os = "haiku", target_os = "redox")))]
190-
if (term.c_cflag & CBAUD) != 0 {
191-
print!(" CBAUD");
192-
}
193-
#[cfg(not(any(
194-
bsd,
195-
solarish,
196-
target_os = "aix",
197-
target_os = "haiku",
198-
target_os = "redox"
199-
)))]
200-
if (term.c_cflag & CBAUDEX) != 0 {
201-
print!(" CBAUDEX");
202-
}
203-
if (term.c_cflag & CSIZE) != 0 {
185+
if term.control_modes.contains(ControlModes::CSIZE) {
204186
print!(" CSIZE");
205187
}
206-
if (term.c_cflag & CSTOPB) != 0 {
188+
if term.control_modes.contains(ControlModes::CSTOPB) {
207189
print!(" CSTOPB");
208190
}
209-
if (term.c_cflag & CREAD) != 0 {
191+
if term.control_modes.contains(ControlModes::CREAD) {
210192
print!(" CREAD");
211193
}
212-
if (term.c_cflag & PARENB) != 0 {
194+
if term.control_modes.contains(ControlModes::PARENB) {
213195
print!(" PARENB");
214196
}
215-
if (term.c_cflag & PARODD) != 0 {
197+
if term.control_modes.contains(ControlModes::PARODD) {
216198
print!(" PARODD");
217199
}
218-
if (term.c_cflag & HUPCL) != 0 {
200+
if term.control_modes.contains(ControlModes::HUPCL) {
219201
print!(" HUPCL");
220202
}
221-
if (term.c_cflag & CLOCAL) != 0 {
203+
if term.control_modes.contains(ControlModes::CLOCAL) {
222204
print!(" CLOCAL");
223205
}
224-
#[cfg(not(any(
225-
bsd,
226-
target_os = "emscripten",
227-
target_os = "haiku",
228-
target_os = "redox",
229-
)))]
230-
if (term.c_cflag & CIBAUD) != 0 {
231-
print!(" CIBAUD");
232-
}
233206
#[cfg(not(any(
234207
bsd,
235208
solarish,
236209
target_os = "emscripten",
237210
target_os = "haiku",
238211
target_os = "redox",
239212
)))]
240-
if (term.c_cflag & CMSPAR) != 0 {
213+
if term.control_modes.contains(ControlModes::CMSPAR) {
241214
print!(" CMSPAR");
242215
}
243216
#[cfg(not(any(target_os = "aix", target_os = "redox")))]
244-
if (term.c_cflag & CRTSCTS) != 0 {
217+
if term.control_modes.contains(ControlModes::CRTSCTS) {
245218
print!(" CRTSCTS");
246219
}
247220
println!();
248221

249222
print!(" - local flags:");
250-
if (term.c_lflag & ISIG) != 0 {
223+
if term.local_modes.contains(LocalModes::ISIG) {
251224
print!(" ISIG");
252225
}
253-
if (term.c_lflag & ICANON) != 0 {
226+
if term.local_modes.contains(LocalModes::ICANON) {
254227
print!(" ICANON");
255228
}
256-
#[cfg(any(linux_raw, all(libc, any(target_arch = "s390x", target_os = "haiku"))))]
257-
if (term.c_lflag & XCASE) != 0 {
229+
#[cfg(any(linux_kernel, target_arch = "s390x", target_os = "haiku"))]
230+
if term.local_modes.contains(LocalModes::XCASE) {
258231
print!(" XCASE");
259232
}
260-
if (term.c_lflag & ECHO) != 0 {
233+
if term.local_modes.contains(LocalModes::ECHO) {
261234
print!(" ECHO");
262235
}
263-
if (term.c_lflag & ECHOE) != 0 {
236+
if term.local_modes.contains(LocalModes::ECHOE) {
264237
print!(" ECHOE");
265238
}
266-
if (term.c_lflag & ECHOK) != 0 {
239+
if term.local_modes.contains(LocalModes::ECHOK) {
267240
print!(" ECHOK");
268241
}
269-
if (term.c_lflag & ECHONL) != 0 {
242+
if term.local_modes.contains(LocalModes::ECHONL) {
270243
print!(" ECHONL");
271244
}
272245
#[cfg(not(any(target_os = "redox")))]
273-
if (term.c_lflag & ECHOCTL) != 0 {
246+
if term.local_modes.contains(LocalModes::ECHOCTL) {
274247
print!(" ECHOCTL");
275248
}
276249
#[cfg(not(any(target_os = "redox")))]
277-
if (term.c_lflag & ECHOPRT) != 0 {
250+
if term.local_modes.contains(LocalModes::ECHOPRT) {
278251
print!(" ECHOPRT");
279252
}
280253
#[cfg(not(any(target_os = "redox")))]
281-
if (term.c_lflag & ECHOKE) != 0 {
254+
if term.local_modes.contains(LocalModes::ECHOKE) {
282255
print!(" ECHOKE");
283256
}
284257
#[cfg(not(any(target_os = "redox")))]
285-
if (term.c_lflag & FLUSHO) != 0 {
258+
if term.local_modes.contains(LocalModes::FLUSHO) {
286259
print!(" FLUSHO");
287260
}
288-
if (term.c_lflag & NOFLSH) != 0 {
261+
if term.local_modes.contains(LocalModes::NOFLSH) {
289262
print!(" NOFLSH");
290263
}
291-
if (term.c_lflag & TOSTOP) != 0 {
264+
if term.local_modes.contains(LocalModes::TOSTOP) {
292265
print!(" TOSTOP");
293266
}
294267
#[cfg(not(any(target_os = "redox")))]
295-
if (term.c_lflag & PENDIN) != 0 {
268+
if term.local_modes.contains(LocalModes::PENDIN) {
296269
print!(" PENDIN");
297270
}
298-
if (term.c_lflag & IEXTEN) != 0 {
271+
if term.local_modes.contains(LocalModes::IEXTEN) {
299272
print!(" IEXTEN");
300273
}
301274
println!();
302275

303276
println!(
304277
" - keys: INTR={} QUIT={} ERASE={} KILL={} EOF={} TIME={} MIN={} ",
305-
key(term.c_cc[VINTR]),
306-
key(term.c_cc[VQUIT]),
307-
key(term.c_cc[VERASE]),
308-
key(term.c_cc[VKILL]),
309-
key(term.c_cc[VEOF]),
310-
term.c_cc[VTIME],
311-
term.c_cc[VMIN]
278+
key(term.special_codes[SpecialCodeIndex::VINTR]),
279+
key(term.special_codes[SpecialCodeIndex::VQUIT]),
280+
key(term.special_codes[SpecialCodeIndex::VERASE]),
281+
key(term.special_codes[SpecialCodeIndex::VKILL]),
282+
key(term.special_codes[SpecialCodeIndex::VEOF]),
283+
term.special_codes[SpecialCodeIndex::VTIME],
284+
term.special_codes[SpecialCodeIndex::VMIN]
312285
);
313286
println!(
314287
" START={} STOP={} SUSP={} EOL={}",
315-
key(term.c_cc[VSTART]),
316-
key(term.c_cc[VSTOP]),
317-
key(term.c_cc[VSUSP]),
318-
key(term.c_cc[VEOL]),
288+
key(term.special_codes[SpecialCodeIndex::VSTART]),
289+
key(term.special_codes[SpecialCodeIndex::VSTOP]),
290+
key(term.special_codes[SpecialCodeIndex::VSUSP]),
291+
key(term.special_codes[SpecialCodeIndex::VEOL]),
319292
);
320293
#[cfg(not(target_os = "haiku"))]
321294
println!(
322295
" REPRINT={} DISCARD={}",
323-
key(term.c_cc[VREPRINT]),
324-
key(term.c_cc[VDISCARD])
296+
key(term.special_codes[SpecialCodeIndex::VREPRINT]),
297+
key(term.special_codes[SpecialCodeIndex::VDISCARD])
325298
);
326299
#[cfg(not(target_os = "haiku"))]
327300
println!(
328301
" WERASE={} VLNEXT={}",
329-
key(term.c_cc[VWERASE]),
330-
key(term.c_cc[VLNEXT]),
302+
key(term.special_codes[SpecialCodeIndex::VWERASE]),
303+
key(term.special_codes[SpecialCodeIndex::VLNEXT]),
304+
);
305+
println!(
306+
" EOL2={}",
307+
key(term.special_codes[SpecialCodeIndex::VEOL2])
331308
);
332-
println!(" EOL2={}", key(term.c_cc[VEOL2]));
333309
}
334310
} else {
335311
println!(" - is not a tty");

0 commit comments

Comments
 (0)