diff --git a/src/arch/arch.rs b/src/arch/arch.rs index 45a280226..94dfb3a89 100644 --- a/src/arch/arch.rs +++ b/src/arch/arch.rs @@ -18,6 +18,10 @@ use std::ffi::CStr; use std::mem::uninitialized; use uucore::c_types::utsname; +static SYNTAX: &'static str = ""; +static SUMMARY: &'static str = "Determine architecture name for current machine."; +static LONG_HELP: &'static str = ""; + struct Arch { arch_name: String } @@ -38,19 +42,8 @@ unsafe fn get_machine_arch() -> Arch { } } -static NAME: &'static str = "arch"; -static VERSION: &'static str = env!("CARGO_PKG_VERSION"); - pub fn uumain(args: Vec) -> i32 { - let mut opts = uucore::coreopts::CoreOptions::new(NAME); - let usage = opts.usage("Determine architecture name for current machine."); - opts.help(format!(" -{0} {1} - -{0} - -{2} -", NAME, VERSION, usage)).parse(args); + new_coreopts!(SYNTAX, SUMMARY, LONG_HELP).parse(args); let machine_arch = unsafe { get_machine_arch() }; let mut output = String::new(); diff --git a/src/cut/cut.rs b/src/cut/cut.rs index 67997d8e1..c7bce0538 100644 --- a/src/cut/cut.rs +++ b/src/cut/cut.rs @@ -25,8 +25,87 @@ mod buffer; mod ranges; mod searcher; -static NAME: &'static str = "cut"; -static VERSION: &'static str = env!("CARGO_PKG_VERSION"); +static SYNTAX: &'static str = "[-d] [-s] [-z] [--output-delimiter] ((-f|-b|-c) {{sequence}}) {{sourcefile}}+"; +static SUMMARY: &'static str = "Prints specified byte or field columns from each line of stdin or the input files"; +static LONG_HELP: &'static str = " + Each call must specify a mode (what to use for columns), + a sequence (which columns to print), and provide a data source + + Specifying a mode + + Use --bytes (-b) or --characters (-c) to specify byte mode + + Use --fields (-f) to specify field mode, where each line is broken into + fields identified by a delimiter character. For example for a typical CSV + you could use this in combination with setting comma as the delimiter + + Specifying a sequence + + A sequence is a group of 1 or more numbers or inclusive ranges separated + by a commas. + + cut -f 2,5-7 some_file.txt + will display the 2nd, 5th, 6th, and 7th field for each source line + + Ranges can extend to the end of the row by excluding the the second number + + cut -f 3- some_file.txt + will display the 3rd field and all fields after for each source line + + The first number of a range can be excluded, and this is effectively the + same as using 1 as the first number: it causes the range to begin at the + first column. Ranges can also display a single column + + cut -f 1,3-5 some_file.txt + will display the 1st, 3rd, 4th, and 5th field for each source line + + The --complement option, when used, inverts the effect of the sequence + + cut --complement -f 4-6 some_file.txt + will display the every field but the 4th, 5th, and 6th + + Specifying a data source + + If no sourcefile arguments are specified, stdin is used as the source of + lines to print + + If sourcefile arguments are specified, stdin is ignored and all files are + read in consecutively if a sourcefile is not successfully read, a warning + will print to stderr, and the eventual status code will be 1, but cut + will continue to read through proceeding sourcefiles + + To print columns from both STDIN and a file argument, use - (dash) as a + sourcefile argument to represent stdin. + + Field Mode options + + The fields in each line are identified by a delimiter (separator) + + Set the delimiter + Set the delimiter which separates fields in the file using the + --delimiter (-d) option. Setting the delimiter is optional. + If not set, a default delimiter of Tab will be used. + + Optionally Filter based on delimiter + If the --only-delimited (-s) flag is provided, only lines which + contain the delimiter will be printed + + Replace the delimiter + If the --output-delimiter option is provided, the argument used for + it will replace the delimiter character in each line printed. This is + useful for transforming tabular data - e.g. to convert a CSV to a + TSV (tab-separated file) + + Line endings + + When the --zero-terminated (-z) option is used, cut sees \\0 (null) as the + 'line ending' character (both for the purposes of reading lines and + separating printed lines) instead of \\n (newline). This is useful for + tabular data where some of the cells may contain newlines + + echo 'ab\\0cd' | cut -z -c 1 + will result in 'a\\0c\\0' +"; struct Options { out_delim: Option, @@ -408,8 +487,7 @@ fn cut_files(mut filenames: Vec, mode: Mode) -> i32 { } pub fn uumain(args: Vec) -> i32 { - let mut opts = uucore::coreopts::CoreOptions::new(NAME); - + let mut opts = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP); opts.optopt("b", "bytes", "filter byte columns from the input source", "sequence"); opts.optopt("c", "characters", "alias for character mode", "sequence"); opts.optopt("d", "delimiter", "specify the delimiter character that separates fields in the input source. Defaults to Tab.", "delimiter"); @@ -419,95 +497,6 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("s", "only-delimited", "in field mode, only print lines which contain the delimiter"); opts.optflag("z", "zero-terminated", "instead of filtering columns based on line, filter columns based on \\0 (NULL character)"); opts.optopt("", "output-delimiter", "in field mode, replace the delimiter in output lines with this option's argument", "new delimiter"); - let usage = opts.usage("Prints specified byte or field columns from each line of stdin or the input files"); - opts.help(format!(" - {0} {1} - - {0} [-d] [-s] [-z] [--output-delimiter] ((-f|-b|-c) {{sequence}}) {{sourcefile}}+ - - {2} - - Reference - - Each call must specify a mode (what to use for columns), - a sequence (which columns to print), and provide a data source - - Specifying a mode - - Use --bytes (-b) or --characters (-c) to specify byte mode - - Use --fields (-f) to specify field mode, where each line is broken into - fields identified by a delimiter character. For example for a typical CSV - you could use this in combination with setting comma as the delimiter - - Specifying a sequence - - A sequence is a group of 1 or more numbers or inclusive ranges separated - by a commas. - - cut -f 2,5-7 some_file.txt - will display the 2nd, 5th, 6th, and 7th field for each source line - - Ranges can extend to the end of the row by excluding the the second number - - cut -f 3- some_file.txt - will display the 3rd field and all fields after for each source line - - The first number of a range can be excluded, and this is effectively the - same as using 1 as the first number: it causes the range to begin at the - first column. Ranges can also display a single column - - cut -f 1,3-5 some_file.txt - will display the 1st, 3rd, 4th, and 5th field for each source line - - The --complement option, when used, inverts the effect of the sequence - - cut --complement -f 4-6 some_file.txt - will display the every field but the 4th, 5th, and 6th - - Specifying a data source - - If no sourcefile arguments are specified, stdin is used as the source of - lines to print - - If sourcefile arguments are specified, stdin is ignored and all files are - read in consecutively if a sourcefile is not successfully read, a warning - will print to stderr, and the eventual status code will be 1, but cut - will continue to read through proceeding sourcefiles - - To print columns from both STDIN and a file argument, use - (dash) as a - sourcefile argument to represent stdin. - - Field Mode options - - The fields in each line are identified by a delimiter (separator) - - Set the delimiter - Set the delimiter which separates fields in the file using the - --delimiter (-d) option. Setting the delimiter is optional. - If not set, a default delimiter of Tab will be used. - - Optionally Filter based on delimiter - If the --only-delimited (-s) flag is provided, only lines which - contain the delimiter will be printed - - Replace the delimiter - If the --output-delimiter option is provided, the argument used for - it will replace the delimiter character in each line printed. This is - useful for transforming tabular data - e.g. to convert a CSV to a - TSV (tab-separated file) - - Line endings - - When the --zero-terminated (-z) option is used, cut sees \\0 (null) as the - 'line ending' character (both for the purposes of reading lines and - separating printed lines) instead of \\n (newline). This is useful for - tabular data where some of the cells may contain newlines - - echo 'ab\\0cd' | cut -z -c 1 - will result in 'a\\0c\\0' - -", NAME, VERSION, usage)); let matches = opts.parse(args); let complement = matches.opt_present("complement"); diff --git a/src/pinky/pinky.rs b/src/pinky/pinky.rs index ecb5da188..ddc8da540 100644 --- a/src/pinky/pinky.rs +++ b/src/pinky/pinky.rs @@ -14,7 +14,6 @@ extern crate uucore; use uucore::c_types::getpwnam; use uucore::utmpx::{self, time, Utmpx}; -use uucore::coreopts; use uucore::libc::{uid_t, gid_t, c_char, S_IWGRP}; use std::io::prelude::*; @@ -29,12 +28,28 @@ use std::ffi::{CStr, CString}; use std::path::PathBuf; -static NAME: &'static str = "pinky"; - +static SYNTAX: &'static str = "[OPTION]... [USER]..."; +static SUMMARY: &'static str = "A lightweight 'finger' program; print user information."; + const BUFSIZE: usize = 1024; pub fn uumain(args: Vec) -> i32 { - let mut opts = coreopts::CoreOptions::new(NAME); + let long_help = &format!(" + -l produce long format output for the specified USERs + -b omit the user's home directory and shell in long format + -h omit the user's project file in long format + -p omit the user's plan file in long format + -s do short format output, this is the default + -f omit the line of column headings in short format + -w omit the user's full name in short format + -i omit the user's full name and remote host in short format + -q omit the user's full name, remote host and idle time + in short format + --help display this help and exit + --version output version information and exit + +The utmp file will be {}", utmpx::DEFAULT_FILE); + let mut opts = new_coreopts!(SYNTAX, SUMMARY, &long_help); opts.optflag("l", "l", "produce long format output for the specified USERs"); @@ -55,27 +70,6 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("", "help", "display this help and exit"); opts.optflag("", "version", "output version information and exit"); - opts.help(format!( - "Usage: {} [OPTION]... [USER]... - - -l produce long format output for the specified USERs - -b omit the user's home directory and shell in long format - -h omit the user's project file in long format - -p omit the user's plan file in long format - -s do short format output, this is the default - -f omit the line of column headings in short format - -w omit the user's full name in short format - -i omit the user's full name and remote host in short format - -q omit the user's full name, remote host and idle time - in short format - --help display this help and exit - --version output version information and exit - -A lightweight 'finger' program; print user information. -The utmp file will be {}", - NAME, - utmpx::DEFAULT_FILE)); - let matches = opts.parse(args); // If true, display the hours:minutes since each user has touched diff --git a/src/uucore/coreopts.rs b/src/uucore/coreopts.rs index 72647accc..b2e2e0084 100644 --- a/src/uucore/coreopts.rs +++ b/src/uucore/coreopts.rs @@ -1,36 +1,39 @@ extern crate getopts; use std::io::Write; -pub struct CoreOptions { - pub options : getopts::Options, - pkgname: &'static str, - longhelp : Option +pub struct HelpText<'a> { + pub name : &'a str, + pub version : &'a str, + pub syntax : &'a str, + pub summary : &'a str, + pub long_help : &'a str, + pub display_usage : bool } -impl<'a> CoreOptions { - pub fn new(name: &'static str) -> Self { +pub struct CoreOptions<'a> { + options : getopts::Options, + help_text : HelpText<'a> +} + +impl<'a> CoreOptions<'a> { + pub fn new(help_text: HelpText<'a>) -> Self { let mut ret = CoreOptions { options : getopts::Options::new(), - pkgname: name, - longhelp: None + help_text : help_text }; ret.options .optflag("", "help", "print usage information") .optflag("", "version", "print name and version number"); ret } - pub fn optopt(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str) -> &mut CoreOptions { + pub fn optopt(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str) -> &mut CoreOptions<'a> { self.options.optopt(short_name, long_name, desc, hint); self } - pub fn optflag(&mut self, short_name: &str, long_name: &str, desc: &str) -> &mut CoreOptions { + pub fn optflag(&mut self, short_name: &str, long_name: &str, desc: &str) -> &mut CoreOptions<'a> { self.options.optflag(short_name, long_name, desc); self } - pub fn help>(&mut self, longhelp : T) -> &mut CoreOptions { - self.longhelp = Some(longhelp.into()); - self - } pub fn usage(&self, summary : &str) -> String { self.options.usage(summary) } @@ -38,20 +41,50 @@ impl<'a> CoreOptions { let matches = match self.options.parse(&args[1..]) { Ok(m) => { Some(m) }, Err(f) => { - eprintln!("{}: {}", self.pkgname, f); - eprintln!("Try '{} --help' for more information.", self.pkgname); - exit!(1) + crash!(1, "{}", f); } }.unwrap(); - if matches.opt_present("help") { - exit!(match self.longhelp { - Some(ref lhelp) => { println!("{}", lhelp); 0} - None => 1 - }); + if matches.opt_present("help") { + let usage_str = if self.help_text.display_usage { + format!("\n {}\n\n Reference\n", + self.options.usage(self.help_text.summary) + ).replace("Options:", " Options:") + } else { String::new() }; + print!(" + {0} {1} + + {0} {2} +{3}{4} +", self.help_text.name, self.help_text.version, self.help_text.syntax, usage_str, self.help_text.long_help); + exit!(0); } else if matches.opt_present("version") { - println!("{} {}", self.pkgname, env!("CARGO_PKG_VERSION")); + println!("{} {}", self.help_text.name, self.help_text.version); exit!(0); } matches } } + +#[macro_export] +macro_rules! new_coreopts { + ($syntax: expr, $summary: expr, $long_help: expr) => ( + uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText { + name: executable!(), + version: env!("CARGO_PKG_VERSION"), + syntax: $syntax, + summary: $summary, + long_help: $long_help, + display_usage: true + }) + ); + ($syntax: expr, $summary: expr, $long_help: expr, $display_usage: expr) => ( + uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText { + name: executable!(), + version: env!("CARGO_PKG_VERSION"), + syntax: $syntax, + summary: $summary, + long_help: $long_help, + display_usage: $display_usage + }) + ); +} \ No newline at end of file diff --git a/src/uucore/lib.rs b/src/uucore/lib.rs index 61b3624ae..994112b74 100644 --- a/src/uucore/lib.rs +++ b/src/uucore/lib.rs @@ -3,6 +3,7 @@ pub extern crate libc; #[macro_use] mod macros; +#[macro_use] pub mod coreopts; #[cfg(feature = "fs")] diff --git a/src/uucore/macros.rs b/src/uucore/macros.rs index c3e7b0c8e..a88d78b84 100644 --- a/src/uucore/macros.rs +++ b/src/uucore/macros.rs @@ -264,15 +264,6 @@ macro_rules! snippet_list_join_or { ); } -//-- message templates : help and version - -#[macro_export] -macro_rules! msg_version { - () => ( - format!("{} {}", executable!(), env!("CARGO_PKG_VERSION")) - ) -} - //-- message templates : invalid input #[macro_export] diff --git a/src/who/who.rs b/src/who/who.rs index e8bb94972..3f5fa402d 100644 --- a/src/who/who.rs +++ b/src/who/who.rs @@ -12,7 +12,6 @@ #[macro_use] extern crate uucore; use uucore::utmpx::{self, time, Utmpx}; -use uucore::coreopts; use uucore::libc::{STDIN_FILENO, time_t, ttyname, S_IWGRP}; use std::borrow::Cow; @@ -21,11 +20,35 @@ use std::ffi::CStr; use std::path::PathBuf; use std::os::unix::fs::MetadataExt; -static NAME: &'static str = "who"; +static SYNTAX: &'static str = "[OPTION]... [ FILE | ARG1 ARG2 ]"; +static SUMMARY: &'static str = "Print information about users who are currently logged in."; +static LONG_HELP: &'static str = " + -a, --all same as -b -d --login -p -r -t -T -u + -b, --boot time of last system boot + -d, --dead print dead processes + -H, --heading print line of column headings + -l, --login print system login processes + --lookup attempt to canonicalize hostnames via DNS + -m only hostname and user associated with stdin + -p, --process print active processes spawned by init + -q, --count all login names and number of users logged on + -r, --runlevel print current runlevel + -s, --short print only name, line, and time (default) + -t, --time print last system clock change + -T, -w, --mesg add user's message status as +, - or ? + -u, --users list users logged in + --message same as -T + --writable same as -T + --help display this help and exit + --version output version information and exit + +If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is common. +If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual. +"; pub fn uumain(args: Vec) -> i32 { - let mut opts = coreopts::CoreOptions::new(NAME); + let mut opts = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP); opts.optflag("a", "all", "same as -b -d --login -p -r -t -T -u"); opts.optflag("b", "boot", "time of last system boot"); opts.optflag("d", "dead", "print dead processes"); @@ -49,32 +72,6 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("", "help", "display this help and exit"); opts.optflag("", "version", "output version information and exit"); - opts.help(format!("Usage: {} [OPTION]... [ FILE | ARG1 ARG2 ] -Print information about users who are currently logged in. - - -a, --all same as -b -d --login -p -r -t -T -u - -b, --boot time of last system boot - -d, --dead print dead processes - -H, --heading print line of column headings - -l, --login print system login processes - --lookup attempt to canonicalize hostnames via DNS - -m only hostname and user associated with stdin - -p, --process print active processes spawned by init - -q, --count all login names and number of users logged on - -r, --runlevel print current runlevel - -s, --short print only name, line, and time (default) - -t, --time print last system clock change - -T, -w, --mesg add user's message status as +, - or ? - -u, --users list users logged in - --message same as -T - --writable same as -T - --help display this help and exit - --version output version information and exit - -If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is common. -If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual.", - NAME)); - let matches = opts.parse(args); // If true, attempt to canonicalize hostnames via a DNS lookup.