diff --git a/Cargo.lock b/Cargo.lock index f539daf36..cebad1823 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2055,7 +2055,7 @@ dependencies = [ name = "uu_seq" version = "0.0.1" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)", "uucore_procs 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)", ] diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index 29d1eb1cf..b7cf3901c 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" path = "src/seq.rs" [dependencies] -getopts = "0.2.18" +clap = "2.33" uucore = { version="0.0.4", package="uucore", git="https://github.com/uutils/uucore.git", branch="canary" } uucore_procs = { version="0.0.4", package="uucore_procs", git="https://github.com/uutils/uucore.git", branch="canary" } diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 57890ab28..d531ffb12 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -3,17 +3,31 @@ // spell-checker:ignore (ToDO) istr chiter argptr ilen -extern crate getopts; +extern crate clap; #[macro_use] extern crate uucore; +use clap::{App, Arg}; use std::cmp; use std::io::{stdout, Write}; -static NAME: &str = "seq"; static VERSION: &str = env!("CARGO_PKG_VERSION"); +static ABOUT: &str = "Display numbers from FIRST to LAST, in steps of INCREMENT."; +static OPT_SEPARATOR: &str = "separator"; +static OPT_TERMINATOR: &str = "terminator"; +static OPT_WIDTHS: &str = "widths"; +static ARG_NUMBERS: &str = "numbers"; + +fn get_usage() -> String { + format!( + "{0} [OPTION]... LAST + {0} [OPTION]... FIRST LAST + {0} [OPTION]... FIRST INCREMENT LAST", + executable!() + ) +} #[derive(Clone)] struct SeqOptions { separator: String, @@ -38,162 +52,57 @@ fn escape_sequences(s: &str) -> String { s.replace("\\n", "\n").replace("\\t", "\t") } -fn parse_options(args: Vec, options: &mut SeqOptions) -> Result, i32> { - let mut seq_args = vec![]; - let mut iter = args.into_iter().skip(1); - while let Some(arg) = iter.next() { - match &arg[..] { - "--help" | "-h" => { - print_help(); - return Err(0); - } - "--version" | "-V" => { - print_version(); - return Err(0); - } - "-s" | "--separator" => match iter.next() { - Some(sep) => options.separator = sep, - None => { - show_error!("expected a separator after {}", arg); - return Err(1); - } - }, - "-t" | "--terminator" => match iter.next() { - Some(term) => options.terminator = Some(term), - None => { - show_error!("expected a terminator after '{}'", arg); - return Err(1); - } - }, - "-w" | "--widths" => options.widths = true, - "--" => { - seq_args.extend(iter); - break; - } - _ => { - if arg.len() > 1 && arg.starts_with('-') { - let argptr: *const String = &arg; // escape from the borrow checker - let mut chiter = unsafe { &(*argptr)[..] }.chars().skip(1); - let mut ch = ' '; - while match chiter.next() { - Some(m) => { - ch = m; - true - } - None => false, - } { - match ch { - 'h' => { - print_help(); - return Err(0); - } - 'V' => { - print_version(); - return Err(0); - } - 's' => match iter.next() { - Some(sep) => { - options.separator = sep; - let next = chiter.next(); - if let Some(n) = next { - show_error!("unexpected character ('{}')", n); - return Err(1); - } - } - None => { - show_error!("expected a separator after {}", arg); - return Err(1); - } - }, - 't' => match iter.next() { - Some(term) => { - options.terminator = Some(term); - let next = chiter.next(); - if let Some(n) = next { - show_error!("unexpected character ('{}')", n); - return Err(1); - } - } - None => { - show_error!("expected a terminator after {}", arg); - return Err(1); - } - }, - 'w' => options.widths = true, - _ => { - seq_args.push(arg); - break; - } - } - } - } else { - seq_args.push(arg); - } - } - }; - } - Ok(seq_args) -} - -fn print_help() { - let mut opts = getopts::Options::new(); - - opts.optopt( - "s", - "separator", - "Separator character (defaults to \\n)", - "", - ); - opts.optopt( - "t", - "terminator", - "Terminator character (defaults to separator)", - "", - ); - opts.optflag( - "w", - "widths", - "Equalize widths of all numbers by padding with zeros", - ); - opts.optflag("h", "help", "print this help text and exit"); - opts.optflag("V", "version", "print version and exit"); - - println!("{} {}\n", NAME, VERSION); - println!( - "Usage:\n {} [-w] [-s string] [-t string] [first [step]] last\n", - NAME - ); - println!("{}", opts.usage("Print sequences of numbers")); -} - -fn print_version() { - println!("{} {}", NAME, VERSION); -} - pub fn uumain(args: impl uucore::Args) -> i32 { - let args = args.collect_str(); + let usage = get_usage(); + let matches = App::new(executable!()) + .version(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 separator)") + .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) + .max_values(3), + ) + .get_matches_from(args); + + let numbers = matches.values_of(ARG_NUMBERS).unwrap().collect::>(); let mut options = SeqOptions { separator: "\n".to_owned(), terminator: None, widths: false, }; - let free = match parse_options(args, &mut options) { - Ok(m) => m, - Err(f) => return f, - }; - if free.is_empty() || free.len() > 3 { - crash!( - 1, - "too {} operands.\nTry '{} --help' for more information.", - if free.is_empty() { "few" } else { "many" }, - NAME - ); - } + options.separator = matches.value_of(OPT_SEPARATOR).unwrap_or("\n").to_string(); + options.terminator = matches.value_of(OPT_TERMINATOR).map(String::from); + options.widths = matches.is_present(OPT_WIDTHS); + let mut largest_dec = 0; let mut padding = 0; - let first = if free.len() > 1 { - let slice = &free[0][..]; + let first = if numbers.len() > 1 { + let slice = &numbers[0][..]; let len = slice.len(); let dec = slice.find('.').unwrap_or(len); largest_dec = len - dec; @@ -208,8 +117,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } else { 1.0 }; - let step = if free.len() > 2 { - let slice = &free[1][..]; + let increment = if numbers.len() > 2 { + let slice = &numbers[1][..]; let len = slice.len(); let dec = slice.find('.').unwrap_or(len); largest_dec = cmp::max(largest_dec, len - dec); @@ -225,7 +134,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 1.0 }; let last = { - let slice = &free[free.len() - 1][..]; + let slice = &numbers[numbers.len() - 1][..]; padding = cmp::max(padding, slice.find('.').unwrap_or_else(|| slice.len())); match parse_float(slice) { Ok(n) => n, @@ -245,7 +154,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { }; print_seq( first, - step, + increment, last, largest_dec, separator, @@ -257,8 +166,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 0 } -fn done_printing(next: f64, step: f64, last: f64) -> bool { - if step >= 0f64 { +fn done_printing(next: f64, increment: f64, last: f64) -> bool { + if increment >= 0f64 { next > last } else { next < last @@ -268,7 +177,7 @@ fn done_printing(next: f64, step: f64, last: f64) -> bool { #[allow(clippy::too_many_arguments)] fn print_seq( first: f64, - step: f64, + increment: f64, last: f64, largest_dec: usize, separator: String, @@ -277,8 +186,8 @@ fn print_seq( padding: usize, ) { let mut i = 0isize; - let mut value = first + i as f64 * step; - while !done_printing(value, step, last) { + let mut value = first + i as f64 * increment; + while !done_printing(value, increment, last) { let istr = format!("{:.*}", largest_dec, value); let ilen = istr.len(); let before_dec = istr.find('.').unwrap_or(ilen); @@ -289,12 +198,12 @@ fn print_seq( } print!("{}", istr); i += 1; - value = first + i as f64 * step; - if !done_printing(value, step, last) { + value = first + i as f64 * increment; + if !done_printing(value, increment, last) { print!("{}", separator); } } - if (first >= last && step < 0f64) || (first <= last && step > 0f64) { + if (first >= last && increment < 0f64) || (first <= last && increment > 0f64) { print!("{}", terminator); } crash_if_err!(1, stdout().flush()); diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 440a2bc98..9ee5c94aa 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -31,3 +31,8 @@ fn test_equalize_widths() { .run() .stdout_is("05\n06\n07\n08\n09\n10\n"); } + +#[test] +fn test_seq_wrong_arg() { + new_ucmd!().args(&["-w", "5", "10", "33", "32"]).fails(); +}