diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 9098d36ee5381..458506fa69d68 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -8,7 +8,9 @@ use crate::io::prelude::*; use crate::cell::{Cell, RefCell}; use crate::fmt; use crate::fs::File; -use crate::io::{self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines}; +use crate::io::{ + self, BorrowedCursor, BufReader, BufWriter, IoSlice, IoSliceMut, LineWriter, Lines, +}; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantMutex, ReentrantMutexGuard}; use crate::sys::stdio; @@ -509,6 +511,82 @@ impl fmt::Debug for StdinLock<'_> { } } +/// Either a [`LineWriter`] or [`BufWriter`] wrapping a [`StdoutRaw`]. +/// +/// Standard output is line-buffered when outputting to a terminal and normally buffered otherwise. +enum StdoutWriter { + LineWriter(LineWriter), + BufWriter(BufWriter), +} + +impl StdoutWriter { + /// Creates a new `StdoutWriter`. Will check if the output is a terminal and use the appropriate writer. + fn new(stdout: StdoutRaw) -> StdoutWriter { + if stdout.0.is_terminal() { + StdoutWriter::LineWriter(LineWriter::new(stdout)) + } else { + StdoutWriter::BufWriter(BufWriter::new(stdout)) + } + } + + /// Creates a new `StdoutWriter` with at least the specified capacity for the internal buffer. + /// + /// Will check if the output is a terminal and use the appropriate writer. + fn with_capacity(capacity: usize, stdout: StdoutRaw) -> StdoutWriter { + if stdout.0.is_terminal() { + StdoutWriter::LineWriter(LineWriter::with_capacity(capacity, stdout)) + } else { + StdoutWriter::BufWriter(BufWriter::with_capacity(capacity, stdout)) + } + } +} + +impl Write for StdoutWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self { + StdoutWriter::LineWriter(writer) => writer.write(buf), + StdoutWriter::BufWriter(writer) => writer.write(buf), + } + } + fn flush(&mut self) -> io::Result<()> { + match self { + StdoutWriter::LineWriter(writer) => writer.flush(), + StdoutWriter::BufWriter(writer) => writer.flush(), + } + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + match self { + StdoutWriter::LineWriter(writer) => writer.write_all(buf), + StdoutWriter::BufWriter(writer) => writer.write_all(buf), + } + } + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + match self { + StdoutWriter::LineWriter(writer) => writer.write_fmt(fmt), + StdoutWriter::BufWriter(writer) => writer.write_fmt(fmt), + } + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + match self { + StdoutWriter::LineWriter(writer) => writer.write_vectored(bufs), + StdoutWriter::BufWriter(writer) => writer.write_vectored(bufs), + } + } + #[inline] + fn is_write_vectored(&self) -> bool { + match self { + StdoutWriter::LineWriter(writer) => writer.is_write_vectored(), + StdoutWriter::BufWriter(writer) => writer.is_write_vectored(), + } + } + fn write_all_vectored(&mut self, bufs: &mut [io::IoSlice<'_>]) -> io::Result<()> { + match self { + StdoutWriter::LineWriter(writer) => writer.write_all_vectored(bufs), + StdoutWriter::BufWriter(writer) => writer.write_all_vectored(bufs), + } + } +} + /// A handle to the global standard output stream of the current process. /// /// Each handle shares a global buffer of data to be written to the standard @@ -533,10 +611,7 @@ impl fmt::Debug for StdinLock<'_> { /// [`io::stdout`]: stdout #[stable(feature = "rust1", since = "1.0.0")] pub struct Stdout { - // FIXME: this should be LineWriter or BufWriter depending on the state of - // stdout (tty or not). Note that if this is not line buffered it - // should also flush-on-panic or some form of flush-on-abort. - inner: &'static ReentrantMutex>>, + inner: &'static ReentrantMutex>, } /// A locked reference to the [`Stdout`] handle. @@ -558,10 +633,10 @@ pub struct Stdout { #[must_use = "if unused stdout will immediately unlock"] #[stable(feature = "rust1", since = "1.0.0")] pub struct StdoutLock<'a> { - inner: ReentrantMutexGuard<'a, RefCell>>, + inner: ReentrantMutexGuard<'a, RefCell>, } -static STDOUT: OnceLock>>> = OnceLock::new(); +static STDOUT: OnceLock>> = OnceLock::new(); /// Constructs a new handle to the standard output of the current process. /// @@ -614,7 +689,7 @@ static STDOUT: OnceLock>>> = OnceLo pub fn stdout() -> Stdout { Stdout { inner: STDOUT - .get_or_init(|| ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))), + .get_or_init(|| ReentrantMutex::new(RefCell::new(StdoutWriter::new(stdout_raw())))), } } @@ -625,7 +700,7 @@ pub fn cleanup() { let mut initialized = false; let stdout = STDOUT.get_or_init(|| { initialized = true; - ReentrantMutex::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw()))) + ReentrantMutex::new(RefCell::new(StdoutWriter::with_capacity(0, stdout_raw()))) }); if !initialized { @@ -634,7 +709,7 @@ pub fn cleanup() { // might have leaked a StdoutLock, which would // otherwise cause a deadlock here. if let Some(lock) = stdout.try_lock() { - *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); + *lock.borrow_mut() = StdoutWriter::with_capacity(0, stdout_raw()); } } } @@ -1082,7 +1157,18 @@ macro_rules! impl_is_terminal { )*} } -impl_is_terminal!(File, Stdin, StdinLock<'_>, Stdout, StdoutLock<'_>, Stderr, StderrLock<'_>); +impl_is_terminal!( + File, + Stdin, + StdinLock<'_>, + Stdout, + StdoutLock<'_>, + Stderr, + StderrLock<'_>, + stdio::Stdin, + stdio::Stdout, + stdio::Stderr +); #[unstable( feature = "print_internals", diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 2180d2974d5ae..0a21662183092 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -11,6 +11,7 @@ use crate::marker::PhantomData; use crate::mem::forget; #[cfg(not(any(target_arch = "wasm32", target_env = "sgx", target_os = "hermit")))] use crate::sys::cvt; +use crate::sys::stdio; use crate::sys_common::{AsInner, FromInner, IntoInner}; /// A borrowed file descriptor. @@ -432,6 +433,15 @@ impl<'a> AsFd for io::StdinLock<'a> { } } +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for stdio::Stdin { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // SAFETY: user code should not close stdin out from under the standard library + unsafe { BorrowedFd::borrow_raw(0) } + } +} + #[stable(feature = "io_safety", since = "1.63.0")] impl AsFd for io::Stdout { #[inline] @@ -449,6 +459,15 @@ impl<'a> AsFd for io::StdoutLock<'a> { } } +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for stdio::Stdout { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // SAFETY: user code should not close stdout out from under the standard library + unsafe { BorrowedFd::borrow_raw(1) } + } +} + #[stable(feature = "io_safety", since = "1.63.0")] impl AsFd for io::Stderr { #[inline] @@ -465,3 +484,12 @@ impl<'a> AsFd for io::StderrLock<'a> { unsafe { BorrowedFd::borrow_raw(2) } } } + +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsFd for stdio::Stderr { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // SAFETY: user code should not close stderr out from under the standard library + unsafe { BorrowedFd::borrow_raw(2) } + } +} diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 274af08a3881c..3d0c08d508756 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -532,6 +532,14 @@ impl<'a> AsHandle for crate::io::StdinLock<'a> { } } +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsHandle for crate::sys::stdio::Stdin { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } + } +} + #[stable(feature = "io_safety", since = "1.63.0")] impl AsHandle for crate::io::Stdout { #[inline] @@ -548,6 +556,14 @@ impl<'a> AsHandle for crate::io::StdoutLock<'a> { } } +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsHandle for crate::sys::stdio::Stdout { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } + } +} + #[stable(feature = "io_safety", since = "1.63.0")] impl AsHandle for crate::io::Stderr { #[inline] @@ -564,6 +580,14 @@ impl<'a> AsHandle for crate::io::StderrLock<'a> { } } +#[stable(feature = "io_safety", since = "1.63.0")] +impl AsHandle for crate::sys::stdio::Stderr { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } + } +} + #[stable(feature = "io_safety", since = "1.63.0")] impl AsHandle for crate::process::ChildStdin { #[inline] diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 1759e2e7f3f91..564bb881aeb13 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -121,6 +121,27 @@ impl AsRawHandle for io::Stderr { } } +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for sys::stdio::Stdin { + fn as_raw_handle(&self) -> RawHandle { + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_INPUT_HANDLE) as RawHandle }) + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for sys::stdio::Stdout { + fn as_raw_handle(&self) -> RawHandle { + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_OUTPUT_HANDLE) as RawHandle }) + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawHandle for sys::stdio::Stderr { + fn as_raw_handle(&self) -> RawHandle { + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_ERROR_HANDLE) as RawHandle }) + } +} + #[stable(feature = "asraw_stdio_locks", since = "1.35.0")] impl<'a> AsRawHandle for io::StdinLock<'a> { fn as_raw_handle(&self) -> RawHandle {