summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pty.rs68
-rw-r--r--src/sys/termios.rs1384
-rw-r--r--test/sys/test_termios.rs89
-rw-r--r--test/test_pty.rs3
4 files changed, 938 insertions, 606 deletions
diff --git a/src/pty.rs b/src/pty.rs
index c450e058..88c9cc14 100644
--- a/src/pty.rs
+++ b/src/pty.rs
@@ -188,23 +188,57 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
use std::ptr;
- let mut slave: libc::c_int = -1;
- let mut master: libc::c_int = -1;
- let c_termios = match termios.into() {
- Some(termios) => termios as *const Termios,
- None => ptr::null() as *const Termios,
- };
- let c_winsize = match winsize.into() {
- Some(ws) => ws as *const Winsize,
- None => ptr::null() as *const Winsize,
- };
- let ret = unsafe {
- libc::openpty(
- &mut master as *mut libc::c_int,
- &mut slave as *mut libc::c_int,
- ptr::null_mut(),
- c_termios as *mut libc::termios,
- c_winsize as *mut Winsize)
+ let mut slave: libc::c_int = unsafe { mem::uninitialized() };
+ let mut master: libc::c_int = unsafe { mem::uninitialized() };
+ let ret = {
+ match (termios.into(), winsize.into()) {
+ (Some(termios), Some(winsize)) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ winsize as *const Winsize as *mut _,
+ )
+ }
+ }
+ (None, Some(winsize)) => {
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ winsize as *const Winsize as *mut _,
+ )
+ }
+ }
+ (Some(termios), None) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ ptr::null_mut(),
+ )
+ }
+ }
+ (None, None) => {
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ }
+ }
+ }
};
Errno::result(ret)?;
diff --git a/src/sys/termios.rs b/src/sys/termios.rs
index 0f8bd6c9..10adf089 100644
--- a/src/sys/termios.rs
+++ b/src/sys/termios.rs
@@ -1,647 +1,859 @@
+//! An interface for controlling asynchronous communication ports
+//!
+//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
+//! underlying types are all implemented in libc for most platforms and either wrapped in safer
+//! types here or exported directly.
+//!
+//! If you are unfamiliar with the `termios` API, you should first read the
+//! [API documentation](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
+//! then come back to understand how `nix` safely wraps it.
+//!
+//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
+//! As this interface is not used with high-bandwidth information, this should be fine in most
+//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
+//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
+//! This means that when crossing the FFI interface to the underlying C library, data is first
+//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
+//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
+//! relatively small across all platforms (on the order of 32-64 bytes).
+//!
+//! The following examples highlight some of the API use cases such that users coming from using C
+//! or reading the standard documentation will understand how to use the safe API exposed here.
+//!
+//! Example disabling processing of the end-of-file control character:
+//!
+//! ```
+//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
+//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
+//! # let mut termios = unsafe { Termios::default_uninit() };
+//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
+//! ```
+//!
+//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
+//! an interface for working with bitfields that is similar to working with the raw unsigned
+//! integer types but offers type safety because of the internal checking that values will always
+//! be a valid combination of the defined flags.
+//!
+//! An example showing some of the basic operations for interacting with the control flags:
+//!
+//! ```
+//! # use self::nix::sys::termios::{CS5, CSIZE, Termios};
+//! # let mut termios = unsafe { Termios::default_uninit() };
+//! termios.control_flags & CSIZE == CS5;
+//! termios.control_flags |= CS5;
+//! ```
+
use {Errno, Result};
-use libc::c_int;
+use libc::{self, c_int, tcflag_t};
+use std::cell::{Ref, RefCell};
+use std::convert::From;
use std::mem;
use std::os::unix::io::RawFd;
-pub use self::ffi::consts::*;
-pub use self::ffi::consts::SetArg::*;
-pub use self::ffi::consts::FlushArg::*;
-pub use self::ffi::consts::FlowArg::*;
-
-mod ffi {
- pub use self::consts::*;
-
- #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "linux"))]
- mod non_android {
- use super::consts::*;
- use libc::c_int;
-
- // `Termios` contains bitflags which are not considered
- // `foreign-function-safe` by the compiler.
- #[allow(improper_ctypes)]
- extern {
- pub fn cfgetispeed(termios: *const Termios) -> speed_t;
- pub fn cfgetospeed(termios: *const Termios) -> speed_t;
- pub fn cfsetispeed(termios: *mut Termios, speed: speed_t) -> c_int;
- pub fn cfsetospeed(termios: *mut Termios, speed: speed_t) -> c_int;
- pub fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int;
- pub fn tcsetattr(fd: c_int,
- optional_actions: c_int,
- termios: *const Termios) -> c_int;
- pub fn tcdrain(fd: c_int) -> c_int;
- pub fn tcflow(fd: c_int, action: c_int) -> c_int;
- pub fn tcflush(fd: c_int, action: c_int) -> c_int;
- pub fn tcsendbreak(fd: c_int, duration: c_int) -> c_int;
- }
- }
-
- #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "linux"))]
- pub use self::non_android::*;
-
- // On Android before 5.0, Bionic directly inline these to ioctl() calls.
- #[cfg(all(target_os = "android", not(target_arch = "mips")))]
- mod android {
- use libc;
- use libc::c_int;
- use super::consts::*;
-
- const TCGETS: c_int = 0x5401;
- const TCSBRK: c_int = 0x5409;
- const TCXONC: c_int = 0x540a;
- const TCFLSH: c_int = 0x540b;
- const TCSBRKP: c_int = 0x5425;
+/// Stores settings for the termios API
+///
+/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
+/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
+/// an open port using `tcgetattr()`.
+#[derive(Clone)]
+pub struct Termios {
+ inner: RefCell<libc::termios>,
+ /// Input mode flags (see `termios.c_iflag` documentation)
+ pub input_flags: InputFlags,
+ /// Output mode flags (see `termios.c_oflag` documentation)
+ pub output_flags: OutputFlags,
+ /// Control mode flags (see `termios.c_cflag` documentation)
+ pub control_flags: ControlFlags,
+ /// Local mode flags (see `termios.c_lflag` documentation)
+ pub local_flags: LocalFlags,
+ /// Control characters (see `termios.c_cc` documentation)
+ pub control_chars: [libc::cc_t; libc::NCCS],
+}
- pub unsafe fn cfgetispeed(termios: *const Termios) -> speed_t {
- ((*termios).c_cflag & CBAUD).bits() as speed_t
- }
- pub unsafe fn cfgetospeed(termios: *const Termios) -> speed_t {
- ((*termios).c_cflag & CBAUD).bits() as speed_t
- }
- pub unsafe fn cfsetispeed(termios: *mut Termios, speed: speed_t) -> c_int {
- (*termios).c_cflag.remove(CBAUD);
- (*termios).c_cflag.insert(ControlFlags::from_bits_truncate(speed) & CBAUD);
- 0
- }
- pub unsafe fn cfsetospeed(termios: *mut Termios, speed: speed_t) -> c_int {
- (*termios).c_cflag.remove(CBAUD);
- (*termios).c_cflag.insert(ControlFlags::from_bits_truncate(speed) & CBAUD);
- 0
- }
- pub unsafe fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int {
- libc::ioctl(fd, TCGETS, termios)
- }
- pub unsafe fn tcsetattr(fd: c_int,
- optional_actions: c_int,
- termios: *const Termios) -> c_int {
- libc::ioctl(fd, optional_actions, termios)
- }
- pub unsafe fn tcdrain(fd: c_int) -> c_int {
- libc::ioctl(fd, TCSBRK, 1)
- }
- pub unsafe fn tcflow(fd: c_int, action: c_int) -> c_int {
- libc::ioctl(fd, TCXONC, action)
- }
- pub unsafe fn tcflush(fd: c_int, action: c_int) -> c_int {
- libc::ioctl(fd, TCFLSH, action)
- }
- pub unsafe fn tcsendbreak(fd: c_int, duration: c_int) -> c_int {
- libc::ioctl(fd, TCSBRKP, duration)
+impl Termios {
+ /// Exposes an immutable reference to the underlying `libc::termios` data structure.
+ ///
+ /// This can be used for interfacing with other FFI functions like:
+ ///
+ /// ```rust
+ /// # extern crate libc;
+ /// # extern crate nix;
+ /// # fn main() {
+ /// # use nix::sys::termios::Termios;
+ /// # let mut termios = unsafe { Termios::default_uninit() };
+ /// let inner_termios = termios.get_libc_termios();
+ /// unsafe { libc::cfgetispeed(&*inner_termios) };
+ /// # }
+ /// ```
+ ///
+ /// There is no public API exposed for functions that modify the underlying `libc::termios`
+ /// data because it requires additional work to maintain type safety.
+ // FIXME: Switch this over to use pub(crate)
+ #[doc(hidden)]
+ pub fn get_libc_termios(&self) -> Ref<libc::termios> {
+ {
+ let mut termios = self.inner.borrow_mut();
+ termios.c_iflag = self.input_flags.bits();
+ termios.c_oflag = self.output_flags.bits();
+ termios.c_cflag = self.control_flags.bits();
+ termios.c_lflag = self.local_flags.bits();
+ termios.c_cc = self.control_chars;
}
+ self.inner.borrow()
}
- #[cfg(target_os = "android")]
- pub use self::android::*;
-
-
- #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
- pub mod consts {
-
- use libc;
-
- use libc::c_int;
-
- pub type tcflag_t = libc::tcflag_t;
- pub type cc_t = libc::cc_t;
- pub type speed_t = libc::speed_t;
-
- #[repr(C)]
- #[derive(Clone, Copy)]
- pub struct Termios {
- pub c_iflag: InputFlags,
- pub c_oflag: OutputFlags,
- pub c_cflag: ControlFlags,
- pub c_lflag: LocalFlags,
- pub c_cc: [cc_t; NCCS],
- pub c_ispeed: speed_t,
- pub c_ospeed: speed_t
- }
-
- #[derive(Clone, Copy, Debug, PartialEq)]
- pub enum BaudRate {
- B0,
- B50,
- B75,
- B110,
- B134,
- B150,
- B200,
- B300,
- B600,
- B1200,
- B1800,
- B2400,
- B4800,
- B9600,
- B19200,
- B38400,
- B7200,
- B14400,
- B28800,
- B57600,
- B76800,
- B115200,
- B230400,
- #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
- B460800,
- #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
- B921600,
- }
-
- impl From<speed_t> for BaudRate {
- fn from(s: speed_t) -> BaudRate {
-
- use libc::{
- B0, B50, B75, B110, B134, B150,
- B200, B300, B600, B1200, B1800, B2400,
- B4800, B9600, B19200, B38400,
- B7200, B14400, B28800, B57600,
- B76800, B115200, B230400};
-
- #[cfg(target_os = "freebsd")]
- use libc::{B460800, B921600};
-
- match s {
- B0 => BaudRate::B0,
- B50 => BaudRate::B50,
- B75 => BaudRate::B75,
- B110 => BaudRate::B110,
- B134 => BaudRate::B134,
- B150 => BaudRate::B150,
- B200 => BaudRate::B200,
- B300 => BaudRate::B300,
- B600 => BaudRate::B600,
- B1200 => BaudRate::B1200,
- B1800 => BaudRate::B1800,
- B2400 => BaudRate::B2400,
- B4800 => BaudRate::B4800,
- B9600 => BaudRate::B9600,
- B19200 => BaudRate::B19200,
- B38400 => BaudRate::B38400,
- B7200 => BaudRate::B7200,
- B14400 => BaudRate::B14400,
- B28800 => BaudRate::B28800,
- B57600 => BaudRate::B57600,
- B76800 => BaudRate::B76800,
- B115200 => BaudRate::B115200,
- B230400 => BaudRate::B230400,
- #[cfg(target_os = "freebsd")]
- B460800 => BaudRate::B460800,
- #[cfg(target_os = "freebsd")]
- B921600 => BaudRate::B921600,
- b @ _ => unreachable!("Invalid baud constant: {}", b),
- }
- }
- }
-
- pub const VEOF: usize = 0;
- pub const VEOL: usize = 1;
- pub const VEOL2: usize = 2;
- pub const VERASE: usize = 3;
- pub const VWERASE: usize = 4;
- pub const VKILL: usize = 5;
- pub const VREPRINT: usize = 6;
- pub const VINTR: usize = 8;
- pub const VQUIT: usize = 9;
- pub const VSUSP: usize = 10;
- pub const VDSUSP: usize = 11;
- pub const VSTART: usize = 12;
- pub const VSTOP: usize = 13;
- pub const VLNEXT: usize = 14;
- pub const VDISCARD: usize = 15;
- pub const VMIN: usize = 16;
- pub const VTIME: usize = 17;
- pub const VSTATUS: usize = 18;
- pub const NCCS: usize = 20;
-
- bitflags! {
- pub struct InputFlags: tcflag_t {
- const IGNBRK = 0x00000001;
- const BRKINT = 0x00000002;
- const IGNPAR = 0x00000004;
- const PARMRK = 0x00000008;
- const INPCK = 0x00000010;
- const ISTRIP = 0x00000020;
- const INLCR = 0x00000040;
- const IGNCR = 0x00000080;
- const ICRNL = 0x00000100;
- const IXON = 0x00000200;
- const IXOFF = 0x00000400;
- const IXANY = 0x00000800;
- const IMAXBEL = 0x00002000;
- #[cfg(not(target_os = "dragonfly"))]
- const IUTF8 = 0x00004000;
- }
- }
-
- bitflags! {
- pub struct OutputFlags: tcflag_t {
- const OPOST = 0x00000001;
- const ONLCR = 0x00000002;
- const OXTABS = 0x00000004;
- const ONOEOT = 0x00000008;
- }
- }
-
- bitflags! {
- pub struct ControlFlags: tcflag_t {
- const CIGNORE = 0x00000001;
- const CSIZE = 0x00000300;
- const CS5 = 0x00000000;
- const CS6 = 0x00000100;
- const CS7 = 0x00000200;
- const CS8 = 0x00000300;
- const CSTOPB = 0x00000400;
- const CREAD = 0x00000800;
- const PARENB = 0x00001000;
- const PARODD = 0x00002000;
- const HUPCL = 0x00004000;
- const CLOCAL = 0x00008000;
- const CCTS_OFLOW = 0x00010000;
- const CRTSCTS = 0x00030000;
- const CRTS_IFLOW = 0x00020000;
- const CDTR_IFLOW = 0x00040000;
- const CDSR_OFLOW = 0x00080000;
- const CCAR_OFLOW = 0x00100000;
- const MDMBUF = 0x00100000;
- }
+ /// Exposes the inner `libc::termios` datastore within `Termios`.
+ ///
+ /// This is unsafe because if this is used to modify the inner libc::termios struct, it will not
+ /// automatically update the safe wrapper type around it. Therefore we disable docs to
+ /// effectively limit its use to nix internals. In this case it should also be paired with a
+ /// call to `update_wrapper()` so that the wrapper-type and internal representation stay
+ /// consistent.
+ unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
+ {
+ let mut termios = self.inner.borrow_mut();
+ termios.c_iflag = self.input_flags.bits();
+ termios.c_oflag = self.output_flags.bits();
+ termios.c_cflag = self.control_flags.bits();
+ termios.c_lflag = self.local_flags.bits();
+ termios.c_cc = self.control_chars;
}
+ self.inner.as_ptr()
+ }
- bitflags! {
- pub struct LocalFlags: tcflag_t {
- const ECHOKE = 0x00000001;
- const ECHOE = 0x00000002;
- const ECHOK = 0x00000004;
- const ECHO = 0x00000008;
- const ECHONL = 0x00000010;
- const ECHOPRT = 0x00000020;
- const ECHOCTL = 0x00000040;
- const ISIG = 0x00000080;
- const ICANON = 0x00000100;
- const ALTWERASE = 0x00000200;
- const IEXTEN = 0x00000400;
- const EXTPROC = 0x00000800;
- const TOSTOP = 0x00400000;
- const FLUSHO = 0x00800000;
- const NOKERNINFO = 0x02000000;
- const PENDIN = 0x20000000;
- const NOFLSH = 0x80000000;
- }
+ /// Allows for easily creating new Termios structs that will be overwritten with real data.
+ ///
+ /// This should only be used when the inner libc::termios struct will be overwritten before it's
+ /// read.
+ // FIXME: Switch this over to use pub(crate)
+ #[doc(hidden)]
+ pub unsafe fn default_uninit() -> Self {
+ Termios {
+ inner: RefCell::new(mem::uninitialized()),
+ input_flags: InputFlags::empty(),
+ output_flags: OutputFlags::empty(),
+ control_flags: ControlFlags::empty(),
+ local_flags: LocalFlags::empty(),
+ control_chars: [0 as libc::cc_t; NCCS],
}
+ }
- pub const NL0: c_int = 0x00000000;
- pub const NL1: c_int = 0x00000100;
- pub const NL2: c_int = 0x00000200;
- pub const NL3: c_int = 0x00000300;
- pub const TAB0: c_int = 0x00000000;
- pub const TAB1: c_int = 0x00000400;
- pub const TAB2: c_int = 0x00000800;
- pub const TAB3: c_int = 0x00000004;
- pub const CR0: c_int = 0x00000000;
- pub const CR1: c_int = 0x00001000;
- pub const CR2: c_int = 0x00002000;
- pub const CR3: c_int = 0x00003000;
- pub const FF0: c_int = 0x00000000;
- pub const FF1: c_int = 0x00004000;
- pub const BS0: c_int = 0x00000000;
- pub const BS1: c_int = 0x00008000;
- pub const VT0: c_int = 0x00000000;
- pub const VT1: c_int = 0x00010000;
-
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum SetArg {
- TCSANOW = 0,
- TCSADRAIN = 1,
- TCSAFLUSH = 2,
- TCSASOFT = 16,
- }
+ /// Updates the wrapper values from the internal `libc::termios` data structure.
+ #[doc(hidden)]
+ pub fn update_wrapper(&mut self) {
+ let termios = *self.inner.borrow_mut();
+ self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
+ self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
+ self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
+ self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
+ self.control_chars = termios.c_cc;
+ }
+}
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum FlushArg {
- TCIFLUSH = 1,
- TCOFLUSH = 2,
- TCIOFLUSH = 3,
+impl From<libc::termios> for Termios {
+ fn from(termios: libc::termios) -> Self {
+ Termios {
+ inner: RefCell::new(termios),
+ input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
+ output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
+ control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
+ local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
+ control_chars: termios.c_cc,
}
+ }
+}
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum FlowArg {
- TCOOFF = 1,
- TCOON = 2,
- TCIOFF = 3,
- TCION = 4,
- }
+impl From<Termios> for libc::termios {
+ fn from(termios: Termios) -> Self {
+ termios.inner.into_inner()
}
+}
- #[cfg(any(target_os = "linux", target_os = "android"))]
- pub mod consts {
- use libc::{c_int, c_uint, c_uchar};
-
- pub type tcflag_t = c_uint;
- pub type cc_t = c_uchar;
- pub type speed_t = c_uint;
-
- #[repr(C)]
- #[derive(Clone, Copy)]
- pub struct Termios {
- pub c_iflag: InputFlags,
- pub c_oflag: OutputFlags,
- pub c_cflag: ControlFlags,
- pub c_lflag: LocalFlags,
- pub c_line: cc_t,
- pub c_cc: [cc_t; NCCS],
- pub c_ispeed: speed_t,
- pub c_ospeed: speed_t
- }
+libc_enum!{
+ /// Baud rates supported by the system
+ ///
+ /// B0 is special and will disable the port.
+ #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
+ #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))]
+ pub enum BaudRate {
+ B0,
+ B50,
+ B75,
+ B110,
+ B134,
+ B150,
+ B200,
+ B300,
+ B600,
+ B1200,
+ B1800,
+ B2400,
+ B4800,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B7200,
+ B9600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B14400,
+ B19200,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B28800,
+ B38400,
+ B57600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B76800,
+ B115200,
+ B230400,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ taget_os = "netbsd"))]
+ B460800,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B576000,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ taget_os = "netbsd"))]
+ B921600,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1152000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B2000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B2500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B3000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B3500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B4000000,
+ }
+}
- #[derive(Clone, Copy, Debug, PartialEq)]
- pub enum BaudRate {
- B0,
- B50,
- B75,
- B110,
- B134,
- B150,
- B200,
- B300,
- B600,
- B1200,
- B1800,
- B2400,
- B4800,
- B9600,
- B19200,
- B38400,
- B57600,
- B115200,
- B230400,
- B460800,
- B500000,
- B576000,
- B921600,
- B1000000,
- B1152000,
- B1500000,
- B2000000,
- B2500000,
- B3000000,
- B3500000,
- B4000000,
+impl From<libc::speed_t> for BaudRate {
+ fn from(s: libc::speed_t) -> BaudRate {
+
+ use libc::{B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800,
+ B9600, B19200, B38400, B57600, B115200, B230400};
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ use libc::{B500000, B576000, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000,
+ B3500000, B4000000};
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ use libc::{B7200, B14400, B28800, B76800};
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd"))]
+ use libc::{B460800, B921600};
+
+ match s {
+ B0 => BaudRate::B0,
+ B50 => BaudRate::B50,
+ B75 => BaudRate::B75,
+ B110 => BaudRate::B110,
+ B134 => BaudRate::B134,
+ B150 => BaudRate::B150,
+ B200 => BaudRate::B200,
+ B300 => BaudRate::B300,
+ B600 => BaudRate::B600,
+ B1200 => BaudRate::B1200,
+ B1800 => BaudRate::B1800,
+ B2400 => BaudRate::B2400,
+ B4800 => BaudRate::B4800,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B7200 => BaudRate::B7200,
+ B9600 => BaudRate::B9600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B14400 => BaudRate::B14400,
+ B19200 => BaudRate::B19200,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B28800 => BaudRate::B28800,
+ B38400 => BaudRate::B38400,
+ B57600 => BaudRate::B57600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B76800 => BaudRate::B76800,
+ B115200 => BaudRate::B115200,
+ B230400 => BaudRate::B230400,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ taget_os = "netbsd"))]
+ B460800 => BaudRate::B460800,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B500000 => BaudRate::B500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B576000 => BaudRate::B576000,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ taget_os = "netbsd"))]
+ B921600 => BaudRate::B921600,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1000000 => BaudRate::B1000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1152000 => BaudRate::B1152000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1500000 => BaudRate::B1500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B2000000 => BaudRate::B2000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B2500000 => BaudRate::B2500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B3000000 => BaudRate::B3000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B3500000 => BaudRate::B3500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B4000000 => BaudRate::B4000000,
+ b @ _ => unreachable!("Invalid baud constant: {}", b),
}
+ }
+}
- impl From<speed_t> for BaudRate {
- fn from(s: speed_t) -> BaudRate {
-
- use libc::{
- B0, B50, B75, B110, B134, B150,
- B200, B300, B600, B1200, B1800, B2400,
- B4800, B9600, B19200, B38400, B57600,
- B115200, B230400, B460800, B500000,
- B576000, B921600, B1000000, B1152000,
- B1500000, B2000000, B2500000, B3000000,
- B3500000, B4000000};
-
- match s {
- B0 => BaudRate::B0,
- B50 => BaudRate::B50,
- B75 => BaudRate::B75,
- B110 => BaudRate::B110,
- B134 => BaudRate::B134,
- B150 => BaudRate::B150,
- B200 => BaudRate::B200,
- B300 => BaudRate::B300,
- B600 => BaudRate::B600,
- B1200 => BaudRate::B1200,
- B1800 => BaudRate::B1800,
- B2400 => BaudRate::B2400,
- B4800 => BaudRate::B4800,
- B9600 => BaudRate::B9600,
- B19200 => BaudRate::B19200,
- B38400 => BaudRate::B38400,
- B57600 => BaudRate::B57600,
- B115200 => BaudRate::B115200,
- B230400 => BaudRate::B230400,
- B460800 => BaudRate::B460800,
- B500000 => BaudRate::B500000,
- B576000 => BaudRate::B576000,
- B921600 => BaudRate::B921600,
- B1000000 => BaudRate::B1000000,
- B1152000 => BaudRate::B1152000,
- B1500000 => BaudRate::B1500000,
- B2000000 => BaudRate::B2000000,
- B2500000 => BaudRate::B2500000,
- B3000000 => BaudRate::B3000000,
- B3500000 => BaudRate::B3500000,
- B4000000 => BaudRate::B4000000,
- b @ _ => unreachable!("Invalid baud constant: {}", b),
- }
- }
- }
+// TODO: Add TCSASOFT, which will require treating this as a bitfield.
+libc_enum! {
+ /// Specify when a port configuration change should occur.
+ ///
+ /// Used as an argument to `tcsetattr()`
+ #[repr(i32)]
+ pub enum SetArg {
+ /// The change will occur immediately
+ TCSANOW,
+ /// The change occurs after all output has been written
+ TCSADRAIN,
+ /// Same as `TCSADRAIN`, but will also flush the input buffer
+ TCSAFLUSH,
+ }
+}
- pub const VEOF: usize = 4;
- pub const VEOL: usize = 11;
- pub const VEOL2: usize = 16;
- pub const VERASE: usize = 2;
- pub const VWERASE: usize = 14;
- pub const VKILL: usize = 3;
- pub const VREPRINT: usize = 12;
- pub const VINTR: usize = 0;
- pub const VQUIT: usize = 1;
- pub const VSUSP: usize = 10;
- pub const VSTART: usize = 8;
- pub const VSTOP: usize = 9;
- pub const VLNEXT: usize = 15;
- pub const VDISCARD: usize = 13;
- pub const VMIN: usize = 6;
- pub const VTIME: usize = 5;
- pub const NCCS: usize = 32;
-
- bitflags! {
- pub struct InputFlags: tcflag_t {
- const IGNBRK = 0x00000001;
- const BRKINT = 0x00000002;
- const IGNPAR = 0x00000004;
- const PARMRK = 0x00000008;
- const INPCK = 0x00000010;
- const ISTRIP = 0x00000020;
- const INLCR = 0x00000040;
- const IGNCR = 0x00000080;
- const ICRNL = 0x00000100;
- const IXON = 0x00000400;
- const IXOFF = 0x00001000;
- const IXANY = 0x00000800;
- const IMAXBEL = 0x00002000;
- const IUTF8 = 0x00004000;
- }
- }
+libc_enum! {
+ /// Specify a combination of the input and output buffers to flush
+ ///
+ /// Used as an argument to `tcflush()`.
+ #[repr(i32)]
+ pub enum FlushArg {
+ /// Flush data that was received but not read
+ TCIFLUSH,
+ /// Flush data written but not transmitted
+ TCOFLUSH,
+ /// Flush both received data not read and written data not transmitted
+ TCIOFLUSH,
+ }
+}
- bitflags! {
- pub struct OutputFlags: tcflag_t {
- const OPOST = 0x00000001;
- const ONLCR = 0x00000004;
- }
- }
+libc_enum! {
+ /// Specify how transmission flow should be altered
+ ///
+ /// Used as an argument to `tcflow()`.
+ #[repr(i32)]
+ pub enum FlowArg {
+ /// Suspend transmission
+ TCOOFF,
+ /// Resume transmission
+ TCOON,
+ /// Transmit a STOP character, which should disable a connected terminal device
+ TCIOFF,
+ /// Transmit a START character, which should re-enable a connected terminal device
+ TCION,
+ }
+}
- bitflags! {
- pub struct ControlFlags: tcflag_t {
- const CSIZE = 0x00000030;
- const CS5 = 0x00000000;
- const CS6 = 0x00000010;
- const CS7 = 0x00000020;
- const CS8 = 0x00000030;
- const CSTOPB = 0x00000040;
- const CREAD = 0x00000080;
- const PARENB = 0x00000100;
- const PARODD = 0x00000200;
- const HUPCL = 0x00000400;
- const CLOCAL = 0x00000800;
- const CRTSCTS = 0x80000000;
- #[cfg(target_os = "android")]
- const CBAUD = 0o0010017;
- }
- }
+// TODO: Make this usable directly as a slice index.
+libc_enum! {
+ /// Indices into the `termios.c_cc` array for special characters.
+ #[repr(usize)]
+ pub enum SpecialCharacterIndices {
+ VDISCARD,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ VDSUSP,
+ VEOF,
+ VEOL,
+ VEOL2,
+ VERASE,
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ VERASE2,
+ VINTR,
+ VKILL,
+ VLNEXT,
+ VMIN,
+ VQUIT,
+ VREPRINT,
+ VSTART,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ VSTATUS,
+ VSTOP,
+ VSUSP,
+ #[cfg(target_os = "linux")]
+ VSWTC,
+ #[cfg(target_os = "haiku")]
+ VSWTCH,
+ VTIME,
+ VWERASE,
+ #[cfg(target_os = "dragonfly")]
+ VCHECKPT,
+ }
+}
- bitflags! {
- pub struct LocalFlags: tcflag_t {
- const ECHOKE = 0x00000800;
- const ECHOE = 0x00000010;
- const ECHOK = 0x00000020;
- const ECHO = 0x00000008;
- const ECHONL = 0x00000040;
- const ECHOPRT = 0x00000400;
- const ECHOCTL = 0x00000200;
- const ISIG = 0x00000001;
- const ICANON = 0x00000002;
- const IEXTEN = 0x00008000;
- const EXTPROC = 0x00010000;
- const TOSTOP = 0x00000100;
- const FLUSHO = 0x00001000;
- const PENDIN = 0x00004000;
- const NOFLSH = 0x00000080;
- }
- }
+pub use libc::NCCS;
+#[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+pub use libc::_POSIX_VDISABLE;
+
+libc_bitflags! {
+ /// Flags for configuring the input mode of a terminal
+ pub flags InputFlags: tcflag_t {
+ IGNBRK,
+ BRKINT,
+ IGNPAR,
+ PARMRK,
+ INPCK,
+ ISTRIP,
+ INLCR,
+ IGNCR,
+ ICRNL,
+ IXON,
+ IXOFF,
+ IXANY,
+ IMAXBEL,
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ IUTF8,
+ }
+}
- pub const NL0: c_int = 0x00000000;
- pub const NL1: c_int = 0x00000100;
- pub const TAB0: c_int = 0x00000000;
- pub const TAB1: c_int = 0x00000800;
- pub const TAB2: c_int = 0x00001000;
- pub const TAB3: c_int = 0x00001800;
- pub const CR0: c_int = 0x00000000;
- pub const CR1: c_int = 0x00000200;
- pub const CR2: c_int = 0x00000400;
- pub const CR3: c_int = 0x00000600;
- pub const FF0: c_int = 0x00000000;
- pub const FF1: c_int = 0x00008000;
- pub const BS0: c_int = 0x00000000;
- pub const BS1: c_int = 0x00002000;
- pub const VT0: c_int = 0x00000000;
- pub const VT1: c_int = 0x00004000;
-
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum SetArg {
- TCSANOW = 0,
- TCSADRAIN = 1,
- TCSAFLUSH = 2,
- }
+libc_bitflags! {
+ /// Flags for configuring the output mode of a terminal
+ pub flags OutputFlags: tcflag_t {
+ OPOST,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "linux",
+ target_os = "openbsd"))]
+ OLCUC,
+ ONLCR,
+ OCRNL as tcflag_t,
+ ONOCR as tcflag_t,
+ ONLRET as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ OFILL as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ OFDEL as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ NL0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ NL1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CR0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CR1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CR2 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CR3 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TAB0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TAB1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TAB2 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TAB3 as tcflag_t,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ XTABS,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ BS0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ BS1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ VT0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ VT1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ FF0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ FF1 as tcflag_t,
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ OXTABS,
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ ONOEOT as tcflag_t,
+
+ // Bitmasks for use with OutputFlags to select specific settings
+ // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+ // is resolved.
+
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ NLDLY as tcflag_t, // FIXME: Datatype needs to be corrected in libc for mac
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CRDLY as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TABDLY as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ BSDLY as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ VTDLY as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ FFDLY as tcflag_t,
+ }
+}
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum FlushArg {
- TCIFLUSH = 0,
- TCOFLUSH = 1,
- TCIOFLUSH = 2,
- }
+libc_bitflags! {
+ /// Flags for setting the control mode of a terminal
+ pub flags ControlFlags: tcflag_t {
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ CIGNORE,
+ CS5,
+ CS6,
+ CS7,
+ CS8,
+ CSTOPB,
+ CREAD,
+ PARENB,
+ PARODD,
+ HUPCL,
+ CLOCAL,
+ CRTSCTS,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ CBAUD,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
+ CMSPAR,
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux",
+ not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
+ CIBAUD,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ CBAUDEX,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ MDMBUF,
+ #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+ CHWFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ CCTS_OFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ CRTS_IFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ CDTR_IFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ CDSR_OFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ CCAR_OFLOW,
+
+ // Bitmasks for use with ControlFlags to select specific settings
+ // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+ // is resolved.
+
+ CSIZE,
+ }
+}
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum FlowArg {
- TCOOFF = 0,
- TCOON = 1,
- TCIOFF = 2,
- TCION = 3,
- }
+libc_bitflags! {
+ /// Flags for setting any local modes
+ pub flags LocalFlags: tcflag_t {
+ ECHOKE,
+ ECHOE,
+ ECHOK,
+ ECHO,
+ ECHONL,
+ ECHOPRT,
+ ECHOCTL,
+ ISIG,
+ ICANON,
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ ALTWERASE,
+ IEXTEN,
+ EXTPROC,
+ TOSTOP,
+ FLUSHO,
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ NOKERNINFO,
+ PENDIN,
+ NOFLSH,
}
}
+/// Get input baud rate (see
+/// [cfgetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+///
+/// `cfgetispeed()` extracts the input baud rate from the given Termios structure.
pub fn cfgetispeed(termios: &Termios) -> BaudRate {
- unsafe {
- ffi::cfgetispeed(termios).into()
- }
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetispeed(&*inner_termios) }.into()
}
+/// Get output baud rate (see
+/// [cfgetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+///
+/// `cfgetospeed()` extracts the output baud rate from the given Termios structure.
pub fn cfgetospeed(termios: &Termios) -> BaudRate {
- unsafe {
- ffi::cfgetospeed(termios).into()
- }
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetospeed(&*inner_termios) }.into()
}
+/// Set input baud rate (see
+/// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+///
+/// `cfsetispeed()` sets the intput baud rate in the given Termios structure.
pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
- Errno::result(unsafe {
- ffi::cfsetispeed(termios, baud as speed_t)
- }).map(drop)
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
}
+/// Set output baud rate (see
+/// [cfsetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+///
+/// `cfsetospeed()` sets the output baud rate in the given termios structure.
pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
- Errno::result(unsafe {
- ffi::cfsetospeed(termios, baud as speed_t)
- }).map(drop)
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
}
+/// Return the configuration of a port
+/// [tcgetattr(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
+///
+/// `tcgetattr()` returns a Termios structure with the current configuration for a port. Modifying
+/// this structure *will not* reconfigure the port, instead the modifications should be done to
+/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
- let mut termios = unsafe { mem::uninitialized() };
+ let mut termios: libc::termios = unsafe { mem::uninitialized() };
- let res = unsafe {
- ffi::tcgetattr(fd, &mut termios)
- };
+ let res = unsafe { libc::tcgetattr(fd, &mut termios) };
try!(Errno::result(res));
- Ok(termios)
+ Ok(termios.into())
}
-pub fn tcsetattr(fd: RawFd,
- actions: SetArg,
- termios: &Termios) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcsetattr(fd, actions as c_int, termios)
- }).map(drop)
+/// Set the configuration for a terminal (see
+/// [tcsetattr(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
+///
+/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
+/// takes affect at a time specified by `actions`. Note that this function may return success if
+/// *any* of the parameters were successfully set, not only if all were set successfully.
+pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
+ let inner_termios = termios.get_libc_termios();
+ Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
}
+/// Block until all output data is written (see
+/// [tcdrain(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
pub fn tcdrain(fd: RawFd) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcdrain(fd)
- }).map(drop)
+ Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
}
+/// Suspend or resume the transmission or reception of data (see
+/// [tcflow(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
+///
+/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
+/// depending on the value of `action`.
pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcflow(fd, action as c_int)
- }).map(drop)
+ Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop)
}
+/// Discard data in the output or input queue (see
+/// [tcflush(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
+///
+/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
+/// depending on the value of `action`.
pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcflush(fd, action as c_int)
- }).map(drop)
+ Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop)
}
-pub fn tcsendbreak(fd: RawFd, action: c_int) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcsendbreak(fd, action)
- }).map(drop)
+/// Send a break for a specific duration (see
+/// [tcsendbreak(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
+///
+/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
+/// of zero-valued bits for an implementation-defined duration.
+pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
+ Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
}
diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs
index a41304d7..0a2d1855 100644
--- a/test/sys/test_termios.rs
+++ b/test/sys/test_termios.rs
@@ -1,6 +1,18 @@
+use std::os::unix::prelude::*;
+
+use nix::{Error, fcntl, unistd};
use nix::errno::Errno;
-use nix::sys::termios;
-use nix::{Error, unistd};
+use nix::pty::openpty;
+use nix::sys::termios::{self, ECHO, OPOST, OCRNL, tcgetattr};
+use nix::unistd::{read, write, close};
+
+/// Helper function analogous to std::io::Write::write_all, but for `RawFd`s
+fn write_all(f: RawFd, buf: &[u8]) {
+ let mut len = 0;
+ while len < buf.len() {
+ len += write(f, &buf[len..]).unwrap();
+ }
+}
#[test]
fn test_tcgetattr() {
@@ -19,3 +31,76 @@ fn test_tcgetattr() {
}
}
}
+
+// Test modifying output flags
+#[test]
+fn test_output_flags() {
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).unwrap();
+ assert!(pty.master > 0);
+ assert!(pty.slave > 0);
+ let termios = tcgetattr(pty.master).unwrap();
+ termios
+ };
+
+ // Make sure postprocessing '\r' isn't specified by default or this test is useless.
+ assert!(!termios.output_flags.contains(OPOST | OCRNL));
+
+ // Specify that '\r' characters should be transformed to '\n'
+ // OPOST is specified to enable post-processing
+ termios.output_flags.insert(OPOST | OCRNL);
+
+ // Open a pty
+ let pty = openpty(None, &termios).unwrap();
+ assert!(pty.master > 0);
+ assert!(pty.slave > 0);
+
+ // Write into the master
+ let string = "foofoofoo\r";
+ write_all(pty.master, string.as_bytes());
+
+ // Read from the slave verifying that the output has been properly transformed
+ let mut buf = [0u8; 10];
+ ::read_exact(pty.slave, &mut buf);
+ let transformed_string = "foofoofoo\n";
+ assert_eq!(&buf, transformed_string.as_bytes());
+}
+
+// Test modifying local flags
+#[test]
+fn test_local_flags() {
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).unwrap();
+ assert!(pty.master > 0);
+ assert!(pty.slave > 0);
+ let termios = tcgetattr(pty.master).unwrap();
+ termios
+ };
+
+ // Make sure echo is specified by default or this test is useless.
+ assert!(termios.local_flags.contains(ECHO));
+
+ // Disable local echo
+ termios.local_flags.remove(ECHO);
+
+ // Open a new pty with our modified termios settings
+ let pty = openpty(None, &termios).unwrap();
+ assert!(pty.master > 0);
+ assert!(pty.slave > 0);
+
+ // Set the master is in nonblocking mode or reading will never return.
+ let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap();
+ let new_flags = fcntl::OFlag::from_bits(flags).unwrap() | fcntl::O_NONBLOCK;
+ fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap();
+
+ // Write into the master
+ let string = "foofoofoo\r";
+ write_all(pty.master, string.as_bytes());
+
+ // Try to read from the master, which should not have anything as echoing was disabled.
+ let mut buf = [0u8; 10];
+ let read = read(pty.master, &mut buf).unwrap_err();
+ assert_eq!(read, Error::Sys(Errno::EAGAIN));
+}
diff --git a/test/test_pty.rs b/test/test_pty.rs
index 8528caf7..53e94724 100644
--- a/test/test_pty.rs
+++ b/test/test_pty.rs
@@ -139,7 +139,8 @@ fn test_openpty_with_termios() {
close(pty.slave).unwrap();
termios
};
- termios.c_oflag &= !ONLCR;
+ // Make sure newlines are not transformed so the data is preserved when sent.
+ termios.output_flags.remove(ONLCR);
let pty = openpty(None, &termios).unwrap();
// Must be valid file descriptors