diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 77f2749443c..a3e6dfd9e1a 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -5,14 +5,17 @@ // spell-checker:ignore (ToDO) fname, algo use clap::{crate_version, value_parser, Arg, ArgAction, Command}; +use hex::decode; use hex::encode; +use std::error::Error; use std::ffi::OsStr; +use std::fmt::Display; use std::fs::File; -use std::io::{self, stdin, BufReader, Read}; +use std::io::{self, stdin, stdout, BufReader, Read, Write}; use std::iter; use std::path::Path; use uucore::{ - error::{FromIo, UResult}, + error::{FromIo, UError, UResult}, format_usage, help_about, help_section, help_usage, sum::{ div_ceil, Blake2b, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha512, Sm3, @@ -36,6 +39,31 @@ const ALGORITHM_OPTIONS_SHA512: &str = "sha512"; const ALGORITHM_OPTIONS_BLAKE2B: &str = "blake2b"; const ALGORITHM_OPTIONS_SM3: &str = "sm3"; +#[derive(Debug)] +enum CkSumError { + RawMultipleFiles, +} + +impl UError for CkSumError { + fn code(&self) -> i32 { + match self { + Self::RawMultipleFiles => 1, + } + } +} + +impl Error for CkSumError {} + +impl Display for CkSumError { + 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") + } + } + } +} + fn detect_algo( program: &str, length: Option, @@ -110,6 +138,7 @@ struct Options { output_bits: usize, untagged: bool, length: Option, + raw: bool, } /// Calculate checksum @@ -123,6 +152,11 @@ fn cksum<'a, I>(mut options: Options, files: I) -> UResult<()> where I: Iterator, { + let files: Vec<_> = files.collect(); + if options.raw && files.len() > 1 { + return Err(Box::new(CkSumError::RawMultipleFiles)); + } + for filename in files { let filename = Path::new(filename); let stdin_buf; @@ -141,6 +175,17 @@ where let (sum, sz) = digest_read(&mut options.digest, &mut file, options.output_bits) .map_err_context(|| "failed to read input".to_string())?; + if options.raw { + let bytes = match options.algo_name { + ALGORITHM_OPTIONS_CRC => sum.parse::().unwrap().to_be_bytes().to_vec(), + ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => { + sum.parse::().unwrap().to_be_bytes().to_vec() + } + _ => decode(sum).unwrap(), + }; + stdout().write_all(&bytes)?; + return Ok(()); + } // The BSD checksum output is 5 digit integer let bsd_width = 5; match (options.algo_name, not_file) { @@ -238,6 +283,7 @@ mod options { pub const FILE: &str = "file"; pub const UNTAGGED: &str = "untagged"; pub const LENGTH: &str = "length"; + pub const RAW: &str = "raw"; } #[uucore::main] @@ -298,6 +344,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { output_bits: bits, length, untagged: matches.get_flag(options::UNTAGGED), + raw: matches.get_flag(options::RAW), }; match matches.get_many::(options::FILE) { @@ -354,5 +401,11 @@ pub fn uu_app() -> Command { .help("digest length in bits; must not exceed the max for the blake2 algorithm and must be a multiple of 8") .action(ArgAction::Set), ) + .arg( + Arg::new(options::RAW) + .long(options::RAW) + .help("emit a raw binary digest, not hexadecimal") + .action(ArgAction::SetTrue), + ) .after_help(AFTER_HELP) } diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index fd55a69e487..b3db0bf0a02 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -287,6 +287,30 @@ fn test_length_is_zero() { .stdout_is_fixture("length_is_zero.expected"); } +#[test] +fn test_raw_single_file() { + for algo in ALGOS { + new_ucmd!() + .arg("--raw") + .arg("lorem_ipsum.txt") + .arg(format!("--algorithm={algo}")) + .succeeds() + .no_stderr() + .stdout_is_fixture_bytes(format!("raw/{algo}_single_file.expected")); + } +} +#[test] +fn test_raw_multiple_files() { + new_ucmd!() + .arg("--raw") + .arg("lorem_ipsum.txt") + .arg("alice_in_wonderland.txt") + .fails() + .no_stdout() + .stderr_contains("cksum: the --raw option is not supported with multiple files") + .code_is(1); +} + #[test] fn test_blake2b_fail_on_directory() { let (at, mut ucmd) = at_and_ucmd!(); diff --git a/tests/fixtures/cksum/raw/blake2b_single_file.expected b/tests/fixtures/cksum/raw/blake2b_single_file.expected new file mode 100644 index 00000000000..19b3112206d --- /dev/null +++ b/tests/fixtures/cksum/raw/blake2b_single_file.expected @@ -0,0 +1 @@ +`x fWEtBlW^J0aX´85B9m \ No newline at end of file diff --git a/tests/fixtures/cksum/raw/bsd_single_file.expected b/tests/fixtures/cksum/raw/bsd_single_file.expected new file mode 100644 index 00000000000..538ab19f728 --- /dev/null +++ b/tests/fixtures/cksum/raw/bsd_single_file.expected @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/fixtures/cksum/raw/crc_single_file.expected b/tests/fixtures/cksum/raw/crc_single_file.expected new file mode 100644 index 00000000000..de175b04582 --- /dev/null +++ b/tests/fixtures/cksum/raw/crc_single_file.expected @@ -0,0 +1 @@ +Ph \ No newline at end of file diff --git a/tests/fixtures/cksum/raw/md5_single_file.expected b/tests/fixtures/cksum/raw/md5_single_file.expected new file mode 100644 index 00000000000..e7b47aebbcb Binary files /dev/null and b/tests/fixtures/cksum/raw/md5_single_file.expected differ diff --git a/tests/fixtures/cksum/raw/sha1_single_file.expected b/tests/fixtures/cksum/raw/sha1_single_file.expected new file mode 100644 index 00000000000..0f634be4749 --- /dev/null +++ b/tests/fixtures/cksum/raw/sha1_single_file.expected @@ -0,0 +1 @@ +к؈:=m毽(%, \ No newline at end of file diff --git a/tests/fixtures/cksum/raw/sha224_single_file.expected b/tests/fixtures/cksum/raw/sha224_single_file.expected new file mode 100644 index 00000000000..bc3fa6bf18f Binary files /dev/null and b/tests/fixtures/cksum/raw/sha224_single_file.expected differ diff --git a/tests/fixtures/cksum/raw/sha256_single_file.expected b/tests/fixtures/cksum/raw/sha256_single_file.expected new file mode 100644 index 00000000000..b9ce06f39d9 --- /dev/null +++ b/tests/fixtures/cksum/raw/sha256_single_file.expected @@ -0,0 +1 @@ + PP 0P g^ SkEC[+? \ No newline at end of file diff --git a/tests/fixtures/cksum/raw/sha384_single_file.expected b/tests/fixtures/cksum/raw/sha384_single_file.expected new file mode 100644 index 00000000000..4b337f859ed --- /dev/null +++ b/tests/fixtures/cksum/raw/sha384_single_file.expected @@ -0,0 +1,3 @@ +K + 2iJMϸ/gLZ{WZ3S +H \ No newline at end of file diff --git a/tests/fixtures/cksum/raw/sha512_single_file.expected b/tests/fixtures/cksum/raw/sha512_single_file.expected new file mode 100644 index 00000000000..79f78cf00cb --- /dev/null +++ b/tests/fixtures/cksum/raw/sha512_single_file.expected @@ -0,0 +1 @@ +Td%VՎsؚ!Yyu)f|T,Bn^OऊVgD k!= \ No newline at end of file diff --git a/tests/fixtures/cksum/raw/sm3_single_file.expected b/tests/fixtures/cksum/raw/sm3_single_file.expected new file mode 100644 index 00000000000..3506406dc42 --- /dev/null +++ b/tests/fixtures/cksum/raw/sm3_single_file.expected @@ -0,0 +1 @@ +m)k] (0CyMg@pi \ No newline at end of file diff --git a/tests/fixtures/cksum/raw/sysv_single_file.expected b/tests/fixtures/cksum/raw/sysv_single_file.expected new file mode 100644 index 00000000000..b975da49c01 --- /dev/null +++ b/tests/fixtures/cksum/raw/sysv_single_file.expected @@ -0,0 +1 @@ +I \ No newline at end of file