diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index ee18e888f..99a6ec23e 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" path = "src/tee.rs" [dependencies] -getopts = "0.2.18" +clap = "2.33.3" libc = "0.2.42" uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["libc"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index e29945382..c54fa0d16 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -5,6 +5,10 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. +#[macro_use] +extern crate uucore; + +use clap::{App, Arg}; use std::fs::OpenOptions; use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write}; use std::path::{Path, PathBuf}; @@ -12,80 +16,61 @@ use std::path::{Path, PathBuf}; #[cfg(unix)] use uucore::libc; -static NAME: &str = "tee"; static VERSION: &str = env!("CARGO_PKG_VERSION"); +static ABOUT: &str = "Copy standard input to each FILE, and also to standard output."; -pub fn uumain(args: impl uucore::Args) -> i32 { - let args = args.collect_str(); - - match options(&args).and_then(exec) { - Ok(_) => 0, - Err(_) => 1, - } +mod options { + pub const APPEND: &str = "append"; + pub const IGNORE_INTERRUPTS: &str = "ignore-interrupts"; + pub const FILE: &str = "file"; } #[allow(dead_code)] struct Options { - program: String, append: bool, ignore_interrupts: bool, - print_and_exit: Option, files: Vec, } -fn options(args: &[String]) -> Result { - let mut opts = getopts::Options::new(); - - opts.optflag("a", "append", "append to the given FILEs, do not overwrite"); - opts.optflag( - "i", - "ignore-interrupts", - "ignore interrupt signals (ignored on non-Unix platforms)", - ); - opts.optflag("h", "help", "display this help and exit"); - opts.optflag("V", "version", "output version information and exit"); - - opts.parse(&args[1..]) - .map_err(|e| Error::new(ErrorKind::Other, format!("{}", e))) - .map(|m| { - let version = format!("{} {}", NAME, VERSION); - let arguments = "[OPTION]... [FILE]..."; - let brief = "Copy standard input to each FILE, and also to standard output."; - let comment = "If a FILE is -, it refers to a file named - ."; - let help = format!( - "{}\n\nUsage:\n {} {}\n\n{}\n{}", - version, - NAME, - arguments, - opts.usage(brief), - comment - ); - let names: Vec = m.free.clone().into_iter().collect(); - let to_print = if m.opt_present("help") { - Some(help) - } else if m.opt_present("version") { - Some(version) - } else { - None - }; - Options { - program: NAME.to_owned(), - append: m.opt_present("append"), - ignore_interrupts: m.opt_present("ignore-interrupts"), - print_and_exit: to_print, - files: names, - } - }) - .map_err(|message| warn(format!("{}", message).as_ref())) +fn get_usage() -> String { + format!("{0} [OPTION]... [FILE]...", executable!()) } -fn exec(options: Options) -> Result<()> { - match options.print_and_exit { - Some(text) => { - println!("{}", text); - Ok(()) - } - None => tee(options), +pub fn uumain(args: impl uucore::Args) -> i32 { + let usage = get_usage(); + + let matches = App::new(executable!()) + .version(VERSION) + .about(ABOUT) + .usage(&usage[..]) + .after_help("If a FILE is -, it refers to a file named - .") + .arg( + Arg::with_name(options::APPEND) + .long(options::APPEND) + .short("a") + .help("append to the given FILEs, do not overwrite"), + ) + .arg( + Arg::with_name(options::IGNORE_INTERRUPTS) + .long(options::IGNORE_INTERRUPTS) + .short("i") + .help("ignore interrupt signals (ignored on non-Unix platforms)"), + ) + .arg(Arg::with_name(options::FILE).multiple(true)) + .get_matches_from(args); + + let options = Options { + append: matches.is_present(options::APPEND), + ignore_interrupts: matches.is_present(options::IGNORE_INTERRUPTS), + files: matches + .values_of(options::FILE) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(), + }; + + match tee(options) { + Ok(_) => 0, + Err(_) => 1, } } @@ -173,7 +158,7 @@ impl Write for NamedWriter { match self.inner.write(buf) { Err(f) => { self.inner = Box::new(sink()) as Box; - warn(format!("{}: {}", self.path.display(), f.to_string()).as_ref()); + show_warning!("{}: {}", self.path.display(), f.to_string()); Err(f) } okay => okay, @@ -184,7 +169,7 @@ impl Write for NamedWriter { match self.inner.flush() { Err(f) => { self.inner = Box::new(sink()) as Box; - warn(format!("{}: {}", self.path.display(), f.to_string()).as_ref()); + show_warning!("{}: {}", self.path.display(), f.to_string()); Err(f) } okay => okay, @@ -200,15 +185,10 @@ impl Read for NamedReader { fn read(&mut self, buf: &mut [u8]) -> Result { match self.inner.read(buf) { Err(f) => { - warn(format!("{}: {}", Path::new("stdin").display(), f.to_string()).as_ref()); + show_warning!("{}: {}", Path::new("stdin").display(), f.to_string()); Err(f) } okay => okay, } } } - -fn warn(message: &str) -> Error { - eprintln!("{}: {}", NAME, message); - Error::new(ErrorKind::Other, format!("{}: {}", NAME, message)) -}