mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge pull request #2221 from jfinkels/head-display-multiple-errors-2
head: display errors for each input file instead of terminating at the first error
This commit is contained in:
commit
efb781f59a
3 changed files with 52 additions and 40 deletions
|
@ -2,7 +2,7 @@ use clap::{App, Arg};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
|
use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||||
use uucore::{crash, executable, show_error};
|
use uucore::{crash, executable, show_error, show_error_custom_description};
|
||||||
|
|
||||||
const EXIT_FAILURE: i32 = 1;
|
const EXIT_FAILURE: i32 = 1;
|
||||||
const EXIT_SUCCESS: i32 = 0;
|
const EXIT_SUCCESS: i32 = 0;
|
||||||
|
@ -400,7 +400,8 @@ fn head_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Resul
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uu_head(options: &HeadOptions) {
|
fn uu_head(options: &HeadOptions) -> Result<(), u32> {
|
||||||
|
let mut error_count = 0;
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for fname in &options.files {
|
for fname in &options.files {
|
||||||
let res = match fname.as_str() {
|
let res = match fname.as_str() {
|
||||||
|
@ -433,30 +434,22 @@ fn uu_head(options: &HeadOptions) {
|
||||||
name => {
|
name => {
|
||||||
let mut file = match std::fs::File::open(name) {
|
let mut file = match std::fs::File::open(name) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(err) => match err.kind() {
|
Err(err) => {
|
||||||
ErrorKind::NotFound => {
|
let prefix = format!("cannot open '{}' for reading", name);
|
||||||
crash!(
|
match err.kind() {
|
||||||
EXIT_FAILURE,
|
ErrorKind::NotFound => {
|
||||||
"head: cannot open '{}' for reading: No such file or directory",
|
show_error_custom_description!(prefix, "No such file or directory");
|
||||||
name
|
}
|
||||||
);
|
ErrorKind::PermissionDenied => {
|
||||||
|
show_error_custom_description!(prefix, "Permission denied");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
show_error_custom_description!(prefix, "{}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ErrorKind::PermissionDenied => {
|
error_count += 1;
|
||||||
crash!(
|
continue;
|
||||||
EXIT_FAILURE,
|
}
|
||||||
"head: cannot open '{}' for reading: Permission denied",
|
|
||||||
name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
crash!(
|
|
||||||
EXIT_FAILURE,
|
|
||||||
"head: cannot open '{}' for reading: {}",
|
|
||||||
name,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
if (options.files.len() > 1 && !options.quiet) || options.verbose {
|
if (options.files.len() > 1 && !options.quiet) || options.verbose {
|
||||||
if !first {
|
if !first {
|
||||||
|
@ -468,21 +461,22 @@ fn uu_head(options: &HeadOptions) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
if fname.as_str() == "-" {
|
let name = if fname.as_str() == "-" {
|
||||||
crash!(
|
"standard input"
|
||||||
EXIT_FAILURE,
|
|
||||||
"head: error reading standard input: Input/output error"
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
crash!(
|
fname
|
||||||
EXIT_FAILURE,
|
};
|
||||||
"head: error reading {}: Input/output error",
|
let prefix = format!("error reading {}", name);
|
||||||
fname
|
show_error_custom_description!(prefix, "Input/output error");
|
||||||
);
|
error_count += 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
if error_count > 0 {
|
||||||
|
Err(error_count)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
|
@ -492,9 +486,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
crash!(EXIT_FAILURE, "head: {}", s);
|
crash!(EXIT_FAILURE, "head: {}", s);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
uu_head(&args);
|
match uu_head(&args) {
|
||||||
|
Ok(_) => EXIT_SUCCESS,
|
||||||
EXIT_SUCCESS
|
Err(_) => EXIT_FAILURE,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -162,6 +162,18 @@ fn test_no_such_file_or_directory() {
|
||||||
.stderr_contains("cannot open 'no_such_file.toml' for reading: No such file or directory");
|
.stderr_contains("cannot open 'no_such_file.toml' for reading: No such file or directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test that each non-existent files gets its own error message printed.
|
||||||
|
#[test]
|
||||||
|
fn test_multiple_nonexistent_files() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["bogusfile1", "bogusfile2"])
|
||||||
|
.fails()
|
||||||
|
.stdout_does_not_contain("==> bogusfile1 <==")
|
||||||
|
.stderr_contains("cannot open 'bogusfile1' for reading: No such file or directory")
|
||||||
|
.stdout_does_not_contain("==> bogusfile2 <==")
|
||||||
|
.stderr_contains("cannot open 'bogusfile2' for reading: No such file or directory");
|
||||||
|
}
|
||||||
|
|
||||||
// there was a bug not caught by previous tests
|
// there was a bug not caught by previous tests
|
||||||
// where for negative n > 3, the total amount of lines
|
// where for negative n > 3, the total amount of lines
|
||||||
// was correct, but it would eat from the second line
|
// was correct, but it would eat from the second line
|
||||||
|
|
|
@ -315,7 +315,12 @@ impl CmdResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stdout_does_not_contain<T: AsRef<str>>(&self, cmp: T) -> &CmdResult {
|
pub fn stdout_does_not_contain<T: AsRef<str>>(&self, cmp: T) -> &CmdResult {
|
||||||
assert!(!self.stdout_str().contains(cmp.as_ref()));
|
assert!(
|
||||||
|
!self.stdout_str().contains(cmp.as_ref()),
|
||||||
|
"'{}' contains '{}' but should not",
|
||||||
|
self.stdout_str(),
|
||||||
|
cmp.as_ref(),
|
||||||
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue