From be685dc6f0df0609cfbd5a382b367caed64911ea Mon Sep 17 00:00:00 2001 From: emirmrkaljevic <93012621+emirmrkaljevic@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:19:59 +0100 Subject: [PATCH] fmt: report unreadable files and go on with the rest --- src/uu/fmt/locales/en-US.ftl | 2 +- src/uu/fmt/locales/fr-FR.ftl | 2 +- src/uu/fmt/src/fmt.rs | 38 ++++++++++++++++++++++++----------- tests/by-util/test_fmt.rs | 39 +++++++++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/uu/fmt/locales/en-US.ftl b/src/uu/fmt/locales/en-US.ftl index 4270e0a89ab..222540c29f5 100644 --- a/src/uu/fmt/locales/en-US.ftl +++ b/src/uu/fmt/locales/en-US.ftl @@ -25,7 +25,7 @@ fmt-error-invalid-tabwidth = Invalid TABWIDTH specification: {$tabwidth} fmt-error-first-option-width = invalid option -- {$option}; -WIDTH is recognized only when it is the first option; use -w N instead Try 'fmt --help' for more information. -fmt-error-read = read error +fmt-error-reading-is-directory = error reading {$file}: Is a directory fmt-error-invalid-width-malformed = invalid width: {$width} fmt-error-cannot-open-for-reading = cannot open {$file} for reading fmt-error-cannot-get-metadata = cannot get metadata for {$file} diff --git a/src/uu/fmt/locales/fr-FR.ftl b/src/uu/fmt/locales/fr-FR.ftl index 0957a973f53..bd19b929926 100644 --- a/src/uu/fmt/locales/fr-FR.ftl +++ b/src/uu/fmt/locales/fr-FR.ftl @@ -25,7 +25,7 @@ fmt-error-invalid-tabwidth = Spécification TABWIDTH invalide : {$tabwidth} fmt-error-first-option-width = option invalide -- {$option} ; -WIDTH n'est reconnu que lorsqu'il est la première option ; utilisez -w N à la place Essayez 'fmt --help' pour plus d'informations. -fmt-error-read = erreur de lecture +fmt-error-reading-is-directory = erreur de lecture de {$file} : C'est un répertoire fmt-error-invalid-width-malformed = largeur invalide : {$width} fmt-error-cannot-open-for-reading = impossible d'ouvrir {$file} en lecture fmt-error-cannot-get-metadata = impossible d'obtenir les métadonnées pour {$file} diff --git a/src/uu/fmt/src/fmt.rs b/src/uu/fmt/src/fmt.rs index c904b89ce9a..e8338c8c22b 100644 --- a/src/uu/fmt/src/fmt.rs +++ b/src/uu/fmt/src/fmt.rs @@ -12,6 +12,7 @@ use std::io::{BufReader, BufWriter, Read, Stdout, Write, stdin, stdout}; use std::path::Path; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; +use uucore::show; use uucore::translate; use uucore::format_usage; @@ -36,8 +37,6 @@ enum FmtError { InvalidTabWidth(String), #[error("{}", translate!("fmt-error-first-option-width", "option" => .0))] FirstOptionWidth(char), - #[error("{}", translate!("fmt-error-read"))] - ReadError, #[error("{}", translate!("fmt-error-invalid-width-malformed", "width" => .0.quote()))] InvalidWidthMalformed(String), } @@ -210,16 +209,31 @@ fn process_file( Box::new(stdin()) as Box } else { let path = Path::new(file_name); - let f = File::open(path).map_err_context( - || translate!("fmt-error-cannot-open-for-reading", "file" => path.quote()), - )?; - if f.metadata() - .map_err_context( - || translate!("fmt-error-cannot-get-metadata", "file" => path.quote()), - )? - .is_dir() - { - return Err(FmtError::ReadError.into()); + // Like GNU fmt, report a file that can't be read and go on with the next one. + let f = match File::open(path) { + Ok(f) => f, + Err(e) => { + show!(e.map_err_context( + || translate!("fmt-error-cannot-open-for-reading", "file" => path.quote()) + )); + return Ok(()); + } + }; + match f.metadata() { + Ok(m) if m.is_dir() => { + show!(USimpleError::new( + 1, + translate!("fmt-error-reading-is-directory", "file" => path.quote()) + )); + return Ok(()); + } + Ok(_) => {} + Err(e) => { + show!(e.map_err_context( + || translate!("fmt-error-cannot-get-metadata", "file" => path.quote()) + )); + return Ok(()); + } } Box::new(f) as Box diff --git a/tests/by-util/test_fmt.rs b/tests/by-util/test_fmt.rs index f72fa3f078f..ac53b61bb12 100644 --- a/tests/by-util/test_fmt.rs +++ b/tests/by-util/test_fmt.rs @@ -7,7 +7,7 @@ #[cfg(target_os = "linux")] use std::os::unix::ffi::OsStringExt; -use uutests::new_ucmd; +use uutests::{at_and_ucmd, new_ucmd}; #[test] fn test_invalid_arg() { @@ -253,6 +253,43 @@ fn test_fmt_non_existent_file() { .stderr_is("fmt: cannot open 'non-existing' for reading: No such file or directory\n"); } +// Windows refuses to open a directory as a file, so the error differs there. +#[cfg(not(windows))] +#[test] +fn test_fmt_directory() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir("dir"); + ucmd.arg("dir") + .fails_with_code(1) + .no_stdout() + .stderr_is("fmt: error reading 'dir': Is a directory\n"); +} + +#[cfg(not(windows))] +#[test] +fn test_fmt_continues_after_directory() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir("dir"); + at.write("a.txt", "one\ntwo\n"); + ucmd.args(&["dir", "non-existing", "a.txt"]) + .fails_with_code(1) + .stdout_is("one two\n") + .stderr_is( + "fmt: error reading 'dir': Is a directory\n\ + fmt: cannot open 'non-existing' for reading: No such file or directory\n", + ); +} + +#[test] +fn test_fmt_continues_after_missing_file() { + let (at, mut ucmd) = at_and_ucmd!(); + at.write("a.txt", "one\ntwo\n"); + ucmd.args(&["non-existing", "a.txt"]) + .fails_with_code(1) + .stdout_is("one two\n") + .stderr_is("fmt: cannot open 'non-existing' for reading: No such file or directory\n"); +} + #[test] fn test_fmt_invalid_goal() { for param in ["-g", "--goal"] {