1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

unexpand: implement "tabs" shortcuts

This commit is contained in:
Daniel Hofstetter 2022-06-17 12:59:02 +02:00
parent fa51f8b986
commit edf4fee48f
2 changed files with 88 additions and 4 deletions

View file

@ -7,7 +7,7 @@
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
// spell-checker:ignore (ToDO) nums aflag uflag scol prevtab amode ctype cwidth nbytes lastcol pctype // spell-checker:ignore (ToDO) nums aflag uflag scol prevtab amode ctype cwidth nbytes lastcol pctype Preprocess
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
@ -97,9 +97,9 @@ struct Options {
impl Options { impl Options {
fn new(matches: &clap::ArgMatches) -> Result<Self, ParseError> { fn new(matches: &clap::ArgMatches) -> Result<Self, ParseError> {
let tabstops = match matches.value_of(options::TABS) { let tabstops = match matches.values_of(options::TABS) {
None => vec![DEFAULT_TABSTOP], None => vec![DEFAULT_TABSTOP],
Some(s) => tabstops_parse(s)?, Some(s) => tabstops_parse(&s.collect::<Vec<&str>>().join(","))?,
}; };
let aflag = (matches.is_present(options::ALL) || matches.is_present(options::TABS)) let aflag = (matches.is_present(options::ALL) || matches.is_present(options::TABS))
@ -120,13 +120,49 @@ impl Options {
} }
} }
/// Decide whether the character is either a digit or a comma.
fn is_digit_or_comma(c: char) -> bool {
c.is_ascii_digit() || c == ','
}
/// Preprocess command line arguments and expand shortcuts. For example, "-7" is expanded to
/// "--tabs=7 --first-only" and "-1,3" to "--tabs=1 --tabs=3 --first-only". However, if "-a" or
/// "--all" is provided, "--first-only" is omitted.
fn expand_shortcuts(args: &[String]) -> Vec<String> {
let mut processed_args = Vec::with_capacity(args.len());
let mut is_all_arg_provided = false;
let mut has_shortcuts = false;
for arg in args {
if arg.starts_with('-') && arg[1..].chars().all(is_digit_or_comma) {
arg[1..]
.split(',')
.filter(|s| !s.is_empty())
.for_each(|s| processed_args.push(format!("--tabs={}", s)));
has_shortcuts = true;
} else {
processed_args.push(arg.to_string());
if arg == "--all" || arg == "-a" {
is_all_arg_provided = true;
}
}
}
if has_shortcuts && !is_all_arg_provided {
processed_args.push("--first-only".into());
}
processed_args
}
#[uucore::main] #[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args let args = args
.collect_str(InvalidEncodingHandling::Ignore) .collect_str(InvalidEncodingHandling::Ignore)
.accept_any(); .accept_any();
let matches = uu_app().get_matches_from(args); let matches = uu_app().get_matches_from(expand_shortcuts(&args));
unexpand(&Options::new(&matches)?).map_err_context(String::new) unexpand(&Options::new(&matches)?).map_err_context(String::new)
} }
@ -163,6 +199,7 @@ pub fn uu_app<'a>() -> Command<'a> {
.long(options::TABS) .long(options::TABS)
.help("use comma separated LIST of tab positions or have tabs N characters apart instead of 8 (enables -a)") .help("use comma separated LIST of tab positions or have tabs N characters apart instead of 8 (enables -a)")
.takes_value(true) .takes_value(true)
.multiple_occurrences(true)
.value_name("N, LIST") .value_name("N, LIST")
) )
.arg( .arg(
@ -379,3 +416,15 @@ fn unexpand(options: &Options) -> std::io::Result<()> {
} }
output.flush() output.flush()
} }
#[cfg(test)]
mod tests {
use crate::is_digit_or_comma;
#[test]
fn test_is_digit_or_comma() {
assert!(is_digit_or_comma('1'));
assert!(is_digit_or_comma(','));
assert!(!is_digit_or_comma('a'));
}
}

View file

@ -156,6 +156,41 @@ fn unexpand_read_from_two_file() {
.success(); .success();
} }
#[test]
fn test_tabs_shortcut() {
new_ucmd!()
.arg("-3")
.pipe_in(" a b")
.run()
.stdout_is("\ta b");
}
#[test]
fn test_tabs_shortcut_combined_with_all_arg() {
fn run_cmd(all_arg: &str) {
new_ucmd!()
.args(&[all_arg, "-3"])
.pipe_in("a b c")
.run()
.stdout_is("a\tb\tc");
}
let all_args = vec!["-a", "--all"];
for arg in all_args {
run_cmd(arg);
}
}
#[test]
fn test_comma_separated_tabs_shortcut() {
new_ucmd!()
.args(&["-a", "-3,9"])
.pipe_in("a b c")
.run()
.stdout_is("a\tb\tc");
}
#[test] #[test]
fn test_tabs_cannot_be_zero() { fn test_tabs_cannot_be_zero() {
new_ucmd!() new_ucmd!()