diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..5474734dcb3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "md5sum/rust-crypto"] + path = md5sum/rust-crypto + url = git://github.com/DaGenix/rust-crypto.git diff --git a/Makefile b/Makefile index 0b998facf17..87177bbb82d 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,4 @@ -# Binaries -RUSTC ?= rustc -RM := rm - -# Flags -RUSTCFLAGS := --opt-level=3 -RMFLAGS := +include common.mk # Possible programs PROGS := \ @@ -16,6 +10,7 @@ PROGS := \ env \ du \ false \ + md5sum \ mkdir \ paste \ printenv \ @@ -70,8 +65,16 @@ command = sh -c '$(1)' # Main exe build rule define EXE_BUILD +ifeq ($(wildcard $(1)/Makefile),) build/$(1): $(1)/$(1).rs $(call command,$(RUSTC) $(RUSTCFLAGS) -o build/$(1) $(1)/$(1).rs) +clean_$(1): +else +build/$(1): $(1)/$(1).rs + cd $(1) && make +clean_$(1): + cd $(1) && make clean +endif endef # Test exe built rules @@ -89,7 +92,7 @@ all: build $(EXES_PATHS) test: tmp $(addprefix test_,$(TESTS)) $(RM) -rf tmp -clean: +clean: $(addprefix clean_,$(EXES)) $(RM) -rf build tmp build: diff --git a/README.md b/README.md index 84e1b0f9319..3d98a6ebcaf 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,6 @@ To do - ls-vdir - ls - make-prime-list -- md5sum - mkfifo - mknod - mktemp diff --git a/common.mk b/common.mk new file mode 100644 index 00000000000..364ad755830 --- /dev/null +++ b/common.mk @@ -0,0 +1,7 @@ +# Binaries +RUSTC ?= rustc +RM := rm + +# Flags +RUSTCFLAGS := --opt-level=3 +RMFLAGS := diff --git a/common/util.rs b/common/util.rs index e26c715e582..db506e3bea5 100644 --- a/common/util.rs +++ b/common/util.rs @@ -18,6 +18,14 @@ macro_rules! show_error( }) ) +#[macro_export] +macro_rules! show_warning( + ($($args:expr),+) => ({ + safe_write!(&mut ::std::io::stderr(), "{}: warning: ", ::NAME); + safe_writeln!(&mut ::std::io::stderr(), $($args),+); + }) +) + #[macro_export] macro_rules! crash( ($exitcode:expr, $($args:expr),+) => ({ diff --git a/md5sum/Makefile b/md5sum/Makefile new file mode 100644 index 00000000000..66f8479980c --- /dev/null +++ b/md5sum/Makefile @@ -0,0 +1,15 @@ +include ../common.mk + +all: ../build/md5sum + +CRYPTO_DIR := rust-crypto +CRYPTO_LIB := $(CRYPTO_DIR)/$(shell $(RUSTC) --crate-file-name --crate-type rlib $(CRYPTO_DIR)/src/rust-crypto/lib.rs) + +../build/md5sum: md5sum.rs $(CRYPTO_LIB) + $(RUSTC) $(RUSTFLAGS) -L $(CRYPTO_DIR) -o $@ $< + +$(CRYPTO_LIB): $(CRYPTO_DIR)/src/rust-crypto/*.rs + cd $(CRYPTO_DIR) && make + +clean: + cd $(CRYPTO_DIR) && make clean diff --git a/md5sum/md5sum.rs b/md5sum/md5sum.rs new file mode 100644 index 00000000000..00fc4dc0681 --- /dev/null +++ b/md5sum/md5sum.rs @@ -0,0 +1,158 @@ +#[crate_id(name = "md5sum", vers = "1.0.0", author = "Arcterus")]; + +#[feature(macro_rules)]; + +extern crate crypto = "rust-crypto"; +extern crate getopts; + +use std::io::fs::File; +use std::io::BufferedReader; +use std::os; +use crypto::digest::Digest; + +#[path = "../common/util.rs"] +mod util; + +static NAME: &'static str = "md5sum"; +static VERSION: &'static str = "1.0.0"; + +fn main() { + let args = os::args(); + + let program = args[0].clone(); + + let opts = [ + getopts::optflag("b", "binary", "read in binary mode"), + getopts::optflag("c", "check", "read MD5 sums 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"), + getopts::optflag("s", "status", "don't output anything, status code shows success"), + 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") + ]; + + let matches = match getopts::getopts(args.tail(), opts) { + Ok(m) => m, + Err(f) => crash!(1, "{}", f.to_err_msg()) + }; + + 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)); + } else if matches.opt_present("version") { + println!("{} v{}", NAME, VERSION); + } else { + let binary = matches.opt_present("binary"); + 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; + md5sum(matches.free, binary, check, tag, status, quiet, strict, warn); + } +} + +fn md5sum(files: Vec<~str>, binary: bool, check: bool, tag: bool, status: bool, quiet: bool, strict: bool, warn: bool) { + let mut md5 = crypto::md5::Md5::new(); + let bytes = md5.output_bits() / 4; + let mut bad_format = 0; + let mut failed = 0; + for filename in files.iter() { + let filename: &str = *filename; + let mut file = safe_unwrap!(File::open(&Path::new(filename))); + if check { + let mut buffer = BufferedReader::new(file); + for (i, line) in buffer.lines().enumerate() { + let line = safe_unwrap!(line); + let (ck_filename, sum) = match from_gnu(line, bytes) { + Some(m) => m, + None => match from_bsd(line, bytes) { + Some(m) => m, + None => { + bad_format += 1; + if strict { + os::set_exit_status(1); + } + if warn { + show_warning!("{}: {}: improperly formatted MD5 checksum line", filename, i + 1); + } + continue; + } + } + }; + let real_sum = calc_sum(&mut md5, &mut safe_unwrap!(File::open(&Path::new(ck_filename))), binary); + if sum == real_sum { + if !quiet { + println!("{}: OK", ck_filename); + } + } else { + if !status { + println!("{}: FAILED", ck_filename); + } + failed += 1; + os::set_exit_status(1); + } + } + } else { + let sum = calc_sum(&mut md5, &mut file, binary); + if tag { + println!("MD5 ({}) = {}", filename, sum); + } else { + println!("{} {}", sum, filename); + } + } + } + if !status { + if bad_format == 1 { + show_warning!("{} line is improperly formatted", bad_format); + } else if bad_format > 1 { + show_warning!("{} lines are improperly formatted", bad_format); + } + if failed > 0 { + show_warning!("{} computed checksum did NOT match", failed); + } + } +} + +fn calc_sum(md5: &mut crypto::md5::Md5, file: &mut File, binary: bool) -> ~str { + let data = + if binary { + safe_unwrap!(file.read_to_end()) + } else { + let val = safe_unwrap!(file.read_to_str()).into_bytes(); // XXX: i don't know why the variable is necessary + val + }; + md5.reset(); + md5.input(data); + md5.result_str() +} + +fn from_gnu<'a>(line: &'a str, bytes: uint) -> Option<(&'a str, &'a str)> { + let sum = line.slice_to(bytes); + if sum.len() < bytes || line.slice(bytes, bytes + 2) != " " { + None + } else { + Some((line.slice(bytes + 2, line.len() - 1), sum)) + } +} + +fn from_bsd<'a>(line: &'a str, bytes: uint) -> Option<(&'a str, &'a str)> { + if line.slice(0, 5) == "MD5 (" { + 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))); + } + } + None +} \ No newline at end of file diff --git a/md5sum/rust-crypto b/md5sum/rust-crypto new file mode 160000 index 00000000000..fd168c53c00 --- /dev/null +++ b/md5sum/rust-crypto @@ -0,0 +1 @@ +Subproject commit fd168c53c00475c4fae3efc329301e80451bd967 diff --git a/truncate/truncate.rs b/truncate/truncate.rs index 566611f5de6..17882e5fe2b 100644 --- a/truncate/truncate.rs +++ b/truncate/truncate.rs @@ -176,7 +176,7 @@ fn parse_size(size: ~str) -> (u64, TruncateMode) { } } slice - }.bytes().to_owned_vec(); + }.to_owned().into_bytes(); let mut number = match u64::parse_bytes(bytes, 10) { Some(num) => num, None => {