mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +00:00
od: Changes command line parser to clap (#1849)
This commit is contained in:
parent
f60790dd41
commit
ca8fbc37bf
4 changed files with 284 additions and 123 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1,5 +1,3 @@
|
||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "advapi32-sys"
|
name = "advapi32-sys"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -1868,7 +1866,7 @@ name = "uu_od"
|
||||||
version = "0.0.4"
|
version = "0.0.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"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)",
|
||||||
"half 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"half 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"uucore 0.0.7",
|
"uucore 0.0.7",
|
||||||
|
|
|
@ -16,7 +16,7 @@ path = "src/od.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
getopts = "0.2.18"
|
clap = "2.33"
|
||||||
half = "1.6"
|
half = "1.6"
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.7", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.7", package="uucore", path="../../uucore" }
|
||||||
|
|
|
@ -41,15 +41,18 @@ use crate::parse_nrofbytes::parse_number_of_bytes;
|
||||||
use crate::partialreader::*;
|
use crate::partialreader::*;
|
||||||
use crate::peekreader::*;
|
use crate::peekreader::*;
|
||||||
use crate::prn_char::format_ascii_dump;
|
use crate::prn_char::format_ascii_dump;
|
||||||
|
use clap::{self, AppSettings, Arg, ArgMatches};
|
||||||
|
|
||||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes
|
const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes
|
||||||
|
static ABOUT: &str = "dump files in octal and other formats";
|
||||||
|
|
||||||
static USAGE: &str = r#"Usage:
|
static USAGE: &str = r#"
|
||||||
od [OPTION]... [--] [FILENAME]...
|
od [OPTION]... [--] [FILENAME]...
|
||||||
od [-abcdDefFhHiIlLoOsxX] [FILENAME] [[+][0x]OFFSET[.][b]]
|
od [-abcdDefFhHiIlLoOsxX] [FILENAME] [[+][0x]OFFSET[.][b]]
|
||||||
od --traditional [OPTION]... [FILENAME] [[+][0x]OFFSET[.][b] [[+][0x]LABEL[.][b]]]
|
od --traditional [OPTION]... [FILENAME] [[+][0x]OFFSET[.][b] [[+][0x]LABEL[.][b]]]"#;
|
||||||
|
|
||||||
|
static LONG_HELP: &str = r#"
|
||||||
Displays data in various human-readable formats. If multiple formats are
|
Displays data in various human-readable formats. If multiple formats are
|
||||||
specified, the output will contain all formats in the order they appear on the
|
specified, the output will contain all formats in the order they appear on the
|
||||||
command line. Each format will be printed on a new line. Only the line
|
command line. Each format will be printed on a new line. Only the line
|
||||||
|
@ -88,85 +91,18 @@ Any type specification can have a "z" suffix, which will add a ASCII dump at
|
||||||
If an error occurred, a diagnostic message will be printed to stderr, and the
|
If an error occurred, a diagnostic message will be printed to stderr, and the
|
||||||
exitcode will be non-zero."#;
|
exitcode will be non-zero."#;
|
||||||
|
|
||||||
fn create_getopts_options() -> getopts::Options {
|
pub(crate) mod options {
|
||||||
let mut opts = getopts::Options::new();
|
pub const ADDRESS_RADIX: &str = "address-radix";
|
||||||
|
pub const SKIP_BYTES: &str = "skip-bytes";
|
||||||
opts.optopt(
|
pub const READ_BYTES: &str = "read-bytes";
|
||||||
"A",
|
pub const ENDIAN: &str = "endian";
|
||||||
"address-radix",
|
pub const STRINGS: &str = "strings";
|
||||||
"Select the base in which file offsets are printed.",
|
pub const FORMAT: &str = "format";
|
||||||
"RADIX",
|
pub const OUTPUT_DUPLICATES: &str = "output-duplicates";
|
||||||
);
|
pub const TRADITIONAL: &str = "traditional";
|
||||||
opts.optopt(
|
pub const WIDTH: &str = "width";
|
||||||
"j",
|
pub const VERSION: &str = "version";
|
||||||
"skip-bytes",
|
pub const FILENAME: &str = "FILENAME";
|
||||||
"Skip bytes input bytes before formatting and writing.",
|
|
||||||
"BYTES",
|
|
||||||
);
|
|
||||||
opts.optopt(
|
|
||||||
"N",
|
|
||||||
"read-bytes",
|
|
||||||
"limit dump to BYTES input bytes",
|
|
||||||
"BYTES",
|
|
||||||
);
|
|
||||||
opts.optopt(
|
|
||||||
"",
|
|
||||||
"endian",
|
|
||||||
"byte order to use for multi-byte formats",
|
|
||||||
"big|little",
|
|
||||||
);
|
|
||||||
opts.optopt(
|
|
||||||
"S",
|
|
||||||
"strings",
|
|
||||||
"output strings of at least BYTES graphic chars. 3 is assumed when \
|
|
||||||
BYTES is not specified.",
|
|
||||||
"BYTES",
|
|
||||||
);
|
|
||||||
opts.optflagmulti("a", "", "named characters, ignoring high-order bit");
|
|
||||||
opts.optflagmulti("b", "", "octal bytes");
|
|
||||||
opts.optflagmulti("c", "", "ASCII characters or backslash escapes");
|
|
||||||
opts.optflagmulti("d", "", "unsigned decimal 2-byte units");
|
|
||||||
opts.optflagmulti("D", "", "unsigned decimal 4-byte units");
|
|
||||||
opts.optflagmulti("o", "", "octal 2-byte units");
|
|
||||||
|
|
||||||
opts.optflagmulti("I", "", "decimal 8-byte units");
|
|
||||||
opts.optflagmulti("L", "", "decimal 8-byte units");
|
|
||||||
opts.optflagmulti("i", "", "decimal 4-byte units");
|
|
||||||
opts.optflagmulti("l", "", "decimal 8-byte units");
|
|
||||||
opts.optflagmulti("x", "", "hexadecimal 2-byte units");
|
|
||||||
opts.optflagmulti("h", "", "hexadecimal 2-byte units");
|
|
||||||
|
|
||||||
opts.optflagmulti("O", "", "octal 4-byte units");
|
|
||||||
opts.optflagmulti("s", "", "decimal 2-byte units");
|
|
||||||
opts.optflagmulti("X", "", "hexadecimal 4-byte units");
|
|
||||||
opts.optflagmulti("H", "", "hexadecimal 4-byte units");
|
|
||||||
|
|
||||||
opts.optflagmulti("e", "", "floating point double precision (64-bit) units");
|
|
||||||
opts.optflagmulti("f", "", "floating point single precision (32-bit) units");
|
|
||||||
opts.optflagmulti("F", "", "floating point double precision (64-bit) units");
|
|
||||||
|
|
||||||
opts.optmulti("t", "format", "select output format or formats", "TYPE");
|
|
||||||
opts.optflag(
|
|
||||||
"v",
|
|
||||||
"output-duplicates",
|
|
||||||
"do not use * to mark line suppression",
|
|
||||||
);
|
|
||||||
opts.optflagopt(
|
|
||||||
"w",
|
|
||||||
"width",
|
|
||||||
"output BYTES bytes per output line. 32 is implied when BYTES is not \
|
|
||||||
specified.",
|
|
||||||
"BYTES",
|
|
||||||
);
|
|
||||||
opts.optflag("", "help", "display this help and exit.");
|
|
||||||
opts.optflag("", "version", "output version information and exit.");
|
|
||||||
opts.optflag(
|
|
||||||
"",
|
|
||||||
"traditional",
|
|
||||||
"compatibility mode with one input, offset and label.",
|
|
||||||
);
|
|
||||||
|
|
||||||
opts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OdOptions {
|
struct OdOptions {
|
||||||
|
@ -182,8 +118,8 @@ struct OdOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OdOptions {
|
impl OdOptions {
|
||||||
fn new(matches: getopts::Matches, args: Vec<String>) -> Result<OdOptions, String> {
|
fn new<'a>(matches: ArgMatches<'a>, args: Vec<String>) -> Result<OdOptions, String> {
|
||||||
let byte_order = match matches.opt_str("endian").as_ref().map(String::as_ref) {
|
let byte_order = match matches.value_of(options::ENDIAN) {
|
||||||
None => ByteOrder::Native,
|
None => ByteOrder::Native,
|
||||||
Some("little") => ByteOrder::Little,
|
Some("little") => ByteOrder::Little,
|
||||||
Some("big") => ByteOrder::Big,
|
Some("big") => ByteOrder::Big,
|
||||||
|
@ -192,7 +128,7 @@ impl OdOptions {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut skip_bytes = match matches.opt_default("skip-bytes", "0") {
|
let mut skip_bytes = match matches.value_of(options::SKIP_BYTES) {
|
||||||
None => 0,
|
None => 0,
|
||||||
Some(s) => match parse_number_of_bytes(&s) {
|
Some(s) => match parse_number_of_bytes(&s) {
|
||||||
Ok(i) => i,
|
Ok(i) => i,
|
||||||
|
@ -223,8 +159,9 @@ impl OdOptions {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut line_bytes = match matches.opt_default("w", "32") {
|
let mut line_bytes = match matches.value_of(options::WIDTH) {
|
||||||
None => 16,
|
None => 16,
|
||||||
|
Some(_) if matches.occurrences_of(options::WIDTH) == 0 => 16,
|
||||||
Some(s) => s.parse::<usize>().unwrap_or(0),
|
Some(s) => s.parse::<usize>().unwrap_or(0),
|
||||||
};
|
};
|
||||||
let min_bytes = formats.iter().fold(1, |max, next| {
|
let min_bytes = formats.iter().fold(1, |max, next| {
|
||||||
|
@ -235,9 +172,9 @@ impl OdOptions {
|
||||||
line_bytes = min_bytes;
|
line_bytes = min_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_duplicates = matches.opt_present("v");
|
let output_duplicates = matches.is_present(options::OUTPUT_DUPLICATES);
|
||||||
|
|
||||||
let read_bytes = match matches.opt_str("read-bytes") {
|
let read_bytes = match matches.value_of(options::READ_BYTES) {
|
||||||
None => None,
|
None => None,
|
||||||
Some(s) => match parse_number_of_bytes(&s) {
|
Some(s) => match parse_number_of_bytes(&s) {
|
||||||
Ok(i) => Some(i),
|
Ok(i) => Some(i),
|
||||||
|
@ -247,10 +184,10 @@ impl OdOptions {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let radix = match matches.opt_str("A") {
|
let radix = match matches.value_of(options::ADDRESS_RADIX) {
|
||||||
None => Radix::Octal,
|
None => Radix::Octal,
|
||||||
Some(s) => {
|
Some(s) => {
|
||||||
let st = s.into_bytes();
|
let st = s.as_bytes();
|
||||||
if st.len() != 1 {
|
if st.len() != 1 {
|
||||||
return Err("Radix must be one of [d, o, n, x]".to_string());
|
return Err("Radix must be one of [d, o, n, x]".to_string());
|
||||||
} else {
|
} else {
|
||||||
|
@ -286,26 +223,244 @@ impl OdOptions {
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let args = args.collect_str();
|
let args = args.collect_str();
|
||||||
|
|
||||||
let opts = create_getopts_options();
|
let clap_opts = clap::App::new(executable!())
|
||||||
|
.version(VERSION)
|
||||||
|
.about(ABOUT)
|
||||||
|
.usage(USAGE)
|
||||||
|
.after_help(LONG_HELP)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::ADDRESS_RADIX)
|
||||||
|
.short("A")
|
||||||
|
.long(options::ADDRESS_RADIX)
|
||||||
|
.help("Select the base in which file offsets are printed.")
|
||||||
|
.value_name("RADIX"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::SKIP_BYTES)
|
||||||
|
.short("j")
|
||||||
|
.long(options::SKIP_BYTES)
|
||||||
|
.help("Skip bytes input bytes before formatting and writing.")
|
||||||
|
.value_name("BYTES"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::READ_BYTES)
|
||||||
|
.short("N")
|
||||||
|
.long(options::READ_BYTES)
|
||||||
|
.help("limit dump to BYTES input bytes")
|
||||||
|
.value_name("BYTES"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::ENDIAN)
|
||||||
|
.long(options::ENDIAN)
|
||||||
|
.help("byte order to use for multi-byte formats")
|
||||||
|
.possible_values(&["big", "little"])
|
||||||
|
.value_name("big|little"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::STRINGS)
|
||||||
|
.short("S")
|
||||||
|
.long(options::STRINGS)
|
||||||
|
.help(
|
||||||
|
"output strings of at least BYTES graphic chars. 3 is assumed when \
|
||||||
|
BYTES is not specified.",
|
||||||
|
)
|
||||||
|
.default_value("3")
|
||||||
|
.value_name("BYTES"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("a")
|
||||||
|
.short("a")
|
||||||
|
.help("named characters, ignoring high-order bit")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("b")
|
||||||
|
.short("b")
|
||||||
|
.help("octal bytes")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("c")
|
||||||
|
.short("c")
|
||||||
|
.help("ASCII characters or backslash escapes")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("d")
|
||||||
|
.short("d")
|
||||||
|
.help("unsigned decimal 2-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("D")
|
||||||
|
.short("D")
|
||||||
|
.help("unsigned decimal 4-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("o")
|
||||||
|
.short("o")
|
||||||
|
.help("octal 2-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("I")
|
||||||
|
.short("I")
|
||||||
|
.help("decimal 8-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("L")
|
||||||
|
.short("L")
|
||||||
|
.help("decimal 8-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("i")
|
||||||
|
.short("i")
|
||||||
|
.help("decimal 4-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("l")
|
||||||
|
.short("l")
|
||||||
|
.help("decimal 8-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("x")
|
||||||
|
.short("x")
|
||||||
|
.help("hexadecimal 2-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("h")
|
||||||
|
.short("h")
|
||||||
|
.help("hexadecimal 2-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("O")
|
||||||
|
.short("O")
|
||||||
|
.help("octal 4-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("s")
|
||||||
|
.short("s")
|
||||||
|
.help("decimal 2-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("X")
|
||||||
|
.short("X")
|
||||||
|
.help("hexadecimal 4-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("H")
|
||||||
|
.short("H")
|
||||||
|
.help("hexadecimal 4-byte units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("e")
|
||||||
|
.short("e")
|
||||||
|
.help("floating point double precision (64-bit) units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("f")
|
||||||
|
.short("f")
|
||||||
|
.help("floating point double precision (32-bit) units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("F")
|
||||||
|
.short("F")
|
||||||
|
.help("floating point double precision (64-bit) units")
|
||||||
|
.multiple(true)
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::FORMAT)
|
||||||
|
.short("t")
|
||||||
|
.long(options::FORMAT)
|
||||||
|
.help("select output format or formats")
|
||||||
|
.multiple(true)
|
||||||
|
.value_name("TYPE"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OUTPUT_DUPLICATES)
|
||||||
|
.short("v")
|
||||||
|
.long(options::OUTPUT_DUPLICATES)
|
||||||
|
.help("do not use * to mark line suppression")
|
||||||
|
.takes_value(false)
|
||||||
|
.possible_values(&["big", "little"]),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::WIDTH)
|
||||||
|
.short("w")
|
||||||
|
.long(options::WIDTH)
|
||||||
|
.help(
|
||||||
|
"output BYTES bytes per output line. 32 is implied when BYTES is not \
|
||||||
|
specified.",
|
||||||
|
)
|
||||||
|
.default_value("32")
|
||||||
|
.value_name("BYTES"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::VERSION)
|
||||||
|
.long(options::VERSION)
|
||||||
|
.help("output version information and exit.")
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::TRADITIONAL)
|
||||||
|
.long(options::TRADITIONAL)
|
||||||
|
.help("compatibility mode with one input, offset and label.")
|
||||||
|
.takes_value(false),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::FILENAME)
|
||||||
|
.hidden(true)
|
||||||
|
.multiple(true)
|
||||||
|
)
|
||||||
|
.settings(&[
|
||||||
|
AppSettings::TrailingVarArg,
|
||||||
|
AppSettings::DontDelimitTrailingValues,
|
||||||
|
AppSettings::DisableVersion,
|
||||||
|
AppSettings::DeriveDisplayOrder,
|
||||||
|
]);
|
||||||
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
let clap_matches = clap_opts
|
||||||
Ok(m) => m,
|
.clone() // Clone to reuse clap_otps to print help
|
||||||
Err(f) => {
|
.get_matches_from(args.clone());
|
||||||
show_usage_error!("{}", f);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if matches.opt_present("help") {
|
if clap_matches.is_present(options::VERSION) {
|
||||||
println!("{}", opts.usage(&USAGE));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if matches.opt_present("version") {
|
|
||||||
println!("{} {}", executable!(), VERSION);
|
println!("{} {}", executable!(), VERSION);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let od_options = match OdOptions::new(matches, args) {
|
let od_options = match OdOptions::new(clap_matches, args) {
|
||||||
Err(s) => {
|
Err(s) => {
|
||||||
show_usage_error!("{}", s);
|
show_usage_error!("{}", s);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
use getopts::Matches;
|
use super::options;
|
||||||
|
use clap::ArgMatches;
|
||||||
|
|
||||||
/// Abstraction for getopts
|
/// Abstraction for getopts
|
||||||
pub trait CommandLineOpts {
|
pub trait CommandLineOpts {
|
||||||
/// returns all command line parameters which do not belong to an option.
|
/// returns all command line parameters which do not belong to an option.
|
||||||
fn inputs(&self) -> Vec<String>;
|
fn inputs(&self) -> Vec<&str>;
|
||||||
/// tests if any of the specified options is present.
|
/// tests if any of the specified options is present.
|
||||||
fn opts_present(&self, _: &[&str]) -> bool;
|
fn opts_present(&self, _: &[&str]) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation for `getopts`
|
/// Implementation for `getopts`
|
||||||
impl CommandLineOpts for Matches {
|
impl<'a> CommandLineOpts for ArgMatches<'a> {
|
||||||
fn inputs(&self) -> Vec<String> {
|
fn inputs(&self) -> Vec<&str> {
|
||||||
self.free.clone()
|
self.values_of(options::FILENAME)
|
||||||
|
.map(|values| values.collect())
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opts_present(&self, opts: &[&str]) -> bool {
|
fn opts_present(&self, opts: &[&str]) -> bool {
|
||||||
self.opts_present(&opts.iter().map(|s| (*s).to_string()).collect::<Vec<_>>())
|
opts.iter().any(|opt| self.is_present(opt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +43,7 @@ pub enum CommandLineInputs {
|
||||||
/// '-' is used as filename if stdin is meant. This is also returned if
|
/// '-' is used as filename if stdin is meant. This is also returned if
|
||||||
/// there is no input, as stdin is the default input.
|
/// there is no input, as stdin is the default input.
|
||||||
pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs, String> {
|
pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs, String> {
|
||||||
let mut input_strings: Vec<String> = matches.inputs();
|
let mut input_strings = matches.inputs();
|
||||||
|
|
||||||
if matches.opts_present(&["traditional"]) {
|
if matches.opts_present(&["traditional"]) {
|
||||||
return parse_inputs_traditional(input_strings);
|
return parse_inputs_traditional(input_strings);
|
||||||
|
@ -59,7 +63,7 @@ pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs,
|
||||||
}
|
}
|
||||||
if input_strings.len() == 2 {
|
if input_strings.len() == 2 {
|
||||||
return Ok(CommandLineInputs::FileAndOffset((
|
return Ok(CommandLineInputs::FileAndOffset((
|
||||||
input_strings[0].clone(),
|
input_strings[0].clone().to_owned(),
|
||||||
n,
|
n,
|
||||||
None,
|
None,
|
||||||
)));
|
)));
|
||||||
|
@ -69,23 +73,27 @@ pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs,
|
||||||
}
|
}
|
||||||
|
|
||||||
if input_strings.is_empty() {
|
if input_strings.is_empty() {
|
||||||
input_strings.push("-".to_string());
|
input_strings.push("-");
|
||||||
}
|
}
|
||||||
Ok(CommandLineInputs::FileNames(input_strings))
|
Ok(CommandLineInputs::FileNames(
|
||||||
|
input_strings.iter().map(|s| s.to_string()).collect(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// interprets inputs when --traditional is on the command line
|
/// interprets inputs when --traditional is on the command line
|
||||||
///
|
///
|
||||||
/// normally returns CommandLineInputs::FileAndOffset, but if no offset is found,
|
/// normally returns CommandLineInputs::FileAndOffset, but if no offset is found,
|
||||||
/// it returns CommandLineInputs::FileNames (also to differentiate from the offset == 0)
|
/// it returns CommandLineInputs::FileNames (also to differentiate from the offset == 0)
|
||||||
pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLineInputs, String> {
|
pub fn parse_inputs_traditional(input_strings: Vec<&str>) -> Result<CommandLineInputs, String> {
|
||||||
match input_strings.len() {
|
match input_strings.len() {
|
||||||
0 => Ok(CommandLineInputs::FileNames(vec!["-".to_string()])),
|
0 => Ok(CommandLineInputs::FileNames(vec!["-".to_string()])),
|
||||||
1 => {
|
1 => {
|
||||||
let offset0 = parse_offset_operand(&input_strings[0]);
|
let offset0 = parse_offset_operand(&input_strings[0]);
|
||||||
Ok(match offset0 {
|
Ok(match offset0 {
|
||||||
Ok(n) => CommandLineInputs::FileAndOffset(("-".to_string(), n, None)),
|
Ok(n) => CommandLineInputs::FileAndOffset(("-".to_string(), n, None)),
|
||||||
_ => CommandLineInputs::FileNames(input_strings),
|
_ => CommandLineInputs::FileNames(
|
||||||
|
input_strings.iter().map(|s| s.to_string()).collect(),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
|
@ -98,7 +106,7 @@ pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLin
|
||||||
Some(m),
|
Some(m),
|
||||||
))),
|
))),
|
||||||
(_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
|
(_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
|
||||||
input_strings[0].clone(),
|
input_strings[0].clone().to_owned(),
|
||||||
m,
|
m,
|
||||||
None,
|
None,
|
||||||
))),
|
))),
|
||||||
|
@ -110,7 +118,7 @@ pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLin
|
||||||
let label = parse_offset_operand(&input_strings[2]);
|
let label = parse_offset_operand(&input_strings[2]);
|
||||||
match (offset, label) {
|
match (offset, label) {
|
||||||
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
|
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
|
||||||
input_strings[0].clone(),
|
input_strings[0].clone().to_owned(),
|
||||||
n,
|
n,
|
||||||
Some(m),
|
Some(m),
|
||||||
))),
|
))),
|
||||||
|
@ -178,8 +186,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CommandLineOpts for MockOptions<'a> {
|
impl<'a> CommandLineOpts for MockOptions<'a> {
|
||||||
fn inputs(&self) -> Vec<String> {
|
fn inputs(&self) -> Vec<&str> {
|
||||||
self.inputs.clone()
|
self.inputs.iter().map(|s| s.as_str()).collect()
|
||||||
}
|
}
|
||||||
fn opts_present(&self, opts: &[&str]) -> bool {
|
fn opts_present(&self, opts: &[&str]) -> bool {
|
||||||
for expected in opts.iter() {
|
for expected in opts.iter() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue