From 804240164b0fbae78f6b78c347c4d2c391674628 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 5 Jun 2022 15:16:48 +0200 Subject: [PATCH] expand: allow specifier only with last value --- src/uu/expand/src/expand.rs | 21 +++++++++++++++++++++ tests/by-util/test_expand.rs | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/uu/expand/src/expand.rs b/src/uu/expand/src/expand.rs index d81d2d313..eb65d63ab 100644 --- a/src/uu/expand/src/expand.rs +++ b/src/uu/expand/src/expand.rs @@ -41,6 +41,7 @@ static DEFAULT_TABSTOP: usize = 8; /// The mode to use when replacing tabs beyond the last one specified in /// the `--tabs` argument. +#[derive(PartialEq)] enum RemainingMode { None, Slash, @@ -65,6 +66,7 @@ fn is_space_or_comma(c: char) -> bool { enum ParseError { InvalidCharacter(String), SpecifierNotAtStartOfNumber(String, String), + SpecifierOnlyAllowedWithLastValue(String), TabSizeCannotBeZero, TabSizeTooLarge(String), TabSizesMustBeAscending, @@ -85,6 +87,11 @@ impl fmt::Display for ParseError { specifier.quote(), s.quote(), ), + Self::SpecifierOnlyAllowedWithLastValue(specifier) => write!( + f, + "{} specifier only allowed with the last value", + specifier.quote() + ), Self::TabSizeCannotBeZero => write!(f, "tab size cannot be 0"), Self::TabSizeTooLarge(s) => write!(f, "tab stop is too large {}", s.quote()), Self::TabSizesMustBeAscending => write!(f, "tab sizes must be ascending"), @@ -112,6 +119,7 @@ fn tabstops_parse(s: &str) -> Result<(RemainingMode, Vec), ParseError> { let mut nums = vec![]; let mut remaining_mode = RemainingMode::None; + let mut is_specifier_already_used = false; for word in s.split(is_space_or_comma) { let bytes = word.as_bytes(); for i in 0..bytes.len() { @@ -139,6 +147,19 @@ fn tabstops_parse(s: &str) -> Result<(RemainingMode, Vec), ParseError> { } } + if is_specifier_already_used { + let specifier = if remaining_mode == RemainingMode::Slash { + "/".to_string() + } else { + "+".to_string() + }; + return Err(ParseError::SpecifierOnlyAllowedWithLastValue( + specifier, + )); + } else if remaining_mode != RemainingMode::None { + is_specifier_already_used = true; + } + // Append this tab stop to the list of all tabstops. nums.push(num); break; diff --git a/tests/by-util/test_expand.rs b/tests/by-util/test_expand.rs index 289743ce8..e981dab2a 100644 --- a/tests/by-util/test_expand.rs +++ b/tests/by-util/test_expand.rs @@ -226,6 +226,24 @@ fn test_tabs_with_specifier_not_at_start() { run_cmd("--tabs=1+2", "+", "+2"); } +#[test] +fn test_tabs_with_specifier_only_allowed_with_last_value() { + fn run_cmd(arg: &str, specifier: &str) { + let expected_msg = format!( + "{} specifier only allowed with the last value", + specifier.quote() + ); + new_ucmd!().arg(arg).fails().stderr_contains(expected_msg); + } + run_cmd("--tabs=/1,2,3", "/"); + run_cmd("--tabs=1,/2,3", "/"); + new_ucmd!().arg("--tabs=1,2,/3").succeeds(); + + run_cmd("--tabs=+1,2,3", "+"); + run_cmd("--tabs=1,+2,3", "+"); + new_ucmd!().arg("--tabs=1,2,+3").succeeds(); +} + #[test] fn test_tabs_with_invalid_chars() { new_ucmd!()