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:
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(
|
||||||
Arg::with_name(options::QUIET_NAME)
|
Arg::with_name(options::QUIET_NAME)
|
||||||
.short("q")
|
.short("q")
|
||||||
.long("--quiet")
|
.long("quiet")
|
||||||
.visible_alias("silent")
|
.visible_alias("silent")
|
||||||
.help("never print headers giving file names")
|
.help("never print headers giving file names")
|
||||||
.overrides_with_all(&[options::VERBOSE_NAME, options::QUIET_NAME]),
|
.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()));
|
return Err(ParseSizeError::ParseFailure(src.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match parse_size(&size_string) {
|
parse_size(&size_string).map(|n| (n, all_but_last))
|
||||||
Ok(n) => Ok((n, all_but_last)),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::fs::File;
|
||||||
use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
|
use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{char, fs::remove_file};
|
use std::{char, fs::remove_file};
|
||||||
use uucore::parse_size::{parse_size, ParseSizeError};
|
use uucore::parse_size::parse_size;
|
||||||
|
|
||||||
static NAME: &str = "split";
|
static NAME: &str = "split";
|
||||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
@ -281,16 +281,7 @@ impl ByteSplitter {
|
||||||
let size_string = &settings.strategy_param;
|
let size_string = &settings.strategy_param;
|
||||||
let size_num = match parse_size(&size_string) {
|
let size_num = match parse_size(&size_string) {
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(e) => match e {
|
Err(e) => crash!(1, "invalid number of bytes: {}", e.to_string()),
|
||||||
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
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ByteSplitter {
|
ByteSplitter {
|
||||||
|
|
|
@ -33,7 +33,6 @@ use uucore::ringbuffer::RingBuffer;
|
||||||
pub mod options {
|
pub mod options {
|
||||||
pub mod verbosity {
|
pub mod verbosity {
|
||||||
pub static QUIET: &str = "quiet";
|
pub static QUIET: &str = "quiet";
|
||||||
pub static SILENT: &str = "silent";
|
|
||||||
pub static VERBOSE: &str = "verbose";
|
pub static VERBOSE: &str = "verbose";
|
||||||
}
|
}
|
||||||
pub static BYTES: &str = "bytes";
|
pub static BYTES: &str = "bytes";
|
||||||
|
@ -77,6 +76,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let app = App::new(executable!())
|
let app = App::new(executable!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about("output the last part of files")
|
.about("output the last part of files")
|
||||||
|
// TODO: add usage
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::BYTES)
|
Arg::with_name(options::BYTES)
|
||||||
.short("c")
|
.short("c")
|
||||||
|
@ -111,13 +111,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Arg::with_name(options::verbosity::QUIET)
|
Arg::with_name(options::verbosity::QUIET)
|
||||||
.short("q")
|
.short("q")
|
||||||
.long(options::verbosity::QUIET)
|
.long(options::verbosity::QUIET)
|
||||||
|
.visible_alias("silent")
|
||||||
|
.overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE])
|
||||||
.help("never output headers giving file names"),
|
.help("never output headers giving file names"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name(options::verbosity::SILENT)
|
|
||||||
.long(options::verbosity::SILENT)
|
|
||||||
.help("synonym of --quiet"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::SLEEP_INT)
|
Arg::with_name(options::SLEEP_INT)
|
||||||
.short("s")
|
.short("s")
|
||||||
|
@ -129,6 +126,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
Arg::with_name(options::verbosity::VERBOSE)
|
Arg::with_name(options::verbosity::VERBOSE)
|
||||||
.short("v")
|
.short("v")
|
||||||
.long(options::verbosity::VERBOSE)
|
.long(options::verbosity::VERBOSE)
|
||||||
|
.overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE])
|
||||||
.help("always output headers giving file names"),
|
.help("always output headers giving file names"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -195,8 +193,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
let verbose = matches.is_present(options::verbosity::VERBOSE);
|
let verbose = matches.is_present(options::verbosity::VERBOSE);
|
||||||
let quiet = matches.is_present(options::verbosity::QUIET)
|
let quiet = matches.is_present(options::verbosity::QUIET);
|
||||||
|| matches.is_present(options::verbosity::SILENT);
|
|
||||||
|
|
||||||
let files: Vec<String> = matches
|
let files: Vec<String> = matches
|
||||||
.values_of(options::ARG_FILES)
|
.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()));
|
return Err(ParseSizeError::ParseFailure(src.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match parse_size(&size_string) {
|
parse_size(&size_string).map(|n| (n, starting_with))
|
||||||
Ok(n) => Ok((n, starting_with)),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,10 +75,9 @@ pub fn parse_size(size: &str) -> Result<usize, ParseSizeError> {
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(_) => return Err(ParseSizeError::size_too_big(size)),
|
Err(_) => return Err(ParseSizeError::size_too_big(size)),
|
||||||
};
|
};
|
||||||
match number.checked_mul(factor) {
|
number
|
||||||
Some(n) => Ok(n),
|
.checked_mul(factor)
|
||||||
None => Err(ParseSizeError::size_too_big(size)),
|
.ok_or(ParseSizeError::size_too_big(size))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -108,22 +107,58 @@ impl fmt::Display for ParseSizeError {
|
||||||
|
|
||||||
impl ParseSizeError {
|
impl ParseSizeError {
|
||||||
fn parse_failure(s: &str) -> ParseSizeError {
|
fn parse_failure(s: &str) -> ParseSizeError {
|
||||||
// has to be handled in the respective uutils because strings differ, e.g.
|
// stderr on linux (GNU coreutils 8.32)
|
||||||
// truncate: Invalid number: ‘fb’
|
// has to be handled in the respective uutils because strings differ, e.g.:
|
||||||
// tail: invalid number of bytes: ‘fb’
|
//
|
||||||
|
// `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))
|
ParseSizeError::ParseFailure(format!("‘{}’", s))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_too_big(s: &str) -> ParseSizeError {
|
fn size_too_big(s: &str) -> ParseSizeError {
|
||||||
// has to be handled in the respective uutils because strings differ, e.g.
|
// stderr on linux (GNU coreutils 8.32)
|
||||||
// truncate: Invalid number: ‘1Y’: Value too large to be stored in data type
|
// has to be handled in the respective uutils because strings differ, e.g.:
|
||||||
// tail: invalid number of bytes: ‘1Y’: Value too large to be stored in data type
|
//
|
||||||
|
// 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
|
// 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.
|
// etc.
|
||||||
ParseSizeError::SizeTooBig(format!(
|
//
|
||||||
"‘{}’: Value too large to be stored in data type",
|
// stderr on macos (brew - GNU coreutils 8.32) also differs for the same version, e.g.:
|
||||||
s
|
// 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]
|
#[test]
|
||||||
fn test_head_invalid_num() {
|
fn test_head_invalid_num() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
@ -258,16 +259,26 @@ fn test_head_invalid_num() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["-c", "1Y", "emptyfile.txt"])
|
.args(&["-c", "1Y", "emptyfile.txt"])
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_is(
|
.stderr_is("head: invalid number of bytes: ‘1Y’: Value too large for defined data type");
|
||||||
"head: invalid number of bytes: ‘1Y’: Value too large to be stored in data type",
|
|
||||||
);
|
|
||||||
#[cfg(not(target_pointer_width = "128"))]
|
#[cfg(not(target_pointer_width = "128"))]
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["-n", "1Y", "emptyfile.txt"])
|
.args(&["-n", "1Y", "emptyfile.txt"])
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_is(
|
.stderr_is("head: invalid number of lines: ‘1Y’: Value too large for defined data type");
|
||||||
"head: invalid number of lines: ‘1Y’: Value too large to be stored in 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]
|
#[test]
|
||||||
|
|
|
@ -284,12 +284,11 @@ fn test_multiple_input_files_with_suppressed_headers() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_input_quiet_flag_overrides_verbose_flag_for_suppressing_headers() {
|
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!()
|
new_ucmd!()
|
||||||
.arg(FOOBAR_TXT)
|
.arg(FOOBAR_TXT)
|
||||||
.arg(FOOBAR_2_TXT)
|
.arg(FOOBAR_2_TXT)
|
||||||
.arg("-q")
|
|
||||||
.arg("-v")
|
.arg("-v")
|
||||||
|
.arg("-q")
|
||||||
.run()
|
.run()
|
||||||
.stdout_is_fixture("foobar_multiple_quiet.expected");
|
.stdout_is_fixture("foobar_multiple_quiet.expected");
|
||||||
}
|
}
|
||||||
|
@ -367,16 +366,26 @@ fn test_tail_invalid_num() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["-c", "1Y", "emptyfile.txt"])
|
.args(&["-c", "1Y", "emptyfile.txt"])
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_is(
|
.stderr_is("tail: invalid number of bytes: ‘1Y’: Value too large for defined data type");
|
||||||
"tail: invalid number of bytes: ‘1Y’: Value too large to be stored in data type",
|
|
||||||
);
|
|
||||||
#[cfg(not(target_pointer_width = "128"))]
|
#[cfg(not(target_pointer_width = "128"))]
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["-n", "1Y", "emptyfile.txt"])
|
.args(&["-n", "1Y", "emptyfile.txt"])
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_is(
|
.stderr_is("tail: invalid number of lines: ‘1Y’: Value too large for defined data type");
|
||||||
"tail: invalid number of lines: ‘1Y’: Value too large to be stored in 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]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue