Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cksum: permit repeated flags, handle overrides correctly, implement base64 output #6041

Merged
merged 3 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 58 additions & 15 deletions src/uu/cksum/src/cksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::io::{self, stdin, stdout, BufReader, Read, Write};
use std::iter;
use std::path::Path;
use uucore::{
encoding,
error::{FromIo, UError, UResult, USimpleError},
format_usage, help_about, help_section, help_usage, show,
sum::{
Expand Down Expand Up @@ -44,6 +45,13 @@ enum CkSumError {
RawMultipleFiles,
}

#[derive(Debug, PartialEq)]
enum OutputFormat {
Hexadecimal,
Raw,
Base64,
}

impl UError for CkSumError {
fn code(&self) -> i32 {
match self {
Expand Down Expand Up @@ -138,7 +146,7 @@ struct Options {
output_bits: usize,
untagged: bool,
length: Option<usize>,
raw: bool,
output_format: OutputFormat,
}

/// Calculate checksum
Expand All @@ -153,7 +161,7 @@ where
I: Iterator<Item = &'a OsStr>,
{
let files: Vec<_> = files.collect();
if options.raw && files.len() > 1 {
if options.output_format == OutputFormat::Raw && files.len() > 1 {
return Err(Box::new(CkSumError::RawMultipleFiles));
}

Expand All @@ -177,7 +185,7 @@ where
};
Box::new(file_buf) as Box<dyn Read>
});
let (sum, sz) = digest_read(&mut options.digest, &mut file, options.output_bits)
let (sum_hex, sz) = digest_read(&mut options.digest, &mut file, options.output_bits)
.map_err_context(|| "failed to read input".to_string())?;
if filename.is_dir() {
show!(USimpleError::new(
Expand All @@ -186,17 +194,25 @@ where
));
continue;
}
if options.raw {
let bytes = match options.algo_name {
ALGORITHM_OPTIONS_CRC => sum.parse::<u32>().unwrap().to_be_bytes().to_vec(),
ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => {
sum.parse::<u16>().unwrap().to_be_bytes().to_vec()
}
_ => decode(sum).unwrap(),
};
stdout().write_all(&bytes)?;
return Ok(());
}
let sum = match options.output_format {
OutputFormat::Raw => {
let bytes = match options.algo_name {
ALGORITHM_OPTIONS_CRC => sum_hex.parse::<u32>().unwrap().to_be_bytes().to_vec(),
ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => {
sum_hex.parse::<u16>().unwrap().to_be_bytes().to_vec()
}
_ => decode(sum_hex).unwrap(),
};
// Cannot handle multiple files anyway, output immediately.
stdout().write_all(&bytes)?;
return Ok(());
}
OutputFormat::Hexadecimal => sum_hex,
OutputFormat::Base64 => match options.algo_name {
ALGORITHM_OPTIONS_CRC | ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => sum_hex,
_ => encoding::encode(encoding::Format::Base64, &decode(sum_hex).unwrap()).unwrap(),
},
};
// The BSD checksum output is 5 digit integer
let bsd_width = 5;
match (options.algo_name, not_file) {
Expand Down Expand Up @@ -286,8 +302,10 @@ mod options {
pub const ALGORITHM: &str = "algorithm";
pub const FILE: &str = "file";
pub const UNTAGGED: &str = "untagged";
pub const TAG: &str = "tag";
pub const LENGTH: &str = "length";
pub const RAW: &str = "raw";
pub const BASE64: &str = "base64";
}

#[uucore::main]
Expand Down Expand Up @@ -342,13 +360,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {

let (name, algo, bits) = detect_algo(algo_name, length);

let output_format = if matches.get_flag(options::RAW) {
OutputFormat::Raw
} else if matches.get_flag(options::BASE64) {
OutputFormat::Base64
} else {
OutputFormat::Hexadecimal
};

let opts = Options {
algo_name: name,
digest: algo,
output_bits: bits,
length,
untagged: matches.get_flag(options::UNTAGGED),
raw: matches.get_flag(options::RAW),
output_format,
};

match matches.get_many::<String>(options::FILE) {
Expand All @@ -365,6 +391,7 @@ pub fn uu_app() -> Command {
.about(ABOUT)
.override_usage(format_usage(USAGE))
.infer_long_args(true)
.args_override_self(true)
.arg(
Arg::new(options::FILE)
.hide(true)
Expand Down Expand Up @@ -395,6 +422,13 @@ pub fn uu_app() -> Command {
Arg::new(options::UNTAGGED)
.long(options::UNTAGGED)
.help("create a reversed style checksum, without digest type")
.action(ArgAction::SetTrue)
.overrides_with(options::TAG),
)
.arg(
Arg::new(options::TAG)
.long(options::TAG)
.help("create a BSD style checksum, undo --untagged (default)")
.action(ArgAction::SetTrue),
)
.arg(
Expand All @@ -411,5 +445,14 @@ pub fn uu_app() -> Command {
.help("emit a raw binary digest, not hexadecimal")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::BASE64)
.long(options::BASE64)
.help("emit a base64 digest, not hexadecimal")
.action(ArgAction::SetTrue)
// Even though this could easily just override an earlier '--raw',
// GNU cksum does not permit these flags to be combined:
.conflicts_with(options::RAW),
)
.after_help(AFTER_HELP)
}
1 change: 1 addition & 0 deletions src/uucore/src/lib/features/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Io(#[from] io::Error),
}

#[derive(Debug)]

Check warning on line 26 in src/uucore/src/lib/features/encoding.rs

View check run for this annotation

Codecov / codecov/patch

src/uucore/src/lib/features/encoding.rs#L26

Added line #L26 was not covered by tests
pub enum EncodeError {
Z85InputLenNotMultipleOf4,
InvalidInput,
Expand Down
85 changes: 85 additions & 0 deletions tests/by-util/test_cksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,29 @@ fn test_stdin_larger_than_128_bytes() {
assert_eq!(bytes_cnt, 2058);
}

#[test]
fn test_repeated_flags() {
new_ucmd!()
.arg("-a")
.arg("sha1")
.arg("--algo=sha256")
.arg("-a=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("md5_single_file.expected");
}

#[test]
fn test_tag_after_untagged() {
new_ucmd!()
.arg("--untagged")
.arg("--tag")
.arg("-a=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("md5_single_file.expected");
}

#[test]
fn test_algorithm_single_file() {
for algo in ALGOS {
Expand Down Expand Up @@ -208,6 +231,17 @@ fn test_untagged_algorithm_single_file() {
}
}

#[test]
fn test_untagged_algorithm_after_tag() {
new_ucmd!()
.arg("--tag")
.arg("--untagged")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("untagged/md5_single_file.expected");
}

#[test]
fn test_untagged_algorithm_multiple_files() {
for algo in ALGOS {
Expand Down Expand Up @@ -291,6 +325,20 @@ fn test_length_is_zero() {
.stdout_is_fixture("length_is_zero.expected");
}

#[test]
fn test_length_repeated() {
new_ucmd!()
.arg("--length=10")
.arg("--length=123456")
.arg("--length=0")
.arg("--algorithm=blake2b")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.no_stderr()
.stdout_is_fixture("length_is_zero.expected");
}

#[test]
fn test_raw_single_file() {
for algo in ALGOS {
Expand All @@ -315,6 +363,43 @@ fn test_raw_multiple_files() {
.code_is(1);
}

#[test]
fn test_base64_raw_conflicts() {
new_ucmd!()
.arg("--base64")
.arg("--raw")
.arg("lorem_ipsum.txt")
.fails()
.no_stdout()
.stderr_contains("--base64")
.stderr_contains("cannot be used with")
.stderr_contains("--raw");
}

#[test]
fn test_base64_single_file() {
for algo in ALGOS {
new_ucmd!()
.arg("--base64")
.arg("lorem_ipsum.txt")
.arg(format!("--algorithm={algo}"))
.succeeds()
.no_stderr()
.stdout_is_fixture_bytes(format!("base64/{algo}_single_file.expected"));
}
}
#[test]
fn test_base64_multiple_files() {
new_ucmd!()
.arg("--base64")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.no_stderr()
.stdout_is_fixture_bytes(format!("base64/md5_multiple_files.expected"));
}

#[test]
fn test_fail_on_folder() {
let (at, mut ucmd) = at_and_ucmd!();
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/blake2b_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BLAKE2b (lorem_ipsum.txt) = DpegkYnlYMN4nAv/HwIBZoYe+FfR+/5FdN4YQuPAbKu5V15K9jCaFmFYwrQI08A4wbSdgos1FYFCzcA5bRGVww==
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/bsd_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
08109 1 lorem_ipsum.txt
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/crc_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
378294376 772 lorem_ipsum.txt
2 changes: 2 additions & 0 deletions tests/fixtures/cksum/base64/md5_multiple_files.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
MD5 (lorem_ipsum.txt) = zXJGkPfcYXdd+sQApx8sqg==
MD5 (alice_in_wonderland.txt) = 9vpwM+FhZqlYmqHAOI/9WA==
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/md5_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MD5 (lorem_ipsum.txt) = zXJGkPfcYXdd+sQApx8sqg==
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/sha1_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SHA1 (lorem_ipsum.txt) = qx3QuuHYiDo9GKZt5q+9KCUs++8=
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/sha224_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SHA224 (lorem_ipsum.txt) = PeZvvK0QbhtAqzkb5WxR0gB+sfnGVdD04pv8AQ==
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/sha256_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SHA256 (lorem_ipsum.txt) = 98QgUBxQ4AswklAQDWfqXpEJgVNrRYL+nENb2Ss/HwI=
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/sha384_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SHA384 (lorem_ipsum.txt) = S+S5Cg0NMpZpkpIQGfJKvIJNz7ixxAgQLx9niPuAupqaTFp7V1ozU6kKjucZSB3L
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/sha512_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SHA512 (lorem_ipsum.txt) = llRkqyVWqtWOvHPYmtIh5Vl5dSnsr8D0ZsEXlc/21uLGD5agfFQs/R9Cbl5P4KSKoVZnukQJayE9CBPNA436BQ==
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/sm3_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SM3 (lorem_ipsum.txt) = bSlrgF0GC/7SKAjfMI27m0MXeU3U7WdAoQdwp4Jpm8I=
1 change: 1 addition & 0 deletions tests/fixtures/cksum/base64/sysv_single_file.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6985 2 lorem_ipsum.txt
Loading