mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
tail: fix big number handling and error message quoting (#10155)
* tail: fix big number handling and error message quoting * fix clippy: remove needless borrows in test_tail
This commit is contained in:
@@ -13,7 +13,7 @@ use std::ffi::OsString;
|
||||
use std::io::IsTerminal;
|
||||
use std::time::Duration;
|
||||
use uucore::error::{UResult, USimpleError, UUsageError};
|
||||
use uucore::parser::parse_signed_num::{SignPrefix, parse_signed_num};
|
||||
use uucore::parser::parse_signed_num::{SignPrefix, parse_signed_num_max};
|
||||
use uucore::parser::parse_size::ParseSizeError;
|
||||
use uucore::parser::parse_time;
|
||||
use uucore::parser::shortcut_value_parser::ShortcutValueParser;
|
||||
@@ -78,7 +78,7 @@ impl FilterMode {
|
||||
Err(e) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
translate!("tail-error-invalid-number-of-bytes", "arg" => format!("'{e}'")),
|
||||
translate!("tail-error-invalid-number-of-bytes", "arg" => e.to_string()),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -366,12 +366,6 @@ pub fn parse_obsolete(arg: &OsString, input: Option<&OsString>) -> UResult<Optio
|
||||
Err(USimpleError::new(
|
||||
1,
|
||||
match e {
|
||||
parse::ParseError::OutOfRange => {
|
||||
translate!("tail-error-invalid-number-out-of-range", "arg" => arg.quote())
|
||||
}
|
||||
parse::ParseError::Overflow => {
|
||||
translate!("tail-error-invalid-number-overflow", "arg" => arg.quote())
|
||||
}
|
||||
// this ensures compatibility to GNU's error message (as tested in misc/tail)
|
||||
parse::ParseError::Context => {
|
||||
translate!(
|
||||
@@ -389,7 +383,7 @@ pub fn parse_obsolete(arg: &OsString, input: Option<&OsString>) -> UResult<Optio
|
||||
}
|
||||
|
||||
fn parse_num(src: &str) -> Result<Signum, ParseSizeError> {
|
||||
let result = parse_signed_num(src)?;
|
||||
let result = parse_signed_num_max(src)?;
|
||||
// tail: '+' means "starting from line/byte N", default/'-' means "last N"
|
||||
let is_plus = result.sign == Some(SignPrefix::Plus);
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ impl Default for ObsoleteArgs {
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum ParseError {
|
||||
OutOfRange,
|
||||
Overflow,
|
||||
Context,
|
||||
InvalidEncoding,
|
||||
}
|
||||
@@ -52,11 +50,7 @@ pub fn parse_obsolete(src: &OsString) -> Option<Result<ObsoleteArgs, ParseError>
|
||||
.unwrap_or(rest.len());
|
||||
let has_num = !rest[..end_num].is_empty();
|
||||
let num: u64 = if has_num {
|
||||
if let Ok(num) = rest[..end_num].parse() {
|
||||
num
|
||||
} else {
|
||||
return Some(Err(ParseError::OutOfRange));
|
||||
}
|
||||
rest[..end_num].parse().unwrap_or(u64::MAX)
|
||||
} else {
|
||||
10
|
||||
};
|
||||
@@ -85,9 +79,7 @@ pub fn parse_obsolete(src: &OsString) -> Option<Result<ObsoleteArgs, ParseError>
|
||||
}
|
||||
|
||||
let multiplier = if mode == 'b' { 512 } else { 1 };
|
||||
let Some(num) = num.checked_mul(multiplier) else {
|
||||
return Some(Err(ParseError::Overflow));
|
||||
};
|
||||
let num = num.saturating_mul(multiplier);
|
||||
|
||||
Some(Ok(ObsoleteArgs {
|
||||
num,
|
||||
|
||||
+59
-20
@@ -1155,16 +1155,17 @@ fn test_invalid_num() {
|
||||
.fails()
|
||||
.stderr_str()
|
||||
.starts_with("tail: invalid number of lines: '1024R'");
|
||||
// 1Y overflows to u64::MAX (like GNU tail 9.9.x), so it succeeds
|
||||
new_ucmd!()
|
||||
.args(&["-c", "1Y", "emptyfile.txt"])
|
||||
.fails()
|
||||
.stderr_str()
|
||||
.starts_with("tail: invalid number of bytes: '1Y': Value too large for defined data type");
|
||||
.args(&["-c", "1Y"])
|
||||
.pipe_in("x")
|
||||
.succeeds()
|
||||
.stdout_is("x");
|
||||
new_ucmd!()
|
||||
.args(&["-n", "1Y", "emptyfile.txt"])
|
||||
.fails()
|
||||
.stderr_str()
|
||||
.starts_with("tail: invalid number of lines: '1Y': Value too large for defined data type");
|
||||
.args(&["-n", "1Y"])
|
||||
.pipe_in("x\n")
|
||||
.succeeds()
|
||||
.stdout_is("x\n");
|
||||
new_ucmd!()
|
||||
.args(&["-c", "-³"])
|
||||
.fails()
|
||||
@@ -1172,6 +1173,45 @@ fn test_invalid_num() {
|
||||
.starts_with("tail: invalid number of bytes: '³'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_oversized_num() {
|
||||
const BIG: &str = "99999999999999999999999999999";
|
||||
const DATA: &str = "abcd";
|
||||
// -c <big> and -n <big>: output all (request more than available)
|
||||
new_ucmd!()
|
||||
.args(&["-c", BIG])
|
||||
.pipe_in(DATA)
|
||||
.succeeds()
|
||||
.stdout_is(DATA);
|
||||
new_ucmd!()
|
||||
.args(&["-n", BIG])
|
||||
.pipe_in("a\nb\n")
|
||||
.succeeds()
|
||||
.stdout_is("a\nb\n");
|
||||
// +<big>: skip beyond input (empty output)
|
||||
new_ucmd!()
|
||||
.args(&["-c", &format!("+{BIG}")])
|
||||
.pipe_in(DATA)
|
||||
.succeeds()
|
||||
.no_stdout();
|
||||
new_ucmd!()
|
||||
.args(&["-n", &format!("+{BIG}")])
|
||||
.pipe_in("a\nb\n")
|
||||
.succeeds()
|
||||
.no_stdout();
|
||||
// Obsolete syntax
|
||||
new_ucmd!()
|
||||
.arg(format!("+{BIG}c"))
|
||||
.pipe_in(DATA)
|
||||
.succeeds()
|
||||
.no_stdout();
|
||||
new_ucmd!()
|
||||
.arg(format!("-{BIG}c"))
|
||||
.pipe_in(DATA)
|
||||
.succeeds()
|
||||
.stdout_is(DATA);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_with_undocumented_sign_bytes() {
|
||||
// tail: '-' is not documented (8.32 man pages)
|
||||
@@ -4767,13 +4807,13 @@ fn test_gnu_args_err() {
|
||||
.fails_with_code(1)
|
||||
.no_stdout()
|
||||
.stderr_is("tail: option used in invalid context -- 2\n");
|
||||
// err-5
|
||||
// err-5: large numbers now clamp to u64::MAX
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-c99999999999999999999")
|
||||
.fails_with_code(1)
|
||||
.no_stdout()
|
||||
.stderr_is("tail: invalid number of bytes: '99999999999999999999'\n");
|
||||
.pipe_in("x")
|
||||
.succeeds()
|
||||
.stdout_is("x");
|
||||
// err-6
|
||||
scene
|
||||
.ucmd()
|
||||
@@ -4787,20 +4827,19 @@ fn test_gnu_args_err() {
|
||||
.fails_with_code(1)
|
||||
.no_stdout()
|
||||
.stderr_is("tail: option used in invalid context -- 5\n");
|
||||
// Large obsolete-syntax numbers clamp to u64::MAX
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-9999999999999999999b")
|
||||
.fails_with_code(1)
|
||||
.no_stdout()
|
||||
.stderr_is("tail: invalid number: '-9999999999999999999b'\n");
|
||||
.pipe_in("x")
|
||||
.succeeds()
|
||||
.stdout_is("x");
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-999999999999999999999b")
|
||||
.fails_with_code(1)
|
||||
.no_stdout()
|
||||
.stderr_is(
|
||||
"tail: invalid number: '-999999999999999999999b': Numerical result out of range\n",
|
||||
);
|
||||
.pipe_in("x")
|
||||
.succeeds()
|
||||
.stdout_is("x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user