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

basename: implement and test repeated flags and arguments

Note in particular that `args_override_self` would *NOT* work here,
since it would in all cases cause `options::NAME` to override itself,
or interfere with `trailing_var_arg`.
This commit is contained in:
Ben Wiederhake 2024-02-25 19:45:32 +01:00
parent 9441806a23
commit f905d9ce41
2 changed files with 81 additions and 67 deletions

View file

@ -27,86 +27,48 @@ pub mod options {
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args.collect_lossy();
// Since options have to go before names,
// if the first argument is not an option, then there is no option,
// and that implies there is exactly one name (no option => no -a option),
// so simple format is used
if args.len() > 1 && !args[1].starts_with('-') {
if args.len() > 3 {
return Err(UUsageError::new(
1,
format!("extra operand {}", args[3].to_string().quote()),
));
}
let suffix = if args.len() > 2 { args[2].as_ref() } else { "" };
println!("{}", basename(&args[1], suffix));
return Ok(());
}
//
// Argument parsing
//
let matches = uu_app().try_get_matches_from(args)?;
// too few arguments
if !matches.contains_id(options::NAME) {
return Err(UUsageError::new(1, "missing operand".to_string()));
}
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO));
let opt_suffix = matches.get_one::<String>(options::SUFFIX).is_some();
let opt_multiple = matches.get_flag(options::MULTIPLE);
let multiple_paths = opt_suffix || opt_multiple;
let name_args_count = matches
let mut name_args = matches
.get_many::<String>(options::NAME)
.map(|n| n.len())
.unwrap_or(0);
// too many arguments
if !multiple_paths && name_args_count > 2 {
.unwrap_or_default()
.collect::<Vec<_>>();
if name_args.is_empty() {
return Err(UUsageError::new(1, "missing operand".to_string()));
}
let multiple_paths =
matches.get_one::<String>(options::SUFFIX).is_some() || matches.get_flag(options::MULTIPLE);
let suffix = if multiple_paths {
matches
.get_one::<String>(options::SUFFIX)
.cloned()
.unwrap_or_default()
} else {
// "simple format"
match name_args.len() {
0 => panic!("already checked"),
1 => String::default(),
2 => name_args.pop().unwrap().clone(),
_ => {
return Err(UUsageError::new(
1,
format!(
"extra operand {}",
matches
.get_many::<String>(options::NAME)
.unwrap()
.nth(2)
.unwrap()
.quote()
),
format!("extra operand {}", name_args[2].quote(),),
));
}
let suffix = if opt_suffix {
matches.get_one::<String>(options::SUFFIX).unwrap()
} else if !opt_multiple && name_args_count > 1 {
matches
.get_many::<String>(options::NAME)
.unwrap()
.nth(1)
.unwrap()
} else {
""
}
};
//
// Main Program Processing
//
let paths: Vec<_> = if multiple_paths {
matches.get_many::<String>(options::NAME).unwrap().collect()
} else {
matches
.get_many::<String>(options::NAME)
.unwrap()
.take(1)
.collect()
};
for path in paths {
print!("{}{}", basename(path, suffix), line_ending);
for path in name_args {
print!("{}{}", basename(path, &suffix), line_ending);
}
Ok(())
@ -123,27 +85,31 @@ pub fn uu_app() -> Command {
.short('a')
.long(options::MULTIPLE)
.help("support multiple arguments and treat each as a NAME")
.action(ArgAction::SetTrue),
.action(ArgAction::SetTrue)
.overrides_with(options::MULTIPLE),
)
.arg(
Arg::new(options::NAME)
.action(clap::ArgAction::Append)
.value_hint(clap::ValueHint::AnyPath)
.hide(true),
.hide(true)
.trailing_var_arg(true),
)
.arg(
Arg::new(options::SUFFIX)
.short('s')
.long(options::SUFFIX)
.value_name("SUFFIX")
.help("remove a trailing SUFFIX; implies -a"),
.help("remove a trailing SUFFIX; implies -a")
.overrides_with(options::SUFFIX),
)
.arg(
Arg::new(options::ZERO)
.short('z')
.long(options::ZERO)
.help("end each output line with NUL, not newline")
.action(ArgAction::SetTrue),
.action(ArgAction::SetTrue)
.overrides_with(options::ZERO),
)
}

View file

@ -202,6 +202,54 @@ fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
}
#[test]
fn test_repeated_multiple() {
new_ucmd!()
.args(&["-aa", "-a", "foo"])
.succeeds()
.stdout_is("foo\n");
}
#[test]
fn test_repeated_multiple_many() {
new_ucmd!()
.args(&["-aa", "-a", "1/foo", "q/bar", "x/y/baz"])
.succeeds()
.stdout_is("foo\nbar\nbaz\n");
}
#[test]
fn test_repeated_suffix_last() {
new_ucmd!()
.args(&["-s", ".h", "-s", ".c", "foo.c"])
.succeeds()
.stdout_is("foo\n");
}
#[test]
fn test_repeated_suffix_not_first() {
new_ucmd!()
.args(&["-s", ".h", "-s", ".c", "foo.h"])
.succeeds()
.stdout_is("foo.h\n");
}
#[test]
fn test_repeated_suffix_multiple() {
new_ucmd!()
.args(&["-as", ".h", "-a", "-s", ".c", "foo.c", "bar.c", "bar.h"])
.succeeds()
.stdout_is("foo\nbar\nbar.h\n");
}
#[test]
fn test_repeated_zero() {
new_ucmd!()
.args(&["-zz", "-z", "foo/bar"])
.succeeds()
.stdout_is("bar\0");
}
#[test]
fn test_zero_does_not_imply_multiple() {
new_ucmd!()