hashsum: Get rid of non-GNU --bits argument

This commit is contained in:
Dorian Peron
2025-12-23 10:10:12 +01:00
parent 8d3774b67e
commit deaf44afaf
6 changed files with 213 additions and 124 deletions
+18 -42
View File
@@ -7,7 +7,6 @@
use std::ffi::{OsStr, OsString};
use std::iter;
use std::num::ParseIntError;
use std::path::Path;
use clap::builder::ValueParser;
@@ -19,7 +18,10 @@ use uucore::checksum::compute::{
use uucore::checksum::validate::{
ChecksumValidateOptions, ChecksumVerbose, perform_checksum_validation,
};
use uucore::checksum::{AlgoKind, ChecksumError, SizedAlgoKind, calculate_blake2b_length_str};
use uucore::checksum::{
AlgoKind, ChecksumError, SizedAlgoKind, calculate_blake2b_length_str,
sanitize_sha2_sha3_length_str,
};
use uucore::error::UResult;
use uucore::line_ending::LineEnding;
use uucore::{format_usage, translate};
@@ -74,9 +76,11 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<(AlgoKind, Optio
set_or_err((AlgoKind::Blake3, None))?;
}
if matches.get_flag("sha3") {
match matches.get_one::<usize>("bits") {
Some(bits @ (224 | 256 | 384 | 512)) => set_or_err((AlgoKind::Sha3, Some(*bits)))?,
Some(bits) => return Err(ChecksumError::InvalidLengthForSha(bits.to_string()).into()),
match matches.get_one::<String>(options::LENGTH) {
Some(len) => set_or_err((
AlgoKind::Sha3,
Some(sanitize_sha2_sha3_length_str(AlgoKind::Sha3, len)?),
))?,
None => return Err(ChecksumError::LengthRequired("SHA3".into()).into()),
}
}
@@ -93,16 +97,10 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<(AlgoKind, Optio
set_or_err((AlgoKind::Sha3, Some(512)))?;
}
if matches.get_flag("shake128") {
match matches.get_one::<usize>("bits") {
Some(bits) => set_or_err((AlgoKind::Shake128, Some(*bits)))?,
None => return Err(ChecksumError::LengthRequired("SHAKE128".into()).into()),
}
set_or_err((AlgoKind::Shake128, Some(128)))?;
}
if matches.get_flag("shake256") {
match matches.get_one::<usize>("bits") {
Some(bits) => set_or_err((AlgoKind::Shake256, Some(*bits)))?,
None => return Err(ChecksumError::LengthRequired("SHAKE256".into()).into()),
}
set_or_err((AlgoKind::Shake256, Some(256)))?;
}
if alg.is_none() {
@@ -112,11 +110,6 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<(AlgoKind, Optio
Ok(alg.unwrap())
}
// TODO: return custom error type
fn parse_bit_num(arg: &str) -> Result<usize, ParseIntError> {
arg.parse()
}
#[uucore::main]
pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
// if there is no program name for some reason, default to "hashsum"
@@ -139,17 +132,16 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
// least somewhat better from a user's perspective.
let matches = uucore::clap_localization::handle_clap_result(command, args)?;
let input_length: Option<&String> = if binary_name == "b2sum" {
matches.get_one::<String>(options::LENGTH)
let length: Option<usize> = if binary_name == "b2sum" {
if let Some(len) = matches.get_one::<String>(options::LENGTH) {
calculate_blake2b_length_str(len)?
} else {
None
}
} else {
None
};
let length = match input_length {
Some(length) => calculate_blake2b_length_str(length)?,
None => None,
};
let (algo_kind, length) = if is_hashsum_bin {
create_algorithm_from_flags(&matches)?
} else {
@@ -371,24 +363,8 @@ fn uu_app_opt_length(command: Command) -> Command {
)
}
pub fn uu_app_bits() -> Command {
uu_app_opt_bits(uu_app_common())
}
fn uu_app_opt_bits(command: Command) -> Command {
// Needed for variable-length output sums (e.g. SHAKE)
command.arg(
Arg::new("bits")
.long("bits")
.help(translate!("hashsum-help-bits"))
.value_name("BITS")
// XXX: should we actually use validators? they're not particularly efficient
.value_parser(parse_bit_num),
)
}
pub fn uu_app_custom() -> Command {
let mut command = uu_app_opt_bits(uu_app_common());
let mut command = uu_app_opt_length(uu_app_common());
let algorithms = &[
("md5", translate!("hashsum-help-md5")),
("sha1", translate!("hashsum-help-sha1")),
+195 -82
View File
@@ -16,87 +16,207 @@ macro_rules! get_hash(
);
macro_rules! test_digest {
($($id:ident $t:ident $size:expr)*) => ($(
($id:ident, $t:ident) => {
mod $id {
use uutests::util::*;
use uutests::util_name;
static DIGEST_ARG: &'static str = concat!("--", stringify!($t));
static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected");
static CHECK_FILE: &'static str = concat!(stringify!($id), ".checkfile");
static INPUT_FILE: &'static str = "input.txt";
mod $id {
use uutests::util::*;
use uutests::util_name;
static DIGEST_ARG: &'static str = concat!("--", stringify!($t));
static BITS_ARG: &'static str = concat!("--bits=", stringify!($size));
static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected");
static CHECK_FILE: &'static str = concat!(stringify!($id), ".checkfile");
static INPUT_FILE: &'static str = "input.txt";
#[test]
fn test_single_file() {
let ts = TestScenario::new(util_name!());
assert_eq!(
ts.fixtures.read(EXPECTED_FILE),
get_hash!(
ts.ucmd()
.arg(DIGEST_ARG)
.arg(INPUT_FILE)
.succeeds()
.no_stderr()
.stdout_str()
)
);
}
#[test]
fn test_single_file() {
let ts = TestScenario::new(util_name!());
assert_eq!(ts.fixtures.read(EXPECTED_FILE),
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg(INPUT_FILE).succeeds().no_stderr().stdout_str()));
#[test]
fn test_stdin() {
let ts = TestScenario::new(util_name!());
assert_eq!(
ts.fixtures.read(EXPECTED_FILE),
get_hash!(
ts.ucmd()
.arg(DIGEST_ARG)
.pipe_in_fixture(INPUT_FILE)
.succeeds()
.no_stderr()
.stdout_str()
)
);
}
#[test]
fn test_check() {
let ts = TestScenario::new(util_name!());
println!("File content='{}'", ts.fixtures.read(INPUT_FILE));
println!("Check file='{}'", ts.fixtures.read(CHECK_FILE));
ts.ucmd()
.args(&[DIGEST_ARG, "--check", CHECK_FILE])
.succeeds()
.no_stderr()
.stdout_is("input.txt: OK\n");
}
#[test]
fn test_zero() {
let ts = TestScenario::new(util_name!());
assert_eq!(
ts.fixtures.read(EXPECTED_FILE),
get_hash!(
ts.ucmd()
.arg(DIGEST_ARG)
.arg("--zero")
.arg(INPUT_FILE)
.succeeds()
.no_stderr()
.stdout_str()
)
);
}
#[test]
fn test_missing_file() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("a", "file1\n");
at.write("c", "file3\n");
ts.ucmd()
.args(&[DIGEST_ARG, "a", "b", "c"])
.fails()
.stdout_contains("a\n")
.stdout_contains("c\n")
.stderr_contains("b: No such file or directory");
}
}
#[test]
fn test_stdin() {
let ts = TestScenario::new(util_name!());
assert_eq!(ts.fixtures.read(EXPECTED_FILE),
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).pipe_in_fixture(INPUT_FILE).succeeds().no_stderr().stdout_str()));
}
#[test]
fn test_check() {
let ts = TestScenario::new(util_name!());
println!("File content='{}'", ts.fixtures.read(INPUT_FILE));
println!("Check file='{}'", ts.fixtures.read(CHECK_FILE));
ts.ucmd()
.args(&[DIGEST_ARG, BITS_ARG, "--check", CHECK_FILE])
.succeeds()
.no_stderr()
.stdout_is("input.txt: OK\n");
}
#[test]
fn test_zero() {
let ts = TestScenario::new(util_name!());
assert_eq!(ts.fixtures.read(EXPECTED_FILE),
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("--zero").arg(INPUT_FILE).succeeds().no_stderr().stdout_str()));
}
#[test]
fn test_missing_file() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("a", "file1\n");
at.write("c", "file3\n");
ts.ucmd()
.args(&[DIGEST_ARG, BITS_ARG, "a", "b", "c"])
.fails()
.stdout_contains("a\n")
.stdout_contains("c\n")
.stderr_contains("b: No such file or directory");
}
}
)*)
};
}
test_digest! {
md5 md5 128
sha1 sha1 160
sha224 sha224 224
sha256 sha256 256
sha384 sha384 384
sha512 sha512 512
sha3_224 sha3 224
sha3_256 sha3 256
sha3_384 sha3 384
sha3_512 sha3 512
shake128_256 shake128 256
shake256_512 shake256 512
b2sum b2sum 512
b3sum b3sum 256
macro_rules! test_digest_with_len {
($id:ident, $t:ident, $size:expr) => {
mod $id {
use uutests::util::*;
use uutests::util_name;
static DIGEST_ARG: &'static str = concat!("--", stringify!($t));
static LENGTH_ARG: &'static str = concat!("--length=", stringify!($size));
static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected");
static CHECK_FILE: &'static str = concat!(stringify!($id), ".checkfile");
static INPUT_FILE: &'static str = "input.txt";
#[test]
fn test_single_file() {
let ts = TestScenario::new(util_name!());
assert_eq!(
ts.fixtures.read(EXPECTED_FILE),
get_hash!(
ts.ucmd()
.arg(DIGEST_ARG)
.arg(LENGTH_ARG)
.arg(INPUT_FILE)
.succeeds()
.no_stderr()
.stdout_str()
)
);
}
#[test]
fn test_stdin() {
let ts = TestScenario::new(util_name!());
assert_eq!(
ts.fixtures.read(EXPECTED_FILE),
get_hash!(
ts.ucmd()
.arg(DIGEST_ARG)
.arg(LENGTH_ARG)
.pipe_in_fixture(INPUT_FILE)
.succeeds()
.no_stderr()
.stdout_str()
)
);
}
#[test]
fn test_check() {
let ts = TestScenario::new(util_name!());
println!("File content='{}'", ts.fixtures.read(INPUT_FILE));
println!("Check file='{}'", ts.fixtures.read(CHECK_FILE));
ts.ucmd()
.args(&[DIGEST_ARG, LENGTH_ARG, "--check", CHECK_FILE])
.succeeds()
.no_stderr()
.stdout_is("input.txt: OK\n");
}
#[test]
fn test_zero() {
let ts = TestScenario::new(util_name!());
assert_eq!(
ts.fixtures.read(EXPECTED_FILE),
get_hash!(
ts.ucmd()
.arg(DIGEST_ARG)
.arg(LENGTH_ARG)
.arg("--zero")
.arg(INPUT_FILE)
.succeeds()
.no_stderr()
.stdout_str()
)
);
}
#[test]
fn test_missing_file() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("a", "file1\n");
at.write("c", "file3\n");
ts.ucmd()
.args(&[DIGEST_ARG, LENGTH_ARG, "a", "b", "c"])
.fails()
.stdout_contains("a\n")
.stdout_contains("c\n")
.stderr_contains("b: No such file or directory");
}
}
};
}
test_digest! {md5, md5}
test_digest! {sha1, sha1}
test_digest! {b3sum, b3sum}
test_digest! {shake128, shake128}
test_digest! {shake256, shake256}
test_digest_with_len! {sha224, sha224, 224}
test_digest_with_len! {sha256, sha256, 256}
test_digest_with_len! {sha384, sha384, 384}
test_digest_with_len! {sha512, sha512, 512}
test_digest_with_len! {sha3_224, sha3, 224}
test_digest_with_len! {sha3_256, sha3, 256}
test_digest_with_len! {sha3_384, sha3, 384}
test_digest_with_len! {sha3_512, sha3, 512}
test_digest_with_len! {b2sum, b2sum, 512}
#[test]
fn test_check_sha1() {
// To make sure that #3815 doesn't happen again
@@ -1037,7 +1157,6 @@ fn test_sha256_binary() {
get_hash!(
ts.ucmd()
.arg("--sha256")
.arg("--bits=256")
.arg("binary.png")
.succeeds()
.no_stderr()
@@ -1054,7 +1173,6 @@ fn test_sha256_stdin_binary() {
get_hash!(
ts.ucmd()
.arg("--sha256")
.arg("--bits=256")
.pipe_in_fixture("binary.png")
.succeeds()
.no_stderr()
@@ -1068,12 +1186,7 @@ fn test_sha256_stdin_binary() {
#[cfg_attr(windows, ignore = "Discussion is in #9168")]
fn test_check_sha256_binary() {
new_ucmd!()
.args(&[
"--sha256",
"--bits=256",
"--check",
"binary.sha256.checkfile",
])
.args(&["--sha256", "--check", "binary.sha256.checkfile"])
.succeeds()
.no_stderr()
.stdout_is("binary.png: OK\n");