use std::io;
use std::os::unix::io::AsRawFd;
pub fn read_password() -> io::Result<String> {
#[repr(C)]
struct Termios {
c_iflag: u32,
c_oflag: u32,
c_cflag: u32,
c_lflag: u32,
c_line: u8,
c_cc: [u8; 32],
c_ispeed: u32,
c_ospeed: u32,
}
const ECHO: u32 = 0x00000008;
const ECHONL: u32 = 0x00000040;
const TCSANOW: i32 = 0;
unsafe extern "C" {
fn tcgetattr(fd: i32, termios: *mut Termios) -> i32;
fn tcsetattr(fd: i32, optional_actions: i32, termios: *const Termios) -> i32;
}
let stdin_fd = io::stdin().as_raw_fd();
let mut termios = std::mem::MaybeUninit::<Termios>::uninit();
unsafe {
if tcgetattr(stdin_fd, termios.as_mut_ptr()) != 0 {
return Err(io::Error::last_os_error());
}
let mut termios = termios.assume_init();
let original_lflag = termios.c_lflag;
termios.c_lflag &= !(ECHO | ECHONL);
if tcsetattr(stdin_fd, TCSANOW, &termios) != 0 {
return Err(io::Error::last_os_error());
}
struct TermiosResetter {
fd: i32,
original_lflag: u32,
termios: Termios,
}
impl Drop for TermiosResetter {
fn drop(&mut self) {
unsafe extern "C" {
fn tcsetattr(fd: i32, optional_actions: i32, termios: *const Termios) -> i32;
}
self.termios.c_lflag = self.original_lflag;
unsafe {
const TCSANOW: i32 = 0;
tcsetattr(self.fd, TCSANOW, &self.termios);
}
}
}
let _resetter = TermiosResetter {
fd: stdin_fd,
original_lflag,
termios,
};
let mut password = String::new();
io::stdin().read_line(&mut password)?;
if password.ends_with('\n') {
password.pop();
if password.ends_with('\r') {
password.pop();
}
}
Ok(password)
}
}
pub fn get_terminal_size() -> Option<(u16, u16)> {
#[repr(C)]
struct Winsize {
ws_row: u16,
ws_col: u16,
ws_xpixel: u16,
ws_ypixel: u16,
}
const TIOCGWINSZ: u64 = 0x5413;
unsafe extern "C" {
fn ioctl(fd: i32, request: u64, argp: *mut Winsize) -> i32;
}
unsafe {
let mut ws: Winsize = std::mem::zeroed();
let stdout_fd = io::stdout().as_raw_fd();
if ioctl(stdout_fd, TIOCGWINSZ, &mut ws) == 0 {
if ws.ws_col > 0 && ws.ws_row > 0 {
return Some((ws.ws_col, ws.ws_row));
}
}
None
}
}