From 0531153fa6331407093474753d5178994e7d1895 Mon Sep 17 00:00:00 2001 From: Michael Debertol Date: Tue, 15 Jun 2021 15:35:02 +0200 Subject: [PATCH] uutils: move clap::App creation to separate functions --- Cargo.lock | 7 + src/uu/arch/src/arch.rs | 13 +- src/uu/base32/src/base32.rs | 5 + src/uu/base32/src/base_common.rs | 17 +- src/uu/base64/src/base64.rs | 1 + src/uu/basename/src/basename.rs | 52 ++--- src/uu/cat/src/cat.rs | 115 +++++----- src/uu/chgrp/src/chgrp.rs | 159 +++++++------- src/uu/chmod/src/chmod.rs | 108 ++++----- src/uu/chown/src/chown.rs | 192 ++++++++-------- src/uu/chroot/src/chroot.rs | 99 +++++---- src/uu/cksum/src/cksum.rs | 17 +- src/uu/comm/src/comm.rs | 22 +- src/uu/cp/src/cp.rs | 18 +- src/uu/csplit/src/csplit.rs | 56 ++--- src/uu/cut/src/cut.rs | 167 +++++++------- src/uu/date/src/date.rs | 132 +++++------ src/uu/df/src/df.rs | 230 +++++++++---------- src/uu/dircolors/src/dircolors.rs | 62 +++--- src/uu/dirname/src/dirname.rs | 24 +- src/uu/du/src/du.rs | 354 +++++++++++++++--------------- src/uu/echo/src/echo.rs | 37 ++-- src/uu/env/src/env.rs | 4 +- src/uu/expand/src/expand.rs | 14 +- src/uu/expr/Cargo.toml | 1 + src/uu/expr/src/expr.rs | 13 +- src/uu/factor/src/cli.rs | 13 +- src/uu/false/Cargo.toml | 1 + src/uu/false/src/false.rs | 9 + src/uu/fmt/src/fmt.rs | 248 ++++++++++----------- src/uu/fold/src/fold.rs | 55 ++--- src/uu/groups/src/groups.rs | 24 +- src/uu/hashsum/src/hashsum.rs | 232 ++++++++++---------- src/uu/head/src/head.rs | 4 +- src/uu/hostid/Cargo.toml | 1 + src/uu/hostid/src/hostid.rs | 15 +- src/uu/hostname/src/hostname.rs | 32 +-- src/uu/id/src/id.rs | 200 ++++++++--------- src/uu/install/src/install.rs | 52 ++--- src/uu/join/src/join.rs | 129 +++++------ src/uu/kill/src/kill.rs | 66 +++--- src/uu/link/src/link.rs | 28 +-- src/uu/ln/src/ln.rs | 122 +++++----- src/uu/logname/src/logname.rs | 12 +- src/uu/ls/src/ls.rs | 27 ++- src/uu/mkdir/src/mkdir.rs | 56 ++--- src/uu/mkfifo/src/mkfifo.rs | 45 ++-- src/uu/mknod/src/mknod.rs | 87 ++++---- src/uu/mktemp/src/mktemp.rs | 112 +++++----- src/uu/more/src/more.rs | 83 +++---- src/uu/mv/src/mv.rs | 104 +++++---- src/uu/nice/src/nice.rs | 30 +-- src/uu/nl/src/nl.rs | 109 ++++----- src/uu/nohup/src/nohup.rs | 28 +-- src/uu/nproc/src/nproc.rs | 38 ++-- src/uu/numfmt/src/numfmt.rs | 38 ++-- src/uu/od/src/od.rs | 76 ++++--- src/uu/paste/src/paste.rs | 29 +-- src/uu/pathchk/src/pathchk.rs | 44 ++-- src/uu/pinky/src/pinky.rs | 112 +++++----- src/uu/pr/Cargo.toml | 1 + src/uu/pr/src/pr.rs | 6 + src/uu/printenv/src/printenv.rs | 36 +-- src/uu/printf/Cargo.toml | 1 + src/uu/printf/src/printf.rs | 16 +- src/uu/ptx/src/ptx.rs | 41 ++-- src/uu/pwd/src/pwd.rs | 36 +-- src/uu/readlink/src/readlink.rs | 128 +++++------ src/uu/realpath/src/realpath.rs | 52 ++--- src/uu/relpath/src/relpath.rs | 42 ++-- src/uu/rm/src/rm.rs | 124 ++++++----- src/uu/rmdir/src/rmdir.rs | 40 ++-- src/uu/seq/src/seq.rs | 74 ++++--- src/uu/shred/src/shred.rs | 115 +++++----- src/uu/shuf/src/shuf.rs | 117 +++++----- src/uu/sleep/src/sleep.rs | 22 +- src/uu/sort/src/sort.rs | 322 +++++++++++++-------------- src/uu/split/src/split.rs | 157 ++++++------- src/uu/stat/src/stat.rs | 28 ++- src/uu/stdbuf/src/stdbuf.rs | 62 +++--- src/uu/sum/src/sum.rs | 39 ++-- src/uu/sync/src/sync.rs | 42 ++-- src/uu/tac/src/tac.rs | 47 ++-- src/uu/tail/src/tail.rs | 140 ++++++------ src/uu/tee/src/tee.rs | 40 ++-- src/uu/test/Cargo.toml | 1 + src/uu/test/src/test.rs | 8 + src/uu/timeout/src/timeout.rs | 35 +-- src/uu/touch/src/touch.rs | 150 ++++++------- src/uu/tr/src/tr.rs | 80 +++---- src/uu/true/Cargo.toml | 1 + src/uu/true/src/true.rs | 9 + src/uu/truncate/src/truncate.rs | 78 +++---- src/uu/tsort/src/tsort.rs | 23 +- src/uu/tty/src/tty.rs | 28 +-- src/uu/uname/src/uname.rs | 88 ++++---- src/uu/unexpand/src/unexpand.rs | 15 +- src/uu/uniq/src/uniq.rs | 84 +++---- src/uu/unlink/src/unlink.rs | 14 +- src/uu/uptime/src/uptime.rs | 24 +- src/uu/users/src/users.rs | 12 +- src/uu/wc/src/wc.rs | 60 ++--- src/uu/who/src/who.rs | 190 ++++++++-------- src/uu/whoami/src/whoami.rs | 8 +- src/uu/yes/src/yes.rs | 8 +- 105 files changed, 3571 insertions(+), 3253 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a059c1cd5..5f96f7f8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1959,6 +1959,7 @@ dependencies = [ name = "uu_expr" version = "0.0.6" dependencies = [ + "clap", "libc", "num-bigint", "num-traits", @@ -1986,6 +1987,7 @@ dependencies = [ name = "uu_false" version = "0.0.6" dependencies = [ + "clap", "uucore", "uucore_procs", ] @@ -2051,6 +2053,7 @@ dependencies = [ name = "uu_hostid" version = "0.0.6" dependencies = [ + "clap", "libc", "uucore", "uucore_procs", @@ -2327,6 +2330,7 @@ name = "uu_pr" version = "0.0.6" dependencies = [ "chrono", + "clap", "getopts", "itertools 0.10.0", "quick-error 2.0.1", @@ -2349,6 +2353,7 @@ dependencies = [ name = "uu_printf" version = "0.0.6" dependencies = [ + "clap", "itertools 0.8.2", "uucore", "uucore_procs", @@ -2585,6 +2590,7 @@ dependencies = [ name = "uu_test" version = "0.0.6" dependencies = [ + "clap", "libc", "redox_syscall 0.1.57", "uucore", @@ -2628,6 +2634,7 @@ dependencies = [ name = "uu_true" version = "0.0.6" dependencies = [ + "clap", "uucore", "uucore_procs", ] diff --git a/src/uu/arch/src/arch.rs b/src/uu/arch/src/arch.rs index eddd24502..955e57389 100644 --- a/src/uu/arch/src/arch.rs +++ b/src/uu/arch/src/arch.rs @@ -17,13 +17,16 @@ static ABOUT: &str = "Display machine architecture"; static SUMMARY: &str = "Determine architecture name for current machine."; pub fn uumain(args: impl uucore::Args) -> i32 { - App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .after_help(SUMMARY) - .get_matches_from(args); + uu_app().get_matches_from(args); let uts = return_if_err!(1, PlatformInfo::new()); println!("{}", uts.machine().trim()); 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .after_help(SUMMARY) +} diff --git a/src/uu/base32/src/base32.rs b/src/uu/base32/src/base32.rs index e6a01cb34..9a29717ac 100644 --- a/src/uu/base32/src/base32.rs +++ b/src/uu/base32/src/base32.rs @@ -10,6 +10,7 @@ extern crate uucore; use std::io::{stdin, Read}; +use clap::App; use uucore::encoding::Format; pub mod base_common; @@ -56,3 +57,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + base_common::base_app(executable!(), VERSION, ABOUT) +} diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index a606351ce..4fc8b495b 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -78,10 +78,17 @@ pub fn parse_base_cmd_args( about: &str, usage: &str, ) -> Result { - let app = App::new(name) + let app = base_app(name, version, about).usage(usage); + let arg_list = args + .collect_str(InvalidEncodingHandling::ConvertLossy) + .accept_any(); + Config::from(app.get_matches_from(arg_list)) +} + +pub fn base_app<'a>(name: &str, version: &'a str, about: &'a str) -> App<'static, 'a> { + App::new(name) .version(version) .about(about) - .usage(usage) // Format arguments. .arg( Arg::with_name(options::DECODE) @@ -106,11 +113,7 @@ pub fn parse_base_cmd_args( ) // "multiple" arguments are used to check whether there is more than one // file passed in. - .arg(Arg::with_name(options::FILE).index(1).multiple(true)); - let arg_list = args - .collect_str(InvalidEncodingHandling::ConvertLossy) - .accept_any(); - Config::from(app.get_matches_from(arg_list)) + .arg(Arg::with_name(options::FILE).index(1).multiple(true)) } pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> Box { diff --git a/src/uu/base64/src/base64.rs b/src/uu/base64/src/base64.rs index 0dd831027..71ed44e6e 100644 --- a/src/uu/base64/src/base64.rs +++ b/src/uu/base64/src/base64.rs @@ -10,6 +10,7 @@ extern crate uucore; use uu_base32::base_common; +pub use uu_base32::uu_app; use uucore::encoding::Format; diff --git a/src/uu/basename/src/basename.rs b/src/uu/basename/src/basename.rs index 098a3e2b2..5450ee3f2 100644 --- a/src/uu/basename/src/basename.rs +++ b/src/uu/basename/src/basename.rs @@ -40,31 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // // Argument parsing // - let matches = App::new(executable!()) - .version(crate_version!()) - .about(SUMMARY) - .usage(&usage[..]) - .arg( - Arg::with_name(options::MULTIPLE) - .short("a") - .long(options::MULTIPLE) - .help("support multiple arguments and treat each as a NAME"), - ) - .arg(Arg::with_name(options::NAME).multiple(true).hidden(true)) - .arg( - Arg::with_name(options::SUFFIX) - .short("s") - .long(options::SUFFIX) - .value_name("SUFFIX") - .help("remove a trailing SUFFIX; implies -a"), - ) - .arg( - Arg::with_name(options::ZERO) - .short("z") - .long(options::ZERO) - .help("end each output line with NUL, not newline"), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); // too few arguments if !matches.is_present(options::NAME) { @@ -116,6 +92,32 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(SUMMARY) + .arg( + Arg::with_name(options::MULTIPLE) + .short("a") + .long(options::MULTIPLE) + .help("support multiple arguments and treat each as a NAME"), + ) + .arg(Arg::with_name(options::NAME).multiple(true).hidden(true)) + .arg( + Arg::with_name(options::SUFFIX) + .short("s") + .long(options::SUFFIX) + .value_name("SUFFIX") + .help("remove a trailing SUFFIX; implies -a"), + ) + .arg( + Arg::with_name(options::ZERO) + .short("z") + .long(options::ZERO) + .help("end each output line with NUL, not newline"), + ) +} + fn basename(fullname: &str, suffix: &str) -> String { // Remove all platform-specific path separators from the end let path = fullname.trim_end_matches(is_separator); diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index 889ba424a..35a5308ed 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -169,7 +169,65 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + let number_mode = if matches.is_present(options::NUMBER_NONBLANK) { + NumberingMode::NonEmpty + } else if matches.is_present(options::NUMBER) { + NumberingMode::All + } else { + NumberingMode::None + }; + + let show_nonprint = vec![ + options::SHOW_ALL.to_owned(), + options::SHOW_NONPRINTING_ENDS.to_owned(), + options::SHOW_NONPRINTING_TABS.to_owned(), + options::SHOW_NONPRINTING.to_owned(), + ] + .iter() + .any(|v| matches.is_present(v)); + + let show_ends = vec![ + options::SHOW_ENDS.to_owned(), + options::SHOW_ALL.to_owned(), + options::SHOW_NONPRINTING_ENDS.to_owned(), + ] + .iter() + .any(|v| matches.is_present(v)); + + let show_tabs = vec![ + options::SHOW_ALL.to_owned(), + options::SHOW_TABS.to_owned(), + options::SHOW_NONPRINTING_TABS.to_owned(), + ] + .iter() + .any(|v| matches.is_present(v)); + + let squeeze_blank = matches.is_present(options::SQUEEZE_BLANK); + let files: Vec = match matches.values_of(options::FILE) { + Some(v) => v.clone().map(|v| v.to_owned()).collect(), + None => vec!["-".to_owned()], + }; + + let options = OutputOptions { + show_ends, + number: number_mode, + show_nonprint, + show_tabs, + squeeze_blank, + }; + let success = cat_files(files, &options).is_ok(); + + if success { + 0 + } else { + 1 + } +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .name(NAME) .version(crate_version!()) .usage(SYNTAX) @@ -229,61 +287,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .long(options::SHOW_NONPRINTING) .help("use ^ and M- notation, except for LF (\\n) and TAB (\\t)"), ) - .get_matches_from(args); - - let number_mode = if matches.is_present(options::NUMBER_NONBLANK) { - NumberingMode::NonEmpty - } else if matches.is_present(options::NUMBER) { - NumberingMode::All - } else { - NumberingMode::None - }; - - let show_nonprint = vec![ - options::SHOW_ALL.to_owned(), - options::SHOW_NONPRINTING_ENDS.to_owned(), - options::SHOW_NONPRINTING_TABS.to_owned(), - options::SHOW_NONPRINTING.to_owned(), - ] - .iter() - .any(|v| matches.is_present(v)); - - let show_ends = vec![ - options::SHOW_ENDS.to_owned(), - options::SHOW_ALL.to_owned(), - options::SHOW_NONPRINTING_ENDS.to_owned(), - ] - .iter() - .any(|v| matches.is_present(v)); - - let show_tabs = vec![ - options::SHOW_ALL.to_owned(), - options::SHOW_TABS.to_owned(), - options::SHOW_NONPRINTING_TABS.to_owned(), - ] - .iter() - .any(|v| matches.is_present(v)); - - let squeeze_blank = matches.is_present(options::SQUEEZE_BLANK); - let files: Vec = match matches.values_of(options::FILE) { - Some(v) => v.clone().map(|v| v.to_owned()).collect(), - None => vec!["-".to_owned()], - }; - - let options = OutputOptions { - show_ends, - number: number_mode, - show_nonprint, - show_tabs, - squeeze_blank, - }; - let success = cat_files(files, &options).is_ok(); - - if success { - 0 - } else { - 1 - } } fn cat_handle( diff --git a/src/uu/chgrp/src/chgrp.rs b/src/uu/chgrp/src/chgrp.rs index 454a0386c..489be59eb 100644 --- a/src/uu/chgrp/src/chgrp.rs +++ b/src/uu/chgrp/src/chgrp.rs @@ -73,84 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let mut app = App::new(executable!()) - .version(VERSION) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::verbosity::CHANGES) - .short("c") - .long(options::verbosity::CHANGES) - .help("like verbose but report only when a change is made"), - ) - .arg( - Arg::with_name(options::verbosity::SILENT) - .short("f") - .long(options::verbosity::SILENT), - ) - .arg( - Arg::with_name(options::verbosity::QUIET) - .long(options::verbosity::QUIET) - .help("suppress most error messages"), - ) - .arg( - Arg::with_name(options::verbosity::VERBOSE) - .short("v") - .long(options::verbosity::VERBOSE) - .help("output a diagnostic for every file processed"), - ) - .arg( - Arg::with_name(options::dereference::DEREFERENCE) - .long(options::dereference::DEREFERENCE), - ) - .arg( - Arg::with_name(options::dereference::NO_DEREFERENCE) - .short("h") - .long(options::dereference::NO_DEREFERENCE) - .help( - "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)", - ), - ) - .arg( - Arg::with_name(options::preserve_root::PRESERVE) - .long(options::preserve_root::PRESERVE) - .help("fail to operate recursively on '/'"), - ) - .arg( - Arg::with_name(options::preserve_root::NO_PRESERVE) - .long(options::preserve_root::NO_PRESERVE) - .help("do not treat '/' specially (the default)"), - ) - .arg( - Arg::with_name(options::REFERENCE) - .long(options::REFERENCE) - .value_name("RFILE") - .help("use RFILE's group rather than specifying GROUP values") - .takes_value(true) - .multiple(false), - ) - .arg( - Arg::with_name(options::RECURSIVE) - .short("R") - .long(options::RECURSIVE) - .help("operate on files and directories recursively"), - ) - .arg( - Arg::with_name(options::traverse::TRAVERSE) - .short(options::traverse::TRAVERSE) - .help("if a command line argument is a symbolic link to a directory, traverse it"), - ) - .arg( - Arg::with_name(options::traverse::NO_TRAVERSE) - .short(options::traverse::NO_TRAVERSE) - .help("do not traverse any symbolic links (default)") - .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]), - ) - .arg( - Arg::with_name(options::traverse::EVERY) - .short(options::traverse::EVERY) - .help("traverse every symbolic link to a directory encountered"), - ); + let mut app = uu_app().usage(&usage[..]); // we change the positional args based on whether // --reference was used. @@ -274,6 +197,86 @@ pub fn uumain(args: impl uucore::Args) -> i32 { executor.exec() } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(VERSION) + .about(ABOUT) + .arg( + Arg::with_name(options::verbosity::CHANGES) + .short("c") + .long(options::verbosity::CHANGES) + .help("like verbose but report only when a change is made"), + ) + .arg( + Arg::with_name(options::verbosity::SILENT) + .short("f") + .long(options::verbosity::SILENT), + ) + .arg( + Arg::with_name(options::verbosity::QUIET) + .long(options::verbosity::QUIET) + .help("suppress most error messages"), + ) + .arg( + Arg::with_name(options::verbosity::VERBOSE) + .short("v") + .long(options::verbosity::VERBOSE) + .help("output a diagnostic for every file processed"), + ) + .arg( + Arg::with_name(options::dereference::DEREFERENCE) + .long(options::dereference::DEREFERENCE), + ) + .arg( + Arg::with_name(options::dereference::NO_DEREFERENCE) + .short("h") + .long(options::dereference::NO_DEREFERENCE) + .help( + "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)", + ), + ) + .arg( + Arg::with_name(options::preserve_root::PRESERVE) + .long(options::preserve_root::PRESERVE) + .help("fail to operate recursively on '/'"), + ) + .arg( + Arg::with_name(options::preserve_root::NO_PRESERVE) + .long(options::preserve_root::NO_PRESERVE) + .help("do not treat '/' specially (the default)"), + ) + .arg( + Arg::with_name(options::REFERENCE) + .long(options::REFERENCE) + .value_name("RFILE") + .help("use RFILE's group rather than specifying GROUP values") + .takes_value(true) + .multiple(false), + ) + .arg( + Arg::with_name(options::RECURSIVE) + .short("R") + .long(options::RECURSIVE) + .help("operate on files and directories recursively"), + ) + .arg( + Arg::with_name(options::traverse::TRAVERSE) + .short(options::traverse::TRAVERSE) + .help("if a command line argument is a symbolic link to a directory, traverse it"), + ) + .arg( + Arg::with_name(options::traverse::NO_TRAVERSE) + .short(options::traverse::NO_TRAVERSE) + .help("do not traverse any symbolic links (default)") + .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]), + ) + .arg( + Arg::with_name(options::traverse::EVERY) + .short(options::traverse::EVERY) + .help("traverse every symbolic link to a directory encountered"), + ) +} + struct Chgrper { dest_gid: gid_t, bit_flag: u8, diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index 2d5787099..d89827c97 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -61,11 +61,64 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let after_help = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&after_help[..]) + .get_matches_from(args); + + let changes = matches.is_present(options::CHANGES); + let quiet = matches.is_present(options::QUIET); + let verbose = matches.is_present(options::VERBOSE); + let preserve_root = matches.is_present(options::PRESERVE_ROOT); + let recursive = matches.is_present(options::RECURSIVE); + let fmode = matches + .value_of(options::REFERENCE) + .and_then(|fref| match fs::metadata(fref) { + Ok(meta) => Some(meta.mode()), + Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err), + }); + let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required + let cmode = if mode_had_minus_prefix { + // clap parsing is finished, now put prefix back + format!("-{}", modes) + } else { + modes.to_string() + }; + let mut files: Vec = matches + .values_of(options::FILE) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + let cmode = if fmode.is_some() { + // "--reference" and MODE are mutually exclusive + // if "--reference" was used MODE needs to be interpreted as another FILE + // it wasn't possible to implement this behavior directly with clap + files.push(cmode); + None + } else { + Some(cmode) + }; + + let chmoder = Chmoder { + changes, + quiet, + verbose, + preserve_root, + recursive, + fmode, + cmode, + }; + match chmoder.chmod(files) { + Ok(()) => {} + Err(e) => return e, + } + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) .arg( Arg::with_name(options::CHANGES) .long(options::CHANGES) @@ -120,55 +173,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .required_unless(options::MODE) .multiple(true), ) - .get_matches_from(args); - - let changes = matches.is_present(options::CHANGES); - let quiet = matches.is_present(options::QUIET); - let verbose = matches.is_present(options::VERBOSE); - let preserve_root = matches.is_present(options::PRESERVE_ROOT); - let recursive = matches.is_present(options::RECURSIVE); - let fmode = matches - .value_of(options::REFERENCE) - .and_then(|fref| match fs::metadata(fref) { - Ok(meta) => Some(meta.mode()), - Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err), - }); - let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required - let cmode = if mode_had_minus_prefix { - // clap parsing is finished, now put prefix back - format!("-{}", modes) - } else { - modes.to_string() - }; - let mut files: Vec = matches - .values_of(options::FILE) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - let cmode = if fmode.is_some() { - // "--reference" and MODE are mutually exclusive - // if "--reference" was used MODE needs to be interpreted as another FILE - // it wasn't possible to implement this behavior directly with clap - files.push(cmode); - None - } else { - Some(cmode) - }; - - let chmoder = Chmoder { - changes, - quiet, - verbose, - preserve_root, - recursive, - fmode, - cmode, - }; - match chmoder.chmod(files) { - Ok(()) => {} - Err(e) => return e, - } - - 0 } // Iterate 'args' and delete the first occurrence diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 7fc7f04d3..e1d3ff22b 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -73,101 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::verbosity::CHANGES) - .short("c") - .long(options::verbosity::CHANGES) - .help("like verbose but report only when a change is made"), - ) - .arg(Arg::with_name(options::dereference::DEREFERENCE).long(options::dereference::DEREFERENCE).help( - "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself", - )) - .arg( - Arg::with_name(options::dereference::NO_DEREFERENCE) - .short("h") - .long(options::dereference::NO_DEREFERENCE) - .help( - "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)", - ), - ) - .arg( - Arg::with_name(options::FROM) - .long(options::FROM) - .help( - "change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute", - ) - .value_name("CURRENT_OWNER:CURRENT_GROUP"), - ) - .arg( - Arg::with_name(options::preserve_root::PRESERVE) - .long(options::preserve_root::PRESERVE) - .help("fail to operate recursively on '/'"), - ) - .arg( - Arg::with_name(options::preserve_root::NO_PRESERVE) - .long(options::preserve_root::NO_PRESERVE) - .help("do not treat '/' specially (the default)"), - ) - .arg( - Arg::with_name(options::verbosity::QUIET) - .long(options::verbosity::QUIET) - .help("suppress most error messages"), - ) - .arg( - Arg::with_name(options::RECURSIVE) - .short("R") - .long(options::RECURSIVE) - .help("operate on files and directories recursively"), - ) - .arg( - Arg::with_name(options::REFERENCE) - .long(options::REFERENCE) - .help("use RFILE's owner and group rather than specifying OWNER:GROUP values") - .value_name("RFILE") - .min_values(1), - ) - .arg(Arg::with_name(options::verbosity::SILENT).short("f").long(options::verbosity::SILENT)) - .arg( - Arg::with_name(options::traverse::TRAVERSE) - .short(options::traverse::TRAVERSE) - .help("if a command line argument is a symbolic link to a directory, traverse it") - .overrides_with_all(&[options::traverse::EVERY, options::traverse::NO_TRAVERSE]), - ) - .arg( - Arg::with_name(options::traverse::EVERY) - .short(options::traverse::EVERY) - .help("traverse every symbolic link to a directory encountered") - .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::NO_TRAVERSE]), - ) - .arg( - Arg::with_name(options::traverse::NO_TRAVERSE) - .short(options::traverse::NO_TRAVERSE) - .help("do not traverse any symbolic links (default)") - .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]), - ) - .arg( - Arg::with_name(options::verbosity::VERBOSE) - .long(options::verbosity::VERBOSE) - .help("output a diagnostic for every file processed"), - ) - .arg( - Arg::with_name(ARG_OWNER) - .multiple(false) - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name(ARG_FILES) - .multiple(true) - .takes_value(true) - .required(true) - .min_values(1), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); /* First arg is the owner/group */ let owner = matches.value_of(ARG_OWNER).unwrap(); @@ -273,6 +179,102 @@ pub fn uumain(args: impl uucore::Args) -> i32 { executor.exec() } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::verbosity::CHANGES) + .short("c") + .long(options::verbosity::CHANGES) + .help("like verbose but report only when a change is made"), + ) + .arg(Arg::with_name(options::dereference::DEREFERENCE).long(options::dereference::DEREFERENCE).help( + "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself", + )) + .arg( + Arg::with_name(options::dereference::NO_DEREFERENCE) + .short("h") + .long(options::dereference::NO_DEREFERENCE) + .help( + "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)", + ), + ) + .arg( + Arg::with_name(options::FROM) + .long(options::FROM) + .help( + "change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute", + ) + .value_name("CURRENT_OWNER:CURRENT_GROUP"), + ) + .arg( + Arg::with_name(options::preserve_root::PRESERVE) + .long(options::preserve_root::PRESERVE) + .help("fail to operate recursively on '/'"), + ) + .arg( + Arg::with_name(options::preserve_root::NO_PRESERVE) + .long(options::preserve_root::NO_PRESERVE) + .help("do not treat '/' specially (the default)"), + ) + .arg( + Arg::with_name(options::verbosity::QUIET) + .long(options::verbosity::QUIET) + .help("suppress most error messages"), + ) + .arg( + Arg::with_name(options::RECURSIVE) + .short("R") + .long(options::RECURSIVE) + .help("operate on files and directories recursively"), + ) + .arg( + Arg::with_name(options::REFERENCE) + .long(options::REFERENCE) + .help("use RFILE's owner and group rather than specifying OWNER:GROUP values") + .value_name("RFILE") + .min_values(1), + ) + .arg(Arg::with_name(options::verbosity::SILENT).short("f").long(options::verbosity::SILENT)) + .arg( + Arg::with_name(options::traverse::TRAVERSE) + .short(options::traverse::TRAVERSE) + .help("if a command line argument is a symbolic link to a directory, traverse it") + .overrides_with_all(&[options::traverse::EVERY, options::traverse::NO_TRAVERSE]), + ) + .arg( + Arg::with_name(options::traverse::EVERY) + .short(options::traverse::EVERY) + .help("traverse every symbolic link to a directory encountered") + .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::NO_TRAVERSE]), + ) + .arg( + Arg::with_name(options::traverse::NO_TRAVERSE) + .short(options::traverse::NO_TRAVERSE) + .help("do not traverse any symbolic links (default)") + .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]), + ) + .arg( + Arg::with_name(options::verbosity::VERBOSE) + .long(options::verbosity::VERBOSE) + .help("output a diagnostic for every file processed"), + ) + .arg( + Arg::with_name(ARG_OWNER) + .multiple(false) + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name(ARG_FILES) + .multiple(true) + .takes_value(true) + .required(true) + .min_values(1), + ) +} + fn parse_spec(spec: &str) -> Result<(Option, Option), String> { let args = spec.split_terminator(':').collect::>(); let usr_only = args.len() == 1 && !args[0].is_empty(); diff --git a/src/uu/chroot/src/chroot.rs b/src/uu/chroot/src/chroot.rs index 86d4a4900..2c0f8522c 100644 --- a/src/uu/chroot/src/chroot.rs +++ b/src/uu/chroot/src/chroot.rs @@ -36,54 +36,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(SYNTAX) - .arg( - Arg::with_name(options::NEWROOT) - .hidden(true) - .required(true) - .index(1), - ) - .arg( - Arg::with_name(options::USER) - .short("u") - .long(options::USER) - .help("User (ID or name) to switch before running the program") - .value_name("USER"), - ) - .arg( - Arg::with_name(options::GROUP) - .short("g") - .long(options::GROUP) - .help("Group (ID or name) to switch to") - .value_name("GROUP"), - ) - .arg( - Arg::with_name(options::GROUPS) - .short("G") - .long(options::GROUPS) - .help("Comma-separated list of groups to switch to") - .value_name("GROUP1,GROUP2..."), - ) - .arg( - Arg::with_name(options::USERSPEC) - .long(options::USERSPEC) - .help( - "Colon-separated user and group to switch to. \ - Same as -u USER -g GROUP. \ - Userspec has higher preference than -u and/or -g", - ) - .value_name("USER:GROUP"), - ) - .arg( - Arg::with_name(options::COMMAND) - .hidden(true) - .multiple(true) - .index(2), - ) - .get_matches_from(args); + let matches = uu_app().get_matches_from(args); let default_shell: &'static str = "/bin/sh"; let default_option: &'static str = "-i"; @@ -138,6 +91,56 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .usage(SYNTAX) + .arg( + Arg::with_name(options::NEWROOT) + .hidden(true) + .required(true) + .index(1), + ) + .arg( + Arg::with_name(options::USER) + .short("u") + .long(options::USER) + .help("User (ID or name) to switch before running the program") + .value_name("USER"), + ) + .arg( + Arg::with_name(options::GROUP) + .short("g") + .long(options::GROUP) + .help("Group (ID or name) to switch to") + .value_name("GROUP"), + ) + .arg( + Arg::with_name(options::GROUPS) + .short("G") + .long(options::GROUPS) + .help("Comma-separated list of groups to switch to") + .value_name("GROUP1,GROUP2..."), + ) + .arg( + Arg::with_name(options::USERSPEC) + .long(options::USERSPEC) + .help( + "Colon-separated user and group to switch to. \ + Same as -u USER -g GROUP. \ + Userspec has higher preference than -u and/or -g", + ) + .value_name("USER:GROUP"), + ) + .arg( + Arg::with_name(options::COMMAND) + .hidden(true) + .multiple(true) + .index(2), + ) +} + fn set_context(root: &Path, options: &clap::ArgMatches) { let userspec_str = options.value_of(options::USERSPEC); let user_str = options.value_of(options::USER).unwrap_or_default(); diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 6a812c186..e88cc78b3 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -180,13 +180,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); - let matches = App::new(executable!()) - .name(NAME) - .version(crate_version!()) - .about(SUMMARY) - .usage(SYNTAX) - .arg(Arg::with_name(options::FILE).hidden(true).multiple(true)) - .get_matches_from(args); + let matches = uu_app().get_matches_from(args); let files: Vec = match matches.values_of(options::FILE) { Some(v) => v.clone().map(|v| v.to_owned()).collect(), @@ -217,3 +211,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { exit_code } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .name(NAME) + .version(crate_version!()) + .about(SUMMARY) + .usage(SYNTAX) + .arg(Arg::with_name(options::FILE).hidden(true).multiple(true)) +} diff --git a/src/uu/comm/src/comm.rs b/src/uu/comm/src/comm.rs index 7a6086bb5..aa10432a2 100644 --- a/src/uu/comm/src/comm.rs +++ b/src/uu/comm/src/comm.rs @@ -137,10 +137,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap(); + let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap(); + + comm(&mut f1, &mut f2, &matches); + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .after_help(LONG_HELP) .arg( Arg::with_name(options::COLUMN_1) @@ -167,12 +177,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 { ) .arg(Arg::with_name(options::FILE_1).required(true)) .arg(Arg::with_name(options::FILE_2).required(true)) - .get_matches_from(args); - - let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap(); - let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap(); - - comm(&mut f1, &mut f2, &matches); - - 0 } diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index e702179ec..12dfeab3f 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -290,13 +290,10 @@ static DEFAULT_ATTRIBUTES: &[Attribute] = &[ Attribute::Timestamps, ]; -pub fn uumain(args: impl uucore::Args) -> i32 { - let usage = get_usage(); - let matches = App::new(executable!()) +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .after_help(&*format!("{}\n{}", LONG_HELP, backup_control::BACKUP_CONTROL_LONG_HELP)) - .usage(&usage[..]) .arg(Arg::with_name(options::TARGET_DIRECTORY) .short("t") .conflicts_with(options::NO_TARGET_DIRECTORY) @@ -464,6 +461,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .arg(Arg::with_name(options::PATHS) .multiple(true)) +} + +pub fn uumain(args: impl uucore::Args) -> i32 { + let usage = get_usage(); + let matches = uu_app() + .after_help(&*format!( + "{}\n{}", + LONG_HELP, + backup_control::BACKUP_CONTROL_LONG_HELP + )) + .usage(&usage[..]) .get_matches_from(args); let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches)); diff --git a/src/uu/csplit/src/csplit.rs b/src/uu/csplit/src/csplit.rs index d69254a3a..048ec80d8 100644 --- a/src/uu/csplit/src/csplit.rs +++ b/src/uu/csplit/src/csplit.rs @@ -711,10 +711,37 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + // get the file to split + let file_name = matches.value_of(options::FILE).unwrap(); + + // get the patterns to split on + let patterns: Vec = matches + .values_of(options::PATTERN) + .unwrap() + .map(str::to_string) + .collect(); + let patterns = return_if_err!(1, patterns::get_patterns(&patterns[..])); + let options = CsplitOptions::new(&matches); + if file_name == "-" { + let stdin = io::stdin(); + crash_if_err!(1, csplit(&options, patterns, stdin.lock())); + } else { + let file = return_if_err!(1, File::open(file_name)); + let file_metadata = return_if_err!(1, file.metadata()); + if !file_metadata.is_file() { + crash!(1, "'{}' is not a regular file", file_name); + } + crash_if_err!(1, csplit(&options, patterns, BufReader::new(file))); + }; + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(SUMMARY) - .usage(&usage[..]) .arg( Arg::with_name(options::SUFFIX_FORMAT) .short("b") @@ -768,29 +795,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .required(true), ) .after_help(LONG_HELP) - .get_matches_from(args); - - // get the file to split - let file_name = matches.value_of(options::FILE).unwrap(); - - // get the patterns to split on - let patterns: Vec = matches - .values_of(options::PATTERN) - .unwrap() - .map(str::to_string) - .collect(); - let patterns = return_if_err!(1, patterns::get_patterns(&patterns[..])); - let options = CsplitOptions::new(&matches); - if file_name == "-" { - let stdin = io::stdin(); - crash_if_err!(1, csplit(&options, patterns, stdin.lock())); - } else { - let file = return_if_err!(1, File::open(file_name)); - let file_metadata = return_if_err!(1, file.metadata()); - if !file_metadata.is_file() { - crash!(1, "'{}' is not a regular file", file_name); - } - crash_if_err!(1, csplit(&options, patterns, BufReader::new(file))); - }; - 0 } diff --git a/src/uu/cut/src/cut.rs b/src/uu/cut/src/cut.rs index 6602b1eb1..e33b8a2fe 100644 --- a/src/uu/cut/src/cut.rs +++ b/src/uu/cut/src/cut.rs @@ -396,88 +396,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); - let matches = App::new(executable!()) - .name(NAME) - .version(crate_version!()) - .usage(SYNTAX) - .about(SUMMARY) - .after_help(LONG_HELP) - .arg( - Arg::with_name(options::BYTES) - .short("b") - .long(options::BYTES) - .takes_value(true) - .help("filter byte columns from the input source") - .allow_hyphen_values(true) - .value_name("LIST") - .display_order(1), - ) - .arg( - Arg::with_name(options::CHARACTERS) - .short("c") - .long(options::CHARACTERS) - .help("alias for character mode") - .takes_value(true) - .allow_hyphen_values(true) - .value_name("LIST") - .display_order(2), - ) - .arg( - Arg::with_name(options::DELIMITER) - .short("d") - .long(options::DELIMITER) - .help("specify the delimiter character that separates fields in the input source. Defaults to Tab.") - .takes_value(true) - .value_name("DELIM") - .display_order(3), - ) - .arg( - Arg::with_name(options::FIELDS) - .short("f") - .long(options::FIELDS) - .help("filter field columns from the input source") - .takes_value(true) - .allow_hyphen_values(true) - .value_name("LIST") - .display_order(4), - ) - .arg( - Arg::with_name(options::COMPLEMENT) - .long(options::COMPLEMENT) - .help("invert the filter - instead of displaying only the filtered columns, display all but those columns") - .takes_value(false) - .display_order(5), - ) - .arg( - Arg::with_name(options::ONLY_DELIMITED) - .short("s") - .long(options::ONLY_DELIMITED) - .help("in field mode, only print lines which contain the delimiter") - .takes_value(false) - .display_order(6), - ) - .arg( - Arg::with_name(options::ZERO_TERMINATED) - .short("z") - .long(options::ZERO_TERMINATED) - .help("instead of filtering columns based on line, filter columns based on \\0 (NULL character)") - .takes_value(false) - .display_order(8), - ) - .arg( - Arg::with_name(options::OUTPUT_DELIMITER) - .long(options::OUTPUT_DELIMITER) - .help("in field mode, replace the delimiter in output lines with this option's argument") - .takes_value(true) - .value_name("NEW_DELIM") - .display_order(7), - ) - .arg( - Arg::with_name(options::FILE) - .hidden(true) - .multiple(true) - ) - .get_matches_from(args); + let matches = uu_app().get_matches_from(args); let complement = matches.is_present(options::COMPLEMENT); @@ -627,3 +546,87 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .name(NAME) + .version(crate_version!()) + .usage(SYNTAX) + .about(SUMMARY) + .after_help(LONG_HELP) + .arg( + Arg::with_name(options::BYTES) + .short("b") + .long(options::BYTES) + .takes_value(true) + .help("filter byte columns from the input source") + .allow_hyphen_values(true) + .value_name("LIST") + .display_order(1), + ) + .arg( + Arg::with_name(options::CHARACTERS) + .short("c") + .long(options::CHARACTERS) + .help("alias for character mode") + .takes_value(true) + .allow_hyphen_values(true) + .value_name("LIST") + .display_order(2), + ) + .arg( + Arg::with_name(options::DELIMITER) + .short("d") + .long(options::DELIMITER) + .help("specify the delimiter character that separates fields in the input source. Defaults to Tab.") + .takes_value(true) + .value_name("DELIM") + .display_order(3), + ) + .arg( + Arg::with_name(options::FIELDS) + .short("f") + .long(options::FIELDS) + .help("filter field columns from the input source") + .takes_value(true) + .allow_hyphen_values(true) + .value_name("LIST") + .display_order(4), + ) + .arg( + Arg::with_name(options::COMPLEMENT) + .long(options::COMPLEMENT) + .help("invert the filter - instead of displaying only the filtered columns, display all but those columns") + .takes_value(false) + .display_order(5), + ) + .arg( + Arg::with_name(options::ONLY_DELIMITED) + .short("s") + .long(options::ONLY_DELIMITED) + .help("in field mode, only print lines which contain the delimiter") + .takes_value(false) + .display_order(6), + ) + .arg( + Arg::with_name(options::ZERO_TERMINATED) + .short("z") + .long(options::ZERO_TERMINATED) + .help("instead of filtering columns based on line, filter columns based on \\0 (NULL character)") + .takes_value(false) + .display_order(8), + ) + .arg( + Arg::with_name(options::OUTPUT_DELIMITER) + .long(options::OUTPUT_DELIMITER) + .help("in field mode, replace the delimiter in output lines with this option's argument") + .takes_value(true) + .value_name("NEW_DELIM") + .display_order(7), + ) + .arg( + Arg::with_name(options::FILE) + .hidden(true) + .multiple(true) + ) +} diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index 11c3eb31f..0071b5e8c 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -142,71 +142,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { {0} [OPTION]... [MMDDhhmm[[CC]YY][.ss]]", NAME ); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&syntax[..]) - .arg( - Arg::with_name(OPT_DATE) - .short("d") - .long(OPT_DATE) - .takes_value(true) - .help("display time described by STRING, not 'now'"), - ) - .arg( - Arg::with_name(OPT_FILE) - .short("f") - .long(OPT_FILE) - .takes_value(true) - .help("like --date; once for each line of DATEFILE"), - ) - .arg( - Arg::with_name(OPT_ISO_8601) - .short("I") - .long(OPT_ISO_8601) - .takes_value(true) - .help(ISO_8601_HELP_STRING), - ) - .arg( - Arg::with_name(OPT_RFC_EMAIL) - .short("R") - .long(OPT_RFC_EMAIL) - .help(RFC_5322_HELP_STRING), - ) - .arg( - Arg::with_name(OPT_RFC_3339) - .long(OPT_RFC_3339) - .takes_value(true) - .help(RFC_3339_HELP_STRING), - ) - .arg( - Arg::with_name(OPT_DEBUG) - .long(OPT_DEBUG) - .help("annotate the parsed date, and warn about questionable usage to stderr"), - ) - .arg( - Arg::with_name(OPT_REFERENCE) - .short("r") - .long(OPT_REFERENCE) - .takes_value(true) - .help("display the last modification time of FILE"), - ) - .arg( - Arg::with_name(OPT_SET) - .short("s") - .long(OPT_SET) - .takes_value(true) - .help(OPT_SET_HELP_STRING), - ) - .arg( - Arg::with_name(OPT_UNIVERSAL) - .short("u") - .long(OPT_UNIVERSAL) - .alias(OPT_UNIVERSAL_2) - .help("print or set Coordinated Universal Time (UTC)"), - ) - .arg(Arg::with_name(OPT_FORMAT).multiple(false)) - .get_matches_from(args); + let matches = uu_app().usage(&syntax[..]).get_matches_from(args); let format = if let Some(form) = matches.value_of(OPT_FORMAT) { if !form.starts_with('+') { @@ -314,6 +250,72 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_DATE) + .short("d") + .long(OPT_DATE) + .takes_value(true) + .help("display time described by STRING, not 'now'"), + ) + .arg( + Arg::with_name(OPT_FILE) + .short("f") + .long(OPT_FILE) + .takes_value(true) + .help("like --date; once for each line of DATEFILE"), + ) + .arg( + Arg::with_name(OPT_ISO_8601) + .short("I") + .long(OPT_ISO_8601) + .takes_value(true) + .help(ISO_8601_HELP_STRING), + ) + .arg( + Arg::with_name(OPT_RFC_EMAIL) + .short("R") + .long(OPT_RFC_EMAIL) + .help(RFC_5322_HELP_STRING), + ) + .arg( + Arg::with_name(OPT_RFC_3339) + .long(OPT_RFC_3339) + .takes_value(true) + .help(RFC_3339_HELP_STRING), + ) + .arg( + Arg::with_name(OPT_DEBUG) + .long(OPT_DEBUG) + .help("annotate the parsed date, and warn about questionable usage to stderr"), + ) + .arg( + Arg::with_name(OPT_REFERENCE) + .short("r") + .long(OPT_REFERENCE) + .takes_value(true) + .help("display the last modification time of FILE"), + ) + .arg( + Arg::with_name(OPT_SET) + .short("s") + .long(OPT_SET) + .takes_value(true) + .help(OPT_SET_HELP_STRING), + ) + .arg( + Arg::with_name(OPT_UNIVERSAL) + .short("u") + .long(OPT_UNIVERSAL) + .alias(OPT_UNIVERSAL_2) + .help("print or set Coordinated Universal Time (UTC)"), + ) + .arg(Arg::with_name(OPT_FORMAT).multiple(false)) +} + /// Return the appropriate format string for the given settings. fn make_format_string(settings: &Settings) -> &str { match settings.format { diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 0836aa43d..1092938df 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -258,120 +258,7 @@ fn use_size(free_size: u64, total_size: u64) -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(OPT_ALL) - .short("a") - .long("all") - .help("include dummy file systems"), - ) - .arg( - Arg::with_name(OPT_BLOCKSIZE) - .short("B") - .long("block-size") - .takes_value(true) - .help( - "scale sizes by SIZE before printing them; e.g.\ - '-BM' prints sizes in units of 1,048,576 bytes", - ), - ) - .arg( - Arg::with_name(OPT_DIRECT) - .long("direct") - .help("show statistics for a file instead of mount point"), - ) - .arg( - Arg::with_name(OPT_TOTAL) - .long("total") - .help("produce a grand total"), - ) - .arg( - Arg::with_name(OPT_HUMAN_READABLE) - .short("h") - .long("human-readable") - .conflicts_with(OPT_HUMAN_READABLE_2) - .help("print sizes in human readable format (e.g., 1K 234M 2G)"), - ) - .arg( - Arg::with_name(OPT_HUMAN_READABLE_2) - .short("H") - .long("si") - .conflicts_with(OPT_HUMAN_READABLE) - .help("likewise, but use powers of 1000 not 1024"), - ) - .arg( - Arg::with_name(OPT_INODES) - .short("i") - .long("inodes") - .help("list inode information instead of block usage"), - ) - .arg( - Arg::with_name(OPT_KILO) - .short("k") - .help("like --block-size=1K"), - ) - .arg( - Arg::with_name(OPT_LOCAL) - .short("l") - .long("local") - .help("limit listing to local file systems"), - ) - .arg( - Arg::with_name(OPT_NO_SYNC) - .long("no-sync") - .conflicts_with(OPT_SYNC) - .help("do not invoke sync before getting usage info (default)"), - ) - .arg( - Arg::with_name(OPT_OUTPUT) - .long("output") - .takes_value(true) - .use_delimiter(true) - .help( - "use the output format defined by FIELD_LIST,\ - or print all fields if FIELD_LIST is omitted.", - ), - ) - .arg( - Arg::with_name(OPT_PORTABILITY) - .short("P") - .long("portability") - .help("use the POSIX output format"), - ) - .arg( - Arg::with_name(OPT_SYNC) - .long("sync") - .conflicts_with(OPT_NO_SYNC) - .help("invoke sync before getting usage info"), - ) - .arg( - Arg::with_name(OPT_TYPE) - .short("t") - .long("type") - .takes_value(true) - .use_delimiter(true) - .help("limit listing to file systems of type TYPE"), - ) - .arg( - Arg::with_name(OPT_PRINT_TYPE) - .short("T") - .long("print-type") - .help("print file system type"), - ) - .arg( - Arg::with_name(OPT_EXCLUDE_TYPE) - .short("x") - .long("exclude-type") - .takes_value(true) - .use_delimiter(true) - .help("limit listing to file systems not of type TYPE"), - ) - .arg(Arg::with_name(OPT_PATHS).multiple(true)) - .help("Filesystem(s) to list") - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let paths: Vec = matches .values_of(OPT_PATHS) @@ -511,3 +398,118 @@ pub fn uumain(args: impl uucore::Args) -> i32 { EXIT_OK } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_ALL) + .short("a") + .long("all") + .help("include dummy file systems"), + ) + .arg( + Arg::with_name(OPT_BLOCKSIZE) + .short("B") + .long("block-size") + .takes_value(true) + .help( + "scale sizes by SIZE before printing them; e.g.\ + '-BM' prints sizes in units of 1,048,576 bytes", + ), + ) + .arg( + Arg::with_name(OPT_DIRECT) + .long("direct") + .help("show statistics for a file instead of mount point"), + ) + .arg( + Arg::with_name(OPT_TOTAL) + .long("total") + .help("produce a grand total"), + ) + .arg( + Arg::with_name(OPT_HUMAN_READABLE) + .short("h") + .long("human-readable") + .conflicts_with(OPT_HUMAN_READABLE_2) + .help("print sizes in human readable format (e.g., 1K 234M 2G)"), + ) + .arg( + Arg::with_name(OPT_HUMAN_READABLE_2) + .short("H") + .long("si") + .conflicts_with(OPT_HUMAN_READABLE) + .help("likewise, but use powers of 1000 not 1024"), + ) + .arg( + Arg::with_name(OPT_INODES) + .short("i") + .long("inodes") + .help("list inode information instead of block usage"), + ) + .arg( + Arg::with_name(OPT_KILO) + .short("k") + .help("like --block-size=1K"), + ) + .arg( + Arg::with_name(OPT_LOCAL) + .short("l") + .long("local") + .help("limit listing to local file systems"), + ) + .arg( + Arg::with_name(OPT_NO_SYNC) + .long("no-sync") + .conflicts_with(OPT_SYNC) + .help("do not invoke sync before getting usage info (default)"), + ) + .arg( + Arg::with_name(OPT_OUTPUT) + .long("output") + .takes_value(true) + .use_delimiter(true) + .help( + "use the output format defined by FIELD_LIST,\ + or print all fields if FIELD_LIST is omitted.", + ), + ) + .arg( + Arg::with_name(OPT_PORTABILITY) + .short("P") + .long("portability") + .help("use the POSIX output format"), + ) + .arg( + Arg::with_name(OPT_SYNC) + .long("sync") + .conflicts_with(OPT_NO_SYNC) + .help("invoke sync before getting usage info"), + ) + .arg( + Arg::with_name(OPT_TYPE) + .short("t") + .long("type") + .takes_value(true) + .use_delimiter(true) + .help("limit listing to file systems of type TYPE"), + ) + .arg( + Arg::with_name(OPT_PRINT_TYPE) + .short("T") + .long("print-type") + .help("print file system type"), + ) + .arg( + Arg::with_name(OPT_EXCLUDE_TYPE) + .short("x") + .long("exclude-type") + .takes_value(true) + .use_delimiter(true) + .help("limit listing to file systems not of type TYPE"), + ) + .arg(Arg::with_name(OPT_PATHS).multiple(true)) + .help("Filesystem(s) to list") +} diff --git a/src/uu/dircolors/src/dircolors.rs b/src/uu/dircolors/src/dircolors.rs index 3200a331f..70b609e31 100644 --- a/src/uu/dircolors/src/dircolors.rs +++ b/src/uu/dircolors/src/dircolors.rs @@ -73,36 +73,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(SUMMARY) - .usage(&usage[..]) - .after_help(LONG_HELP) - .arg( - Arg::with_name(options::BOURNE_SHELL) - .long("sh") - .short("b") - .visible_alias("bourne-shell") - .help("output Bourne shell code to set LS_COLORS") - .display_order(1), - ) - .arg( - Arg::with_name(options::C_SHELL) - .long("csh") - .short("c") - .visible_alias("c-shell") - .help("output C shell code to set LS_COLORS") - .display_order(2), - ) - .arg( - Arg::with_name(options::PRINT_DATABASE) - .long("print-database") - .short("p") - .help("print the byte counts") - .display_order(3), - ) - .arg(Arg::with_name(options::FILE).hidden(true).multiple(true)) - .get_matches_from(&args); + let matches = uu_app().usage(&usage[..]).get_matches_from(&args); let files = matches .values_of(options::FILE) @@ -181,6 +152,37 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(SUMMARY) + .after_help(LONG_HELP) + .arg( + Arg::with_name(options::BOURNE_SHELL) + .long("sh") + .short("b") + .visible_alias("bourne-shell") + .help("output Bourne shell code to set LS_COLORS") + .display_order(1), + ) + .arg( + Arg::with_name(options::C_SHELL) + .long("csh") + .short("c") + .visible_alias("c-shell") + .help("output C shell code to set LS_COLORS") + .display_order(2), + ) + .arg( + Arg::with_name(options::PRINT_DATABASE) + .long("print-database") + .short("p") + .help("print the byte counts") + .display_order(3), + ) + .arg(Arg::with_name(options::FILE).hidden(true).multiple(true)) +} + pub trait StrUtils { /// Remove comments and trim whitespace fn purify(&self) -> &Self; diff --git a/src/uu/dirname/src/dirname.rs b/src/uu/dirname/src/dirname.rs index ad42517d4..356f2e6b1 100644 --- a/src/uu/dirname/src/dirname.rs +++ b/src/uu/dirname/src/dirname.rs @@ -38,18 +38,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let after_help = get_long_usage(); - let matches = App::new(executable!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&after_help[..]) - .version(crate_version!()) - .arg( - Arg::with_name(options::ZERO) - .long(options::ZERO) - .short("z") - .help("separate output with NUL rather than newline"), - ) - .arg(Arg::with_name(options::DIR).hidden(true).multiple(true)) .get_matches_from(args); let separator = if matches.is_present(options::ZERO) { @@ -92,3 +83,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .about(ABOUT) + .version(crate_version!()) + .arg( + Arg::with_name(options::ZERO) + .long(options::ZERO) + .short("z") + .help("separate output with NUL rather than newline"), + ) + .arg(Arg::with_name(options::DIR).hidden(true).multiple(true)) +} diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index 623faf62c..e5f0e6efc 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -387,182 +387,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(SUMMARY) - .usage(&usage[..]) - .after_help(LONG_HELP) - .arg( - Arg::with_name(options::ALL) - .short("a") - .long(options::ALL) - .help("write counts for all files, not just directories"), - ) - .arg( - Arg::with_name(options::APPARENT_SIZE) - .long(options::APPARENT_SIZE) - .help( - "print apparent sizes, rather than disk usage \ - although the apparent size is usually smaller, it may be larger due to holes \ - in ('sparse') files, internal fragmentation, indirect blocks, and the like" - ) - .alias("app") // The GNU test suite uses this alias - ) - .arg( - Arg::with_name(options::BLOCK_SIZE) - .short("B") - .long(options::BLOCK_SIZE) - .value_name("SIZE") - .help( - "scale sizes by SIZE before printing them. \ - E.g., '-BM' prints sizes in units of 1,048,576 bytes. See SIZE format below." - ) - ) - .arg( - Arg::with_name(options::BYTES) - .short("b") - .long("bytes") - .help("equivalent to '--apparent-size --block-size=1'") - ) - .arg( - Arg::with_name(options::TOTAL) - .long("total") - .short("c") - .help("produce a grand total") - ) - .arg( - Arg::with_name(options::MAX_DEPTH) - .short("d") - .long("max-depth") - .value_name("N") - .help( - "print the total for a directory (or file, with --all) \ - only if it is N or fewer levels below the command \ - line argument; --max-depth=0 is the same as --summarize" - ) - ) - .arg( - Arg::with_name(options::HUMAN_READABLE) - .long("human-readable") - .short("h") - .help("print sizes in human readable format (e.g., 1K 234M 2G)") - ) - .arg( - Arg::with_name("inodes") - .long("inodes") - .help( - "list inode usage information instead of block usage like --block-size=1K" - ) - ) - .arg( - Arg::with_name(options::BLOCK_SIZE_1K) - .short("k") - .help("like --block-size=1K") - ) - .arg( - Arg::with_name(options::COUNT_LINKS) - .short("l") - .long("count-links") - .help("count sizes many times if hard linked") - ) - .arg( - Arg::with_name(options::DEREFERENCE) - .short("L") - .long(options::DEREFERENCE) - .help("dereference all symbolic links") - ) - // .arg( - // Arg::with_name("no-dereference") - // .short("P") - // .long("no-dereference") - // .help("don't follow any symbolic links (this is the default)") - // ) - .arg( - Arg::with_name(options::BLOCK_SIZE_1M) - .short("m") - .help("like --block-size=1M") - ) - .arg( - Arg::with_name(options::NULL) - .short("0") - .long("null") - .help("end each output line with 0 byte rather than newline") - ) - .arg( - Arg::with_name(options::SEPARATE_DIRS) - .short("S") - .long("separate-dirs") - .help("do not include size of subdirectories") - ) - .arg( - Arg::with_name(options::SUMMARIZE) - .short("s") - .long("summarize") - .help("display only a total for each argument") - ) - .arg( - Arg::with_name(options::SI) - .long(options::SI) - .help("like -h, but use powers of 1000 not 1024") - ) - .arg( - Arg::with_name(options::ONE_FILE_SYSTEM) - .short("x") - .long(options::ONE_FILE_SYSTEM) - .help("skip directories on different file systems") - ) - .arg( - Arg::with_name(options::THRESHOLD) - .short("t") - .long(options::THRESHOLD) - .alias("th") - .value_name("SIZE") - .number_of_values(1) - .allow_hyphen_values(true) - .help("exclude entries smaller than SIZE if positive, \ - or entries greater than SIZE if negative") - ) - // .arg( - // Arg::with_name("") - // .short("x") - // .long("exclude-from") - // .value_name("FILE") - // .help("exclude files that match any pattern in FILE") - // ) - // .arg( - // Arg::with_name("exclude") - // .long("exclude") - // .value_name("PATTERN") - // .help("exclude files that match PATTERN") - // ) - .arg( - Arg::with_name(options::TIME) - .long(options::TIME) - .value_name("WORD") - .require_equals(true) - .min_values(0) - .possible_values(&["atime", "access", "use", "ctime", "status", "birth", "creation"]) - .help( - "show time of the last modification of any file in the \ - directory, or any of its subdirectories. If WORD is given, show time as WORD instead \ - of modification time: atime, access, use, ctime, status, birth or creation" - ) - ) - .arg( - Arg::with_name(options::TIME_STYLE) - .long(options::TIME_STYLE) - .value_name("STYLE") - .help( - "show times using style STYLE: \ - full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'" - ) - ) - .arg( - Arg::with_name(options::FILE) - .hidden(true) - .multiple(true) - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let summarize = matches.is_present(options::SUMMARIZE); @@ -743,6 +568,183 @@ Try '{} --help' for more information.", 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(SUMMARY) + .after_help(LONG_HELP) + .arg( + Arg::with_name(options::ALL) + .short("a") + .long(options::ALL) + .help("write counts for all files, not just directories"), + ) + .arg( + Arg::with_name(options::APPARENT_SIZE) + .long(options::APPARENT_SIZE) + .help( + "print apparent sizes, rather than disk usage \ + although the apparent size is usually smaller, it may be larger due to holes \ + in ('sparse') files, internal fragmentation, indirect blocks, and the like" + ) + .alias("app") // The GNU test suite uses this alias + ) + .arg( + Arg::with_name(options::BLOCK_SIZE) + .short("B") + .long(options::BLOCK_SIZE) + .value_name("SIZE") + .help( + "scale sizes by SIZE before printing them. \ + E.g., '-BM' prints sizes in units of 1,048,576 bytes. See SIZE format below." + ) + ) + .arg( + Arg::with_name(options::BYTES) + .short("b") + .long("bytes") + .help("equivalent to '--apparent-size --block-size=1'") + ) + .arg( + Arg::with_name(options::TOTAL) + .long("total") + .short("c") + .help("produce a grand total") + ) + .arg( + Arg::with_name(options::MAX_DEPTH) + .short("d") + .long("max-depth") + .value_name("N") + .help( + "print the total for a directory (or file, with --all) \ + only if it is N or fewer levels below the command \ + line argument; --max-depth=0 is the same as --summarize" + ) + ) + .arg( + Arg::with_name(options::HUMAN_READABLE) + .long("human-readable") + .short("h") + .help("print sizes in human readable format (e.g., 1K 234M 2G)") + ) + .arg( + Arg::with_name("inodes") + .long("inodes") + .help( + "list inode usage information instead of block usage like --block-size=1K" + ) + ) + .arg( + Arg::with_name(options::BLOCK_SIZE_1K) + .short("k") + .help("like --block-size=1K") + ) + .arg( + Arg::with_name(options::COUNT_LINKS) + .short("l") + .long("count-links") + .help("count sizes many times if hard linked") + ) + .arg( + Arg::with_name(options::DEREFERENCE) + .short("L") + .long(options::DEREFERENCE) + .help("dereference all symbolic links") + ) + // .arg( + // Arg::with_name("no-dereference") + // .short("P") + // .long("no-dereference") + // .help("don't follow any symbolic links (this is the default)") + // ) + .arg( + Arg::with_name(options::BLOCK_SIZE_1M) + .short("m") + .help("like --block-size=1M") + ) + .arg( + Arg::with_name(options::NULL) + .short("0") + .long("null") + .help("end each output line with 0 byte rather than newline") + ) + .arg( + Arg::with_name(options::SEPARATE_DIRS) + .short("S") + .long("separate-dirs") + .help("do not include size of subdirectories") + ) + .arg( + Arg::with_name(options::SUMMARIZE) + .short("s") + .long("summarize") + .help("display only a total for each argument") + ) + .arg( + Arg::with_name(options::SI) + .long(options::SI) + .help("like -h, but use powers of 1000 not 1024") + ) + .arg( + Arg::with_name(options::ONE_FILE_SYSTEM) + .short("x") + .long(options::ONE_FILE_SYSTEM) + .help("skip directories on different file systems") + ) + .arg( + Arg::with_name(options::THRESHOLD) + .short("t") + .long(options::THRESHOLD) + .alias("th") + .value_name("SIZE") + .number_of_values(1) + .allow_hyphen_values(true) + .help("exclude entries smaller than SIZE if positive, \ + or entries greater than SIZE if negative") + ) + // .arg( + // Arg::with_name("") + // .short("x") + // .long("exclude-from") + // .value_name("FILE") + // .help("exclude files that match any pattern in FILE") + // ) + // .arg( + // Arg::with_name("exclude") + // .long("exclude") + // .value_name("PATTERN") + // .help("exclude files that match PATTERN") + // ) + .arg( + Arg::with_name(options::TIME) + .long(options::TIME) + .value_name("WORD") + .require_equals(true) + .min_values(0) + .possible_values(&["atime", "access", "use", "ctime", "status", "birth", "creation"]) + .help( + "show time of the last modification of any file in the \ + directory, or any of its subdirectories. If WORD is given, show time as WORD instead \ + of modification time: atime, access, use, ctime, status, birth or creation" + ) + ) + .arg( + Arg::with_name(options::TIME_STYLE) + .long(options::TIME_STYLE) + .value_name("STYLE") + .help( + "show times using style STYLE: \ + full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'" + ) + ) + .arg( + Arg::with_name(options::FILE) + .hidden(true) + .multiple(true) + ) +} + #[derive(Clone, Copy)] enum Threshold { Lower(u64), diff --git a/src/uu/echo/src/echo.rs b/src/uu/echo/src/echo.rs index d83a4fe06..8c976c2b4 100644 --- a/src/uu/echo/src/echo.rs +++ b/src/uu/echo/src/echo.rs @@ -117,7 +117,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let args = args .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + let no_newline = matches.is_present(options::NO_NEWLINE); + let escaped = matches.is_present(options::ENABLE_BACKSLASH_ESCAPE); + let values: Vec = match matches.values_of(options::STRING) { + Some(s) => s.map(|s| s.to_string()).collect(), + None => vec!["".to_string()], + }; + + match execute(no_newline, escaped, values) { + Ok(_) => 0, + Err(f) => { + show_error!("{}", f); + 1 + } + } +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .name(NAME) // TrailingVarArg specifies the final positional argument is a VarArg // and it doesn't attempts the parse any further args. @@ -154,22 +173,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .multiple(true) .allow_hyphen_values(true), ) - .get_matches_from(args); - - let no_newline = matches.is_present(options::NO_NEWLINE); - let escaped = matches.is_present(options::ENABLE_BACKSLASH_ESCAPE); - let values: Vec = match matches.values_of(options::STRING) { - Some(s) => s.map(|s| s.to_string()).collect(), - None => vec!["".to_string()], - }; - - match execute(no_newline, escaped, values) { - Ok(_) => 0, - Err(f) => { - show_error!("{}", f); - 1 - } - } } fn execute(no_newline: bool, escaped: bool, free: Vec) -> io::Result<()> { diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 0ea66d7e9..51ff92801 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -114,7 +114,7 @@ fn build_command<'a, 'b>(args: &'a mut Vec<&'b str>) -> (Cow<'b, str>, &'a [&'b (progname, &args[..]) } -fn create_app() -> App<'static, 'static> { +pub fn uu_app() -> App<'static, 'static> { App::new(crate_name!()) .version(crate_version!()) .author(crate_authors!()) @@ -158,7 +158,7 @@ fn create_app() -> App<'static, 'static> { } fn run_env(args: impl uucore::Args) -> Result<(), i32> { - let app = create_app(); + let app = uu_app(); let matches = app.get_matches_from(args); let ignore_env = matches.is_present("ignore-environment"); diff --git a/src/uu/expand/src/expand.rs b/src/uu/expand/src/expand.rs index d9d669e7c..66c3eb259 100644 --- a/src/uu/expand/src/expand.rs +++ b/src/uu/expand/src/expand.rs @@ -108,10 +108,16 @@ impl Options { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + expand(Options::new(&matches)); + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .after_help(LONG_HELP) .arg( Arg::with_name(options::INITIAL) @@ -138,10 +144,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .hidden(true) .takes_value(true) ) - .get_matches_from(args); - - expand(Options::new(&matches)); - 0 } fn open(path: String) -> BufReader> { diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index ed992bf71..0906856d1 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/expr.rs" [dependencies] +clap = "2.33.3" libc = "0.2.42" num-bigint = "0.4.0" num-traits = "0.2.14" diff --git a/src/uu/expr/src/expr.rs b/src/uu/expr/src/expr.rs index 8238917f7..92c15565d 100644 --- a/src/uu/expr/src/expr.rs +++ b/src/uu/expr/src/expr.rs @@ -8,13 +8,20 @@ #[macro_use] extern crate uucore; +use clap::{crate_version, App, Arg}; use uucore::InvalidEncodingHandling; mod syntax_tree; mod tokens; -static NAME: &str = "expr"; -static VERSION: &str = env!("CARGO_PKG_VERSION"); +const VERSION: &str = "version"; +const HELP: &str = "help"; + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .arg(Arg::with_name(VERSION).long(VERSION)) + .arg(Arg::with_name(HELP).long(HELP)) +} pub fn uumain(args: impl uucore::Args) -> i32 { let args = args @@ -133,5 +140,5 @@ Environment variables: } fn print_version() { - println!("{} {}", NAME, VERSION); + println!("{} {}", executable!(), crate_version!()); } diff --git a/src/uu/factor/src/cli.rs b/src/uu/factor/src/cli.rs index af5e3cdb0..0f5d21362 100644 --- a/src/uu/factor/src/cli.rs +++ b/src/uu/factor/src/cli.rs @@ -36,11 +36,7 @@ fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box i32 { - let matches = App::new(executable!()) - .version(crate_version!()) - .about(SUMMARY) - .arg(Arg::with_name(options::NUMBER).multiple(true)) - .get_matches_from(args); + let matches = uu_app().get_matches_from(args); let stdout = stdout(); let mut w = io::BufWriter::new(stdout.lock()); @@ -68,3 +64,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(SUMMARY) + .arg(Arg::with_name(options::NUMBER).multiple(true)) +} diff --git a/src/uu/false/Cargo.toml b/src/uu/false/Cargo.toml index d7cbcd13a..644051d59 100644 --- a/src/uu/false/Cargo.toml +++ b/src/uu/false/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/false.rs" [dependencies] +clap = "2.33.3" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/false/src/false.rs b/src/uu/false/src/false.rs index 917c43fa0..aaeb6b751 100644 --- a/src/uu/false/src/false.rs +++ b/src/uu/false/src/false.rs @@ -5,6 +5,15 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. +use clap::{App, AppSettings}; +use uucore::executable; + pub fn uumain(_: impl uucore::Args) -> i32 { 1 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .setting(AppSettings::DisableHelpFlags) + .setting(AppSettings::DisableVersion) +} diff --git a/src/uu/fmt/src/fmt.rs b/src/uu/fmt/src/fmt.rs index 91f59e076..9eceaa56c 100644 --- a/src/uu/fmt/src/fmt.rs +++ b/src/uu/fmt/src/fmt.rs @@ -77,129 +77,7 @@ pub struct FmtOptions { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(OPT_CROWN_MARGIN) - .short("c") - .long(OPT_CROWN_MARGIN) - .help( - "First and second line of paragraph - may have different indentations, in which - case the first line's indentation is preserved, - and each subsequent line's indentation matches the second line.", - ), - ) - .arg( - Arg::with_name(OPT_TAGGED_PARAGRAPH) - .short("t") - .long("tagged-paragraph") - .help( - "Like -c, except that the first and second line of a paragraph *must* - have different indentation or they are treated as separate paragraphs.", - ), - ) - .arg( - Arg::with_name(OPT_PRESERVE_HEADERS) - .short("m") - .long("preserve-headers") - .help( - "Attempt to detect and preserve mail headers in the input. - Be careful when combining this flag with -p.", - ), - ) - .arg( - Arg::with_name(OPT_SPLIT_ONLY) - .short("s") - .long("split-only") - .help("Split lines only, do not reflow."), - ) - .arg( - Arg::with_name(OPT_UNIFORM_SPACING) - .short("u") - .long("uniform-spacing") - .help( - "Insert exactly one - space between words, and two between sentences. - Sentence breaks in the input are detected as [?!.] - followed by two spaces or a newline; other punctuation - is not interpreted as a sentence break.", - ), - ) - .arg( - Arg::with_name(OPT_PREFIX) - .short("p") - .long("prefix") - .help( - "Reformat only lines - beginning with PREFIX, reattaching PREFIX to reformatted lines. - Unless -x is specified, leading whitespace will be ignored - when matching PREFIX.", - ) - .value_name("PREFIX"), - ) - .arg( - Arg::with_name(OPT_SKIP_PREFIX) - .short("P") - .long("skip-prefix") - .help( - "Do not reformat lines - beginning with PSKIP. Unless -X is specified, leading whitespace - will be ignored when matching PSKIP", - ) - .value_name("PSKIP"), - ) - .arg( - Arg::with_name(OPT_EXACT_PREFIX) - .short("x") - .long("exact-prefix") - .help( - "PREFIX must match at the - beginning of the line with no preceding whitespace.", - ), - ) - .arg( - Arg::with_name(OPT_EXACT_SKIP_PREFIX) - .short("X") - .long("exact-skip-prefix") - .help( - "PSKIP must match at the - beginning of the line with no preceding whitespace.", - ), - ) - .arg( - Arg::with_name(OPT_WIDTH) - .short("w") - .long("width") - .help("Fill output lines up to a maximum of WIDTH columns, default 79.") - .value_name("WIDTH"), - ) - .arg( - Arg::with_name(OPT_GOAL) - .short("g") - .long("goal") - .help("Goal width, default ~0.94*WIDTH. Must be less than WIDTH.") - .value_name("GOAL"), - ) - .arg(Arg::with_name(OPT_QUICK).short("q").long("quick").help( - "Break lines more quickly at the - expense of a potentially more ragged appearance.", - )) - .arg( - Arg::with_name(OPT_TAB_WIDTH) - .short("T") - .long("tab-width") - .help( - "Treat tabs as TABWIDTH spaces for - determining line length, default 8. Note that this is used only for - calculating line lengths; tabs are preserved in the output.", - ) - .value_name("TABWIDTH"), - ) - .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true)) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let mut files: Vec = matches .values_of(ARG_FILES) @@ -331,3 +209,127 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_CROWN_MARGIN) + .short("c") + .long(OPT_CROWN_MARGIN) + .help( + "First and second line of paragraph + may have different indentations, in which + case the first line's indentation is preserved, + and each subsequent line's indentation matches the second line.", + ), + ) + .arg( + Arg::with_name(OPT_TAGGED_PARAGRAPH) + .short("t") + .long("tagged-paragraph") + .help( + "Like -c, except that the first and second line of a paragraph *must* + have different indentation or they are treated as separate paragraphs.", + ), + ) + .arg( + Arg::with_name(OPT_PRESERVE_HEADERS) + .short("m") + .long("preserve-headers") + .help( + "Attempt to detect and preserve mail headers in the input. + Be careful when combining this flag with -p.", + ), + ) + .arg( + Arg::with_name(OPT_SPLIT_ONLY) + .short("s") + .long("split-only") + .help("Split lines only, do not reflow."), + ) + .arg( + Arg::with_name(OPT_UNIFORM_SPACING) + .short("u") + .long("uniform-spacing") + .help( + "Insert exactly one + space between words, and two between sentences. + Sentence breaks in the input are detected as [?!.] + followed by two spaces or a newline; other punctuation + is not interpreted as a sentence break.", + ), + ) + .arg( + Arg::with_name(OPT_PREFIX) + .short("p") + .long("prefix") + .help( + "Reformat only lines + beginning with PREFIX, reattaching PREFIX to reformatted lines. + Unless -x is specified, leading whitespace will be ignored + when matching PREFIX.", + ) + .value_name("PREFIX"), + ) + .arg( + Arg::with_name(OPT_SKIP_PREFIX) + .short("P") + .long("skip-prefix") + .help( + "Do not reformat lines + beginning with PSKIP. Unless -X is specified, leading whitespace + will be ignored when matching PSKIP", + ) + .value_name("PSKIP"), + ) + .arg( + Arg::with_name(OPT_EXACT_PREFIX) + .short("x") + .long("exact-prefix") + .help( + "PREFIX must match at the + beginning of the line with no preceding whitespace.", + ), + ) + .arg( + Arg::with_name(OPT_EXACT_SKIP_PREFIX) + .short("X") + .long("exact-skip-prefix") + .help( + "PSKIP must match at the + beginning of the line with no preceding whitespace.", + ), + ) + .arg( + Arg::with_name(OPT_WIDTH) + .short("w") + .long("width") + .help("Fill output lines up to a maximum of WIDTH columns, default 79.") + .value_name("WIDTH"), + ) + .arg( + Arg::with_name(OPT_GOAL) + .short("g") + .long("goal") + .help("Goal width, default ~0.94*WIDTH. Must be less than WIDTH.") + .value_name("GOAL"), + ) + .arg(Arg::with_name(OPT_QUICK).short("q").long("quick").help( + "Break lines more quickly at the + expense of a potentially more ragged appearance.", + )) + .arg( + Arg::with_name(OPT_TAB_WIDTH) + .short("T") + .long("tab-width") + .help( + "Treat tabs as TABWIDTH spaces for + determining line length, default 8. Note that this is used only for + calculating line lengths; tabs are preserved in the output.", + ) + .value_name("TABWIDTH"), + ) + .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true)) +} diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index 118f7f5f9..1dbc8cdc7 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -36,7 +36,35 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .accept_any(); let (args, obs_width) = handle_obsolete(&args[..]); - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + let bytes = matches.is_present(options::BYTES); + let spaces = matches.is_present(options::SPACES); + let poss_width = match matches.value_of(options::WIDTH) { + Some(v) => Some(v.to_owned()), + None => obs_width, + }; + + let width = match poss_width { + Some(inp_width) => match inp_width.parse::() { + Ok(width) => width, + Err(e) => crash!(1, "illegal width value (\"{}\"): {}", inp_width, e), + }, + None => 80, + }; + + let files = match matches.values_of(options::FILE) { + Some(v) => v.map(|v| v.to_owned()).collect(), + None => vec!["-".to_owned()], + }; + + fold(files, bytes, spaces, width); + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .name(NAME) .version(crate_version!()) .usage(SYNTAX) @@ -68,31 +96,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .takes_value(true), ) .arg(Arg::with_name(options::FILE).hidden(true).multiple(true)) - .get_matches_from(args); - - let bytes = matches.is_present(options::BYTES); - let spaces = matches.is_present(options::SPACES); - let poss_width = match matches.value_of(options::WIDTH) { - Some(v) => Some(v.to_owned()), - None => obs_width, - }; - - let width = match poss_width { - Some(inp_width) => match inp_width.parse::() { - Ok(width) => width, - Err(e) => crash!(1, "illegal width value (\"{}\"): {}", inp_width, e), - }, - None => 80, - }; - - let files = match matches.values_of(options::FILE) { - Some(v) => v.map(|v| v.to_owned()).collect(), - None => vec!["-".to_owned()], - }; - - fold(files, bytes, spaces, width); - - 0 } fn handle_obsolete(args: &[String]) -> (Vec, Option) { diff --git a/src/uu/groups/src/groups.rs b/src/uu/groups/src/groups.rs index 6585f3d16..a40d1a490 100644 --- a/src/uu/groups/src/groups.rs +++ b/src/uu/groups/src/groups.rs @@ -35,17 +35,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::USERS) - .multiple(true) - .takes_value(true) - .value_name(options::USERS), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let users: Vec = matches .values_of(options::USERS) @@ -93,3 +83,15 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } exit_code } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::USERS) + .multiple(true) + .takes_value(true) + .value_name(options::USERS), + ) +} diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index a007473ab..d9feb6648 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -285,119 +285,7 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 { // Default binary in Windows, text mode otherwise let binary_flag_default = cfg!(windows); - let binary_help = format!( - "read in binary mode{}", - if binary_flag_default { - " (default)" - } else { - "" - } - ); - - let text_help = format!( - "read in text mode{}", - if binary_flag_default { - "" - } else { - " (default)" - } - ); - - let mut app = App::new(executable!()) - .version(crate_version!()) - .about("Compute and check message digests.") - .arg( - Arg::with_name("binary") - .short("b") - .long("binary") - .help(&binary_help), - ) - .arg( - Arg::with_name("check") - .short("c") - .long("check") - .help("read hashsums from the FILEs and check them"), - ) - .arg( - Arg::with_name("tag") - .long("tag") - .help("create a BSD-style checksum"), - ) - .arg( - Arg::with_name("text") - .short("t") - .long("text") - .help(&text_help) - .conflicts_with("binary"), - ) - .arg( - Arg::with_name("quiet") - .short("q") - .long("quiet") - .help("don't print OK for each successfully verified file"), - ) - .arg( - Arg::with_name("status") - .short("s") - .long("status") - .help("don't output anything, status code shows success"), - ) - .arg( - Arg::with_name("strict") - .long("strict") - .help("exit non-zero for improperly formatted checksum lines"), - ) - .arg( - Arg::with_name("warn") - .short("w") - .long("warn") - .help("warn about improperly formatted checksum lines"), - ) - // Needed for variable-length output sums (e.g. SHAKE) - .arg( - Arg::with_name("bits") - .long("bits") - .help("set the size of the output (only for SHAKE)") - .takes_value(true) - .value_name("BITS") - // XXX: should we actually use validators? they're not particularly efficient - .validator(is_valid_bit_num), - ) - .arg( - Arg::with_name("FILE") - .index(1) - .multiple(true) - .value_name("FILE"), - ); - - if !is_custom_binary(&binary_name) { - let algorithms = &[ - ("md5", "work with MD5"), - ("sha1", "work with SHA1"), - ("sha224", "work with SHA224"), - ("sha256", "work with SHA256"), - ("sha384", "work with SHA384"), - ("sha512", "work with SHA512"), - ("sha3", "work with SHA3"), - ("sha3-224", "work with SHA3-224"), - ("sha3-256", "work with SHA3-256"), - ("sha3-384", "work with SHA3-384"), - ("sha3-512", "work with SHA3-512"), - ( - "shake128", - "work with SHAKE128 using BITS for the output size", - ), - ( - "shake256", - "work with SHAKE256 using BITS for the output size", - ), - ("b2sum", "work with BLAKE2"), - ]; - - for (name, desc) in algorithms { - app = app.arg(Arg::with_name(name).long(name).help(desc)); - } - } + let app = uu_app(&binary_name); // FIXME: this should use get_matches_from_safe() and crash!(), but at the moment that just // causes "error: " to be printed twice (once from crash!() and once from clap). With @@ -445,6 +333,124 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 { } } +pub fn uu_app_common() -> App<'static, 'static> { + #[cfg(windows)] + const BINARY_HELP: &str = "read in binary mode (default)"; + #[cfg(not(windows))] + const BINARY_HELP: &str = "read in binary mode"; + #[cfg(windows)] + const TEXT_HELP: &str = "read in text mode"; + #[cfg(not(windows))] + const TEXT_HELP: &str = "read in text mode (default)"; + App::new(executable!()) + .version(crate_version!()) + .about("Compute and check message digests.") + .arg( + Arg::with_name("binary") + .short("b") + .long("binary") + .help(BINARY_HELP), + ) + .arg( + Arg::with_name("check") + .short("c") + .long("check") + .help("read hashsums from the FILEs and check them"), + ) + .arg( + Arg::with_name("tag") + .long("tag") + .help("create a BSD-style checksum"), + ) + .arg( + Arg::with_name("text") + .short("t") + .long("text") + .help(TEXT_HELP) + .conflicts_with("binary"), + ) + .arg( + Arg::with_name("quiet") + .short("q") + .long("quiet") + .help("don't print OK for each successfully verified file"), + ) + .arg( + Arg::with_name("status") + .short("s") + .long("status") + .help("don't output anything, status code shows success"), + ) + .arg( + Arg::with_name("strict") + .long("strict") + .help("exit non-zero for improperly formatted checksum lines"), + ) + .arg( + Arg::with_name("warn") + .short("w") + .long("warn") + .help("warn about improperly formatted checksum lines"), + ) + // Needed for variable-length output sums (e.g. SHAKE) + .arg( + Arg::with_name("bits") + .long("bits") + .help("set the size of the output (only for SHAKE)") + .takes_value(true) + .value_name("BITS") + // XXX: should we actually use validators? they're not particularly efficient + .validator(is_valid_bit_num), + ) + .arg( + Arg::with_name("FILE") + .index(1) + .multiple(true) + .value_name("FILE"), + ) +} + +pub fn uu_app_custom() -> App<'static, 'static> { + let mut app = uu_app_common(); + let algorithms = &[ + ("md5", "work with MD5"), + ("sha1", "work with SHA1"), + ("sha224", "work with SHA224"), + ("sha256", "work with SHA256"), + ("sha384", "work with SHA384"), + ("sha512", "work with SHA512"), + ("sha3", "work with SHA3"), + ("sha3-224", "work with SHA3-224"), + ("sha3-256", "work with SHA3-256"), + ("sha3-384", "work with SHA3-384"), + ("sha3-512", "work with SHA3-512"), + ( + "shake128", + "work with SHAKE128 using BITS for the output size", + ), + ( + "shake256", + "work with SHAKE256 using BITS for the output size", + ), + ("b2sum", "work with BLAKE2"), + ]; + + for (name, desc) in algorithms { + app = app.arg(Arg::with_name(name).long(name).help(desc)); + } + app +} + +// hashsum is handled differently in build.rs, therefore this is not the same +// as in other utilities. +fn uu_app(binary_name: &str) -> App<'static, 'static> { + if !is_custom_binary(binary_name) { + uu_app_custom() + } else { + uu_app_common() + } +} + #[allow(clippy::cognitive_complexity)] fn hashsum<'a, I>(mut options: Options, files: I) -> Result<(), i32> where diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index aceecd941..e17e17034 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -40,7 +40,7 @@ mod take; use lines::zlines; use take::take_all_but; -fn app<'a>() -> App<'a, 'a> { +pub fn uu_app() -> App<'static, 'static> { App::new(executable!()) .version(crate_version!()) .about(ABOUT) @@ -167,7 +167,7 @@ impl HeadOptions { ///Construct options from matches pub fn get_from(args: impl uucore::Args) -> Result { - let matches = app().get_matches_from(arg_iterate(args)?); + let matches = uu_app().get_matches_from(arg_iterate(args)?); let mut options = HeadOptions::new(); diff --git a/src/uu/hostid/Cargo.toml b/src/uu/hostid/Cargo.toml index ab6954104..ab8b43f05 100644 --- a/src/uu/hostid/Cargo.toml +++ b/src/uu/hostid/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/hostid.rs" [dependencies] +clap = "2.33.3" libc = "0.2.42" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/hostid/src/hostid.rs b/src/uu/hostid/src/hostid.rs index 551866521..e9fc08379 100644 --- a/src/uu/hostid/src/hostid.rs +++ b/src/uu/hostid/src/hostid.rs @@ -10,12 +10,10 @@ #[macro_use] extern crate uucore; +use clap::{crate_version, App}; use libc::c_long; -use uucore::InvalidEncodingHandling; static SYNTAX: &str = "[options]"; -static SUMMARY: &str = ""; -static LONG_HELP: &str = ""; // currently rust libc interface doesn't include gethostid extern "C" { @@ -23,14 +21,17 @@ extern "C" { } pub fn uumain(args: impl uucore::Args) -> i32 { - app!(SYNTAX, SUMMARY, LONG_HELP).parse( - args.collect_str(InvalidEncodingHandling::ConvertLossy) - .accept_any(), - ); + uu_app().get_matches_from(args); hostid(); 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .usage(SYNTAX) +} + fn hostid() { /* * POSIX says gethostid returns a "32-bit identifier" but is silent diff --git a/src/uu/hostname/src/hostname.rs b/src/uu/hostname/src/hostname.rs index ff312fb58..fe477d7b5 100644 --- a/src/uu/hostname/src/hostname.rs +++ b/src/uu/hostname/src/hostname.rs @@ -52,10 +52,25 @@ fn get_usage() -> String { } fn execute(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + match matches.value_of(OPT_HOST) { + None => display_hostname(&matches), + Some(host) => { + if let Err(err) = hostname::set(host) { + show_error!("{}", err); + 1 + } else { + 0 + } + } + } +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .arg( Arg::with_name(OPT_DOMAIN) .short("d") @@ -80,19 +95,6 @@ fn execute(args: impl uucore::Args) -> i32 { possible", )) .arg(Arg::with_name(OPT_HOST)) - .get_matches_from(args); - - match matches.value_of(OPT_HOST) { - None => display_hostname(&matches), - Some(host) => { - if let Err(err) = hostname::set(host) { - show_error!("{}", err); - 1 - } else { - 0 - } - } - } } fn display_hostname(matches: &ArgMatches) -> i32 { diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index e6233f0b7..d5acc97f3 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -115,106 +115,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let after_help = get_description(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&after_help[..]) - .arg( - Arg::with_name(options::OPT_AUDIT) - .short("A") - .conflicts_with_all(&[ - options::OPT_GROUP, - options::OPT_EFFECTIVE_USER, - options::OPT_HUMAN_READABLE, - options::OPT_PASSWORD, - options::OPT_GROUPS, - options::OPT_ZERO, - ]) - .help( - "Display the process audit user ID and other process audit properties,\n\ - which requires privilege (not available on Linux).", - ), - ) - .arg( - Arg::with_name(options::OPT_EFFECTIVE_USER) - .short("u") - .long(options::OPT_EFFECTIVE_USER) - .conflicts_with(options::OPT_GROUP) - .help("Display only the effective user ID as a number."), - ) - .arg( - Arg::with_name(options::OPT_GROUP) - .short("g") - .long(options::OPT_GROUP) - .help("Display only the effective group ID as a number"), - ) - .arg( - Arg::with_name(options::OPT_GROUPS) - .short("G") - .long(options::OPT_GROUPS) - .conflicts_with_all(&[ - options::OPT_GROUP, - options::OPT_EFFECTIVE_USER, - options::OPT_HUMAN_READABLE, - options::OPT_PASSWORD, - options::OPT_AUDIT, - ]) - .help( - "Display only the different group IDs as white-space separated numbers, \ - in no particular order.", - ), - ) - .arg( - Arg::with_name(options::OPT_HUMAN_READABLE) - .short("p") - .help("Make the output human-readable. Each display is on a separate line."), - ) - .arg( - Arg::with_name(options::OPT_NAME) - .short("n") - .long(options::OPT_NAME) - .help( - "Display the name of the user or group ID for the -G, -g and -u options \ - instead of the number.\nIf any of the ID numbers cannot be mapped into \ - names, the number will be displayed as usual.", - ), - ) - .arg( - Arg::with_name(options::OPT_PASSWORD) - .short("P") - .help("Display the id as a password file entry."), - ) - .arg( - Arg::with_name(options::OPT_REAL_ID) - .short("r") - .long(options::OPT_REAL_ID) - .help( - "Display the real ID for the -G, -g and -u options instead of \ - the effective ID.", - ), - ) - .arg( - Arg::with_name(options::OPT_ZERO) - .short("z") - .long(options::OPT_ZERO) - .help( - "delimit entries with NUL characters, not whitespace;\n\ - not permitted in default format", - ), - ) - .arg( - Arg::with_name(options::OPT_CONTEXT) - .short("Z") - .long(options::OPT_CONTEXT) - .help("NotImplemented: print only the security context of the process"), - ) - .arg( - Arg::with_name(options::ARG_USERS) - .multiple(true) - .takes_value(true) - .value_name(options::ARG_USERS), - ) .get_matches_from(args); let users: Vec = matches @@ -385,6 +288,107 @@ pub fn uumain(args: impl uucore::Args) -> i32 { exit_code } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::OPT_AUDIT) + .short("A") + .conflicts_with_all(&[ + options::OPT_GROUP, + options::OPT_EFFECTIVE_USER, + options::OPT_HUMAN_READABLE, + options::OPT_PASSWORD, + options::OPT_GROUPS, + options::OPT_ZERO, + ]) + .help( + "Display the process audit user ID and other process audit properties,\n\ + which requires privilege (not available on Linux).", + ), + ) + .arg( + Arg::with_name(options::OPT_EFFECTIVE_USER) + .short("u") + .long(options::OPT_EFFECTIVE_USER) + .conflicts_with(options::OPT_GROUP) + .help("Display only the effective user ID as a number."), + ) + .arg( + Arg::with_name(options::OPT_GROUP) + .short("g") + .long(options::OPT_GROUP) + .help("Display only the effective group ID as a number"), + ) + .arg( + Arg::with_name(options::OPT_GROUPS) + .short("G") + .long(options::OPT_GROUPS) + .conflicts_with_all(&[ + options::OPT_GROUP, + options::OPT_EFFECTIVE_USER, + options::OPT_HUMAN_READABLE, + options::OPT_PASSWORD, + options::OPT_AUDIT, + ]) + .help( + "Display only the different group IDs as white-space separated numbers, \ + in no particular order.", + ), + ) + .arg( + Arg::with_name(options::OPT_HUMAN_READABLE) + .short("p") + .help("Make the output human-readable. Each display is on a separate line."), + ) + .arg( + Arg::with_name(options::OPT_NAME) + .short("n") + .long(options::OPT_NAME) + .help( + "Display the name of the user or group ID for the -G, -g and -u options \ + instead of the number.\nIf any of the ID numbers cannot be mapped into \ + names, the number will be displayed as usual.", + ), + ) + .arg( + Arg::with_name(options::OPT_PASSWORD) + .short("P") + .help("Display the id as a password file entry."), + ) + .arg( + Arg::with_name(options::OPT_REAL_ID) + .short("r") + .long(options::OPT_REAL_ID) + .help( + "Display the real ID for the -G, -g and -u options instead of \ + the effective ID.", + ), + ) + .arg( + Arg::with_name(options::OPT_ZERO) + .short("z") + .long(options::OPT_ZERO) + .help( + "delimit entries with NUL characters, not whitespace;\n\ + not permitted in default format", + ), + ) + .arg( + Arg::with_name(options::OPT_CONTEXT) + .short("Z") + .long(options::OPT_CONTEXT) + .help("NotImplemented: print only the security context of the process"), + ) + .arg( + Arg::with_name(options::ARG_USERS) + .multiple(true) + .takes_value(true) + .value_name(options::ARG_USERS), + ) +} + fn pretty(possible_pw: Option) { if let Some(p) = possible_pw { print!("uid\t{}\ngroups\t", p.name()); diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index 3992ac25e..e45797750 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -98,10 +98,35 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + let paths: Vec = matches + .values_of(ARG_FILES) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + + if let Err(s) = check_unimplemented(&matches) { + show_error!("Unimplemented feature: {}", s); + return 2; + } + + let behavior = match behavior(&matches) { + Ok(x) => x, + Err(ret) => { + return ret; + } + }; + + match behavior.main_function { + MainFunction::Directory => directory(paths, behavior), + MainFunction::Standard => standard(paths, behavior), + } +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .arg( Arg::with_name(OPT_BACKUP) .long(OPT_BACKUP) @@ -228,29 +253,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .value_name("CONTEXT") ) .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1)) - .get_matches_from(args); - - let paths: Vec = matches - .values_of(ARG_FILES) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - - if let Err(s) = check_unimplemented(&matches) { - show_error!("Unimplemented feature: {}", s); - return 2; - } - - let behavior = match behavior(&matches) { - Ok(x) => x, - Err(ret) => { - return ret; - } - }; - - match behavior.main_function { - MainFunction::Directory => directory(paths, behavior), - MainFunction::Standard => standard(paths, behavior), - } } /// Check for unimplemented command line arguments. diff --git a/src/uu/join/src/join.rs b/src/uu/join/src/join.rs index 4cdfe2141..60721f212 100644 --- a/src/uu/join/src/join.rs +++ b/src/uu/join/src/join.rs @@ -442,7 +442,72 @@ impl<'a> State<'a> { } pub fn uumain(args: impl uucore::Args) -> i32 { - let matches = App::new(NAME) + let matches = uu_app().get_matches_from(args); + + let keys = parse_field_number_option(matches.value_of("j")); + let key1 = parse_field_number_option(matches.value_of("1")); + let key2 = parse_field_number_option(matches.value_of("2")); + + let mut settings: Settings = Default::default(); + + if let Some(value) = matches.value_of("v") { + settings.print_unpaired = parse_file_number(value); + settings.print_joined = false; + } else if let Some(value) = matches.value_of("a") { + settings.print_unpaired = parse_file_number(value); + } + + settings.ignore_case = matches.is_present("i"); + settings.key1 = get_field_number(keys, key1); + settings.key2 = get_field_number(keys, key2); + + if let Some(value) = matches.value_of("t") { + settings.separator = match value.len() { + 0 => Sep::Line, + 1 => Sep::Char(value.chars().next().unwrap()), + _ => crash!(1, "multi-character tab {}", value), + }; + } + + if let Some(format) = matches.value_of("o") { + if format == "auto" { + settings.autoformat = true; + } else { + settings.format = format + .split(|c| c == ' ' || c == ',' || c == '\t') + .map(Spec::parse) + .collect(); + } + } + + if let Some(empty) = matches.value_of("e") { + settings.empty = empty.to_string(); + } + + if matches.is_present("nocheck-order") { + settings.check_order = CheckOrder::Disabled; + } + + if matches.is_present("check-order") { + settings.check_order = CheckOrder::Enabled; + } + + if matches.is_present("header") { + settings.headers = true; + } + + let file1 = matches.value_of("file1").unwrap(); + let file2 = matches.value_of("file2").unwrap(); + + if file1 == "-" && file2 == "-" { + crash!(1, "both files cannot be standard input"); + } + + exec(file1, file2, &settings) +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(NAME) .version(crate_version!()) .about( "For each pair of input lines with identical join fields, write a line to @@ -542,68 +607,6 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2", .value_name("FILE2") .hidden(true), ) - .get_matches_from(args); - - let keys = parse_field_number_option(matches.value_of("j")); - let key1 = parse_field_number_option(matches.value_of("1")); - let key2 = parse_field_number_option(matches.value_of("2")); - - let mut settings: Settings = Default::default(); - - if let Some(value) = matches.value_of("v") { - settings.print_unpaired = parse_file_number(value); - settings.print_joined = false; - } else if let Some(value) = matches.value_of("a") { - settings.print_unpaired = parse_file_number(value); - } - - settings.ignore_case = matches.is_present("i"); - settings.key1 = get_field_number(keys, key1); - settings.key2 = get_field_number(keys, key2); - - if let Some(value) = matches.value_of("t") { - settings.separator = match value.len() { - 0 => Sep::Line, - 1 => Sep::Char(value.chars().next().unwrap()), - _ => crash!(1, "multi-character tab {}", value), - }; - } - - if let Some(format) = matches.value_of("o") { - if format == "auto" { - settings.autoformat = true; - } else { - settings.format = format - .split(|c| c == ' ' || c == ',' || c == '\t') - .map(Spec::parse) - .collect(); - } - } - - if let Some(empty) = matches.value_of("e") { - settings.empty = empty.to_string(); - } - - if matches.is_present("nocheck-order") { - settings.check_order = CheckOrder::Disabled; - } - - if matches.is_present("check-order") { - settings.check_order = CheckOrder::Enabled; - } - - if matches.is_present("header") { - settings.headers = true; - } - - let file1 = matches.value_of("file1").unwrap(); - let file2 = matches.value_of("file2").unwrap(); - - if file1 == "-" && file2 == "-" { - crash!(1, "both files cannot be standard input"); - } - - exec(file1, file2, &settings) } fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 { diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index c48864564..92868efdb 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -43,38 +43,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let (args, obs_signal) = handle_obsolete(args); let usage = format!("{} [OPTIONS]... PID...", executable!()); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::LIST) - .short("l") - .long(options::LIST) - .help("Lists signals") - .conflicts_with(options::TABLE) - .conflicts_with(options::TABLE_OLD), - ) - .arg( - Arg::with_name(options::TABLE) - .short("t") - .long(options::TABLE) - .help("Lists table of signals"), - ) - .arg(Arg::with_name(options::TABLE_OLD).short("L").hidden(true)) - .arg( - Arg::with_name(options::SIGNAL) - .short("s") - .long(options::SIGNAL) - .help("Sends given signal") - .takes_value(true), - ) - .arg( - Arg::with_name(options::PIDS_OR_SIGNALS) - .hidden(true) - .multiple(true), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let mode = if matches.is_present(options::TABLE) || matches.is_present(options::TABLE_OLD) { Mode::Table @@ -106,6 +75,39 @@ pub fn uumain(args: impl uucore::Args) -> i32 { EXIT_OK } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::LIST) + .short("l") + .long(options::LIST) + .help("Lists signals") + .conflicts_with(options::TABLE) + .conflicts_with(options::TABLE_OLD), + ) + .arg( + Arg::with_name(options::TABLE) + .short("t") + .long(options::TABLE) + .help("Lists table of signals"), + ) + .arg(Arg::with_name(options::TABLE_OLD).short("L").hidden(true)) + .arg( + Arg::with_name(options::SIGNAL) + .short("s") + .long(options::SIGNAL) + .help("Sends given signal") + .takes_value(true), + ) + .arg( + Arg::with_name(options::PIDS_OR_SIGNALS) + .hidden(true) + .multiple(true), + ) +} + fn handle_obsolete(mut args: Vec) -> (Vec, Option) { let mut i = 0; while i < args.len() { diff --git a/src/uu/link/src/link.rs b/src/uu/link/src/link.rs index 08401ebaf..ad7702044 100644 --- a/src/uu/link/src/link.rs +++ b/src/uu/link/src/link.rs @@ -32,19 +32,7 @@ pub fn normalize_error_message(e: Error) -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::FILES) - .hidden(true) - .required(true) - .min_values(2) - .max_values(2) - .takes_value(true), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let files: Vec<_> = matches .values_of_os(options::FILES) @@ -61,3 +49,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::FILES) + .hidden(true) + .required(true) + .min_values(2) + .max_values(2) + .takes_value(true), + ) +} diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index 29cab58e5..b08eba97a 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -97,11 +97,71 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let long_usage = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&long_usage[..]) + .get_matches_from(args); + + /* the list of files */ + + let paths: Vec = matches + .values_of(ARG_FILES) + .unwrap() + .map(PathBuf::from) + .collect(); + + let overwrite_mode = if matches.is_present(options::FORCE) { + OverwriteMode::Force + } else if matches.is_present(options::INTERACTIVE) { + OverwriteMode::Interactive + } else { + OverwriteMode::NoClobber + }; + + let backup_mode = if matches.is_present(options::B) { + BackupMode::ExistingBackup + } else if matches.is_present(options::BACKUP) { + match matches.value_of(options::BACKUP) { + None => BackupMode::ExistingBackup, + Some(mode) => match mode { + "simple" | "never" => BackupMode::SimpleBackup, + "numbered" | "t" => BackupMode::NumberedBackup, + "existing" | "nil" => BackupMode::ExistingBackup, + "none" | "off" => BackupMode::NoBackup, + _ => panic!(), // cannot happen as it is managed by clap + }, + } + } else { + BackupMode::NoBackup + }; + + let backup_suffix = if matches.is_present(options::SUFFIX) { + matches.value_of(options::SUFFIX).unwrap() + } else { + "~" + }; + + let settings = Settings { + overwrite: overwrite_mode, + backup: backup_mode, + suffix: backup_suffix.to_string(), + symbolic: matches.is_present(options::SYMBOLIC), + relative: matches.is_present(options::RELATIVE), + target_dir: matches + .value_of(options::TARGET_DIRECTORY) + .map(String::from), + no_target_dir: matches.is_present(options::NO_TARGET_DIRECTORY), + no_dereference: matches.is_present(options::NO_DEREFERENCE), + verbose: matches.is_present(options::VERBOSE), + }; + + exec(&paths[..], &settings) +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) .arg(Arg::with_name(options::B).short(options::B).help( "make a backup of each file that would otherwise be overwritten or \ removed", @@ -198,62 +258,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .required(true) .min_values(1), ) - .get_matches_from(args); - - /* the list of files */ - - let paths: Vec = matches - .values_of(ARG_FILES) - .unwrap() - .map(PathBuf::from) - .collect(); - - let overwrite_mode = if matches.is_present(options::FORCE) { - OverwriteMode::Force - } else if matches.is_present(options::INTERACTIVE) { - OverwriteMode::Interactive - } else { - OverwriteMode::NoClobber - }; - - let backup_mode = if matches.is_present(options::B) { - BackupMode::ExistingBackup - } else if matches.is_present(options::BACKUP) { - match matches.value_of(options::BACKUP) { - None => BackupMode::ExistingBackup, - Some(mode) => match mode { - "simple" | "never" => BackupMode::SimpleBackup, - "numbered" | "t" => BackupMode::NumberedBackup, - "existing" | "nil" => BackupMode::ExistingBackup, - "none" | "off" => BackupMode::NoBackup, - _ => panic!(), // cannot happen as it is managed by clap - }, - } - } else { - BackupMode::NoBackup - }; - - let backup_suffix = if matches.is_present(options::SUFFIX) { - matches.value_of(options::SUFFIX).unwrap() - } else { - "~" - }; - - let settings = Settings { - overwrite: overwrite_mode, - backup: backup_mode, - suffix: backup_suffix.to_string(), - symbolic: matches.is_present(options::SYMBOLIC), - relative: matches.is_present(options::RELATIVE), - target_dir: matches - .value_of(options::TARGET_DIRECTORY) - .map(String::from), - no_target_dir: matches.is_present(options::NO_TARGET_DIRECTORY), - no_dereference: matches.is_present(options::NO_DEREFERENCE), - verbose: matches.is_present(options::VERBOSE), - }; - - exec(&paths[..], &settings) } fn exec(files: &[PathBuf], settings: &Settings) -> i32 { diff --git a/src/uu/logname/src/logname.rs b/src/uu/logname/src/logname.rs index ba5880403..4a6f43418 100644 --- a/src/uu/logname/src/logname.rs +++ b/src/uu/logname/src/logname.rs @@ -45,11 +45,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .accept_any(); let usage = get_usage(); - let _ = App::new(executable!()) - .version(crate_version!()) - .about(SUMMARY) - .usage(&usage[..]) - .get_matches_from(args); + let _ = uu_app().usage(&usage[..]).get_matches_from(args); match get_userlogin() { Some(userlogin) => println!("{}", userlogin), @@ -58,3 +54,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(SUMMARY) +} diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 01bc27055..6ca3f4bbe 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -558,10 +558,22 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let app = App::new(executable!()) + let app = uu_app().usage(&usage[..]); + + let matches = app.get_matches_from(args); + + let locs = matches + .values_of(options::PATHS) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_else(|| vec![String::from(".")]); + + list(locs, Config::from(matches)) +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) // Format arguments .arg( @@ -1095,16 +1107,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // Positional arguments .arg(Arg::with_name(options::PATHS).multiple(true).takes_value(true)) - .after_help(AFTER_HELP); - - let matches = app.get_matches_from(args); - - let locs = matches - .values_of(options::PATHS) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_else(|| vec![String::from(".")]); - - list(locs, Config::from(matches)) + .after_help(AFTER_HELP) } /// Represents a Path along with it's associated data diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index c5ff8b76c..82d561213 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -32,10 +32,37 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // Linux-specific options, not implemented // opts.optflag("Z", "context", "set SELinux security context" + // " of each created directory to CTX"), - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + let dirs: Vec = matches + .values_of(ARG_DIRS) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + + let verbose = matches.is_present(OPT_VERBOSE); + let recursive = matches.is_present(OPT_PARENTS); + + // Translate a ~str in octal form to u16, default to 755 + // Not tested on Windows + let mode_match = matches.value_of(OPT_MODE); + let mode: u16 = match mode_match { + Some(m) => { + let res: Option = u16::from_str_radix(m, 8).ok(); + match res { + Some(r) => r, + _ => crash!(1, "no mode given"), + } + } + _ => 0o755_u16, + }; + + exec(dirs, recursive, mode, verbose) +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .arg( Arg::with_name(OPT_MODE) .short("m") @@ -62,31 +89,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .takes_value(true) .min_values(1), ) - .get_matches_from(args); - - let dirs: Vec = matches - .values_of(ARG_DIRS) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - - let verbose = matches.is_present(OPT_VERBOSE); - let recursive = matches.is_present(OPT_PARENTS); - - // Translate a ~str in octal form to u16, default to 755 - // Not tested on Windows - let mode_match = matches.value_of(OPT_MODE); - let mode: u16 = match mode_match { - Some(m) => { - let res: Option = u16::from_str_radix(m, 8).ok(); - match res { - Some(r) => r, - _ => crash!(1, "no mode given"), - } - } - _ => 0o755_u16, - }; - - exec(dirs, recursive, mode, verbose) } /** diff --git a/src/uu/mkfifo/src/mkfifo.rs b/src/uu/mkfifo/src/mkfifo.rs index b8a6bbe38..ad12e230d 100644 --- a/src/uu/mkfifo/src/mkfifo.rs +++ b/src/uu/mkfifo/src/mkfifo.rs @@ -29,27 +29,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); - let matches = App::new(executable!()) - .name(NAME) - .version(crate_version!()) - .usage(USAGE) - .about(SUMMARY) - .arg( - Arg::with_name(options::MODE) - .short("m") - .long(options::MODE) - .help("file permissions for the fifo") - .default_value("0666") - .value_name("0666"), - ) - .arg( - Arg::with_name(options::SE_LINUX_SECURITY_CONTEXT) - .short(options::SE_LINUX_SECURITY_CONTEXT) - .help("set the SELinux security context to default type") - ) - .arg(Arg::with_name(options::CONTEXT).long(options::CONTEXT).value_name("CTX").help("like -Z, or if CTX is specified then set the SELinux\nor SMACK security context to CTX")) - .arg(Arg::with_name(options::FIFO).hidden(true).multiple(true)) - .get_matches_from(args); + let matches = uu_app().get_matches_from(args); if matches.is_present(options::CONTEXT) { crash!(1, "--context is not implemented"); @@ -88,3 +68,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 { exit_code } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .name(NAME) + .version(crate_version!()) + .usage(USAGE) + .about(SUMMARY) + .arg( + Arg::with_name(options::MODE) + .short("m") + .long(options::MODE) + .help("file permissions for the fifo") + .default_value("0666") + .value_name("0666"), + ) + .arg( + Arg::with_name(options::SE_LINUX_SECURITY_CONTEXT) + .short(options::SE_LINUX_SECURITY_CONTEXT) + .help("set the SELinux security context to default type") + ) + .arg(Arg::with_name(options::CONTEXT).long(options::CONTEXT).value_name("CTX").help("like -Z, or if CTX is specified then set the SELinux\nor SMACK security context to CTX")) + .arg(Arg::with_name(options::FIFO).hidden(true).multiple(true)) +} diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index a1f361e55..8cc7db908 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -89,48 +89,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // opts.optflag("Z", "", "set the SELinux security context to default type"); // opts.optopt("", "context", "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX"); - let matches = App::new(executable!()) - .version(crate_version!()) - .usage(USAGE) - .after_help(LONG_HELP) - .about(ABOUT) - .arg( - Arg::with_name("mode") - .short("m") - .long("mode") - .value_name("MODE") - .help("set file permission bits to MODE, not a=rw - umask"), - ) - .arg( - Arg::with_name("name") - .value_name("NAME") - .help("name of the new file") - .required(true) - .index(1), - ) - .arg( - Arg::with_name("type") - .value_name("TYPE") - .help("type of the new file (b, c, u or p)") - .required(true) - .validator(valid_type) - .index(2), - ) - .arg( - Arg::with_name("major") - .value_name("MAJOR") - .help("major file type") - .validator(valid_u64) - .index(3), - ) - .arg( - Arg::with_name("minor") - .value_name("MINOR") - .help("minor file type") - .validator(valid_u64) - .index(4), - ) - .get_matches_from(args); + let matches = uu_app().get_matches_from(args); let mode = match get_mode(&matches) { Ok(mode) => mode, @@ -185,6 +144,50 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .usage(USAGE) + .after_help(LONG_HELP) + .about(ABOUT) + .arg( + Arg::with_name("mode") + .short("m") + .long("mode") + .value_name("MODE") + .help("set file permission bits to MODE, not a=rw - umask"), + ) + .arg( + Arg::with_name("name") + .value_name("NAME") + .help("name of the new file") + .required(true) + .index(1), + ) + .arg( + Arg::with_name("type") + .value_name("TYPE") + .help("type of the new file (b, c, u or p)") + .required(true) + .validator(valid_type) + .index(2), + ) + .arg( + Arg::with_name("major") + .value_name("MAJOR") + .help("major file type") + .validator(valid_u64) + .index(3), + ) + .arg( + Arg::with_name("minor") + .value_name("MINOR") + .help("minor file type") + .validator(valid_u64) + .index(4), + ) +} + fn get_mode(matches: &ArgMatches) -> Result { match matches.value_of("mode") { None => Ok(MODE_RW_UGO), diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index a052766e8..bbccf6628 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -40,61 +40,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(OPT_DIRECTORY) - .short("d") - .long(OPT_DIRECTORY) - .help("Make a directory instead of a file"), - ) - .arg( - Arg::with_name(OPT_DRY_RUN) - .short("u") - .long(OPT_DRY_RUN) - .help("do not create anything; merely print a name (unsafe)"), - ) - .arg( - Arg::with_name(OPT_QUIET) - .short("q") - .long("quiet") - .help("Fail silently if an error occurs."), - ) - .arg( - Arg::with_name(OPT_SUFFIX) - .long(OPT_SUFFIX) - .help( - "append SUFFIX to TEMPLATE; SUFFIX must not contain a path separator. \ - This option is implied if TEMPLATE does not end with X.", - ) - .value_name("SUFFIX"), - ) - .arg( - Arg::with_name(OPT_TMPDIR) - .short("p") - .long(OPT_TMPDIR) - .help( - "interpret TEMPLATE relative to DIR; if DIR is not specified, use \ - $TMPDIR ($TMP on windows) if set, else /tmp. With this option, TEMPLATE must not \ - be an absolute name; unlike with -t, TEMPLATE may contain \ - slashes, but mktemp creates only the final component", - ) - .value_name("DIR"), - ) - .arg(Arg::with_name(OPT_T).short(OPT_T).help( - "Generate a template (using the supplied prefix and TMPDIR (TMP on windows) if set) \ - to create a filename template [deprecated]", - )) - .arg( - Arg::with_name(ARG_TEMPLATE) - .multiple(false) - .takes_value(true) - .max_values(1) - .default_value(DEFAULT_TEMPLATE), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let template = matches.value_of(ARG_TEMPLATE).unwrap(); let tmpdir = matches.value_of(OPT_TMPDIR).unwrap_or_default(); @@ -171,6 +117,62 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_DIRECTORY) + .short("d") + .long(OPT_DIRECTORY) + .help("Make a directory instead of a file"), + ) + .arg( + Arg::with_name(OPT_DRY_RUN) + .short("u") + .long(OPT_DRY_RUN) + .help("do not create anything; merely print a name (unsafe)"), + ) + .arg( + Arg::with_name(OPT_QUIET) + .short("q") + .long("quiet") + .help("Fail silently if an error occurs."), + ) + .arg( + Arg::with_name(OPT_SUFFIX) + .long(OPT_SUFFIX) + .help( + "append SUFFIX to TEMPLATE; SUFFIX must not contain a path separator. \ + This option is implied if TEMPLATE does not end with X.", + ) + .value_name("SUFFIX"), + ) + .arg( + Arg::with_name(OPT_TMPDIR) + .short("p") + .long(OPT_TMPDIR) + .help( + "interpret TEMPLATE relative to DIR; if DIR is not specified, use \ + $TMPDIR ($TMP on windows) if set, else /tmp. With this option, TEMPLATE must not \ + be an absolute name; unlike with -t, TEMPLATE may contain \ + slashes, but mktemp creates only the final component", + ) + .value_name("DIR"), + ) + .arg(Arg::with_name(OPT_T).short(OPT_T).help( + "Generate a template (using the supplied prefix and TMPDIR (TMP on windows) if set) \ + to create a filename template [deprecated]", + )) + .arg( + Arg::with_name(ARG_TEMPLATE) + .multiple(false) + .takes_value(true) + .max_values(1) + .default_value(DEFAULT_TEMPLATE), + ) +} + fn parse_template(temp: &str) -> Option<(&str, usize, &str)> { let right = match temp.rfind('X') { Some(r) => r + 1, diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index d7fba5080..8f25cd7e4 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -51,7 +51,49 @@ pub mod options { const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n"; pub fn uumain(args: impl uucore::Args) -> i32 { - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + let mut buff = String::new(); + let silent = matches.is_present(options::SILENT); + if let Some(files) = matches.values_of(options::FILES) { + let mut stdout = setup_term(); + let length = files.len(); + + let mut files_iter = files.peekable(); + while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) { + let file = Path::new(file); + if file.is_dir() { + terminal::disable_raw_mode().unwrap(); + show_usage_error!("'{}' is a directory.", file.display()); + return 1; + } + if !file.exists() { + terminal::disable_raw_mode().unwrap(); + show_error!("cannot open {}: No such file or directory", file.display()); + return 1; + } + if length > 1 { + buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap())); + } + let mut reader = BufReader::new(File::open(file).unwrap()); + reader.read_to_string(&mut buff).unwrap(); + more(&buff, &mut stdout, next_file.copied(), silent); + buff.clear(); + } + reset_term(&mut stdout); + } else if atty::isnt(atty::Stream::Stdin) { + stdin().read_to_string(&mut buff).unwrap(); + let mut stdout = setup_term(); + more(&buff, &mut stdout, None, silent); + reset_term(&mut stdout); + } else { + show_usage_error!("bad usage"); + } + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .about("A file perusal filter for CRT viewing.") .version(crate_version!()) .arg( @@ -138,45 +180,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .multiple(true) .help("Path to the files to be read"), ) - .get_matches_from(args); - - let mut buff = String::new(); - let silent = matches.is_present(options::SILENT); - if let Some(files) = matches.values_of(options::FILES) { - let mut stdout = setup_term(); - let length = files.len(); - - let mut files_iter = files.peekable(); - while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) { - let file = Path::new(file); - if file.is_dir() { - terminal::disable_raw_mode().unwrap(); - show_usage_error!("'{}' is a directory.", file.display()); - return 1; - } - if !file.exists() { - terminal::disable_raw_mode().unwrap(); - show_error!("cannot open {}: No such file or directory", file.display()); - return 1; - } - if length > 1 { - buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap())); - } - let mut reader = BufReader::new(File::open(file).unwrap()); - reader.read_to_string(&mut buff).unwrap(); - more(&buff, &mut stdout, next_file.copied(), silent); - buff.clear(); - } - reset_term(&mut stdout); - } else if atty::isnt(atty::Stream::Stdin) { - stdin().read_to_string(&mut buff).unwrap(); - let mut stdout = setup_term(); - more(&buff, &mut stdout, None, silent); - reset_term(&mut stdout); - } else { - show_usage_error!("bad usage"); - } - 0 } #[cfg(not(target_os = "fuchsia"))] diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index d709a2117..4a761861f 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -70,11 +70,64 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app() + .after_help(&*format!( + "{}\n{}", + LONG_HELP, + backup_control::BACKUP_CONTROL_LONG_HELP + )) + .usage(&usage[..]) + .get_matches_from(args); + + let files: Vec = matches + .values_of(ARG_FILES) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + + let overwrite_mode = determine_overwrite_mode(&matches); + let backup_mode = backup_control::determine_backup_mode( + matches.is_present(OPT_BACKUP_NO_ARG) || matches.is_present(OPT_BACKUP), + matches.value_of(OPT_BACKUP), + ); + + if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup { + show_usage_error!("options --backup and --no-clobber are mutually exclusive"); + return 1; + } + + let backup_suffix = backup_control::determine_backup_suffix(matches.value_of(OPT_SUFFIX)); + + let behavior = Behavior { + overwrite: overwrite_mode, + backup: backup_mode, + suffix: backup_suffix, + update: matches.is_present(OPT_UPDATE), + target_dir: matches.value_of(OPT_TARGET_DIRECTORY).map(String::from), + no_target_dir: matches.is_present(OPT_NO_TARGET_DIRECTORY), + verbose: matches.is_present(OPT_VERBOSE), + }; + + let paths: Vec = { + fn strip_slashes(p: &Path) -> &Path { + p.components().as_path() + } + let to_owned = |p: &Path| p.to_owned(); + let paths = files.iter().map(Path::new); + + if matches.is_present(OPT_STRIP_TRAILING_SLASHES) { + paths.map(strip_slashes).map(to_owned).collect() + } else { + paths.map(to_owned).collect() + } + }; + + exec(&paths[..], behavior) +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .after_help(&*format!("{}\n{}", LONG_HELP, backup_control::BACKUP_CONTROL_LONG_HELP)) - .usage(&usage[..]) .arg( Arg::with_name(OPT_BACKUP) .long(OPT_BACKUP) @@ -153,51 +206,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .min_values(2) .required(true) ) - .get_matches_from(args); - - let files: Vec = matches - .values_of(ARG_FILES) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - - let overwrite_mode = determine_overwrite_mode(&matches); - let backup_mode = backup_control::determine_backup_mode( - matches.is_present(OPT_BACKUP_NO_ARG) || matches.is_present(OPT_BACKUP), - matches.value_of(OPT_BACKUP), - ); - - if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup { - show_usage_error!("options --backup and --no-clobber are mutually exclusive"); - return 1; - } - - let backup_suffix = backup_control::determine_backup_suffix(matches.value_of(OPT_SUFFIX)); - - let behavior = Behavior { - overwrite: overwrite_mode, - backup: backup_mode, - suffix: backup_suffix, - update: matches.is_present(OPT_UPDATE), - target_dir: matches.value_of(OPT_TARGET_DIRECTORY).map(String::from), - no_target_dir: matches.is_present(OPT_NO_TARGET_DIRECTORY), - verbose: matches.is_present(OPT_VERBOSE), - }; - - let paths: Vec = { - fn strip_slashes(p: &Path) -> &Path { - p.components().as_path() - } - let to_owned = |p: &Path| p.to_owned(); - let paths = files.iter().map(Path::new); - - if matches.is_present(OPT_STRIP_TRAILING_SLASHES) { - paths.map(strip_slashes).map(to_owned).collect() - } else { - paths.map(to_owned).collect() - } - }; - - exec(&paths[..], behavior) } fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { diff --git a/src/uu/nice/src/nice.rs b/src/uu/nice/src/nice.rs index 77baad0ca..d5a4094d1 100644 --- a/src/uu/nice/src/nice.rs +++ b/src/uu/nice/src/nice.rs @@ -46,20 +46,7 @@ process).", pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .setting(AppSettings::TrailingVarArg) - .version(crate_version!()) - .usage(&usage[..]) - .arg( - Arg::with_name(options::ADJUSTMENT) - .short("n") - .long(options::ADJUSTMENT) - .help("add N to the niceness (default is 10)") - .takes_value(true) - .allow_hyphen_values(true), - ) - .arg(Arg::with_name(options::COMMAND).multiple(true)) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let mut niceness = unsafe { nix::errno::Errno::clear(); @@ -120,3 +107,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 126 } } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .setting(AppSettings::TrailingVarArg) + .version(crate_version!()) + .arg( + Arg::with_name(options::ADJUSTMENT) + .short("n") + .long(options::ADJUSTMENT) + .help("add N to the niceness (default is 10)") + .takes_value(true) + .allow_hyphen_values(true), + ) + .arg(Arg::with_name(options::COMMAND).multiple(true)) +} diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index a3181e11f..81e76aa26 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -88,7 +88,62 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + // A mutable settings object, initialized with the defaults. + let mut settings = Settings { + header_numbering: NumberingStyle::NumberForNone, + body_numbering: NumberingStyle::NumberForAll, + footer_numbering: NumberingStyle::NumberForNone, + section_delimiter: ['\\', ':'], + starting_line_number: 1, + line_increment: 1, + join_blank_lines: 1, + number_width: 6, + number_format: NumberFormat::Right, + renumber: true, + number_separator: String::from("\t"), + }; + + // Update the settings from the command line options, and terminate the + // program if some options could not successfully be parsed. + let parse_errors = helper::parse_options(&mut settings, &matches); + if !parse_errors.is_empty() { + show_error!("Invalid arguments supplied."); + for message in &parse_errors { + println!("{}", message); + } + return 1; + } + + let mut read_stdin = false; + let files: Vec = match matches.values_of(options::FILE) { + Some(v) => v.clone().map(|v| v.to_owned()).collect(), + None => vec!["-".to_owned()], + }; + + for file in &files { + if file == "-" { + // If both file names and '-' are specified, we choose to treat first all + // regular files, and then read from stdin last. + read_stdin = true; + continue; + } + let path = Path::new(file); + let reader = File::open(path).unwrap(); + let mut buffer = BufReader::new(reader); + nl(&mut buffer, &settings); + } + + if read_stdin { + let mut buffer = BufReader::new(stdin()); + nl(&mut buffer, &settings); + } + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .name(NAME) .version(crate_version!()) .usage(USAGE) @@ -169,58 +224,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .help("use NUMBER columns for line numbers") .value_name("NUMBER"), ) - .get_matches_from(args); - - // A mutable settings object, initialized with the defaults. - let mut settings = Settings { - header_numbering: NumberingStyle::NumberForNone, - body_numbering: NumberingStyle::NumberForAll, - footer_numbering: NumberingStyle::NumberForNone, - section_delimiter: ['\\', ':'], - starting_line_number: 1, - line_increment: 1, - join_blank_lines: 1, - number_width: 6, - number_format: NumberFormat::Right, - renumber: true, - number_separator: String::from("\t"), - }; - - // Update the settings from the command line options, and terminate the - // program if some options could not successfully be parsed. - let parse_errors = helper::parse_options(&mut settings, &matches); - if !parse_errors.is_empty() { - show_error!("Invalid arguments supplied."); - for message in &parse_errors { - println!("{}", message); - } - return 1; - } - - let mut read_stdin = false; - let files: Vec = match matches.values_of(options::FILE) { - Some(v) => v.clone().map(|v| v.to_owned()).collect(), - None => vec!["-".to_owned()], - }; - - for file in &files { - if file == "-" { - // If both file names and '-' are specified, we choose to treat first all - // regular files, and then read from stdin last. - read_stdin = true; - continue; - } - let path = Path::new(file); - let reader = File::open(path).unwrap(); - let mut buffer = BufReader::new(reader); - nl(&mut buffer, &settings); - } - - if read_stdin { - let mut buffer = BufReader::new(stdin()); - nl(&mut buffer, &settings); - } - 0 } // nl implements the main functionality for an individual buffer. diff --git a/src/uu/nohup/src/nohup.rs b/src/uu/nohup/src/nohup.rs index 4e6fd7a7e..acc101e4e 100644 --- a/src/uu/nohup/src/nohup.rs +++ b/src/uu/nohup/src/nohup.rs @@ -45,19 +45,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .after_help(LONG_HELP) - .arg( - Arg::with_name(options::CMD) - .hidden(true) - .required(true) - .multiple(true), - ) - .setting(AppSettings::TrailingVarArg) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); replace_fds(); @@ -82,6 +70,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .after_help(LONG_HELP) + .arg( + Arg::with_name(options::CMD) + .hidden(true) + .required(true) + .multiple(true), + ) + .setting(AppSettings::TrailingVarArg) +} + fn replace_fds() { if atty::is(atty::Stream::Stdin) { let new_stdin = match File::open(Path::new("/dev/null")) { diff --git a/src/uu/nproc/src/nproc.rs b/src/uu/nproc/src/nproc.rs index 13f1862d2..1f284685b 100644 --- a/src/uu/nproc/src/nproc.rs +++ b/src/uu/nproc/src/nproc.rs @@ -33,24 +33,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(OPT_ALL) - .short("") - .long(OPT_ALL) - .help("print the number of cores available to the system"), - ) - .arg( - Arg::with_name(OPT_IGNORE) - .short("") - .long(OPT_IGNORE) - .takes_value(true) - .help("ignore up to N cores"), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let mut ignore = match matches.value_of(OPT_IGNORE) { Some(numstr) => match numstr.parse() { @@ -86,6 +69,25 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_ALL) + .short("") + .long(OPT_ALL) + .help("print the number of cores available to the system"), + ) + .arg( + Arg::with_name(OPT_IGNORE) + .short("") + .long(OPT_IGNORE) + .takes_value(true) + .help("ignore up to N cores"), + ) +} + #[cfg(any( target_os = "linux", target_vendor = "apple", diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index b534a9789..01f12c51b 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -156,10 +156,28 @@ fn parse_options(args: &ArgMatches) -> Result { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + let result = + parse_options(&matches).and_then(|options| match matches.values_of(options::NUMBER) { + Some(values) => handle_args(values, options), + None => handle_stdin(options), + }); + + match result { + Err(e) => { + std::io::stdout().flush().expect("error flushing stdout"); + show_error!("{}", e); + 1 + } + _ => 0, + } +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .after_help(LONG_HELP) .setting(AppSettings::AllowNegativeNumbers) .arg( @@ -224,20 +242,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .possible_values(&["up", "down", "from-zero", "towards-zero", "nearest"]), ) .arg(Arg::with_name(options::NUMBER).hidden(true).multiple(true)) - .get_matches_from(args); - - let result = - parse_options(&matches).and_then(|options| match matches.values_of(options::NUMBER) { - Some(values) => handle_args(values, options), - None => handle_stdin(options), - }); - - match result { - Err(e) => { - std::io::stdout().flush().expect("error flushing stdout"); - show_error!("{}", e); - 1 - } - _ => 0, - } } diff --git a/src/uu/od/src/od.rs b/src/uu/od/src/od.rs index bf6c39011..ec5bb595a 100644 --- a/src/uu/od/src/od.rs +++ b/src/uu/od/src/od.rs @@ -214,7 +214,45 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); - let clap_opts = clap::App::new(executable!()) + let clap_opts = uu_app(); + + let clap_matches = clap_opts + .clone() // Clone to reuse clap_opts to print help + .get_matches_from(args.clone()); + + let od_options = match OdOptions::new(clap_matches, args) { + Err(s) => { + crash!(1, "{}", s); + } + Ok(o) => o, + }; + + let mut input_offset = + InputOffset::new(od_options.radix, od_options.skip_bytes, od_options.label); + + let mut input = open_input_peek_reader( + &od_options.input_strings, + od_options.skip_bytes, + od_options.read_bytes, + ); + let mut input_decoder = InputDecoder::new( + &mut input, + od_options.line_bytes, + PEEK_BUFFER_SIZE, + od_options.byte_order, + ); + + let output_info = OutputInfo::new( + od_options.line_bytes, + &od_options.formats[..], + od_options.output_duplicates, + ); + + odfunc(&mut input_offset, &mut input_decoder, &output_info) +} + +pub fn uu_app() -> clap::App<'static, 'static> { + clap::App::new(executable!()) .version(crate_version!()) .about(ABOUT) .usage(USAGE) @@ -434,41 +472,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { AppSettings::DontDelimitTrailingValues, AppSettings::DisableVersion, AppSettings::DeriveDisplayOrder, - ]); - - let clap_matches = clap_opts - .clone() // Clone to reuse clap_opts to print help - .get_matches_from(args.clone()); - - let od_options = match OdOptions::new(clap_matches, args) { - Err(s) => { - crash!(1, "{}", s); - } - Ok(o) => o, - }; - - let mut input_offset = - InputOffset::new(od_options.radix, od_options.skip_bytes, od_options.label); - - let mut input = open_input_peek_reader( - &od_options.input_strings, - od_options.skip_bytes, - od_options.read_bytes, - ); - let mut input_decoder = InputDecoder::new( - &mut input, - od_options.line_bytes, - PEEK_BUFFER_SIZE, - od_options.byte_order, - ); - - let output_info = OutputInfo::new( - od_options.line_bytes, - &od_options.formats[..], - od_options.output_duplicates, - ); - - odfunc(&mut input_offset, &mut input_decoder, &output_info) + ]) } /// Loops through the input line by line, calling print_bytes to take care of the output. diff --git a/src/uu/paste/src/paste.rs b/src/uu/paste/src/paste.rs index f2fa3c81c..7f7969687 100644 --- a/src/uu/paste/src/paste.rs +++ b/src/uu/paste/src/paste.rs @@ -37,7 +37,22 @@ fn read_line( } pub fn uumain(args: impl uucore::Args) -> i32 { - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + let serial = matches.is_present(options::SERIAL); + let delimiters = matches.value_of(options::DELIMITER).unwrap().to_owned(); + let files = matches + .values_of(options::FILE) + .unwrap() + .map(|s| s.to_owned()) + .collect(); + paste(files, serial, delimiters); + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) .arg( @@ -61,18 +76,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .multiple(true) .default_value("-"), ) - .get_matches_from(args); - - let serial = matches.is_present(options::SERIAL); - let delimiters = matches.value_of(options::DELIMITER).unwrap().to_owned(); - let files = matches - .values_of(options::FILE) - .unwrap() - .map(|s| s.to_owned()) - .collect(); - paste(files, serial, delimiters); - - 0 } fn paste(filenames: Vec, serial: bool, delimiters: String) { diff --git a/src/uu/pathchk/src/pathchk.rs b/src/uu/pathchk/src/pathchk.rs index 358881509..335266456 100644 --- a/src/uu/pathchk/src/pathchk.rs +++ b/src/uu/pathchk/src/pathchk.rs @@ -49,27 +49,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::POSIX) - .short("p") - .help("check for most POSIX systems"), - ) - .arg( - Arg::with_name(options::POSIX_SPECIAL) - .short("P") - .help(r#"check for empty names and leading "-""#), - ) - .arg( - Arg::with_name(options::PORTABILITY) - .long(options::PORTABILITY) - .help("check for all POSIX systems (equivalent to -p -P)"), - ) - .arg(Arg::with_name(options::PATH).hidden(true).multiple(true)) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); // set working mode let is_posix = matches.values_of(options::POSIX).is_some(); @@ -115,6 +95,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::POSIX) + .short("p") + .help("check for most POSIX systems"), + ) + .arg( + Arg::with_name(options::POSIX_SPECIAL) + .short("P") + .help(r#"check for empty names and leading "-""#), + ) + .arg( + Arg::with_name(options::PORTABILITY) + .long(options::PORTABILITY) + .help("check for all POSIX systems (equivalent to -p -P)"), + ) + .arg(Arg::with_name(options::PATH).hidden(true).multiple(true)) +} + // check a path, given as a slice of it's components and an operating mode fn check_path(mode: &Mode, path: &[String]) -> bool { match *mode { diff --git a/src/uu/pinky/src/pinky.rs b/src/uu/pinky/src/pinky.rs index 33dcff274..16bcfd3c9 100644 --- a/src/uu/pinky/src/pinky.rs +++ b/src/uu/pinky/src/pinky.rs @@ -60,62 +60,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let after_help = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&after_help[..]) - .arg( - Arg::with_name(options::LONG_FORMAT) - .short("l") - .requires(options::USER) - .help("produce long format output for the specified USERs"), - ) - .arg( - Arg::with_name(options::OMIT_HOME_DIR) - .short("b") - .help("omit the user's home directory and shell in long format"), - ) - .arg( - Arg::with_name(options::OMIT_PROJECT_FILE) - .short("h") - .help("omit the user's project file in long format"), - ) - .arg( - Arg::with_name(options::OMIT_PLAN_FILE) - .short("p") - .help("omit the user's plan file in long format"), - ) - .arg( - Arg::with_name(options::SHORT_FORMAT) - .short("s") - .help("do short format output, this is the default"), - ) - .arg( - Arg::with_name(options::OMIT_HEADINGS) - .short("f") - .help("omit the line of column headings in short format"), - ) - .arg( - Arg::with_name(options::OMIT_NAME) - .short("w") - .help("omit the user's full name in short format"), - ) - .arg( - Arg::with_name(options::OMIT_NAME_HOST) - .short("i") - .help("omit the user's full name and remote host in short format"), - ) - .arg( - Arg::with_name(options::OMIT_NAME_HOST_TIME) - .short("q") - .help("omit the user's full name, remote host and idle time in short format"), - ) - .arg( - Arg::with_name(options::USER) - .takes_value(true) - .multiple(true), - ) .get_matches_from(args); let users: Vec = matches @@ -182,6 +129,63 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::LONG_FORMAT) + .short("l") + .requires(options::USER) + .help("produce long format output for the specified USERs"), + ) + .arg( + Arg::with_name(options::OMIT_HOME_DIR) + .short("b") + .help("omit the user's home directory and shell in long format"), + ) + .arg( + Arg::with_name(options::OMIT_PROJECT_FILE) + .short("h") + .help("omit the user's project file in long format"), + ) + .arg( + Arg::with_name(options::OMIT_PLAN_FILE) + .short("p") + .help("omit the user's plan file in long format"), + ) + .arg( + Arg::with_name(options::SHORT_FORMAT) + .short("s") + .help("do short format output, this is the default"), + ) + .arg( + Arg::with_name(options::OMIT_HEADINGS) + .short("f") + .help("omit the line of column headings in short format"), + ) + .arg( + Arg::with_name(options::OMIT_NAME) + .short("w") + .help("omit the user's full name in short format"), + ) + .arg( + Arg::with_name(options::OMIT_NAME_HOST) + .short("i") + .help("omit the user's full name and remote host in short format"), + ) + .arg( + Arg::with_name(options::OMIT_NAME_HOST_TIME) + .short("q") + .help("omit the user's full name, remote host and idle time in short format"), + ) + .arg( + Arg::with_name(options::USER) + .takes_value(true) + .multiple(true), + ) +} + struct Pinky { include_idle: bool, include_heading: bool, diff --git a/src/uu/pr/Cargo.toml b/src/uu/pr/Cargo.toml index 6d9ec2304..de519161a 100644 --- a/src/uu/pr/Cargo.toml +++ b/src/uu/pr/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/pr.rs" [dependencies] +clap = "2.33.3" uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["utmpx", "entries"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } getopts = "0.2.21" diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index 239a0970f..d6b9e8ca3 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -23,6 +23,7 @@ use std::fs::{metadata, File}; use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Stdout, Write}; #[cfg(unix)] use std::os::unix::fs::FileTypeExt; +use uucore::executable; type IOError = std::io::Error; @@ -167,6 +168,11 @@ quick_error! { } } +pub fn uu_app() -> clap::App<'static, 'static> { + // TODO: migrate to clap to get more shell completions + clap::App::new(executable!()) +} + pub fn uumain(args: impl uucore::Args) -> i32 { let args = args .collect_str(uucore::InvalidEncodingHandling::Ignore) diff --git a/src/uu/printenv/src/printenv.rs b/src/uu/printenv/src/printenv.rs index 5c2594835..6e0ca7157 100644 --- a/src/uu/printenv/src/printenv.rs +++ b/src/uu/printenv/src/printenv.rs @@ -26,23 +26,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(OPT_NULL) - .short("0") - .long(OPT_NULL) - .help("end each output line with 0 byte rather than newline"), - ) - .arg( - Arg::with_name(ARG_VARIABLES) - .multiple(true) - .takes_value(true) - .min_values(1), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let variables: Vec = matches .values_of(ARG_VARIABLES) @@ -69,3 +53,21 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_NULL) + .short("0") + .long(OPT_NULL) + .help("end each output line with 0 byte rather than newline"), + ) + .arg( + Arg::with_name(ARG_VARIABLES) + .multiple(true) + .takes_value(true) + .min_values(1), + ) +} diff --git a/src/uu/printf/Cargo.toml b/src/uu/printf/Cargo.toml index bc77d31be..13d54fcca 100644 --- a/src/uu/printf/Cargo.toml +++ b/src/uu/printf/Cargo.toml @@ -18,6 +18,7 @@ edition = "2018" path = "src/printf.rs" [dependencies] +clap = "2.33.3" itertools = "0.8.0" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/printf/src/printf.rs b/src/uu/printf/src/printf.rs index 88d18838d..efa9aea57 100644 --- a/src/uu/printf/src/printf.rs +++ b/src/uu/printf/src/printf.rs @@ -2,14 +2,18 @@ // spell-checker:ignore (change!) each's // spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr +#[macro_use] +extern crate uucore; + +use clap::{crate_version, App, Arg}; use uucore::InvalidEncodingHandling; mod cli; mod memo; mod tokenize; -static NAME: &str = "printf"; -static VERSION: &str = env!("CARGO_PKG_VERSION"); +const VERSION: &str = "version"; +const HELP: &str = "help"; static LONGHELP_LEAD: &str = "printf USAGE: printf FORMATSTRING [ARGUMENT]... @@ -290,10 +294,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if formatstr == "--help" { print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY); } else if formatstr == "--version" { - println!("{} {}", NAME, VERSION); + println!("{} {}", executable!(), crate_version!()); } else { let printf_args = &args[2..]; memo::Memo::run_all(formatstr, printf_args); } 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .arg(Arg::with_name(VERSION).long(VERSION)) + .arg(Arg::with_name(HELP).long(HELP)) +} diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index 31da8f05d..01b14bc4d 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -638,7 +638,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .accept_any(); // let mut opts = Options::new(); - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + let input_files: Vec = match &matches.values_of(options::FILE) { + Some(v) => v.clone().map(|v| v.to_owned()).collect(), + None => vec!["-".to_string()], + }; + + let config = get_config(&matches); + let word_filter = WordFilter::new(&matches, &config); + let file_map = read_input(&input_files, &config); + let word_set = create_word_set(&config, &word_filter, &file_map); + let output_file = if !config.gnu_ext && matches.args.len() == 2 { + matches.value_of(options::FILE).unwrap_or("-").to_string() + } else { + "-".to_owned() + }; + write_traditional_output(&config, &file_map, &word_set, &output_file); + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .name(NAME) .version(crate_version!()) .usage(BRIEF) @@ -762,22 +783,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .value_name("NUMBER") .takes_value(true), ) - .get_matches_from(args); - - let input_files: Vec = match &matches.values_of(options::FILE) { - Some(v) => v.clone().map(|v| v.to_owned()).collect(), - None => vec!["-".to_string()], - }; - - let config = get_config(&matches); - let word_filter = WordFilter::new(&matches, &config); - let file_map = read_input(&input_files, &config); - let word_set = create_word_set(&config, &word_filter, &file_map); - let output_file = if !config.gnu_ext && matches.args.len() == 2 { - matches.value_of(options::FILE).unwrap_or("-").to_string() - } else { - "-".to_owned() - }; - write_traditional_output(&config, &file_map, &word_set, &output_file); - 0 } diff --git a/src/uu/pwd/src/pwd.rs b/src/uu/pwd/src/pwd.rs index 9b4e5c600..764a63a88 100644 --- a/src/uu/pwd/src/pwd.rs +++ b/src/uu/pwd/src/pwd.rs @@ -39,23 +39,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(OPT_LOGICAL) - .short("L") - .long(OPT_LOGICAL) - .help("use PWD from environment, even if it contains symlinks"), - ) - .arg( - Arg::with_name(OPT_PHYSICAL) - .short("P") - .long(OPT_PHYSICAL) - .help("avoid all symlinks"), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); match env::current_dir() { Ok(logical_path) => { @@ -73,3 +57,21 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_LOGICAL) + .short("L") + .long(OPT_LOGICAL) + .help("use PWD from environment, even if it contains symlinks"), + ) + .arg( + Arg::with_name(OPT_PHYSICAL) + .short("P") + .long(OPT_PHYSICAL) + .help("avoid all symlinks"), + ) +} diff --git a/src/uu/readlink/src/readlink.rs b/src/uu/readlink/src/readlink.rs index 02e286315..826fa0254 100644 --- a/src/uu/readlink/src/readlink.rs +++ b/src/uu/readlink/src/readlink.rs @@ -35,69 +35,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(OPT_CANONICALIZE) - .short("f") - .long(OPT_CANONICALIZE) - .help( - "canonicalize by following every symlink in every component of the \ - given name recursively; all but the last component must exist", - ), - ) - .arg( - Arg::with_name(OPT_CANONICALIZE_EXISTING) - .short("e") - .long("canonicalize-existing") - .help( - "canonicalize by following every symlink in every component of the \ - given name recursively, all components must exist", - ), - ) - .arg( - Arg::with_name(OPT_CANONICALIZE_MISSING) - .short("m") - .long(OPT_CANONICALIZE_MISSING) - .help( - "canonicalize by following every symlink in every component of the \ - given name recursively, without requirements on components existence", - ), - ) - .arg( - Arg::with_name(OPT_NO_NEWLINE) - .short("n") - .long(OPT_NO_NEWLINE) - .help("do not output the trailing delimiter"), - ) - .arg( - Arg::with_name(OPT_QUIET) - .short("q") - .long(OPT_QUIET) - .help("suppress most error messages"), - ) - .arg( - Arg::with_name(OPT_SILENT) - .short("s") - .long(OPT_SILENT) - .help("suppress most error messages"), - ) - .arg( - Arg::with_name(OPT_VERBOSE) - .short("v") - .long(OPT_VERBOSE) - .help("report error message"), - ) - .arg( - Arg::with_name(OPT_ZERO) - .short("z") - .long(OPT_ZERO) - .help("separate output with NUL rather than newline"), - ) - .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true)) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let mut no_newline = matches.is_present(OPT_NO_NEWLINE); let use_zero = matches.is_present(OPT_ZERO); @@ -159,6 +97,70 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_CANONICALIZE) + .short("f") + .long(OPT_CANONICALIZE) + .help( + "canonicalize by following every symlink in every component of the \ + given name recursively; all but the last component must exist", + ), + ) + .arg( + Arg::with_name(OPT_CANONICALIZE_EXISTING) + .short("e") + .long("canonicalize-existing") + .help( + "canonicalize by following every symlink in every component of the \ + given name recursively, all components must exist", + ), + ) + .arg( + Arg::with_name(OPT_CANONICALIZE_MISSING) + .short("m") + .long(OPT_CANONICALIZE_MISSING) + .help( + "canonicalize by following every symlink in every component of the \ + given name recursively, without requirements on components existence", + ), + ) + .arg( + Arg::with_name(OPT_NO_NEWLINE) + .short("n") + .long(OPT_NO_NEWLINE) + .help("do not output the trailing delimiter"), + ) + .arg( + Arg::with_name(OPT_QUIET) + .short("q") + .long(OPT_QUIET) + .help("suppress most error messages"), + ) + .arg( + Arg::with_name(OPT_SILENT) + .short("s") + .long(OPT_SILENT) + .help("suppress most error messages"), + ) + .arg( + Arg::with_name(OPT_VERBOSE) + .short("v") + .long(OPT_VERBOSE) + .help("report error message"), + ) + .arg( + Arg::with_name(OPT_ZERO) + .short("z") + .long(OPT_ZERO) + .help("separate output with NUL rather than newline"), + ) + .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true)) +} + fn show(path: &Path, no_newline: bool, use_zero: bool) { let path = path.to_str().unwrap(); if use_zero { diff --git a/src/uu/realpath/src/realpath.rs b/src/uu/realpath/src/realpath.rs index 1a96b7f80..fe2ad4ccc 100644 --- a/src/uu/realpath/src/realpath.rs +++ b/src/uu/realpath/src/realpath.rs @@ -29,10 +29,35 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + /* the list of files */ + + let paths: Vec = matches + .values_of(ARG_FILES) + .unwrap() + .map(PathBuf::from) + .collect(); + + let strip = matches.is_present(OPT_STRIP); + let zero = matches.is_present(OPT_ZERO); + let quiet = matches.is_present(OPT_QUIET); + let mut retcode = 0; + for path in &paths { + if let Err(e) = resolve_path(path, strip, zero) { + if !quiet { + show_error!("{}: {}", e, path.display()); + } + retcode = 1 + }; + } + retcode +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .arg( Arg::with_name(OPT_QUIET) .short("q") @@ -58,29 +83,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .required(true) .min_values(1), ) - .get_matches_from(args); - - /* the list of files */ - - let paths: Vec = matches - .values_of(ARG_FILES) - .unwrap() - .map(PathBuf::from) - .collect(); - - let strip = matches.is_present(OPT_STRIP); - let zero = matches.is_present(OPT_ZERO); - let quiet = matches.is_present(OPT_QUIET); - let mut retcode = 0; - for path in &paths { - if let Err(e) = resolve_path(path, strip, zero) { - if !quiet { - show_error!("{}: {}", e, path.display()); - } - retcode = 1 - }; - } - retcode } /// Resolve a path to an absolute form and print it. diff --git a/src/uu/relpath/src/relpath.rs b/src/uu/relpath/src/relpath.rs index a997e1c5f..cb0fba7cc 100644 --- a/src/uu/relpath/src/relpath.rs +++ b/src/uu/relpath/src/relpath.rs @@ -35,26 +35,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .accept_any(); let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::DIR) - .short("d") - .takes_value(true) - .help("If any of FROM and TO is not subpath of DIR, output absolute path instead of relative"), - ) - .arg( - Arg::with_name(options::TO) - .required(true) - .takes_value(true), - ) - .arg( - Arg::with_name(options::FROM) - .takes_value(true), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let to = Path::new(matches.value_of(options::TO).unwrap()).to_path_buf(); // required let from = match matches.value_of(options::FROM) { @@ -99,3 +80,24 @@ pub fn uumain(args: impl uucore::Args) -> i32 { println!("{}", result.display()); 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::DIR) + .short("d") + .takes_value(true) + .help("If any of FROM and TO is not subpath of DIR, output absolute path instead of relative"), + ) + .arg( + Arg::with_name(options::TO) + .required(true) + .takes_value(true), + ) + .arg( + Arg::with_name(options::FROM) + .takes_value(true), + ) +} diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 40a24cea7..259d1ab39 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -77,11 +77,72 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let long_usage = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&long_usage[..]) + .get_matches_from(args); + + let files: Vec = matches + .values_of(ARG_FILES) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + + let force = matches.is_present(OPT_FORCE); + + if files.is_empty() && !force { + // Still check by hand and not use clap + // Because "rm -f" is a thing + show_error!("missing an argument"); + show_error!("for help, try '{0} --help'", executable!()); + return 1; + } else { + let options = Options { + force, + interactive: { + if matches.is_present(OPT_PROMPT) { + InteractiveMode::Always + } else if matches.is_present(OPT_PROMPT_MORE) { + InteractiveMode::Once + } else if matches.is_present(OPT_INTERACTIVE) { + match matches.value_of(OPT_INTERACTIVE).unwrap() { + "none" => InteractiveMode::None, + "once" => InteractiveMode::Once, + "always" => InteractiveMode::Always, + val => crash!(1, "Invalid argument to interactive ({})", val), + } + } else { + InteractiveMode::None + } + }, + one_fs: matches.is_present(OPT_ONE_FILE_SYSTEM), + preserve_root: !matches.is_present(OPT_NO_PRESERVE_ROOT), + recursive: matches.is_present(OPT_RECURSIVE) || matches.is_present(OPT_RECURSIVE_R), + dir: matches.is_present(OPT_DIR), + verbose: matches.is_present(OPT_VERBOSE), + }; + if options.interactive == InteractiveMode::Once && (options.recursive || files.len() > 3) { + let msg = if options.recursive { + "Remove all arguments recursively? " + } else { + "Remove all arguments? " + }; + if !prompt(msg) { + return 0; + } + } + + if remove(files, options) { + return 1; + } + } + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) .arg( Arg::with_name(OPT_FORCE) @@ -151,63 +212,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .takes_value(true) .min_values(1) ) - .get_matches_from(args); - - let files: Vec = matches - .values_of(ARG_FILES) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - - let force = matches.is_present(OPT_FORCE); - - if files.is_empty() && !force { - // Still check by hand and not use clap - // Because "rm -f" is a thing - show_error!("missing an argument"); - show_error!("for help, try '{0} --help'", executable!()); - return 1; - } else { - let options = Options { - force, - interactive: { - if matches.is_present(OPT_PROMPT) { - InteractiveMode::Always - } else if matches.is_present(OPT_PROMPT_MORE) { - InteractiveMode::Once - } else if matches.is_present(OPT_INTERACTIVE) { - match matches.value_of(OPT_INTERACTIVE).unwrap() { - "none" => InteractiveMode::None, - "once" => InteractiveMode::Once, - "always" => InteractiveMode::Always, - val => crash!(1, "Invalid argument to interactive ({})", val), - } - } else { - InteractiveMode::None - } - }, - one_fs: matches.is_present(OPT_ONE_FILE_SYSTEM), - preserve_root: !matches.is_present(OPT_NO_PRESERVE_ROOT), - recursive: matches.is_present(OPT_RECURSIVE) || matches.is_present(OPT_RECURSIVE_R), - dir: matches.is_present(OPT_DIR), - verbose: matches.is_present(OPT_VERBOSE), - }; - if options.interactive == InteractiveMode::Once && (options.recursive || files.len() > 3) { - let msg = if options.recursive { - "Remove all arguments recursively? " - } else { - "Remove all arguments? " - }; - if !prompt(msg) { - return 0; - } - } - - if remove(files, options) { - return 1; - } - } - - 0 } // TODO: implement one-file-system (this may get partially implemented in walkdir) diff --git a/src/uu/rmdir/src/rmdir.rs b/src/uu/rmdir/src/rmdir.rs index fc22cca09..8dbaf79a8 100644 --- a/src/uu/rmdir/src/rmdir.rs +++ b/src/uu/rmdir/src/rmdir.rs @@ -33,10 +33,29 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + let dirs: Vec = matches + .values_of(ARG_DIRS) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + + let ignore = matches.is_present(OPT_IGNORE_FAIL_NON_EMPTY); + let parents = matches.is_present(OPT_PARENTS); + let verbose = matches.is_present(OPT_VERBOSE); + + match remove(dirs, ignore, parents, verbose) { + Ok(()) => ( /* pass */ ), + Err(e) => return e, + } + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .arg( Arg::with_name(OPT_IGNORE_FAIL_NON_EMPTY) .long(OPT_IGNORE_FAIL_NON_EMPTY) @@ -64,23 +83,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .min_values(1) .required(true), ) - .get_matches_from(args); - - let dirs: Vec = matches - .values_of(ARG_DIRS) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - - let ignore = matches.is_present(OPT_IGNORE_FAIL_NON_EMPTY); - let parents = matches.is_present(OPT_PARENTS); - let verbose = matches.is_present(OPT_VERBOSE); - - match remove(dirs, ignore, parents, verbose) { - Ok(()) => ( /* pass */ ), - Err(e) => return e, - } - - 0 } fn remove(dirs: Vec, ignore: bool, parents: bool, verbose: bool) -> Result<(), i32> { diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 954d15f2f..50a93d3af 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -87,42 +87,7 @@ impl FromStr for Number { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .setting(AppSettings::AllowLeadingHyphen) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(OPT_SEPARATOR) - .short("s") - .long("separator") - .help("Separator character (defaults to \\n)") - .takes_value(true) - .number_of_values(1), - ) - .arg( - Arg::with_name(OPT_TERMINATOR) - .short("t") - .long("terminator") - .help("Terminator character (defaults to \\n)") - .takes_value(true) - .number_of_values(1), - ) - .arg( - Arg::with_name(OPT_WIDTHS) - .short("w") - .long("widths") - .help("Equalize widths of all numbers by padding with zeros"), - ) - .arg( - Arg::with_name(ARG_NUMBERS) - .multiple(true) - .takes_value(true) - .allow_hyphen_values(true) - .max_values(3) - .required(true), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let numbers = matches.values_of(ARG_NUMBERS).unwrap().collect::>(); @@ -197,6 +162,43 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .setting(AppSettings::AllowLeadingHyphen) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(OPT_SEPARATOR) + .short("s") + .long("separator") + .help("Separator character (defaults to \\n)") + .takes_value(true) + .number_of_values(1), + ) + .arg( + Arg::with_name(OPT_TERMINATOR) + .short("t") + .long("terminator") + .help("Terminator character (defaults to \\n)") + .takes_value(true) + .number_of_values(1), + ) + .arg( + Arg::with_name(OPT_WIDTHS) + .short("w") + .long("widths") + .help("Equalize widths of all numbers by padding with zeros"), + ) + .arg( + Arg::with_name(ARG_NUMBERS) + .multiple(true) + .takes_value(true) + .allow_hyphen_values(true) + .max_values(3) + .required(true), + ) +} + fn done_printing(next: &T, increment: &T, last: &T) -> bool { if increment >= &T::zero() { next > last diff --git a/src/uu/shred/src/shred.rs b/src/uu/shred/src/shred.rs index 177143811..90336ea95 100644 --- a/src/uu/shred/src/shred.rs +++ b/src/uu/shred/src/shred.rs @@ -272,62 +272,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let app = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .after_help(AFTER_HELP) - .usage(&usage[..]) - .arg( - Arg::with_name(options::FORCE) - .long(options::FORCE) - .short("f") - .help("change permissions to allow writing if necessary"), - ) - .arg( - Arg::with_name(options::ITERATIONS) - .long(options::ITERATIONS) - .short("n") - .help("overwrite N times instead of the default (3)") - .value_name("NUMBER") - .default_value("3"), - ) - .arg( - Arg::with_name(options::SIZE) - .long(options::SIZE) - .short("s") - .takes_value(true) - .value_name("N") - .help("shred this many bytes (suffixes like K, M, G accepted)"), - ) - .arg( - Arg::with_name(options::REMOVE) - .short("u") - .long(options::REMOVE) - .help("truncate and remove file after overwriting; See below"), - ) - .arg( - Arg::with_name(options::VERBOSE) - .long(options::VERBOSE) - .short("v") - .help("show progress"), - ) - .arg( - Arg::with_name(options::EXACT) - .long(options::EXACT) - .short("x") - .help( - "do not round file sizes up to the next full block;\n\ - this is the default for non-regular files", - ), - ) - .arg( - Arg::with_name(options::ZERO) - .long(options::ZERO) - .short("z") - .help("add a final overwrite with zeros to hide shredding"), - ) - // Positional arguments - .arg(Arg::with_name(options::FILE).hidden(true).multiple(true)); + let app = uu_app().usage(&usage[..]); let matches = app.get_matches_from(args); @@ -384,6 +329,64 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .after_help(AFTER_HELP) + .arg( + Arg::with_name(options::FORCE) + .long(options::FORCE) + .short("f") + .help("change permissions to allow writing if necessary"), + ) + .arg( + Arg::with_name(options::ITERATIONS) + .long(options::ITERATIONS) + .short("n") + .help("overwrite N times instead of the default (3)") + .value_name("NUMBER") + .default_value("3"), + ) + .arg( + Arg::with_name(options::SIZE) + .long(options::SIZE) + .short("s") + .takes_value(true) + .value_name("N") + .help("shred this many bytes (suffixes like K, M, G accepted)"), + ) + .arg( + Arg::with_name(options::REMOVE) + .short("u") + .long(options::REMOVE) + .help("truncate and remove file after overwriting; See below"), + ) + .arg( + Arg::with_name(options::VERBOSE) + .long(options::VERBOSE) + .short("v") + .help("show progress"), + ) + .arg( + Arg::with_name(options::EXACT) + .long(options::EXACT) + .short("x") + .help( + "do not round file sizes up to the next full block;\n\ + this is the default for non-regular files", + ), + ) + .arg( + Arg::with_name(options::ZERO) + .long(options::ZERO) + .short("z") + .help("add a final overwrite with zeros to hide shredding"), + ) + // Positional arguments + .arg(Arg::with_name(options::FILE).hidden(true).multiple(true)) +} + // TODO: Add support for all postfixes here up to and including EiB // http://www.gnu.org/software/coreutils/manual/coreutils.html#Block-size fn get_size(size_str_opt: Option) -> Option { diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index 2d1f558de..4690d1c6e 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -56,7 +56,66 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + let mode = if let Some(args) = matches.values_of(options::ECHO) { + Mode::Echo(args.map(String::from).collect()) + } else if let Some(range) = matches.value_of(options::INPUT_RANGE) { + match parse_range(range) { + Ok(m) => Mode::InputRange(m), + Err(msg) => { + crash!(1, "{}", msg); + } + } + } else { + Mode::Default(matches.value_of(options::FILE).unwrap_or("-").to_string()) + }; + + let options = Options { + head_count: match matches.value_of(options::HEAD_COUNT) { + Some(count) => match count.parse::() { + Ok(val) => val, + Err(_) => { + show_error!("invalid line count: '{}'", count); + return 1; + } + }, + None => std::usize::MAX, + }, + output: matches.value_of(options::OUTPUT).map(String::from), + random_source: matches.value_of(options::RANDOM_SOURCE).map(String::from), + repeat: matches.is_present(options::REPEAT), + sep: if matches.is_present(options::ZERO_TERMINATED) { + 0x00_u8 + } else { + 0x0a_u8 + }, + }; + + match mode { + Mode::Echo(args) => { + let mut evec = args.iter().map(String::as_bytes).collect::>(); + find_seps(&mut evec, options.sep); + shuf_bytes(&mut evec, options); + } + Mode::InputRange((b, e)) => { + let rvec = (b..e).map(|x| format!("{}", x)).collect::>(); + let mut rvec = rvec.iter().map(String::as_bytes).collect::>(); + shuf_bytes(&mut rvec, options); + } + Mode::Default(filename) => { + let fdata = read_input_file(&filename); + let mut fdata = vec![&fdata[..]]; + find_seps(&mut fdata, options.sep); + shuf_bytes(&mut fdata, options); + } + } + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .name(NAME) .version(crate_version!()) .template(TEMPLATE) @@ -118,62 +177,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .help("line delimiter is NUL, not newline"), ) .arg(Arg::with_name(options::FILE).takes_value(true)) - .get_matches_from(args); - - let mode = if let Some(args) = matches.values_of(options::ECHO) { - Mode::Echo(args.map(String::from).collect()) - } else if let Some(range) = matches.value_of(options::INPUT_RANGE) { - match parse_range(range) { - Ok(m) => Mode::InputRange(m), - Err(msg) => { - crash!(1, "{}", msg); - } - } - } else { - Mode::Default(matches.value_of(options::FILE).unwrap_or("-").to_string()) - }; - - let options = Options { - head_count: match matches.value_of(options::HEAD_COUNT) { - Some(count) => match count.parse::() { - Ok(val) => val, - Err(_) => { - show_error!("invalid line count: '{}'", count); - return 1; - } - }, - None => std::usize::MAX, - }, - output: matches.value_of(options::OUTPUT).map(String::from), - random_source: matches.value_of(options::RANDOM_SOURCE).map(String::from), - repeat: matches.is_present(options::REPEAT), - sep: if matches.is_present(options::ZERO_TERMINATED) { - 0x00_u8 - } else { - 0x0a_u8 - }, - }; - - match mode { - Mode::Echo(args) => { - let mut evec = args.iter().map(String::as_bytes).collect::>(); - find_seps(&mut evec, options.sep); - shuf_bytes(&mut evec, options); - } - Mode::InputRange((b, e)) => { - let rvec = (b..e).map(|x| format!("{}", x)).collect::>(); - let mut rvec = rvec.iter().map(String::as_bytes).collect::>(); - shuf_bytes(&mut rvec, options); - } - Mode::Default(filename) => { - let fdata = read_input_file(&filename); - let mut fdata = vec![&fdata[..]]; - find_seps(&mut fdata, options.sep); - shuf_bytes(&mut fdata, options); - } - } - - 0 } fn read_input_file(filename: &str) -> Vec { diff --git a/src/uu/sleep/src/sleep.rs b/src/uu/sleep/src/sleep.rs index c78c1cfc9..ada3336df 100644 --- a/src/uu/sleep/src/sleep.rs +++ b/src/uu/sleep/src/sleep.rs @@ -35,10 +35,20 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + if let Some(values) = matches.values_of(options::NUMBER) { + let numbers = values.collect(); + sleep(numbers); + } + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .after_help(LONG_HELP) .arg( Arg::with_name(options::NUMBER) @@ -49,14 +59,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .multiple(true) .required(true), ) - .get_matches_from(args); - - if let Some(values) = matches.values_of(options::NUMBER) { - let numbers = values.collect(); - sleep(numbers); - } - - 0 } fn sleep(args: Vec<&str>) { diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index d0e574627..fb0241945 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -944,10 +944,170 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let mut settings: GlobalSettings = Default::default(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + settings.debug = matches.is_present(options::DEBUG); + + // check whether user specified a zero terminated list of files for input, otherwise read files from args + let mut files: Vec = if matches.is_present(options::FILES0_FROM) { + let files0_from: Vec = matches + .values_of(options::FILES0_FROM) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + + let mut files = Vec::new(); + for path in &files0_from { + let reader = open(path.as_str()); + let buf_reader = BufReader::new(reader); + for line in buf_reader.split(b'\0').flatten() { + files.push( + std::str::from_utf8(&line) + .expect("Could not parse string from zero terminated input.") + .to_string(), + ); + } + } + files + } else { + matches + .values_of(options::FILES) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default() + }; + + settings.mode = if matches.is_present(options::modes::HUMAN_NUMERIC) + || matches.value_of(options::modes::SORT) == Some("human-numeric") + { + SortMode::HumanNumeric + } else if matches.is_present(options::modes::MONTH) + || matches.value_of(options::modes::SORT) == Some("month") + { + SortMode::Month + } else if matches.is_present(options::modes::GENERAL_NUMERIC) + || matches.value_of(options::modes::SORT) == Some("general-numeric") + { + SortMode::GeneralNumeric + } else if matches.is_present(options::modes::NUMERIC) + || matches.value_of(options::modes::SORT) == Some("numeric") + { + SortMode::Numeric + } else if matches.is_present(options::modes::VERSION) + || matches.value_of(options::modes::SORT) == Some("version") + { + SortMode::Version + } else if matches.is_present(options::modes::RANDOM) + || matches.value_of(options::modes::SORT) == Some("random") + { + settings.salt = get_rand_string(); + SortMode::Random + } else { + SortMode::Default + }; + + settings.dictionary_order = matches.is_present(options::DICTIONARY_ORDER); + settings.ignore_non_printing = matches.is_present(options::IGNORE_NONPRINTING); + if matches.is_present(options::PARALLEL) { + // "0" is default - threads = num of cores + settings.threads = matches + .value_of(options::PARALLEL) + .map(String::from) + .unwrap_or_else(|| "0".to_string()); + env::set_var("RAYON_NUM_THREADS", &settings.threads); + } + + settings.buffer_size = matches + .value_of(options::BUF_SIZE) + .map_or(DEFAULT_BUF_SIZE, |s| { + GlobalSettings::parse_byte_count(s) + .unwrap_or_else(|e| crash!(2, "{}", format_error_message(e, s, options::BUF_SIZE))) + }); + + settings.tmp_dir = matches + .value_of(options::TMP_DIR) + .map(PathBuf::from) + .unwrap_or_else(env::temp_dir); + + settings.compress_prog = matches.value_of(options::COMPRESS_PROG).map(String::from); + + if let Some(n_merge) = matches.value_of(options::BATCH_SIZE) { + settings.merge_batch_size = n_merge + .parse() + .unwrap_or_else(|_| crash!(2, "invalid --batch-size argument '{}'", n_merge)); + } + + settings.zero_terminated = matches.is_present(options::ZERO_TERMINATED); + settings.merge = matches.is_present(options::MERGE); + + settings.check = matches.is_present(options::check::CHECK); + if matches.is_present(options::check::CHECK_SILENT) + || matches!( + matches.value_of(options::check::CHECK), + Some(options::check::SILENT) | Some(options::check::QUIET) + ) + { + settings.check_silent = true; + settings.check = true; + }; + + settings.ignore_case = matches.is_present(options::IGNORE_CASE); + + settings.ignore_leading_blanks = matches.is_present(options::IGNORE_LEADING_BLANKS); + + settings.output_file = matches.value_of(options::OUTPUT).map(String::from); + settings.reverse = matches.is_present(options::REVERSE); + settings.stable = matches.is_present(options::STABLE); + settings.unique = matches.is_present(options::UNIQUE); + + if files.is_empty() { + /* if no file, default to stdin */ + files.push("-".to_owned()); + } else if settings.check && files.len() != 1 { + crash!(1, "extra operand `{}' not allowed with -c", files[1]) + } + + if let Some(arg) = matches.args.get(options::SEPARATOR) { + let separator = arg.vals[0].to_string_lossy(); + let separator = separator; + if separator.len() != 1 { + crash!(1, "separator must be exactly one character long"); + } + settings.separator = Some(separator.chars().next().unwrap()) + } + + if let Some(values) = matches.values_of(options::KEY) { + for value in values { + settings + .selectors + .push(FieldSelector::parse(value, &settings)); + } + } + + if !matches.is_present(options::KEY) { + // add a default selector matching the whole line + let key_settings = KeySettings::from(&settings); + settings.selectors.push( + FieldSelector::new( + KeyPosition { + field: 1, + char: 1, + ignore_blanks: key_settings.ignore_blanks, + }, + None, + key_settings, + ) + .unwrap(), + ); + } + + settings.init_precomputed(); + + exec(&files, &settings) +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .arg( Arg::with_name(options::modes::SORT) .long(options::modes::SORT) @@ -1169,164 +1329,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .help("underline the parts of the line that are actually used for sorting"), ) .arg(Arg::with_name(options::FILES).multiple(true).takes_value(true)) - .get_matches_from(args); - - settings.debug = matches.is_present(options::DEBUG); - - // check whether user specified a zero terminated list of files for input, otherwise read files from args - let mut files: Vec = if matches.is_present(options::FILES0_FROM) { - let files0_from: Vec = matches - .values_of(options::FILES0_FROM) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - - let mut files = Vec::new(); - for path in &files0_from { - let reader = open(path.as_str()); - let buf_reader = BufReader::new(reader); - for line in buf_reader.split(b'\0').flatten() { - files.push( - std::str::from_utf8(&line) - .expect("Could not parse string from zero terminated input.") - .to_string(), - ); - } - } - files - } else { - matches - .values_of(options::FILES) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default() - }; - - settings.mode = if matches.is_present(options::modes::HUMAN_NUMERIC) - || matches.value_of(options::modes::SORT) == Some("human-numeric") - { - SortMode::HumanNumeric - } else if matches.is_present(options::modes::MONTH) - || matches.value_of(options::modes::SORT) == Some("month") - { - SortMode::Month - } else if matches.is_present(options::modes::GENERAL_NUMERIC) - || matches.value_of(options::modes::SORT) == Some("general-numeric") - { - SortMode::GeneralNumeric - } else if matches.is_present(options::modes::NUMERIC) - || matches.value_of(options::modes::SORT) == Some("numeric") - { - SortMode::Numeric - } else if matches.is_present(options::modes::VERSION) - || matches.value_of(options::modes::SORT) == Some("version") - { - SortMode::Version - } else if matches.is_present(options::modes::RANDOM) - || matches.value_of(options::modes::SORT) == Some("random") - { - settings.salt = get_rand_string(); - SortMode::Random - } else { - SortMode::Default - }; - - settings.dictionary_order = matches.is_present(options::DICTIONARY_ORDER); - settings.ignore_non_printing = matches.is_present(options::IGNORE_NONPRINTING); - if matches.is_present(options::PARALLEL) { - // "0" is default - threads = num of cores - settings.threads = matches - .value_of(options::PARALLEL) - .map(String::from) - .unwrap_or_else(|| "0".to_string()); - env::set_var("RAYON_NUM_THREADS", &settings.threads); - } - - settings.buffer_size = matches - .value_of(options::BUF_SIZE) - .map_or(DEFAULT_BUF_SIZE, |s| { - GlobalSettings::parse_byte_count(s) - .unwrap_or_else(|e| crash!(2, "{}", format_error_message(e, s, options::BUF_SIZE))) - }); - - settings.tmp_dir = matches - .value_of(options::TMP_DIR) - .map(PathBuf::from) - .unwrap_or_else(env::temp_dir); - - settings.compress_prog = matches.value_of(options::COMPRESS_PROG).map(String::from); - - if let Some(n_merge) = matches.value_of(options::BATCH_SIZE) { - settings.merge_batch_size = n_merge - .parse() - .unwrap_or_else(|_| crash!(2, "invalid --batch-size argument '{}'", n_merge)); - } - - settings.zero_terminated = matches.is_present(options::ZERO_TERMINATED); - settings.merge = matches.is_present(options::MERGE); - - settings.check = matches.is_present(options::check::CHECK); - if matches.is_present(options::check::CHECK_SILENT) - || matches!( - matches.value_of(options::check::CHECK), - Some(options::check::SILENT) | Some(options::check::QUIET) - ) - { - settings.check_silent = true; - settings.check = true; - }; - - settings.ignore_case = matches.is_present(options::IGNORE_CASE); - - settings.ignore_leading_blanks = matches.is_present(options::IGNORE_LEADING_BLANKS); - - settings.output_file = matches.value_of(options::OUTPUT).map(String::from); - settings.reverse = matches.is_present(options::REVERSE); - settings.stable = matches.is_present(options::STABLE); - settings.unique = matches.is_present(options::UNIQUE); - - if files.is_empty() { - /* if no file, default to stdin */ - files.push("-".to_owned()); - } else if settings.check && files.len() != 1 { - crash!(1, "extra operand `{}' not allowed with -c", files[1]) - } - - if let Some(arg) = matches.args.get(options::SEPARATOR) { - let separator = arg.vals[0].to_string_lossy(); - let separator = separator; - if separator.len() != 1 { - crash!(1, "separator must be exactly one character long"); - } - settings.separator = Some(separator.chars().next().unwrap()) - } - - if let Some(values) = matches.values_of(options::KEY) { - for value in values { - settings - .selectors - .push(FieldSelector::parse(value, &settings)); - } - } - - if !matches.is_present(options::KEY) { - // add a default selector matching the whole line - let key_settings = KeySettings::from(&settings); - settings.selectors.push( - FieldSelector::new( - KeyPosition { - field: 1, - char: 1, - ignore_blanks: key_settings.ignore_blanks, - }, - None, - key_settings, - ) - .unwrap(), - ); - } - - settings.init_precomputed(); - - exec(&files, &settings) } fn exec(files: &[String], settings: &GlobalSettings) -> i32 { diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index ad5c083aa..ccc98ee5e 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -30,7 +30,7 @@ static OPT_ADDITIONAL_SUFFIX: &str = "additional-suffix"; static OPT_FILTER: &str = "filter"; static OPT_NUMERIC_SUFFIXES: &str = "numeric-suffixes"; static OPT_SUFFIX_LENGTH: &str = "suffix-length"; -static OPT_DEFAULT_SUFFIX_LENGTH: usize = 2; +static OPT_DEFAULT_SUFFIX_LENGTH: &str = "2"; static OPT_VERBOSE: &str = "verbose"; static ARG_INPUT: &str = "input"; @@ -54,85 +54,10 @@ size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let long_usage = get_long_usage(); - let default_suffix_length_str = OPT_DEFAULT_SUFFIX_LENGTH.to_string(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about("Create output files containing consecutive or interleaved sections of input") + let matches = uu_app() .usage(&usage[..]) .after_help(&long_usage[..]) - // strategy (mutually exclusive) - .arg( - Arg::with_name(OPT_BYTES) - .short("b") - .long(OPT_BYTES) - .takes_value(true) - .default_value("2") - .help("use suffixes of length N (default 2)"), - ) - .arg( - Arg::with_name(OPT_LINE_BYTES) - .short("C") - .long(OPT_LINE_BYTES) - .takes_value(true) - .default_value("2") - .help("put at most SIZE bytes of lines per output file"), - ) - .arg( - Arg::with_name(OPT_LINES) - .short("l") - .long(OPT_LINES) - .takes_value(true) - .default_value("1000") - .help("write to shell COMMAND file name is $FILE (Currently not implemented for Windows)"), - ) - // rest of the arguments - .arg( - Arg::with_name(OPT_ADDITIONAL_SUFFIX) - .long(OPT_ADDITIONAL_SUFFIX) - .takes_value(true) - .default_value("") - .help("additional suffix to append to output file names"), - ) - .arg( - Arg::with_name(OPT_FILTER) - .long(OPT_FILTER) - .takes_value(true) - .help("write to shell COMMAND file name is $FILE (Currently not implemented for Windows)"), - ) - .arg( - Arg::with_name(OPT_NUMERIC_SUFFIXES) - .short("d") - .long(OPT_NUMERIC_SUFFIXES) - .takes_value(true) - .default_value("0") - .help("use numeric suffixes instead of alphabetic"), - ) - .arg( - Arg::with_name(OPT_SUFFIX_LENGTH) - .short("a") - .long(OPT_SUFFIX_LENGTH) - .takes_value(true) - .default_value(default_suffix_length_str.as_str()) - .help("use suffixes of length N (default 2)"), - ) - .arg( - Arg::with_name(OPT_VERBOSE) - .long(OPT_VERBOSE) - .help("print a diagnostic just before each output file is opened"), - ) - .arg( - Arg::with_name(ARG_INPUT) - .takes_value(true) - .default_value("-") - .index(1) - ) - .arg( - Arg::with_name(ARG_PREFIX) - .takes_value(true) - .default_value("x") - .index(2) - ) .get_matches_from(args); let mut settings = Settings { @@ -201,6 +126,84 @@ pub fn uumain(args: impl uucore::Args) -> i32 { split(&settings) } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about("Create output files containing consecutive or interleaved sections of input") + // strategy (mutually exclusive) + .arg( + Arg::with_name(OPT_BYTES) + .short("b") + .long(OPT_BYTES) + .takes_value(true) + .default_value("2") + .help("use suffixes of length N (default 2)"), + ) + .arg( + Arg::with_name(OPT_LINE_BYTES) + .short("C") + .long(OPT_LINE_BYTES) + .takes_value(true) + .default_value("2") + .help("put at most SIZE bytes of lines per output file"), + ) + .arg( + Arg::with_name(OPT_LINES) + .short("l") + .long(OPT_LINES) + .takes_value(true) + .default_value("1000") + .help("write to shell COMMAND file name is $FILE (Currently not implemented for Windows)"), + ) + // rest of the arguments + .arg( + Arg::with_name(OPT_ADDITIONAL_SUFFIX) + .long(OPT_ADDITIONAL_SUFFIX) + .takes_value(true) + .default_value("") + .help("additional suffix to append to output file names"), + ) + .arg( + Arg::with_name(OPT_FILTER) + .long(OPT_FILTER) + .takes_value(true) + .help("write to shell COMMAND file name is $FILE (Currently not implemented for Windows)"), + ) + .arg( + Arg::with_name(OPT_NUMERIC_SUFFIXES) + .short("d") + .long(OPT_NUMERIC_SUFFIXES) + .takes_value(true) + .default_value("0") + .help("use numeric suffixes instead of alphabetic"), + ) + .arg( + Arg::with_name(OPT_SUFFIX_LENGTH) + .short("a") + .long(OPT_SUFFIX_LENGTH) + .takes_value(true) + .default_value(OPT_DEFAULT_SUFFIX_LENGTH) + .help("use suffixes of length N (default 2)"), + ) + .arg( + Arg::with_name(OPT_VERBOSE) + .long(OPT_VERBOSE) + .help("print a diagnostic just before each output file is opened"), + ) + .arg( + Arg::with_name(ARG_INPUT) + .takes_value(true) + .default_value("-") + .index(1) + ) + .arg( + Arg::with_name(ARG_PREFIX) + .takes_value(true) + .default_value("x") + .index(2) + ) +} + #[allow(dead_code)] struct Settings { prefix: String, diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 7bf3db4c2..70c06bdf6 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -947,11 +947,24 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let long_usage = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&long_usage[..]) + .get_matches_from(args); + + match Stater::new(matches) { + Ok(stater) => stater.exec(), + Err(e) => { + show_error!("{}", e); + 1 + } + } +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) .arg( Arg::with_name(options::DEREFERENCE) .short("L") @@ -996,13 +1009,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .takes_value(true) .min_values(1), ) - .get_matches_from(args); - - match Stater::new(matches) { - Ok(stater) => stater.exec(), - Err(e) => { - show_error!("{}", e); - 1 - } - } } diff --git a/src/uu/stdbuf/src/stdbuf.rs b/src/uu/stdbuf/src/stdbuf.rs index fc0c83ec8..7460a2cb2 100644 --- a/src/uu/stdbuf/src/stdbuf.rs +++ b/src/uu/stdbuf/src/stdbuf.rs @@ -154,10 +154,40 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .accept_any(); let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + let options = ProgramOptions::try_from(&matches) + .unwrap_or_else(|e| crash!(125, "{}\nTry 'stdbuf --help' for more information.", e.0)); + + let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap(); + let mut command = Command::new(command_values.next().unwrap()); + let command_params: Vec<&str> = command_values.collect(); + + let mut tmp_dir = tempdir().unwrap(); + let (preload_env, libstdbuf) = return_if_err!(1, get_preload_env(&mut tmp_dir)); + command.env(preload_env, libstdbuf); + set_command_env(&mut command, "_STDBUF_I", options.stdin); + set_command_env(&mut command, "_STDBUF_O", options.stdout); + set_command_env(&mut command, "_STDBUF_E", options.stderr); + command.args(command_params); + + let mut process = match command.spawn() { + Ok(p) => p, + Err(e) => crash!(1, "failed to execute process: {}", e), + }; + match process.wait() { + Ok(status) => match status.code() { + Some(i) => i, + None => crash!(1, "process killed by signal {}", status.signal().unwrap()), + }, + Err(e) => crash!(1, "{}", e), + } +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .after_help(LONG_HELP) .setting(AppSettings::TrailingVarArg) .arg( @@ -191,32 +221,4 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .hidden(true) .required(true), ) - .get_matches_from(args); - - let options = ProgramOptions::try_from(&matches) - .unwrap_or_else(|e| crash!(125, "{}\nTry 'stdbuf --help' for more information.", e.0)); - - let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap(); - let mut command = Command::new(command_values.next().unwrap()); - let command_params: Vec<&str> = command_values.collect(); - - let mut tmp_dir = tempdir().unwrap(); - let (preload_env, libstdbuf) = return_if_err!(1, get_preload_env(&mut tmp_dir)); - command.env(preload_env, libstdbuf); - set_command_env(&mut command, "_STDBUF_I", options.stdin); - set_command_env(&mut command, "_STDBUF_O", options.stdout); - set_command_env(&mut command, "_STDBUF_E", options.stderr); - command.args(command_params); - - let mut process = match command.spawn() { - Ok(p) => p, - Err(e) => crash!(1, "failed to execute process: {}", e), - }; - match process.wait() { - Ok(status) => match status.code() { - Some(i) => i, - None => crash!(1, "process killed by signal {}", status.signal().unwrap()), - }, - Err(e) => crash!(1, "{}", e), - } } diff --git a/src/uu/sum/src/sum.rs b/src/uu/sum/src/sum.rs index 4d42d7a97..0ce612859 100644 --- a/src/uu/sum/src/sum.rs +++ b/src/uu/sum/src/sum.rs @@ -98,24 +98,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) - .name(NAME) - .version(crate_version!()) - .usage(USAGE) - .about(SUMMARY) - .arg(Arg::with_name(options::FILE).multiple(true).hidden(true)) - .arg( - Arg::with_name(options::BSD_COMPATIBLE) - .short(options::BSD_COMPATIBLE) - .help("use the BSD sum algorithm, use 1K blocks (default)"), - ) - .arg( - Arg::with_name(options::SYSTEM_V_COMPATIBLE) - .short("s") - .long(options::SYSTEM_V_COMPATIBLE) - .help("use System V sum algorithm, use 512 bytes blocks"), - ) - .get_matches_from(args); + let matches = uu_app().get_matches_from(args); let files: Vec = match matches.values_of(options::FILE) { Some(v) => v.clone().map(|v| v.to_owned()).collect(), @@ -155,3 +138,23 @@ pub fn uumain(args: impl uucore::Args) -> i32 { exit_code } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .name(NAME) + .version(crate_version!()) + .usage(USAGE) + .about(SUMMARY) + .arg(Arg::with_name(options::FILE).multiple(true).hidden(true)) + .arg( + Arg::with_name(options::BSD_COMPATIBLE) + .short(options::BSD_COMPATIBLE) + .help("use the BSD sum algorithm, use 1K blocks (default)"), + ) + .arg( + Arg::with_name(options::SYSTEM_V_COMPATIBLE) + .short("s") + .long(options::SYSTEM_V_COMPATIBLE) + .help("use System V sum algorithm, use 512 bytes blocks"), + ) +} diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index 53d1a5701..4fcdf49f9 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -166,26 +166,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::FILE_SYSTEM) - .short("f") - .long(options::FILE_SYSTEM) - .conflicts_with(options::DATA) - .help("sync the file systems that contain the files (Linux and Windows only)"), - ) - .arg( - Arg::with_name(options::DATA) - .short("d") - .long(options::DATA) - .conflicts_with(options::FILE_SYSTEM) - .help("sync only file data, no unneeded metadata (Linux only)"), - ) - .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true)) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let files: Vec = matches .values_of(ARG_FILES) @@ -211,6 +192,27 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::FILE_SYSTEM) + .short("f") + .long(options::FILE_SYSTEM) + .conflicts_with(options::DATA) + .help("sync the file systems that contain the files (Linux and Windows only)"), + ) + .arg( + Arg::with_name(options::DATA) + .short("d") + .long(options::DATA) + .conflicts_with(options::FILE_SYSTEM) + .help("sync only file data, no unneeded metadata (Linux only)"), + ) + .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true)) +} + fn sync() -> isize { unsafe { platform::do_sync() } } diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index be1852ec5..ae1fd9bc5 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -31,7 +31,31 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + let before = matches.is_present(options::BEFORE); + let regex = matches.is_present(options::REGEX); + let separator = match matches.value_of(options::SEPARATOR) { + Some(m) => { + if m.is_empty() { + crash!(1, "separator cannot be empty") + } else { + m.to_owned() + } + } + None => "\n".to_owned(), + }; + + let files: Vec = match matches.values_of(options::FILE) { + Some(v) => v.map(|v| v.to_owned()).collect(), + None => vec!["-".to_owned()], + }; + + tac(files, before, regex, &separator[..]) +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .name(NAME) .version(crate_version!()) .usage(USAGE) @@ -58,27 +82,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .takes_value(true), ) .arg(Arg::with_name(options::FILE).hidden(true).multiple(true)) - .get_matches_from(args); - - let before = matches.is_present(options::BEFORE); - let regex = matches.is_present(options::REGEX); - let separator = match matches.value_of(options::SEPARATOR) { - Some(m) => { - if m.is_empty() { - crash!(1, "separator cannot be empty") - } else { - m.to_owned() - } - } - None => "\n".to_owned(), - }; - - let files: Vec = match matches.values_of(options::FILE) { - Some(v) => v.map(|v| v.to_owned()).collect(), - None => vec!["-".to_owned()], - }; - - tac(files, before, regex, &separator[..]) } fn tac(filenames: Vec, before: bool, _: bool, separator: &str) -> i32 { diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index 8950886a2..4970cdcc2 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -72,74 +72,7 @@ impl Default for Settings { pub fn uumain(args: impl uucore::Args) -> i32 { let mut settings: Settings = Default::default(); - let app = App::new(executable!()) - .version(crate_version!()) - .about("output the last part of files") - // TODO: add usage - .arg( - Arg::with_name(options::BYTES) - .short("c") - .long(options::BYTES) - .takes_value(true) - .allow_hyphen_values(true) - .overrides_with_all(&[options::BYTES, options::LINES]) - .help("Number of bytes to print"), - ) - .arg( - Arg::with_name(options::FOLLOW) - .short("f") - .long(options::FOLLOW) - .help("Print the file as it grows"), - ) - .arg( - Arg::with_name(options::LINES) - .short("n") - .long(options::LINES) - .takes_value(true) - .allow_hyphen_values(true) - .overrides_with_all(&[options::BYTES, options::LINES]) - .help("Number of lines to print"), - ) - .arg( - Arg::with_name(options::PID) - .long(options::PID) - .takes_value(true) - .help("with -f, terminate after process ID, PID dies"), - ) - .arg( - Arg::with_name(options::verbosity::QUIET) - .short("q") - .long(options::verbosity::QUIET) - .visible_alias("silent") - .overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE]) - .help("never output headers giving file names"), - ) - .arg( - Arg::with_name(options::SLEEP_INT) - .short("s") - .takes_value(true) - .long(options::SLEEP_INT) - .help("Number or seconds to sleep between polling the file when running with -f"), - ) - .arg( - Arg::with_name(options::verbosity::VERBOSE) - .short("v") - .long(options::verbosity::VERBOSE) - .overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE]) - .help("always output headers giving file names"), - ) - .arg( - Arg::with_name(options::ZERO_TERM) - .short("z") - .long(options::ZERO_TERM) - .help("Line delimiter is NUL, not newline"), - ) - .arg( - Arg::with_name(options::ARG_FILES) - .multiple(true) - .takes_value(true) - .min_values(1), - ); + let app = uu_app(); let matches = app.get_matches_from(args); @@ -244,6 +177,77 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about("output the last part of files") + // TODO: add usage + .arg( + Arg::with_name(options::BYTES) + .short("c") + .long(options::BYTES) + .takes_value(true) + .allow_hyphen_values(true) + .overrides_with_all(&[options::BYTES, options::LINES]) + .help("Number of bytes to print"), + ) + .arg( + Arg::with_name(options::FOLLOW) + .short("f") + .long(options::FOLLOW) + .help("Print the file as it grows"), + ) + .arg( + Arg::with_name(options::LINES) + .short("n") + .long(options::LINES) + .takes_value(true) + .allow_hyphen_values(true) + .overrides_with_all(&[options::BYTES, options::LINES]) + .help("Number of lines to print"), + ) + .arg( + Arg::with_name(options::PID) + .long(options::PID) + .takes_value(true) + .help("with -f, terminate after process ID, PID dies"), + ) + .arg( + Arg::with_name(options::verbosity::QUIET) + .short("q") + .long(options::verbosity::QUIET) + .visible_alias("silent") + .overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE]) + .help("never output headers giving file names"), + ) + .arg( + Arg::with_name(options::SLEEP_INT) + .short("s") + .takes_value(true) + .long(options::SLEEP_INT) + .help("Number or seconds to sleep between polling the file when running with -f"), + ) + .arg( + Arg::with_name(options::verbosity::VERBOSE) + .short("v") + .long(options::verbosity::VERBOSE) + .overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE]) + .help("always output headers giving file names"), + ) + .arg( + Arg::with_name(options::ZERO_TERM) + .short("z") + .long(options::ZERO_TERM) + .help("Line delimiter is NUL, not newline"), + ) + .arg( + Arg::with_name(options::ARG_FILES) + .multiple(true) + .takes_value(true) + .min_values(1), + ) +} + fn follow(readers: &mut [BufReader], filenames: &[String], settings: &Settings) { assert!(settings.follow); let mut last = readers.len() - 1; diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index f5f24d944..a207dee63 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -39,25 +39,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_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 matches = uu_app().usage(&usage[..]).get_matches_from(args); let options = Options { append: matches.is_present(options::APPEND), @@ -74,6 +56,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .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)) +} + #[cfg(unix)] fn ignore_interrupts() -> Result<()> { let ret = unsafe { libc::signal(libc::SIGINT, libc::SIG_IGN) }; diff --git a/src/uu/test/Cargo.toml b/src/uu/test/Cargo.toml index e1f6e62e7..cd0282a45 100644 --- a/src/uu/test/Cargo.toml +++ b/src/uu/test/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/test.rs" [dependencies] +clap = "2.33.3" libc = "0.2.42" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/test/src/test.rs b/src/uu/test/src/test.rs index 107ad2df4..dba840d3c 100644 --- a/src/uu/test/src/test.rs +++ b/src/uu/test/src/test.rs @@ -10,9 +10,17 @@ mod parser; +use clap::{App, AppSettings}; use parser::{parse, Symbol}; use std::ffi::{OsStr, OsString}; use std::path::Path; +use uucore::executable; + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .setting(AppSettings::DisableHelpFlags) + .setting(AppSettings::DisableVersion) +} pub fn uumain(mut args: impl uucore::Args) -> i32 { let program = args.next().unwrap_or_else(|| OsString::from("test")); diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index f21a0265f..464414c5e 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -102,9 +102,25 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let app = App::new("timeout") + let app = uu_app().usage(&usage[..]); + + let matches = app.get_matches_from(args); + + let config = Config::from(matches); + timeout( + &config.command, + config.duration, + config.signal, + config.kill_after, + config.foreground, + config.preserve_status, + config.verbose, + ) +} + +pub fn uu_app() -> App<'static, 'static> { + App::new("timeout") .version(crate_version!()) - .usage(&usage[..]) .about(ABOUT) .arg( Arg::with_name(options::FOREGROUND) @@ -144,20 +160,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .required(true) .multiple(true) ) - .setting(AppSettings::TrailingVarArg); - - let matches = app.get_matches_from(args); - - let config = Config::from(matches); - timeout( - &config.command, - config.duration, - config.signal, - config.kill_after, - config.foreground, - config.preserve_status, - config.verbose, - ) + .setting(AppSettings::TrailingVarArg) } /// Remove pre-existing SIGCHLD handlers that would make waiting for the child's exit code fail. diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 2e1c3c8e8..3e9ff5624 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -55,80 +55,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::ACCESS) - .short("a") - .help("change only the access time"), - ) - .arg( - Arg::with_name(options::sources::CURRENT) - .short("t") - .help("use [[CC]YY]MMDDhhmm[.ss] instead of the current time") - .value_name("STAMP") - .takes_value(true), - ) - .arg( - Arg::with_name(options::sources::DATE) - .short("d") - .long(options::sources::DATE) - .help("parse argument and use it instead of current time") - .value_name("STRING"), - ) - .arg( - Arg::with_name(options::MODIFICATION) - .short("m") - .help("change only the modification time"), - ) - .arg( - Arg::with_name(options::NO_CREATE) - .short("c") - .long(options::NO_CREATE) - .help("do not create any files"), - ) - .arg( - Arg::with_name(options::NO_DEREF) - .short("h") - .long(options::NO_DEREF) - .help( - "affect each symbolic link instead of any referenced file \ - (only for systems that can change the timestamps of a symlink)", - ), - ) - .arg( - Arg::with_name(options::sources::REFERENCE) - .short("r") - .long(options::sources::REFERENCE) - .help("use this file's times instead of the current time") - .value_name("FILE"), - ) - .arg( - Arg::with_name(options::TIME) - .long(options::TIME) - .help( - "change only the specified time: \"access\", \"atime\", or \ - \"use\" are equivalent to -a; \"modify\" or \"mtime\" are \ - equivalent to -m", - ) - .value_name("WORD") - .possible_values(&["access", "atime", "use"]) - .takes_value(true), - ) - .arg( - Arg::with_name(ARG_FILES) - .multiple(true) - .takes_value(true) - .min_values(1), - ) - .group(ArgGroup::with_name(options::SOURCES).args(&[ - options::sources::CURRENT, - options::sources::DATE, - options::sources::REFERENCE, - ])) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let files: Vec = matches .values_of(ARG_FILES) @@ -236,6 +163,81 @@ pub fn uumain(args: impl uucore::Args) -> i32 { error_code } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::ACCESS) + .short("a") + .help("change only the access time"), + ) + .arg( + Arg::with_name(options::sources::CURRENT) + .short("t") + .help("use [[CC]YY]MMDDhhmm[.ss] instead of the current time") + .value_name("STAMP") + .takes_value(true), + ) + .arg( + Arg::with_name(options::sources::DATE) + .short("d") + .long(options::sources::DATE) + .help("parse argument and use it instead of current time") + .value_name("STRING"), + ) + .arg( + Arg::with_name(options::MODIFICATION) + .short("m") + .help("change only the modification time"), + ) + .arg( + Arg::with_name(options::NO_CREATE) + .short("c") + .long(options::NO_CREATE) + .help("do not create any files"), + ) + .arg( + Arg::with_name(options::NO_DEREF) + .short("h") + .long(options::NO_DEREF) + .help( + "affect each symbolic link instead of any referenced file \ + (only for systems that can change the timestamps of a symlink)", + ), + ) + .arg( + Arg::with_name(options::sources::REFERENCE) + .short("r") + .long(options::sources::REFERENCE) + .help("use this file's times instead of the current time") + .value_name("FILE"), + ) + .arg( + Arg::with_name(options::TIME) + .long(options::TIME) + .help( + "change only the specified time: \"access\", \"atime\", or \ + \"use\" are equivalent to -a; \"modify\" or \"mtime\" are \ + equivalent to -m", + ) + .value_name("WORD") + .possible_values(&["access", "atime", "use"]) + .takes_value(true), + ) + .arg( + Arg::with_name(ARG_FILES) + .multiple(true) + .takes_value(true) + .min_values(1), + ) + .group(ArgGroup::with_name(options::SOURCES).args(&[ + options::sources::CURRENT, + options::sources::DATE, + options::sources::REFERENCE, + ])) +} + fn stat(path: &str, follow: bool) -> (FileTime, FileTime) { let metadata = if follow { fs::symlink_metadata(path) diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs index 9916af7db..28ce70c22 100644 --- a/src/uu/tr/src/tr.rs +++ b/src/uu/tr/src/tr.rs @@ -249,46 +249,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let after_help = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&after_help[..]) - .arg( - Arg::with_name(options::COMPLEMENT) - // .visible_short_alias('C') // TODO: requires clap "3.0.0-beta.2" - .short("c") - .long(options::COMPLEMENT) - .help("use the complement of SET1"), - ) - .arg( - Arg::with_name("C") // work around for `Arg::visible_short_alias` - .short("C") - .help("same as -c"), - ) - .arg( - Arg::with_name(options::DELETE) - .short("d") - .long(options::DELETE) - .help("delete characters in SET1, do not translate"), - ) - .arg( - Arg::with_name(options::SQUEEZE) - .long(options::SQUEEZE) - .short("s") - .help( - "replace each sequence of a repeated character that is - listed in the last specified SET, with a single occurrence - of that character", - ), - ) - .arg( - Arg::with_name(options::TRUNCATE) - .long(options::TRUNCATE) - .short("t") - .help("first truncate SET1 to length of SET2"), - ) - .arg(Arg::with_name(options::SETS).multiple(true)) .get_matches_from(args); let delete_flag = matches.is_present(options::DELETE); @@ -358,3 +321,44 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::COMPLEMENT) + // .visible_short_alias('C') // TODO: requires clap "3.0.0-beta.2" + .short("c") + .long(options::COMPLEMENT) + .help("use the complement of SET1"), + ) + .arg( + Arg::with_name("C") // work around for `Arg::visible_short_alias` + .short("C") + .help("same as -c"), + ) + .arg( + Arg::with_name(options::DELETE) + .short("d") + .long(options::DELETE) + .help("delete characters in SET1, do not translate"), + ) + .arg( + Arg::with_name(options::SQUEEZE) + .long(options::SQUEEZE) + .short("s") + .help( + "replace each sequence of a repeated character that is + listed in the last specified SET, with a single occurrence + of that character", + ), + ) + .arg( + Arg::with_name(options::TRUNCATE) + .long(options::TRUNCATE) + .short("t") + .help("first truncate SET1 to length of SET2"), + ) + .arg(Arg::with_name(options::SETS).multiple(true)) +} diff --git a/src/uu/true/Cargo.toml b/src/uu/true/Cargo.toml index 9f13318fd..f121d56de 100644 --- a/src/uu/true/Cargo.toml +++ b/src/uu/true/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/true.rs" [dependencies] +clap = "2.33.3" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/true/src/true.rs b/src/uu/true/src/true.rs index 7cb23f621..521ca2ea5 100644 --- a/src/uu/true/src/true.rs +++ b/src/uu/true/src/true.rs @@ -5,6 +5,15 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. +use clap::{App, AppSettings}; +use uucore::executable; + pub fn uumain(_: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .setting(AppSettings::DisableHelpFlags) + .setting(AppSettings::DisableVersion) +} diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index 8ef246833..bb7aa61d4 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -93,45 +93,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let long_usage = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&long_usage[..]) - .arg( - Arg::with_name(options::IO_BLOCKS) - .short("o") - .long(options::IO_BLOCKS) - .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)") - ) - .arg( - Arg::with_name(options::NO_CREATE) - .short("c") - .long(options::NO_CREATE) - .help("do not create files that do not exist") - ) - .arg( - Arg::with_name(options::REFERENCE) - .short("r") - .long(options::REFERENCE) - .required_unless(options::SIZE) - .help("base the size of each file on the size of RFILE") - .value_name("RFILE") - ) - .arg( - Arg::with_name(options::SIZE) - .short("s") - .long(options::SIZE) - .required_unless(options::REFERENCE) - .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified") - .value_name("SIZE") - ) - .arg(Arg::with_name(options::ARG_FILES) - .value_name("FILE") - .multiple(true) - .takes_value(true) - .required(true) - .min_values(1)) .get_matches_from(args); let files: Vec = matches @@ -168,6 +132,46 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::IO_BLOCKS) + .short("o") + .long(options::IO_BLOCKS) + .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)") + ) + .arg( + Arg::with_name(options::NO_CREATE) + .short("c") + .long(options::NO_CREATE) + .help("do not create files that do not exist") + ) + .arg( + Arg::with_name(options::REFERENCE) + .short("r") + .long(options::REFERENCE) + .required_unless(options::SIZE) + .help("base the size of each file on the size of RFILE") + .value_name("RFILE") + ) + .arg( + Arg::with_name(options::SIZE) + .short("s") + .long(options::SIZE) + .required_unless(options::REFERENCE) + .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified") + .value_name("SIZE") + ) + .arg(Arg::with_name(options::ARG_FILES) + .value_name("FILE") + .multiple(true) + .takes_value(true) + .required(true) + .min_values(1)) +} + /// Truncate the named file to the specified size. /// /// If `create` is true, then the file will be created if it does not diff --git a/src/uu/tsort/src/tsort.rs b/src/uu/tsort/src/tsort.rs index 8bd6dabef..0a323f837 100644 --- a/src/uu/tsort/src/tsort.rs +++ b/src/uu/tsort/src/tsort.rs @@ -30,16 +30,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) - .version(crate_version!()) - .usage(USAGE) - .about(SUMMARY) - .arg( - Arg::with_name(options::FILE) - .default_value("-") - .hidden(true), - ) - .get_matches_from(args); + let matches = uu_app().get_matches_from(args); let input = matches .value_of(options::FILE) @@ -98,6 +89,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .usage(USAGE) + .about(SUMMARY) + .arg( + Arg::with_name(options::FILE) + .default_value("-") + .hidden(true), + ) +} + // We use String as a representation of node here // but using integer may improve performance. struct Graph { diff --git a/src/uu/tty/src/tty.rs b/src/uu/tty/src/tty.rs index cc5052dea..7412cdf45 100644 --- a/src/uu/tty/src/tty.rs +++ b/src/uu/tty/src/tty.rs @@ -33,19 +33,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::SILENT) - .long(options::SILENT) - .visible_alias("quiet") - .short("s") - .help("print nothing, only return an exit status") - .required(false), - ) - .get_matches_from_safe(args); + let matches = uu_app().usage(&usage[..]).get_matches_from_safe(args); let matches = match matches { Ok(m) => m, @@ -88,3 +76,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 { libc::EXIT_FAILURE } } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::SILENT) + .long(options::SILENT) + .visible_alias("quiet") + .short("s") + .help("print nothing, only return an exit status") + .required(false), + ) +} diff --git a/src/uu/uname/src/uname.rs b/src/uu/uname/src/uname.rs index aa591ee18..dda859722 100644 --- a/src/uu/uname/src/uname.rs +++ b/src/uu/uname/src/uname.rs @@ -47,49 +47,7 @@ const HOST_OS: &str = "Redox"; pub fn uumain(args: impl uucore::Args) -> i32 { let usage = format!("{} [OPTION]...", executable!()); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg(Arg::with_name(options::ALL) - .short("a") - .long(options::ALL) - .help("Behave as though all of the options -mnrsv were specified.")) - .arg(Arg::with_name(options::KERNELNAME) - .short("s") - .long(options::KERNELNAME) - .alias("sysname") // Obsolescent option in GNU uname - .help("print the kernel name.")) - .arg(Arg::with_name(options::NODENAME) - .short("n") - .long(options::NODENAME) - .help("print the nodename (the nodename may be a name that the system is known by to a communications network).")) - .arg(Arg::with_name(options::KERNELRELEASE) - .short("r") - .long(options::KERNELRELEASE) - .alias("release") // Obsolescent option in GNU uname - .help("print the operating system release.")) - .arg(Arg::with_name(options::KERNELVERSION) - .short("v") - .long(options::KERNELVERSION) - .help("print the operating system version.")) - .arg(Arg::with_name(options::HWPLATFORM) - .short("i") - .long(options::HWPLATFORM) - .help("print the hardware platform (non-portable)")) - .arg(Arg::with_name(options::MACHINE) - .short("m") - .long(options::MACHINE) - .help("print the machine hardware name.")) - .arg(Arg::with_name(options::PROCESSOR) - .short("p") - .long(options::PROCESSOR) - .help("print the processor type (non-portable)")) - .arg(Arg::with_name(options::OS) - .short("o") - .long(options::OS) - .help("print the operating system name.")) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let uname = return_if_err!(1, PlatformInfo::new()); let mut output = String::new(); @@ -155,3 +113,47 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg(Arg::with_name(options::ALL) + .short("a") + .long(options::ALL) + .help("Behave as though all of the options -mnrsv were specified.")) + .arg(Arg::with_name(options::KERNELNAME) + .short("s") + .long(options::KERNELNAME) + .alias("sysname") // Obsolescent option in GNU uname + .help("print the kernel name.")) + .arg(Arg::with_name(options::NODENAME) + .short("n") + .long(options::NODENAME) + .help("print the nodename (the nodename may be a name that the system is known by to a communications network).")) + .arg(Arg::with_name(options::KERNELRELEASE) + .short("r") + .long(options::KERNELRELEASE) + .alias("release") // Obsolescent option in GNU uname + .help("print the operating system release.")) + .arg(Arg::with_name(options::KERNELVERSION) + .short("v") + .long(options::KERNELVERSION) + .help("print the operating system version.")) + .arg(Arg::with_name(options::HWPLATFORM) + .short("i") + .long(options::HWPLATFORM) + .help("print the hardware platform (non-portable)")) + .arg(Arg::with_name(options::MACHINE) + .short("m") + .long(options::MACHINE) + .help("print the machine hardware name.")) + .arg(Arg::with_name(options::PROCESSOR) + .short("p") + .long(options::PROCESSOR) + .help("print the processor type (non-portable)")) + .arg(Arg::with_name(options::OS) + .short("o") + .long(options::OS) + .help("print the operating system name.")) +} diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 92b3c7520..50e3f186d 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -94,7 +94,15 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); - let matches = App::new(executable!()) + let matches = uu_app().get_matches_from(args); + + unexpand(Options::new(matches)); + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .name(NAME) .version(crate_version!()) .usage(USAGE) @@ -126,11 +134,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .long(options::NO_UTF8) .takes_value(false) .help("interpret input file as 8-bit ASCII rather than UTF-8")) - .get_matches_from(args); - - unexpand(Options::new(matches)); - - 0 } fn open(path: String) -> BufReader> { diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index aee024dd4..20639c850 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -238,11 +238,52 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let long_usage = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&long_usage[..]) + .get_matches_from(args); + + let files: Vec = matches + .values_of(ARG_FILES) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + + let (in_file_name, out_file_name) = match files.len() { + 0 => ("-".to_owned(), "-".to_owned()), + 1 => (files[0].clone(), "-".to_owned()), + 2 => (files[0].clone(), files[1].clone()), + _ => { + // Cannot happen as clap will fail earlier + crash!(1, "Extra operand: {}", files[2]); + } + }; + + let uniq = Uniq { + repeats_only: matches.is_present(options::REPEATED) + || matches.is_present(options::ALL_REPEATED), + uniques_only: matches.is_present(options::UNIQUE), + all_repeated: matches.is_present(options::ALL_REPEATED) + || matches.is_present(options::GROUP), + delimiters: get_delimiter(&matches), + show_counts: matches.is_present(options::COUNT), + skip_fields: opt_parsed(options::SKIP_FIELDS, &matches), + slice_start: opt_parsed(options::SKIP_CHARS, &matches), + slice_stop: opt_parsed(options::CHECK_CHARS, &matches), + ignore_case: matches.is_present(options::IGNORE_CASE), + zero_terminated: matches.is_present(options::ZERO_TERMINATED), + }; + uniq.print_uniq( + &mut open_input_file(in_file_name), + &mut open_output_file(out_file_name), + ); + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) .arg( Arg::with_name(options::ALL_REPEATED) .short("D") @@ -329,43 +370,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .takes_value(true) .max_values(2), ) - .get_matches_from(args); - - let files: Vec = matches - .values_of(ARG_FILES) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - - let (in_file_name, out_file_name) = match files.len() { - 0 => ("-".to_owned(), "-".to_owned()), - 1 => (files[0].clone(), "-".to_owned()), - 2 => (files[0].clone(), files[1].clone()), - _ => { - // Cannot happen as clap will fail earlier - crash!(1, "Extra operand: {}", files[2]); - } - }; - - let uniq = Uniq { - repeats_only: matches.is_present(options::REPEATED) - || matches.is_present(options::ALL_REPEATED), - uniques_only: matches.is_present(options::UNIQUE), - all_repeated: matches.is_present(options::ALL_REPEATED) - || matches.is_present(options::GROUP), - delimiters: get_delimiter(&matches), - show_counts: matches.is_present(options::COUNT), - skip_fields: opt_parsed(options::SKIP_FIELDS, &matches), - slice_start: opt_parsed(options::SKIP_CHARS, &matches), - slice_stop: opt_parsed(options::CHECK_CHARS, &matches), - ignore_case: matches.is_present(options::IGNORE_CASE), - zero_terminated: matches.is_present(options::ZERO_TERMINATED), - }; - uniq.print_uniq( - &mut open_input_file(in_file_name), - &mut open_output_file(out_file_name), - ); - - 0 } fn get_delimiter(matches: &ArgMatches) -> Delimiters { diff --git a/src/uu/unlink/src/unlink.rs b/src/uu/unlink/src/unlink.rs index 343f2653f..49f17cb12 100644 --- a/src/uu/unlink/src/unlink.rs +++ b/src/uu/unlink/src/unlink.rs @@ -33,12 +33,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg(Arg::with_name(OPT_PATH).hidden(true).multiple(true)) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let paths: Vec = matches .values_of(OPT_PATH) @@ -98,3 +93,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg(Arg::with_name(OPT_PATH).hidden(true).multiple(true)) +} diff --git a/src/uu/uptime/src/uptime.rs b/src/uu/uptime/src/uptime.rs index 3683a4de0..35270093c 100644 --- a/src/uu/uptime/src/uptime.rs +++ b/src/uu/uptime/src/uptime.rs @@ -38,17 +38,7 @@ fn get_usage() -> String { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) - .usage(&usage[..]) - .arg( - Arg::with_name(options::SINCE) - .short("s") - .long(options::SINCE) - .help("system up since"), - ) - .get_matches_from(args); + let matches = uu_app().usage(&usage[..]).get_matches_from(args); let (boot_time, user_count) = process_utmpx(); let uptime = get_uptime(boot_time); @@ -73,6 +63,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg( + Arg::with_name(options::SINCE) + .short("s") + .long(options::SINCE) + .help("system up since"), + ) +} + #[cfg(unix)] fn print_loadavg() { use uucore::libc::c_double; diff --git a/src/uu/users/src/users.rs b/src/uu/users/src/users.rs index 5b1f1c037..ef878497c 100644 --- a/src/uu/users/src/users.rs +++ b/src/uu/users/src/users.rs @@ -34,12 +34,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let after_help = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&after_help[..]) - .arg(Arg::with_name(ARG_FILES).takes_value(true).max_values(1)) .get_matches_from(args); let files: Vec = matches @@ -66,3 +63,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) + .arg(Arg::with_name(ARG_FILES).takes_value(true).max_values(1)) +} diff --git a/src/uu/wc/src/wc.rs b/src/uu/wc/src/wc.rs index d1e1f75ca..0bcc66664 100644 --- a/src/uu/wc/src/wc.rs +++ b/src/uu/wc/src/wc.rs @@ -134,10 +134,39 @@ impl Input { pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); - let matches = App::new(executable!()) + let matches = uu_app().usage(&usage[..]).get_matches_from(args); + + let mut inputs: Vec = matches + .values_of(ARG_FILES) + .map(|v| { + v.map(|i| { + if i == "-" { + Input::Stdin(StdinKind::Explicit) + } else { + Input::Path(ToString::to_string(i)) + } + }) + .collect() + }) + .unwrap_or_default(); + + if inputs.is_empty() { + inputs.push(Input::Stdin(StdinKind::Implicit)); + } + + let settings = Settings::new(&matches); + + if wc(inputs, &settings).is_ok() { + 0 + } else { + 1 + } +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) .version(crate_version!()) .about(ABOUT) - .usage(&usage[..]) .arg( Arg::with_name(options::BYTES) .short("c") @@ -169,33 +198,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .help("print the word counts"), ) .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true)) - .get_matches_from(args); - - let mut inputs: Vec = matches - .values_of(ARG_FILES) - .map(|v| { - v.map(|i| { - if i == "-" { - Input::Stdin(StdinKind::Explicit) - } else { - Input::Path(ToString::to_string(i)) - } - }) - .collect() - }) - .unwrap_or_default(); - - if inputs.is_empty() { - inputs.push(Input::Stdin(StdinKind::Implicit)); - } - - let settings = Settings::new(&matches); - - if wc(inputs, &settings).is_ok() { - 0 - } else { - 1 - } } fn word_count_from_reader( diff --git a/src/uu/who/src/who.rs b/src/uu/who/src/who.rs index 047452240..6a9c88710 100644 --- a/src/uu/who/src/who.rs +++ b/src/uu/who/src/who.rs @@ -64,11 +64,105 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let usage = get_usage(); let after_help = get_long_usage(); - let matches = App::new(executable!()) - .version(crate_version!()) - .about(ABOUT) + let matches = uu_app() .usage(&usage[..]) .after_help(&after_help[..]) + .get_matches_from(args); + + let files: Vec = matches + .values_of(options::FILE) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); + + // If true, attempt to canonicalize hostnames via a DNS lookup. + let do_lookup = matches.is_present(options::LOOKUP); + + // If true, display only a list of usernames and count of + // the users logged on. + // Ignored for 'who am i'. + let short_list = matches.is_present(options::COUNT); + + let all = matches.is_present(options::ALL); + + // If true, display a line at the top describing each field. + let include_heading = matches.is_present(options::HEADING); + + // If true, display a '+' for each user if mesg y, a '-' if mesg n, + // or a '?' if their tty cannot be statted. + let include_mesg = all || matches.is_present(options::MESG) || matches.is_present("w"); + + // If true, display the last boot time. + let need_boottime = all || matches.is_present(options::BOOT); + + // If true, display dead processes. + let need_deadprocs = all || matches.is_present(options::DEAD); + + // If true, display processes waiting for user login. + let need_login = all || matches.is_present(options::LOGIN); + + // If true, display processes started by init. + let need_initspawn = all || matches.is_present(options::PROCESS); + + // If true, display the last clock change. + let need_clockchange = all || matches.is_present(options::TIME); + + // If true, display the current runlevel. + let need_runlevel = all || matches.is_present(options::RUNLEVEL); + + let use_defaults = !(all + || need_boottime + || need_deadprocs + || need_login + || need_initspawn + || need_runlevel + || need_clockchange + || matches.is_present(options::USERS)); + + // If true, display user processes. + let need_users = all || matches.is_present(options::USERS) || use_defaults; + + // If true, display the hours:minutes since each user has touched + // the keyboard, or "." if within the last minute, or "old" if + // not within the last day. + let include_idle = need_deadprocs || need_login || need_runlevel || need_users; + + // If true, display process termination & exit status. + let include_exit = need_deadprocs; + + // If true, display only name, line, and time fields. + let short_output = !include_exit && use_defaults; + + // If true, display info only for the controlling tty. + let my_line_only = matches.is_present(options::ONLY_HOSTNAME_USER) || files.len() == 2; + + let mut who = Who { + do_lookup, + short_list, + short_output, + include_idle, + include_heading, + include_mesg, + include_exit, + need_boottime, + need_deadprocs, + need_login, + need_initspawn, + need_clockchange, + need_runlevel, + need_users, + my_line_only, + args: files, + }; + + who.exec(); + + 0 +} + +pub fn uu_app() -> App<'static, 'static> { + App::new(executable!()) + .version(crate_version!()) + .about(ABOUT) .arg( Arg::with_name(options::ALL) .long(options::ALL) @@ -164,96 +258,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .min_values(1) .max_values(2), ) - .get_matches_from(args); - - let files: Vec = matches - .values_of(options::FILE) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); - - // If true, attempt to canonicalize hostnames via a DNS lookup. - let do_lookup = matches.is_present(options::LOOKUP); - - // If true, display only a list of usernames and count of - // the users logged on. - // Ignored for 'who am i'. - let short_list = matches.is_present(options::COUNT); - - let all = matches.is_present(options::ALL); - - // If true, display a line at the top describing each field. - let include_heading = matches.is_present(options::HEADING); - - // If true, display a '+' for each user if mesg y, a '-' if mesg n, - // or a '?' if their tty cannot be statted. - let include_mesg = all || matches.is_present(options::MESG) || matches.is_present("w"); - - // If true, display the last boot time. - let need_boottime = all || matches.is_present(options::BOOT); - - // If true, display dead processes. - let need_deadprocs = all || matches.is_present(options::DEAD); - - // If true, display processes waiting for user login. - let need_login = all || matches.is_present(options::LOGIN); - - // If true, display processes started by init. - let need_initspawn = all || matches.is_present(options::PROCESS); - - // If true, display the last clock change. - let need_clockchange = all || matches.is_present(options::TIME); - - // If true, display the current runlevel. - let need_runlevel = all || matches.is_present(options::RUNLEVEL); - - let use_defaults = !(all - || need_boottime - || need_deadprocs - || need_login - || need_initspawn - || need_runlevel - || need_clockchange - || matches.is_present(options::USERS)); - - // If true, display user processes. - let need_users = all || matches.is_present(options::USERS) || use_defaults; - - // If true, display the hours:minutes since each user has touched - // the keyboard, or "." if within the last minute, or "old" if - // not within the last day. - let include_idle = need_deadprocs || need_login || need_runlevel || need_users; - - // If true, display process termination & exit status. - let include_exit = need_deadprocs; - - // If true, display only name, line, and time fields. - let short_output = !include_exit && use_defaults; - - // If true, display info only for the controlling tty. - let my_line_only = matches.is_present(options::ONLY_HOSTNAME_USER) || files.len() == 2; - - let mut who = Who { - do_lookup, - short_list, - short_output, - include_idle, - include_heading, - include_mesg, - include_exit, - need_boottime, - need_deadprocs, - need_login, - need_initspawn, - need_clockchange, - need_runlevel, - need_users, - my_line_only, - args: files, - }; - - who.exec(); - - 0 } struct Who { diff --git a/src/uu/whoami/src/whoami.rs b/src/uu/whoami/src/whoami.rs index 383fb40b5..bd2eea1e3 100644 --- a/src/uu/whoami/src/whoami.rs +++ b/src/uu/whoami/src/whoami.rs @@ -1,3 +1,5 @@ +use clap::App; + // * This file is part of the uutils coreutils package. // * // * (c) Jordi Boggiano @@ -15,7 +17,7 @@ extern crate uucore; mod platform; pub fn uumain(args: impl uucore::Args) -> i32 { - let app = app_from_crate!(); + let app = uu_app(); if let Err(err) = app.get_matches_from_safe(args) { if err.kind == clap::ErrorKind::HelpDisplayed @@ -34,6 +36,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } } +pub fn uu_app() -> App<'static, 'static> { + app_from_crate!() +} + pub fn exec() { unsafe { match platform::get_username() { diff --git a/src/uu/yes/src/yes.rs b/src/uu/yes/src/yes.rs index 1fc2d92bc..2c0d43000 100644 --- a/src/uu/yes/src/yes.rs +++ b/src/uu/yes/src/yes.rs @@ -12,7 +12,7 @@ extern crate clap; #[macro_use] extern crate uucore; -use clap::Arg; +use clap::{App, Arg}; use std::borrow::Cow; use std::io::{self, Write}; use uucore::zero_copy::ZeroCopyWriter; @@ -22,7 +22,7 @@ use uucore::zero_copy::ZeroCopyWriter; const BUF_SIZE: usize = 16 * 1024; pub fn uumain(args: impl uucore::Args) -> i32 { - let app = app_from_crate!().arg(Arg::with_name("STRING").index(1).multiple(true)); + let app = uu_app(); let matches = match app.get_matches_from_safe(args) { Ok(m) => m, @@ -56,6 +56,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } +pub fn uu_app() -> App<'static, 'static> { + app_from_crate!().arg(Arg::with_name("STRING").index(1).multiple(true)) +} + #[cfg(not(feature = "latency"))] fn prepare_buffer<'a>(input: &'a str, buffer: &'a mut [u8; BUF_SIZE]) -> &'a [u8] { if input.len() < BUF_SIZE / 2 {