diff --git a/src/uu/paste/src/paste.rs b/src/uu/paste/src/paste.rs index 00e62cab2e8..7ede4b78684 100644 --- a/src/uu/paste/src/paste.rs +++ b/src/uu/paste/src/paste.rs @@ -12,7 +12,8 @@ use std::iter::Cycle; use std::path::Path; use std::rc::Rc; use std::slice::Iter; -use uucore::error::{UResult, USimpleError}; +use uucore::display::Quotable; +use uucore::error::{FromIo, UError, UResult, USimpleError}; use uucore::format_usage; use uucore::i18n::charmap::mb_char_len; use uucore::line_ending::LineEnding; @@ -107,8 +108,9 @@ fn paste( ) } else { let path = Path::new(&filename); - let file = File::open(path)?; - InputSource::File(BufReader::new(file)) + let file = File::open(path) + .map_err_context(|| filename.as_os_str().maybe_quote().to_string())?; + InputSource::File(BufReader::new(file), filename) }; input_source_vec.push(input_source); @@ -136,14 +138,24 @@ fn paste( let mut delimiter_state = DelimiterState::new(&unescaped_and_encoded_delimiters); let mut output = Vec::new(); + let mut read_error: Option> = None; if serial { for input_source in &mut input_source_vec { output.clear(); - while let read @ 1.. = input_source.read_until(line_ending_byte, &mut output)? { - remove_trailing_line_ending_byte(line_ending_byte, read, &mut output); - delimiter_state.write_delimiter(&mut output); + loop { + match input_source.read_until(line_ending_byte, &mut output) { + Ok(0) => break, + Ok(read) => { + remove_trailing_line_ending_byte(line_ending_byte, read, &mut output); + delimiter_state.write_delimiter(&mut output); + } + Err(error) => { + read_error.get_or_insert(error); + break; + } + } } delimiter_state.remove_trailing_delimiter(&mut output); @@ -168,14 +180,19 @@ fn paste( if eof[i] { eof_count += 1; } else { - match input_source.read_until(line_ending_byte, &mut output)? { - 0 => { + match input_source.read_until(line_ending_byte, &mut output) { + Ok(0) => { eof[i] = true; eof_count += 1; } - read => { + Ok(read) => { remove_trailing_line_ending_byte(line_ending_byte, read, &mut output); } + Err(error) => { + read_error.get_or_insert(error); + eof[i] = true; + eof_count += 1; + } } } @@ -200,6 +217,10 @@ fn paste( } } + if let Some(error) = read_error { + return Err(error); + } + Ok(()) } @@ -376,14 +397,16 @@ impl<'a> DelimiterState<'a> { } enum InputSource { - File(BufReader), + File(BufReader, OsString), StandardInput(Rc>), } impl InputSource { fn read(&mut self, buf: &mut [u8]) -> UResult { let us = match self { - Self::File(bu) => bu.read(buf)?, + Self::File(reader, name) => reader + .read(buf) + .map_err_context(|| name.as_os_str().maybe_quote().to_string())?, Self::StandardInput(rc) => rc .try_borrow() .map_err(|bo| { @@ -398,7 +421,9 @@ impl InputSource { fn read_until(&mut self, byte: u8, buf: &mut Vec) -> UResult { let us = match self { - Self::File(bu) => bu.read_until(byte, buf)?, + Self::File(reader, name) => reader + .read_until(byte, buf) + .map_err_context(|| name.as_os_str().maybe_quote().to_string())?, Self::StandardInput(rc) => rc .try_borrow() .map_err(|bo| { diff --git a/tests/by-util/test_paste.rs b/tests/by-util/test_paste.rs index 020fceb80aa..0095c1ead25 100644 --- a/tests/by-util/test_paste.rs +++ b/tests/by-util/test_paste.rs @@ -609,3 +609,24 @@ fn test_crlf_delimiter_not_eaten_by_an_empty_line() { .succeeds() .stdout_is(expected); } + +#[test] +fn test_missing_file_error_includes_filename() { + new_ucmd!() + .arg("missing.txt") + .fails_with_code(1) + .stderr_is("paste: missing.txt: No such file or directory\n"); +} + +#[test] +fn test_directory_error_includes_filename() { + let (at, mut ucmd) = at_and_ucmd!(); + at.write("a.txt", "a\n"); + at.mkdir("d"); + + let result = ucmd.args(&["a.txt", "d"]).fails_with_code(1); + result.stderr_contains("paste: d:"); + if !cfg!(windows) { + result.stdout_is("a\t\n"); + } +}