diff --git a/Cargo.toml b/Cargo.toml index 3a0be3485..b553dc414 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,9 +65,14 @@ default = ["with-dirs"] with-dirs = ["dirs-next"] with-fuzzy = ["skim"] case_insensitive_history_search = ["regex"] +arbitrary-file-descriptors=[] [package.metadata.docs.rs] features = ["with-dirs", "with-fuzzy"] all-features = false no-default-features = true default-target = "x86_64-unknown-linux-gnu" + +[[example]] +name = "arbitrary_file_descriptors" +required-features = ["arbitrary-file-descriptors"] \ No newline at end of file diff --git a/examples/arbitrary_file_descriptors.rs b/examples/arbitrary_file_descriptors.rs new file mode 100644 index 000000000..0552655bc --- /dev/null +++ b/examples/arbitrary_file_descriptors.rs @@ -0,0 +1,29 @@ +use rustyline::{Behavior, Config, Editor, Result}; +use std::fs::OpenOptions; +use std::io; + +fn main() -> Result<()> { + #![cfg(all(unix, not(target_arch = "wasm32")))] + { + use std::os::unix::io::IntoRawFd; + + let mut path = String::new(); + io::stdin().read_line(&mut path)?; + let terminal = OpenOptions::new() + .read(true) + .write(true) + .open(path.trim())?; + let terminal_fd = terminal.into_raw_fd(); + let config = Config::builder() + .behavior(Behavior::ArbitraryFileDescriptors { + output: terminal_fd, + input: terminal_fd, + }) + .build(); + let mut rl = Editor::<()>::with_config(config); + loop { + let line = rl.readline("> ")?; + println!("Line: {}", line); + } + } +} diff --git a/src/config.rs b/src/config.rs index f7f84715d..5ef2f23b2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -300,6 +300,17 @@ pub enum Behavior { /// Use terminal-style interaction whenever possible, even if 'stdin' and/or /// 'stdout' are not terminals. PreferTerm, + + /// Use the provided file descriptors. + /// rustyline will not automatically close these file descriptors. + #[cfg(feature = "arbitrary-file-descriptors")] + #[cfg(all(unix, not(target_arch = "wasm32")))] + ArbitraryFileDescriptors { + /// the file descriptor for input + input: std::os::unix::io::RawFd, + /// the file descriptor for output + output: std::os::unix::io::RawFd, + }, // TODO // Use file-style interaction, reading input from the given file. // useFile diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 6f1c7034a..b478ac69d 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -1182,33 +1182,49 @@ impl Term for PosixTerminal { bell_style: BellStyle, enable_bracketed_paste: bool, ) -> Self { - let (tty_in, is_in_a_tty, tty_out, is_out_a_tty, close_on_drop) = - if behavior == Behavior::PreferTerm { - let tty = OpenOptions::new().read(true).write(true).open("/dev/tty"); - if let Ok(tty) = tty { - let fd = tty.into_raw_fd(); - let is_a_tty = is_a_tty(fd); // TODO: useless ? - (fd, is_a_tty, fd, is_a_tty, true) - } else { - ( - libc::STDIN_FILENO, - is_a_tty(libc::STDIN_FILENO), - libc::STDOUT_FILENO, - is_a_tty(libc::STDOUT_FILENO), - false, - ) + let (tty_in, is_in_a_tty, tty_out, is_out_a_tty, close_on_drop, use_controlling_terminal) = + match behavior { + #[cfg(feature = "arbitrary-file-descriptors")] + #[cfg(all(unix, not(target_arch = "wasm32")))] + Behavior::ArbitraryFileDescriptors { input, output } => ( + input, + is_a_tty(input), + output, + is_a_tty(output), + false, + false, + ), + Behavior::PreferTerm => { + let tty = OpenOptions::new().read(true).write(true).open("/dev/tty"); + if let Ok(tty) = tty { + let fd = tty.into_raw_fd(); + let is_a_tty = is_a_tty(fd); // TODO: useless ? + (fd, is_a_tty, fd, is_a_tty, true, true) + } else { + ( + libc::STDIN_FILENO, + is_a_tty(libc::STDIN_FILENO), + libc::STDOUT_FILENO, + is_a_tty(libc::STDOUT_FILENO), + false, + true, + ) + } } - } else { - ( + Behavior::Stdio => ( libc::STDIN_FILENO, is_a_tty(libc::STDIN_FILENO), libc::STDOUT_FILENO, is_a_tty(libc::STDOUT_FILENO), false, - ) + true, + ), }; let term = Self { - unsupported: is_unsupported_term(), + // if we are not using the controlling terminal of the process, + // the identity of the terminal used cannot be determined. + // So just assume the it is supported + unsupported: use_controlling_terminal && is_unsupported_term(), tty_in, is_in_a_tty, tty_out,