diff options
Diffstat (limited to 'vendor/terminal_size/src/unix.rs')
-rw-r--r-- | vendor/terminal_size/src/unix.rs | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/vendor/terminal_size/src/unix.rs b/vendor/terminal_size/src/unix.rs new file mode 100644 index 0000000..59ac276 --- /dev/null +++ b/vendor/terminal_size/src/unix.rs @@ -0,0 +1,117 @@ +use super::{Height, Width}; +use std::os::unix::io::RawFd; + +/// Returns the size of the terminal defaulting to STDOUT, if available. +/// +/// If STDOUT is not a tty, returns `None` +pub fn terminal_size() -> Option<(Width, Height)> { + terminal_size_using_fd(libc::STDOUT_FILENO) +} + +/// Returns the size of the terminal using the given file descriptor, if available. +/// +/// If the given file descriptor is not a tty, returns `None` +pub fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> { + use libc::ioctl; + use libc::isatty; + use libc::{winsize as WinSize, TIOCGWINSZ}; + let is_tty: bool = unsafe { isatty(fd) == 1 }; + + if !is_tty { + return None; + } + + let mut winsize = WinSize { + ws_row: 0, + ws_col: 0, + ws_xpixel: 0, + ws_ypixel: 0, + }; + + if unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut winsize) } == -1 { + return None; + } + + let rows = winsize.ws_row; + let cols = winsize.ws_col; + + if rows > 0 && cols > 0 { + Some((Width(cols), Height(rows))) + } else { + None + } +} + +#[test] +/// Compare with the output of `stty size` +fn compare_with_stty() { + use std::process::Command; + use std::process::Stdio; + + let (rows, cols) = if cfg!(target_os = "illumos") { + // illumos stty(1) does not accept a device argument, instead using + // stdin unconditionally: + let output = Command::new("stty") + .stdin(Stdio::inherit()) + .output() + .unwrap(); + assert!(output.status.success()); + + // stdout includes the row and columns thus: "rows = 80; columns = 24;" + let vals = String::from_utf8(output.stdout) + .unwrap() + .lines() + .map(|line| { + // Split each line on semicolons to get "k = v" strings: + line.split(';') + .map(str::trim) + .map(str::to_string) + .collect::<Vec<_>>() + }) + .flatten() + .filter_map(|term| { + // split each "k = v" string and look for rows/columns: + match term.splitn(2, " = ").collect::<Vec<_>>().as_slice() { + ["rows", n] | ["columns", n] => Some(n.parse().unwrap()), + _ => None, + } + }) + .collect::<Vec<_>>(); + (vals[0], vals[1]) + } else { + let output = if cfg!(target_os = "linux") { + Command::new("stty") + .arg("size") + .arg("-F") + .arg("/dev/stderr") + .stderr(Stdio::inherit()) + .output() + .unwrap() + } else { + Command::new("stty") + .arg("-f") + .arg("/dev/stderr") + .arg("size") + .stderr(Stdio::inherit()) + .output() + .unwrap() + }; + + assert!(output.status.success()); + let stdout = String::from_utf8(output.stdout).unwrap(); + // stdout is "rows cols" + let mut data = stdout.split_whitespace(); + println!("{}", stdout); + let rows = u16::from_str_radix(data.next().unwrap(), 10).unwrap(); + let cols = u16::from_str_radix(data.next().unwrap(), 10).unwrap(); + (rows, cols) + }; + println!("{} {}", rows, cols); + + if let Some((Width(w), Height(h))) = terminal_size() { + assert_eq!(rows, h); + assert_eq!(cols, w); + } else { + panic!("terminal_size() return None"); + } +} |