blob: 02f06268fdfea714cbab6fbb5ecbb85d43c8ee65 [file] [log] [blame]
use crate::backend::c;
use crate::{backend, io};
use bitflags::bitflags;
/// `struct termios` for use with [`tcgetattr`] and [`tcsetattr`].
///
/// [`tcgetattr`]: crate::termios::tcgetattr
/// [`tcsetattr`]: crate::termios::tcsetattr
#[repr(C)]
#[derive(Clone)]
pub struct Termios {
/// How is input interpreted?
#[doc(alias = "c_iflag")]
pub input_modes: InputModes,
/// How is output translated?
#[doc(alias = "c_oflag")]
pub output_modes: OutputModes,
/// Low-level configuration flags.
#[doc(alias = "c_cflag")]
pub control_modes: ControlModes,
/// High-level configuration flags.
#[doc(alias = "c_lflag")]
pub local_modes: LocalModes,
/// Line discipline.
#[doc(alias = "c_line")]
#[cfg(not(all(linux_raw, any(target_arch = "powerpc", target_arch = "powerpc64"))))]
#[cfg(any(
linux_like,
target_env = "newlib",
target_os = "fuchsia",
target_os = "haiku",
target_os = "redox"
))]
pub line_discipline: c::cc_t,
/// How are various special control codes handled?
#[doc(alias = "c_cc")]
#[cfg(not(target_os = "haiku"))]
pub special_codes: SpecialCodes,
#[cfg(target_os = "nto")]
pub(crate) __reserved: [c::c_uint; 3],
/// Line discipline.
// On PowerPC, this field comes after `c_cc`.
#[doc(alias = "c_line")]
#[cfg(all(linux_raw, any(target_arch = "powerpc", target_arch = "powerpc64")))]
pub line_discipline: c::cc_t,
/// See the `input_speed` and `set_input_seed` functions.
///
/// On Linux and BSDs, this is the arbitrary integer speed value. On all
/// other platforms, this is the encoded speed value.
#[cfg(not(any(solarish, all(libc, target_env = "newlib"), target_os = "aix")))]
pub(crate) input_speed: c::speed_t,
/// See the `output_speed` and `set_output_seed` functions.
///
/// On Linux and BSDs, this is the integer speed value. On all other
/// platforms, this is the encoded speed value.
#[cfg(not(any(solarish, all(libc, target_env = "newlib"), target_os = "aix")))]
pub(crate) output_speed: c::speed_t,
/// How are various special control codes handled?
#[doc(alias = "c_cc")]
#[cfg(target_os = "haiku")]
pub special_codes: SpecialCodes,
}
impl Termios {
/// `cfmakeraw(self)`—Set a `Termios` value to the settings for “raw” mode.
///
/// In raw mode, input is available a byte at a time, echoing is disabled,
/// and special terminal input and output codes are disabled.
#[cfg(not(target_os = "nto"))]
#[doc(alias = "cfmakeraw")]
#[inline]
pub fn make_raw(&mut self) {
backend::termios::syscalls::cfmakeraw(self)
}
/// Return the input communication speed.
///
/// Unlike the `c_ispeed` field in GLIBC and others, this returns the
/// integer value of the speed, rather than the `B*` encoded constant
/// value.
#[doc(alias = "c_ispeed")]
#[doc(alias = "cfgetispeed")]
#[doc(alias = "cfgetspeed")]
#[inline]
pub fn input_speed(&self) -> u32 {
// On Linux and BSDs, `input_speed` is the arbitrary integer speed.
#[cfg(any(linux_kernel, bsd))]
{
debug_assert!(u32::try_from(self.input_speed).is_ok());
self.input_speed as u32
}
// On illumos, `input_speed` is not present.
#[cfg(any(solarish, all(libc, target_env = "newlib"), target_os = "aix"))]
unsafe {
speed::decode(c::cfgetispeed(crate::utils::as_ptr(self).cast())).unwrap()
}
// On other platforms, it's the encoded speed.
#[cfg(not(any(
linux_kernel,
bsd,
solarish,
all(libc, target_env = "newlib"),
target_os = "aix"
)))]
{
speed::decode(self.input_speed).unwrap()
}
}
/// Return the output communication speed.
///
/// Unlike the `c_ospeed` field in GLIBC and others, this returns the
/// arbitrary integer value of the speed, rather than the `B*` encoded
/// constant value.
#[inline]
pub fn output_speed(&self) -> u32 {
// On Linux and BSDs, `input_speed` is the arbitrary integer speed.
#[cfg(any(linux_kernel, bsd))]
{
debug_assert!(u32::try_from(self.output_speed).is_ok());
self.output_speed as u32
}
// On illumos, `output_speed` is not present.
#[cfg(any(solarish, all(libc, target_env = "newlib"), target_os = "aix"))]
unsafe {
speed::decode(c::cfgetospeed(crate::utils::as_ptr(self).cast())).unwrap()
}
// On other platforms, it's the encoded speed.
#[cfg(not(any(
linux_kernel,
bsd,
solarish,
all(libc, target_env = "newlib"),
target_os = "aix"
)))]
{
speed::decode(self.output_speed).unwrap()
}
}
/// Set the input and output communication speeds.
///
/// Unlike the `c_ispeed` and `c_ospeed` fields in GLIBC and others, this
/// takes the arbitrary integer value of the speed, rather than the `B*`
/// encoded constant value. Not all implementations support all integer
/// values; use the constants in the [`speed`] module for likely-supported
/// speeds.
#[cfg(not(target_os = "nto"))]
#[doc(alias = "cfsetspeed")]
#[doc(alias = "CBAUD")]
#[doc(alias = "CBAUDEX")]
#[doc(alias = "CIBAUD")]
#[doc(alias = "CIBAUDEX")]
#[inline]
pub fn set_speed(&mut self, new_speed: u32) -> io::Result<()> {
backend::termios::syscalls::set_speed(self, new_speed)
}
/// Set the input communication speed.
///
/// Unlike the `c_ispeed` field in GLIBC and others, this takes the
/// arbitrary integer value of the speed, rather than the `B*` encoded
/// constant value. Not all implementations support all integer values; use
/// the constants in the [`speed`] module for known-supported speeds.
///
/// On some platforms, changing the input speed changes the output speed
/// to the same speed.
#[doc(alias = "c_ispeed")]
#[doc(alias = "cfsetispeed")]
#[doc(alias = "CIBAUD")]
#[doc(alias = "CIBAUDEX")]
#[inline]
pub fn set_input_speed(&mut self, new_speed: u32) -> io::Result<()> {
backend::termios::syscalls::set_input_speed(self, new_speed)
}
/// Set the output communication speed.
///
/// Unlike the `c_ospeed` field in GLIBC and others, this takes the
/// arbitrary integer value of the speed, rather than the `B*` encoded
/// constant value. Not all implementations support all integer values; use
/// the constants in the [`speed`] module for known-supported speeds.
///
/// On some platforms, changing the output speed changes the input speed
/// to the same speed.
#[doc(alias = "c_ospeed")]
#[doc(alias = "cfsetospeed")]
#[doc(alias = "CBAUD")]
#[doc(alias = "CBAUDEX")]
#[inline]
pub fn set_output_speed(&mut self, new_speed: u32) -> io::Result<()> {
backend::termios::syscalls::set_output_speed(self, new_speed)
}
}
impl core::fmt::Debug for Termios {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut d = f.debug_struct("Termios");
d.field("input_modes", &self.input_modes);
d.field("output_modes", &self.output_modes);
d.field("control_modes", &self.control_modes);
d.field("local_modes", &self.local_modes);
#[cfg(any(
linux_like,
target_env = "newlib",
target_os = "fuchsia",
target_os = "haiku",
target_os = "redox"
))]
{
d.field("line_discipline", &self.line_discipline);
}
d.field("special_codes", &self.special_codes);
d.field("input_speed", &self.input_speed());
d.field("output_speed", &self.output_speed());
d.finish()
}
}
bitflags! {
/// Flags controlling terminal input.
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct InputModes: c::tcflag_t {
/// `IGNBRK`
const IGNBRK = c::IGNBRK;
/// `BRKINT`
const BRKINT = c::BRKINT;
/// `IGNPAR`
const IGNPAR = c::IGNPAR;
/// `PARMRK`
const PARMRK = c::PARMRK;
/// `INPCK`
const INPCK = c::INPCK;
/// `ISTRIP`
const ISTRIP = c::ISTRIP;
/// `INLCR`
const INLCR = c::INLCR;
/// `IGNCR`
const IGNCR = c::IGNCR;
/// `ICRNL`
const ICRNL = c::ICRNL;
/// `IUCLC`
#[cfg(any(linux_kernel, solarish, target_os = "aix", target_os = "haiku", target_os = "nto"))]
const IUCLC = c::IUCLC;
/// `IXON`
const IXON = c::IXON;
/// `IXANY`
#[cfg(not(target_os = "redox"))]
const IXANY = c::IXANY;
/// `IXOFF`
const IXOFF = c::IXOFF;
/// `IMAXBEL`
#[cfg(not(any(target_os = "haiku", target_os = "redox")))]
const IMAXBEL = c::IMAXBEL;
/// `IUTF8`
#[cfg(not(any(
freebsdlike,
netbsdlike,
solarish,
target_os = "aix",
target_os = "emscripten",
target_os = "haiku",
target_os = "hurd",
target_os = "redox",
)))]
const IUTF8 = c::IUTF8;
/// <https://docs.rs/bitflags/latest/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// Flags controlling terminal output.
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct OutputModes: c::tcflag_t {
/// `OPOST`
const OPOST = c::OPOST;
/// `OLCUC`
#[cfg(not(any(
apple,
freebsdlike,
target_os = "aix",
target_os = "netbsd",
target_os = "redox",
)))]
const OLCUC = c::OLCUC;
/// `ONLCR`
const ONLCR = c::ONLCR;
/// `OCRNL`
const OCRNL = c::OCRNL;
/// `ONOCR`
const ONOCR = c::ONOCR;
/// `ONLRET`
const ONLRET = c::ONLRET;
/// `OFILL`
#[cfg(not(bsd))]
const OFILL = c::OFILL;
/// `OFDEL`
#[cfg(not(bsd))]
const OFDEL = c::OFDEL;
/// `NLDLY`
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
const NLDLY = c::NLDLY;
/// `NL0`
#[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
const NL0 = c::NL0;
/// `NL1`
#[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
const NL1 = c::NL1;
/// `CRDLY`
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
const CRDLY = c::CRDLY;
/// `CR0`
#[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
const CR0 = c::CR0;
/// `CR1`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const CR1 = c::CR1;
/// `CR2`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const CR2 = c::CR2;
/// `CR3`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const CR3 = c::CR3;
/// `TABDLY`
#[cfg(not(any(
netbsdlike,
solarish,
target_os = "dragonfly",
target_os = "redox",
)))]
const TABDLY = c::TABDLY;
/// `TAB0`
#[cfg(not(any(
netbsdlike,
solarish,
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "redox",
)))]
const TAB0 = c::TAB0;
/// `TAB1`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const TAB1 = c::TAB1;
/// `TAB2`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const TAB2 = c::TAB2;
/// `TAB3`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const TAB3 = c::TAB3;
/// `XTABS`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "redox",
)))]
const XTABS = c::XTABS;
/// `BSDLY`
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
const BSDLY = c::BSDLY;
/// `BS0`
#[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
const BS0 = c::BS0;
/// `BS1`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const BS1 = c::BS1;
/// `FFDLY`
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
const FFDLY = c::FFDLY;
/// `FF0`
#[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
const FF0 = c::FF0;
/// `FF1`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const FF1 = c::FF1;
/// `VTDLY`
#[cfg(not(any(bsd, solarish, target_os = "redox")))]
const VTDLY = c::VTDLY;
/// `VT0`
#[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
const VT0 = c::VT0;
/// `VT1`
#[cfg(not(any(
target_env = "musl",
bsd,
solarish,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
)))]
const VT1 = c::VT1;
/// <https://docs.rs/bitflags/latest/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// Flags controlling special terminal modes.
///
/// `CBAUD`, `CBAUDEX`, `CIBAUD`, and `CIBAUDEX` are not defined here,
/// because they're handled automatically by [`Termios::set_speed`] and
/// related functions.
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ControlModes: c::tcflag_t {
/// `CSIZE`
const CSIZE = c::CSIZE;
/// `CS5`
const CS5 = c::CS5;
/// `CS6`
const CS6 = c::CS6;
/// `CS7`
const CS7 = c::CS7;
/// `CS8`
const CS8 = c::CS8;
/// `CSTOPB`
const CSTOPB = c::CSTOPB;
/// `CREAD`
const CREAD = c::CREAD;
/// `PARENB`
const PARENB = c::PARENB;
/// `PARODD`
const PARODD = c::PARODD;
/// `HUPCL`
const HUPCL = c::HUPCL;
/// `CLOCAL`
const CLOCAL = c::CLOCAL;
/// `CRTSCTS`
#[cfg(not(any(target_os = "aix", target_os = "nto", target_os = "redox")))]
const CRTSCTS = c::CRTSCTS;
/// `CMSPAR`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "emscripten",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
target_os = "redox",
)))]
const CMSPAR = c::CMSPAR;
/// <https://docs.rs/bitflags/latest/bitflags/#externally-defined-flags>
const _ = !0;
}
}
bitflags! {
/// Flags controlling “local” terminal modes.
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct LocalModes: c::tcflag_t {
/// `XCASE`
#[cfg(any(linux_kernel, target_arch = "s390x", target_os = "haiku"))]
const XCASE = c::XCASE;
/// `ECHOCTL`
#[cfg(not(target_os = "redox"))]
const ECHOCTL = c::ECHOCTL;
/// `ECHOPRT`
#[cfg(not(any(target_os = "nto", target_os = "redox")))]
const ECHOPRT = c::ECHOPRT;
/// `ECHOKE`
#[cfg(not(target_os = "redox"))]
const ECHOKE = c::ECHOKE;
/// `FLUSHO`
#[cfg(not(any(target_os = "nto", target_os = "redox")))]
const FLUSHO = c::FLUSHO;
/// `PENDIN`
#[cfg(not(any(target_os = "nto", target_os = "redox")))]
const PENDIN = c::PENDIN;
/// `EXTPROC`
#[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "nto", target_os = "redox")))]
const EXTPROC = c::EXTPROC;
/// `ISIG`
const ISIG = c::ISIG;
/// `ICANON`—A flag for the `c_lflag` field of [`Termios`] indicating
/// canonical mode.
const ICANON = c::ICANON;
/// `ECHO`
const ECHO = c::ECHO;
/// `ECHOE`
const ECHOE = c::ECHOE;
/// `ECHOK`
const ECHOK = c::ECHOK;
/// `ECHONL`
const ECHONL = c::ECHONL;
/// `NOFLSH`
const NOFLSH = c::NOFLSH;
/// `TOSTOP`
const TOSTOP = c::TOSTOP;
/// `IEXTEN`
const IEXTEN = c::IEXTEN;
/// <https://docs.rs/bitflags/latest/bitflags/#externally-defined-flags>
const _ = !0;
}
}
/// Speeds for use with [`Termios::set_input_speed`] and
/// [`Termios::set_output_speed`].
///
/// Unlike in some platforms' libc APIs, these always have the same numerical
/// value as their names; for example, `B50` has the value `50`, and so on.
/// Consequently, it's not necessary to use them. They are provided here
/// because they help identify speeds which are likely to be supported, on
/// platforms which don't support arbitrary speeds.
pub mod speed {
#[cfg(not(bsd))]
use crate::backend::c;
/// `B0`
pub const B0: u32 = 0;
/// `B50`
pub const B50: u32 = 50;
/// `B75`
pub const B75: u32 = 75;
/// `B110`
pub const B110: u32 = 110;
/// `B134`
pub const B134: u32 = 134;
/// `B150`
pub const B150: u32 = 150;
/// `B200`
pub const B200: u32 = 200;
/// `B300`
pub const B300: u32 = 300;
/// `B600`
pub const B600: u32 = 600;
/// `B1200`
pub const B1200: u32 = 1200;
/// `B1800`
pub const B1800: u32 = 1800;
/// `B2400`
pub const B2400: u32 = 2400;
/// `B4800`
pub const B4800: u32 = 4800;
/// `B9600`
pub const B9600: u32 = 9600;
/// `B19200`
#[doc(alias = "EXTA")]
pub const B19200: u32 = 19200;
/// `B38400`
#[doc(alias = "EXTB")]
pub const B38400: u32 = 38400;
/// `B57600`
#[cfg(not(target_os = "aix"))]
pub const B57600: u32 = 57600;
/// `B115200`
#[cfg(not(target_os = "aix"))]
pub const B115200: u32 = 115_200;
/// `B230400`
#[cfg(not(target_os = "aix"))]
pub const B230400: u32 = 230_400;
/// `B460800`
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "openbsd"
)))]
pub const B460800: u32 = 460_800;
/// `B500000`
#[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))]
pub const B500000: u32 = 500_000;
/// `B576000`
#[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))]
pub const B576000: u32 = 576_000;
/// `B921600`
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "openbsd"
)))]
pub const B921600: u32 = 921_600;
/// `B1000000`
#[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))]
pub const B1000000: u32 = 1_000_000;
/// `B1152000`
#[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))]
pub const B1152000: u32 = 1_152_000;
/// `B1500000`
#[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))]
pub const B1500000: u32 = 1_500_000;
/// `B2000000`
#[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))]
pub const B2000000: u32 = 2_000_000;
/// `B2500000`
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "solaris",
)))]
pub const B2500000: u32 = 2_500_000;
/// `B3000000`
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "solaris",
)))]
pub const B3000000: u32 = 3_000_000;
/// `B3500000`
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "solaris",
)))]
pub const B3500000: u32 = 3_500_000;
/// `B4000000`
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "solaris",
)))]
pub const B4000000: u32 = 4_000_000;
/// Translate from a `c::speed_t` code to an arbitrary integer speed value
/// `u32`.
///
/// On BSD platforms, integer speed values are already the same as their
/// encoded values, and on Linux platforms, we use `TCGETS2`/`TCSETS2`
/// and the `c_ispeed`/`c_ospeed`` fields, except that on Linux on
/// PowerPC on QEMU, `TCGETS2`/`TCSETS2` don't set `c_ispeed`/`c_ospeed`.
#[cfg(not(any(
bsd,
all(
linux_kernel,
not(any(target_arch = "powerpc", target_arch = "powerpc64"))
)
)))]
pub(crate) const fn decode(encoded_speed: c::speed_t) -> Option<u32> {
match encoded_speed {
c::B0 => Some(0),
c::B50 => Some(50),
c::B75 => Some(75),
c::B110 => Some(110),
c::B134 => Some(134),
c::B150 => Some(150),
c::B200 => Some(200),
c::B300 => Some(300),
c::B600 => Some(600),
c::B1200 => Some(1200),
c::B1800 => Some(1800),
c::B2400 => Some(2400),
c::B4800 => Some(4800),
c::B9600 => Some(9600),
c::B19200 => Some(19200),
c::B38400 => Some(38400),
#[cfg(not(target_os = "aix"))]
c::B57600 => Some(57600),
#[cfg(not(target_os = "aix"))]
c::B115200 => Some(115_200),
#[cfg(not(any(target_os = "aix", target_os = "nto")))]
c::B230400 => Some(230_400),
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "nto",
target_os = "openbsd"
)))]
c::B460800 => Some(460_800),
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto"
)))]
c::B500000 => Some(500_000),
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto"
)))]
c::B576000 => Some(576_000),
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "nto",
target_os = "openbsd"
)))]
c::B921600 => Some(921_600),
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris"
)))]
c::B1000000 => Some(1_000_000),
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris"
)))]
c::B1152000 => Some(1_152_000),
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris"
)))]
c::B1500000 => Some(1_500_000),
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris"
)))]
c::B2000000 => Some(2_000_000),
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris",
)))]
c::B2500000 => Some(2_500_000),
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris",
)))]
c::B3000000 => Some(3_000_000),
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris",
)))]
c::B3500000 => Some(3_500_000),
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris",
)))]
c::B4000000 => Some(4_000_000),
_ => None,
}
}
/// Translate from an arbitrary `u32` arbitrary integer speed value to a
/// `c::speed_t` code.
#[cfg(not(bsd))]
pub(crate) const fn encode(speed: u32) -> Option<c::speed_t> {
match speed {
0 => Some(c::B0),
50 => Some(c::B50),
75 => Some(c::B75),
110 => Some(c::B110),
134 => Some(c::B134),
150 => Some(c::B150),
200 => Some(c::B200),
300 => Some(c::B300),
600 => Some(c::B600),
1200 => Some(c::B1200),
1800 => Some(c::B1800),
2400 => Some(c::B2400),
4800 => Some(c::B4800),
9600 => Some(c::B9600),
19200 => Some(c::B19200),
38400 => Some(c::B38400),
#[cfg(not(target_os = "aix"))]
57600 => Some(c::B57600),
#[cfg(not(target_os = "aix"))]
115_200 => Some(c::B115200),
#[cfg(not(any(target_os = "aix", target_os = "nto")))]
230_400 => Some(c::B230400),
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "nto",
target_os = "openbsd",
)))]
460_800 => Some(c::B460800),
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto"
)))]
500_000 => Some(c::B500000),
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto"
)))]
576_000 => Some(c::B576000),
#[cfg(not(any(
apple,
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "nto",
target_os = "openbsd"
)))]
921_600 => Some(c::B921600),
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris"
)))]
1_000_000 => Some(c::B1000000),
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris"
)))]
1_152_000 => Some(c::B1152000),
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris"
)))]
1_500_000 => Some(c::B1500000),
#[cfg(not(any(
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris"
)))]
2_000_000 => Some(c::B2000000),
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris",
)))]
2_500_000 => Some(c::B2500000),
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris",
)))]
3_000_000 => Some(c::B3000000),
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris",
)))]
3_500_000 => Some(c::B3500000),
#[cfg(not(any(
target_arch = "sparc",
target_arch = "sparc64",
bsd,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "solaris",
)))]
4_000_000 => Some(c::B4000000),
_ => None,
}
}
}
/// An array indexed by [`SpecialCodeIndex`] indicating the current values
/// of various special control codes.
#[repr(transparent)]
#[derive(Clone, Debug)]
pub struct SpecialCodes(pub(crate) [c::cc_t; c::NCCS as usize]);
impl core::ops::Index<SpecialCodeIndex> for SpecialCodes {
type Output = c::cc_t;
fn index(&self, index: SpecialCodeIndex) -> &Self::Output {
&self.0[index.0]
}
}
impl core::ops::IndexMut<SpecialCodeIndex> for SpecialCodes {
fn index_mut(&mut self, index: SpecialCodeIndex) -> &mut Self::Output {
&mut self.0[index.0]
}
}
/// Indices for use with [`Termios::special_codes`].
pub struct SpecialCodeIndex(usize);
#[rustfmt::skip]
impl SpecialCodeIndex {
/// `VINTR`
pub const VINTR: Self = Self(c::VINTR as usize);
/// `VQUIT`
pub const VQUIT: Self = Self(c::VQUIT as usize);
/// `VERASE`
pub const VERASE: Self = Self(c::VERASE as usize);
/// `VKILL`
pub const VKILL: Self = Self(c::VKILL as usize);
/// `VEOF`
pub const VEOF: Self = Self(c::VEOF as usize);
/// `VTIME`
pub const VTIME: Self = Self(c::VTIME as usize);
/// `VMIN`
pub const VMIN: Self = Self(c::VMIN as usize);
/// `VSWTC`
#[cfg(not(any(
bsd,
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "hurd",
target_os = "nto",
)))]
pub const VSWTC: Self = Self(c::VSWTC as usize);
/// `VSTART`
pub const VSTART: Self = Self(c::VSTART as usize);
/// `VSTOP`
pub const VSTOP: Self = Self(c::VSTOP as usize);
/// `VSUSP`
pub const VSUSP: Self = Self(c::VSUSP as usize);
/// `VEOL`
pub const VEOL: Self = Self(c::VEOL as usize);
/// `VREPRINT`
#[cfg(not(target_os = "haiku"))]
pub const VREPRINT: Self = Self(c::VREPRINT as usize);
/// `VDISCARD`
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
pub const VDISCARD: Self = Self(c::VDISCARD as usize);
/// `VWERASE`
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
pub const VWERASE: Self = Self(c::VWERASE as usize);
/// `VLNEXT`
#[cfg(not(target_os = "haiku"))]
pub const VLNEXT: Self = Self(c::VLNEXT as usize);
/// `VEOL2`
pub const VEOL2: Self = Self(c::VEOL2 as usize);
}
/// `TCSA*` values for use with [`tcsetattr`].
///
/// [`tcsetattr`]: crate::termios::tcsetattr
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(u32)]
pub enum OptionalActions {
/// `TCSANOW`—Make the change immediately.
#[doc(alias = "TCSANOW")]
Now = c::TCSANOW as u32,
/// `TCSADRAIN`—Make the change after all output has been transmitted.
#[doc(alias = "TCSADRAIN")]
Drain = c::TCSADRAIN as u32,
/// `TCSAFLUSH`—Discard any pending input and then make the change
/// after all output has been transmitted.
#[doc(alias = "TCSAFLUSH")]
Flush = c::TCSAFLUSH as u32,
}
/// `TC*` values for use with [`tcflush`].
///
/// [`tcflush`]: crate::termios::tcflush
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(u32)]
pub enum QueueSelector {
/// `TCIFLUSH`—Flush data received but not read.
#[doc(alias = "TCIFLUSH")]
IFlush = c::TCIFLUSH as u32,
/// `TCOFLUSH`—Flush data written but not transmitted.
#[doc(alias = "TCOFLUSH")]
OFlush = c::TCOFLUSH as u32,
/// `TCIOFLUSH`—`IFlush` and `OFlush` combined.
#[doc(alias = "TCIOFLUSH")]
IOFlush = c::TCIOFLUSH as u32,
}
/// `TC*` values for use with [`tcflow`].
///
/// [`tcflow`]: crate::termios::tcflow
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(u32)]
pub enum Action {
/// `TCOOFF`—Suspend output.
#[doc(alias = "TCOOFF")]
OOff = c::TCOOFF as u32,
/// `TCOON`—Restart suspended output.
#[doc(alias = "TCOON")]
OOn = c::TCOON as u32,
/// `TCIOFF`—Transmits a STOP byte.
#[doc(alias = "TCIOFF")]
IOff = c::TCIOFF as u32,
/// `TCION`—Transmits a START byte.
#[doc(alias = "TCION")]
IOn = c::TCION as u32,
}
/// `struct winsize` for use with [`tcgetwinsize`].
///
/// [`tcgetwinsize`]: crate::termios::tcgetwinsize
#[doc(alias = "winsize")]
pub type Winsize = c::winsize;
#[test]
fn termios_layouts() {
check_renamed_type!(InputModes, tcflag_t);
check_renamed_type!(OutputModes, tcflag_t);
check_renamed_type!(ControlModes, tcflag_t);
check_renamed_type!(LocalModes, tcflag_t);
// On platforms with a termios/termios2 split, check `termios`.
#[cfg(linux_raw)]
{
check_renamed_type!(Termios, termios2);
check_renamed_struct_renamed_field!(Termios, termios2, input_modes, c_iflag);
check_renamed_struct_renamed_field!(Termios, termios2, output_modes, c_oflag);
check_renamed_struct_renamed_field!(Termios, termios2, control_modes, c_cflag);
check_renamed_struct_renamed_field!(Termios, termios2, local_modes, c_lflag);
check_renamed_struct_renamed_field!(Termios, termios2, line_discipline, c_line);
check_renamed_struct_renamed_field!(Termios, termios2, special_codes, c_cc);
check_renamed_struct_renamed_field!(Termios, termios2, input_speed, c_ispeed);
check_renamed_struct_renamed_field!(Termios, termios2, output_speed, c_ospeed);
// We assume that `termios` has the same layout as `termios2` minus the
// `c_ispeed` and `c_ospeed` fields.
check_renamed_struct_renamed_field!(Termios, termios, input_modes, c_iflag);
check_renamed_struct_renamed_field!(Termios, termios, output_modes, c_oflag);
check_renamed_struct_renamed_field!(Termios, termios, control_modes, c_cflag);
check_renamed_struct_renamed_field!(Termios, termios, local_modes, c_lflag);
check_renamed_struct_renamed_field!(Termios, termios, special_codes, c_cc);
// On everything except PowerPC, `termios` matches `termios2` except
// for the addition of `c_ispeed` and `c_ospeed`.
#[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
const_assert_eq!(
memoffset::offset_of!(Termios, input_speed),
core::mem::size_of::<c::termios>()
);
// On PowerPC, `termios2` is `termios`.
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
assert_eq_size!(c::termios2, c::termios);
}
#[cfg(not(linux_raw))]
{
// On Mips, Sparc, and Android, the libc lacks the ospeed and ispeed
// fields.
#[cfg(all(
not(all(
target_env = "gnu",
any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
)
)),
not(all(libc, target_os = "android"))
))]
check_renamed_type!(Termios, termios);
#[cfg(not(all(
not(all(
target_env = "gnu",
any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
)
)),
not(all(libc, target_os = "android"))
)))]
const_assert!(core::mem::size_of::<Termios>() >= core::mem::size_of::<c::termios>());
check_renamed_struct_renamed_field!(Termios, termios, input_modes, c_iflag);
check_renamed_struct_renamed_field!(Termios, termios, output_modes, c_oflag);
check_renamed_struct_renamed_field!(Termios, termios, control_modes, c_cflag);
check_renamed_struct_renamed_field!(Termios, termios, local_modes, c_lflag);
#[cfg(any(
linux_like,
target_env = "newlib",
target_os = "fuchsia",
target_os = "haiku",
target_os = "redox"
))]
check_renamed_struct_renamed_field!(Termios, termios, line_discipline, c_line);
check_renamed_struct_renamed_field!(Termios, termios, special_codes, c_cc);
#[cfg(not(any(
linux_kernel,
solarish,
target_os = "emscripten",
target_os = "fuchsia"
)))]
{
check_renamed_struct_renamed_field!(Termios, termios, input_speed, c_ispeed);
check_renamed_struct_renamed_field!(Termios, termios, output_speed, c_ospeed);
}
#[cfg(any(target_env = "musl", target_os = "fuchsia"))]
{
check_renamed_struct_renamed_field!(Termios, termios, input_speed, __c_ispeed);
check_renamed_struct_renamed_field!(Termios, termios, output_speed, __c_ospeed);
}
}
check_renamed_type!(OptionalActions, c_int);
check_renamed_type!(QueueSelector, c_int);
check_renamed_type!(Action, c_int);
}
#[test]
#[cfg(not(any(
solarish,
target_os = "emscripten",
target_os = "haiku",
target_os = "redox"
)))]
fn termios_legacy() {
// Check that our doc aliases above are correct.
const_assert_eq!(c::EXTA, c::B19200);
const_assert_eq!(c::EXTB, c::B38400);
}
#[cfg(bsd)]
#[test]
fn termios_bsd() {
// On BSD platforms we can assume that the `B*` constants have their
// arbitrary integer speed value. Confirm this.
const_assert_eq!(c::B0, 0);
const_assert_eq!(c::B50, 50);
const_assert_eq!(c::B19200, 19200);
const_assert_eq!(c::B38400, 38400);
}
#[test]
#[cfg(not(bsd))]
fn termios_speed_encoding() {
assert_eq!(speed::encode(0), Some(c::B0));
assert_eq!(speed::encode(50), Some(c::B50));
assert_eq!(speed::encode(19200), Some(c::B19200));
assert_eq!(speed::encode(38400), Some(c::B38400));
assert_eq!(speed::encode(1), None);
assert_eq!(speed::encode(!0), None);
#[cfg(not(linux_kernel))]
{
assert_eq!(speed::decode(c::B0), Some(0));
assert_eq!(speed::decode(c::B50), Some(50));
assert_eq!(speed::decode(c::B19200), Some(19200));
assert_eq!(speed::decode(c::B38400), Some(38400));
}
}
#[cfg(linux_kernel)]
#[test]
fn termios_ioctl_contiguity() {
// When using `termios2`, we assume that we can add the optional actions
// value to the ioctl request code. Test this assumption.
const_assert_eq!(c::TCSETS2, c::TCSETS2 + 0);
const_assert_eq!(c::TCSETSW2, c::TCSETS2 + 1);
const_assert_eq!(c::TCSETSF2, c::TCSETS2 + 2);
const_assert_eq!(c::TCSANOW - c::TCSANOW, 0);
const_assert_eq!(c::TCSADRAIN - c::TCSANOW, 1);
const_assert_eq!(c::TCSAFLUSH - c::TCSANOW, 2);
// MIPS is different here.
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
assert_eq!(i128::from(c::TCSANOW) - i128::from(c::TCSETS), 0);
assert_eq!(i128::from(c::TCSADRAIN) - i128::from(c::TCSETS), 1);
assert_eq!(i128::from(c::TCSAFLUSH) - i128::from(c::TCSETS), 2);
}
#[cfg(not(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
)))]
{
const_assert_eq!(c::TCSANOW, 0);
const_assert_eq!(c::TCSADRAIN, 1);
const_assert_eq!(c::TCSAFLUSH, 2);
}
}
#[cfg(linux_kernel)]
#[test]
fn termios_cibaud() {
// Test an assumption.
const_assert_eq!(c::CIBAUD, c::CBAUD << c::IBSHIFT);
}