duplicate_finder/tty/
unix.rs

1extern crate libc;
2use super::{Height, Width};
3
4// We need to convert from c_int to c_ulong at least on DragonFly and FreeBSD.
5#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
6fn ioctl_conv<T: Into<libc::c_ulong>>(v: T) -> libc::c_ulong {
7    v.into()
8}
9
10// No-op on any other operating system.
11#[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
12fn ioctl_conv<T: Copy>(v: T) -> T {
13    v
14}
15
16/// Returns the size of the terminal, if available.
17///
18/// If STDOUT is not a tty, returns `None`
19pub fn terminal_size() -> Option<(Width, Height)> {
20    use self::libc::{ioctl, isatty, winsize, STDOUT_FILENO, TIOCGWINSZ};
21    let is_tty: bool = unsafe { isatty(STDOUT_FILENO) == 1 };
22
23    if !is_tty {
24        return None;
25    }
26
27    let (rows, cols) = unsafe {
28        let mut winsize = winsize {
29            ws_row: 0,
30            ws_col: 0,
31            ws_xpixel: 0,
32            ws_ypixel: 0,
33        };
34        ioctl(STDOUT_FILENO, ioctl_conv(TIOCGWINSZ), &mut winsize);
35        let rows = if winsize.ws_row > 0 {
36            winsize.ws_row
37        } else {
38            0
39        };
40        let cols = if winsize.ws_col > 0 {
41            winsize.ws_col
42        } else {
43            0
44        };
45        (rows as u16, cols as u16)
46    };
47
48    if rows > 0 && cols > 0 {
49        Some((Width(cols), Height(rows)))
50    } else {
51        None
52    }
53}
54
55/// Return string that move the cursor `n` lines up.
56pub fn move_cursor_up(n: usize) -> String {
57    format!("\x1B[{}A", n)
58}
59
60#[test]
61/// Compare with the output of `stty size`
62fn compare_with_stty() {
63    use std::process::Command;
64    use std::process::Stdio;
65    let mut args = vec!["-F", "/dev/stderr", "size"];
66    if cfg!(target_os = "macos") {
67        args[0] = "-f"
68    }
69    let output = Command::new("stty")
70        .args(&args)
71        .stderr(Stdio::inherit())
72        .output()
73        .unwrap();
74    let stdout = String::from_utf8(output.stdout).unwrap();
75    assert!(output.status.success());
76
77    // stdout is "rows cols"
78    let mut data = stdout.split_whitespace();
79    let rows = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
80    let cols = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
81    println!("{}", stdout);
82    println!("{} {}", rows, cols);
83
84    if let Some((Width(w), Height(h))) = terminal_size() {
85        assert_eq!(rows, h);
86        assert_eq!(cols, w);
87    }
88}