diff --git a/src/uu/shuf/locales/en-US.ftl b/src/uu/shuf/locales/en-US.ftl index fc80355a30e..c10bcac03f0 100644 --- a/src/uu/shuf/locales/en-US.ftl +++ b/src/uu/shuf/locales/en-US.ftl @@ -23,7 +23,7 @@ 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-invalid-input-range-too-large = invalid input range: { $range }: Value too large to be stored in data type shuf-error-write-failed = write failed shuf-error-memory-exhausted = memory exhausted diff --git a/src/uu/shuf/locales/fr-FR.ftl b/src/uu/shuf/locales/fr-FR.ftl index b800aba02e8..a0a2082fab8 100644 --- a/src/uu/shuf/locales/fr-FR.ftl +++ b/src/uu/shuf/locales/fr-FR.ftl @@ -20,7 +20,7 @@ shuf-error-failed-to-open-for-writing = échec de l'ouverture de { $file } en é shuf-error-failed-to-open-random-source = échec de l'ouverture de la source aléatoire { $file } shuf-error-read-error = erreur de lecture shuf-error-no-lines-to-repeat = aucune ligne à répéter -shuf-error-start-exceeds-end = le début dépasse la fin -shuf-error-missing-dash = '-' manquant +shuf-error-invalid-input-range = plage d'entrée invalide : { $range } +shuf-error-invalid-input-range-too-large = plage d'entrée invalide : { $range } : valeur trop grande pour être stockée dans le type de données shuf-error-write-failed = échec de l'écriture shuf-error-memory-exhausted = mémoire épuisée diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index b523001ce51..fc1e03449e6 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -8,6 +8,7 @@ use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::{self, BufReader, BufWriter, Read, Write, stdin, stdout}; +use std::num::IntErrorKind; use std::ops::RangeInclusive; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -78,8 +79,8 @@ 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)?) } else { let mut operands = matches .get_many::(options::FILE_OR_ARGS) @@ -197,7 +198,6 @@ pub fn uu_app() -> Command { .long(options::INPUT_RANGE) .value_name("LO-HI") .help(translate!("shuf-help-input-range")) - .value_parser(parse_range) .conflicts_with(options::FILE_OR_ARGS), ) .arg( @@ -445,17 +445,39 @@ fn shuf_exec( Ok(()) } -fn parse_range(input_range: &str) -> Result, String> { - 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())?; - if begin <= end || begin == end + 1 { - Ok(begin..=end) - } else { - Err(translate!("shuf-error-start-exceeds-end")) - } +/// Parse the `LO-HI` of `-i`. +/// +/// Whatever is wrong with it, GNU reports the range as a whole rather than +/// the part that failed, and adds a detail only when a bound does not fit in +/// a `u64`. +fn parse_range(input_range: &str) -> UResult> { + let invalid = || { + USimpleError::new( + 1, + translate!("shuf-error-invalid-input-range", "range" => input_range.quote()), + ) + }; + let too_large = || { + USimpleError::new( + 1, + translate!("shuf-error-invalid-input-range-too-large", "range" => input_range.quote()), + ) + }; + + let Some((from, to)) = input_range.split_once('-') else { + return Err(invalid()); + }; + let parse = |bound: &str| match bound.parse::() { + Ok(n) => Ok(n), + Err(e) if *e.kind() == IntErrorKind::PosOverflow => Err(too_large()), + Err(_) => Err(invalid()), + }; + let begin = parse(from)?; + let end = parse(to)?; + if begin <= end || begin == end + 1 { + Ok(begin..=end) } else { - Err(translate!("shuf-error-missing-dash")) + Err(invalid()) } } diff --git a/tests/by-util/test_shuf.rs b/tests/by-util/test_shuf.rs index db48c2d2998..9cbf90d61ff 100644 --- a/tests/by-util/test_shuf.rs +++ b/tests/by-util/test_shuf.rs @@ -745,21 +745,45 @@ fn test_shuf_invalid_input_range_one() { new_ucmd!() .args(&["-i", "0"]) .fails() - .stderr_contains("invalid value '0' for '--input-range ': missing '-'"); + .stderr_contains("shuf: invalid input range: '0'\n"); } #[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("shuf: invalid input range: 'a-9'\n"); } #[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("shuf: invalid input range: '0-b'\n"); +} + +/// Whatever is wrong with the LO-HI of -i, GNU names the range as a whole +/// and exits 1, and adds a detail only when a bound overflows. +#[test] +fn test_shuf_invalid_input_range_message() { + for range in ["5-1", "abc", "1-", "1-2-3", ""] { + new_ucmd!() + .args(&["-i", range]) + .fails_with_code(1) + .no_stdout() + .stderr_only(format!("shuf: invalid input range: '{range}'\n")); + } + + new_ucmd!() + .args(&["-i", "99999999999999999999999-1"]) + .fails_with_code(1) + .no_stdout() + .stderr_only( + "shuf: invalid input range: '99999999999999999999999-1': \ + Value too large to be stored in data type\n", + ); } #[test] @@ -867,7 +891,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("shuf: invalid input range: '5-3'\n"); } #[test] @@ -897,7 +921,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("shuf: invalid input range: '5-3'\n"); } // This test fails if we forget to flush the `BufWriter`.