mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
split: hyphenated values + tests
This commit is contained in:
parent
7f905a3b8d
commit
6f37b4b4cf
2 changed files with 120 additions and 11 deletions
|
@ -73,11 +73,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
/// following GNU `split` behavior
|
/// following GNU `split` behavior
|
||||||
fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
||||||
let mut obs_lines = None;
|
let mut obs_lines = None;
|
||||||
|
let mut preceding_long_opt_req_value = false;
|
||||||
|
let mut preceding_short_opt_req_value = false;
|
||||||
let filtered_args = args
|
let filtered_args = args
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|slice| {
|
.filter_map(|slice| {
|
||||||
|
let filter: Option<String>;
|
||||||
|
// check if the slice is a true short option (and not hyphen prefixed value of an option)
|
||||||
|
// and if so, a short option that can contain obsolete lines value
|
||||||
if slice.starts_with('-')
|
if slice.starts_with('-')
|
||||||
&& !slice.starts_with("--")
|
&& !slice.starts_with("--")
|
||||||
|
&& !preceding_long_opt_req_value
|
||||||
|
&& !preceding_short_opt_req_value
|
||||||
&& !slice.starts_with("-a")
|
&& !slice.starts_with("-a")
|
||||||
&& !slice.starts_with("-b")
|
&& !slice.starts_with("-b")
|
||||||
&& !slice.starts_with("-C")
|
&& !slice.starts_with("-C")
|
||||||
|
@ -109,7 +116,7 @@ fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
||||||
|
|
||||||
if obs_lines_extracted.is_empty() {
|
if obs_lines_extracted.is_empty() {
|
||||||
// no obsolete lines value found/extracted
|
// no obsolete lines value found/extracted
|
||||||
Some(slice.to_owned())
|
filter = Some(slice.to_owned());
|
||||||
} else {
|
} else {
|
||||||
// obsolete lines value was extracted
|
// obsolete lines value was extracted
|
||||||
obs_lines = Some(obs_lines_extracted.iter().collect());
|
obs_lines = Some(obs_lines_extracted.iter().collect());
|
||||||
|
@ -117,16 +124,41 @@ fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
||||||
// there were some short options in front of or after obsolete lines value
|
// there were some short options in front of or after obsolete lines value
|
||||||
// i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value
|
// i.e. '-xd100' or '-100de' or similar, which after extraction of obsolete lines value
|
||||||
// would look like '-xd' or '-de' or similar
|
// would look like '-xd' or '-de' or similar
|
||||||
Some(filtered_slice.iter().collect())
|
filter = Some(filtered_slice.iter().collect());
|
||||||
} else {
|
} else {
|
||||||
None
|
filter = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// either not a short option
|
// either not a short option
|
||||||
// or a short option that cannot have obsolete lines value in it
|
// or a short option that cannot have obsolete lines value in it
|
||||||
Some(slice.to_owned())
|
filter = Some(slice.to_owned());
|
||||||
}
|
}
|
||||||
|
// capture if current slice is a preceding long option that requires value and does not use '=' to assign that value
|
||||||
|
// following slice should be treaded as value for this option
|
||||||
|
// even if it starts with '-' (which would be treated as hyphen prefixed value)
|
||||||
|
if slice.starts_with("--") {
|
||||||
|
preceding_long_opt_req_value = &slice[2..] == OPT_BYTES
|
||||||
|
|| &slice[2..] == OPT_LINE_BYTES
|
||||||
|
|| &slice[2..] == OPT_LINES
|
||||||
|
|| &slice[2..] == OPT_ADDITIONAL_SUFFIX
|
||||||
|
|| &slice[2..] == OPT_FILTER
|
||||||
|
|| &slice[2..] == OPT_NUMBER
|
||||||
|
|| &slice[2..] == OPT_SUFFIX_LENGTH;
|
||||||
|
}
|
||||||
|
// capture if current slice is a preceding short option that requires value and does not have value in the same slice (value separated by whitespace)
|
||||||
|
// following slice should be treaded as value for this option
|
||||||
|
// even if it starts with '-' (which would be treated as hyphen prefixed value)
|
||||||
|
preceding_short_opt_req_value =
|
||||||
|
slice == "-b" || slice == "-C" || slice == "-l" || slice == "-n" || slice == "-a";
|
||||||
|
// slice is a value
|
||||||
|
// reset preceding option flags
|
||||||
|
if !slice.starts_with('-') {
|
||||||
|
preceding_short_opt_req_value = false;
|
||||||
|
preceding_long_opt_req_value = false;
|
||||||
|
}
|
||||||
|
// return filter
|
||||||
|
filter
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
(filtered_args, obs_lines)
|
(filtered_args, obs_lines)
|
||||||
|
@ -144,6 +176,7 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(OPT_BYTES)
|
Arg::new(OPT_BYTES)
|
||||||
.short('b')
|
.short('b')
|
||||||
.long(OPT_BYTES)
|
.long(OPT_BYTES)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
.value_name("SIZE")
|
.value_name("SIZE")
|
||||||
.help("put SIZE bytes per output file"),
|
.help("put SIZE bytes per output file"),
|
||||||
)
|
)
|
||||||
|
@ -151,14 +184,15 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(OPT_LINE_BYTES)
|
Arg::new(OPT_LINE_BYTES)
|
||||||
.short('C')
|
.short('C')
|
||||||
.long(OPT_LINE_BYTES)
|
.long(OPT_LINE_BYTES)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
.value_name("SIZE")
|
.value_name("SIZE")
|
||||||
.default_value("2")
|
|
||||||
.help("put at most SIZE bytes of lines per output file"),
|
.help("put at most SIZE bytes of lines per output file"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(OPT_LINES)
|
Arg::new(OPT_LINES)
|
||||||
.short('l')
|
.short('l')
|
||||||
.long(OPT_LINES)
|
.long(OPT_LINES)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
.value_name("NUMBER")
|
.value_name("NUMBER")
|
||||||
.default_value("1000")
|
.default_value("1000")
|
||||||
.help("put NUMBER lines/records per output file"),
|
.help("put NUMBER lines/records per output file"),
|
||||||
|
@ -167,6 +201,7 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(OPT_NUMBER)
|
Arg::new(OPT_NUMBER)
|
||||||
.short('n')
|
.short('n')
|
||||||
.long(OPT_NUMBER)
|
.long(OPT_NUMBER)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
.value_name("CHUNKS")
|
.value_name("CHUNKS")
|
||||||
.help("generate CHUNKS output files; see explanation below"),
|
.help("generate CHUNKS output files; see explanation below"),
|
||||||
)
|
)
|
||||||
|
@ -174,6 +209,7 @@ pub fn uu_app() -> Command {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(OPT_ADDITIONAL_SUFFIX)
|
Arg::new(OPT_ADDITIONAL_SUFFIX)
|
||||||
.long(OPT_ADDITIONAL_SUFFIX)
|
.long(OPT_ADDITIONAL_SUFFIX)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
.value_name("SUFFIX")
|
.value_name("SUFFIX")
|
||||||
.default_value("")
|
.default_value("")
|
||||||
.help("additional SUFFIX to append to output file names"),
|
.help("additional SUFFIX to append to output file names"),
|
||||||
|
@ -181,6 +217,7 @@ pub fn uu_app() -> Command {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(OPT_FILTER)
|
Arg::new(OPT_FILTER)
|
||||||
.long(OPT_FILTER)
|
.long(OPT_FILTER)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
.value_name("COMMAND")
|
.value_name("COMMAND")
|
||||||
.value_hint(clap::ValueHint::CommandName)
|
.value_hint(clap::ValueHint::CommandName)
|
||||||
.help(
|
.help(
|
||||||
|
@ -250,9 +287,10 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(OPT_SUFFIX_LENGTH)
|
Arg::new(OPT_SUFFIX_LENGTH)
|
||||||
.short('a')
|
.short('a')
|
||||||
.long(OPT_SUFFIX_LENGTH)
|
.long(OPT_SUFFIX_LENGTH)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
.value_name("N")
|
.value_name("N")
|
||||||
.default_value(OPT_DEFAULT_SUFFIX_LENGTH)
|
.default_value(OPT_DEFAULT_SUFFIX_LENGTH)
|
||||||
.help("use suffixes of fixed length N. 0 implies dynamic length."),
|
.help("use suffixes of fixed length N. 0 implies dynamic length, starting with 2"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(OPT_VERBOSE)
|
Arg::new(OPT_VERBOSE)
|
||||||
|
|
|
@ -254,6 +254,18 @@ fn test_additional_suffix_no_slash() {
|
||||||
.usage_error("invalid suffix 'a/b', contains directory separator");
|
.usage_error("invalid suffix 'a/b', contains directory separator");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_split_additional_suffix_hyphen_value() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let name = "split_additional_suffix";
|
||||||
|
RandomFile::new(&at, name).add_lines(2000);
|
||||||
|
ucmd.args(&["--additional-suffix", "-300", name]).succeeds();
|
||||||
|
|
||||||
|
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]-300$");
|
||||||
|
assert_eq!(glob.count(), 2);
|
||||||
|
assert_eq!(glob.collate(), at.read_bytes(name));
|
||||||
|
}
|
||||||
|
|
||||||
// note: the test_filter* tests below are unix-only
|
// note: the test_filter* tests below are unix-only
|
||||||
// windows support has been waived for now because of the difficulty of getting
|
// windows support has been waived for now because of the difficulty of getting
|
||||||
// the `cmd` call right
|
// the `cmd` call right
|
||||||
|
@ -436,9 +448,9 @@ fn test_split_obs_lines_starts_combined_shorts() {
|
||||||
/// Test for using both obsolete lines (standalone) option and short/long lines option simultaneously
|
/// Test for using both obsolete lines (standalone) option and short/long lines option simultaneously
|
||||||
#[test]
|
#[test]
|
||||||
fn test_split_both_lines_and_obs_lines_standalone() {
|
fn test_split_both_lines_and_obs_lines_standalone() {
|
||||||
// This test will ensure that if both lines option '-l' or '--lines'
|
// This test will ensure that:
|
||||||
// and obsolete lines option '-100' are used
|
// if both lines option '-l' or '--lines' (with value) and obsolete lines option '-100' are used - it fails
|
||||||
// it fails
|
// if standalone lines option is used incorrectly and treated as a hyphen prefixed value of other option - it fails
|
||||||
let scene = TestScenario::new(util_name!());
|
let scene = TestScenario::new(util_name!());
|
||||||
let at = &scene.fixtures;
|
let at = &scene.fixtures;
|
||||||
at.touch("file");
|
at.touch("file");
|
||||||
|
@ -455,18 +467,77 @@ fn test_split_both_lines_and_obs_lines_standalone() {
|
||||||
.fails()
|
.fails()
|
||||||
.code_is(1)
|
.code_is(1)
|
||||||
.stderr_contains("split: cannot split in more than one way\n");
|
.stderr_contains("split: cannot split in more than one way\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test for using obsolete lines option incorrectly, so it is treated as a hyphen prefixed value of other option
|
||||||
|
#[test]
|
||||||
|
fn test_split_obs_lines_as_other_option_value() {
|
||||||
|
// This test will ensure that:
|
||||||
|
// if obsolete lines option is used incorrectly and treated as a hyphen prefixed value of other option - it fails
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
at.touch("file");
|
||||||
|
|
||||||
scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.args(&["--lines", "-200", "file"])
|
.args(&["--lines", "-200", "file"])
|
||||||
.fails()
|
.fails()
|
||||||
.code_is(1)
|
.code_is(1)
|
||||||
.stderr_contains("split: cannot split in more than one way\n");
|
.stderr_contains("split: invalid number of lines: '-200'\n");
|
||||||
scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.args(&["-l", "-200", "file"])
|
.args(&["-l", "-200", "file"])
|
||||||
.fails()
|
.fails()
|
||||||
.code_is(1)
|
.code_is(1)
|
||||||
.stderr_contains("split: cannot split in more than one way\n");
|
.stderr_contains("split: invalid number of lines: '-200'\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-a", "-200", "file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("split: invalid suffix length: '-200'\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["--suffix-length", "-d200e", "file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("split: invalid suffix length: '-d200e'\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-C", "-200", "file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("split: invalid number of bytes: '-200'\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["--line-bytes", "-x200a4", "file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("split: invalid number of bytes: '-x200a4'\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-b", "-200", "file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("split: invalid number of bytes: '-200'\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["--bytes", "-200xd", "file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("split: invalid number of bytes: '-200xd'\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-n", "-200", "file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("split: invalid number of chunks: -200\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["--number", "-e200", "file"])
|
||||||
|
.fails()
|
||||||
|
.code_is(1)
|
||||||
|
.stderr_contains("split: invalid number of chunks: -e200\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test for using more than one obsolete lines option (standalone)
|
/// Test for using more than one obsolete lines option (standalone)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue