diff --git a/Cargo.lock b/Cargo.lock index 8232e2e27..aa8dcdf27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -629,6 +629,7 @@ dependencies = [ "uu_seq", "uu_sha1sum", "uu_sha224sum", + "uu_sha256sum", "uu_shred", "uu_shuf", "uu_sleep", @@ -4010,6 +4011,18 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_sha256sum" +version = "0.6.0" +dependencies = [ + "clap", + "codspeed-divan-compat", + "fluent", + "tempfile", + "uu_checksum_common", + "uucore", +] + [[package]] name = "uu_shred" version = "0.6.0" diff --git a/Cargo.toml b/Cargo.toml index 789586c3d..801f67979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,7 @@ feat_common_core = [ "md5sum", "sha1sum", "sha224sum", + "sha256sum", "comm", "cp", "csplit", @@ -445,6 +446,7 @@ b2sum = { optional = true, version = "0.6.0", package = "uu_b2sum", path = "src/ md5sum = { optional = true, version = "0.6.0", package = "uu_md5sum", path = "src/uu/md5sum" } sha1sum = { optional = true, version = "0.6.0", package = "uu_sha1sum", path = "src/uu/sha1sum" } sha224sum = { optional = true, version = "0.6.0", package = "uu_sha224sum", path = "src/uu/sha224sum" } +sha256sum = { optional = true, version = "0.6.0", package = "uu_sha256sum", path = "src/uu/sha256sum" } comm = { optional = true, version = "0.6.0", package = "uu_comm", path = "src/uu/comm" } cp = { optional = true, version = "0.6.0", package = "uu_cp", path = "src/uu/cp" } csplit = { optional = true, version = "0.6.0", package = "uu_csplit", path = "src/uu/csplit" } diff --git a/GNUmakefile b/GNUmakefile index f713e6390..998968d09 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -96,7 +96,6 @@ SELINUX_PROGS := \ runcon HASHSUM_PROGS := \ - sha256sum \ sha384sum \ sha512sum diff --git a/build.rs b/build.rs index 49d8c675c..c206e892f 100644 --- a/build.rs +++ b/build.rs @@ -91,7 +91,6 @@ pub fn main() { phf_map.entry(krate, format!("({krate}::uumain, {krate}::uu_app_custom)")); let map_value = format!("({krate}::uumain, {krate}::uu_app_common)"); - phf_map.entry("sha256sum", map_value.clone()); phf_map.entry("sha384sum", map_value.clone()); phf_map.entry("sha512sum", map_value.clone()); } diff --git a/src/common/validation.rs b/src/common/validation.rs index c5aaac3d0..aa01b4679 100644 --- a/src/common/validation.rs +++ b/src/common/validation.rs @@ -51,7 +51,7 @@ fn get_canonical_util_name(util_name: &str) -> &str { "[" => "test", // hashsum aliases - all these hash commands are aliases for hashsum - "sha256sum" | "sha384sum" | "sha512sum" => "hashsum", + "sha384sum" | "sha512sum" => "hashsum", "dir" => "ls", // dir is an alias for ls diff --git a/src/uu/sha256sum/Cargo.toml b/src/uu/sha256sum/Cargo.toml new file mode 100644 index 000000000..2ca6204c0 --- /dev/null +++ b/src/uu/sha256sum/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "uu_sha256sum" +description = "sha256sum ~ (uutils) Print or check the SHA256 checksums" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/sha256sum" +version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +keywords.workspace = true +categories.workspace = true +edition.workspace = true +readme.workspace = true + +[lints] +workspace = true + +[lib] +path = "src/sha256sum.rs" + +[dependencies] +clap = { workspace = true } +uu_checksum_common = { workspace = true } +uucore = { workspace = true, features = [ + "checksum", + "encoding", + "sum", + "hardware", +] } +fluent = { workspace = true } + +[dev-dependencies] +divan = { workspace = true } +tempfile = { workspace = true } +uucore = { workspace = true, features = ["benchmark"] } + +[[bin]] +name = "sha256sum" +path = "src/main.rs" diff --git a/src/uu/sha256sum/LICENSE b/src/uu/sha256sum/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/sha256sum/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/sha256sum/locales/en-US.ftl b/src/uu/sha256sum/locales/en-US.ftl new file mode 100644 index 000000000..60a0b4a3f --- /dev/null +++ b/src/uu/sha256sum/locales/en-US.ftl @@ -0,0 +1,2 @@ +sha256sum-about = Print or check the SHA256 checksums +sha256sum-usage = sha256sum [OPTIONS] [FILE]... diff --git a/src/uu/sha256sum/locales/fr-FR.ftl b/src/uu/sha256sum/locales/fr-FR.ftl new file mode 100644 index 000000000..baaa2f83b --- /dev/null +++ b/src/uu/sha256sum/locales/fr-FR.ftl @@ -0,0 +1,2 @@ +sha256sum-about = Afficher le SHA256 et la taille de chaque fichier +sha256sum-usage = sha256sum [OPTION]... [FICHIER]... diff --git a/src/uu/sha256sum/src/main.rs b/src/uu/sha256sum/src/main.rs new file mode 100644 index 000000000..323cd315d --- /dev/null +++ b/src/uu/sha256sum/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_sha256sum); diff --git a/src/uu/sha256sum/src/sha256sum.rs b/src/uu/sha256sum/src/sha256sum.rs new file mode 100644 index 000000000..ab47a23df --- /dev/null +++ b/src/uu/sha256sum/src/sha256sum.rs @@ -0,0 +1 @@ +uu_checksum_common::declare_standalone!("sha256sum", uucore::checksum::AlgoKind::Sha256); diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index b013e9128..3e6003f31 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -173,7 +173,7 @@ pub fn get_canonical_util_name(util_name: &str) -> &str { "[" => "test", // hashsum aliases - all these hash commands are aliases for hashsum - "sha256sum" | "sha384sum" | "sha512sum" => "hashsum", + "sha384sum" | "sha512sum" => "hashsum", "dir" => "ls", // dir is an alias for ls diff --git a/src/uucore/src/lib/mods/locale.rs b/src/uucore/src/lib/mods/locale.rs index c69a5452d..da5a996d5 100644 --- a/src/uucore/src/lib/mods/locale.rs +++ b/src/uucore/src/lib/mods/locale.rs @@ -157,7 +157,16 @@ fn create_bundle( try_add_resource_from(get_locales_dir(util_name).ok()); // checksum binaries also require fluent files from the checksum_common crate - if ["cksum", "b2sum", "md5sum", "sha1sum", "sha224sum"].contains(&util_name) { + if [ + "cksum", + "b2sum", + "md5sum", + "sha1sum", + "sha224sum", + "sha256sum", + ] + .contains(&util_name) + { try_add_resource_from(get_locales_dir("checksum_common").ok()); } diff --git a/tests/by-util/test_hashsum.rs b/tests/by-util/test_hashsum.rs index ea044f6ab..ff6d92f2d 100644 --- a/tests/by-util/test_hashsum.rs +++ b/tests/by-util/test_hashsum.rs @@ -205,7 +205,6 @@ test_digest! {b3sum, b3sum} test_digest! {shake128, shake128} test_digest! {shake256, shake256} -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} @@ -618,6 +617,7 @@ fn test_conflicting_arg() { .fails_with_code(1); } +#[ignore = "moved to standalone"] #[test] fn test_tag() { let scene = TestScenario::new(util_name!()); @@ -1184,6 +1184,7 @@ fn test_check_md5_comment_leading_space() { .stderr_contains("WARNING: 1 line is improperly formatted"); } +#[ignore = "moved to standalone"] #[test] fn test_sha256_binary() { let ts = TestScenario::new(util_name!()); @@ -1200,6 +1201,7 @@ fn test_sha256_binary() { ); } +#[ignore = "moved to standalone"] #[test] fn test_sha256_stdin_binary() { let ts = TestScenario::new(util_name!()); @@ -1217,8 +1219,8 @@ fn test_sha256_stdin_binary() { } // This test is currently disabled on windows +#[ignore = "moved to standalone"] #[test] -#[cfg_attr(windows, ignore = "Discussion is in #9168")] fn test_check_sha256_binary() { new_ucmd!() .args(&["--sha256", "--check", "binary.sha256.checkfile"]) @@ -1241,12 +1243,12 @@ fn test_help_shows_correct_utility_name() { // .stdout_does_not_contain("Usage: hashsum"); // Test sha256sum - scene - .ccmd("sha256sum") - .arg("--help") - .succeeds() - .stdout_contains("Usage: sha256sum") - .stdout_does_not_contain("Usage: hashsum"); + // scene + // .ccmd("sha256sum") + // .arg("--help") + // .succeeds() + // .stdout_contains("Usage: sha256sum") + // .stdout_does_not_contain("Usage: hashsum"); // Test b2sum // scene diff --git a/tests/by-util/test_sha256sum.rs b/tests/by-util/test_sha256sum.rs new file mode 100644 index 000000000..b3b538384 --- /dev/null +++ b/tests/by-util/test_sha256sum.rs @@ -0,0 +1,181 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; +// spell-checker:ignore checkfile, testf, ntestf +macro_rules! get_hash( + ($str:expr) => ( + $str.split(' ').collect::>()[0] + ); +); + +macro_rules! test_digest { + ($id:ident) => { + mod $id { + use uutests::util::*; + use uutests::util_name; + 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(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() + .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(&["--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("--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(&["a", "b", "c"]) + .fails() + .stdout_contains("a\n") + .stdout_contains("c\n") + .stderr_contains("b: No such file or directory"); + } + } + }; +} + +test_digest! {sha256} + +#[test] +fn test_invalid_arg() { + new_ucmd!().arg("--definitely-invalid").fails_with_code(1); +} + +#[test] +fn test_conflicting_arg() { + new_ucmd!().arg("--tag").arg("--check").fails_with_code(1); + new_ucmd!().arg("--tag").arg("--text").fails_with_code(1); +} + +#[test] +fn test_tag() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.write("foobar", "foo bar\n"); + scene + .ccmd("sha256sum") + .arg("--tag") + .arg("foobar") + .succeeds() + .stdout_is( + "SHA256 (foobar) = 1f2ec52b774368781bed1d1fb140a92e0eb6348090619c9291f9a5a3c8e8d151\n", + ); +} + +#[test] +fn test_sha256_binary() { + let ts = TestScenario::new(util_name!()); + assert_eq!( + ts.fixtures.read("binary.sha256.expected"), + get_hash!( + ts.ucmd() + .arg("binary.png") + .succeeds() + .no_stderr() + .stdout_str() + ) + ); +} + +#[test] +fn test_sha256_stdin_binary() { + let ts = TestScenario::new(util_name!()); + assert_eq!( + ts.fixtures.read("binary.sha256.expected"), + get_hash!( + ts.ucmd() + .pipe_in_fixture("binary.png") + .succeeds() + .no_stderr() + .stdout_str() + ) + ); +} + +// This test is currently disabled on windows +#[test] +#[cfg_attr(windows, ignore = "Discussion is in #9168")] +fn test_check_sha256_binary() { + new_ucmd!() + .args(&["--check", "binary.sha256.checkfile"]) + .succeeds() + .no_stderr() + .stdout_is("binary.png: OK\n"); +} + +#[test] +fn test_help_shows_correct_utility_name() { + // Test that help output shows the actual utility name instead of "hashsum" + new_ucmd!() + .arg("--help") + .succeeds() + .stdout_contains("Usage: sha256sum") + .stdout_does_not_contain("Usage: hashsum"); +} diff --git a/tests/fixtures/sha256sum/binary.png b/tests/fixtures/sha256sum/binary.png new file mode 100644 index 000000000..6c4161338 Binary files /dev/null and b/tests/fixtures/sha256sum/binary.png differ diff --git a/tests/fixtures/hashsum/binary.sha256.checkfile b/tests/fixtures/sha256sum/binary.sha256.checkfile similarity index 100% rename from tests/fixtures/hashsum/binary.sha256.checkfile rename to tests/fixtures/sha256sum/binary.sha256.checkfile diff --git a/tests/fixtures/hashsum/binary.sha256.expected b/tests/fixtures/sha256sum/binary.sha256.expected similarity index 100% rename from tests/fixtures/hashsum/binary.sha256.expected rename to tests/fixtures/sha256sum/binary.sha256.expected diff --git a/tests/fixtures/sha256sum/input.txt b/tests/fixtures/sha256sum/input.txt new file mode 100644 index 000000000..8c01d89ae --- /dev/null +++ b/tests/fixtures/sha256sum/input.txt @@ -0,0 +1 @@ +hello, world \ No newline at end of file diff --git a/tests/fixtures/hashsum/sha256.checkfile b/tests/fixtures/sha256sum/sha256.checkfile similarity index 100% rename from tests/fixtures/hashsum/sha256.checkfile rename to tests/fixtures/sha256sum/sha256.checkfile diff --git a/tests/fixtures/hashsum/sha256.expected b/tests/fixtures/sha256sum/sha256.expected similarity index 100% rename from tests/fixtures/hashsum/sha256.expected rename to tests/fixtures/sha256sum/sha256.expected diff --git a/tests/tests.rs b/tests/tests.rs index b51ed1c5b..26cf7b7aa 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -84,6 +84,10 @@ mod test_sha1sum; #[path = "by-util/test_sha224sum.rs"] mod test_sha224sum; +#[cfg(feature = "sha256sum")] +#[path = "by-util/test_sha256sum.rs"] +mod test_sha256sum; + #[cfg(feature = "cp")] #[path = "by-util/test_cp.rs"] mod test_cp;