mirror of
https://github.com/uutils/coreutils
synced 2024-12-15 07:42:48 +00:00
Merge pull request #297 from torkve/master
hashsum and its (md5sum,sha1sum,...)
This commit is contained in:
commit
c663a3571e
2 changed files with 126 additions and 33 deletions
20
Makefile
20
Makefile
|
@ -21,7 +21,7 @@ PROGS := \
|
||||||
false \
|
false \
|
||||||
fmt \
|
fmt \
|
||||||
fold \
|
fold \
|
||||||
md5sum \
|
hashsum \
|
||||||
mkdir \
|
mkdir \
|
||||||
nl \
|
nl \
|
||||||
paste \
|
paste \
|
||||||
|
@ -63,6 +63,14 @@ ifneq ($(OS),Windows_NT)
|
||||||
PROGS := $(PROGS) $(UNIX_PROGS)
|
PROGS := $(PROGS) $(UNIX_PROGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ALIASES := \
|
||||||
|
hashsum:md5sum \
|
||||||
|
hashsum:sha1sum \
|
||||||
|
hashsum:sha224sum \
|
||||||
|
hashsum:sha256sum \
|
||||||
|
hashsum:sha384sum \
|
||||||
|
hashsum:sha512sum
|
||||||
|
|
||||||
BUILD ?= $(PROGS)
|
BUILD ?= $(PROGS)
|
||||||
|
|
||||||
# Output names
|
# Output names
|
||||||
|
@ -111,6 +119,15 @@ build/$(2): $(1)/$(1).rs | build deps
|
||||||
$(call command,$(RUSTC) $(RUSTCFLAGS) -L build/ --crate-type rlib --dep-info build/$(1).d $(1)/$(1).rs --out-dir build)
|
$(call command,$(RUSTC) $(RUSTCFLAGS) -L build/ --crate-type rlib --dep-info build/$(1).d $(1)/$(1).rs --out-dir build)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
# Aliases build rule
|
||||||
|
ALIAS_SOURCE = $(firstword $(subst :, ,$(1)))
|
||||||
|
ALIAS_TARGET = $(word 2,$(subst :, ,$(1)))
|
||||||
|
define MAKE_ALIAS
|
||||||
|
all: build/$(call ALIAS_TARGET,$(1))
|
||||||
|
build/$(call ALIAS_TARGET,$(1)): build/$(call ALIAS_SOURCE,$(1))
|
||||||
|
$(call command,install build/$(call ALIAS_SOURCE,$(1)) build/$(call ALIAS_TARGET,$(1)))
|
||||||
|
endef
|
||||||
|
|
||||||
# Test exe built rules
|
# Test exe built rules
|
||||||
define TEST_BUILD
|
define TEST_BUILD
|
||||||
test_$(1): tmp/$(1)_test build/$(1)
|
test_$(1): tmp/$(1)_test build/$(1)
|
||||||
|
@ -158,6 +175,7 @@ ifeq ($(MULTICALL), 1)
|
||||||
$(foreach crate,$(CRATES),$(eval $(call CRATE_BUILD,$(crate),$(shell $(RUSTC) --crate-type rlib --crate-file-name --out-dir build $(crate)/$(crate).rs))))
|
$(foreach crate,$(CRATES),$(eval $(call CRATE_BUILD,$(crate),$(shell $(RUSTC) --crate-type rlib --crate-file-name --out-dir build $(crate)/$(crate).rs))))
|
||||||
else
|
else
|
||||||
$(foreach exe,$(EXES),$(eval $(call EXE_BUILD,$(exe))))
|
$(foreach exe,$(EXES),$(eval $(call EXE_BUILD,$(exe))))
|
||||||
|
$(foreach alias,$(ALIASES),$(eval $(call MAKE_ALIAS,$(alias))))
|
||||||
endif
|
endif
|
||||||
$(foreach test,$(TESTS),$(eval $(call TEST_BUILD,$(test))))
|
$(foreach test,$(TESTS),$(eval $(call TEST_BUILD,$(test))))
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#![crate_id(name = "md5sum", vers = "1.0.0", author = "Arcterus")]
|
#![crate_id = "hashsum#1.0.0"]
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the uutils coreutils package.
|
* This file is part of the uutils coreutils package.
|
||||||
*
|
*
|
||||||
* (c) Arcterus <arcterus@mail.com>
|
* (c) Arcterus <arcterus@mail.com>
|
||||||
|
* (c) Vsevolod Velichko <torkvemada@sorokdva.net>
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
@ -20,23 +21,81 @@ use std::io::stdio::stdin_raw;
|
||||||
use std::io::BufferedReader;
|
use std::io::BufferedReader;
|
||||||
use std::os;
|
use std::os;
|
||||||
use crypto::digest::Digest;
|
use crypto::digest::Digest;
|
||||||
|
use crypto::md5::Md5;
|
||||||
|
use crypto::sha1::Sha1;
|
||||||
|
use crypto::sha2::{Sha224, Sha256, Sha384, Sha512};
|
||||||
|
|
||||||
#[path = "../common/util.rs"]
|
#[path = "../common/util.rs"]
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
static NAME: &'static str = "md5sum";
|
static NAME: &'static str = "hashsum";
|
||||||
static VERSION: &'static str = "1.0.0";
|
static VERSION: &'static str = "1.0.0";
|
||||||
|
|
||||||
|
fn is_custom_binary(program: &str) -> bool {
|
||||||
|
match program {
|
||||||
|
"md5sum" | "sha1sum"
|
||||||
|
| "sha224sum" | "sha256sum"
|
||||||
|
| "sha384sum" | "sha512sum" => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_algo_opts(program: &str) -> Vec<getopts::OptGroup> {
|
||||||
|
if is_custom_binary(program) {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
vec!(
|
||||||
|
getopts::optflag("", "md5", "work with MD5"),
|
||||||
|
getopts::optflag("", "sha1", "work with SHA1"),
|
||||||
|
getopts::optflag("", "sha224", "work with SHA224"),
|
||||||
|
getopts::optflag("", "sha256", "work with SHA256"),
|
||||||
|
getopts::optflag("", "sha384", "work with SHA384"),
|
||||||
|
getopts::optflag("", "sha512", "work with SHA512")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_algo(program: &str, matches: &getopts::Matches) -> (&str, Box<Digest>) {
|
||||||
|
let mut alg: Option<Box<Digest>> = None;
|
||||||
|
let mut name: &'static str = "";
|
||||||
|
match program {
|
||||||
|
"md5sum" => ("MD5", box Md5::new() as Box<Digest>),
|
||||||
|
"sha1sum" => ("SHA1", box Sha1::new() as Box<Digest>),
|
||||||
|
"sha224sum" => ("SHA224", box Sha224::new() as Box<Digest>),
|
||||||
|
"sha256sum" => ("SHA256", box Sha256::new() as Box<Digest>),
|
||||||
|
"sha384sum" => ("SHA384", box Sha384::new() as Box<Digest>),
|
||||||
|
"sha512sum" => ("SHA512", box Sha512::new() as Box<Digest>),
|
||||||
|
_ => {
|
||||||
|
{
|
||||||
|
let set_or_crash = |n: &'static str, val: Box<Digest>| -> () {
|
||||||
|
if alg.is_some() { crash!(1, "You cannot combine multiple hash algorithms!") };
|
||||||
|
name = n;
|
||||||
|
alg = Some(val);
|
||||||
|
};
|
||||||
|
if matches.opt_present("md5") { set_or_crash("MD5", box Md5::new()) };
|
||||||
|
if matches.opt_present("sha1") { set_or_crash("SHA1", box Sha1::new()) };
|
||||||
|
if matches.opt_present("sha224") { set_or_crash("SHA224", box Sha224::new()) };
|
||||||
|
if matches.opt_present("sha256") { set_or_crash("SHA256", box Sha256::new()) };
|
||||||
|
if matches.opt_present("sha384") { set_or_crash("SHA384", box Sha384::new()) };
|
||||||
|
if matches.opt_present("sha512") { set_or_crash("SHA512", box Sha512::new()) };
|
||||||
|
}
|
||||||
|
if alg.is_none() { crash!(1, "You must specify hash algorithm!") };
|
||||||
|
(name, alg.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn main() { os::set_exit_status(uumain(os::args())); }
|
fn main() { os::set_exit_status(uumain(os::args())); }
|
||||||
|
|
||||||
pub fn uumain(args: Vec<String>) -> int {
|
pub fn uumain(args: Vec<String>) -> int {
|
||||||
|
|
||||||
let program = args.get(0).clone();
|
let program = args.get(0).clone();
|
||||||
|
let binary = Path::new(program.as_slice());
|
||||||
|
let binary_name = binary.filename_str().unwrap();
|
||||||
|
|
||||||
let opts = [
|
let mut opts: Vec<getopts::OptGroup> = vec!(
|
||||||
getopts::optflag("b", "binary", "read in binary mode"),
|
getopts::optflag("b", "binary", "read in binary mode"),
|
||||||
getopts::optflag("c", "check", "read MD5 sums from the FILEs and check them"),
|
getopts::optflag("c", "check", "read hashsums from the FILEs and check them"),
|
||||||
getopts::optflag("", "tag", "create a BSD-style checksum"),
|
getopts::optflag("", "tag", "create a BSD-style checksum"),
|
||||||
getopts::optflag("t", "text", "read in text mode (default)"),
|
getopts::optflag("t", "text", "read in text mode (default)"),
|
||||||
getopts::optflag("q", "quiet", "don't print OK for each successfully verified file"),
|
getopts::optflag("q", "quiet", "don't print OK for each successfully verified file"),
|
||||||
|
@ -44,24 +103,23 @@ pub fn uumain(args: Vec<String>) -> int {
|
||||||
getopts::optflag("", "strict", "exit non-zero for improperly formatted checksum lines"),
|
getopts::optflag("", "strict", "exit non-zero for improperly formatted checksum lines"),
|
||||||
getopts::optflag("w", "warn", "warn about improperly formatted checksum lines"),
|
getopts::optflag("w", "warn", "warn about improperly formatted checksum lines"),
|
||||||
getopts::optflag("h", "help", "display this help and exit"),
|
getopts::optflag("h", "help", "display this help and exit"),
|
||||||
getopts::optflag("V", "version", "output version information and exit")
|
getopts::optflag("V", "version", "output version information and exit"),
|
||||||
];
|
);
|
||||||
|
|
||||||
let matches = match getopts::getopts(args.tail(), opts) {
|
opts.push_all_move(get_algo_opts(binary_name.as_slice()));
|
||||||
|
|
||||||
|
let matches = match getopts::getopts(args.tail(), opts.as_slice()) {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(f) => crash!(1, "{}", f)
|
Err(f) => crash!(1, "{}", f)
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches.opt_present("help") {
|
if matches.opt_present("help") {
|
||||||
println!("{} v{}", NAME, VERSION);
|
usage(program.as_slice(), binary_name.as_slice(), opts.as_slice());
|
||||||
println!("");
|
|
||||||
println!("Usage:");
|
|
||||||
println!(" {} [OPTION]... [FILE]...", program);
|
|
||||||
println!("");
|
|
||||||
print!("{}", getopts::usage("Compute and check MD5 message digests.", opts));
|
|
||||||
} else if matches.opt_present("version") {
|
} else if matches.opt_present("version") {
|
||||||
println!("{} v{}", NAME, VERSION);
|
version();
|
||||||
} else {
|
} else {
|
||||||
|
let (name, algo) = detect_algo(binary_name.as_slice(), &matches);
|
||||||
|
|
||||||
let binary = matches.opt_present("binary");
|
let binary = matches.opt_present("binary");
|
||||||
let check = matches.opt_present("check");
|
let check = matches.opt_present("check");
|
||||||
let tag = matches.opt_present("tag");
|
let tag = matches.opt_present("tag");
|
||||||
|
@ -74,7 +132,7 @@ pub fn uumain(args: Vec<String>) -> int {
|
||||||
} else {
|
} else {
|
||||||
matches.free
|
matches.free
|
||||||
};
|
};
|
||||||
match md5sum(files, binary, check, tag, status, quiet, strict, warn) {
|
match hashsum(name, algo, files, binary, check, tag, status, quiet, strict, warn) {
|
||||||
Ok(()) => return 0,
|
Ok(()) => return 0,
|
||||||
Err(e) => return e
|
Err(e) => return e
|
||||||
}
|
}
|
||||||
|
@ -83,9 +141,25 @@ pub fn uumain(args: Vec<String>) -> int {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn md5sum(files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool, quiet: bool, strict: bool, warn: bool) -> Result<(), int> {
|
fn version() {
|
||||||
let mut md5 = crypto::md5::Md5::new();
|
println!("{} v{}", NAME, VERSION);
|
||||||
let bytes = md5.output_bits() / 4;
|
}
|
||||||
|
|
||||||
|
fn usage(program: &str, binary_name: &str, opts: &[getopts::OptGroup]) {
|
||||||
|
version();
|
||||||
|
println!("");
|
||||||
|
println!("Usage:");
|
||||||
|
if is_custom_binary(binary_name) {
|
||||||
|
println!(" {} [OPTION]... [FILE]...", program);
|
||||||
|
} else {
|
||||||
|
println!(" {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512}} [OPTION]... [FILE]...", program);
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
print!("{}", getopts::usage("Compute and check message digests.", opts));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool, quiet: bool, strict: bool, warn: bool) -> Result<(), int> {
|
||||||
|
let bytes = digest.output_bits() / 4;
|
||||||
let mut bad_format = 0;
|
let mut bad_format = 0;
|
||||||
let mut failed = 0;
|
let mut failed = 0;
|
||||||
for filename in files.iter() {
|
for filename in files.iter() {
|
||||||
|
@ -104,7 +178,7 @@ fn md5sum(files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool
|
||||||
let line = safe_unwrap!(line);
|
let line = safe_unwrap!(line);
|
||||||
let (ck_filename, sum) = match from_gnu(line.as_slice(), bytes) {
|
let (ck_filename, sum) = match from_gnu(line.as_slice(), bytes) {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
None => match from_bsd(line.as_slice(), bytes) {
|
None => match from_bsd(algoname, line.as_slice(), bytes) {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
None => {
|
None => {
|
||||||
bad_format += 1;
|
bad_format += 1;
|
||||||
|
@ -112,13 +186,13 @@ fn md5sum(files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool
|
||||||
return Err(1);
|
return Err(1);
|
||||||
}
|
}
|
||||||
if warn {
|
if warn {
|
||||||
show_warning!("{}: {}: improperly formatted MD5 checksum line", filename, i + 1);
|
show_warning!("{}: {}: improperly formatted {} checksum line", filename, i + 1, algoname);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let real_sum = calc_sum(&mut md5, &mut safe_unwrap!(File::open(&Path::new(ck_filename))), binary);
|
let real_sum = calc_sum(&mut digest, &mut safe_unwrap!(File::open(&Path::new(ck_filename))), binary);
|
||||||
if sum == real_sum.as_slice() {
|
if sum == real_sum.as_slice() {
|
||||||
if !quiet {
|
if !quiet {
|
||||||
println!("{}: OK", ck_filename);
|
println!("{}: OK", ck_filename);
|
||||||
|
@ -131,9 +205,9 @@ fn md5sum(files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let sum = calc_sum(&mut md5, &mut file, binary);
|
let sum = calc_sum(&mut digest, &mut file, binary);
|
||||||
if tag {
|
if tag {
|
||||||
println!("MD5 ({}) = {}", filename, sum);
|
println!("{} ({}) = {}", algoname, filename, sum);
|
||||||
} else {
|
} else {
|
||||||
println!("{} {}", sum, filename);
|
println!("{} {}", sum, filename);
|
||||||
}
|
}
|
||||||
|
@ -153,16 +227,16 @@ fn md5sum(files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_sum(md5: &mut crypto::md5::Md5, file: &mut Reader, binary: bool) -> String {
|
fn calc_sum(digest: &mut Box<Digest>, file: &mut Reader, binary: bool) -> String {
|
||||||
let data =
|
let data =
|
||||||
if binary {
|
if binary {
|
||||||
(safe_unwrap!(file.read_to_end()))
|
(safe_unwrap!(file.read_to_end()))
|
||||||
} else {
|
} else {
|
||||||
(safe_unwrap!(file.read_to_str())).into_bytes()
|
(safe_unwrap!(file.read_to_str())).into_bytes()
|
||||||
};
|
};
|
||||||
md5.reset();
|
digest.reset();
|
||||||
md5.input(data.as_slice());
|
digest.input(data.as_slice());
|
||||||
md5.result_str()
|
digest.result_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_gnu<'a>(line: &'a str, bytes: uint) -> Option<(&'a str, &'a str)> {
|
fn from_gnu<'a>(line: &'a str, bytes: uint) -> Option<(&'a str, &'a str)> {
|
||||||
|
@ -174,14 +248,15 @@ fn from_gnu<'a>(line: &'a str, bytes: uint) -> Option<(&'a str, &'a str)> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_bsd<'a>(line: &'a str, bytes: uint) -> Option<(&'a str, &'a str)> {
|
fn from_bsd<'a>(algoname: &str, line: &'a str, bytes: uint) -> Option<(&'a str, &'a str)> {
|
||||||
if line.slice(0, 5) == "MD5 (" {
|
let expected = format!("{} (", algoname);
|
||||||
|
if line.slice(0, expected.len()) == expected.as_slice() {
|
||||||
let rparen = match line.find(')') {
|
let rparen = match line.find(')') {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
None => return None
|
None => return None
|
||||||
};
|
};
|
||||||
if rparen > 5 && line.slice(rparen + 1, rparen + 4) == " = " && line.len() - 1 == rparen + 4 + bytes {
|
if rparen > expected.len() && line.slice(rparen + 1, rparen + 4) == " = " && line.len() - 1 == rparen + 4 + bytes {
|
||||||
return Some((line.slice(5, rparen), line.slice(rparen + 4, line.len() - 1)));
|
return Some((line.slice(expected.len(), rparen), line.slice(rparen + 4, line.len() - 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
Loading…
Reference in a new issue