use super::Cmd; use clap::{Arg, ArgAction, Command}; use md5::{Digest, Md5}; use std::{ fmt::Write, fs::File, io::{self, BufRead, BufReader, Read}, process, error::Error, }; #[derive(Debug, Default)] pub struct Md5sum; impl Cmd for Md5sum { fn cli(&self) -> clap::Command { Command::new("md5sum") .about("compute and check MD5 message digest") .author("Nathan Fisher") .version(env!("CARGO_PKG_VERSION")) .args([ Arg::new("check") .help("read checksums from the FILEs and check them") .short('c') .long("check") .action(ArgAction::SetTrue), Arg::new("file") .value_name("FILE") .num_args(1..) .default_value("-"), ]) } fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box> { let Some(matches) = matches else { return Err(io::Error::new(io::ErrorKind::Other, "no input").into()); }; if let Some(files) = matches.get_many::("file") { let mut erred = 0; for f in files { if matches.get_flag("check") { if f == "-" { return Err( io::Error::new(io::ErrorKind::Other, "no file specified").into() ); } let fd = File::open(f)?; let reader = BufReader::new(fd); for line in reader.lines() { let line = line?; let mut split = line.split_whitespace(); let sum = split.next().ok_or::>( io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(), )?; let file = split.next().ok_or::>( io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(), )?; let mut hasher = Md5::new(); let mut buf = vec![]; let mut fd = File::open(file)?; let _s = fd.read_to_end(&mut buf)?; hasher.update(&buf); let res = hasher.finalize(); let mut s = String::new(); for c in res { write!(s, "{c:x}")?; } if s.as_str() == sum { println!("{file}: OK"); } else { println!("{file}: FAILED"); erred += 1; } } } else { let mut hasher = Md5::new(); let mut buf = vec![]; if f == "-" { let _s = io::stdin().read_to_end(&mut buf)?; } else { let mut fd = File::open(f)?; let _s = fd.read_to_end(&mut buf)?; } hasher.update(&buf); let res = hasher.finalize(); for c in res { print!("{c:x}"); } println!(" {f}"); } } if erred > 0 { println!("md5sum: WARNING: {erred} computed checksum did NOT match"); process::exit(1); } } Ok(()) } fn path(&self) -> Option { Some(crate::Path::UsrBin) } }