mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge pull request #7192 from RenjiSann/cksum-fix-error-handling
cksum: Update error and flags handling to improver GNU's match
This commit is contained in:
commit
5129aba0f0
4 changed files with 545 additions and 91 deletions
|
@ -13,8 +13,9 @@ use std::iter;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::checksum::{
|
use uucore::checksum::{
|
||||||
calculate_blake2b_length, detect_algo, digest_reader, perform_checksum_validation,
|
calculate_blake2b_length, detect_algo, digest_reader, perform_checksum_validation,
|
||||||
ChecksumError, ChecksumOptions, ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD,
|
ChecksumError, ChecksumOptions, ChecksumVerbose, ALGORITHM_OPTIONS_BLAKE2B,
|
||||||
ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV, SUPPORTED_ALGORITHMS,
|
ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV,
|
||||||
|
SUPPORTED_ALGORITHMS,
|
||||||
};
|
};
|
||||||
use uucore::{
|
use uucore::{
|
||||||
encoding,
|
encoding,
|
||||||
|
@ -322,13 +323,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|| iter::once(OsStr::new("-")).collect::<Vec<_>>(),
|
|| iter::once(OsStr::new("-")).collect::<Vec<_>>(),
|
||||||
|files| files.map(OsStr::new).collect::<Vec<_>>(),
|
|files| files.map(OsStr::new).collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let verbose = ChecksumVerbose::new(status, quiet, warn);
|
||||||
|
|
||||||
let opts = ChecksumOptions {
|
let opts = ChecksumOptions {
|
||||||
binary: binary_flag,
|
binary: binary_flag,
|
||||||
ignore_missing,
|
ignore_missing,
|
||||||
quiet,
|
|
||||||
status,
|
|
||||||
strict,
|
strict,
|
||||||
warn,
|
verbose,
|
||||||
};
|
};
|
||||||
|
|
||||||
return perform_checksum_validation(files.iter().copied(), algo_option, length, opts);
|
return perform_checksum_validation(files.iter().copied(), algo_option, length, opts);
|
||||||
|
@ -462,19 +464,22 @@ pub fn uu_app() -> Command {
|
||||||
.short('w')
|
.short('w')
|
||||||
.long("warn")
|
.long("warn")
|
||||||
.help("warn about improperly formatted checksum lines")
|
.help("warn about improperly formatted checksum lines")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue)
|
||||||
|
.overrides_with_all([options::STATUS, options::QUIET]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::STATUS)
|
Arg::new(options::STATUS)
|
||||||
.long("status")
|
.long("status")
|
||||||
.help("don't output anything, status code shows success")
|
.help("don't output anything, status code shows success")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue)
|
||||||
|
.overrides_with_all([options::WARN, options::QUIET]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::QUIET)
|
Arg::new(options::QUIET)
|
||||||
.long(options::QUIET)
|
.long(options::QUIET)
|
||||||
.help("don't print OK for each successfully verified file")
|
.help("don't print OK for each successfully verified file")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue)
|
||||||
|
.overrides_with_all([options::WARN, options::STATUS]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::IGNORE_MISSING)
|
Arg::new(options::IGNORE_MISSING)
|
||||||
|
|
|
@ -24,6 +24,7 @@ use uucore::checksum::escape_filename;
|
||||||
use uucore::checksum::perform_checksum_validation;
|
use uucore::checksum::perform_checksum_validation;
|
||||||
use uucore::checksum::ChecksumError;
|
use uucore::checksum::ChecksumError;
|
||||||
use uucore::checksum::ChecksumOptions;
|
use uucore::checksum::ChecksumOptions;
|
||||||
|
use uucore::checksum::ChecksumVerbose;
|
||||||
use uucore::checksum::HashAlgorithm;
|
use uucore::checksum::HashAlgorithm;
|
||||||
use uucore::error::{FromIo, UResult};
|
use uucore::error::{FromIo, UResult};
|
||||||
use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
|
use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
|
||||||
|
@ -240,13 +241,14 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
|
||||||
|| iter::once(OsStr::new("-")).collect::<Vec<_>>(),
|
|| iter::once(OsStr::new("-")).collect::<Vec<_>>(),
|
||||||
|files| files.map(OsStr::new).collect::<Vec<_>>(),
|
|files| files.map(OsStr::new).collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let verbose = ChecksumVerbose::new(status, quiet, warn);
|
||||||
|
|
||||||
let opts = ChecksumOptions {
|
let opts = ChecksumOptions {
|
||||||
binary,
|
binary,
|
||||||
ignore_missing,
|
ignore_missing,
|
||||||
quiet,
|
|
||||||
status,
|
|
||||||
strict,
|
strict,
|
||||||
warn,
|
verbose,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Execute the checksum validation
|
// Execute the checksum validation
|
||||||
|
@ -356,14 +358,16 @@ pub fn uu_app_common() -> Command {
|
||||||
.short('q')
|
.short('q')
|
||||||
.long(options::QUIET)
|
.long(options::QUIET)
|
||||||
.help("don't print OK for each successfully verified file")
|
.help("don't print OK for each successfully verified file")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue)
|
||||||
|
.overrides_with_all([options::STATUS, options::WARN]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::STATUS)
|
Arg::new(options::STATUS)
|
||||||
.short('s')
|
.short('s')
|
||||||
.long("status")
|
.long("status")
|
||||||
.help("don't output anything, status code shows success")
|
.help("don't output anything, status code shows success")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue)
|
||||||
|
.overrides_with_all([options::QUIET, options::WARN]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::STRICT)
|
Arg::new(options::STRICT)
|
||||||
|
@ -382,7 +386,8 @@ pub fn uu_app_common() -> Command {
|
||||||
.short('w')
|
.short('w')
|
||||||
.long("warn")
|
.long("warn")
|
||||||
.help("warn about improperly formatted checksum lines")
|
.help("warn about improperly formatted checksum lines")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue)
|
||||||
|
.overrides_with_all([options::QUIET, options::STATUS]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("zero")
|
Arg::new("zero")
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{set_exit_code, FromIo, UError, UResult, USimpleError},
|
error::{FromIo, UError, UResult, USimpleError},
|
||||||
os_str_as_bytes, os_str_from_bytes, read_os_string_lines, show, show_error, show_warning_caps,
|
os_str_as_bytes, os_str_from_bytes, read_os_string_lines, show, show_error, show_warning_caps,
|
||||||
sum::{
|
sum::{
|
||||||
Blake2b, Blake3, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha3_224,
|
Blake2b, Blake3, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha3_224,
|
||||||
|
@ -130,10 +130,12 @@ impl From<ChecksumError> for LineCheckError {
|
||||||
enum FileCheckError {
|
enum FileCheckError {
|
||||||
/// a generic UError was encountered in sub-functions
|
/// a generic UError was encountered in sub-functions
|
||||||
UError(Box<dyn UError>),
|
UError(Box<dyn UError>),
|
||||||
/// the checksum file is improperly formatted.
|
|
||||||
ImproperlyFormatted,
|
|
||||||
/// reading of the checksum file failed
|
/// reading of the checksum file failed
|
||||||
CantOpenChecksumFile,
|
CantOpenChecksumFile,
|
||||||
|
/// processing of the file is considered as a failure regarding the
|
||||||
|
/// provided flags. This however does not stop the processing of
|
||||||
|
/// further files.
|
||||||
|
Failed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<dyn UError>> for FileCheckError {
|
impl From<Box<dyn UError>> for FileCheckError {
|
||||||
|
@ -148,15 +150,57 @@ impl From<ChecksumError> for FileCheckError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)]
|
||||||
|
pub enum ChecksumVerbose {
|
||||||
|
Status,
|
||||||
|
Quiet,
|
||||||
|
Normal,
|
||||||
|
Warning,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChecksumVerbose {
|
||||||
|
pub fn new(status: bool, quiet: bool, warn: bool) -> Self {
|
||||||
|
use ChecksumVerbose::*;
|
||||||
|
|
||||||
|
// Assume only one of the three booleans will be enabled at once.
|
||||||
|
// This is ensured by clap's overriding arguments.
|
||||||
|
match (status, quiet, warn) {
|
||||||
|
(true, _, _) => Status,
|
||||||
|
(_, true, _) => Quiet,
|
||||||
|
(_, _, true) => Warning,
|
||||||
|
_ => Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn over_status(self) -> bool {
|
||||||
|
self > Self::Status
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn over_quiet(self) -> bool {
|
||||||
|
self > Self::Quiet
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn at_least_warning(self) -> bool {
|
||||||
|
self >= Self::Warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ChecksumVerbose {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This struct regroups CLI flags.
|
/// This struct regroups CLI flags.
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub struct ChecksumOptions {
|
pub struct ChecksumOptions {
|
||||||
pub binary: bool,
|
pub binary: bool,
|
||||||
pub ignore_missing: bool,
|
pub ignore_missing: bool,
|
||||||
pub quiet: bool,
|
|
||||||
pub status: bool,
|
|
||||||
pub strict: bool,
|
pub strict: bool,
|
||||||
pub warn: bool,
|
pub verbose: ChecksumVerbose,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
@ -235,20 +279,19 @@ pub fn create_sha3(bits: Option<usize>) -> UResult<HashAlgorithm> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::comparison_chain)]
|
#[allow(clippy::comparison_chain)]
|
||||||
fn cksum_output(res: &ChecksumResult, status: bool) {
|
fn print_cksum_report(res: &ChecksumResult) {
|
||||||
if res.bad_format == 1 {
|
if res.bad_format == 1 {
|
||||||
show_warning_caps!("{} line is improperly formatted", res.bad_format);
|
show_warning_caps!("{} line is improperly formatted", res.bad_format);
|
||||||
} else if res.bad_format > 1 {
|
} else if res.bad_format > 1 {
|
||||||
show_warning_caps!("{} lines are improperly formatted", res.bad_format);
|
show_warning_caps!("{} lines are improperly formatted", res.bad_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !status {
|
if res.failed_cksum == 1 {
|
||||||
if res.failed_cksum == 1 {
|
show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum);
|
||||||
show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum);
|
} else if res.failed_cksum > 1 {
|
||||||
} else if res.failed_cksum > 1 {
|
show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum);
|
||||||
show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.failed_open_file == 1 {
|
if res.failed_open_file == 1 {
|
||||||
show_warning_caps!("{} listed file could not be read", res.failed_open_file);
|
show_warning_caps!("{} listed file could not be read", res.failed_open_file);
|
||||||
} else if res.failed_open_file > 1 {
|
} else if res.failed_open_file > 1 {
|
||||||
|
@ -284,10 +327,10 @@ impl FileChecksumResult {
|
||||||
|
|
||||||
/// The cli options might prevent to display on the outcome of the
|
/// The cli options might prevent to display on the outcome of the
|
||||||
/// comparison on STDOUT.
|
/// comparison on STDOUT.
|
||||||
fn can_display(&self, opts: ChecksumOptions) -> bool {
|
fn can_display(&self, verbose: ChecksumVerbose) -> bool {
|
||||||
match self {
|
match self {
|
||||||
FileChecksumResult::Ok => !opts.status && !opts.quiet,
|
FileChecksumResult::Ok => verbose.over_quiet(),
|
||||||
FileChecksumResult::Failed => !opts.status,
|
FileChecksumResult::Failed => verbose.over_status(),
|
||||||
FileChecksumResult::CantOpen => true,
|
FileChecksumResult::CantOpen => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,9 +353,9 @@ fn print_file_report<W: Write>(
|
||||||
filename: &[u8],
|
filename: &[u8],
|
||||||
result: FileChecksumResult,
|
result: FileChecksumResult,
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
opts: ChecksumOptions,
|
verbose: ChecksumVerbose,
|
||||||
) {
|
) {
|
||||||
if result.can_display(opts) {
|
if result.can_display(verbose) {
|
||||||
let _ = write!(w, "{prefix}");
|
let _ = write!(w, "{prefix}");
|
||||||
let _ = w.write_all(filename);
|
let _ = w.write_all(filename);
|
||||||
let _ = writeln!(w, ": {result}");
|
let _ = writeln!(w, ": {result}");
|
||||||
|
@ -589,7 +632,7 @@ fn get_file_to_check(
|
||||||
filename_bytes,
|
filename_bytes,
|
||||||
FileChecksumResult::CantOpen,
|
FileChecksumResult::CantOpen,
|
||||||
"",
|
"",
|
||||||
opts,
|
opts.verbose,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
match File::open(filename) {
|
match File::open(filename) {
|
||||||
|
@ -648,12 +691,11 @@ fn get_input_file(filename: &OsStr) -> UResult<Box<dyn Read>> {
|
||||||
fn identify_algo_name_and_length(
|
fn identify_algo_name_and_length(
|
||||||
line_info: &LineInfo,
|
line_info: &LineInfo,
|
||||||
algo_name_input: Option<&str>,
|
algo_name_input: Option<&str>,
|
||||||
|
last_algo: &mut Option<String>,
|
||||||
) -> Option<(String, Option<usize>)> {
|
) -> Option<(String, Option<usize>)> {
|
||||||
let algorithm = line_info
|
let algo_from_line = line_info.algo_name.clone().unwrap_or_default();
|
||||||
.algo_name
|
let algorithm = algo_from_line.to_lowercase();
|
||||||
.clone()
|
*last_algo = Some(algo_from_line);
|
||||||
.unwrap_or_default()
|
|
||||||
.to_lowercase();
|
|
||||||
|
|
||||||
// check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file
|
// check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file
|
||||||
// (for example SHA1 (f) = d...)
|
// (for example SHA1 (f) = d...)
|
||||||
|
@ -711,7 +753,7 @@ fn compute_and_check_digest_from_file(
|
||||||
filename,
|
filename,
|
||||||
FileChecksumResult::from_bool(checksum_correct),
|
FileChecksumResult::from_bool(checksum_correct),
|
||||||
prefix,
|
prefix,
|
||||||
opts,
|
opts.verbose,
|
||||||
);
|
);
|
||||||
|
|
||||||
if checksum_correct {
|
if checksum_correct {
|
||||||
|
@ -726,11 +768,13 @@ fn process_algo_based_line(
|
||||||
line_info: &LineInfo,
|
line_info: &LineInfo,
|
||||||
cli_algo_name: Option<&str>,
|
cli_algo_name: Option<&str>,
|
||||||
opts: ChecksumOptions,
|
opts: ChecksumOptions,
|
||||||
|
last_algo: &mut Option<String>,
|
||||||
) -> Result<(), LineCheckError> {
|
) -> Result<(), LineCheckError> {
|
||||||
let filename_to_check = line_info.filename.as_slice();
|
let filename_to_check = line_info.filename.as_slice();
|
||||||
|
|
||||||
let (algo_name, algo_byte_len) = identify_algo_name_and_length(line_info, cli_algo_name)
|
let (algo_name, algo_byte_len) =
|
||||||
.ok_or(LineCheckError::ImproperlyFormatted)?;
|
identify_algo_name_and_length(line_info, cli_algo_name, last_algo)
|
||||||
|
.ok_or(LineCheckError::ImproperlyFormatted)?;
|
||||||
|
|
||||||
// If the digest bitlen is known, we can check the format of the expected
|
// If the digest bitlen is known, we can check the format of the expected
|
||||||
// checksum with it.
|
// checksum with it.
|
||||||
|
@ -789,13 +833,13 @@ fn process_non_algo_based_line(
|
||||||
/// matched the expected.
|
/// matched the expected.
|
||||||
/// If the comparison didn't happen, return a `LineChecksumError`.
|
/// If the comparison didn't happen, return a `LineChecksumError`.
|
||||||
fn process_checksum_line(
|
fn process_checksum_line(
|
||||||
filename_input: &OsStr,
|
|
||||||
line: &OsStr,
|
line: &OsStr,
|
||||||
i: usize,
|
i: usize,
|
||||||
cli_algo_name: Option<&str>,
|
cli_algo_name: Option<&str>,
|
||||||
cli_algo_length: Option<usize>,
|
cli_algo_length: Option<usize>,
|
||||||
opts: ChecksumOptions,
|
opts: ChecksumOptions,
|
||||||
cached_regex: &mut Option<LineFormat>,
|
cached_regex: &mut Option<LineFormat>,
|
||||||
|
last_algo: &mut Option<String>,
|
||||||
) -> Result<(), LineCheckError> {
|
) -> Result<(), LineCheckError> {
|
||||||
let line_bytes = os_str_as_bytes(line)?;
|
let line_bytes = os_str_as_bytes(line)?;
|
||||||
|
|
||||||
|
@ -806,34 +850,19 @@ fn process_checksum_line(
|
||||||
|
|
||||||
// Use `LineInfo` to extract the data of a line.
|
// Use `LineInfo` to extract the data of a line.
|
||||||
// Then, depending on its format, apply a different pre-treatment.
|
// Then, depending on its format, apply a different pre-treatment.
|
||||||
if let Some(line_info) = LineInfo::parse(line, cached_regex) {
|
let Some(line_info) = LineInfo::parse(line, cached_regex) else {
|
||||||
if line_info.format == LineFormat::AlgoBased {
|
return Err(LineCheckError::ImproperlyFormatted);
|
||||||
process_algo_based_line(&line_info, cli_algo_name, opts)
|
};
|
||||||
} else if let Some(cli_algo) = cli_algo_name {
|
|
||||||
// If we match a non-algo based regex, we expect a cli argument
|
|
||||||
// to give us the algorithm to use
|
|
||||||
process_non_algo_based_line(i, &line_info, cli_algo, cli_algo_length, opts)
|
|
||||||
} else {
|
|
||||||
// We have no clue of what algorithm to use
|
|
||||||
return Err(LineCheckError::ImproperlyFormatted);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if opts.warn {
|
|
||||||
let algo = if let Some(algo_name_input) = cli_algo_name {
|
|
||||||
algo_name_input.to_uppercase()
|
|
||||||
} else {
|
|
||||||
"Unknown algorithm".to_string()
|
|
||||||
};
|
|
||||||
eprintln!(
|
|
||||||
"{}: {}: {}: improperly formatted {} checksum line",
|
|
||||||
util_name(),
|
|
||||||
&filename_input.maybe_quote(),
|
|
||||||
i + 1,
|
|
||||||
algo
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(LineCheckError::ImproperlyFormatted)
|
if line_info.format == LineFormat::AlgoBased {
|
||||||
|
process_algo_based_line(&line_info, cli_algo_name, opts, last_algo)
|
||||||
|
} else if let Some(cli_algo) = cli_algo_name {
|
||||||
|
// If we match a non-algo based regex, we expect a cli argument
|
||||||
|
// to give us the algorithm to use
|
||||||
|
process_non_algo_based_line(i, &line_info, cli_algo, cli_algo_length, opts)
|
||||||
|
} else {
|
||||||
|
// We have no clue of what algorithm to use
|
||||||
|
return Err(LineCheckError::ImproperlyFormatted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,7 +885,6 @@ fn process_checksum_file(
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Could not read the file, show the error and continue to the next file
|
// Could not read the file, show the error and continue to the next file
|
||||||
show_error!("{e}");
|
show_error!("{e}");
|
||||||
set_exit_code(1);
|
|
||||||
return Err(FileCheckError::CantOpenChecksumFile);
|
return Err(FileCheckError::CantOpenChecksumFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -868,16 +896,20 @@ fn process_checksum_file(
|
||||||
// cached_regex is used to ensure that several non algo-based checksum line
|
// cached_regex is used to ensure that several non algo-based checksum line
|
||||||
// will use the same regex.
|
// will use the same regex.
|
||||||
let mut cached_regex = None;
|
let mut cached_regex = None;
|
||||||
|
// last_algo caches the algorithm used in the last line to print a warning
|
||||||
|
// message for the current line if improperly formatted.
|
||||||
|
// Behavior tested in gnu_cksum_c::test_warn
|
||||||
|
let mut last_algo = None;
|
||||||
|
|
||||||
for (i, line) in lines.iter().enumerate() {
|
for (i, line) in lines.iter().enumerate() {
|
||||||
let line_result = process_checksum_line(
|
let line_result = process_checksum_line(
|
||||||
filename_input,
|
|
||||||
line,
|
line,
|
||||||
i,
|
i,
|
||||||
cli_algo_name,
|
cli_algo_name,
|
||||||
cli_algo_length,
|
cli_algo_length,
|
||||||
opts,
|
opts,
|
||||||
&mut cached_regex,
|
&mut cached_regex,
|
||||||
|
&mut last_algo,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Match a first time to elude critical UErrors, and increment the total
|
// Match a first time to elude critical UErrors, and increment the total
|
||||||
|
@ -893,7 +925,26 @@ fn process_checksum_file(
|
||||||
match line_result {
|
match line_result {
|
||||||
Ok(()) => res.correct += 1,
|
Ok(()) => res.correct += 1,
|
||||||
Err(DigestMismatch) => res.failed_cksum += 1,
|
Err(DigestMismatch) => res.failed_cksum += 1,
|
||||||
Err(ImproperlyFormatted) => res.bad_format += 1,
|
Err(ImproperlyFormatted) => {
|
||||||
|
res.bad_format += 1;
|
||||||
|
|
||||||
|
if opts.verbose.at_least_warning() {
|
||||||
|
let algo = if let Some(algo_name_input) = cli_algo_name {
|
||||||
|
Cow::Owned(algo_name_input.to_uppercase())
|
||||||
|
} else if let Some(algo) = &last_algo {
|
||||||
|
Cow::Borrowed(algo.as_str())
|
||||||
|
} else {
|
||||||
|
Cow::Borrowed("Unknown algorithm")
|
||||||
|
};
|
||||||
|
eprintln!(
|
||||||
|
"{}: {}: {}: improperly formatted {} checksum line",
|
||||||
|
util_name(),
|
||||||
|
&filename_input.maybe_quote(),
|
||||||
|
i + 1,
|
||||||
|
algo
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(CantOpenFile | FileIsDirectory) => res.failed_open_file += 1,
|
Err(CantOpenFile | FileIsDirectory) => res.failed_open_file += 1,
|
||||||
Err(FileNotFound) if !opts.ignore_missing => res.failed_open_file += 1,
|
Err(FileNotFound) if !opts.ignore_missing => res.failed_open_file += 1,
|
||||||
_ => continue,
|
_ => continue,
|
||||||
|
@ -903,36 +954,43 @@ fn process_checksum_file(
|
||||||
// not a single line correctly formatted found
|
// not a single line correctly formatted found
|
||||||
// return an error
|
// return an error
|
||||||
if res.total_properly_formatted() == 0 {
|
if res.total_properly_formatted() == 0 {
|
||||||
if !opts.status {
|
if opts.verbose.over_status() {
|
||||||
log_no_properly_formatted(get_filename_for_output(filename_input, input_is_stdin));
|
log_no_properly_formatted(get_filename_for_output(filename_input, input_is_stdin));
|
||||||
}
|
}
|
||||||
set_exit_code(1);
|
return Err(FileCheckError::Failed);
|
||||||
return Err(FileCheckError::ImproperlyFormatted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if any incorrectly formatted line, show it
|
// if any incorrectly formatted line, show it
|
||||||
cksum_output(&res, opts.status);
|
if opts.verbose.over_status() {
|
||||||
|
print_cksum_report(&res);
|
||||||
|
}
|
||||||
|
|
||||||
if opts.ignore_missing && res.correct == 0 {
|
if opts.ignore_missing && res.correct == 0 {
|
||||||
// we have only bad format
|
// we have only bad format
|
||||||
// and we had ignore-missing
|
// and we had ignore-missing
|
||||||
eprintln!(
|
if opts.verbose.over_status() {
|
||||||
"{}: {}: no file was verified",
|
eprintln!(
|
||||||
util_name(),
|
"{}: {}: no file was verified",
|
||||||
filename_input.maybe_quote(),
|
util_name(),
|
||||||
);
|
filename_input.maybe_quote(),
|
||||||
set_exit_code(1);
|
);
|
||||||
|
}
|
||||||
|
return Err(FileCheckError::Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// strict means that we should have an exit code.
|
// strict means that we should have an exit code.
|
||||||
if opts.strict && res.bad_format > 0 {
|
if opts.strict && res.bad_format > 0 {
|
||||||
set_exit_code(1);
|
return Err(FileCheckError::Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have any failed checksum verification, we set an exit code
|
// If a file was missing, return an error unless we explicitly ignore it.
|
||||||
// except if we have ignore_missing
|
if res.failed_open_file > 0 && !opts.ignore_missing {
|
||||||
if (res.failed_cksum > 0 || res.failed_open_file > 0) && !opts.ignore_missing {
|
return Err(FileCheckError::Failed);
|
||||||
set_exit_code(1);
|
}
|
||||||
|
|
||||||
|
// Obviously, if a checksum failed at some point, report the error.
|
||||||
|
if res.failed_cksum > 0 {
|
||||||
|
return Err(FileCheckError::Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -950,16 +1008,23 @@ pub fn perform_checksum_validation<'a, I>(
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a OsStr>,
|
I: Iterator<Item = &'a OsStr>,
|
||||||
{
|
{
|
||||||
|
let mut failed = false;
|
||||||
|
|
||||||
// if cksum has several input files, it will print the result for each file
|
// if cksum has several input files, it will print the result for each file
|
||||||
for filename_input in files {
|
for filename_input in files {
|
||||||
use FileCheckError::*;
|
use FileCheckError::*;
|
||||||
match process_checksum_file(filename_input, algo_name_input, length_input, opts) {
|
match process_checksum_file(filename_input, algo_name_input, length_input, opts) {
|
||||||
Err(UError(e)) => return Err(e),
|
Err(UError(e)) => return Err(e),
|
||||||
Err(CantOpenChecksumFile | ImproperlyFormatted) | Ok(_) => continue,
|
Err(Failed | CantOpenChecksumFile) => failed = true,
|
||||||
|
Ok(_) => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
if failed {
|
||||||
|
Err(USimpleError::new(1, ""))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn digest_reader<T: Read>(
|
pub fn digest_reader<T: Read>(
|
||||||
|
@ -1416,7 +1481,7 @@ mod tests {
|
||||||
|
|
||||||
for (filename, result, prefix, expected) in cases {
|
for (filename, result, prefix, expected) in cases {
|
||||||
let mut buffer: Vec<u8> = vec![];
|
let mut buffer: Vec<u8> = vec![];
|
||||||
print_file_report(&mut buffer, filename, *result, prefix, opts);
|
print_file_report(&mut buffer, filename, *result, prefix, opts.verbose);
|
||||||
assert_eq!(&buffer, expected)
|
assert_eq!(&buffer, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore (words) asdf algo algos asha mgmt xffname hexa GFYEQ HYQK Yqxb
|
// spell-checker:ignore (words) asdf algo algos asha mgmt xffname hexa GFYEQ HYQK Yqxb dont
|
||||||
|
|
||||||
use crate::common::util::TestScenario;
|
use crate::common::util::TestScenario;
|
||||||
|
|
||||||
|
@ -1284,6 +1284,18 @@ fn test_several_files_error_mgmt() {
|
||||||
.stderr_contains("incorrect: no properly ");
|
.stderr_contains("incorrect: no properly ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_unknown_checksum_file() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("missing")
|
||||||
|
.fails()
|
||||||
|
.stderr_only("cksum: missing: No such file or directory\n");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_comment_line() {
|
fn test_check_comment_line() {
|
||||||
// A comment in a checksum file shall be discarded unnoticed.
|
// A comment in a checksum file shall be discarded unnoticed.
|
||||||
|
@ -1811,6 +1823,373 @@ mod gnu_cksum_base64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This module reimplements the cksum-c.sh GNU test.
|
||||||
|
mod gnu_cksum_c {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const INVALID_SUM: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaafdb57c725157cb40b5aee8d937b8351477e";
|
||||||
|
|
||||||
|
fn make_scene() -> TestScenario {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
at.write("input", "9\n7\n1\n4\n2\n6\n3\n5\n8\n10\n");
|
||||||
|
|
||||||
|
let algos: &[&[&str]] = &[
|
||||||
|
&["-a", "sha384"],
|
||||||
|
&["-a", "blake2b"],
|
||||||
|
&["-a", "blake2b", "-l", "384"],
|
||||||
|
&["-a", "sm3"],
|
||||||
|
];
|
||||||
|
|
||||||
|
for args in algos {
|
||||||
|
let result = scene.ucmd().args(args).succeeds();
|
||||||
|
let stdout = result.stdout();
|
||||||
|
at.append_bytes("CHECKSUMS", stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_signed_checksums() {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_individual_digests_in_mixed_file() {
|
||||||
|
let scene = make_scene();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("-a")
|
||||||
|
.arg("sm3")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.succeeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_against_older_non_hex_formats() {
|
||||||
|
let scene = make_scene();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("-c")
|
||||||
|
.arg("-a")
|
||||||
|
.arg("crc")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails();
|
||||||
|
|
||||||
|
let crc_cmd = scene.ucmd().arg("-a").arg("crc").arg("input").succeeds();
|
||||||
|
let crc_cmd_out = crc_cmd.stdout();
|
||||||
|
scene.fixtures.write_bytes("CHECKSUMS.crc", crc_cmd_out);
|
||||||
|
|
||||||
|
scene.ucmd().arg("-c").arg("CHECKSUMS.crc").fails();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status() {
|
||||||
|
let scene = make_scene();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--status")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.succeeds()
|
||||||
|
.no_output();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_scene_with_comment() -> TestScenario {
|
||||||
|
let scene = make_scene();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.fixtures
|
||||||
|
.append("CHECKSUMS", "# Very important comment\n");
|
||||||
|
|
||||||
|
scene
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_with_comment() {
|
||||||
|
let scene = make_scene_with_comment();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--status")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.succeeds()
|
||||||
|
.no_output();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_scene_with_invalid_line() -> TestScenario {
|
||||||
|
let scene = make_scene_with_comment();
|
||||||
|
|
||||||
|
scene.fixtures.append("CHECKSUMS", "invalid_line\n");
|
||||||
|
|
||||||
|
scene
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_strict() {
|
||||||
|
let scene = make_scene_with_invalid_line();
|
||||||
|
|
||||||
|
// without strict, succeeds
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.succeeds()
|
||||||
|
.stderr_contains("1 line is improperly formatted");
|
||||||
|
|
||||||
|
// with strict, fails
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--strict")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("1 line is improperly formatted");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_scene_with_two_invalid_lines() -> TestScenario {
|
||||||
|
let scene = make_scene_with_comment();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.fixtures
|
||||||
|
.append("CHECKSUMS", "invalid_line\ninvalid_line\n");
|
||||||
|
|
||||||
|
scene
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_strict_plural_checks() {
|
||||||
|
let scene = make_scene_with_two_invalid_lines();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--strict")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("2 lines are improperly formatted");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_scene_with_incorrect_checksum() -> TestScenario {
|
||||||
|
let scene = make_scene_with_two_invalid_lines();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.fixtures
|
||||||
|
.append("CHECKSUMS", &format!("SM3 (input) = {INVALID_SUM}\n"));
|
||||||
|
|
||||||
|
scene
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_with_incorrect_checksum() {
|
||||||
|
let scene = make_scene_with_incorrect_checksum();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails()
|
||||||
|
.stdout_contains("input: FAILED")
|
||||||
|
.stderr_contains("1 computed checksum did NOT match");
|
||||||
|
|
||||||
|
// also fails with strict
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--strict")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails()
|
||||||
|
.stdout_contains("input: FAILED")
|
||||||
|
.stderr_contains("1 computed checksum did NOT match");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_with_errors() {
|
||||||
|
let scene = make_scene_with_incorrect_checksum();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--status")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails()
|
||||||
|
.no_output();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_with_non_existing_file() {
|
||||||
|
let scene = make_scene();
|
||||||
|
scene
|
||||||
|
.fixtures
|
||||||
|
.write("CHECKSUMS2", &format!("SM3 (input2) = {INVALID_SUM}\n"));
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS2")
|
||||||
|
.fails()
|
||||||
|
.stdout_contains("input2: FAILED open or read")
|
||||||
|
.stderr_contains("1 listed file could not be read");
|
||||||
|
|
||||||
|
// also fails with strict
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--strict")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS2")
|
||||||
|
.fails()
|
||||||
|
.stdout_contains("input2: FAILED open or read")
|
||||||
|
.stderr_contains("1 listed file could not be read");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_scene_with_another_improperly_formatted() -> TestScenario {
|
||||||
|
let scene = make_scene_with_incorrect_checksum();
|
||||||
|
|
||||||
|
scene.fixtures.append(
|
||||||
|
"CHECKSUMS",
|
||||||
|
&format!("BLAKE2b (missing-file) = {INVALID_SUM}\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
scene
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_warn() {
|
||||||
|
let scene = make_scene_with_another_improperly_formatted();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--warn")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.run()
|
||||||
|
.stderr_contains("CHECKSUMS: 6: improperly formatted SM3 checksum line")
|
||||||
|
.stderr_contains("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_scene_with_checksum_missing() -> TestScenario {
|
||||||
|
let scene = make_scene_with_another_improperly_formatted();
|
||||||
|
|
||||||
|
scene.fixtures.write(
|
||||||
|
"CHECKSUMS-missing",
|
||||||
|
&format!("SM3 (nonexistent) = {INVALID_SUM}\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
scene
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ignore_missing() {
|
||||||
|
let scene = make_scene_with_checksum_missing();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--ignore-missing")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS-missing")
|
||||||
|
.fails()
|
||||||
|
.stdout_does_not_contain("nonexistent: No such file or directory")
|
||||||
|
.stdout_does_not_contain("nonexistent: FAILED open or read")
|
||||||
|
.stderr_contains("CHECKSUMS-missing: no file was verified");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_and_warn() {
|
||||||
|
let scene = make_scene_with_checksum_missing();
|
||||||
|
|
||||||
|
// --status before --warn
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--status")
|
||||||
|
.arg("--warn")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line")
|
||||||
|
.stderr_contains("WARNING: 3 lines are improperly formatted")
|
||||||
|
.stderr_contains("WARNING: 1 computed checksum did NOT match");
|
||||||
|
|
||||||
|
// --warn before --status (status hides the results)
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--warn")
|
||||||
|
.arg("--status")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails()
|
||||||
|
.stderr_does_not_contain("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line")
|
||||||
|
.stderr_does_not_contain("WARNING: 3 lines are improperly formatted")
|
||||||
|
.stderr_does_not_contain("WARNING: 1 computed checksum did NOT match");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_and_ignore_missing() {
|
||||||
|
let scene = make_scene_with_checksum_missing();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--status")
|
||||||
|
.arg("--ignore-missing")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS")
|
||||||
|
.fails()
|
||||||
|
.no_output();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_warn_and_ignore_missing() {
|
||||||
|
let scene = make_scene_with_checksum_missing();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--status")
|
||||||
|
.arg("--warn")
|
||||||
|
.arg("--ignore-missing")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUMS-missing")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("CHECKSUMS-missing: no file was verified")
|
||||||
|
.stdout_does_not_contain("nonexistent: No such file or directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_several_files_dont_exist() {
|
||||||
|
let scene = make_scene();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("non-existing-1")
|
||||||
|
.arg("non-existing-2")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("non-existing-1: No such file or directory")
|
||||||
|
.stderr_contains("non-existing-2: No such file or directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_several_files_empty() {
|
||||||
|
let scene = make_scene();
|
||||||
|
scene.fixtures.touch("empty-1");
|
||||||
|
scene.fixtures.touch("empty-2");
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("empty-1")
|
||||||
|
.arg("empty-2")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("empty-1: no properly formatted checksum lines found")
|
||||||
|
.stderr_contains("empty-2: no properly formatted checksum lines found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The tests in this module check the behavior of cksum when given different
|
/// The tests in this module check the behavior of cksum when given different
|
||||||
/// checksum formats and algorithms in the same file, while specifying an
|
/// checksum formats and algorithms in the same file, while specifying an
|
||||||
/// algorithm on CLI or not.
|
/// algorithm on CLI or not.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue