From ab2bdf9caa2a5a844873b963d270eaee14d4da28 Mon Sep 17 00:00:00 2001 From: arbelonson-source <269032023+arbelonson-source@users.noreply.github.com> Date: Mon, 31 Aug 2026 00:53:38 +0300 Subject: [PATCH] shuf: report every malformed -i range the way GNU does The range was parsed by a clap `value_parser`, so each way of getting it wrong surfaced clap's wording and its own reason: $ shuf -i 5-1 error: invalid value '5-1' for '--input-range ': start exceeds end GNU reports all of them identically, as `invalid input range: '5-1'`. A range beginning with a hyphen never even reached the check, since clap rejected `-1-5` as an unknown option first. Take the range verbatim, allow hyphen-leading values, and check it in `uumain` so the utility owns the message. Four tests asserted clap's wording and now assert GNU's; each was checked against GNU 9.11 first. --- src/uu/shuf/locales/en-US.ftl | 3 +-- src/uu/shuf/src/shuf.rs | 25 +++++++++++++++++-------- tests/by-util/test_shuf.rs | 31 ++++++++++++++++++++++--------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/uu/shuf/locales/en-US.ftl b/src/uu/shuf/locales/en-US.ftl index fc80355a30e..0a1de82e130 100644 --- a/src/uu/shuf/locales/en-US.ftl +++ b/src/uu/shuf/locales/en-US.ftl @@ -23,7 +23,6 @@ shuf-error-read-error = read error shuf-error-read-random-bytes = reading random bytes failed shuf-error-end-of-random-bytes = end of random source shuf-error-no-lines-to-repeat = no lines to repeat -shuf-error-start-exceeds-end = start exceeds end -shuf-error-missing-dash = missing '-' +shuf-error-invalid-input-range = invalid input range: { $range } shuf-error-write-failed = write failed shuf-error-memory-exhausted = memory exhausted diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index b523001ce51..688cb5d5940 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -78,8 +78,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .cloned() .collect(), ) - } else if let Some(range) = matches.get_one(options::INPUT_RANGE).cloned() { - Mode::InputRange(range) + } else if let Some(range) = matches.get_one::(options::INPUT_RANGE) { + Mode::InputRange(parse_range(range).map_err(|_| { + USimpleError::new( + 1, + translate!("shuf-error-invalid-input-range", "range" => range.quote()), + ) + })?) } else { let mut operands = matches .get_many::(options::FILE_OR_ARGS) @@ -197,7 +202,11 @@ pub fn uu_app() -> Command { .long(options::INPUT_RANGE) .value_name("LO-HI") .help(translate!("shuf-help-input-range")) - .value_parser(parse_range) + // GNU reports every malformed range the same way, so the range + // is taken verbatim here and checked in `uumain`. Parsing it as + // a clap value would wrap the reason in clap's own wording, and + // a range like `-1-5` would not even reach us. + .allow_hyphen_values(true) .conflicts_with(options::FILE_OR_ARGS), ) .arg( @@ -445,17 +454,17 @@ fn shuf_exec( Ok(()) } -fn parse_range(input_range: &str) -> Result, String> { +fn parse_range(input_range: &str) -> Result, ()> { if let Some((from, to)) = input_range.split_once('-') { - let begin = from.parse::().map_err(|e| e.to_string())?; - let end = to.parse::().map_err(|e| e.to_string())?; + let begin = from.parse::().map_err(|_| ())?; + let end = to.parse::().map_err(|_| ())?; if begin <= end || begin == end + 1 { Ok(begin..=end) } else { - Err(translate!("shuf-error-start-exceeds-end")) + Err(()) } } else { - Err(translate!("shuf-error-missing-dash")) + Err(()) } } diff --git a/tests/by-util/test_shuf.rs b/tests/by-util/test_shuf.rs index db48c2d2998..4fc5a151f1b 100644 --- a/tests/by-util/test_shuf.rs +++ b/tests/by-util/test_shuf.rs @@ -745,21 +745,23 @@ fn test_shuf_invalid_input_range_one() { new_ucmd!() .args(&["-i", "0"]) .fails() - .stderr_contains("invalid value '0' for '--input-range ': missing '-'"); + .stderr_contains("invalid input range: '0'"); } #[test] fn test_shuf_invalid_input_range_two() { - new_ucmd!().args(&["-i", "a-9"]).fails().stderr_contains( - "invalid value 'a-9' for '--input-range ': invalid digit found in string", - ); + new_ucmd!() + .args(&["-i", "a-9"]) + .fails() + .stderr_contains("invalid input range: 'a-9'"); } #[test] fn test_shuf_invalid_input_range_three() { - new_ucmd!().args(&["-i", "0-b"]).fails().stderr_contains( - "invalid value '0-b' for '--input-range ': invalid digit found in string", - ); + new_ucmd!() + .args(&["-i", "0-b"]) + .fails() + .stderr_contains("invalid input range: '0-b'"); } #[test] @@ -867,7 +869,7 @@ fn test_range_empty_minus_one() { .arg("-i5-3") .fails() .no_stdout() - .stderr_contains("invalid value '5-3' for '--input-range ': start exceeds end\n"); + .stderr_contains("invalid input range: '5-3'"); } #[test] @@ -897,7 +899,7 @@ fn test_range_repeat_empty_minus_one() { .arg("-ri5-3") .fails() .no_stdout() - .stderr_contains("invalid value '5-3' for '--input-range ': start exceeds end\n"); + .stderr_contains("invalid input range: '5-3'"); } // This test fails if we forget to flush the `BufWriter`. @@ -1163,3 +1165,14 @@ fn test_seed_long_range_no_repeat() { fn test_empty_range_no_repeat() { new_ucmd!().arg("-i4-3").succeeds().no_output(); } + +#[test] +fn test_shuf_invalid_input_range_leading_hyphen() { + // A range starting with a hyphen never reached the range check at all; + // clap rejected it as an unknown option first. GNU reports it like any + // other malformed range. + new_ucmd!() + .args(&["-i", "-1-5"]) + .fails() + .stderr_contains("invalid input range: '-1-5'"); +}