use std::fs::File;
use std::io::{self, BufReader};
use std::mem::MaybeUninit;
use std::os::unix::io::AsRawFd;
use libc::{c_int, tcgetattr, tcsetattr, termios, ECHO, ECHONL, TCSANOW};
use zeroize::Zeroizing;
struct HiddenInput {
fd: i32,
term_orig: termios,
}
impl HiddenInput {
fn new(fd: i32) -> io::Result<HiddenInput> {
let mut term_uninit = MaybeUninit::<termios>::uninit();
io_result(unsafe { tcgetattr(fd, term_uninit.as_mut_ptr()) })?;
let mut term = unsafe { term_uninit.assume_init() };
let term_orig = term;
term.c_lflag &= !ECHO;
term.c_lflag |= ECHONL;
io_result(unsafe { tcsetattr(fd, TCSANOW, &term) })?;
Ok(HiddenInput { fd, term_orig })
}
}
impl Drop for HiddenInput {
fn drop(&mut self) {
unsafe {
tcsetattr(self.fd, TCSANOW, &self.term_orig);
}
}
}
fn io_result(ret: c_int) -> io::Result<()> {
match ret {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}
pub fn from_tty() -> io::Result<Zeroizing<String>> {
let tty = File::open("/dev/tty")?;
let fd = tty.as_raw_fd();
let mut reader = BufReader::new(tty);
let _hidden_input = HiddenInput::new(fd)?;
crate::from_bufread(&mut reader)
}