1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

head/tail/split: make error handling of NUM/SIZE arguments more

consistent

* add tests for each flag that takes NUM/SIZE arguments
* fix bug in tail where 'quiet' and 'verbose' flags did not override each other POSIX style
This commit is contained in:
Jan Scheer 2021-06-03 20:37:29 +02:00
parent 5898b99627
commit ad26b7a042
7 changed files with 93 additions and 56 deletions

View file

@ -76,7 +76,7 @@ fn app<'a>() -> App<'a, 'a> {
.arg(
Arg::with_name(options::QUIET_NAME)
.short("q")
.long("--quiet")
.long("quiet")
.visible_alias("silent")
.help("never print headers giving file names")
.overrides_with_all(&[options::VERBOSE_NAME, options::QUIET_NAME]),

View file

@ -108,10 +108,7 @@ pub fn parse_num(src: &str) -> Result<(usize, bool), ParseSizeError> {
return Err(ParseSizeError::ParseFailure(src.to_string()));
}
match parse_size(&size_string) {
Ok(n) => Ok((n, all_but_last)),
Err(e) => Err(e),
}
parse_size(&size_string).map(|n| (n, all_but_last))
}
#[cfg(test)]

View file

@ -19,7 +19,7 @@ use std::fs::File;
use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path;
use std::{char, fs::remove_file};
use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::parse_size::parse_size;
static NAME: &str = "split";
static VERSION: &str = env!("CARGO_PKG_VERSION");
@ -281,16 +281,7 @@ impl ByteSplitter {
let size_string = &settings.strategy_param;
let size_num = match parse_size(&size_string) {
Ok(n) => n,
Err(e) => match e {
ParseSizeError::ParseFailure(_) => {
crash!(1, "invalid number of bytes: {}", e.to_string())
}
ParseSizeError::SizeTooBig(_) => crash!(
1,
"invalid number of bytes: {}: Value too large for defined data type",
size_string
),
},
Err(e) => crash!(1, "invalid number of bytes: {}", e.to_string()),
};
ByteSplitter {

View file

@ -33,7 +33,6 @@ use uucore::ringbuffer::RingBuffer;
pub mod options {
pub mod verbosity {
pub static QUIET: &str = "quiet";
pub static SILENT: &str = "silent";
pub static VERBOSE: &str = "verbose";
}
pub static BYTES: &str = "bytes";
@ -77,6 +76,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let app = App::new(executable!())
.version(crate_version!())
.about("output the last part of files")
// TODO: add usage
.arg(
Arg::with_name(options::BYTES)
.short("c")
@ -111,13 +111,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Arg::with_name(options::verbosity::QUIET)
.short("q")
.long(options::verbosity::QUIET)
.visible_alias("silent")
.overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE])
.help("never output headers giving file names"),
)
.arg(
Arg::with_name(options::verbosity::SILENT)
.long(options::verbosity::SILENT)
.help("synonym of --quiet"),
)
.arg(
Arg::with_name(options::SLEEP_INT)
.short("s")
@ -129,6 +126,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Arg::with_name(options::verbosity::VERBOSE)
.short("v")
.long(options::verbosity::VERBOSE)
.overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE])
.help("always output headers giving file names"),
)
.arg(
@ -195,8 +193,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
let verbose = matches.is_present(options::verbosity::VERBOSE);
let quiet = matches.is_present(options::verbosity::QUIET)
|| matches.is_present(options::verbosity::SILENT);
let quiet = matches.is_present(options::verbosity::QUIET);
let files: Vec<String> = matches
.values_of(options::ARG_FILES)
@ -423,8 +420,5 @@ fn parse_num(src: &str) -> Result<(usize, bool), ParseSizeError> {
return Err(ParseSizeError::ParseFailure(src.to_string()));
}
match parse_size(&size_string) {
Ok(n) => Ok((n, starting_with)),
Err(e) => Err(e),
}
parse_size(&size_string).map(|n| (n, starting_with))
}

View file

@ -75,10 +75,9 @@ pub fn parse_size(size: &str) -> Result<usize, ParseSizeError> {
Ok(n) => n,
Err(_) => return Err(ParseSizeError::size_too_big(size)),
};
match number.checked_mul(factor) {
Some(n) => Ok(n),
None => Err(ParseSizeError::size_too_big(size)),
}
number
.checked_mul(factor)
.ok_or(ParseSizeError::size_too_big(size))
}
#[derive(Debug, PartialEq, Eq)]
@ -108,22 +107,58 @@ impl fmt::Display for ParseSizeError {
impl ParseSizeError {
fn parse_failure(s: &str) -> ParseSizeError {
// has to be handled in the respective uutils because strings differ, e.g.
// truncate: Invalid number: fb
// tail: invalid number of bytes: fb
// stderr on linux (GNU coreutils 8.32)
// has to be handled in the respective uutils because strings differ, e.g.:
//
// `NUM`
// head: invalid number of bytes: 1fb
// tail: invalid number of bytes: 1fb
//
// `SIZE`
// split: invalid number of bytes: 1fb
// truncate: Invalid number: 1fb
//
// `MODE`
// stdbuf: invalid mode 1fb
//
// `SIZE`
// sort: invalid suffix in --buffer-size argument '1fb'
// sort: invalid --buffer-size argument 'fb'
//
// `SIZE`
// du: invalid suffix in --buffer-size argument '1fb'
// du: invalid suffix in --threshold argument '1fb'
// du: invalid --buffer-size argument 'fb'
// du: invalid --threshold argument 'fb'
//
// `BYTES`
// od: invalid suffix in --read-bytes argument '1fb'
// od: invalid --read-bytes argument argument 'fb'
// --skip-bytes
// --width
// --strings
// etc.
ParseSizeError::ParseFailure(format!("{}", s))
}
fn size_too_big(s: &str) -> ParseSizeError {
// has to be handled in the respective uutils because strings differ, e.g.
// truncate: Invalid number: 1Y: Value too large to be stored in data type
// tail: invalid number of bytes: 1Y: Value too large to be stored in data type
// stderr on linux (GNU coreutils 8.32)
// has to be handled in the respective uutils because strings differ, e.g.:
//
// head: invalid number of bytes: 1Y: Value too large for defined data type
// tail: invalid number of bytes: 1Y: Value too large for defined data type
// split: invalid number of bytes: 1Y: Value too large for defined data type
// truncate: Invalid number: 1Y: Value too large for defined data type
// stdbuf: invalid mode 1Y: Value too large for defined data type
// sort: -S argument '1Y' too large
// du: -B argument '1Y' too large
// od: -N argument '1Y' too large
// etc.
ParseSizeError::SizeTooBig(format!(
"{}: Value too large to be stored in data type",
s
))
//
// stderr on macos (brew - GNU coreutils 8.32) also differs for the same version, e.g.:
// ghead: invalid number of bytes: 1Y: Value too large to be stored in data type
// gtail: invalid number of bytes: 1Y: Value too large to be stored in data type
ParseSizeError::SizeTooBig(format!("{}: Value too large for defined data type", s))
}
}

View file

@ -244,6 +244,7 @@ hello
",
);
}
#[test]
fn test_head_invalid_num() {
new_ucmd!()
@ -258,16 +259,26 @@ fn test_head_invalid_num() {
new_ucmd!()
.args(&["-c", "1Y", "emptyfile.txt"])
.fails()
.stderr_is(
"head: invalid number of bytes: 1Y: Value too large to be stored in data type",
);
.stderr_is("head: invalid number of bytes: 1Y: Value too large for defined data type");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["-n", "1Y", "emptyfile.txt"])
.fails()
.stderr_is(
"head: invalid number of lines: 1Y: Value too large to be stored in data type",
);
.stderr_is("head: invalid number of lines: 1Y: Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["-c", size])
.fails()
.code_is(1)
.stderr_only(format!(
"head: invalid number of bytes: {}: Value too large for defined data type",
size
));
}
}
}
#[test]

View file

@ -284,12 +284,11 @@ fn test_multiple_input_files_with_suppressed_headers() {
#[test]
fn test_multiple_input_quiet_flag_overrides_verbose_flag_for_suppressing_headers() {
// TODO: actually the later one should win, i.e. -qv should lead to headers being printed, -vq to them being suppressed
new_ucmd!()
.arg(FOOBAR_TXT)
.arg(FOOBAR_2_TXT)
.arg("-q")
.arg("-v")
.arg("-q")
.run()
.stdout_is_fixture("foobar_multiple_quiet.expected");
}
@ -367,16 +366,26 @@ fn test_tail_invalid_num() {
new_ucmd!()
.args(&["-c", "1Y", "emptyfile.txt"])
.fails()
.stderr_is(
"tail: invalid number of bytes: 1Y: Value too large to be stored in data type",
);
.stderr_is("tail: invalid number of bytes: 1Y: Value too large for defined data type");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["-n", "1Y", "emptyfile.txt"])
.fails()
.stderr_is(
"tail: invalid number of lines: 1Y: Value too large to be stored in data type",
);
.stderr_is("tail: invalid number of lines: 1Y: Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["-c", size])
.fails()
.code_is(1)
.stderr_only(format!(
"tail: invalid number of bytes: {}: Value too large for defined data type",
size
));
}
}
}
#[test]