From 66ccb1a47918a9b81f8a5edb2f18da3db4980af8 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 2 Jun 2024 23:31:52 +0200 Subject: [PATCH 01/13] cksum/hashsum: add support of --check with base64 --- src/uucore/src/lib/features/checksum.rs | 71 ++++++++++++++++++++++++- tests/by-util/test_cksum.rs | 20 +++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 8de289483..908db5b8b 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -4,6 +4,7 @@ // file that was distributed with this source code. // spell-checker:ignore anotherfile invalidchecksum +use data_encoding::BASE64; use os_display::Quotable; use regex::Regex; use std::{ @@ -24,6 +25,7 @@ use crate::{ }, util_name, }; +use quick_error::quick_error; use std::io::stdin; use std::io::BufRead; @@ -314,6 +316,8 @@ pub fn detect_algo(algo: &str, length: Option) -> UResult // 2. [* ] // 3. [*] (only one space) const ALGO_BASED_REGEX: &str = r"^\s*\\?(?P(?:[A-Z0-9]+|BLAKE2b))(?:-(?P\d+))?\s?\((?P.*)\)\s*=\s*(?P[a-fA-F0-9]+)$"; +const ALGO_BASED_REGEX_BASE64: &str = r"^\s*\\?(?P(?:[A-Z0-9]+|BLAKE2b))(?:-(?P\d+))?\s?\((?P.*)\)\s*=\s*(?P[A-Za-z0-9+/]+={0,2})$"; + const DOUBLE_SPACE_REGEX: &str = r"^(?P[a-fA-F0-9]+)\s{2}(?P.*)$"; // In this case, we ignore the * @@ -338,6 +342,7 @@ fn determine_regex( let algo_based_regex = Regex::new(ALGO_BASED_REGEX).unwrap(); let double_space_regex = Regex::new(DOUBLE_SPACE_REGEX).unwrap(); let single_space_regex = Regex::new(SINGLE_SPACE_REGEX).unwrap(); + let algo_based_regex_base64 = Regex::new(ALGO_BASED_REGEX_BASE64).unwrap(); for line in lines { let line_trim = line.trim(); @@ -347,6 +352,8 @@ fn determine_regex( return Ok((double_space_regex, false)); } else if single_space_regex.is_match(line_trim) { return Ok((single_space_regex, false)); + } else if algo_based_regex_base64.is_match(line_trim) { + return Ok((algo_based_regex_base64, true)); } } @@ -359,6 +366,40 @@ fn determine_regex( ) } +// Function to convert bytes to a hexadecimal string +fn bytes_to_hex(bytes: &[u8]) -> String { + bytes + .iter() + .map(|byte| format!("{:02x}", byte)) + .collect::>() + .join("") +} + +// Function to get the expected checksum + +fn get_expected_checksum( + filename: &str, + caps: ®ex::Captures, + chosen_regex: &Regex, +) -> UResult { + if chosen_regex.as_str() == ALGO_BASED_REGEX_BASE64 { + let ck = caps.name("checksum").unwrap().as_str(); + match BASE64.decode(ck.as_bytes()) { + Ok(decoded_bytes) => { + match std::str::from_utf8(&decoded_bytes) { + Ok(decoded_str) => Ok(decoded_str.to_string()), + Err(_) => Ok(bytes_to_hex(&decoded_bytes)), // Handle as raw bytes if not valid UTF-8 + } + } + Err(_) => Err(Box::new( + ChecksumError::NoProperlyFormattedChecksumLinesFound((&filename).to_string()), + )), + } + } else { + Ok(caps.name("checksum").unwrap().as_str().to_string()) + } +} + /*** * Do the checksum validation (can be strict or not) */ @@ -423,7 +464,8 @@ where filename_to_check = &filename_to_check[1..]; } - let expected_checksum = caps.name("checksum").unwrap().as_str(); + let expected_checksum = + get_expected_checksum(&filename_to_check, &caps, &chosen_regex)?; // If the algo_name is provided, we use it, otherwise we try to detect it let (algo_name, length) = if is_algo_based_format { @@ -964,4 +1006,31 @@ mod tests { let result = determine_regex(filename, false, &lines_invalid); assert!(result.is_err()); } + + #[test] + fn test_get_expected_checksum() { + let re = Regex::new(ALGO_BASED_REGEX_BASE64).unwrap(); + let caps = re + .captures("SHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") + .unwrap(); + + let result = get_expected_checksum("filename", &caps, &re); + + assert_eq!( + result.unwrap(), + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ); + } + + #[test] + fn test_get_expected_checksum_invalid() { + let re = Regex::new(ALGO_BASED_REGEX_BASE64).unwrap(); + let caps = re + .captures("SHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU") + .unwrap(); + + let result = get_expected_checksum("filename", &caps, &re); + + assert!(result.is_err()); + } } diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index 04a05124c..d1a0d224e 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -1201,3 +1201,23 @@ fn test_check_directory_error() { .fails() .stderr_contains(err_msg); } + +#[test] +fn test_check_base64_hashes() { + let hashes = + "MD5 (empty) = 1B2M2Y8AsgTpgAmY7PhCfg==\nSHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=\nBLAKE2b (empty) = eGoC90IBWQPGxv2FJVLScpEvR0DhWEdhiobiF/cfVBnSXhAxr+5YUxOJZESTTrBLkDpoWxRIt1XVb3Aa/pvizg==\n" + ; + + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.touch("empty"); + at.write("check", hashes); + + scene + .ucmd() + .arg("--check") + .arg(at.subdir.join("check")) + .succeeds() + .stdout_is("empty: OK\nempty: OK\nempty: OK\n"); +} From edae51d1a61edeee2e81896c1eb0949962b33310 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 4 Jun 2024 21:46:37 +0200 Subject: [PATCH 02/13] cksum/hashsum: move to quick-error --- Cargo.lock | 1 + src/uucore/Cargo.toml | 3 +- src/uucore/src/lib/features/checksum.rs | 116 ++++++++++-------------- 3 files changed, 49 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 032c8e77e..4395dfe3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3443,6 +3443,7 @@ dependencies = [ "number_prefix", "once_cell", "os_display", + "quick-error", "regex", "sha1", "sha2", diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 5730803c9..3a87fe26a 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -52,6 +52,7 @@ blake2b_simd = { workspace = true, optional = true } blake3 = { workspace = true, optional = true } sm3 = { workspace = true, optional = true } regex = { workspace = true, optional = true } +quick-error = { workspace = true, optional = true } [target.'cfg(unix)'.dependencies] walkdir = { workspace = true, optional = true } @@ -76,7 +77,7 @@ default = [] # * non-default features backup-control = [] colors = [] -checksum = ["regex", "sum"] +checksum = ["quick-error", "regex", "sum"] encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"] entries = ["libc"] fs = ["dunce", "libc", "winapi-util", "windows-sys"] diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 908db5b8b..fbea911ef 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -8,9 +8,7 @@ use data_encoding::BASE64; use os_display::Quotable; use regex::Regex; use std::{ - error::Error, ffi::OsStr, - fmt::Display, fs::File, io::{self, BufReader, Read}, path::Path, @@ -70,82 +68,60 @@ pub struct HashAlgorithm { pub bits: usize, } -#[derive(Debug)] -pub enum ChecksumError { - RawMultipleFiles, - IgnoreNotCheck, - InvalidOutputSizeForSha3, - BitsRequiredForSha3, - BitsRequiredForShake128, - BitsRequiredForShake256, - UnknownAlgorithm, - InvalidLength, - LengthOnlyForBlake2b, - BinaryTextConflict, - AlgorithmNotSupportedWithCheck, - CombineMultipleAlgorithms, - NeedAlgorithmToHash, - NoProperlyFormattedChecksumLinesFound(String), +quick_error! { + #[derive(Debug)] + pub enum ChecksumError { + RawMultipleFiles { + display("the --raw option is not supported with multiple files") + } + IgnoreNotCheck { + display("the --ignore-missing option is meaningful only when verifying checksums") + } + InvalidOutputSizeForSha3 { + display("Invalid output size for SHA3 (expected 224, 256, 384, or 512)") + } + BitsRequiredForSha3 { + display("--bits required for SHA3") + } + BitsRequiredForShake128 { + display("--bits required for SHAKE128") + } + BitsRequiredForShake256 { + display("--bits required for SHAKE256") + } + UnknownAlgorithm { + display("unknown algorithm: clap should have prevented this case") + } + InvalidLength { + display("length is not a multiple of 8") + } + LengthOnlyForBlake2b { + display("--length is only supported with --algorithm=blake2b") + } + BinaryTextConflict { + display("the --binary and --text options are meaningless when verifying checksums") + } + AlgorithmNotSupportedWithCheck { + display("--check is not supported with --algorithm={{bsd,sysv,crc}}") + } + CombineMultipleAlgorithms { + display("You cannot combine multiple hash algorithms!") + } + NeedAlgorithmToHash { + display("Needs an algorithm to hash with.\nUse --help for more information.") + } + NoProperlyFormattedChecksumLinesFound(filename: String) { + display("{filename}: no properly formatted checksum lines found") + } + } } -impl Error for ChecksumError {} - impl UError for ChecksumError { fn code(&self) -> i32 { 1 } } -impl Display for ChecksumError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::RawMultipleFiles => { - write!(f, "the --raw option is not supported with multiple files") - } - Self::IgnoreNotCheck => write!( - f, - "the --ignore-missing option is meaningful only when verifying checksums" - ), - Self::InvalidOutputSizeForSha3 => write!( - f, - "Invalid output size for SHA3 (expected 224, 256, 384, or 512)" - ), - Self::BitsRequiredForSha3 => write!(f, "--bits required for SHA3"), - Self::BitsRequiredForShake128 => write!(f, "--bits required for SHAKE128"), - Self::BitsRequiredForShake256 => write!(f, "--bits required for SHAKE256"), - Self::UnknownAlgorithm => { - write!(f, "unknown algorithm: clap should have prevented this case") - } - Self::InvalidLength => write!(f, "length is not a multiple of 8"), - Self::LengthOnlyForBlake2b => { - write!(f, "--length is only supported with --algorithm=blake2b") - } - Self::BinaryTextConflict => write!( - f, - "the --binary and --text options are meaningless when verifying checksums" - ), - Self::AlgorithmNotSupportedWithCheck => write!( - f, - "--check is not supported with --algorithm={{bsd,sysv,crc}}" - ), - Self::CombineMultipleAlgorithms => { - write!(f, "You cannot combine multiple hash algorithms!") - } - Self::NeedAlgorithmToHash => write!( - f, - "Needs an algorithm to hash with.\nUse --help for more information." - ), - Self::NoProperlyFormattedChecksumLinesFound(filename) => { - write!( - f, - "{}: no properly formatted checksum lines found", - filename - ) - } - } - } -} - /// Creates a SHA3 hasher instance based on the specified bits argument. /// /// # Returns From cc8bda5defde43321b8028c565bb0302e7429deb Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 19 Jun 2024 18:31:34 +0200 Subject: [PATCH 03/13] cksum/hashsum: move to thiserror --- Cargo.lock | 1 - src/uucore/Cargo.toml | 3 +- src/uucore/src/lib/features/checksum.rs | 100 ++++++++++-------------- 3 files changed, 42 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4395dfe3e..032c8e77e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3443,7 +3443,6 @@ dependencies = [ "number_prefix", "once_cell", "os_display", - "quick-error", "regex", "sha1", "sha2", diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 3a87fe26a..b9122735f 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -52,7 +52,6 @@ blake2b_simd = { workspace = true, optional = true } blake3 = { workspace = true, optional = true } sm3 = { workspace = true, optional = true } regex = { workspace = true, optional = true } -quick-error = { workspace = true, optional = true } [target.'cfg(unix)'.dependencies] walkdir = { workspace = true, optional = true } @@ -77,7 +76,7 @@ default = [] # * non-default features backup-control = [] colors = [] -checksum = ["quick-error", "regex", "sum"] +checksum = ["thiserror", "regex", "sum"] encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"] entries = ["libc"] fs = ["dunce", "libc", "winapi-util", "windows-sys"] diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index fbea911ef..a2b341b40 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -23,9 +23,9 @@ use crate::{ }, util_name, }; -use quick_error::quick_error; use std::io::stdin; use std::io::BufRead; +use thiserror::Error; pub const ALGORITHM_OPTIONS_SYSV: &str = "sysv"; pub const ALGORITHM_OPTIONS_BSD: &str = "bsd"; @@ -68,52 +68,36 @@ pub struct HashAlgorithm { pub bits: usize, } -quick_error! { - #[derive(Debug)] - pub enum ChecksumError { - RawMultipleFiles { - display("the --raw option is not supported with multiple files") - } - IgnoreNotCheck { - display("the --ignore-missing option is meaningful only when verifying checksums") - } - InvalidOutputSizeForSha3 { - display("Invalid output size for SHA3 (expected 224, 256, 384, or 512)") - } - BitsRequiredForSha3 { - display("--bits required for SHA3") - } - BitsRequiredForShake128 { - display("--bits required for SHAKE128") - } - BitsRequiredForShake256 { - display("--bits required for SHAKE256") - } - UnknownAlgorithm { - display("unknown algorithm: clap should have prevented this case") - } - InvalidLength { - display("length is not a multiple of 8") - } - LengthOnlyForBlake2b { - display("--length is only supported with --algorithm=blake2b") - } - BinaryTextConflict { - display("the --binary and --text options are meaningless when verifying checksums") - } - AlgorithmNotSupportedWithCheck { - display("--check is not supported with --algorithm={{bsd,sysv,crc}}") - } - CombineMultipleAlgorithms { - display("You cannot combine multiple hash algorithms!") - } - NeedAlgorithmToHash { - display("Needs an algorithm to hash with.\nUse --help for more information.") - } - NoProperlyFormattedChecksumLinesFound(filename: String) { - display("{filename}: no properly formatted checksum lines found") - } - } +#[derive(Debug, Error)] +pub enum ChecksumError { + #[error("the --raw option is not supported with multiple files")] + RawMultipleFiles, + #[error("the --ignore-missing option is meaningful only when verifying checksums")] + IgnoreNotCheck, + #[error("Invalid output size for SHA3 (expected 224, 256, 384, or 512)")] + InvalidOutputSizeForSha3, + #[error("--bits required for SHA3")] + BitsRequiredForSha3, + #[error("--bits required for SHAKE128")] + BitsRequiredForShake128, + #[error("--bits required for SHAKE256")] + BitsRequiredForShake256, + #[error("unknown algorithm: clap should have prevented this case")] + UnknownAlgorithm, + #[error("length is not a multiple of 8")] + InvalidLength, + #[error("--length is only supported with --algorithm=blake2b")] + LengthOnlyForBlake2b, + #[error("the --binary and --text options are meaningless when verifying checksums")] + BinaryTextConflict, + #[error("--check is not supported with --algorithm={{bsd,sysv,crc}}")] + AlgorithmNotSupportedWithCheck, + #[error("You cannot combine multiple hash algorithms!")] + CombineMultipleAlgorithms, + #[error("Needs an algorithm to hash with.\nUse --help for more information.")] + NeedAlgorithmToHash, + #[error("{filename}: no properly formatted checksum lines found")] + NoProperlyFormattedChecksumLinesFound { filename: String }, } impl UError for ChecksumError { @@ -332,14 +316,10 @@ fn determine_regex( return Ok((algo_based_regex_base64, true)); } } - - Err( - ChecksumError::NoProperlyFormattedChecksumLinesFound(get_filename_for_output( - filename, - input_is_stdin, - )) - .into(), - ) + Err(ChecksumError::NoProperlyFormattedChecksumLinesFound { + filename: get_filename_for_output(filename, input_is_stdin), + } + .into()) } // Function to convert bytes to a hexadecimal string @@ -368,7 +348,9 @@ fn get_expected_checksum( } } Err(_) => Err(Box::new( - ChecksumError::NoProperlyFormattedChecksumLinesFound((&filename).to_string()), + ChecksumError::NoProperlyFormattedChecksumLinesFound { + filename: (&filename).to_string(), + }, )), } } else { @@ -579,9 +561,9 @@ where // return an error if !properly_formatted { if !status { - return Err(ChecksumError::NoProperlyFormattedChecksumLinesFound( - get_filename_for_output(filename_input, input_is_stdin), - ) + return Err(ChecksumError::NoProperlyFormattedChecksumLinesFound { + filename: get_filename_for_output(filename_input, input_is_stdin), + } .into()); } set_exit_code(1); From e6aad9505547574c267232057528917b81f4306c Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 3 Jun 2024 20:17:00 +0200 Subject: [PATCH 04/13] cksum/hashsum: move the file mgmt into a function --- src/uucore/src/lib/features/checksum.rs | 36 +++++++++++++------------ 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index a2b341b40..9be3d6f56 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -358,6 +358,24 @@ fn get_expected_checksum( } } +fn get_input_file(filename_input: &OsStr, input_is_stdin: bool) -> UResult> { + if input_is_stdin { + Ok(Box::new(stdin())) // Use stdin if "-" is specified + } else { + match File::open(filename_input) { + Ok(f) => Ok(Box::new(f)), + Err(_) => Err(io::Error::new( + io::ErrorKind::Other, + format!( + "{}: No such file or directory", + filename_input.to_string_lossy() + ), + ) + .into()), + } + } +} + /*** * Do the checksum validation (can be strict or not) */ @@ -386,23 +404,7 @@ where let mut properly_formatted = false; let input_is_stdin = filename_input == OsStr::new("-"); - let file: Box = if input_is_stdin { - Box::new(stdin()) // Use stdin if "-" is specified - } else { - match File::open(filename_input) { - Ok(f) => Box::new(f), - Err(_) => { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "{}: No such file or directory", - filename_input.to_string_lossy() - ), - ) - .into()); - } - } - }; + let file: Box = get_input_file(filename_input, input_is_stdin)?; let reader = BufReader::new(file); let lines: Vec = reader.lines().collect::>()?; From a6bae64ddefc4636a2b00641bf40dd173329b73d Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 3 Jun 2024 20:40:16 +0200 Subject: [PATCH 05/13] cksum/hashsum: use a struct to keep the results --- src/uucore/src/lib/features/checksum.rs | 90 ++++++++++++------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 9be3d6f56..1b6fd1ba3 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -68,6 +68,14 @@ pub struct HashAlgorithm { pub bits: usize, } +struct ChecksumResult { + pub bad_format: i32, + pub failed_cksum: i32, + pub failed_open_file: i32, + pub correct_format: i32, + pub properly_formatted: bool, +} + #[derive(Debug, Error)] pub enum ChecksumError { #[error("the --raw option is not supported with multiple files")] @@ -142,31 +150,25 @@ pub fn create_sha3(bits: Option) -> UResult { } #[allow(clippy::comparison_chain)] -pub fn cksum_output( - bad_format: i32, - failed_cksum: i32, - failed_open_file: i32, - ignore_missing: bool, - status: bool, -) { - if bad_format == 1 { - show_warning_caps!("{} line is improperly formatted", bad_format); - } else if bad_format > 1 { - show_warning_caps!("{} lines are improperly formatted", bad_format); +fn cksum_output(res: &ChecksumResult, ignore_missing: bool, status: bool) { + if res.bad_format == 1 { + show_warning_caps!("{} line is improperly formatted", res.bad_format); + } else if res.bad_format > 1 { + show_warning_caps!("{} lines are improperly formatted", res.bad_format); } if !status { - if failed_cksum == 1 { - show_warning_caps!("{} computed checksum did NOT match", failed_cksum); - } else if failed_cksum > 1 { - show_warning_caps!("{} computed checksums did NOT match", failed_cksum); + if res.failed_cksum == 1 { + show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum); + } else if res.failed_cksum > 1 { + show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum); } } if !ignore_missing { - if failed_open_file == 1 { - show_warning_caps!("{} listed file could not be read", failed_open_file); - } else if failed_open_file > 1 { - show_warning_caps!("{} listed files could not be read", failed_open_file); + if res.failed_open_file == 1 { + show_warning_caps!("{} listed file could not be read", res.failed_open_file); + } else if res.failed_open_file > 1 { + show_warning_caps!("{} listed files could not be read", res.failed_open_file); } } } @@ -397,11 +399,13 @@ where { // if cksum has several input files, it will print the result for each file for filename_input in files { - let mut bad_format = 0; - let mut failed_cksum = 0; - let mut failed_open_file = 0; - let mut correct_format = 0; - let mut properly_formatted = false; + let mut res = ChecksumResult { + bad_format: 0, + failed_cksum: 0, + failed_open_file: 0, + correct_format: 0, + properly_formatted: false, + }; let input_is_stdin = filename_input == OsStr::new("-"); let file: Box = get_input_file(filename_input, input_is_stdin)?; @@ -413,7 +417,7 @@ where for (i, line) in lines.iter().enumerate() { if let Some(caps) = chosen_regex.captures(line) { - properly_formatted = true; + res.properly_formatted = true; let mut filename_to_check = caps.name("filename").unwrap().as_str(); if filename_to_check.starts_with('*') @@ -436,14 +440,14 @@ where // (for example SHA1 (f) = d...) // Also handle the case cksum -s sm3 but the file contains other formats if algo_name_input.is_some() && algo_name_input != Some(&algorithm) { - bad_format += 1; - properly_formatted = false; + res.bad_format += 1; + res.properly_formatted = false; continue; } if !SUPPORTED_ALGORITHMS.contains(&algorithm.as_str()) { // Not supported algo, leave early - properly_formatted = false; + res.properly_formatted = false; continue; } @@ -452,7 +456,7 @@ where if bits_value % 8 == 0 { Some(Some(bits_value / 8)) } else { - properly_formatted = false; + res.properly_formatted = false; None // Return None to signal a divisibility issue } }); @@ -474,13 +478,13 @@ where if is_algo_based_format && algo_name_input.map_or(false, |input| algo_name != input) { - bad_format += 1; + res.bad_format += 1; continue; } if algo_name.is_empty() { // we haven't been able to detect the algo name. No point to continue - properly_formatted = false; + res.properly_formatted = false; continue; } let mut algo = detect_algo(&algo_name, length)?; @@ -508,7 +512,7 @@ where show!(err.map_err_context(|| filename_to_check.to_string())); println!("{}: FAILED open or read", filename_to_check); } - failed_open_file += 1; + res.failed_open_file += 1; // we could not open the file but we want to continue continue; @@ -528,12 +532,12 @@ where if !quiet && !status { println!("{prefix}{filename_to_check}: OK"); } - correct_format += 1; + res.correct_format += 1; } else { if !status { println!("{prefix}{filename_to_check}: FAILED"); } - failed_cksum += 1; + res.failed_cksum += 1; } } else { if line.is_empty() { @@ -555,13 +559,13 @@ where ); } - bad_format += 1; + res.bad_format += 1; } } // not a single line correctly formatted found // return an error - if !properly_formatted { + if !res.properly_formatted { if !status { return Err(ChecksumError::NoProperlyFormattedChecksumLinesFound { filename: get_filename_for_output(filename_input, input_is_stdin), @@ -573,7 +577,7 @@ where return Ok(()); } - if ignore_missing && correct_format == 0 { + if ignore_missing && res.correct_format == 0 { // we have only bad format // and we had ignore-missing eprintln!( @@ -585,24 +589,18 @@ where } // strict means that we should have an exit code. - if strict && bad_format > 0 { + if strict && res.bad_format > 0 { set_exit_code(1); } // if we have any failed checksum verification, we set an exit code // except if we have ignore_missing - if (failed_cksum > 0 || failed_open_file > 0) && !ignore_missing { + if (res.failed_cksum > 0 || res.failed_open_file > 0) && !ignore_missing { set_exit_code(1); } // if any incorrectly formatted line, show it - cksum_output( - bad_format, - failed_cksum, - failed_open_file, - ignore_missing, - status, - ); + cksum_output(&res, ignore_missing, status); } Ok(()) } From 520907e22b644b4c87ce0f1664b853aacb99b2d0 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 4 Jun 2024 19:11:21 +0200 Subject: [PATCH 06/13] cksum/hashsum: Move the opening into a dedicated function --- src/uucore/src/lib/features/checksum.rs | 69 ++++++++++++++++--------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 1b6fd1ba3..d859c7f07 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -360,6 +360,42 @@ fn get_expected_checksum( } } +/// Returns a reader that reads from the specified file, or from stdin if `input_is_stdin` is true. +fn get_file_to_check( + filename_to_check: &str, + filename_to_check_unescaped: &str, + ignore_missing: bool, + res: &mut ChecksumResult, +) -> Option> { + if filename_to_check == "-" { + Some(Box::new(stdin())) // Use stdin if "-" is specified in the checksum file + } else { + match File::open(filename_to_check_unescaped) { + Ok(f) => { + if f.metadata().ok()?.is_dir() { + show!(USimpleError::new( + 1, + format!("{}: Is a directory", filename_to_check_unescaped) + )); + None + } else { + Some(Box::new(f)) + } + } + Err(err) => { + if !ignore_missing { + // yes, we have both stderr and stdout here + show!(err.map_err_context(|| filename_to_check.to_string())); + println!("{}: FAILED open or read", filename_to_check); + } + res.failed_open_file += 1; + // we could not open the file but we want to continue + None + } + } + } +} + fn get_input_file(filename_input: &OsStr, input_is_stdin: bool) -> UResult> { if input_is_stdin { Ok(Box::new(stdin())) // Use stdin if "-" is specified @@ -492,32 +528,15 @@ where let (filename_to_check_unescaped, prefix) = unescape_filename(filename_to_check); // manage the input file - let file_to_check: Box = if filename_to_check == "-" { - Box::new(stdin()) // Use stdin if "-" is specified in the checksum file - } else { - match File::open(&filename_to_check_unescaped) { - Ok(f) => { - if f.metadata()?.is_dir() { - show!(USimpleError::new( - 1, - format!("{}: Is a directory", filename_to_check_unescaped) - )); - continue; - } - Box::new(f) - } - Err(err) => { - if !ignore_missing { - // yes, we have both stderr and stdout here - show!(err.map_err_context(|| filename_to_check.to_string())); - println!("{}: FAILED open or read", filename_to_check); - } - res.failed_open_file += 1; - // we could not open the file but we want to continue - continue; - } - } + let file_to_check = match get_file_to_check( + filename_to_check, + &filename_to_check_unescaped, + ignore_missing, + &mut res, + ) { + Some(file) => file, + None => continue, }; let mut file_reader = BufReader::new(file_to_check); From e7898ab32a286c94a31c942bd12ca7b928aa56bc Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 4 Jun 2024 19:13:54 +0200 Subject: [PATCH 07/13] cksum/hashsum: Move the algo mgmt into a dedicated function --- src/uucore/src/lib/features/checksum.rs | 111 +++++++++++++----------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index d859c7f07..21eba69dc 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -414,11 +414,63 @@ fn get_input_file(filename_input: &OsStr, input_is_stdin: bool) -> UResult, + length_input: Option, + res: &mut ChecksumResult, +) -> (String, Option) { + if is_algo_based_format { + // When the algo-based format is matched, extract details from regex captures + let algorithm = caps.name("algo").map_or("", |m| m.as_str()).to_lowercase(); + + // check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file + // (for example SHA1 (f) = d...) + // Also handle the case cksum -s sm3 but the file contains other formats + if algo_name_input.is_some() && algo_name_input != Some(&algorithm) { + res.bad_format += 1; + res.properly_formatted = false; + return (String::new(), None); + } + + if !SUPPORTED_ALGORITHMS.contains(&algorithm.as_str()) { + // Not supported algo, leave early + res.properly_formatted = false; + return (String::new(), None); + } + + let bits = caps.name("bits").map_or(Some(None), |m| { + let bits_value = m.as_str().parse::().unwrap(); + if bits_value % 8 == 0 { + Some(Some(bits_value / 8)) + } else { + res.properly_formatted = false; + None // Return None to signal a divisibility issue + } + }); + + if bits.is_none() { + // If bits is None, we have a parsing or divisibility issue + // Exit the loop outside of the closure + return (String::new(), None); + } + + (algorithm, bits.unwrap()) + } else if let Some(a) = algo_name_input { + // When a specific algorithm name is input, use it and use the provided bits + (a.to_lowercase(), length_input) + } else { + // Default case if no algorithm is specified and non-algo based format is matched + (String::new(), None) + } +} + /*** * Do the checksum validation (can be strict or not) */ #[allow(clippy::too_many_arguments)] -#[allow(clippy::cognitive_complexity)] pub fn perform_checksum_validation<'a, I>( files: I, strict: bool, @@ -465,58 +517,17 @@ where } let expected_checksum = - get_expected_checksum(&filename_to_check, &caps, &chosen_regex)?; + get_expected_checksum(filename_to_check, &caps, &chosen_regex)?; // If the algo_name is provided, we use it, otherwise we try to detect it - let (algo_name, length) = if is_algo_based_format { - // When the algo-based format is matched, extract details from regex captures - let algorithm = caps.name("algo").map_or("", |m| m.as_str()).to_lowercase(); - // check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file - // (for example SHA1 (f) = d...) - // Also handle the case cksum -s sm3 but the file contains other formats - if algo_name_input.is_some() && algo_name_input != Some(&algorithm) { - res.bad_format += 1; - res.properly_formatted = false; - continue; - } - - if !SUPPORTED_ALGORITHMS.contains(&algorithm.as_str()) { - // Not supported algo, leave early - res.properly_formatted = false; - continue; - } - - let bits = caps.name("bits").map_or(Some(None), |m| { - let bits_value = m.as_str().parse::().unwrap(); - if bits_value % 8 == 0 { - Some(Some(bits_value / 8)) - } else { - res.properly_formatted = false; - None // Return None to signal a divisibility issue - } - }); - - if bits.is_none() { - // If bits is None, we have a parsing or divisibility issue - // Exit the loop outside of the closure - continue; - } - - (algorithm, bits.unwrap()) - } else if let Some(a) = algo_name_input { - // When a specific algorithm name is input, use it and use the provided bits - (a.to_lowercase(), length_input) - } else { - // Default case if no algorithm is specified and non-algo based format is matched - (String::new(), None) - }; - - if is_algo_based_format && algo_name_input.map_or(false, |input| algo_name != input) - { - res.bad_format += 1; - continue; - } + let (algo_name, length) = get_algo_name_and_length( + &caps, + is_algo_based_format, + algo_name_input, + length_input, + &mut res, + ); if algo_name.is_empty() { // we haven't been able to detect the algo name. No point to continue From c2292a8da64521f2a60acca5fc9bfad9ac15ba52 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 4 Jun 2024 19:13:54 +0200 Subject: [PATCH 08/13] cksum/hashsum: Move the algo mgmt into a dedicated function --- src/uucore/src/lib/features/checksum.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 21eba69dc..298826a7c 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -396,6 +396,7 @@ fn get_file_to_check( } } +/// Returns a reader to the list of checksums fn get_input_file(filename_input: &OsStr, input_is_stdin: bool) -> UResult> { if input_is_stdin { Ok(Box::new(stdin())) // Use stdin if "-" is specified From 6922e7f0570cb0581a6d31a580297d808d69c986 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 4 Jun 2024 21:39:58 +0200 Subject: [PATCH 09/13] cksum/hashsum: Simplify the determine_regex function --- src/uucore/src/lib/features/checksum.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 298826a7c..4691c9132 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore anotherfile invalidchecksum +// spell-checker:ignore anotherfile invalidchecksum regexes use data_encoding::BASE64; use os_display::Quotable; @@ -301,21 +301,19 @@ fn determine_regex( input_is_stdin: bool, lines: &[String], ) -> UResult<(Regex, bool)> { - let algo_based_regex = Regex::new(ALGO_BASED_REGEX).unwrap(); - let double_space_regex = Regex::new(DOUBLE_SPACE_REGEX).unwrap(); - let single_space_regex = Regex::new(SINGLE_SPACE_REGEX).unwrap(); - let algo_based_regex_base64 = Regex::new(ALGO_BASED_REGEX_BASE64).unwrap(); + let regexes = [ + (Regex::new(ALGO_BASED_REGEX).unwrap(), true), + (Regex::new(DOUBLE_SPACE_REGEX).unwrap(), false), + (Regex::new(SINGLE_SPACE_REGEX).unwrap(), false), + (Regex::new(ALGO_BASED_REGEX_BASE64).unwrap(), true), + ]; for line in lines { let line_trim = line.trim(); - if algo_based_regex.is_match(line_trim) { - return Ok((algo_based_regex, true)); - } else if double_space_regex.is_match(line_trim) { - return Ok((double_space_regex, false)); - } else if single_space_regex.is_match(line_trim) { - return Ok((single_space_regex, false)); - } else if algo_based_regex_base64.is_match(line_trim) { - return Ok((algo_based_regex_base64, true)); + for (regex, is_algo_based) in ®exes { + if regex.is_match(line_trim) { + return Ok((regex.clone(), *is_algo_based)); + } } } Err(ChecksumError::NoProperlyFormattedChecksumLinesFound { From ab2cc2f17abadcb7afd0da213eab6093c439cbda Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 4 Jun 2024 22:26:22 +0200 Subject: [PATCH 10/13] cksum base64: update the GNU test to manage our output --- .../cspell.dictionaries/workspace.wordlist.txt | 3 +++ src/uucore/src/lib/features/checksum.rs | 2 +- util/gnu-patches/tests_cksum_base64.patch | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 util/gnu-patches/tests_cksum_base64.patch diff --git a/.vscode/cspell.dictionaries/workspace.wordlist.txt b/.vscode/cspell.dictionaries/workspace.wordlist.txt index ce4822f1e..ee34a3811 100644 --- a/.vscode/cspell.dictionaries/workspace.wordlist.txt +++ b/.vscode/cspell.dictionaries/workspace.wordlist.txt @@ -336,3 +336,6 @@ uutils # * function names getcwd + +# * other +algs diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 4691c9132..03ec74107 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore anotherfile invalidchecksum regexes +// spell-checker:ignore anotherfile invalidchecksum regexes JWZG use data_encoding::BASE64; use os_display::Quotable; diff --git a/util/gnu-patches/tests_cksum_base64.patch b/util/gnu-patches/tests_cksum_base64.patch new file mode 100644 index 000000000..2a8ed0af4 --- /dev/null +++ b/util/gnu-patches/tests_cksum_base64.patch @@ -0,0 +1,15 @@ +diff --git a/tests/cksum/cksum-base64.pl b/tests/cksum/cksum-base64.pl +index a037a1628..c6d87d447 100755 +--- a/tests/cksum/cksum-base64.pl ++++ b/tests/cksum/cksum-base64.pl +@@ -91,8 +91,8 @@ my $prog = 'cksum'; + my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); + + # Ensure hash names from cksum --help match those in @pairs above. +-my $help_algs = join ' ', map { m{^ ([[:alpha:]]\S+)} } +- grep { m{^ ([[:alpha:]]\S+)} } split ('\n', `cksum --help`); ++my $help_algs = join ' ', map { m{^\s*-\s*([[:alpha:]]\S+):} ? $1 : () } ++ grep { m{^\s*-\s*([[:alpha:]]\S+):} } split ('\n', `cksum --help`); + my $test_algs = join ' ', map {$_->[0]} @pairs; + $help_algs eq $test_algs or die "$help_algs not equal to\n$test_algs"; + From 2e704b85728137a893272928f5806727e808ab20 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 18 Jun 2024 18:49:17 +0200 Subject: [PATCH 11/13] Address review comments --- src/uucore/src/lib/features/checksum.rs | 197 ++++++++++++------------ 1 file changed, 95 insertions(+), 102 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 03ec74107..0616079f3 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -68,12 +68,11 @@ pub struct HashAlgorithm { pub bits: usize, } +#[derive(Default)] struct ChecksumResult { pub bad_format: i32, pub failed_cksum: i32, pub failed_open_file: i32, - pub correct_format: i32, - pub properly_formatted: bool, } #[derive(Debug, Error)] @@ -322,7 +321,7 @@ fn determine_regex( .into()) } -// Function to convert bytes to a hexadecimal string +// Converts bytes to a hexadecimal string fn bytes_to_hex(bytes: &[u8]) -> String { bytes .iter() @@ -331,8 +330,6 @@ fn bytes_to_hex(bytes: &[u8]) -> String { .join("") } -// Function to get the expected checksum - fn get_expected_checksum( filename: &str, caps: ®ex::Captures, @@ -358,22 +355,21 @@ fn get_expected_checksum( } } -/// Returns a reader that reads from the specified file, or from stdin if `input_is_stdin` is true. +/// Returns a reader that reads from the specified file, or from stdin if `filename_to_check` is "-". fn get_file_to_check( - filename_to_check: &str, - filename_to_check_unescaped: &str, + filename: &str, ignore_missing: bool, res: &mut ChecksumResult, ) -> Option> { - if filename_to_check == "-" { + if filename == "-" { Some(Box::new(stdin())) // Use stdin if "-" is specified in the checksum file } else { - match File::open(filename_to_check_unescaped) { + match File::open(filename) { Ok(f) => { if f.metadata().ok()?.is_dir() { show!(USimpleError::new( 1, - format!("{}: Is a directory", filename_to_check_unescaped) + format!("{}: Is a directory", filename) )); None } else { @@ -383,8 +379,8 @@ fn get_file_to_check( Err(err) => { if !ignore_missing { // yes, we have both stderr and stdout here - show!(err.map_err_context(|| filename_to_check.to_string())); - println!("{}: FAILED open or read", filename_to_check); + show!(err.map_err_context(|| filename.to_string())); + println!("{}: FAILED open or read", filename); } res.failed_open_file += 1; // we could not open the file but we want to continue @@ -395,75 +391,69 @@ fn get_file_to_check( } /// Returns a reader to the list of checksums -fn get_input_file(filename_input: &OsStr, input_is_stdin: bool) -> UResult> { - if input_is_stdin { - Ok(Box::new(stdin())) // Use stdin if "-" is specified - } else { - match File::open(filename_input) { - Ok(f) => Ok(Box::new(f)), - Err(_) => Err(io::Error::new( - io::ErrorKind::Other, - format!( - "{}: No such file or directory", - filename_input.to_string_lossy() - ), - ) - .into()), +fn get_input_file(filename: &OsStr) -> UResult> { + match File::open(filename) { + Ok(f) => { + if f.metadata()?.is_dir() { + Err(io::Error::new( + io::ErrorKind::Other, + format!("{}: Is a directory", filename.to_string_lossy()), + ) + .into()) + } else { + Ok(Box::new(f)) + } } + Err(_) => Err(io::Error::new( + io::ErrorKind::Other, + format!("{}: No such file or directory", filename.to_string_lossy()), + ) + .into()), } } /// Extracts the algorithm name and length from the regex captures if the algo-based format is matched. -fn get_algo_name_and_length( +fn identify_algo_name_and_length( caps: ®ex::Captures, - is_algo_based_format: bool, algo_name_input: Option<&str>, - length_input: Option, res: &mut ChecksumResult, + properly_formatted: &mut bool, ) -> (String, Option) { - if is_algo_based_format { - // When the algo-based format is matched, extract details from regex captures - let algorithm = caps.name("algo").map_or("", |m| m.as_str()).to_lowercase(); + // When the algo-based format is matched, extract details from regex captures + let algorithm = caps.name("algo").map_or("", |m| m.as_str()).to_lowercase(); - // check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file - // (for example SHA1 (f) = d...) - // Also handle the case cksum -s sm3 but the file contains other formats - if algo_name_input.is_some() && algo_name_input != Some(&algorithm) { - res.bad_format += 1; - res.properly_formatted = false; - return (String::new(), None); - } - - if !SUPPORTED_ALGORITHMS.contains(&algorithm.as_str()) { - // Not supported algo, leave early - res.properly_formatted = false; - return (String::new(), None); - } - - let bits = caps.name("bits").map_or(Some(None), |m| { - let bits_value = m.as_str().parse::().unwrap(); - if bits_value % 8 == 0 { - Some(Some(bits_value / 8)) - } else { - res.properly_formatted = false; - None // Return None to signal a divisibility issue - } - }); - - if bits.is_none() { - // If bits is None, we have a parsing or divisibility issue - // Exit the loop outside of the closure - return (String::new(), None); - } - - (algorithm, bits.unwrap()) - } else if let Some(a) = algo_name_input { - // When a specific algorithm name is input, use it and use the provided bits - (a.to_lowercase(), length_input) - } else { - // Default case if no algorithm is specified and non-algo based format is matched - (String::new(), None) + // check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file + // (for example SHA1 (f) = d...) + // Also handle the case cksum -s sm3 but the file contains other formats + if algo_name_input.is_some() && algo_name_input != Some(&algorithm) { + res.bad_format += 1; + *properly_formatted = false; + return (String::new(), None); } + + if !SUPPORTED_ALGORITHMS.contains(&algorithm.as_str()) { + // Not supported algo, leave early + *properly_formatted = false; + return (String::new(), None); + } + + let bits = caps.name("bits").map_or(Some(None), |m| { + let bits_value = m.as_str().parse::().unwrap(); + if bits_value % 8 == 0 { + Some(Some(bits_value / 8)) + } else { + *properly_formatted = false; + None // Return None to signal a divisibility issue + } + }); + + if bits.is_none() { + // If bits is None, we have a parsing or divisibility issue + // Exit the loop outside of the closure + return (String::new(), None); + } + + (algorithm, bits.unwrap()) } /*** @@ -486,16 +476,17 @@ where { // if cksum has several input files, it will print the result for each file for filename_input in files { - let mut res = ChecksumResult { - bad_format: 0, - failed_cksum: 0, - failed_open_file: 0, - correct_format: 0, - properly_formatted: false, - }; + let mut correct_format = 0; + let mut properly_formatted = false; + let mut res = ChecksumResult::default(); let input_is_stdin = filename_input == OsStr::new("-"); - let file: Box = get_input_file(filename_input, input_is_stdin)?; + let file: Box = if input_is_stdin { + // Use stdin if "-" is specified + Box::new(stdin()) + } else { + get_input_file(filename_input)? + }; let reader = BufReader::new(file); let lines: Vec = reader.lines().collect::>()?; @@ -504,7 +495,7 @@ where for (i, line) in lines.iter().enumerate() { if let Some(caps) = chosen_regex.captures(line) { - res.properly_formatted = true; + properly_formatted = true; let mut filename_to_check = caps.name("filename").unwrap().as_str(); if filename_to_check.starts_with('*') @@ -520,17 +511,24 @@ where // If the algo_name is provided, we use it, otherwise we try to detect it - let (algo_name, length) = get_algo_name_and_length( - &caps, - is_algo_based_format, - algo_name_input, - length_input, - &mut res, - ); + let (algo_name, length) = if is_algo_based_format { + identify_algo_name_and_length( + &caps, + algo_name_input, + &mut res, + &mut properly_formatted, + ) + } else if let Some(a) = algo_name_input { + // When a specific algorithm name is input, use it and use the provided bits + (a.to_lowercase(), length_input) + } else { + // Default case if no algorithm is specified and non-algo based format is matched + (String::new(), None) + }; if algo_name.is_empty() { // we haven't been able to detect the algo name. No point to continue - res.properly_formatted = false; + properly_formatted = false; continue; } let mut algo = detect_algo(&algo_name, length)?; @@ -538,17 +536,12 @@ where let (filename_to_check_unescaped, prefix) = unescape_filename(filename_to_check); // manage the input file - - let file_to_check = match get_file_to_check( - filename_to_check, - &filename_to_check_unescaped, - ignore_missing, - &mut res, - ) { - Some(file) => file, - None => continue, - }; - + let file_to_check = + match get_file_to_check(&filename_to_check_unescaped, ignore_missing, &mut res) + { + Some(file) => file, + None => continue, + }; let mut file_reader = BufReader::new(file_to_check); // Read the file and calculate the checksum let create_fn = &mut algo.create_fn; @@ -561,7 +554,7 @@ where if !quiet && !status { println!("{prefix}{filename_to_check}: OK"); } - res.correct_format += 1; + correct_format += 1; } else { if !status { println!("{prefix}{filename_to_check}: FAILED"); @@ -594,7 +587,7 @@ where // not a single line correctly formatted found // return an error - if !res.properly_formatted { + if !properly_formatted { if !status { return Err(ChecksumError::NoProperlyFormattedChecksumLinesFound { filename: get_filename_for_output(filename_input, input_is_stdin), @@ -606,7 +599,7 @@ where return Ok(()); } - if ignore_missing && res.correct_format == 0 { + if ignore_missing && correct_format == 0 { // we have only bad format // and we had ignore-missing eprintln!( From adcc548f7cdc430e12cc4cac94c9b839957bd970 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 25 Jun 2024 19:05:28 +0200 Subject: [PATCH 12/13] cksum: Allow the indiv build --- src/uucore/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index b9122735f..8110615d5 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -76,7 +76,7 @@ default = [] # * non-default features backup-control = [] colors = [] -checksum = ["thiserror", "regex", "sum"] +checksum = ["data-encoding", "thiserror", "regex", "sum"] encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"] entries = ["libc"] fs = ["dunce", "libc", "winapi-util", "windows-sys"] From 212ddf7d8c0a8774b71bf151910a5028091cf49a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 26 Jun 2024 23:45:46 +0200 Subject: [PATCH 13/13] Adjust identify_algo_name_and_length to return Option<(String, Option)> instead of (String, Option) --- src/uucore/src/lib/features/checksum.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 0616079f3..9f4fe1d77 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -418,7 +418,7 @@ fn identify_algo_name_and_length( algo_name_input: Option<&str>, res: &mut ChecksumResult, properly_formatted: &mut bool, -) -> (String, Option) { +) -> Option<(String, Option)> { // When the algo-based format is matched, extract details from regex captures let algorithm = caps.name("algo").map_or("", |m| m.as_str()).to_lowercase(); @@ -428,13 +428,13 @@ fn identify_algo_name_and_length( if algo_name_input.is_some() && algo_name_input != Some(&algorithm) { res.bad_format += 1; *properly_formatted = false; - return (String::new(), None); + return None; } if !SUPPORTED_ALGORITHMS.contains(&algorithm.as_str()) { // Not supported algo, leave early *properly_formatted = false; - return (String::new(), None); + return None; } let bits = caps.name("bits").map_or(Some(None), |m| { @@ -445,15 +445,9 @@ fn identify_algo_name_and_length( *properly_formatted = false; None // Return None to signal a divisibility issue } - }); + })?; - if bits.is_none() { - // If bits is None, we have a parsing or divisibility issue - // Exit the loop outside of the closure - return (String::new(), None); - } - - (algorithm, bits.unwrap()) + Some((algorithm, bits)) } /*** @@ -518,6 +512,7 @@ where &mut res, &mut properly_formatted, ) + .unwrap_or((String::new(), None)) } else if let Some(a) = algo_name_input { // When a specific algorithm name is input, use it and use the provided bits (a.to_lowercase(), length_input)