diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index 99a6ec23e..51bba2e45 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -17,6 +17,7 @@ path = "src/tee.rs" [dependencies] clap = "2.33.3" libc = "0.2.42" +retain_mut = "0.1.2" uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["libc"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index c54fa0d16..4b0f19038 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -12,6 +12,7 @@ use clap::{App, Arg}; use std::fs::OpenOptions; use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write}; use std::path::{Path, PathBuf}; +use retain_mut::RetainMut; #[cfg(unix)] use uucore::libc; @@ -93,18 +94,33 @@ fn tee(options: Options) -> Result<()> { if options.ignore_interrupts { ignore_interrupts()? } - let mut writers: Vec> = options + let mut writers: Vec = options .files .clone() .into_iter() - .map(|file| open(file, options.append)) + .map(|file| + NamedWriter { + name: file.clone(), + inner: open(file, options.append), + } + ) .collect(); - writers.push(Box::new(stdout())); - let output = &mut MultiWriter { writers }; + + + writers.insert(0, NamedWriter { + name: "'standard output'".to_owned(), + inner: Box::new(stdout()), + }); + + let mut output = MultiWriter::new(writers); let input = &mut NamedReader { inner: Box::new(stdin()) as Box, }; - if copy(input, output).is_err() || output.flush().is_err() { + + // TODO: replaced generic 'copy' call to be able to stop copying + // if all outputs are closed (due to errors) + if copy(input, &mut output).is_err() || output.flush().is_err() || + output.error_occured() { Err(Error::new(ErrorKind::Other, "")) } else { Ok(()) @@ -112,7 +128,7 @@ fn tee(options: Options) -> Result<()> { } fn open(name: String, append: bool) -> Box { - let path = PathBuf::from(name); + let path = PathBuf::from(name.clone()); let inner: Box = { let mut options = OpenOptions::new(); let mode = if append { @@ -125,55 +141,68 @@ fn open(name: String, append: bool) -> Box { Err(_) => Box::new(sink()), } }; - Box::new(NamedWriter { inner, path }) as Box + Box::new(NamedWriter { inner, name }) as Box } struct MultiWriter { - writers: Vec>, + writers: Vec, + initial_len: usize, +} + +impl MultiWriter { + fn new (writers: Vec) -> Self { + Self { + initial_len: writers.len(), + writers, + } + } + fn error_occured(&self) -> bool { + self.writers.len() != self.initial_len + } } impl Write for MultiWriter { fn write(&mut self, buf: &[u8]) -> Result { - for writer in &mut self.writers { - writer.write_all(buf)?; - } + self.writers.retain_mut(|writer| { + let result = writer.write_all(buf); + match result { + Err(f) => { + show_info!("{}: {}", writer.name, f.to_string()); + false + } + _ => true + } + }); Ok(buf.len()) } fn flush(&mut self) -> Result<()> { - for writer in &mut self.writers { - writer.flush()?; - } + self.writers.retain_mut(|writer| { + let result = writer.flush(); + match result { + Err(f) => { + show_info!("{}: {}", writer.name, f.to_string()); + false + } + _ => true + } + }); Ok(()) } } struct NamedWriter { inner: Box, - path: PathBuf, + pub name: String, } impl Write for NamedWriter { fn write(&mut self, buf: &[u8]) -> Result { - match self.inner.write(buf) { - Err(f) => { - self.inner = Box::new(sink()) as Box; - show_warning!("{}: {}", self.path.display(), f.to_string()); - Err(f) - } - okay => okay, - } + self.inner.write(buf) } fn flush(&mut self) -> Result<()> { - match self.inner.flush() { - Err(f) => { - self.inner = Box::new(sink()) as Box; - show_warning!("{}: {}", self.path.display(), f.to_string()); - Err(f) - } - okay => okay, - } + self.inner.flush() } } @@ -185,7 +214,7 @@ impl Read for NamedReader { fn read(&mut self, buf: &mut [u8]) -> Result { match self.inner.read(buf) { Err(f) => { - show_warning!("{}: {}", Path::new("stdin").display(), f.to_string()); + show_info!("{}: {}", Path::new("stdin").display(), f.to_string()); Err(f) } okay => okay, diff --git a/tests/by-util/test_tee.rs b/tests/by-util/test_tee.rs index 5819b3044..f01677ae7 100644 --- a/tests/by-util/test_tee.rs +++ b/tests/by-util/test_tee.rs @@ -60,28 +60,31 @@ fn test_tee_append() { #[test] #[cfg(target_os = "linux")] -fn test_tee_no_more_writeable_stdout() { - let (_at, mut ucmd) = at_and_ucmd!(); +fn test_tee_no_more_writeable_1() { + // equals to 'tee /dev/full out2 (); let file_out = "tee_file_out"; - let _result = ucmd + let result = ucmd .arg("/dev/full") .arg(file_out) .pipe_in(&content[..]) .fails(); - // TODO: comment in after https://github.com/uutils/coreutils/issues/1805 is fixed - // assert_eq!(at.read(file_out), content); - // assert!(result.stdout.contains(&content)); - // assert!(result.stderr.contains("No space left on device")); + assert_eq!(at.read(file_out), content); + assert!(result.stdout.contains(&content)); + assert!(result.stderr.contains("No space left on device")); } #[test] #[cfg(target_os = "linux")] -fn test_tee_no_more_writeable_stdin() { +fn test_tee_no_more_writeable_2() { + // should be equals to 'tee out1 out2 >/dev/full