From cd455e835842831125df0ca23507384f5ae06c8b Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jackpot51@gmail.com>
Date: Mon, 31 Jul 2017 21:17:47 -0600
Subject: [PATCH] Move system specific features into sys module

---
 src/async.rs               |  4 +-
 src/lib.rs                 | 46 ++++++++++++++-------
 src/raw.rs                 | 65 +++++++----------------------
 src/size.rs                | 85 --------------------------------------
 src/sys/redox/attr.rs      | 33 +++++++++++++++
 src/sys/redox/mod.rs       | 15 +++++++
 src/sys/redox/size.rs      | 18 ++++++++
 src/{ => sys/redox}/tty.rs | 23 +----------
 src/sys/unix/attr.rs       | 29 +++++++++++++
 src/sys/unix/mod.rs        | 33 +++++++++++++++
 src/sys/unix/size.rs       | 48 +++++++++++++++++++++
 src/sys/unix/tty.rs        | 17 ++++++++
 src/termios.rs             | 45 --------------------
 13 files changed, 244 insertions(+), 217 deletions(-)
 delete mode 100644 src/size.rs
 create mode 100644 src/sys/redox/attr.rs
 create mode 100644 src/sys/redox/mod.rs
 create mode 100644 src/sys/redox/size.rs
 rename src/{ => sys/redox}/tty.rs (51%)
 create mode 100644 src/sys/unix/attr.rs
 create mode 100644 src/sys/unix/mod.rs
 create mode 100644 src/sys/unix/size.rs
 create mode 100644 src/sys/unix/tty.rs
 delete mode 100644 src/termios.rs

diff --git a/src/async.rs b/src/async.rs
index 6255b78d..f58b0444 100644
--- a/src/async.rs
+++ b/src/async.rs
@@ -2,7 +2,7 @@ use std::io::{self, Read};
 use std::sync::mpsc;
 use std::thread;
 
-use tty;
+use sys::tty::get_tty;
 
 /// Construct an asynchronous handle to the TTY standard input.
 ///
@@ -17,7 +17,7 @@ use tty;
 pub fn async_stdin() -> AsyncReader {
     let (send, recv) = mpsc::channel();
 
-    thread::spawn(move || for i in tty::get_tty().unwrap().bytes() {
+    thread::spawn(move || for i in get_tty().unwrap().bytes() {
                       if send.send(i).is_err() {
                           return;
                       }
diff --git a/src/lib.rs b/src/lib.rs
index 8dd4d2fa..b284eddd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,27 +11,20 @@
 //! For more information refer to the [README](https://github.com/ticki/termion).
 #![warn(missing_docs)]
 
-#[cfg(not(target_os = "redox"))]
-extern crate libc;
-
-#[cfg(not(target_os = "redox"))]
-mod termios;
-
 #[cfg(target_os = "redox")]
-extern crate redox_termios;
+#[path="sys/redox/mod.rs"]
+mod sys;
 
-#[cfg(target_os = "redox")]
-extern crate syscall;
+#[cfg(unix)]
+#[path="sys/unix/mod.rs"]
+mod sys;
+
+pub use sys::size::terminal_size;
+pub use sys::tty::{is_tty, get_tty};
 
 mod async;
 pub use async::{AsyncReader, async_stdin};
 
-mod size;
-pub use size::terminal_size;
-
-mod tty;
-pub use tty::{is_tty, get_tty};
-
 #[macro_use]
 mod macros;
 pub mod clear;
@@ -43,3 +36,26 @@ pub mod raw;
 pub mod screen;
 pub mod scroll;
 pub mod style;
+
+#[cfg(test)]
+mod test {
+    use super::sys;
+
+    #[test]
+    fn test_get_terminal_attr() {
+        sys::attr::get_terminal_attr().unwrap();
+        sys::attr::get_terminal_attr().unwrap();
+        sys::attr::get_terminal_attr().unwrap();
+    }
+
+    #[test]
+    fn test_set_terminal_attr() {
+        let ios = sys::attr::get_terminal_attr().unwrap();
+        sys::attr::set_terminal_attr(&ios).unwrap();
+    }
+
+    #[test]
+    fn test_size() {
+        sys::size::terminal_size().unwrap();
+    }
+}
diff --git a/src/raw.rs b/src/raw.rs
index bd9c8ec0..5421d565 100644
--- a/src/raw.rs
+++ b/src/raw.rs
@@ -25,6 +25,9 @@
 use std::io::{self, Write};
 use std::ops;
 
+use sys::Termios;
+use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr};
+
 /// The timeout of an escape code control sequence, in milliseconds.
 pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
 
@@ -32,34 +35,14 @@ pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
 /// dropped.
 ///
 /// Restoring will entirely bring back the old TTY state.
-#[cfg(target_os = "redox")]
-pub struct RawTerminal<W: Write> {
-    output: W,
-}
-
-#[cfg(target_os = "redox")]
-impl<W: Write> Drop for RawTerminal<W> {
-    fn drop(&mut self) {
-        let _ = write!(self, csi!("?82l"));
-        let _ = self.flush();
-    }
-}
-
-#[cfg(not(target_os = "redox"))]
-use termios::Termios;
-/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
-/// dropped.
-#[cfg(not(target_os = "redox"))]
 pub struct RawTerminal<W: Write> {
     prev_ios: Termios,
     output: W,
 }
 
-#[cfg(not(target_os = "redox"))]
 impl<W: Write> Drop for RawTerminal<W> {
     fn drop(&mut self) {
-        use termios::set_terminal_attr;
-        set_terminal_attr(&mut self.prev_ios as *mut _);
+        set_terminal_attr(&self.prev_ios).unwrap();
     }
 }
 
@@ -103,36 +86,18 @@ pub trait IntoRawMode: Write + Sized {
 }
 
 impl<W: Write> IntoRawMode for W {
-    #[cfg(not(target_os = "redox"))]
     fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
-        use termios::{cfmakeraw, get_terminal_attr, set_terminal_attr};
-
-        let (mut ios, exit) = get_terminal_attr();
+        let mut ios = get_terminal_attr()?;
         let prev_ios = ios;
-        if exit != 0 {
-            return Err(io::Error::new(io::ErrorKind::Other, "Unable to get Termios attribute."));
-        }
-
-        unsafe {
-            cfmakeraw(&mut ios);
-        }
-
-        if set_terminal_attr(&mut ios as *mut _) != 0 {
-            Err(io::Error::new(io::ErrorKind::Other, "Unable to set Termios attribute."))
-        } else {
-            let res = RawTerminal {
-                prev_ios: prev_ios,
-                output: self,
-            };
-            Ok(res)
-        }
-    }
 
-    #[cfg(target_os = "redox")]
-    fn into_raw_mode(mut self) -> io::Result<RawTerminal<W>> {
-        write!(self, csi!("?82h"))?;
-        self.flush()?;
-        Ok(RawTerminal { output: self })
+        raw_terminal_attr(&mut ios);
+
+        set_terminal_attr(&ios)?;
+
+        Ok(RawTerminal {
+            prev_ios: prev_ios,
+            output: self,
+        })
     }
 }
 
@@ -145,6 +110,8 @@ mod test {
     fn test_into_raw_mode() {
         let mut out = stdout().into_raw_mode().unwrap();
 
-        out.write_all(b"this is a test, muahhahahah").unwrap();
+        out.write_all(b"this is a test, muahhahahah\r\n").unwrap();
+
+        drop(out);
     }
 }
diff --git a/src/size.rs b/src/size.rs
deleted file mode 100644
index 3317ce52..00000000
--- a/src/size.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use std::io;
-
-#[cfg(not(target_os = "redox"))]
-use libc::c_ushort;
-
-#[cfg(not(target_os = "redox"))]
-#[repr(C)]
-struct TermSize {
-    row: c_ushort,
-    col: c_ushort,
-    _x: c_ushort,
-    _y: c_ushort,
-}
-
-// Since attributes on non-item statements is not stable yet, we use a function.
-#[cfg(not(target_os = "android"))]
-#[cfg(not(target_os = "redox"))]
-#[cfg(target_pointer_width = "64")]
-#[cfg(not(target_env = "musl"))]
-fn tiocgwinsz() -> u64 {
-    use termios::TIOCGWINSZ;
-    TIOCGWINSZ as u64
-}
-#[cfg(not(target_os = "android"))]
-#[cfg(not(target_os = "redox"))]
-#[cfg(target_pointer_width = "32")]
-#[cfg(not(target_env = "musl"))]
-fn tiocgwinsz() -> u32 {
-    use termios::TIOCGWINSZ;
-    TIOCGWINSZ as u32
-}
-
-#[cfg(any(target_env = "musl", target_os = "android"))]
-fn tiocgwinsz() -> i32 {
-    use termios::TIOCGWINSZ;
-    TIOCGWINSZ as i32
-}
-
-/// Get the size of the terminal.
-#[cfg(not(target_os = "redox"))]
-pub fn terminal_size() -> io::Result<(u16, u16)> {
-    use libc::ioctl;
-    use libc::STDOUT_FILENO;
-
-    use std::mem;
-    unsafe {
-        let mut size: TermSize = mem::zeroed();
-
-        if ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _) == 0 {
-            Ok((size.col as u16, size.row as u16))
-        } else {
-            Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
-        }
-    }
-}
-
-/// Get the size of the terminal.
-#[cfg(target_os = "redox")]
-pub fn terminal_size() -> io::Result<(u16, u16)> {
-    use redox_termios;
-    use syscall;
-
-    if let Ok(fd) = syscall::dup(1, b"winsize") {
-        let mut winsize = redox_termios::Winsize::default();
-        let res = syscall::read(fd, &mut winsize);
-        let _ = syscall::close(fd);
-        if let Ok(count) = res {
-            if count == winsize.len() {
-                return Ok((winsize.ws_col, winsize.ws_row));
-            }
-        }
-    }
-
-    Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_size() {
-        assert!(terminal_size().is_ok());
-    }
-}
diff --git a/src/sys/redox/attr.rs b/src/sys/redox/attr.rs
new file mode 100644
index 00000000..c6489a51
--- /dev/null
+++ b/src/sys/redox/attr.rs
@@ -0,0 +1,33 @@
+use std::io;
+
+use super::{cvt, syscall, Termios};
+
+pub fn get_terminal_attr() -> io::Result<Termios> {
+    let mut termios = Termios::default();
+
+    let fd = cvt(syscall::dup(0, b"termios"))?;
+    let res = cvt(syscall::read(fd, &mut termios));
+    let _ = syscall::close(fd);
+
+    if res? == termios.len() {
+        Ok(termios)
+    } else {
+        Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal attributes."))
+    }
+}
+
+pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
+    let fd = cvt(syscall::dup(0, b"termios"))?;
+    let res = cvt(syscall::write(fd, termios));
+    let _ = syscall::close(fd);
+
+    if res? == termios.len() {
+        Ok(())
+    } else {
+        Err(io::Error::new(io::ErrorKind::Other, "Unable to set the terminal attributes."))
+    }
+}
+
+pub fn raw_terminal_attr(ios: &mut Termios) {
+    ios.make_raw()
+}
diff --git a/src/sys/redox/mod.rs b/src/sys/redox/mod.rs
new file mode 100644
index 00000000..2a9b875e
--- /dev/null
+++ b/src/sys/redox/mod.rs
@@ -0,0 +1,15 @@
+extern crate redox_termios;
+extern crate syscall;
+
+use std::io;
+
+pub use self::redox_termios::Termios;
+
+pub mod attr;
+pub mod size;
+pub mod tty;
+
+// Support function for converting syscall error to io error
+fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
+    result.map_err(|err| io::Error::from_raw_os_error(err.errno))
+}
diff --git a/src/sys/redox/size.rs b/src/sys/redox/size.rs
new file mode 100644
index 00000000..07f64a24
--- /dev/null
+++ b/src/sys/redox/size.rs
@@ -0,0 +1,18 @@
+use std::io;
+
+use super::{cvt, redox_termios, syscall};
+
+/// Get the size of the terminal.
+pub fn terminal_size() -> io::Result<(u16, u16)> {
+    let mut winsize = redox_termios::Winsize::default();
+
+    let fd = cvt(syscall::dup(1, b"winsize"))?;
+    let res = cvt(syscall::read(fd, &mut winsize));
+    let _ = syscall::close(fd);
+
+    if res? == winsize.len() {
+        Ok((winsize.ws_col, winsize.ws_row))
+    } else {
+        Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
+    }
+}
diff --git a/src/tty.rs b/src/sys/redox/tty.rs
similarity index 51%
rename from src/tty.rs
rename to src/sys/redox/tty.rs
index f153e0ee..9179b396 100644
--- a/src/tty.rs
+++ b/src/sys/redox/tty.rs
@@ -1,19 +1,10 @@
-use std::{fs, io};
+use std::{env, fs, io};
 use std::os::unix::io::AsRawFd;
 
-/// Is this stream a TTY?
-#[cfg(not(target_os = "redox"))]
-pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
-    use libc;
-
-    unsafe { libc::isatty(stream.as_raw_fd()) == 1 }
-}
+use super::syscall;
 
 /// Is this stream a TTY?
-#[cfg(target_os = "redox")]
 pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
-    use syscall;
-
     if let Ok(fd) = syscall::dup(stream.as_raw_fd(), b"termios") {
         let _ = syscall::close(fd);
         true
@@ -25,17 +16,7 @@ pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
 /// Get the TTY device.
 ///
 /// This allows for getting stdio representing _only_ the TTY, and not other streams.
-#[cfg(target_os = "redox")]
 pub fn get_tty() -> io::Result<fs::File> {
-    use std::env;
     let tty = try!(env::var("TTY").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x)));
     fs::OpenOptions::new().read(true).write(true).open(tty)
 }
-
-/// Get the TTY device.
-///
-/// This allows for getting stdio representing _only_ the TTY, and not other streams.
-#[cfg(not(target_os = "redox"))]
-pub fn get_tty() -> io::Result<fs::File> {
-    fs::OpenOptions::new().read(true).write(true).open("/dev/tty")
-}
diff --git a/src/sys/unix/attr.rs b/src/sys/unix/attr.rs
new file mode 100644
index 00000000..5e21fbac
--- /dev/null
+++ b/src/sys/unix/attr.rs
@@ -0,0 +1,29 @@
+use std::{io, mem};
+
+use super::{cvt, Termios};
+use super::libc::c_int;
+
+pub fn get_terminal_attr() -> io::Result<Termios> {
+    extern "C" {
+        pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
+    }
+    unsafe {
+        let mut termios = mem::zeroed();
+        cvt(tcgetattr(0, &mut termios))?;
+        Ok(termios)
+    }
+}
+
+pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
+    extern "C" {
+        pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
+    }
+    cvt(unsafe { tcsetattr(0, 0, termios) }).and(Ok(()))
+}
+
+pub fn raw_terminal_attr(termios: &mut Termios) {
+    extern "C" {
+        pub fn cfmakeraw(termptr: *mut Termios);
+    }
+    unsafe { cfmakeraw(termios) }
+}
diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs
new file mode 100644
index 00000000..08d73feb
--- /dev/null
+++ b/src/sys/unix/mod.rs
@@ -0,0 +1,33 @@
+extern crate libc;
+
+use std::io;
+
+pub use self::libc::termios as Termios;
+
+pub mod attr;
+pub mod size;
+pub mod tty;
+
+// Support functions for converting libc return values to io errors {
+trait IsMinusOne {
+    fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+        ($($t:ident)*) => ($(impl IsMinusOne for $t {
+            fn is_minus_one(&self) -> bool {
+                *self == -1
+            }
+        })*)
+    }
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+    if t.is_minus_one() {
+        Err(io::Error::last_os_error())
+    } else {
+        Ok(t)
+    }
+}
+// } End of support functions
diff --git a/src/sys/unix/size.rs b/src/sys/unix/size.rs
new file mode 100644
index 00000000..9c2aaf1a
--- /dev/null
+++ b/src/sys/unix/size.rs
@@ -0,0 +1,48 @@
+use std::{io, mem};
+
+use super::cvt;
+use super::libc::{c_ushort, ioctl, STDOUT_FILENO};
+
+#[repr(C)]
+struct TermSize {
+    row: c_ushort,
+    col: c_ushort,
+    _x: c_ushort,
+    _y: c_ushort,
+}
+
+#[cfg(target_os = "linux")]
+pub const TIOCGWINSZ: usize = 0x00005413;
+
+#[cfg(not(target_os = "linux"))]
+pub const TIOCGWINSZ: usize = 0x40087468;
+
+// Since attributes on non-item statements is not stable yet, we use a function.
+#[cfg(not(target_os = "android"))]
+#[cfg(not(target_os = "redox"))]
+#[cfg(target_pointer_width = "64")]
+#[cfg(not(target_env = "musl"))]
+fn tiocgwinsz() -> u64 {
+    TIOCGWINSZ as u64
+}
+#[cfg(not(target_os = "android"))]
+#[cfg(not(target_os = "redox"))]
+#[cfg(target_pointer_width = "32")]
+#[cfg(not(target_env = "musl"))]
+fn tiocgwinsz() -> u32 {
+    TIOCGWINSZ as u32
+}
+
+#[cfg(any(target_env = "musl", target_os = "android"))]
+fn tiocgwinsz() -> i32 {
+    TIOCGWINSZ as i32
+}
+
+/// Get the size of the terminal.
+pub fn terminal_size() -> io::Result<(u16, u16)> {
+    unsafe {
+        let mut size: TermSize = mem::zeroed();
+        cvt(ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _))?;
+        Ok((size.col as u16, size.row as u16))
+    }
+}
diff --git a/src/sys/unix/tty.rs b/src/sys/unix/tty.rs
new file mode 100644
index 00000000..2be93634
--- /dev/null
+++ b/src/sys/unix/tty.rs
@@ -0,0 +1,17 @@
+use std::{fs, io};
+use std::os::unix::io::AsRawFd;
+
+use super::libc;
+
+
+/// Is this stream a TTY?
+pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
+    unsafe { libc::isatty(stream.as_raw_fd()) == 1 }
+}
+
+/// Get the TTY device.
+///
+/// This allows for getting stdio representing _only_ the TTY, and not other streams.
+pub fn get_tty() -> io::Result<fs::File> {
+    fs::OpenOptions::new().read(true).write(true).open("/dev/tty")
+}
diff --git a/src/termios.rs b/src/termios.rs
deleted file mode 100644
index 420408f4..00000000
--- a/src/termios.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use libc::c_int;
-use std::mem;
-
-pub use libc::termios as Termios;
-
-#[cfg(target_os = "linux")]
-pub const TIOCGWINSZ: usize = 0x00005413;
-
-#[cfg(not(target_os = "linux"))]
-pub const TIOCGWINSZ: usize = 0x40087468;
-
-extern "C" {
-    pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
-    pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *mut Termios) -> c_int;
-    pub fn cfmakeraw(termptr: *mut Termios);
-}
-
-pub fn get_terminal_attr() -> (Termios, c_int) {
-    unsafe {
-        let mut ios = mem::zeroed();
-        let attr = tcgetattr(0, &mut ios);
-        (ios, attr)
-    }
-}
-
-pub fn set_terminal_attr(ios: *mut Termios) -> c_int {
-    unsafe { tcsetattr(0, 0, ios) }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_get_terminal_attr() {
-        get_terminal_attr();
-        get_terminal_attr();
-        get_terminal_attr();
-    }
-    #[test]
-    fn test_set_terminal_attr() {
-        let mut ios = get_terminal_attr().0;
-        set_terminal_attr(&mut ios as *mut _);
-    }
-}
-- 
GitLab