nix/sys/
termios.rs

1//! An interface for controlling asynchronous communication ports
2//!
3//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
4//! underlying types are all implemented in libc for most platforms and either wrapped in safer
5//! types here or exported directly.
6//!
7//! If you are unfamiliar with the `termios` API, you should first read the
8//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
9//! then come back to understand how `nix` safely wraps it.
10//!
11//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
12//! As this interface is not used with high-bandwidth information, this should be fine in most
13//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
14//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
15//! This means that when crossing the FFI interface to the underlying C library, data is first
16//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
17//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
18//! relatively small across all platforms (on the order of 32-64 bytes).
19//!
20//! The following examples highlight some of the API use cases such that users coming from using C
21//! or reading the standard documentation will understand how to use the safe API exposed here.
22//!
23//! Example disabling processing of the end-of-file control character:
24//!
25//! ```
26//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
27//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
28//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
29//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
30//! ```
31//!
32//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
33//! an interface for working with bitfields that is similar to working with the raw unsigned
34//! integer types but offers type safety because of the internal checking that values will always
35//! be a valid combination of the defined flags.
36//!
37//! An example showing some of the basic operations for interacting with the control flags:
38//!
39//! ```
40//! # use self::nix::sys::termios::{ControlFlags, Termios};
41//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
42//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5;
43//! termios.control_flags |= ControlFlags::CS5;
44//! ```
45//!
46//! # Baud rates
47//!
48//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both
49//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs
50//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer
51//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following
52//! conventions:
53//!
54//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
55//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
56//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
57//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
58//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
59//!
60//! The most common use case of specifying a baud rate using the enum will work the same across
61//! platforms:
62//!
63//! ```rust
64//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
65//! # fn main() {
66//! # let mut t: Termios = unsafe { std::mem::zeroed() };
67//! cfsetispeed(&mut t, BaudRate::B9600);
68//! cfsetospeed(&mut t, BaudRate::B9600);
69//! cfsetspeed(&mut t, BaudRate::B9600);
70//! # }
71//! ```
72//!
73//! Additionally round-tripping baud rates is consistent across platforms:
74//!
75//! ```rust
76//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
77//! # fn main() {
78//! # let mut t: Termios = unsafe { std::mem::zeroed() };
79//! # cfsetspeed(&mut t, BaudRate::B9600);
80//! let speed = cfgetispeed(&t);
81//! assert_eq!(speed, cfgetospeed(&t));
82//! cfsetispeed(&mut t, speed);
83//! # }
84//! ```
85//!
86//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
87//!
88#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
89                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
90            doc = " ```rust,ignore")]
91#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
92                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
93            doc = " ```rust")]
94//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
95//! # fn main() {
96//! # let mut t: Termios = unsafe { std::mem::zeroed() };
97//! # cfsetspeed(&mut t, BaudRate::B9600);
98//! assert_eq!(cfgetispeed(&t), BaudRate::B9600);
99//! assert_eq!(cfgetospeed(&t), BaudRate::B9600);
100//! # }
101//! ```
102//!
103//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
104//!
105#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
106                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
107            doc = " ```rust")]
108#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
109                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
110            doc = " ```rust,ignore")]
111//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
112//! # fn main() {
113//! # let mut t: Termios = unsafe { std::mem::zeroed() };
114//! # cfsetspeed(&mut t, 9600u32);
115//! assert_eq!(cfgetispeed(&t), 9600u32);
116//! assert_eq!(cfgetospeed(&t), 9600u32);
117//! # }
118//! ```
119//!
120//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
121//!
122#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
123                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
124            doc = " ```rust")]
125#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
126                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
127            doc = " ```rust,ignore")]
128//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
129//! # fn main() {
130//! # let mut t: Termios = unsafe { std::mem::zeroed() };
131//! # cfsetspeed(&mut t, 9600u32);
132//! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into());
133//! assert_eq!(u32::from(BaudRate::B9600), 9600u32);
134//! # }
135//! ```
136//!
137//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
138//! by specifying baud rates directly using `u32`s:
139//!
140#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
141                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
142            doc = " ```rust")]
143#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
144                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
145            doc = " ```rust,ignore")]
146//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
147//! # fn main() {
148//! # let mut t: Termios = unsafe { std::mem::zeroed() };
149//! cfsetispeed(&mut t, 9600u32);
150//! cfsetospeed(&mut t, 9600u32);
151//! cfsetspeed(&mut t, 9600u32);
152//! # }
153//! ```
154use cfg_if::cfg_if;
155use crate::Result;
156use crate::errno::Errno;
157use libc::{self, c_int, tcflag_t};
158use std::cell::{Ref, RefCell};
159use std::convert::From;
160use std::mem;
161use std::os::unix::io::RawFd;
162
163use crate::unistd::Pid;
164
165/// Stores settings for the termios API
166///
167/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
168/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
169/// an open port using `tcgetattr()`.
170#[derive(Clone, Debug, Eq, PartialEq)]
171pub struct Termios {
172    inner: RefCell<libc::termios>,
173    /// Input mode flags (see `termios.c_iflag` documentation)
174    pub input_flags: InputFlags,
175    /// Output mode flags (see `termios.c_oflag` documentation)
176    pub output_flags: OutputFlags,
177    /// Control mode flags (see `termios.c_cflag` documentation)
178    pub control_flags: ControlFlags,
179    /// Local mode flags (see `termios.c_lflag` documentation)
180    pub local_flags: LocalFlags,
181    /// Control characters (see `termios.c_cc` documentation)
182    pub control_chars: [libc::cc_t; NCCS],
183}
184
185impl Termios {
186    /// Exposes an immutable reference to the underlying `libc::termios` data structure.
187    ///
188    /// This is not part of `nix`'s public API because it requires additional work to maintain type
189    /// safety.
190    pub(crate) fn get_libc_termios(&self) -> Ref<libc::termios> {
191        {
192            let mut termios = self.inner.borrow_mut();
193            termios.c_iflag = self.input_flags.bits();
194            termios.c_oflag = self.output_flags.bits();
195            termios.c_cflag = self.control_flags.bits();
196            termios.c_lflag = self.local_flags.bits();
197            termios.c_cc = self.control_chars;
198        }
199        self.inner.borrow()
200    }
201
202    /// Exposes the inner `libc::termios` datastore within `Termios`.
203    ///
204    /// This is unsafe because if this is used to modify the inner `libc::termios` struct, it will
205    /// not automatically update the safe wrapper type around it. In this case it should also be
206    /// paired with a call to `update_wrapper()` so that the wrapper-type and internal
207    /// representation stay consistent.
208    pub(crate) unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
209        {
210            let mut termios = self.inner.borrow_mut();
211            termios.c_iflag = self.input_flags.bits();
212            termios.c_oflag = self.output_flags.bits();
213            termios.c_cflag = self.control_flags.bits();
214            termios.c_lflag = self.local_flags.bits();
215            termios.c_cc = self.control_chars;
216        }
217        self.inner.as_ptr()
218    }
219
220    /// Updates the wrapper values from the internal `libc::termios` data structure.
221    pub(crate) fn update_wrapper(&mut self) {
222        let termios = *self.inner.borrow_mut();
223        self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
224        self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
225        self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
226        self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
227        self.control_chars = termios.c_cc;
228    }
229}
230
231impl From<libc::termios> for Termios {
232    fn from(termios: libc::termios) -> Self {
233        Termios {
234            inner: RefCell::new(termios),
235            input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
236            output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
237            control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
238            local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
239            control_chars: termios.c_cc,
240        }
241    }
242}
243
244impl From<Termios> for libc::termios {
245    fn from(termios: Termios) -> Self {
246        termios.inner.into_inner()
247    }
248}
249
250libc_enum!{
251    /// Baud rates supported by the system.
252    ///
253    /// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this
254    /// enum.
255    ///
256    /// B0 is special and will disable the port.
257    #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
258    #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))]
259    #[non_exhaustive]
260    pub enum BaudRate {
261        B0,
262        B50,
263        B75,
264        B110,
265        B134,
266        B150,
267        B200,
268        B300,
269        B600,
270        B1200,
271        B1800,
272        B2400,
273        B4800,
274        #[cfg(any(target_os = "dragonfly",
275                target_os = "freebsd",
276                target_os = "macos",
277                target_os = "netbsd",
278                target_os = "openbsd"))]
279        B7200,
280        B9600,
281        #[cfg(any(target_os = "dragonfly",
282                target_os = "freebsd",
283                target_os = "macos",
284                target_os = "netbsd",
285                target_os = "openbsd"))]
286        B14400,
287        B19200,
288        #[cfg(any(target_os = "dragonfly",
289                target_os = "freebsd",
290                target_os = "macos",
291                target_os = "netbsd",
292                target_os = "openbsd"))]
293        B28800,
294        B38400,
295        B57600,
296        #[cfg(any(target_os = "dragonfly",
297                target_os = "freebsd",
298                target_os = "macos",
299                target_os = "netbsd",
300                target_os = "openbsd"))]
301        B76800,
302        B115200,
303        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
304        B153600,
305        B230400,
306        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
307        B307200,
308        #[cfg(any(target_os = "android",
309                  target_os = "freebsd",
310                  target_os = "illumos",
311                  target_os = "linux",
312                  target_os = "netbsd",
313                  target_os = "solaris"))]
314        B460800,
315        #[cfg(any(target_os = "android", target_os = "linux"))]
316        B500000,
317        #[cfg(any(target_os = "android", target_os = "linux"))]
318        B576000,
319        #[cfg(any(target_os = "android",
320                  target_os = "freebsd",
321                  target_os = "illumos",
322                  target_os = "linux",
323                  target_os = "netbsd",
324                  target_os = "solaris"))]
325        B921600,
326        #[cfg(any(target_os = "android", target_os = "linux"))]
327        B1000000,
328        #[cfg(any(target_os = "android", target_os = "linux"))]
329        B1152000,
330        #[cfg(any(target_os = "android", target_os = "linux"))]
331        B1500000,
332        #[cfg(any(target_os = "android", target_os = "linux"))]
333        B2000000,
334        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
335        B2500000,
336        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
337        B3000000,
338        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
339        B3500000,
340        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
341        B4000000,
342    }
343    impl TryFrom<libc::speed_t>
344}
345
346#[cfg(any(target_os = "freebsd",
347          target_os = "dragonfly",
348          target_os = "ios",
349          target_os = "macos",
350          target_os = "netbsd",
351          target_os = "openbsd"))]
352impl From<BaudRate> for u32 {
353    fn from(b: BaudRate) -> u32 {
354        b as u32
355    }
356}
357
358// TODO: Add TCSASOFT, which will require treating this as a bitfield.
359libc_enum! {
360    /// Specify when a port configuration change should occur.
361    ///
362    /// Used as an argument to `tcsetattr()`
363    #[repr(i32)]
364    #[non_exhaustive]
365    pub enum SetArg {
366        /// The change will occur immediately
367        TCSANOW,
368        /// The change occurs after all output has been written
369        TCSADRAIN,
370        /// Same as `TCSADRAIN`, but will also flush the input buffer
371        TCSAFLUSH,
372    }
373}
374
375libc_enum! {
376    /// Specify a combination of the input and output buffers to flush
377    ///
378    /// Used as an argument to `tcflush()`.
379    #[repr(i32)]
380    #[non_exhaustive]
381    pub enum FlushArg {
382        /// Flush data that was received but not read
383        TCIFLUSH,
384        /// Flush data written but not transmitted
385        TCOFLUSH,
386        /// Flush both received data not read and written data not transmitted
387        TCIOFLUSH,
388    }
389}
390
391libc_enum! {
392    /// Specify how transmission flow should be altered
393    ///
394    /// Used as an argument to `tcflow()`.
395    #[repr(i32)]
396    #[non_exhaustive]
397    pub enum FlowArg {
398        /// Suspend transmission
399        TCOOFF,
400        /// Resume transmission
401        TCOON,
402        /// Transmit a STOP character, which should disable a connected terminal device
403        TCIOFF,
404        /// Transmit a START character, which should re-enable a connected terminal device
405        TCION,
406    }
407}
408
409// TODO: Make this usable directly as a slice index.
410libc_enum! {
411    /// Indices into the `termios.c_cc` array for special characters.
412    #[repr(usize)]
413    #[non_exhaustive]
414    pub enum SpecialCharacterIndices {
415        VDISCARD,
416        #[cfg(any(target_os = "dragonfly",
417                target_os = "freebsd",
418                target_os = "illumos",
419                target_os = "macos",
420                target_os = "netbsd",
421                target_os = "openbsd",
422                target_os = "solaris"))]
423        VDSUSP,
424        VEOF,
425        VEOL,
426        VEOL2,
427        VERASE,
428        #[cfg(any(target_os = "dragonfly",
429                  target_os = "freebsd",
430                  target_os = "illumos",
431                  target_os = "solaris"))]
432        VERASE2,
433        VINTR,
434        VKILL,
435        VLNEXT,
436        #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
437                target_os = "illumos", target_os = "solaris")))]
438        VMIN,
439        VQUIT,
440        VREPRINT,
441        VSTART,
442        #[cfg(any(target_os = "dragonfly",
443                target_os = "freebsd",
444                target_os = "illumos",
445                target_os = "macos",
446                target_os = "netbsd",
447                target_os = "openbsd",
448                target_os = "solaris"))]
449        VSTATUS,
450        VSTOP,
451        VSUSP,
452        #[cfg(target_os = "linux")]
453        VSWTC,
454        #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))]
455        VSWTCH,
456        #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
457                target_os = "illumos", target_os = "solaris")))]
458        VTIME,
459        VWERASE,
460        #[cfg(target_os = "dragonfly")]
461        VCHECKPT,
462    }
463}
464
465#[cfg(any(all(target_os = "linux", target_arch = "sparc64"),
466        target_os = "illumos", target_os = "solaris"))]
467impl SpecialCharacterIndices {
468    pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
469    pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
470}
471
472pub use libc::NCCS;
473#[cfg(any(target_os = "dragonfly",
474          target_os = "freebsd",
475          target_os = "linux",
476          target_os = "macos",
477          target_os = "netbsd",
478          target_os = "openbsd"))]
479pub use libc::_POSIX_VDISABLE;
480
481libc_bitflags! {
482    /// Flags for configuring the input mode of a terminal
483    pub struct InputFlags: tcflag_t {
484        IGNBRK;
485        BRKINT;
486        IGNPAR;
487        PARMRK;
488        INPCK;
489        ISTRIP;
490        INLCR;
491        IGNCR;
492        ICRNL;
493        IXON;
494        IXOFF;
495        #[cfg(not(target_os = "redox"))]
496        IXANY;
497        #[cfg(not(target_os = "redox"))]
498        IMAXBEL;
499        #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
500        IUTF8;
501    }
502}
503
504libc_bitflags! {
505    /// Flags for configuring the output mode of a terminal
506    pub struct OutputFlags: tcflag_t {
507        OPOST;
508        #[cfg(any(target_os = "android",
509                  target_os = "haiku",
510                  target_os = "linux",
511                  target_os = "openbsd"))]
512        OLCUC;
513        ONLCR;
514        OCRNL as tcflag_t;
515        ONOCR as tcflag_t;
516        ONLRET as tcflag_t;
517        #[cfg(any(target_os = "android",
518                  target_os = "haiku",
519                  target_os = "ios",
520                  target_os = "linux",
521                  target_os = "macos"))]
522        OFILL as tcflag_t;
523        #[cfg(any(target_os = "android",
524                  target_os = "haiku",
525                  target_os = "ios",
526                  target_os = "linux",
527                  target_os = "macos"))]
528        OFDEL as tcflag_t;
529        #[cfg(any(target_os = "android",
530                  target_os = "haiku",
531                  target_os = "ios",
532                  target_os = "linux",
533                  target_os = "macos"))]
534        NL0 as tcflag_t;
535        #[cfg(any(target_os = "android",
536                  target_os = "haiku",
537                  target_os = "ios",
538                  target_os = "linux",
539                  target_os = "macos"))]
540        NL1 as tcflag_t;
541        #[cfg(any(target_os = "android",
542                  target_os = "haiku",
543                  target_os = "ios",
544                  target_os = "linux",
545                  target_os = "macos"))]
546        CR0 as tcflag_t;
547        #[cfg(any(target_os = "android",
548                  target_os = "haiku",
549                  target_os = "ios",
550                  target_os = "linux",
551                  target_os = "macos"))]
552        CR1 as tcflag_t;
553        #[cfg(any(target_os = "android",
554                  target_os = "haiku",
555                  target_os = "ios",
556                  target_os = "linux",
557                  target_os = "macos"))]
558        CR2 as tcflag_t;
559        #[cfg(any(target_os = "android",
560                  target_os = "haiku",
561                  target_os = "ios",
562                  target_os = "linux",
563                  target_os = "macos"))]
564        CR3 as tcflag_t;
565        #[cfg(any(target_os = "android",
566                  target_os = "freebsd",
567                  target_os = "haiku",
568                  target_os = "ios",
569                  target_os = "linux",
570                  target_os = "macos"))]
571        TAB0 as tcflag_t;
572        #[cfg(any(target_os = "android",
573                  target_os = "haiku",
574                  target_os = "ios",
575                  target_os = "linux",
576                  target_os = "macos"))]
577        TAB1 as tcflag_t;
578        #[cfg(any(target_os = "android",
579                  target_os = "haiku",
580                  target_os = "ios",
581                  target_os = "linux",
582                  target_os = "macos"))]
583        TAB2 as tcflag_t;
584        #[cfg(any(target_os = "android",
585                  target_os = "freebsd",
586                  target_os = "haiku",
587                  target_os = "ios",
588                  target_os = "linux",
589                  target_os = "macos"))]
590        TAB3 as tcflag_t;
591        #[cfg(any(target_os = "android", target_os = "linux"))]
592        XTABS;
593        #[cfg(any(target_os = "android",
594                  target_os = "haiku",
595                  target_os = "ios",
596                  target_os = "linux",
597                  target_os = "macos"))]
598        BS0 as tcflag_t;
599        #[cfg(any(target_os = "android",
600                  target_os = "haiku",
601                  target_os = "ios",
602                  target_os = "linux",
603                  target_os = "macos"))]
604        BS1 as tcflag_t;
605        #[cfg(any(target_os = "android",
606                  target_os = "haiku",
607                  target_os = "ios",
608                  target_os = "linux",
609                  target_os = "macos"))]
610        VT0 as tcflag_t;
611        #[cfg(any(target_os = "android",
612                  target_os = "haiku",
613                  target_os = "ios",
614                  target_os = "linux",
615                  target_os = "macos"))]
616        VT1 as tcflag_t;
617        #[cfg(any(target_os = "android",
618                  target_os = "haiku",
619                  target_os = "ios",
620                  target_os = "linux",
621                  target_os = "macos"))]
622        FF0 as tcflag_t;
623        #[cfg(any(target_os = "android",
624                  target_os = "haiku",
625                  target_os = "ios",
626                  target_os = "linux",
627                  target_os = "macos"))]
628        FF1 as tcflag_t;
629        #[cfg(any(target_os = "freebsd",
630                  target_os = "dragonfly",
631                  target_os = "ios",
632                  target_os = "macos",
633                  target_os = "netbsd",
634                  target_os = "openbsd"))]
635        OXTABS;
636        #[cfg(any(target_os = "freebsd",
637                  target_os = "dragonfly",
638                  target_os = "macos",
639                  target_os = "netbsd",
640                  target_os = "openbsd"))]
641        ONOEOT as tcflag_t;
642
643        // Bitmasks for use with OutputFlags to select specific settings
644        // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
645        // is resolved.
646
647        #[cfg(any(target_os = "android",
648                  target_os = "haiku",
649                  target_os = "ios",
650                  target_os = "linux",
651                  target_os = "macos"))]
652        NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac
653        #[cfg(any(target_os = "android",
654                  target_os = "haiku",
655                  target_os = "ios",
656                  target_os = "linux",
657                  target_os = "macos"))]
658        CRDLY as tcflag_t;
659        #[cfg(any(target_os = "android",
660                  target_os = "freebsd",
661                  target_os = "haiku",
662                  target_os = "ios",
663                  target_os = "linux",
664                  target_os = "macos"))]
665        TABDLY as tcflag_t;
666        #[cfg(any(target_os = "android",
667                  target_os = "haiku",
668                  target_os = "ios",
669                  target_os = "linux",
670                  target_os = "macos"))]
671        BSDLY as tcflag_t;
672        #[cfg(any(target_os = "android",
673                  target_os = "haiku",
674                  target_os = "ios",
675                  target_os = "linux",
676                  target_os = "macos"))]
677        VTDLY as tcflag_t;
678        #[cfg(any(target_os = "android",
679                  target_os = "haiku",
680                  target_os = "ios",
681                  target_os = "linux",
682                  target_os = "macos"))]
683        FFDLY as tcflag_t;
684    }
685}
686
687libc_bitflags! {
688    /// Flags for setting the control mode of a terminal
689    pub struct ControlFlags: tcflag_t {
690        #[cfg(any(target_os = "dragonfly",
691                  target_os = "freebsd",
692                  target_os = "ios",
693                  target_os = "macos",
694                  target_os = "netbsd",
695                  target_os = "openbsd"))]
696        CIGNORE;
697        CS5;
698        CS6;
699        CS7;
700        CS8;
701        CSTOPB;
702        CREAD;
703        PARENB;
704        PARODD;
705        HUPCL;
706        CLOCAL;
707        #[cfg(not(target_os = "redox"))]
708        CRTSCTS;
709        #[cfg(any(target_os = "android", target_os = "linux"))]
710        CBAUD;
711        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
712        CMSPAR;
713        #[cfg(any(target_os = "android",
714                  all(target_os = "linux",
715                      not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
716        CIBAUD;
717        #[cfg(any(target_os = "android", target_os = "linux"))]
718        CBAUDEX;
719        #[cfg(any(target_os = "dragonfly",
720                  target_os = "freebsd",
721                  target_os = "macos",
722                  target_os = "netbsd",
723                  target_os = "openbsd"))]
724        MDMBUF;
725        #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
726        CHWFLOW;
727        #[cfg(any(target_os = "dragonfly",
728                  target_os = "freebsd",
729                  target_os = "netbsd",
730                  target_os = "openbsd"))]
731        CCTS_OFLOW;
732        #[cfg(any(target_os = "dragonfly",
733                  target_os = "freebsd",
734                  target_os = "netbsd",
735                  target_os = "openbsd"))]
736        CRTS_IFLOW;
737        #[cfg(any(target_os = "dragonfly",
738                  target_os = "freebsd"))]
739        CDTR_IFLOW;
740        #[cfg(any(target_os = "dragonfly",
741                  target_os = "freebsd"))]
742        CDSR_OFLOW;
743        #[cfg(any(target_os = "dragonfly",
744                  target_os = "freebsd"))]
745        CCAR_OFLOW;
746
747        // Bitmasks for use with ControlFlags to select specific settings
748        // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
749        // is resolved.
750
751        CSIZE;
752    }
753}
754
755libc_bitflags! {
756    /// Flags for setting any local modes
757    pub struct LocalFlags: tcflag_t {
758        #[cfg(not(target_os = "redox"))]
759        ECHOKE;
760        ECHOE;
761        ECHOK;
762        ECHO;
763        ECHONL;
764        #[cfg(not(target_os = "redox"))]
765        ECHOPRT;
766        #[cfg(not(target_os = "redox"))]
767        ECHOCTL;
768        ISIG;
769        ICANON;
770        #[cfg(any(target_os = "freebsd",
771                  target_os = "dragonfly",
772                  target_os = "ios",
773                  target_os = "macos",
774                  target_os = "netbsd",
775                  target_os = "openbsd"))]
776        ALTWERASE;
777        IEXTEN;
778        #[cfg(not(target_os = "redox"))]
779        EXTPROC;
780        TOSTOP;
781        #[cfg(not(target_os = "redox"))]
782        FLUSHO;
783        #[cfg(any(target_os = "freebsd",
784                  target_os = "dragonfly",
785                  target_os = "ios",
786                  target_os = "macos",
787                  target_os = "netbsd",
788                  target_os = "openbsd"))]
789        NOKERNINFO;
790        #[cfg(not(target_os = "redox"))]
791        PENDIN;
792        NOFLSH;
793    }
794}
795
796cfg_if!{
797    if #[cfg(any(target_os = "freebsd",
798                 target_os = "dragonfly",
799                 target_os = "ios",
800                 target_os = "macos",
801                 target_os = "netbsd",
802                 target_os = "openbsd"))] {
803        /// Get input baud rate (see
804        /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
805        ///
806        /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
807        // The cast is not unnecessary on all platforms.
808        #[allow(clippy::unnecessary_cast)]
809        pub fn cfgetispeed(termios: &Termios) -> u32 {
810            let inner_termios = termios.get_libc_termios();
811            unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
812        }
813
814        /// Get output baud rate (see
815        /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
816        ///
817        /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
818        // The cast is not unnecessary on all platforms.
819        #[allow(clippy::unnecessary_cast)]
820        pub fn cfgetospeed(termios: &Termios) -> u32 {
821            let inner_termios = termios.get_libc_termios();
822            unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
823        }
824
825        /// Set input baud rate (see
826        /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
827        ///
828        /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
829        pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
830            let inner_termios = unsafe { termios.get_libc_termios_mut() };
831            let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
832            termios.update_wrapper();
833            Errno::result(res).map(drop)
834        }
835
836        /// Set output baud rate (see
837        /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
838        ///
839        /// `cfsetospeed()` sets the output baud rate in the given termios structure.
840        pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
841            let inner_termios = unsafe { termios.get_libc_termios_mut() };
842            let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
843            termios.update_wrapper();
844            Errno::result(res).map(drop)
845        }
846
847        /// Set both the input and output baud rates (see
848        /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
849        ///
850        /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
851        /// this is part of the 4.4BSD standard and not part of POSIX.
852        pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
853            let inner_termios = unsafe { termios.get_libc_termios_mut() };
854            let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
855            termios.update_wrapper();
856            Errno::result(res).map(drop)
857        }
858    } else {
859        use std::convert::TryInto;
860
861        /// Get input baud rate (see
862        /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
863        ///
864        /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
865        pub fn cfgetispeed(termios: &Termios) -> BaudRate {
866            let inner_termios = termios.get_libc_termios();
867            unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
868        }
869
870        /// Get output baud rate (see
871        /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
872        ///
873        /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
874        pub fn cfgetospeed(termios: &Termios) -> BaudRate {
875            let inner_termios = termios.get_libc_termios();
876            unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
877        }
878
879        /// Set input baud rate (see
880        /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
881        ///
882        /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
883        pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
884            let inner_termios = unsafe { termios.get_libc_termios_mut() };
885            let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
886            termios.update_wrapper();
887            Errno::result(res).map(drop)
888        }
889
890        /// Set output baud rate (see
891        /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
892        ///
893        /// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
894        pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
895            let inner_termios = unsafe { termios.get_libc_termios_mut() };
896            let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
897            termios.update_wrapper();
898            Errno::result(res).map(drop)
899        }
900
901        /// Set both the input and output baud rates (see
902        /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
903        ///
904        /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
905        /// this is part of the 4.4BSD standard and not part of POSIX.
906        pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
907            let inner_termios = unsafe { termios.get_libc_termios_mut() };
908            let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
909            termios.update_wrapper();
910            Errno::result(res).map(drop)
911        }
912    }
913}
914
915/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
916/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
917///
918/// `cfmakeraw()` configures the termios structure such that input is available character-by-
919/// character, echoing is disabled, and all special input and output processing is disabled. Note
920/// that this is a non-standard function, but is available on Linux and BSDs.
921pub fn cfmakeraw(termios: &mut Termios) {
922    let inner_termios = unsafe { termios.get_libc_termios_mut() };
923    unsafe {
924        libc::cfmakeraw(inner_termios);
925    }
926    termios.update_wrapper();
927}
928
929/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
930/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)).
931///
932/// Note that this is a non-standard function, available on FreeBSD.
933#[cfg(target_os = "freebsd")]
934pub fn cfmakesane(termios: &mut Termios) {
935    let inner_termios = unsafe { termios.get_libc_termios_mut() };
936    unsafe {
937        libc::cfmakesane(inner_termios);
938    }
939    termios.update_wrapper();
940}
941
942/// Return the configuration of a port
943/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
944///
945/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
946/// this structure *will not* reconfigure the port, instead the modifications should be done to
947/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
948pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
949    let mut termios = mem::MaybeUninit::uninit();
950
951    let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
952
953    Errno::result(res)?;
954
955    unsafe { Ok(termios.assume_init().into()) }
956}
957
958/// Set the configuration for a terminal (see
959/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
960///
961/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
962/// takes affect at a time specified by `actions`. Note that this function may return success if
963/// *any* of the parameters were successfully set, not only if all were set successfully.
964pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
965    let inner_termios = termios.get_libc_termios();
966    Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
967}
968
969/// Block until all output data is written (see
970/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
971pub fn tcdrain(fd: RawFd) -> Result<()> {
972    Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
973}
974
975/// Suspend or resume the transmission or reception of data (see
976/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
977///
978/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
979/// depending on the value of `action`.
980pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
981    Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop)
982}
983
984/// Discard data in the output or input queue (see
985/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
986///
987/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
988/// depending on the value of `action`.
989pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
990    Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop)
991}
992
993/// Send a break for a specific duration (see
994/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
995///
996/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
997/// of zero-valued bits for an implementation-defined duration.
998pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
999    Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
1000}
1001
1002/// Get the session controlled by the given terminal (see
1003/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
1004pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
1005    let res = unsafe { libc::tcgetsid(fd) };
1006
1007    Errno::result(res).map(Pid::from_raw)
1008}
1009
1010#[cfg(test)]
1011mod test {
1012    use super::*;
1013    use std::convert::TryFrom;
1014
1015    #[test]
1016    fn try_from() {
1017        assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
1018        assert!(BaudRate::try_from(999999999).is_err());
1019    }
1020}