1use clap::builder::ValueParser;
9use clap::{Arg, ArgAction, Command};
10use std::ffi::OsString;
11use std::io::{Write, stdout};
12use std::path::PathBuf;
13use uucore::display::Quotable;
14use uucore::error::{UResult, UUsageError};
15use uucore::format_usage;
16use uucore::line_ending::LineEnding;
17
18use uucore::translate;
19
20pub mod options {
21 pub static MULTIPLE: &str = "multiple";
22 pub static NAME: &str = "name";
23 pub static SUFFIX: &str = "suffix";
24 pub static ZERO: &str = "zero";
25}
26
27#[uucore::main]
28pub fn uumain(args: impl uucore::Args) -> UResult<()> {
29 let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
33
34 let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO));
35
36 let mut name_args = matches
37 .get_many::<OsString>(options::NAME)
38 .unwrap_or_default()
39 .collect::<Vec<_>>();
40 if name_args.is_empty() {
41 return Err(UUsageError::new(
42 1,
43 translate!("basename-error-missing-operand"),
44 ));
45 }
46 let multiple_paths = matches.get_one::<OsString>(options::SUFFIX).is_some()
47 || matches.get_flag(options::MULTIPLE);
48 let suffix = if multiple_paths {
49 matches
50 .get_one::<OsString>(options::SUFFIX)
51 .cloned()
52 .unwrap_or_default()
53 } else {
54 match name_args.len() {
56 0 => panic!("already checked"),
57 1 => OsString::default(),
58 2 => name_args.pop().unwrap().clone(),
59 _ => {
60 return Err(UUsageError::new(
61 1,
62 translate!("basename-error-extra-operand",
63 "operand" => name_args[2].quote()),
64 ));
65 }
66 }
67 };
68
69 for path in name_args {
74 stdout().write_all(&basename(path, &suffix)?)?;
75 print!("{line_ending}");
76 }
77
78 Ok(())
79}
80
81pub fn uu_app() -> Command {
82 Command::new(uucore::util_name())
83 .version(uucore::crate_version!())
84 .help_template(uucore::localized_help_template(uucore::util_name()))
85 .about(translate!("basename-about"))
86 .override_usage(format_usage(&translate!("basename-usage")))
87 .infer_long_args(true)
88 .arg(
89 Arg::new(options::MULTIPLE)
90 .short('a')
91 .long(options::MULTIPLE)
92 .help(translate!("basename-help-multiple"))
93 .action(ArgAction::SetTrue)
94 .overrides_with(options::MULTIPLE),
95 )
96 .arg(
97 Arg::new(options::NAME)
98 .action(ArgAction::Append)
99 .value_parser(ValueParser::os_string())
100 .value_hint(clap::ValueHint::AnyPath)
101 .hide(true)
102 .trailing_var_arg(true),
103 )
104 .arg(
105 Arg::new(options::SUFFIX)
106 .short('s')
107 .long(options::SUFFIX)
108 .value_name("SUFFIX")
109 .value_parser(ValueParser::os_string())
110 .help(translate!("basename-help-suffix"))
111 .overrides_with(options::SUFFIX),
112 )
113 .arg(
114 Arg::new(options::ZERO)
115 .short('z')
116 .long(options::ZERO)
117 .help(translate!("basename-help-zero"))
118 .action(ArgAction::SetTrue)
119 .overrides_with(options::ZERO),
120 )
121}
122
123fn basename(fullname: &OsString, suffix: &OsString) -> UResult<Vec<u8>> {
126 let fullname_bytes = uucore::os_str_as_bytes(fullname)?;
127
128 if fullname_bytes.ends_with(b"/.") {
130 return Ok(b".".into());
131 }
132
133 let pb = PathBuf::from(fullname);
135
136 pb.components().next_back().map_or(Ok([].into()), |c| {
137 let name = c.as_os_str();
138 let name_bytes = uucore::os_str_as_bytes(name)?;
139 if name == suffix {
140 Ok(name_bytes.into())
141 } else {
142 let suffix_bytes = uucore::os_str_as_bytes(suffix)?;
143 Ok(name_bytes
144 .strip_suffix(suffix_bytes)
145 .unwrap_or(name_bytes)
146 .into())
147 }
148 })
149}