From 64d98f8ee037282c35007b64c2649055c56af1db Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:03 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- vendor/terminal_size/src/unix.rs | 111 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 vendor/terminal_size/src/unix.rs (limited to 'vendor/terminal_size/src/unix.rs') diff --git a/vendor/terminal_size/src/unix.rs b/vendor/terminal_size/src/unix.rs new file mode 100644 index 000000000..59af979d0 --- /dev/null +++ b/vendor/terminal_size/src/unix.rs @@ -0,0 +1,111 @@ +use super::{Height, Width}; +use std::os::unix::io::RawFd; +use rustix::fd::BorrowedFd; + +/// 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(rustix::io::raw_stdout()) +} + +/// 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 rustix::termios::{isatty, tcgetwinsize}; + + // TODO: Once I/O safety is stabilized, the enlosing function here should + // be unsafe due to taking a `RawFd`. We should then move the main + // logic here into a new function which takes a `BorrowedFd` and is safe. + let fd = unsafe { BorrowedFd::borrow_raw(fd) }; + + if !isatty(fd) { + return None; + } + + let winsize = tcgetwinsize(fd).ok()?; + + 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::>() + }) + .flatten() + .filter_map(|term| { + // split each "k = v" string and look for rows/columns: + match term.splitn(2, " = ").collect::>().as_slice() { + ["rows", n] | ["columns", n] => Some(n.parse().unwrap()), + _ => None, + } + }) + .collect::>(); + (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"); + } +} -- cgit v1.2.3