#![allow(unsafe_code)]
pub fn columns() -> Option<usize> {
const STDOUT: i32 = 1;
const STDERR: i32 = 2;
columns_of(STDOUT).or_else(|| columns_of(STDERR))
}
#[cfg(unix)]
fn columns_of(fd: i32) -> Option<usize> {
use std::ffi::{c_int, c_ulong};
#[repr(C)]
struct Winsize {
rows: u16,
columns: u16,
width_pixels: u16,
height_pixels: u16,
}
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc",
target_arch = "sparc64",
))
))]
const TIOCGWINSZ: c_ulong = 0x5413;
#[cfg(target_os = "android")]
const TIOCGWINSZ: c_ulong = 0x5413;
#[cfg(not(any(
target_os = "android",
all(
target_os = "linux",
not(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc",
target_arch = "sparc64",
))
)
)))]
const TIOCGWINSZ: c_ulong = 0x4008_7468;
unsafe extern "C" {
fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int;
}
let mut size = Winsize {
rows: 0,
columns: 0,
width_pixels: 0,
height_pixels: 0,
};
let result = unsafe { ioctl(fd as c_int, TIOCGWINSZ, &raw mut size) };
if result != 0 {
return None;
}
(size.columns > 0).then_some(size.columns as usize)
}
#[cfg(windows)]
fn columns_of(fd: i32) -> Option<usize> {
use std::ffi::c_void;
#[repr(C)]
#[derive(Default)]
struct Coord {
x: i16,
y: i16,
}
#[repr(C)]
#[derive(Default)]
struct SmallRect {
left: i16,
top: i16,
right: i16,
bottom: i16,
}
#[repr(C)]
#[derive(Default)]
struct ScreenBufferInfo {
size: Coord,
cursor_position: Coord,
attributes: u16,
window: SmallRect,
maximum_window_size: Coord,
}
const STD_OUTPUT_HANDLE: u32 = -11_i32 as u32;
const STD_ERROR_HANDLE: u32 = -12_i32 as u32;
const INVALID_HANDLE_VALUE: *mut c_void = -1_isize as *mut c_void;
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetStdHandle(which: u32) -> *mut c_void;
fn GetConsoleScreenBufferInfo(handle: *mut c_void, info: *mut ScreenBufferInfo) -> i32;
}
let which = match fd {
2 => STD_ERROR_HANDLE,
_ => STD_OUTPUT_HANDLE,
};
let columns = unsafe {
let handle = GetStdHandle(which);
if handle.is_null() || handle == INVALID_HANDLE_VALUE {
return None;
}
let mut info = ScreenBufferInfo::default();
if GetConsoleScreenBufferInfo(handle, &raw mut info) == 0 {
return None;
}
i32::from(info.window.right) - i32::from(info.window.left) + 1
};
(columns > 0).then_some(columns as usize)
}
#[cfg(not(any(unix, windows)))]
fn columns_of(_fd: i32) -> Option<usize> {
None
}
#[cfg(test)]
mod tests {
#[test]
fn asking_the_terminal_never_answers_zero() {
assert_ne!(super::columns(), Some(0));
}
}