diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-prompt/src/unix.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-prompt/src/unix.rs')
-rw-r--r-- | vendor/gix-prompt/src/unix.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/vendor/gix-prompt/src/unix.rs b/vendor/gix-prompt/src/unix.rs new file mode 100644 index 000000000..a08d2d777 --- /dev/null +++ b/vendor/gix-prompt/src/unix.rs @@ -0,0 +1,93 @@ +/// The path to the default TTY on linux +pub const TTY_PATH: &str = "/dev/tty"; + +#[cfg(unix)] +pub(crate) mod imp { + use std::{ + io::{BufRead, Write}, + os::unix::io::{AsRawFd, RawFd}, + }; + + use nix::sys::{termios, termios::Termios}; + use parking_lot::{const_mutex, lock_api::MutexGuard, Mutex, RawMutex}; + + use crate::{unix::TTY_PATH, Error, Mode, Options}; + + static TERM_STATE: Mutex<Option<Termios>> = const_mutex(None); + + /// Ask the user given a `prompt`, returning the result. + pub(crate) fn ask(prompt: &str, Options { mode, .. }: &Options<'_>) -> Result<String, Error> { + match mode { + Mode::Disable => Err(Error::Disabled), + Mode::Hidden => { + let state = TERM_STATE.lock(); + let mut in_out = std::fs::OpenOptions::new().write(true).read(true).open(TTY_PATH)?; + let restore = save_term_state_and_disable_echo(state, in_out.as_raw_fd())?; + in_out.write_all(prompt.as_bytes())?; + + let mut buf_read = std::io::BufReader::with_capacity(64, in_out); + let mut out = String::with_capacity(64); + buf_read.read_line(&mut out)?; + + out.pop(); + if out.ends_with('\r') { + out.pop(); + } + restore.now()?; + Ok(out) + } + Mode::Visible => { + let mut in_out = std::fs::OpenOptions::new().write(true).read(true).open(TTY_PATH)?; + in_out.write_all(prompt.as_bytes())?; + + let mut buf_read = std::io::BufReader::with_capacity(64, in_out); + let mut out = String::with_capacity(64); + buf_read.read_line(&mut out)?; + Ok(out.trim_end().to_owned()) + } + } + } + + type TermiosGuard<'a> = MutexGuard<'a, RawMutex, Option<Termios>>; + + struct RestoreTerminalStateOnDrop<'a> { + state: TermiosGuard<'a>, + fd: RawFd, + } + + impl<'a> RestoreTerminalStateOnDrop<'a> { + fn now(mut self) -> Result<(), Error> { + let state = self.state.take().expect("BUG: we exist only if something is saved"); + termios::tcsetattr(self.fd, termios::SetArg::TCSAFLUSH, &state)?; + Ok(()) + } + } + + impl<'a> Drop for RestoreTerminalStateOnDrop<'a> { + fn drop(&mut self) { + if let Some(state) = self.state.take() { + termios::tcsetattr(self.fd, termios::SetArg::TCSAFLUSH, &state).ok(); + } + } + } + + fn save_term_state_and_disable_echo( + mut state: TermiosGuard<'_>, + fd: RawFd, + ) -> Result<RestoreTerminalStateOnDrop<'_>, Error> { + assert!( + state.is_none(), + "BUG: recursive calls are not possible and we restore afterwards" + ); + + let prev = termios::tcgetattr(fd)?; + let mut new = prev.clone(); + *state = prev.into(); + + new.local_flags &= !termios::LocalFlags::ECHO; + new.local_flags |= termios::LocalFlags::ECHONL; + termios::tcsetattr(fd, termios::SetArg::TCSAFLUSH, &new)?; + + Ok(RestoreTerminalStateOnDrop { fd, state }) + } +} |