diff --git a/Cargo.lock b/Cargo.lock index e35bcc68c..abdf482ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "advapi32-sys" version = "0.2.0" @@ -1653,6 +1655,7 @@ dependencies = [ name = "uu_base32" version = "0.0.6" dependencies = [ + "clap", "uucore", "uucore_procs", ] diff --git a/src/uu/base32/Cargo.toml b/src/uu/base32/Cargo.toml index a1d7ba17e..1b448af0a 100644 --- a/src/uu/base32/Cargo.toml +++ b/src/uu/base32/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/base32.rs" [dependencies] +clap = "2.33" uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/base32/src/base32.rs b/src/uu/base32/src/base32.rs index b5f346f48..6d9f7f08f 100644 --- a/src/uu/base32/src/base32.rs +++ b/src/uu/base32/src/base32.rs @@ -10,9 +10,14 @@ extern crate uucore; use uucore::encoding::Format; use uucore::InvalidEncodingHandling; +use std::fs::File; +use std::io::{stdin, BufReader}; +use std::path::Path; + +use clap::{App, Arg}; + mod base_common; -static SYNTAX: &str = "[OPTION]... [FILE]"; static SUMMARY: &str = "Base32 encode or decode FILE, or standard input, to standard output."; static LONG_HELP: &str = " With no FILE, or when FILE is -, read standard input. @@ -23,14 +28,128 @@ static LONG_HELP: &str = " to attempt to recover from any other non-alphabet bytes in the encoded stream. "; +static VERSION: &str = env!("CARGO_PKG_VERSION"); + +fn get_usage() -> String { + format!("{0} [OPTION]... [FILE]", executable!()) +} + +pub mod options { + pub static DECODE: &str = "decode"; + pub static WRAP: &str = "wrap"; + pub static IGNORE_GARBAGE: &str = "ignore-garbage"; + pub static FILE: &str = "file"; +} + +struct Config { + decode: bool, + ignore_garbage: bool, + wrap_cols: Option, + to_read: Option, +} + +impl Config { + fn from(options: clap::ArgMatches) -> Config { + let file: Option = match options.values_of(options::FILE) { + Some(mut values) => { + let name = values.next().unwrap(); + if values.len() != 0 { + crash!(3, "extra operand ‘{}’", name); + } + + if name == "-" { + None + } else { + if !Path::exists(Path::new(name)) { + crash!(2, "{}: No such file or directory", name); + } + Some(name.to_owned()) + } + } + None => None, + }; + + let cols = match options.value_of(options::WRAP) { + Some(num) => match num.parse::() { + Ok(n) => Some(n), + Err(e) => { + crash!(1, "invalid wrap size: ‘{}’: {}", num, e); + } + }, + None => None, + }; + + Config { + decode: options.is_present(options::DECODE), + ignore_garbage: options.is_present(options::IGNORE_GARBAGE), + wrap_cols: cols, + to_read: file, + } + } +} pub fn uumain(args: impl uucore::Args) -> i32 { - base_common::execute( - args.collect_str(InvalidEncodingHandling::ConvertLossy) - .accept_any(), - SYNTAX, - SUMMARY, - LONG_HELP, - Format::Base32, - ) + let format = Format::Base32; + let usage = get_usage(); + let app = App::new(executable!()) + .version(VERSION) + .about(SUMMARY) + .usage(&usage[..]) + .about(LONG_HELP) + // Format arguments. + .arg( + Arg::with_name(options::DECODE) + .short("d") + .long(options::DECODE) + .help("decode data"), + ) + .arg( + Arg::with_name(options::IGNORE_GARBAGE) + .short("i") + .long(options::IGNORE_GARBAGE) + .help("when decoding, ignore non-alphabetic characters"), + ) + .arg( + Arg::with_name(options::WRAP) + .short("w") + .long(options::WRAP) + .takes_value(true) + .help( + "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)", + ), + ) + // "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(); + let config: Config = Config::from(app.get_matches_from(arg_list)); + match config.to_read { + // Read from file. + Some(name) => { + let file_buf = safe_unwrap!(File::open(Path::new(&name))); + let mut input = BufReader::new(file_buf); + base_common::handle_input( + &mut input, + format, + config.wrap_cols, + config.ignore_garbage, + config.decode, + ); + } + // stdin + None => { + base_common::handle_input( + &mut stdin().lock(), + format, + config.wrap_cols, + config.ignore_garbage, + config.decode, + ); + } + }; + + 0 } diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 3f1436fb2..a4b49e499 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -7,68 +7,11 @@ // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. -use std::fs::File; -use std::io::{stdin, stdout, BufReader, Read, Write}; -use std::path::Path; +use std::io::{stdout, Read, Write}; use uucore::encoding::{wrap_print, Data, Format}; -pub fn execute( - args: Vec, - syntax: &str, - summary: &str, - long_help: &str, - format: Format, -) -> i32 { - let matches = app!(syntax, summary, long_help) - .optflag("d", "decode", "decode data") - .optflag( - "i", - "ignore-garbage", - "when decoding, ignore non-alphabetic characters", - ) - .optopt( - "w", - "wrap", - "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)", - "COLS", - ) - .parse(args); - - let line_wrap = matches.opt_str("wrap").map(|s| match s.parse() { - Ok(n) => n, - Err(e) => { - crash!(1, "invalid wrap size: ‘{}’: {}", s, e); - } - }); - let ignore_garbage = matches.opt_present("ignore-garbage"); - let decode = matches.opt_present("decode"); - - if matches.free.len() > 1 { - show_usage_error!("extra operand ‘{}’", matches.free[0]); - return 1; - } - - if matches.free.is_empty() || &matches.free[0][..] == "-" { - let stdin_raw = stdin(); - handle_input( - &mut stdin_raw.lock(), - format, - line_wrap, - ignore_garbage, - decode, - ); - } else { - let path = Path::new(matches.free[0].as_str()); - let file_buf = safe_unwrap!(File::open(&path)); - let mut input = BufReader::new(file_buf); - handle_input(&mut input, format, line_wrap, ignore_garbage, decode); - }; - - 0 -} - -fn handle_input( +pub fn handle_input( input: &mut R, format: Format, line_wrap: Option, diff --git a/src/uu/pinky/src/pinky.rs b/src/uu/pinky/src/pinky.rs index 3392c9a79..a02096bc8 100644 --- a/src/uu/pinky/src/pinky.rs +++ b/src/uu/pinky/src/pinky.rs @@ -28,7 +28,9 @@ static SUMMARY: &str = "A lightweight 'finger' program; print user information. const BUFSIZE: usize = 1024; pub fn uumain(args: impl uucore::Args) -> i32 { - let args = args.collect_str(InvalidEncodingHandling::Ignore).accept_any(); + let args = args + .collect_str(InvalidEncodingHandling::Ignore) + .accept_any(); let long_help = &format!( " diff --git a/tests/by-util/test_base32.rs b/tests/by-util/test_base32.rs index d3527d26a..157503d83 100644 --- a/tests/by-util/test_base32.rs +++ b/tests/by-util/test_base32.rs @@ -15,6 +15,21 @@ fn test_encode() { .pipe_in(input) .succeeds() .stdout_only("JBSWY3DPFQQFO33SNRSCC===\n"); + + // Using '-' as our file + new_ucmd!() + .arg("-") + .pipe_in(input) + .succeeds() + .stdout_only("JBSWY3DPFQQFO33SNRSCC===\n"); +} + +#[test] +fn test_base32_encode_file() { + new_ucmd!() + .arg("input-simple.txt") + .succeeds() + .stdout_only("JBSWY3DPFQQFO33SNRSCCCQ=\n"); } #[test] @@ -71,8 +86,7 @@ fn test_wrap() { fn test_wrap_no_arg() { for wrap_param in vec!["-w", "--wrap"] { new_ucmd!().arg(wrap_param).fails().stderr_only(format!( - "base32: error: Argument to option '{}' missing\n", - if wrap_param == "-w" { "w" } else { "wrap" } + "error: The argument '--wrap \' requires a value but none was supplied\n\nUSAGE:\n base32 [OPTION]... [FILE]\n\nFor more information try --help" )); } } @@ -87,3 +101,21 @@ fn test_wrap_bad_arg() { .stderr_only("base32: error: invalid wrap size: ‘b’: invalid digit found in string\n"); } } + +#[test] +fn test_base32_extra_operand() { + // Expect a failure when multiple files are specified. + new_ucmd!() + .arg("a.txt") + .arg("a.txt") + .fails() + .stderr_only("base32: error: extra operand ‘a.txt’"); +} + +#[test] +fn test_base32_file_not_found() { + new_ucmd!() + .arg("a.txt") + .fails() + .stderr_only("base32: error: a.txt: No such file or directory"); +} diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index d0a93f871..187b9a1ba 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -13,7 +13,12 @@ fn test_increase_file_size() { file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -25,7 +30,12 @@ fn test_increase_file_size_kb() { file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -46,7 +56,12 @@ fn test_reference() { file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -58,7 +73,12 @@ fn test_decrease_file_size() { ucmd.args(&["--size=-4", TFILE2]).succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -70,7 +90,12 @@ fn test_space_in_size() { ucmd.args(&["--size", " 4", TFILE2]).succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -99,7 +124,12 @@ fn test_at_most_shrinks() { ucmd.args(&["--size", "<4", TFILE2]).succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -111,7 +141,12 @@ fn test_at_most_no_change() { ucmd.args(&["--size", "<40", TFILE2]).succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -123,7 +158,12 @@ fn test_at_least_grows() { ucmd.args(&["--size", ">15", TFILE2]).succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -135,7 +175,12 @@ fn test_at_least_no_change() { ucmd.args(&["--size", ">4", TFILE2]).succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -147,7 +192,12 @@ fn test_round_down() { ucmd.args(&["--size", "/4", TFILE2]).succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } #[test] @@ -159,5 +209,10 @@ fn test_round_up() { ucmd.args(&["--size", "*4", TFILE2]).succeeds(); file.seek(SeekFrom::End(0)).unwrap(); let actual = file.seek(SeekFrom::Current(0)).unwrap(); - assert!(expected == actual, "expected '{}' got '{}'", expected, actual); + assert!( + expected == actual, + "expected '{}' got '{}'", + expected, + actual + ); } diff --git a/tests/fixtures/base32/input-simple.txt b/tests/fixtures/base32/input-simple.txt new file mode 100644 index 000000000..8ab686eaf --- /dev/null +++ b/tests/fixtures/base32/input-simple.txt @@ -0,0 +1 @@ +Hello, World!