diff --git a/Makefile b/Makefile index 55123eb00..17ed29197 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ PROGS := \ false \ fmt \ fold \ - md5sum \ + hashsum \ mkdir \ nl \ paste \ @@ -63,6 +63,14 @@ ifneq ($(OS),Windows_NT) PROGS := $(PROGS) $(UNIX_PROGS) endif +ALIASES := \ + hashsum:md5sum \ + hashsum:sha1sum \ + hashsum:sha224sum \ + hashsum:sha256sum \ + hashsum:sha384sum \ + hashsum:sha512sum + BUILD ?= $(PROGS) # 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) 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 define TEST_BUILD 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)))) else $(foreach exe,$(EXES),$(eval $(call EXE_BUILD,$(exe)))) +$(foreach alias,$(ALIASES),$(eval $(call MAKE_ALIAS,$(alias)))) endif $(foreach test,$(TESTS),$(eval $(call TEST_BUILD,$(test)))) diff --git a/md5sum/md5sum.rs b/hashsum/hashsum.rs similarity index 50% rename from md5sum/md5sum.rs rename to hashsum/hashsum.rs index 5a6425209..fd226d736 100644 --- a/md5sum/md5sum.rs +++ b/hashsum/hashsum.rs @@ -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. * * (c) Arcterus + * (c) Vsevolod Velichko * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,23 +21,81 @@ use std::io::stdio::stdin_raw; use std::io::BufferedReader; use std::os; use crypto::digest::Digest; +use crypto::md5::Md5; +use crypto::sha1::Sha1; +use crypto::sha2::{Sha224, Sha256, Sha384, Sha512}; #[path = "../common/util.rs"] mod util; -static NAME: &'static str = "md5sum"; +static NAME: &'static str = "hashsum"; 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 { + 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) { + let mut alg: Option> = None; + let mut name: &'static str = ""; + match program { + "md5sum" => ("MD5", box Md5::new() as Box), + "sha1sum" => ("SHA1", box Sha1::new() as Box), + "sha224sum" => ("SHA224", box Sha224::new() as Box), + "sha256sum" => ("SHA256", box Sha256::new() as Box), + "sha384sum" => ("SHA384", box Sha384::new() as Box), + "sha512sum" => ("SHA512", box Sha512::new() as Box), + _ => { + { + let set_or_crash = |n: &'static str, val: Box| -> () { + 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)] fn main() { os::set_exit_status(uumain(os::args())); } pub fn uumain(args: Vec) -> int { - 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 = vec!( 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("t", "text", "read in text mode (default)"), getopts::optflag("q", "quiet", "don't print OK for each successfully verified file"), @@ -44,24 +103,23 @@ pub fn uumain(args: Vec) -> int { getopts::optflag("", "strict", "exit non-zero for 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("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, Err(f) => crash!(1, "{}", f) }; if matches.opt_present("help") { - println!("{} v{}", NAME, VERSION); - println!(""); - println!("Usage:"); - println!(" {} [OPTION]... [FILE]...", program); - println!(""); - print!("{}", getopts::usage("Compute and check MD5 message digests.", opts)); + usage(program.as_slice(), binary_name.as_slice(), opts.as_slice()); } else if matches.opt_present("version") { - println!("{} v{}", NAME, VERSION); + version(); } else { + let (name, algo) = detect_algo(binary_name.as_slice(), &matches); + let binary = matches.opt_present("binary"); let check = matches.opt_present("check"); let tag = matches.opt_present("tag"); @@ -74,7 +132,7 @@ pub fn uumain(args: Vec) -> int { } else { 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, Err(e) => return e } @@ -83,9 +141,25 @@ pub fn uumain(args: Vec) -> int { 0 } -fn md5sum(files: Vec, binary: bool, check: bool, tag: bool, status: bool, quiet: bool, strict: bool, warn: bool) -> Result<(), int> { - let mut md5 = crypto::md5::Md5::new(); - let bytes = md5.output_bits() / 4; +fn version() { + println!("{} v{}", NAME, VERSION); +} + +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, files: Vec, 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 failed = 0; for filename in files.iter() { @@ -104,7 +178,7 @@ fn md5sum(files: Vec, binary: bool, check: bool, tag: bool, status: bool let line = safe_unwrap!(line); let (ck_filename, sum) = match from_gnu(line.as_slice(), bytes) { Some(m) => m, - None => match from_bsd(line.as_slice(), bytes) { + None => match from_bsd(algoname, line.as_slice(), bytes) { Some(m) => m, None => { bad_format += 1; @@ -112,13 +186,13 @@ fn md5sum(files: Vec, binary: bool, check: bool, tag: bool, status: bool return Err(1); } if warn { - show_warning!("{}: {}: improperly formatted MD5 checksum line", filename, i + 1); + show_warning!("{}: {}: improperly formatted {} checksum line", filename, i + 1, algoname); } 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 !quiet { println!("{}: OK", ck_filename); @@ -131,9 +205,9 @@ fn md5sum(files: Vec, binary: bool, check: bool, tag: bool, status: bool } } } else { - let sum = calc_sum(&mut md5, &mut file, binary); + let sum = calc_sum(&mut digest, &mut file, binary); if tag { - println!("MD5 ({}) = {}", filename, sum); + println!("{} ({}) = {}", algoname, filename, sum); } else { println!("{} {}", sum, filename); } @@ -153,16 +227,16 @@ fn md5sum(files: Vec, binary: bool, check: bool, tag: bool, status: bool Ok(()) } -fn calc_sum(md5: &mut crypto::md5::Md5, file: &mut Reader, binary: bool) -> String { +fn calc_sum(digest: &mut Box, file: &mut Reader, binary: bool) -> String { let data = if binary { (safe_unwrap!(file.read_to_end())) } else { (safe_unwrap!(file.read_to_str())).into_bytes() }; - md5.reset(); - md5.input(data.as_slice()); - md5.result_str() + digest.reset(); + digest.input(data.as_slice()); + digest.result_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)> { - if line.slice(0, 5) == "MD5 (" { +fn from_bsd<'a>(algoname: &str, line: &'a str, bytes: uint) -> Option<(&'a str, &'a str)> { + let expected = format!("{} (", algoname); + if line.slice(0, expected.len()) == expected.as_slice() { let rparen = match line.find(')') { Some(m) => m, None => return None }; - if rparen > 5 && 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))); + if rparen > expected.len() && line.slice(rparen + 1, rparen + 4) == " = " && line.len() - 1 == rparen + 4 + bytes { + return Some((line.slice(expected.len(), rparen), line.slice(rparen + 4, line.len() - 1))); } } None