diff --git a/src/uu/checksum_common/src/lib.rs b/src/uu/checksum_common/src/lib.rs index 56ded13f19e..67b729778f0 100644 --- a/src/uu/checksum_common/src/lib.rs +++ b/src/uu/checksum_common/src/lib.rs @@ -7,6 +7,7 @@ use std::borrow::Borrow; use std::ffi::OsString; +use std::io; use clap::builder::ValueParser; use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint}; @@ -213,7 +214,7 @@ pub fn checksum_main( line_ending, }; - perform_checksum_computation(opts, files)?; + perform_checksum_computation(io::stdout(), opts, files)?; Ok(()) } diff --git a/src/uu/cksum/src/main.rs b/src/uu/cksum/src/main.rs index 7b9fdbe7690..acce8d94a08 100644 --- a/src/uu/cksum/src/main.rs +++ b/src/uu/cksum/src/main.rs @@ -3,4 +3,4 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code.uucore::bin!(uu_cksum); -uucore::bin!(uu_cksum); +uucore::bin!(uu_cksum, no_flush); diff --git a/src/uucore/locales/en-US.ftl b/src/uucore/locales/en-US.ftl index 93a76157768..f408b4ee69e 100644 --- a/src/uucore/locales/en-US.ftl +++ b/src/uucore/locales/en-US.ftl @@ -7,6 +7,7 @@ common-tip = tip common-usage = Usage common-help = help common-version = version +common-write-error = write error # Common clap error messages clap-error-unexpected-argument = { $error_word }: unexpected argument '{ $arg }' found diff --git a/src/uucore/locales/fr-FR.ftl b/src/uucore/locales/fr-FR.ftl index 98a18fbce39..507a536cc6e 100644 --- a/src/uucore/locales/fr-FR.ftl +++ b/src/uucore/locales/fr-FR.ftl @@ -7,6 +7,7 @@ common-tip = conseil common-usage = Utilisation common-help = aide common-version = version +common-write-error = erreur d'écriture # Messages d'erreur clap communs clap-error-unexpected-argument = { $error_word } : argument inattendu '{ $arg }' trouvé diff --git a/src/uucore/src/lib/features/checksum/compute.rs b/src/uucore/src/lib/features/checksum/compute.rs index b6e1e12d780..54526adea90 100644 --- a/src/uucore/src/lib/features/checksum/compute.rs +++ b/src/uucore/src/lib/features/checksum/compute.rs @@ -7,7 +7,7 @@ use std::ffi::OsStr; use std::fs::File; -use std::io::{self, BufReader, Read, Write}; +use std::io; use std::path::Path; use crate::checksum::{ @@ -131,12 +131,13 @@ impl OutputFormat { } } -fn print_legacy_checksum( +fn write_legacy_checksum( + mut w: impl io::Write, options: &ChecksumComputeOptions, filename: &OsStr, sum: &DigestOutput, size: usize, -) { +) -> io::Result<()> { debug_assert!(options.algo_kind.is_legacy()); debug_assert!(matches!(sum, DigestOutput::U16(_) | DigestOutput::Crc(_))); @@ -148,32 +149,40 @@ fn print_legacy_checksum( // Print the sum match (options.algo_kind, sum) { - (SizedAlgoKind::Sysv, DigestOutput::U16(sum)) => print!( + (SizedAlgoKind::Sysv, DigestOutput::U16(sum)) => write!( + w, "{prefix}{sum} {}", size.div_ceil(options.algo_kind.bitlen()), - ), + )?, (SizedAlgoKind::Bsd, DigestOutput::U16(sum)) => { // The BSD checksum output is 5 digit integer let bsd_width = 5; - print!( + write!( + w, "{prefix}{sum:0bsd_width$} {:bsd_width$}", size.div_ceil(options.algo_kind.bitlen()), - ); + )?; } (SizedAlgoKind::Crc | SizedAlgoKind::Crc32b, DigestOutput::Crc(sum)) => { - print!("{prefix}{sum} {size}"); + write!(w, "{prefix}{sum} {size}")?; } (algo, output) => unreachable!("Bug: Invalid legacy checksum ({algo:?}, {output:?})"), } // Print the filename after a space if not stdin if escaped_filename != "-" { - print!(" "); - let _dropped_result = io::stdout().write_all(escaped_filename.as_bytes()); + write!(w, " ")?; + w.write_all(escaped_filename.as_bytes())?; } + Ok(()) } -fn print_tagged_checksum(options: &ChecksumComputeOptions, filename: &OsStr, sum: &String) { +fn write_tagged_checksum( + mut w: impl io::Write, + options: &ChecksumComputeOptions, + filename: &OsStr, + sum: &String, +) -> io::Result<()> { let (escaped_filename, prefix) = if options.line_ending == LineEnding::Nul { (filename.to_string_lossy().to_string(), "") } else { @@ -181,21 +190,22 @@ fn print_tagged_checksum(options: &ChecksumComputeOptions, filename: &OsStr, sum }; // Print algo name and opening parenthesis. - print!("{prefix}{} (", options.algo_kind.to_tag()); + write!(w, "{prefix}{} (", options.algo_kind.to_tag())?; // Print filename - let _dropped_result = io::stdout().write_all(escaped_filename.as_bytes()); + w.write_all(escaped_filename.as_bytes())?; // Print closing parenthesis and sum - print!(") = {sum}"); + write!(w, ") = {sum}") } -fn print_untagged_checksum( +fn write_untagged_checksum( + mut w: impl io::Write, options: &ChecksumComputeOptions, filename: &OsStr, sum: &String, reading_mode: ReadingMode, -) { +) -> io::Result<()> { let (escaped_filename, prefix) = if options.line_ending == LineEnding::Nul { (filename.to_string_lossy().to_string(), "") } else { @@ -203,10 +213,10 @@ fn print_untagged_checksum( }; // Print checksum and reading mode flag - print!("{prefix}{sum} {}", reading_mode.as_char()); + write!(w, "{prefix}{sum} {}", reading_mode.as_char())?; // Print filename - let _dropped_result = io::stdout().write_all(escaped_filename.as_bytes()); + w.write_all(escaped_filename.as_bytes()) } /// Calculate checksum @@ -215,8 +225,13 @@ fn print_untagged_checksum( /// /// * `options` - CLI options for the assigning checksum algorithm /// * `files` - A iterator of [`OsStr`] which is a bunch of files that are using for calculating checksum -pub fn perform_checksum_computation<'a, I>(options: ChecksumComputeOptions, files: I) -> UResult<()> +pub fn perform_checksum_computation<'a, W, I>( + mut w: W, + options: ChecksumComputeOptions, + files: I, +) -> UResult<()> where + W: io::Write, I: Iterator, { let mut files = files.peekable(); @@ -239,11 +254,11 @@ where } // Handle the file input - let mut file = BufReader::with_capacity( + let mut file = io::BufReader::with_capacity( READ_BUFFER_SIZE, if filename == "-" { stdin_buf = io::stdin(); - Box::new(stdin_buf) as Box + Box::new(stdin_buf) as Box } else { file_buf = match File::open(filepath) { Ok(file) => file, @@ -252,7 +267,7 @@ where continue; } }; - Box::new(file_buf) as Box + Box::new(file_buf) as Box }, ); @@ -275,30 +290,38 @@ where match options.output_format { OutputFormat::Raw => { // Cannot handle multiple files anyway, output immediately. - digest_output.write_raw(io::stdout())?; + digest_output + .write_raw(&mut w) + .map_err(ChecksumError::Write)?; return Ok(()); } OutputFormat::Legacy => { - print_legacy_checksum(&options, filename, &digest_output, sz); - } - OutputFormat::Tagged(digest_format) => { - print_tagged_checksum( - &options, - filename, - &encode_sum(digest_output, digest_format)?, - ); - } - OutputFormat::Untagged(digest_format, reading_mode) => { - print_untagged_checksum( - &options, - filename, - &encode_sum(digest_output, digest_format)?, - reading_mode, - ); + write_legacy_checksum(&mut w, &options, filename, &digest_output, sz) + .map_err(ChecksumError::Write)?; } + OutputFormat::Tagged(digest_format) => write_tagged_checksum( + &mut w, + &options, + filename, + &encode_sum(digest_output, digest_format)?, + ) + .map_err(ChecksumError::Write)?, + OutputFormat::Untagged(digest_format, reading_mode) => write_untagged_checksum( + &mut w, + &options, + filename, + &encode_sum(digest_output, digest_format)?, + reading_mode, + ) + .map_err(ChecksumError::Write)?, } - print!("{}", options.line_ending); + write!(w, "{}", options.line_ending).map_err(ChecksumError::Write)?; + } + + if options.line_ending != LineEnding::Newline { + // Flush the writer if we didn't write newlines. + w.flush().map_err(ChecksumError::Write)?; } Ok(()) } diff --git a/src/uucore/src/lib/features/checksum/mod.rs b/src/uucore/src/lib/features/checksum/mod.rs index d18e2adaad8..d11cf5d6130 100644 --- a/src/uucore/src/lib/features/checksum/mod.rs +++ b/src/uucore/src/lib/features/checksum/mod.rs @@ -12,12 +12,12 @@ use std::num::IntErrorKind; use os_display::Quotable; use thiserror::Error; -use crate::error::{UError, UResult}; -use crate::show_error; +use crate::error::{UError, UResult, strip_errno}; use crate::sum::{ Blake2b, Blake3, Bsd, CRC32B, Crc, Digest, DigestOutput, DigestWriter, Md5, Sha1, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Sha224, Sha256, Sha384, Sha512, Shake128, Shake256, Sm3, SysV, }; +use crate::{show_error, translate}; pub mod compute; pub mod validate; @@ -432,8 +432,8 @@ pub enum ChecksumError { NeedAlgorithmToHash, #[error("unknown algorithm: {0}: clap should have prevented this case")] UnknownAlgorithm(String), - #[error("")] - Io(#[from] io::Error), + #[error("{}: {}", translate!("common-write-error"), strip_errno(.0))] + Write(io::Error), } impl UError for ChecksumError { diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 16a18e3b3d4..0304896c786 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -181,13 +181,9 @@ pub fn get_canonical_util_name(util_name: &str) -> &str { } } -/// Execute utility code for `util`. -/// -/// This macro expands to a main function that invokes the `uumain` function in `util` -/// Exits with code returned by `uumain`. #[macro_export] -macro_rules! bin { - ($util:ident) => { +macro_rules! bin_inner { + ($util:ident, $post:expr) => { pub fn main() { use std::io::Write; use uucore::locale; @@ -211,13 +207,28 @@ macro_rules! bin { // execute utility code let code = $util::uumain(uucore::args_os()); + $post + + std::process::exit(code); + } + }; +} +/// Execute utility code for `util`. +/// +/// This macro expands to a main function that invokes the `uumain` function in `util` +/// Exits with code returned by `uumain`. +#[macro_export] +macro_rules! bin { + ($util:ident, no_flush) => { + ::uucore::bin_inner! {$util, {}} + }; + ($util:ident) => { + ::uucore::bin_inner! {$util, { // (defensively) flush stdout for utility prior to exit; see if let Err(e) = std::io::stdout().flush() { eprintln!("Error flushing stdout: {e}"); } - - std::process::exit(code); - } + }} }; }