diff options
Diffstat (limited to 'rust/src/getpass.rs')
-rw-r--r-- | rust/src/getpass.rs | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/rust/src/getpass.rs b/rust/src/getpass.rs new file mode 100644 index 0000000..f40da0e --- /dev/null +++ b/rust/src/getpass.rs @@ -0,0 +1,65 @@ +extern crate libc; + +use std::io::{self, Write}; +use std::mem; +use std::str; + +fn error_checker(ret: i32) -> io::Result<()> { + if ret == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } +} + +fn tcgetattr(fd: i32) -> io::Result<libc::termios> { + unsafe { + let mut tios: libc::termios = mem::zeroed(); + error_checker(libc::tcgetattr(fd, &mut tios))?; + Ok(tios) + } +} + +fn tcsetattr(fd: i32, other_options: i32, tios: &libc::termios) -> io::Result<()> { + unsafe { + error_checker(libc::tcsetattr(fd, other_options, tios))?; + Ok(()) + } +} + +fn without_echo<T, F: FnOnce() -> io::Result<T>>(f: F) -> io::Result<T> { + let tios_old = tcgetattr(0)?; + + let mut tios_new = tios_old; + tios_new.c_lflag &= !libc::ECHO; + tios_new.c_lflag |= libc::ECHONL; + + match tcsetattr(0, libc::TCSANOW, &tios_new) { + Ok(()) => {}, + Err(e) => { + // Just blindly try to reset the terminal, in case something changed + let _ = tcsetattr(0, libc::TCSANOW, &tios_old); + return Err(e); + } + } + + let ret = f()?; + + tcsetattr(0, libc::TCSANOW, &tios_old)?; + + Ok(ret) +} + +pub fn get_pass(prompt: &str) -> io::Result<String> { + print!("{}", prompt); + io::stdout().flush()?; + + without_echo(|| { + let mut line = String::new(); + io::stdin().read_line(&mut line)?; + if line.ends_with('\n') { + line.pop(); + } + Ok(line) + }) +} |