introduce checksum_common

This commit is contained in:
Dorian Peron
2026-01-06 20:07:40 +01:00
parent 53700fccdd
commit 320c80b196
9 changed files with 518 additions and 0 deletions
Generated
+11
View File
@@ -3239,6 +3239,17 @@ dependencies = [
"uucore",
]
[[package]]
name = "uu_checksum_common"
version = "0.6.0"
dependencies = [
"clap",
"codspeed-divan-compat",
"fluent",
"tempfile",
"uucore",
]
[[package]]
name = "uu_chgrp"
version = "0.6.0"
+1
View File
@@ -407,6 +407,7 @@ uucore = { version = "0.6.0", package = "uucore", path = "src/uucore" }
uucore_procs = { version = "0.6.0", package = "uucore_procs", path = "src/uucore_procs" }
uu_ls = { version = "0.6.0", path = "src/uu/ls" }
uu_base32 = { version = "0.6.0", path = "src/uu/base32" }
uu_checksum_common = { version = "0.6.0", path = "src/uu/checksum_common" }
uutests = { version = "0.6.0", package = "uutests", path = "tests/uutests" }
[dependencies]
+10
View File
@@ -1692,12 +1692,22 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uu_checksum_common"
version = "0.6.0"
dependencies = [
"clap",
"fluent",
"uucore",
]
[[package]]
name = "uu_cksum"
version = "0.6.0"
dependencies = [
"clap",
"fluent",
"uu_checksum_common",
"uucore",
]
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "uu_checksum_common"
description = "Base for checksum utils"
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/lib.rs"
[dependencies]
clap = { workspace = true }
uucore = { workspace = true, features = [
"checksum",
"encoding",
"sum",
"hardware",
] }
fluent = { workspace = true }
[dev-dependencies]
divan = { workspace = true }
tempfile = { workspace = true }
# [[bench]]
# name = "b2sum_bench"
# harness = false
+1
View File
@@ -0,0 +1 @@
../../../LICENSE
+19
View File
@@ -0,0 +1,19 @@
ck-common-after-help = With no FILE or when FILE is -, read standard input
# checksum argument help messages
ck-common-help-algorithm = select the digest type to use. See DIGEST below
ck-common-help-untagged = create a reversed style checksum, without digest type
ck-common-help-tag-default = create a BSD style checksum (default)
ck-common-help-tag = create a BSD style checksum
ck-common-help-text = read in text mode (default)
ck-common-help-length = digest length in bits; must not exceed the max size and must be a multiple of 8 for blake2b; must be 224, 256, 384, or 512 for sha2 or sha3
ck-common-help-check = read checksums from the FILEs and check them
ck-common-help-base64 = emit base64-encoded digests, not hexadecimal
ck-common-help-raw = emit a raw binary digest, not hexadecimal
ck-common-help-zero = end each output line with NUL, not newline, and disable file name escaping
ck-common-help-strict = exit non-zero for improperly formatted checksum lines
ck-common-help-warn = warn about improperly formatted checksum lines
ck-common-help-status = don't output anything, status code shows success
ck-common-help-quiet = don't print OK for each successfully verified file
ck-common-help-ignore-missing = don't fail or report status for missing files
ck-common-help-debug = print CPU hardware capability detection info used by cksum
+19
View File
@@ -0,0 +1,19 @@
ck-common-after-help = Sans FICHIER ou quand FICHER est -, lit l'entrée standard
# Messages d'aide d'arguments checksum
ck-common-help-algorithm = sélectionner le type de condensé à utiliser. Voir DIGEST ci-dessous
ck-common-help-untagged = créer une somme de contrôle de style inversé, sans type de condensé
ck-common-help-tag-default = créer une somme de contrôle de style BSD (par défaut)
ck-common-help-tag = créer une somme de contrôle de style BSD
ck-common-help-text = lire en mode texte (par défaut)
ck-common-help-length = longueur du condensé en bits ; ne doit pas dépasser le maximum pour l'algorithme blake2 et doit être un multiple de 8
ck-common-help-raw = émettre un condensé binaire brut, pas hexadécimal
ck-common-help-strict = sortir avec un code non-zéro pour les lignes de somme de contrôle mal formatées
ck-common-help-check = lire les sommes de hachage des FICHIERs et les vérifier
ck-common-help-base64 = émettre un condensé base64, pas hexadécimal
ck-common-help-warn = avertir des lignes de somme de contrôle mal formatées
ck-common-help-status = ne rien afficher, le code de statut indique le succès
ck-common-help-quiet = ne pas afficher OK pour chaque fichier vérifié avec succès
ck-common-help-ignore-missing = ne pas échouer ou signaler le statut pour les fichiers manquants
ck-common-help-zero = terminer chaque ligne de sortie avec NUL, pas un saut de ligne, et désactiver l'échappement des noms de fichiers
ck-common-help-debug = afficher les informations de débogage sur la détection de la prise en charge matérielle du processeur
+215
View File
@@ -0,0 +1,215 @@
// 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 clap::{Arg, ArgAction, Command};
use uucore::{checksum::SUPPORTED_ALGORITHMS, translate};
/// List of all options that can be encountered in checksum utils
pub mod options {
// cksum-specific
pub const ALGORITHM: &str = "algorithm";
pub const DEBUG: &str = "debug";
// positional arg
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";
pub const CHECK: &str = "check";
pub const TEXT: &str = "text";
pub const BINARY: &str = "binary";
pub const ZERO: &str = "zero";
// check-specific
pub const STRICT: &str = "strict";
pub const STATUS: &str = "status";
pub const WARN: &str = "warn";
pub const IGNORE_MISSING: &str = "ignore-missing";
pub const QUIET: &str = "quiet";
}
/// `ChecksumCommand` is a convenience trait to more easily declare checksum
/// CLI interfaces with
pub trait ChecksumCommand {
fn with_algo(self) -> Self;
fn with_length(self) -> Self;
fn with_check_and_opts(self) -> Self;
fn with_binary(self) -> Self;
fn with_text(self, is_default: bool) -> Self;
fn with_tag(self, is_default: bool) -> Self;
fn with_untagged(self) -> Self;
fn with_raw(self) -> Self;
fn with_base64(self) -> Self;
fn with_zero(self) -> Self;
fn with_debug(self) -> Self;
}
impl ChecksumCommand for Command {
fn with_algo(self) -> Self {
self.arg(
Arg::new(options::ALGORITHM)
.long(options::ALGORITHM)
.short('a')
.help(translate!("ck-common-help-algorithm"))
.value_name("ALGORITHM")
.value_parser(SUPPORTED_ALGORITHMS),
)
}
fn with_length(self) -> Self {
self.arg(
Arg::new(options::LENGTH)
.long(options::LENGTH)
.short('l')
.help(translate!("ck-common-help-length"))
.action(ArgAction::Set),
)
}
fn with_check_and_opts(self) -> Self {
self.arg(
Arg::new(options::CHECK)
.short('c')
.long(options::CHECK)
.help(translate!("ck-common-help-check"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::WARN)
.short('w')
.long("warn")
.help(translate!("ck-common-help-warn"))
.action(ArgAction::SetTrue)
.overrides_with_all([options::STATUS, options::QUIET]),
)
.arg(
Arg::new(options::STATUS)
.long("status")
.help(translate!("ck-common-help-status"))
.action(ArgAction::SetTrue)
.overrides_with_all([options::WARN, options::QUIET]),
)
.arg(
Arg::new(options::QUIET)
.long(options::QUIET)
.help(translate!("ck-common-help-quiet"))
.action(ArgAction::SetTrue)
.overrides_with_all([options::STATUS, options::WARN]),
)
.arg(
Arg::new(options::IGNORE_MISSING)
.long(options::IGNORE_MISSING)
.help(translate!("ck-common-help-ignore-missing"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::STRICT)
.long(options::STRICT)
.help(translate!("ck-common-help-strict"))
.action(ArgAction::SetTrue),
)
}
fn with_binary(self) -> Self {
self.arg(
Arg::new(options::BINARY)
.long(options::BINARY)
.short('b')
.hide(true)
.action(ArgAction::SetTrue),
)
}
fn with_text(self, is_default: bool) -> Self {
let mut arg = Arg::new(options::TEXT)
.long(options::TEXT)
.short('t')
.action(ArgAction::SetTrue);
arg = if is_default {
arg.help(translate!("ck-common-help-text"))
} else {
arg.hide(true)
};
self.arg(arg)
}
fn with_tag(self, default: bool) -> Self {
let mut arg = Arg::new(options::TAG)
.long(options::TAG)
.action(ArgAction::SetTrue);
arg = if default {
arg.help(translate!("ck-common-help-tag-default"))
} else {
arg.help(translate!("ck-common-help-tag"))
};
self.arg(arg)
}
fn with_untagged(self) -> Self {
self.arg(
Arg::new(options::UNTAGGED)
.long(options::UNTAGGED)
.help(translate!("ck-common-help-untagged"))
.action(ArgAction::SetTrue),
)
}
fn with_raw(self) -> Self {
self.arg(
Arg::new(options::RAW)
.long(options::RAW)
.help(translate!("ck-common-help-raw"))
.action(ArgAction::SetTrue),
)
}
fn with_base64(self) -> Self {
self.arg(
Arg::new(options::BASE64)
.long(options::BASE64)
.help(translate!("ck-common-help-base64"))
.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),
)
}
fn with_zero(self) -> Self {
self.arg(
Arg::new(options::ZERO)
.long(options::ZERO)
.short('z')
.help(translate!("ck-common-help-zero"))
.action(ArgAction::SetTrue),
)
}
fn with_debug(self) -> Self {
self.arg(
Arg::new(options::DEBUG)
.long(options::DEBUG)
.help(translate!("ck-common-help-debug"))
.action(ArgAction::SetTrue),
)
}
}
+207
View File
@@ -0,0 +1,207 @@
// 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.
// spell-checker:ignore (ToDO) algo
use std::ffi::OsString;
use clap::builder::ValueParser;
use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint};
use uucore::checksum::compute::{
ChecksumComputeOptions, OutputFormat, perform_checksum_computation,
};
use uucore::checksum::validate::{self, ChecksumValidateOptions, ChecksumVerbose};
use uucore::checksum::{AlgoKind, ChecksumError, SizedAlgoKind};
use uucore::error::UResult;
use uucore::line_ending::LineEnding;
use uucore::{crate_version, format_usage, localized_help_template, util_name};
mod cli;
pub use cli::ChecksumCommand;
pub use cli::options;
/// Expands to generate the right `uumain` and `uu_app` functions
/// for standalone checksum binaries.
///
/// Example:
/// ```
/// use uu_checksum_common::declare_standalone;
/// use uucore::checksum::AlgoKind;
///
/// declare_standalone!("sha512sum", AlgoKind::Sha512);
/// ```
#[macro_export]
macro_rules! declare_standalone {
($bin:literal, $kind:expr) => {
#[::uucore::main]
pub fn uumain(args: impl ::uucore::Args) -> ::uucore::error::UResult<()> {
::uu_checksum_common::standalone_main($kind, uu_app(), args)
}
#[inline]
pub fn uu_app() -> ::clap::Command {
::uu_checksum_common::standalone_checksum_app(
::uucore::translate!(concat!($bin, "-about")),
::uucore::translate!(concat!($bin, "-usage")),
)
}
};
}
/// Entrypoint for standalone checksums accepting the `--length` argument
///
/// Note: Ideally, we wouldn't require a `cmd` to be passed to the function,
/// but for localization purposes, the standalone binaries must declare their
/// command (with about and usage) themselves, otherwise calling --help from
/// the multicall binary results in an unformatted output.
pub fn standalone_with_length_main(
algo: AlgoKind,
cmd: Command,
args: impl uucore::Args,
validate_len: fn(&str) -> UResult<Option<usize>>,
) -> UResult<()> {
let matches = uucore::clap_localization::handle_clap_result(cmd, args)?;
let algo = Some(algo);
let length = matches
.get_one::<String>(options::LENGTH)
.map(String::as_str)
.map(validate_len)
.transpose()?
.flatten();
let format = OutputFormat::from_standalone(std::env::args_os());
checksum_main(algo, length, matches, format?)
}
/// Entrypoint for standalone checksums *NOT* accepting the `--length` argument
pub fn standalone_main(algo: AlgoKind, cmd: Command, args: impl uucore::Args) -> UResult<()> {
let matches = uucore::clap_localization::handle_clap_result(cmd, args)?;
let algo = Some(algo);
let format = OutputFormat::from_standalone(std::env::args_os());
checksum_main(algo, None, matches, format?)
}
/// Base command processing for all the checksum executables.
pub fn default_checksum_app(about: String, usage: String) -> Command {
Command::new(util_name())
.version(crate_version!())
.help_template(localized_help_template(util_name()))
.about(about)
.override_usage(format_usage(&usage))
.infer_long_args(true)
.args_override_self(true)
.arg(
Arg::new(options::FILE)
.hide(true)
.action(ArgAction::Append)
.value_parser(ValueParser::os_string())
.default_value("-")
.hide_default_value(true)
.value_hint(ValueHint::FilePath),
)
}
/// Command processing for standalone checksums accepting the `--length`
/// argument
pub fn standalone_checksum_app_with_length(about: String, usage: String) -> Command {
default_checksum_app(about, usage)
.with_binary()
.with_check_and_opts()
.with_length()
.with_tag(false)
.with_text(true)
.with_zero()
}
/// Command processing for standalone checksums *NOT* accepting the `--length`
/// argument
pub fn standalone_checksum_app(about: String, usage: String) -> Command {
default_checksum_app(about, usage)
.with_binary()
.with_check_and_opts()
.with_tag(false)
.with_text(true)
.with_zero()
}
/// This is the common entrypoint to all checksum utils. Performs some
/// validation on arguments and proceeds in computing or checking mode.
pub fn checksum_main(
algo: Option<AlgoKind>,
length: Option<usize>,
matches: ArgMatches,
output_format: OutputFormat,
) -> UResult<()> {
let check = matches.get_flag("check");
let check_flag = |flag| match (check, matches.get_flag(flag)) {
(_, false) => Ok(false),
(true, true) => Ok(true),
(false, true) => Err(ChecksumError::CheckOnlyFlag(flag.into())),
};
// Each of the following flags are only expected in --check mode.
// If we encounter them otherwise, end with an error.
let ignore_missing = check_flag("ignore-missing")?;
let warn = check_flag("warn")?;
let quiet = check_flag("quiet")?;
let strict = check_flag("strict")?;
let status = check_flag("status")?;
// clap provides the default value -. So we unwrap() safety.
let files = matches
.get_many::<OsString>(options::FILE)
.unwrap()
.map(|s| s.as_os_str());
if check {
// cksum does not support '--check'ing legacy algorithms
if algo.is_some_and(AlgoKind::is_legacy) {
return Err(ChecksumError::AlgorithmNotSupportedWithCheck.into());
}
let text_flag = matches.get_flag(options::TEXT);
let binary_flag = matches.get_flag(options::BINARY);
let tag = matches.get_flag(options::TAG);
if tag || binary_flag || text_flag {
return Err(ChecksumError::BinaryTextConflict.into());
}
// Execute the checksum validation based on the presence of files or the use of stdin
let verbose = ChecksumVerbose::new(status, quiet, warn);
let opts = ChecksumValidateOptions {
ignore_missing,
strict,
verbose,
};
return validate::perform_checksum_validation(files, algo, length, opts);
}
// Not --check
// Set the default algorithm to CRC when not '--check'ing.
let algo_kind = algo.unwrap_or(AlgoKind::Crc);
let algo = SizedAlgoKind::from_unsized(algo_kind, length)?;
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO));
let opts = ChecksumComputeOptions {
algo_kind: algo,
output_format,
line_ending,
};
perform_checksum_computation(opts, files)?;
Ok(())
}