Skip to main content

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