|
5 | 5 |
|
6 | 6 | // spell-checker:ignore espidf nopipe |
7 | 7 |
|
| 8 | +use core::mem::ManuallyDrop; |
8 | 9 | use std::ffi::OsString; |
| 10 | +use std::fs::File; |
9 | 11 | use std::fs::OpenOptions; |
10 | | -use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin, stdout}; |
| 12 | +#[cfg(windows)] |
| 13 | +use std::io::stdout; |
| 14 | +use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin}; |
| 15 | +#[cfg(windows)] |
| 16 | +use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _}; |
11 | 17 | use std::path::PathBuf; |
12 | 18 | use uucore::display::Quotable; |
13 | 19 | use uucore::error::{UResult, strip_errno}; |
@@ -80,7 +86,7 @@ fn tee(options: &Options) -> Result<()> { |
80 | 86 | 0, |
81 | 87 | NamedWriter { |
82 | 88 | name: translate!("tee-standard-output").into(), |
83 | | - inner: Writer::Stdout(stdout()), |
| 89 | + inner: Writer::stdout_raw(), |
84 | 90 | }, |
85 | 91 | ); |
86 | 92 |
|
@@ -197,21 +203,16 @@ impl MultiWriter { |
197 | 203 |
|
198 | 204 | fn write_flush(&mut self, buf: &[u8]) -> Result<()> { |
199 | 205 | let mode = self.output_error_mode; |
200 | | - self.writers.retain_mut(|writer| { |
201 | | - let res = (|| { |
202 | | - writer.inner.write_all(buf)?; |
203 | | - writer.inner.flush() |
204 | | - })(); |
205 | | - match res { |
| 206 | + self.writers |
| 207 | + .retain_mut(|writer| match writer.inner.write_all(buf) { |
206 | 208 | Ok(()) => true, |
207 | 209 | Err(e) => { |
208 | 210 | if let Err(e) = process_error(mode, e, writer, &mut self.ignored_errors) { |
209 | 211 | self.aborted.get_or_insert(e); |
210 | 212 | } |
211 | 213 | false |
212 | 214 | } |
213 | | - } |
214 | | - }); |
| 215 | + }); |
215 | 216 | self.aborted.take().map_or( |
216 | 217 | if self.writers.is_empty() { |
217 | 218 | // This error kind will never be raised by the standard |
@@ -250,23 +251,38 @@ fn process_error( |
250 | 251 | } |
251 | 252 |
|
252 | 253 | enum Writer { |
253 | | - File(std::fs::File), |
254 | | - Stdout(std::io::Stdout), |
| 254 | + File(File), |
| 255 | + StdoutRaw(ManuallyDrop<File>), |
| 256 | +} |
| 257 | + |
| 258 | +impl Writer { |
| 259 | + #[cfg(not(windows))] |
| 260 | + fn stdout_raw() -> Self { |
| 261 | + // SAFETY: We ensure that the file descriptor is never closed by |
| 262 | + // wrapping the `File` in `ManuallyDrop`. |
| 263 | + let fd = unsafe { rustix::stdio::take_stdout() }; |
| 264 | + let f = File::from(fd); |
| 265 | + Self::StdoutRaw(ManuallyDrop::new(f)) |
| 266 | + } |
| 267 | + |
| 268 | + #[cfg(windows)] |
| 269 | + fn stdout_raw() -> Self { |
| 270 | + let handle = stdout().as_raw_handle(); |
| 271 | + let f = unsafe { File::from_raw_handle(handle) }; |
| 272 | + Self::StdoutRaw(ManuallyDrop::new(f)) |
| 273 | + } |
255 | 274 | } |
256 | 275 |
|
257 | 276 | impl Write for Writer { |
258 | 277 | fn write(&mut self, buf: &[u8]) -> Result<usize> { |
259 | 278 | match self { |
260 | 279 | Self::File(f) => f.write(buf), |
261 | | - Self::Stdout(s) => s.write(buf), |
| 280 | + Self::StdoutRaw(s) => s.write(buf), |
262 | 281 | } |
263 | 282 | } |
264 | 283 |
|
265 | 284 | fn flush(&mut self) -> Result<()> { |
266 | | - match self { |
267 | | - Self::File(f) => f.flush(), |
268 | | - Self::Stdout(s) => s.flush(), |
269 | | - } |
| 285 | + Ok(()) |
270 | 286 | } |
271 | 287 | } |
272 | 288 |
|
|
0 commit comments