mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 04:27:45 +00:00
hashsum: switch from getopts to clap (#1542)
* hashsum: switch from getopts to clap Additionally, slightly refactor. This commit will be the first of a series of commits refactoring (at the very least) `hashsum`.
This commit is contained in:
parent
f17a112781
commit
e02b8a60f7
3 changed files with 241 additions and 211 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1357,8 +1357,8 @@ dependencies = [
|
||||||
name = "uu_hashsum"
|
name = "uu_hashsum"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -16,7 +16,7 @@ path = "src/hashsum.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
digest = "0.6.2"
|
digest = "0.6.2"
|
||||||
getopts = "0.2.18"
|
clap = "2"
|
||||||
hex = "0.2.0"
|
hex = "0.2.0"
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
md5 = "0.3.5"
|
md5 = "0.3.5"
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) algo, algoname, regexes, nread
|
// spell-checker:ignore (ToDO) algo, algoname, regexes, nread
|
||||||
|
|
||||||
extern crate getopts;
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
extern crate hex;
|
extern crate hex;
|
||||||
extern crate md5;
|
extern crate md5;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
@ -25,6 +26,7 @@ mod digest;
|
||||||
|
|
||||||
use self::digest::Digest;
|
use self::digest::Digest;
|
||||||
|
|
||||||
|
use clap::{App, Arg, ArgMatches};
|
||||||
use hex::ToHex;
|
use hex::ToHex;
|
||||||
use md5::Context as Md5;
|
use md5::Context as Md5;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
@ -32,12 +34,27 @@ use sha1::Sha1;
|
||||||
use sha2::{Sha224, Sha256, Sha384, Sha512};
|
use sha2::{Sha224, Sha256, Sha384, Sha512};
|
||||||
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
|
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, stdin, BufRead, BufReader, Read};
|
use std::io::{self, stdin, BufRead, BufReader, Read};
|
||||||
|
use std::iter;
|
||||||
|
use std::num::ParseIntError;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
static NAME: &str = "hashsum";
|
const NAME: &str = "hashsum";
|
||||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
||||||
|
struct Options {
|
||||||
|
algoname: &'static str,
|
||||||
|
digest: Box<dyn Digest + 'static>,
|
||||||
|
binary: bool,
|
||||||
|
check: bool,
|
||||||
|
tag: bool,
|
||||||
|
status: bool,
|
||||||
|
quiet: bool,
|
||||||
|
strict: bool,
|
||||||
|
warn: bool,
|
||||||
|
output_bits: usize,
|
||||||
|
}
|
||||||
|
|
||||||
fn is_custom_binary(program: &str) -> bool {
|
fn is_custom_binary(program: &str) -> bool {
|
||||||
match program {
|
match program {
|
||||||
|
@ -49,9 +66,9 @@ fn is_custom_binary(program: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn detect_algo(
|
fn detect_algo<'a>(
|
||||||
program: &str,
|
program: &str,
|
||||||
matches: &getopts::Matches,
|
matches: &ArgMatches<'a>,
|
||||||
) -> (&'static str, Box<dyn Digest + 'static>, usize) {
|
) -> (&'static str, Box<dyn Digest + 'static>, usize) {
|
||||||
let mut alg: Option<Box<dyn Digest>> = None;
|
let mut alg: Option<Box<dyn Digest>> = None;
|
||||||
let mut name: &'static str = "";
|
let mut name: &'static str = "";
|
||||||
|
@ -63,7 +80,7 @@ fn detect_algo(
|
||||||
"sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box<dyn Digest>, 256),
|
"sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box<dyn Digest>, 256),
|
||||||
"sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<dyn Digest>, 384),
|
"sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<dyn Digest>, 384),
|
||||||
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
|
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
|
||||||
"sha3sum" => match matches.opt_str("bits") {
|
"sha3sum" => match matches.value_of("bits") {
|
||||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||||
Ok(224) => (
|
Ok(224) => (
|
||||||
"SHA3-224",
|
"SHA3-224",
|
||||||
|
@ -113,7 +130,7 @@ fn detect_algo(
|
||||||
Box::new(Sha3_512::new()) as Box<dyn Digest>,
|
Box::new(Sha3_512::new()) as Box<dyn Digest>,
|
||||||
512,
|
512,
|
||||||
),
|
),
|
||||||
"shake128sum" => match matches.opt_str("bits") {
|
"shake128sum" => match matches.value_of("bits") {
|
||||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||||
Ok(bits) => (
|
Ok(bits) => (
|
||||||
"SHAKE128",
|
"SHAKE128",
|
||||||
|
@ -124,7 +141,7 @@ fn detect_algo(
|
||||||
},
|
},
|
||||||
None => crash!(1, "--bits required for SHAKE-128"),
|
None => crash!(1, "--bits required for SHAKE-128"),
|
||||||
},
|
},
|
||||||
"shake256sum" => match matches.opt_str("bits") {
|
"shake256sum" => match matches.value_of("bits") {
|
||||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||||
Ok(bits) => (
|
Ok(bits) => (
|
||||||
"SHAKE256",
|
"SHAKE256",
|
||||||
|
@ -145,26 +162,26 @@ fn detect_algo(
|
||||||
alg = Some(val);
|
alg = Some(val);
|
||||||
output_bits = bits
|
output_bits = bits
|
||||||
};
|
};
|
||||||
if matches.opt_present("md5") {
|
if matches.is_present("md5") {
|
||||||
set_or_crash("MD5", Box::new(Md5::new()), 128)
|
set_or_crash("MD5", Box::new(Md5::new()), 128)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha1") {
|
if matches.is_present("sha1") {
|
||||||
set_or_crash("SHA1", Box::new(Sha1::new()), 160)
|
set_or_crash("SHA1", Box::new(Sha1::new()), 160)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha224") {
|
if matches.is_present("sha224") {
|
||||||
set_or_crash("SHA224", Box::new(Sha224::new()), 224)
|
set_or_crash("SHA224", Box::new(Sha224::new()), 224)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha256") {
|
if matches.is_present("sha256") {
|
||||||
set_or_crash("SHA256", Box::new(Sha256::new()), 256)
|
set_or_crash("SHA256", Box::new(Sha256::new()), 256)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha384") {
|
if matches.is_present("sha384") {
|
||||||
set_or_crash("SHA384", Box::new(Sha384::new()), 384)
|
set_or_crash("SHA384", Box::new(Sha384::new()), 384)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha512") {
|
if matches.is_present("sha512") {
|
||||||
set_or_crash("SHA512", Box::new(Sha512::new()), 512)
|
set_or_crash("SHA512", Box::new(Sha512::new()), 512)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha3") {
|
if matches.is_present("sha3") {
|
||||||
match matches.opt_str("bits") {
|
match matches.value_of("bits") {
|
||||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||||
Ok(224) => set_or_crash(
|
Ok(224) => set_or_crash(
|
||||||
"SHA3-224",
|
"SHA3-224",
|
||||||
|
@ -195,20 +212,20 @@ fn detect_algo(
|
||||||
None => crash!(1, "--bits required for SHA3"),
|
None => crash!(1, "--bits required for SHA3"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha3-224") {
|
if matches.is_present("sha3-224") {
|
||||||
set_or_crash("SHA3-224", Box::new(Sha3_224::new()), 224)
|
set_or_crash("SHA3-224", Box::new(Sha3_224::new()), 224)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha3-256") {
|
if matches.is_present("sha3-256") {
|
||||||
set_or_crash("SHA3-256", Box::new(Sha3_256::new()), 256)
|
set_or_crash("SHA3-256", Box::new(Sha3_256::new()), 256)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha3-384") {
|
if matches.is_present("sha3-384") {
|
||||||
set_or_crash("SHA3-384", Box::new(Sha3_384::new()), 384)
|
set_or_crash("SHA3-384", Box::new(Sha3_384::new()), 384)
|
||||||
}
|
}
|
||||||
if matches.opt_present("sha3-512") {
|
if matches.is_present("sha3-512") {
|
||||||
set_or_crash("SHA3-512", Box::new(Sha3_512::new()), 512)
|
set_or_crash("SHA3-512", Box::new(Sha3_512::new()), 512)
|
||||||
}
|
}
|
||||||
if matches.opt_present("shake128") {
|
if matches.is_present("shake128") {
|
||||||
match matches.opt_str("bits") {
|
match matches.value_of("bits") {
|
||||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||||
Ok(bits) => set_or_crash("SHAKE128", Box::new(Shake128::new()), bits),
|
Ok(bits) => set_or_crash("SHAKE128", Box::new(Shake128::new()), bits),
|
||||||
Err(err) => crash!(1, "{}", err),
|
Err(err) => crash!(1, "{}", err),
|
||||||
|
@ -216,8 +233,8 @@ fn detect_algo(
|
||||||
None => crash!(1, "--bits required for SHAKE-128"),
|
None => crash!(1, "--bits required for SHAKE-128"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if matches.opt_present("shake256") {
|
if matches.is_present("shake256") {
|
||||||
match matches.opt_str("bits") {
|
match matches.value_of("bits") {
|
||||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||||
Ok(bits) => set_or_crash("SHAKE256", Box::new(Shake256::new()), bits),
|
Ok(bits) => set_or_crash("SHAKE256", Box::new(Shake256::new()), bits),
|
||||||
Err(err) => crash!(1, "{}", err),
|
Err(err) => crash!(1, "{}", err),
|
||||||
|
@ -234,211 +251,219 @@ fn detect_algo(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
// TODO: return custom error type
|
||||||
let args = args.collect_str();
|
fn parse_bit_num(arg: &str) -> Result<usize, ParseIntError> {
|
||||||
|
usize::from_str_radix(arg, 10)
|
||||||
|
}
|
||||||
|
|
||||||
let program = &args[0];
|
fn is_valid_bit_num(arg: String) -> Result<(), String> {
|
||||||
let binary_name = Path::new(program).file_name().unwrap().to_str().unwrap();
|
parse_bit_num(&arg)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|e| format!("{}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uumain(mut args: impl uucore::Args) -> i32 {
|
||||||
|
// if there is no program name for some reason, default to "hashsum"
|
||||||
|
let program = args.next().unwrap_or_else(|| OsString::from(NAME));
|
||||||
|
let binary_name = Path::new(&program)
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or_else(|| OsStr::new(NAME))
|
||||||
|
.to_string_lossy();
|
||||||
|
|
||||||
|
let args = iter::once(program.clone()).chain(args);
|
||||||
|
|
||||||
// Default binary in Windows, text mode otherwise
|
// Default binary in Windows, text mode otherwise
|
||||||
let binary_flag_default = cfg!(windows);
|
let binary_flag_default = cfg!(windows);
|
||||||
|
|
||||||
let mut opts = getopts::Options::new();
|
let binary_help = format!(
|
||||||
opts.optflag(
|
"read in binary mode{}",
|
||||||
"b",
|
if binary_flag_default {
|
||||||
"binary",
|
" (default)"
|
||||||
&format!(
|
|
||||||
"read in binary mode{}",
|
|
||||||
if binary_flag_default {
|
|
||||||
" (default)"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
),
|
|
||||||
);
|
|
||||||
opts.optflag("c", "check", "read hashsums from the FILEs and check them");
|
|
||||||
opts.optflag("", "tag", "create a BSD-style checksum");
|
|
||||||
opts.optflag(
|
|
||||||
"t",
|
|
||||||
"text",
|
|
||||||
&format!(
|
|
||||||
"read in text mode{}",
|
|
||||||
if binary_flag_default {
|
|
||||||
""
|
|
||||||
} else {
|
|
||||||
" (default)"
|
|
||||||
}
|
|
||||||
),
|
|
||||||
);
|
|
||||||
opts.optflag(
|
|
||||||
"q",
|
|
||||||
"quiet",
|
|
||||||
"don't print OK for each successfully verified file",
|
|
||||||
);
|
|
||||||
opts.optflag(
|
|
||||||
"s",
|
|
||||||
"status",
|
|
||||||
"don't output anything, status code shows success",
|
|
||||||
);
|
|
||||||
opts.optflag(
|
|
||||||
"",
|
|
||||||
"strict",
|
|
||||||
"exit non-zero for improperly formatted checksum lines",
|
|
||||||
);
|
|
||||||
opts.optflag(
|
|
||||||
"w",
|
|
||||||
"warn",
|
|
||||||
"warn about improperly formatted checksum lines",
|
|
||||||
);
|
|
||||||
opts.optflag("h", "help", "display this help and exit");
|
|
||||||
opts.optflag("V", "version", "output version information and exit");
|
|
||||||
|
|
||||||
if !is_custom_binary(program) {
|
|
||||||
opts.optflag("", "md5", "work with MD5");
|
|
||||||
opts.optflag("", "sha1", "work with SHA1");
|
|
||||||
opts.optflag("", "sha224", "work with SHA224");
|
|
||||||
opts.optflag("", "sha256", "work with SHA256");
|
|
||||||
opts.optflag("", "sha384", "work with SHA384");
|
|
||||||
opts.optflag("", "sha512", "work with SHA512");
|
|
||||||
opts.optflag("", "sha3", "work with SHA3");
|
|
||||||
opts.optflag("", "sha3-224", "work with SHA3-224");
|
|
||||||
opts.optflag("", "sha3-256", "work with SHA3-256");
|
|
||||||
opts.optflag("", "sha3-384", "work with SHA3-384");
|
|
||||||
opts.optflag("", "sha3-512", "work with SHA3-512");
|
|
||||||
opts.optflag(
|
|
||||||
"",
|
|
||||||
"shake128",
|
|
||||||
"work with SHAKE128 using BITS for the output size",
|
|
||||||
);
|
|
||||||
opts.optflag(
|
|
||||||
"",
|
|
||||||
"shake256",
|
|
||||||
"work with SHAKE256 using BITS for the output size",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needed for variable-length output sums (e.g. SHAKE)
|
|
||||||
opts.optopt(
|
|
||||||
"",
|
|
||||||
"bits",
|
|
||||||
"set the size of the output (only for SHAKE)",
|
|
||||||
"BITS",
|
|
||||||
);
|
|
||||||
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(f) => crash!(1, "{}", f),
|
|
||||||
};
|
|
||||||
|
|
||||||
if matches.opt_present("help") {
|
|
||||||
usage(program, binary_name, &opts);
|
|
||||||
} else if matches.opt_present("version") {
|
|
||||||
version();
|
|
||||||
} else {
|
|
||||||
let (name, algo, bits) = detect_algo(binary_name, &matches);
|
|
||||||
|
|
||||||
let binary_flag = matches.opt_present("binary");
|
|
||||||
let text_flag = matches.opt_present("text");
|
|
||||||
if binary_flag && text_flag {
|
|
||||||
crash!(1, "cannot set binary and text mode at the same time");
|
|
||||||
}
|
|
||||||
let binary = if binary_flag {
|
|
||||||
true
|
|
||||||
} else if text_flag {
|
|
||||||
false
|
|
||||||
} else {
|
} else {
|
||||||
binary_flag_default
|
""
|
||||||
};
|
|
||||||
let check = matches.opt_present("check");
|
|
||||||
let tag = matches.opt_present("tag");
|
|
||||||
let status = matches.opt_present("status");
|
|
||||||
let quiet = matches.opt_present("quiet") || status;
|
|
||||||
let strict = matches.opt_present("strict");
|
|
||||||
let warn = matches.opt_present("warn") && !status;
|
|
||||||
let files = if matches.free.is_empty() {
|
|
||||||
vec!["-".to_owned()]
|
|
||||||
} else {
|
|
||||||
matches.free
|
|
||||||
};
|
|
||||||
match hashsum(
|
|
||||||
name, algo, files, binary, check, tag, status, quiet, strict, warn, bits,
|
|
||||||
) {
|
|
||||||
Ok(()) => return 0,
|
|
||||||
Err(e) => return e,
|
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
|
|
||||||
0
|
let text_help = format!(
|
||||||
}
|
"read in text mode{}",
|
||||||
|
if binary_flag_default {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
" (default)"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
fn version() {
|
let mut app = App::new(executable!())
|
||||||
println!("{} {}", NAME, VERSION);
|
.version(crate_version!())
|
||||||
}
|
.about("Compute and check message digests.")
|
||||||
|
.arg(
|
||||||
fn usage(program: &str, binary_name: &str, opts: &getopts::Options) {
|
Arg::with_name("binary")
|
||||||
let spec = if is_custom_binary(binary_name) {
|
.short("b")
|
||||||
format!(" {} [OPTION]... [FILE]...", program)
|
.long("binary")
|
||||||
} else {
|
.help(&binary_help),
|
||||||
format!(
|
|
||||||
" {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512|\
|
|
||||||
--sha3|--sha3-224|--sha3-256|--sha3-384|--sha3-512|\
|
|
||||||
--shake128|--shake256}} [OPTION]... [FILE]...",
|
|
||||||
program
|
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("check")
|
||||||
|
.short("c")
|
||||||
|
.long("check")
|
||||||
|
.help("read hashsums from the FILEs and check them"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("tag")
|
||||||
|
.long("tag")
|
||||||
|
.help("create a BSD-style checksum"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("text")
|
||||||
|
.short("t")
|
||||||
|
.long("text")
|
||||||
|
.help(&text_help)
|
||||||
|
.conflicts_with("binary"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("quiet")
|
||||||
|
.short("q")
|
||||||
|
.long("quiet")
|
||||||
|
.help("don't print OK for each successfully verified file"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("status")
|
||||||
|
.short("s")
|
||||||
|
.long("status")
|
||||||
|
.help("don't output anything, status code shows success"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("strict")
|
||||||
|
.long("strict")
|
||||||
|
.help("exit non-zero for improperly formatted checksum lines"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("warn")
|
||||||
|
.short("w")
|
||||||
|
.long("warn")
|
||||||
|
.help("warn about improperly formatted checksum lines"),
|
||||||
|
)
|
||||||
|
// Needed for variable-length output sums (e.g. SHAKE)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("bits")
|
||||||
|
.long("bits")
|
||||||
|
.help("set the size of the output (only for SHAKE)")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("BITS")
|
||||||
|
// XXX: should we actually use validators? they're not particularly efficient
|
||||||
|
.validator(is_valid_bit_num),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("FILE")
|
||||||
|
.index(1)
|
||||||
|
.multiple(true)
|
||||||
|
.value_name("FILE"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if !is_custom_binary(&binary_name) {
|
||||||
|
let algos = &[
|
||||||
|
("md5", "work with MD5"),
|
||||||
|
("sha1", "work with SHA1"),
|
||||||
|
("sha224", "work with SHA224"),
|
||||||
|
("sha256", "work with SHA256"),
|
||||||
|
("sha384", "work with SHA384"),
|
||||||
|
("sha512", "work with SHA512"),
|
||||||
|
("sha3", "work with SHA3"),
|
||||||
|
("sha3-224", "work with SHA3-224"),
|
||||||
|
("sha3-256", "work with SHA3-256"),
|
||||||
|
("sha3-384", "work with SHA3-384"),
|
||||||
|
("sha3-512", "work with SHA3-512"),
|
||||||
|
(
|
||||||
|
"shake128",
|
||||||
|
"work with SHAKE128 using BITS for the output size",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"shake256",
|
||||||
|
"work with SHAKE256 using BITS for the output size",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (name, desc) in algos {
|
||||||
|
app = app.arg(Arg::with_name(name).long(name).help(desc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this should use get_matches_from_safe() and crash!(), but at the moment that just
|
||||||
|
// causes "error: " to be printed twice (once from crash!() and once from clap). With
|
||||||
|
// the current setup, the name of the utility is not printed, but I think this is at
|
||||||
|
// least somewhat better from a user's perspective.
|
||||||
|
let matches = app.get_matches_from(args);
|
||||||
|
|
||||||
|
let (name, algo, bits) = detect_algo(&binary_name, &matches);
|
||||||
|
|
||||||
|
let binary = if matches.is_present("binary") {
|
||||||
|
true
|
||||||
|
} else if matches.is_present("text") {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
binary_flag_default
|
||||||
|
};
|
||||||
|
let check = matches.is_present("check");
|
||||||
|
let tag = matches.is_present("tag");
|
||||||
|
let status = matches.is_present("status");
|
||||||
|
let quiet = matches.is_present("quiet") || status;
|
||||||
|
let strict = matches.is_present("strict");
|
||||||
|
let warn = matches.is_present("warn") && !status;
|
||||||
|
|
||||||
|
let opts = Options {
|
||||||
|
algoname: name,
|
||||||
|
digest: algo,
|
||||||
|
output_bits: bits,
|
||||||
|
binary,
|
||||||
|
check,
|
||||||
|
tag,
|
||||||
|
status,
|
||||||
|
quiet,
|
||||||
|
strict,
|
||||||
|
warn,
|
||||||
};
|
};
|
||||||
|
|
||||||
let msg = format!(
|
let res = match matches.values_of_os("FILE") {
|
||||||
"{} {}
|
Some(files) => hashsum(opts, files),
|
||||||
|
None => hashsum(opts, iter::once(OsStr::new("-"))),
|
||||||
|
};
|
||||||
|
|
||||||
Usage:
|
match res {
|
||||||
{}
|
Ok(()) => 0,
|
||||||
|
Err(e) => e,
|
||||||
Compute and check message digests.",
|
}
|
||||||
NAME, VERSION, spec
|
|
||||||
);
|
|
||||||
|
|
||||||
print!("{}", opts.usage(&msg));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
fn hashsum<'a, I>(mut options: Options, files: I) -> Result<(), i32>
|
||||||
fn hashsum(
|
where
|
||||||
algoname: &str,
|
I: Iterator<Item = &'a OsStr>,
|
||||||
mut digest: Box<dyn Digest>,
|
{
|
||||||
files: Vec<String>,
|
|
||||||
binary: bool,
|
|
||||||
check: bool,
|
|
||||||
tag: bool,
|
|
||||||
status: bool,
|
|
||||||
quiet: bool,
|
|
||||||
strict: bool,
|
|
||||||
warn: bool,
|
|
||||||
output_bits: usize,
|
|
||||||
) -> Result<(), i32> {
|
|
||||||
let mut bad_format = 0;
|
let mut bad_format = 0;
|
||||||
let mut failed = 0;
|
let mut failed = 0;
|
||||||
let binary_marker = if binary { "*" } else { " " };
|
let binary_marker = if options.binary { "*" } else { " " };
|
||||||
for filename in &files {
|
for filename in files {
|
||||||
let filename: &str = filename;
|
let filename = Path::new(filename);
|
||||||
|
|
||||||
let stdin_buf;
|
let stdin_buf;
|
||||||
let file_buf;
|
let file_buf;
|
||||||
let mut file = BufReader::new(if filename == "-" {
|
let mut file = BufReader::new(if filename == OsStr::new("-") {
|
||||||
stdin_buf = stdin();
|
stdin_buf = stdin();
|
||||||
Box::new(stdin_buf) as Box<dyn Read>
|
Box::new(stdin_buf) as Box<dyn Read>
|
||||||
} else {
|
} else {
|
||||||
file_buf = safe_unwrap!(File::open(filename));
|
file_buf = safe_unwrap!(File::open(filename));
|
||||||
Box::new(file_buf) as Box<dyn Read>
|
Box::new(file_buf) as Box<dyn Read>
|
||||||
});
|
});
|
||||||
if check {
|
if options.check {
|
||||||
// Set up Regexes for line validation and parsing
|
// Set up Regexes for line validation and parsing
|
||||||
let bytes = digest.output_bits() / 4;
|
let bytes = options.digest.output_bits() / 4;
|
||||||
let gnu_re = safe_unwrap!(Regex::new(&format!(
|
let gnu_re = safe_unwrap!(Regex::new(&format!(
|
||||||
r"^(?P<digest>[a-fA-F0-9]{{{}}}) (?P<binary>[ \*])(?P<fileName>.*)",
|
r"^(?P<digest>[a-fA-F0-9]{{{}}}) (?P<binary>[ \*])(?P<fileName>.*)",
|
||||||
bytes
|
bytes
|
||||||
)));
|
)));
|
||||||
let bsd_re = safe_unwrap!(Regex::new(&format!(
|
let bsd_re = safe_unwrap!(Regex::new(&format!(
|
||||||
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{{{digest_size}}})",
|
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{{{digest_size}}})",
|
||||||
algorithm = algoname,
|
algorithm = options.algoname,
|
||||||
digest_size = bytes
|
digest_size = bytes
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
@ -459,15 +484,15 @@ fn hashsum(
|
||||||
),
|
),
|
||||||
None => {
|
None => {
|
||||||
bad_format += 1;
|
bad_format += 1;
|
||||||
if strict {
|
if options.strict {
|
||||||
return Err(1);
|
return Err(1);
|
||||||
}
|
}
|
||||||
if warn {
|
if options.warn {
|
||||||
show_warning!(
|
show_warning!(
|
||||||
"{}: {}: improperly formatted {} checksum line",
|
"{}: {}: improperly formatted {} checksum line",
|
||||||
filename,
|
filename.display(),
|
||||||
i + 1,
|
i + 1,
|
||||||
algoname
|
options.algoname
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -477,33 +502,38 @@ fn hashsum(
|
||||||
let f = safe_unwrap!(File::open(ck_filename));
|
let f = safe_unwrap!(File::open(ck_filename));
|
||||||
let mut ckf = BufReader::new(Box::new(f) as Box<dyn Read>);
|
let mut ckf = BufReader::new(Box::new(f) as Box<dyn Read>);
|
||||||
let real_sum = safe_unwrap!(digest_reader(
|
let real_sum = safe_unwrap!(digest_reader(
|
||||||
&mut *digest,
|
&mut *options.digest,
|
||||||
&mut ckf,
|
&mut ckf,
|
||||||
binary_check,
|
binary_check,
|
||||||
output_bits
|
options.output_bits
|
||||||
))
|
))
|
||||||
.to_ascii_lowercase();
|
.to_ascii_lowercase();
|
||||||
if sum == real_sum {
|
if sum == real_sum {
|
||||||
if !quiet {
|
if !options.quiet {
|
||||||
println!("{}: OK", ck_filename);
|
println!("{}: OK", ck_filename);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !status {
|
if !options.status {
|
||||||
println!("{}: FAILED", ck_filename);
|
println!("{}: FAILED", ck_filename);
|
||||||
}
|
}
|
||||||
failed += 1;
|
failed += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let sum = safe_unwrap!(digest_reader(&mut *digest, &mut file, binary, output_bits));
|
let sum = safe_unwrap!(digest_reader(
|
||||||
if tag {
|
&mut *options.digest,
|
||||||
println!("{} ({}) = {}", algoname, filename, sum);
|
&mut file,
|
||||||
|
options.binary,
|
||||||
|
options.output_bits
|
||||||
|
));
|
||||||
|
if options.tag {
|
||||||
|
println!("{} ({}) = {}", options.algoname, filename.display(), sum);
|
||||||
} else {
|
} else {
|
||||||
println!("{} {}{}", sum, binary_marker, filename);
|
println!("{} {}{}", sum, binary_marker, filename.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !status {
|
if !options.status {
|
||||||
match bad_format.cmp(&1) {
|
match bad_format.cmp(&1) {
|
||||||
Ordering::Equal => show_warning!("{} line is improperly formatted", bad_format),
|
Ordering::Equal => show_warning!("{} line is improperly formatted", bad_format),
|
||||||
Ordering::Greater => show_warning!("{} lines are improperly formatted", bad_format),
|
Ordering::Greater => show_warning!("{} lines are improperly formatted", bad_format),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue