From e3cac647f65b0efac62a6f37a2924aed888e0d78 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Fri, 10 Jun 2022 08:52:59 +0200 Subject: [PATCH] expand: add support for "--tabs" shortcuts Fixes #3575 --- src/uu/expand/src/expand.rs | 47 +++++++++++++++++++++++++++++++++--- tests/by-util/test_expand.rs | 30 +++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/uu/expand/src/expand.rs b/src/uu/expand/src/expand.rs index eb65d63abec..7a539f5a938 100644 --- a/src/uu/expand/src/expand.rs +++ b/src/uu/expand/src/expand.rs @@ -7,13 +7,14 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) ctype cwidth iflag nbytes nspaces nums tspaces uflag +// spell-checker:ignore (ToDO) ctype cwidth iflag nbytes nspaces nums tspaces uflag Preprocess #[macro_use] extern crate uucore; use clap::{crate_version, Arg, ArgMatches, Command}; use std::error::Error; +use std::ffi::OsString; use std::fmt; use std::fs::File; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write}; @@ -21,7 +22,7 @@ use std::num::IntErrorKind; use std::str::from_utf8; use unicode_width::UnicodeWidthChar; use uucore::display::Quotable; -use uucore::error::{FromIo, UError, UResult}; +use uucore::error::{FromIo, UError, UResult, UUsageError}; use uucore::format_usage; static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output. @@ -61,6 +62,11 @@ fn is_space_or_comma(c: char) -> bool { c == ' ' || c == ',' } +/// Decide whether the character is either a digit or a comma. +fn is_digit_or_comma(c: char) -> bool { + c.is_ascii_digit() || c == ',' +} + /// Errors that can occur when parsing a `--tabs` argument. #[derive(Debug)] enum ParseError { @@ -243,9 +249,35 @@ impl Options { } } +/// Preprocess command line arguments and expand shortcuts. For example, "-7" is expanded to +/// "--tabs=7" and "-1,3" to "--tabs=1 --tabs=3". +fn expand_shortcuts<'a>( + mut args: impl uucore::Args + 'a, +) -> Result + 'a>, Box<(dyn UError + 'static)>> { + // argv[0] is always present + let mut processed_args = vec![args.next().unwrap()]; + + for arg in args { + if let Some(s) = arg.to_str() { + if s.starts_with('-') && s[1..].chars().all(is_digit_or_comma) { + s[1..] + .split(',') + .filter(|s| !s.is_empty()) + .for_each(|s| processed_args.push(format!("--tabs={}", s).into())); + } else { + processed_args.push(arg); + } + } else { + return Err(UUsageError::new(1, "bad argument encoding".to_owned())); + } + } + + Ok(Box::new(processed_args.into_iter())) +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let matches = uu_app().get_matches_from(args); + let matches = uu_app().get_matches_from(expand_shortcuts(args)?); expand(&Options::new(&matches)?).map_err_context(|| "failed to write output".to_string()) } @@ -445,6 +477,8 @@ fn expand(options: &Options) -> std::io::Result<()> { #[cfg(test)] mod tests { + use crate::is_digit_or_comma; + use super::next_tabstop; use super::RemainingMode; @@ -468,4 +502,11 @@ mod tests { assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::Slash), 2); assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::Slash), 4); } + + #[test] + fn test_is_digit_or_comma() { + assert!(is_digit_or_comma('1')); + assert!(is_digit_or_comma(',')); + assert!(!is_digit_or_comma('a')); + } } diff --git a/tests/by-util/test_expand.rs b/tests/by-util/test_expand.rs index e981dab2a09..555671707f0 100644 --- a/tests/by-util/test_expand.rs +++ b/tests/by-util/test_expand.rs @@ -263,3 +263,33 @@ fn test_tabs_with_too_large_size() { new_ucmd!().arg(arg).fails().stderr_contains(expected_error); } + +#[test] +fn test_tabs_shortcut() { + new_ucmd!() + .args(&["-2", "-5", "-7"]) + .pipe_in("\ta\tb\tc") + .succeeds() + // 01234567890 + .stdout_is(" a b c"); +} + +#[test] +fn test_comma_separated_tabs_shortcut() { + new_ucmd!() + .args(&["-2,5", "-7"]) + .pipe_in("\ta\tb\tc") + .succeeds() + // 01234567890 + .stdout_is(" a b c"); +} + +#[test] +fn test_tabs_and_tabs_shortcut_mixed() { + new_ucmd!() + .args(&["-2", "--tabs=5", "-7"]) + .pipe_in("\ta\tb\tc") + .succeeds() + // 01234567890 + .stdout_is(" a b c"); +}