mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27: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:
parent
5898b99627
commit
ad26b7a042
7 changed files with 93 additions and 56 deletions
|
@ -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]),
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue