Skip to content

Commit 00e21a9

Browse files
Add set_nonblocking method on Stdin
1 parent 5949391 commit 00e21a9

File tree

4 files changed

+102
-0
lines changed

4 files changed

+102
-0
lines changed

src/libstd/io/stdio.rs

+66
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ impl Read for StdinRaw {
9797
Initializer::nop()
9898
}
9999
}
100+
impl StdinRaw {
101+
#[cfg(any(unix, windows))]
102+
#[stable(since = "1.46.0", feature = "stdin_nonblocking")]
103+
pub unsafe fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
104+
self.0.set_nonblocking(nonblocking)
105+
}
106+
}
100107
impl Write for StdoutRaw {
101108
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
102109
self.0.write(buf)
@@ -365,6 +372,33 @@ impl Stdin {
365372
pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
366373
self.lock().read_line(buf)
367374
}
375+
376+
/// Set the `stdin` in non-blocking mode.
377+
///
378+
/// It is useful in case you're playing with termcaps. However, please note that it is
379+
/// unsafe because it'll modify the behaviour of all other stdin.
380+
///
381+
/// # Example
382+
///
383+
/// ```no_run
384+
/// use std::io::{self, Read};
385+
///
386+
/// fn main() -> io::Result<()> {
387+
/// let mut buffer = String::new();
388+
/// let stdin = io::stdin();
389+
/// let mut handle = stdin.lock();
390+
///
391+
/// handle.set_nonblocking(true)?;
392+
/// handle.read_to_string(&mut buffer)?;
393+
///
394+
/// Ok(())
395+
/// }
396+
/// ```
397+
#[cfg(any(unix, windows))]
398+
#[stable(since = "1.46.0", feature = "stdin_nonblocking")]
399+
pub unsafe fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
400+
self.lock().set_nonblocking(nonblocking)
401+
}
368402
}
369403

370404
#[stable(feature = "std_debug", since = "1.16.0")]
@@ -439,6 +473,38 @@ impl fmt::Debug for StdinLock<'_> {
439473
}
440474
}
441475

476+
impl StdinLock<'_> {
477+
/// Set the `stdin` in non-blocking mode.
478+
///
479+
/// It is useful in case you're playing with termcaps. However, please note that it is
480+
/// unsafe because it'll modify the behaviour of all other stdin.
481+
///
482+
/// # Example
483+
///
484+
/// ```no_run
485+
/// use std::io::{self, Read};
486+
///
487+
/// fn main() -> io::Result<()> {
488+
/// let mut buffer = String::new();
489+
/// let stdin = io::stdin();
490+
/// let mut handle = stdin.lock();
491+
///
492+
/// handle.set_nonblocking(true)?;
493+
/// handle.read_to_string(&mut buffer)?;
494+
///
495+
/// Ok(())
496+
/// }
497+
/// ```
498+
#[cfg(any(unix, windows))]
499+
#[stable(since = "1.46.0", feature = "stdin_nonblocking")]
500+
pub unsafe fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
501+
match self.inner.get_ref() {
502+
Maybe::Real(ref stdin) => stdin.set_nonblocking(nonblocking),
503+
Maybe::Fake => Ok(()),
504+
}
505+
}
506+
}
507+
442508
/// A handle to the global standard output stream of the current process.
443509
///
444510
/// Each handle shares a global buffer of data to be written to the standard

src/libstd/sys/unix/stdio.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::io::{self, IoSlice, IoSliceMut};
22
use crate::mem::ManuallyDrop;
3+
use crate::sys::cvt;
34
use crate::sys::fd::FileDesc;
45

56
pub struct Stdin(());
@@ -10,6 +11,16 @@ impl Stdin {
1011
pub fn new() -> io::Result<Stdin> {
1112
Ok(Stdin(()))
1213
}
14+
pub unsafe fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
15+
let mut flags = libc::fcntl(libc::STDIN_FILENO, libc::F_GETFL);
16+
if nonblocking {
17+
flags |= libc::O_NONBLOCK;
18+
} else {
19+
flags &= !libc::O_NONBLOCK;
20+
}
21+
cvt(libc::fcntl(libc::STDIN_FILENO, libc::F_SETFL, flags))?;
22+
Ok(())
23+
}
1324
}
1425

1526
impl io::Read for Stdin {

src/libstd/sys/windows/c.rs

+3
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ if #[cfg(not(target_vendor = "uwp"))] {
669669
pub const HANDLE_FLAG_INHERIT: DWORD = 0x00000001;
670670

671671
pub const TOKEN_READ: DWORD = 0x20008;
672+
pub const ENABLE_LINE_INPUT: DWORD = 0x0002;
672673

673674
extern "system" {
674675
#[link_name = "SystemFunction036"]
@@ -688,6 +689,8 @@ if #[cfg(not(target_vendor = "uwp"))] {
688689

689690
pub fn GetConsoleMode(hConsoleHandle: HANDLE,
690691
lpMode: LPDWORD) -> BOOL;
692+
pub fn SetConsoleMode(hConsoleHandle: HANDLE,
693+
lpMode: DWORD) -> BOOL;
691694
// Allowed but unused by UWP
692695
pub fn OpenProcessToken(ProcessHandle: HANDLE,
693696
DesiredAccess: DWORD,

src/libstd/sys/windows/stdio.rs

+22
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,28 @@ impl Stdin {
134134
pub fn new() -> io::Result<Stdin> {
135135
Ok(Stdin { surrogate: 0 })
136136
}
137+
pub unsafe fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
138+
let handle = get_handle(c::STD_INPUT_HANDLE)?;
139+
let mut mode = 0;
140+
if c::GetConsoleMode(handle, &mut mode) == 0 {
141+
return Err(io::Error::new(
142+
io::ErrorKind::InvalidInput,
143+
"Cannot get console mode for the given handle",
144+
));
145+
}
146+
if nonblocking {
147+
mode &= !c::ENABLE_LINE_INPUT;
148+
} else {
149+
mode |= c::ENABLE_LINE_INPUT;
150+
}
151+
if c::SetConsoleMode(handle, mode) == 0 {
152+
return Err(io::Error::new(
153+
io::ErrorKind::InvalidInput,
154+
"Cannot set console mode for the given handle",
155+
));
156+
}
157+
Ok(())
158+
}
137159
}
138160

139161
impl io::Read for Stdin {

0 commit comments

Comments
 (0)