Skip to content

Commit 541d849

Browse files
authored
fix: ignore stdin redirection to ensure always accessing the real tty (#2425)
1 parent 66f015d commit 541d849

File tree

1 file changed

+99
-36
lines changed

1 file changed

+99
-36
lines changed

yazi-adapter/src/stdin.rs

Lines changed: 99 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
1-
use std::{io::{Error, ErrorKind}, time::{Duration, Instant}};
1+
use std::{io::{Error, ErrorKind}, ops::Deref, time::{Duration, Instant}};
22

33
pub struct AsyncStdin {
4+
fd: Fd,
45
#[cfg(unix)]
56
fds: libc::fd_set,
67
}
78

9+
impl Default for AsyncStdin {
10+
fn default() -> Self {
11+
let fd = Fd::new().expect("failed to open stdin");
12+
#[cfg(unix)]
13+
{
14+
let mut me = Self { fd, fds: unsafe { std::mem::MaybeUninit::zeroed().assume_init() } };
15+
me.reset();
16+
me
17+
}
18+
#[cfg(windows)]
19+
{
20+
Self { fd }
21+
}
22+
}
23+
}
24+
825
impl AsyncStdin {
926
pub fn read_until<P>(&mut self, timeout: Duration, predicate: P) -> (Vec<u8>, std::io::Result<()>)
1027
where
@@ -21,7 +38,7 @@ impl AsyncStdin {
2138
continue;
2239
}
2340

24-
let b = Self::read_u8()?;
41+
let b = self.read_u8()?;
2542
buf.push(b);
2643

2744
if predicate(b, &buf) {
@@ -36,15 +53,7 @@ impl AsyncStdin {
3653
}
3754
}
3855

39-
#[cfg(unix)]
40-
impl Default for AsyncStdin {
41-
fn default() -> Self {
42-
let mut me = Self { fds: unsafe { std::mem::MaybeUninit::zeroed().assume_init() } };
43-
me.reset();
44-
me
45-
}
46-
}
47-
56+
// --- Unix
4857
#[cfg(unix)]
4958
impl AsyncStdin {
5059
pub fn poll(&mut self, timeout: Duration) -> std::io::Result<bool> {
@@ -54,13 +63,7 @@ impl AsyncStdin {
5463
};
5564

5665
let result = unsafe {
57-
libc::select(
58-
libc::STDIN_FILENO + 1,
59-
&mut self.fds,
60-
std::ptr::null_mut(),
61-
std::ptr::null_mut(),
62-
&mut tv,
63-
)
66+
libc::select(*self.fd + 1, &mut self.fds, std::ptr::null_mut(), std::ptr::null_mut(), &mut tv)
6467
};
6568

6669
match result {
@@ -73,9 +76,9 @@ impl AsyncStdin {
7376
}
7477
}
7578

76-
pub fn read_u8() -> std::io::Result<u8> {
79+
pub fn read_u8(&mut self) -> std::io::Result<u8> {
7780
let mut b = 0;
78-
match unsafe { libc::read(libc::STDIN_FILENO, &mut b as *mut _ as *mut _, 1) } {
81+
match unsafe { libc::read(*self.fd, &mut b as *mut _ as *mut _, 1) } {
7982
-1 => Err(Error::last_os_error()),
8083
0 => Err(Error::new(ErrorKind::UnexpectedEof, "unexpected EOF")),
8184
_ => Ok(b),
@@ -85,42 +88,31 @@ impl AsyncStdin {
8588
fn reset(&mut self) {
8689
unsafe {
8790
libc::FD_ZERO(&mut self.fds);
88-
libc::FD_SET(libc::STDIN_FILENO, &mut self.fds);
91+
libc::FD_SET(*self.fd, &mut self.fds);
8992
}
9093
}
9194
}
9295

93-
#[cfg(windows)]
94-
impl Default for AsyncStdin {
95-
fn default() -> Self { Self {} }
96-
}
97-
96+
// --- Windows
9897
#[cfg(windows)]
9998
impl AsyncStdin {
10099
pub fn poll(&mut self, timeout: Duration) -> std::io::Result<bool> {
101-
use std::os::windows::io::AsRawHandle;
102-
103100
use windows_sys::Win32::{Foundation::{WAIT_FAILED, WAIT_OBJECT_0}, System::Threading::WaitForSingleObject};
104101

105-
let handle = std::io::stdin().as_raw_handle();
106102
let millis = timeout.as_millis();
107-
match unsafe { WaitForSingleObject(handle, millis as u32) } {
103+
match unsafe { WaitForSingleObject(*self.fd, millis as u32) } {
108104
WAIT_FAILED => Err(Error::last_os_error()),
109105
WAIT_OBJECT_0 => Ok(true),
110106
_ => Ok(false),
111107
}
112108
}
113109

114-
pub fn read_u8() -> std::io::Result<u8> {
115-
use std::os::windows::io::AsRawHandle;
116-
110+
pub fn read_u8(&mut self) -> std::io::Result<u8> {
117111
use windows_sys::Win32::Storage::FileSystem::ReadFile;
118112

119113
let mut buf = 0;
120114
let mut bytes = 0;
121-
let success = unsafe {
122-
ReadFile(std::io::stdin().as_raw_handle(), &mut buf, 1, &mut bytes, std::ptr::null_mut())
123-
};
115+
let success = unsafe { ReadFile(*self.fd, &mut buf, 1, &mut bytes, std::ptr::null_mut()) };
124116

125117
if success == 0 {
126118
return Err(Error::last_os_error());
@@ -130,3 +122,74 @@ impl AsyncStdin {
130122
Ok(buf)
131123
}
132124
}
125+
126+
// --- Fd
127+
struct Fd {
128+
#[cfg(unix)]
129+
inner: std::os::fd::RawFd,
130+
#[cfg(windows)]
131+
inner: std::os::windows::io::RawHandle,
132+
close: bool,
133+
}
134+
135+
impl Deref for Fd {
136+
#[cfg(unix)]
137+
type Target = std::os::fd::RawFd;
138+
#[cfg(windows)]
139+
type Target = std::os::windows::io::RawHandle;
140+
141+
fn deref(&self) -> &Self::Target { &self.inner }
142+
}
143+
144+
impl Drop for Fd {
145+
fn drop(&mut self) {
146+
#[cfg(unix)]
147+
if self.close {
148+
unsafe { libc::close(self.inner) };
149+
}
150+
#[cfg(windows)]
151+
if self.close {
152+
unsafe { windows_sys::Win32::Foundation::CloseHandle(self.inner) };
153+
}
154+
}
155+
}
156+
157+
impl Fd {
158+
#[cfg(unix)]
159+
fn new() -> std::io::Result<Self> {
160+
use std::{fs::OpenOptions, os::fd::IntoRawFd};
161+
162+
Ok(if unsafe { libc::isatty(libc::STDIN_FILENO) } == 1 {
163+
Self { inner: libc::STDIN_FILENO, close: false }
164+
} else {
165+
Self {
166+
inner: OpenOptions::new().read(true).write(true).open("/dev/tty")?.into_raw_fd(),
167+
close: true,
168+
}
169+
})
170+
}
171+
172+
#[cfg(windows)]
173+
fn new() -> std::io::Result<Self> {
174+
use windows_sys::Win32::{Foundation::{GENERIC_READ, GENERIC_WRITE, INVALID_HANDLE_VALUE}, Storage::FileSystem::{CreateFileW, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING}};
175+
176+
let name: Vec<u16> = "CONIN$\0".encode_utf16().collect();
177+
let result = unsafe {
178+
CreateFileW(
179+
name.as_ptr(),
180+
GENERIC_READ | GENERIC_WRITE,
181+
FILE_SHARE_READ | FILE_SHARE_WRITE,
182+
std::ptr::null_mut(),
183+
OPEN_EXISTING,
184+
0,
185+
std::ptr::null_mut(),
186+
)
187+
};
188+
189+
if result == INVALID_HANDLE_VALUE {
190+
Err(std::io::Error::last_os_error())
191+
} else {
192+
Ok(Self { inner: result, close: true })
193+
}
194+
}
195+
}

0 commit comments

Comments
 (0)