aboutsummaryrefslogtreecommitdiff
path: root/rust/src/getpass.rs
blob: f40da0eb5a7acf18e745031639ec7c2fa3bf21b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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)
    })
}