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

ls: update to clap 4

This commit is contained in:
Terts Diepraam 2022-09-29 19:59:27 +02:00
parent eadbea74d6
commit c6cff20f18
3 changed files with 161 additions and 119 deletions

View file

@ -16,7 +16,7 @@ path = "src/ls.rs"
[dependencies] [dependencies]
chrono = { version="^0.4.19", default-features=false, features=["std", "alloc", "clock"]} chrono = { version="^0.4.19", default-features=false, features=["std", "alloc", "clock"]}
clap = { version = "3.2", features = ["wrap_help", "cargo", "env"] } clap = { version = "4.0", features = ["wrap_help", "cargo", "env"] }
unicode-width = "0.1.8" unicode-width = "0.1.8"
number_prefix = "0.4" number_prefix = "0.4"
term_grid = "0.1.5" term_grid = "0.1.5"

View file

@ -12,7 +12,7 @@ extern crate uucore;
use clap::{ use clap::{
builder::{NonEmptyStringValueParser, ValueParser}, builder::{NonEmptyStringValueParser, ValueParser},
crate_version, Arg, Command, crate_version, Arg, ArgAction, Command,
}; };
use glob::{MatchOptions, Pattern}; use glob::{MatchOptions, Pattern};
use lscolors::LsColors; use lscolors::LsColors;
@ -268,7 +268,7 @@ impl Display for LsError {
} }
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Debug)]
pub enum Format { pub enum Format {
Columns, Columns,
Long, Long,
@ -328,7 +328,7 @@ fn parse_time_style(options: &clap::ArgMatches) -> Result<TimeStyle, LsError> {
if let Some(field) = options.get_one::<String>(options::TIME_STYLE) { if let Some(field) = options.get_one::<String>(options::TIME_STYLE) {
//If both FULL_TIME and TIME_STYLE are present //If both FULL_TIME and TIME_STYLE are present
//The one added last is dominant //The one added last is dominant
if options.contains_id(options::FULL_TIME) if options.get_flag(options::FULL_TIME)
&& options.indices_of(options::FULL_TIME).unwrap().last() && options.indices_of(options::FULL_TIME).unwrap().last()
> options.indices_of(options::TIME_STYLE).unwrap().last() > options.indices_of(options::TIME_STYLE).unwrap().last()
{ {
@ -348,7 +348,7 @@ fn parse_time_style(options: &clap::ArgMatches) -> Result<TimeStyle, LsError> {
}, },
} }
} }
} else if options.contains_id(options::FULL_TIME) { } else if options.get_flag(options::FULL_TIME) {
Ok(TimeStyle::FullIso) Ok(TimeStyle::FullIso)
} else { } else {
Ok(TimeStyle::Locale) Ok(TimeStyle::Locale)
@ -425,7 +425,7 @@ struct PaddingCollection {
impl Config { impl Config {
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn from(options: &clap::ArgMatches) -> UResult<Self> { pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
let context = options.contains_id(options::CONTEXT); let context = options.get_flag(options::CONTEXT);
let (mut format, opt) = if let Some(format_) = options.get_one::<String>(options::FORMAT) { let (mut format, opt) = if let Some(format_) = options.get_one::<String>(options::FORMAT) {
( (
match format_.as_str() { match format_.as_str() {
@ -439,13 +439,13 @@ impl Config {
}, },
Some(options::FORMAT), Some(options::FORMAT),
) )
} else if options.contains_id(options::format::LONG) { } else if options.get_flag(options::format::LONG) {
(Format::Long, Some(options::format::LONG)) (Format::Long, Some(options::format::LONG))
} else if options.contains_id(options::format::ACROSS) { } else if options.get_flag(options::format::ACROSS) {
(Format::Across, Some(options::format::ACROSS)) (Format::Across, Some(options::format::ACROSS))
} else if options.contains_id(options::format::COMMAS) { } else if options.get_flag(options::format::COMMAS) {
(Format::Commas, Some(options::format::COMMAS)) (Format::Commas, Some(options::format::COMMAS))
} else if options.contains_id(options::format::COLUMNS) { } else if options.get_flag(options::format::COLUMNS) {
(Format::Columns, Some(options::format::COLUMNS)) (Format::Columns, Some(options::format::COLUMNS))
} else if atty::is(atty::Stream::Stdout) { } else if atty::is(atty::Stream::Stdout) {
(Format::Columns, None) (Format::Columns, None)
@ -479,21 +479,30 @@ impl Config {
options::FULL_TIME, options::FULL_TIME,
] ]
.iter() .iter()
.flat_map(|opt| options.indices_of(opt)) .flat_map(|opt| {
if options.value_source(opt) == Some(clap::parser::ValueSource::CommandLine) {
options.indices_of(opt)
} else {
None
}
})
.flatten() .flatten()
.any(|i| i >= idx) .any(|i| i >= idx)
{ {
format = Format::Long; format = Format::Long;
} else if let Some(mut indices) = options.indices_of(options::format::ONE_LINE) { } else if let Some(mut indices) = options.indices_of(options::format::ONE_LINE) {
if indices.any(|i| i > idx) { if options.value_source(options::format::ONE_LINE)
== Some(clap::parser::ValueSource::CommandLine)
&& indices.any(|i| i > idx)
{
format = Format::OneLine; format = Format::OneLine;
} }
} }
} }
let files = if options.contains_id(options::files::ALL) { let files = if options.get_flag(options::files::ALL) {
Files::All Files::All
} else if options.contains_id(options::files::ALMOST_ALL) { } else if options.get_flag(options::files::ALMOST_ALL) {
Files::AlmostAll Files::AlmostAll
} else { } else {
Files::Normal Files::Normal
@ -510,15 +519,15 @@ impl Config {
// below should never happen as clap already restricts the values. // below should never happen as clap already restricts the values.
_ => unreachable!("Invalid field for --sort"), _ => unreachable!("Invalid field for --sort"),
} }
} else if options.contains_id(options::sort::TIME) { } else if options.get_flag(options::sort::TIME) {
Sort::Time Sort::Time
} else if options.contains_id(options::sort::SIZE) { } else if options.get_flag(options::sort::SIZE) {
Sort::Size Sort::Size
} else if options.contains_id(options::sort::NONE) { } else if options.get_flag(options::sort::NONE) {
Sort::None Sort::None
} else if options.contains_id(options::sort::VERSION) { } else if options.get_flag(options::sort::VERSION) {
Sort::Version Sort::Version
} else if options.contains_id(options::sort::EXTENSION) { } else if options.get_flag(options::sort::EXTENSION) {
Sort::Extension Sort::Extension
} else { } else {
Sort::Name Sort::Name
@ -532,9 +541,9 @@ impl Config {
// below should never happen as clap already restricts the values. // below should never happen as clap already restricts the values.
_ => unreachable!("Invalid field for --time"), _ => unreachable!("Invalid field for --time"),
} }
} else if options.contains_id(options::time::ACCESS) { } else if options.get_flag(options::time::ACCESS) {
Time::Access Time::Access
} else if options.contains_id(options::time::CHANGE) { } else if options.get_flag(options::time::CHANGE) {
Time::Change Time::Change
} else { } else {
Time::Modification Time::Modification
@ -555,14 +564,14 @@ impl Config {
.get_one::<String>(options::size::BLOCK_SIZE) .get_one::<String>(options::size::BLOCK_SIZE)
.unwrap() .unwrap()
.eq("si") .eq("si")
|| options.contains_id(options::size::SI); || options.get_flag(options::size::SI);
let opt_hr = (cmd_line_bs.is_some() let opt_hr = (cmd_line_bs.is_some()
&& options && options
.get_one::<String>(options::size::BLOCK_SIZE) .get_one::<String>(options::size::BLOCK_SIZE)
.unwrap() .unwrap()
.eq("human-readable")) .eq("human-readable"))
|| options.contains_id(options::size::HUMAN_READABLE); || options.get_flag(options::size::HUMAN_READABLE);
let opt_kb = options.contains_id(options::size::KIBIBYTES); let opt_kb = options.get_flag(options::size::KIBIBYTES);
let bs_env_var = std::env::var_os("BLOCK_SIZE"); let bs_env_var = std::env::var_os("BLOCK_SIZE");
let ls_bs_env_var = std::env::var_os("LS_BLOCK_SIZE"); let ls_bs_env_var = std::env::var_os("LS_BLOCK_SIZE");
@ -611,12 +620,12 @@ impl Config {
}; };
let long = { let long = {
let author = options.contains_id(options::AUTHOR); let author = options.get_flag(options::AUTHOR);
let group = !options.contains_id(options::NO_GROUP) let group = !options.get_flag(options::NO_GROUP)
&& !options.contains_id(options::format::LONG_NO_GROUP); && !options.get_flag(options::format::LONG_NO_GROUP);
let owner = !options.contains_id(options::format::LONG_NO_OWNER); let owner = !options.get_flag(options::format::LONG_NO_OWNER);
#[cfg(unix)] #[cfg(unix)]
let numeric_uid_gid = options.contains_id(options::format::LONG_NUMERIC_UID_GID); let numeric_uid_gid = options.get_flag(options::format::LONG_NUMERIC_UID_GID);
LongFormat { LongFormat {
author, author,
group, group,
@ -660,9 +669,9 @@ impl Config {
}; };
#[allow(clippy::needless_bool)] #[allow(clippy::needless_bool)]
let mut show_control = if options.contains_id(options::HIDE_CONTROL_CHARS) { let mut show_control = if options.get_flag(options::HIDE_CONTROL_CHARS) {
false false
} else if options.contains_id(options::SHOW_CONTROL_CHARS) { } else if options.get_flag(options::SHOW_CONTROL_CHARS) {
true true
} else { } else {
!atty::is(atty::Stream::Stdout) !atty::is(atty::Stream::Stdout)
@ -703,13 +712,13 @@ impl Config {
}, },
_ => unreachable!("Should have been caught by Clap"), _ => unreachable!("Should have been caught by Clap"),
} }
} else if options.contains_id(options::quoting::LITERAL) { } else if options.get_flag(options::quoting::LITERAL) {
QuotingStyle::Literal { show_control } QuotingStyle::Literal { show_control }
} else if options.contains_id(options::quoting::ESCAPE) { } else if options.get_flag(options::quoting::ESCAPE) {
QuotingStyle::C { QuotingStyle::C {
quotes: quoting_style::Quotes::None, quotes: quoting_style::Quotes::None,
} }
} else if options.contains_id(options::quoting::C) { } else if options.get_flag(options::quoting::C) {
QuotingStyle::C { QuotingStyle::C {
quotes: quoting_style::Quotes::Double, quotes: quoting_style::Quotes::Double,
} }
@ -745,9 +754,9 @@ impl Config {
} }
&_ => IndicatorStyle::None, &_ => IndicatorStyle::None,
} }
} else if options.contains_id(options::indicator_style::SLASH) { } else if options.get_flag(options::indicator_style::SLASH) {
IndicatorStyle::Slash IndicatorStyle::Slash
} else if options.contains_id(options::indicator_style::FILE_TYPE) { } else if options.get_flag(options::indicator_style::FILE_TYPE) {
IndicatorStyle::FileType IndicatorStyle::FileType
} else { } else {
IndicatorStyle::None IndicatorStyle::None
@ -756,7 +765,7 @@ impl Config {
let mut ignore_patterns: Vec<Pattern> = Vec::new(); let mut ignore_patterns: Vec<Pattern> = Vec::new();
if options.contains_id(options::IGNORE_BACKUPS) { if options.get_flag(options::IGNORE_BACKUPS) {
ignore_patterns.push(Pattern::new("*~").unwrap()); ignore_patterns.push(Pattern::new("*~").unwrap());
ignore_patterns.push(Pattern::new(".*~").unwrap()); ignore_patterns.push(Pattern::new(".*~").unwrap());
} }
@ -815,7 +824,13 @@ impl Config {
options::quoting::ESCAPE, options::quoting::ESCAPE,
options::quoting::LITERAL, options::quoting::LITERAL,
]; ];
let get_last = |flag: &str| -> usize { options.index_of(flag).unwrap_or(0) }; let get_last = |flag: &str| -> usize {
if options.value_source(flag) == Some(clap::parser::ValueSource::CommandLine) {
options.index_of(flag).unwrap_or(0)
} else {
0
}
};
if get_last(options::ZERO) if get_last(options::ZERO)
> zero_formats_opts > zero_formats_opts
.into_iter() .into_iter()
@ -863,13 +878,13 @@ impl Config {
None None
}; };
let dereference = if options.contains_id(options::dereference::ALL) { let dereference = if options.get_flag(options::dereference::ALL) {
Dereference::All Dereference::All
} else if options.contains_id(options::dereference::ARGS) { } else if options.get_flag(options::dereference::ARGS) {
Dereference::Args Dereference::Args
} else if options.contains_id(options::dereference::DIR_ARGS) { } else if options.get_flag(options::dereference::DIR_ARGS) {
Dereference::DirArgs Dereference::DirArgs
} else if options.contains_id(options::DIRECTORY) } else if options.get_flag(options::DIRECTORY)
|| indicator_style == IndicatorStyle::Classify || indicator_style == IndicatorStyle::Classify
|| format == Format::Long || format == Format::Long
{ {
@ -882,18 +897,18 @@ impl Config {
format, format,
files, files,
sort, sort,
recursive: options.contains_id(options::RECURSIVE), recursive: options.get_flag(options::RECURSIVE),
reverse: options.contains_id(options::REVERSE), reverse: options.get_flag(options::REVERSE),
dereference, dereference,
ignore_patterns, ignore_patterns,
size_format, size_format,
directory: options.contains_id(options::DIRECTORY), directory: options.get_flag(options::DIRECTORY),
time, time,
color, color,
#[cfg(unix)] #[cfg(unix)]
inode: options.contains_id(options::INODE), inode: options.get_flag(options::INODE),
long, long,
alloc_size: options.contains_id(options::size::ALLOCATION_SIZE), alloc_size: options.get_flag(options::size::ALLOCATION_SIZE),
block_size, block_size,
width, width,
quoting_style, quoting_style,
@ -910,8 +925,8 @@ impl Config {
false false
} }
}, },
group_directories_first: options.contains_id(options::GROUP_DIRECTORIES_FIRST), group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST),
eol: if options.contains_id(options::ZERO) { eol: if options.get_flag(options::ZERO) {
'\0' '\0'
} else { } else {
'\n' '\n'
@ -936,23 +951,24 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
list(locs, &config) list(locs, &config)
} }
pub fn uu_app<'a>() -> Command<'a> { pub fn uu_app() -> Command {
Command::new(uucore::util_name()) Command::new(uucore::util_name())
.version(crate_version!()) .version(crate_version!())
.override_usage(format_usage(USAGE)) .override_usage(format_usage(USAGE))
.about(ABOUT) .about(ABOUT)
.infer_long_args(true) .infer_long_args(true)
.disable_help_flag(true)
.arg( .arg(
Arg::new(options::HELP) Arg::new(options::HELP)
.long(options::HELP) .long(options::HELP)
.help("Print help information."), .help("Print help information.")
.action(ArgAction::Help),
) )
// Format arguments // Format arguments
.arg( .arg(
Arg::new(options::FORMAT) Arg::new(options::FORMAT)
.long(options::FORMAT) .long(options::FORMAT)
.help("Set the display format.") .help("Set the display format.")
.takes_value(true)
.value_parser([ .value_parser([
"long", "long",
"verbose", "verbose",
@ -983,7 +999,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::format::LONG, options::format::LONG,
options::format::ACROSS, options::format::ACROSS,
options::format::COLUMNS, options::format::COLUMNS,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::format::LONG) Arg::new(options::format::LONG)
@ -996,7 +1013,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::format::LONG, options::format::LONG,
options::format::ACROSS, options::format::ACROSS,
options::format::COLUMNS, options::format::COLUMNS,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::format::ACROSS) Arg::new(options::format::ACROSS)
@ -1008,7 +1026,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::format::LONG, options::format::LONG,
options::format::ACROSS, options::format::ACROSS,
options::format::COLUMNS, options::format::COLUMNS,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
// silently ignored (see #3624) // silently ignored (see #3624)
@ -1016,7 +1035,6 @@ pub fn uu_app<'a>() -> Command<'a> {
.short('T') .short('T')
.long(options::format::TAB_SIZE) .long(options::format::TAB_SIZE)
.env("TABSIZE") .env("TABSIZE")
.takes_value(true)
.value_name("COLS") .value_name("COLS")
.help("Assume tab stops at each COLS instead of 8 (unimplemented)"), .help("Assume tab stops at each COLS instead of 8 (unimplemented)"),
) )
@ -1030,20 +1048,23 @@ pub fn uu_app<'a>() -> Command<'a> {
options::format::LONG, options::format::LONG,
options::format::ACROSS, options::format::ACROSS,
options::format::COLUMNS, options::format::COLUMNS,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::ZERO) Arg::new(options::ZERO)
.long(options::ZERO) .long(options::ZERO)
.conflicts_with(options::DIRED) .conflicts_with(options::DIRED)
.overrides_with(options::ZERO) .overrides_with(options::ZERO)
.help("List entries separated by ASCII NUL characters."), .help("List entries separated by ASCII NUL characters.")
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::DIRED) Arg::new(options::DIRED)
.long(options::DIRED) .long(options::DIRED)
.short('D') .short('D')
.hide(true), .hide(true)
.action(ArgAction::SetTrue),
) )
// The next four arguments do not override with the other format // The next four arguments do not override with the other format
// options, see the comment in Config::from for the reason. // options, see the comment in Config::from for the reason.
@ -1055,7 +1076,7 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(options::format::ONE_LINE) Arg::new(options::format::ONE_LINE)
.short('1') .short('1')
.help("List one file per line.") .help("List one file per line.")
.multiple_occurrences(true), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::format::LONG_NO_GROUP) Arg::new(options::format::LONG_NO_GROUP)
@ -1064,26 +1085,25 @@ pub fn uu_app<'a>() -> Command<'a> {
"Long format without group information. \ "Long format without group information. \
Identical to --format=long with --no-group.", Identical to --format=long with --no-group.",
) )
.multiple_occurrences(true), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::format::LONG_NO_OWNER) Arg::new(options::format::LONG_NO_OWNER)
.short('g') .short('g')
.help("Long format without owner information.") .help("Long format without owner information.")
.multiple_occurrences(true), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::format::LONG_NUMERIC_UID_GID) Arg::new(options::format::LONG_NUMERIC_UID_GID)
.short('n') .short('n')
.long(options::format::LONG_NUMERIC_UID_GID) .long(options::format::LONG_NUMERIC_UID_GID)
.help("-l with numeric UIDs and GIDs.") .help("-l with numeric UIDs and GIDs.")
.multiple_occurrences(true), .action(ArgAction::SetTrue),
) )
// Quoting style // Quoting style
.arg( .arg(
Arg::new(options::QUOTING_STYLE) Arg::new(options::QUOTING_STYLE)
.long(options::QUOTING_STYLE) .long(options::QUOTING_STYLE)
.takes_value(true)
.help("Set quoting style.") .help("Set quoting style.")
.value_parser([ .value_parser([
"literal", "literal",
@ -1111,7 +1131,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::quoting::LITERAL, options::quoting::LITERAL,
options::quoting::ESCAPE, options::quoting::ESCAPE,
options::quoting::C, options::quoting::C,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::quoting::ESCAPE) Arg::new(options::quoting::ESCAPE)
@ -1123,7 +1144,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::quoting::LITERAL, options::quoting::LITERAL,
options::quoting::ESCAPE, options::quoting::ESCAPE,
options::quoting::C, options::quoting::C,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::quoting::C) Arg::new(options::quoting::C)
@ -1135,7 +1157,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::quoting::LITERAL, options::quoting::LITERAL,
options::quoting::ESCAPE, options::quoting::ESCAPE,
options::quoting::C, options::quoting::C,
]), ])
.action(ArgAction::SetTrue),
) )
// Control characters // Control characters
.arg( .arg(
@ -1143,13 +1166,15 @@ pub fn uu_app<'a>() -> Command<'a> {
.short('q') .short('q')
.long(options::HIDE_CONTROL_CHARS) .long(options::HIDE_CONTROL_CHARS)
.help("Replace control characters with '?' if they are not escaped.") .help("Replace control characters with '?' if they are not escaped.")
.overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::SHOW_CONTROL_CHARS) Arg::new(options::SHOW_CONTROL_CHARS)
.long(options::SHOW_CONTROL_CHARS) .long(options::SHOW_CONTROL_CHARS)
.help("Show control characters 'as is' if they are not escaped.") .help("Show control characters 'as is' if they are not escaped.")
.overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS])
.action(ArgAction::SetTrue),
) )
// Time arguments // Time arguments
.arg( .arg(
@ -1162,7 +1187,6 @@ pub fn uu_app<'a>() -> Command<'a> {
\tbirth time: birth, creation;", \tbirth time: birth, creation;",
) )
.value_name("field") .value_name("field")
.takes_value(true)
.value_parser([ .value_parser([
"atime", "access", "use", "ctime", "status", "birth", "creation", "atime", "access", "use", "ctime", "status", "birth", "creation",
]) ])
@ -1179,7 +1203,8 @@ pub fn uu_app<'a>() -> Command<'a> {
time. When explicitly sorting by time (--sort=time or -t) or when not \ time. When explicitly sorting by time (--sort=time or -t) or when not \
using a long listing format, sort according to the status change time.", using a long listing format, sort according to the status change time.",
) )
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::time::ACCESS) Arg::new(options::time::ACCESS)
@ -1190,14 +1215,14 @@ pub fn uu_app<'a>() -> Command<'a> {
sorting by time (--sort=time or -t) or when not using a long listing \ sorting by time (--sort=time or -t) or when not using a long listing \
format, sort according to the access time.", format, sort according to the access time.",
) )
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE])
.action(ArgAction::SetTrue),
) )
// Hide and ignore // Hide and ignore
.arg( .arg(
Arg::new(options::HIDE) Arg::new(options::HIDE)
.long(options::HIDE) .long(options::HIDE)
.takes_value(true) .action(ArgAction::Append)
.multiple_occurrences(true)
.value_name("PATTERN") .value_name("PATTERN")
.help( .help(
"do not list implied entries matching shell PATTERN (overridden by -a or -A)", "do not list implied entries matching shell PATTERN (overridden by -a or -A)",
@ -1207,8 +1232,7 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(options::IGNORE) Arg::new(options::IGNORE)
.short('I') .short('I')
.long(options::IGNORE) .long(options::IGNORE)
.takes_value(true) .action(ArgAction::Append)
.multiple_occurrences(true)
.value_name("PATTERN") .value_name("PATTERN")
.help("do not list implied entries matching shell PATTERN"), .help("do not list implied entries matching shell PATTERN"),
) )
@ -1216,7 +1240,8 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(options::IGNORE_BACKUPS) Arg::new(options::IGNORE_BACKUPS)
.short('B') .short('B')
.long(options::IGNORE_BACKUPS) .long(options::IGNORE_BACKUPS)
.help("Ignore entries which end with ~."), .help("Ignore entries which end with ~.")
.action(ArgAction::SetTrue),
) )
// Sort arguments // Sort arguments
.arg( .arg(
@ -1224,7 +1249,6 @@ pub fn uu_app<'a>() -> Command<'a> {
.long(options::SORT) .long(options::SORT)
.help("Sort by <field>: name, none (-U), time (-t), size (-S) or extension (-X)") .help("Sort by <field>: name, none (-U), time (-t), size (-S) or extension (-X)")
.value_name("field") .value_name("field")
.takes_value(true)
.value_parser(["name", "none", "time", "size", "version", "extension"]) .value_parser(["name", "none", "time", "size", "version", "extension"])
.require_equals(true) .require_equals(true)
.overrides_with_all(&[ .overrides_with_all(&[
@ -1247,7 +1271,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::sort::NONE, options::sort::NONE,
options::sort::VERSION, options::sort::VERSION,
options::sort::EXTENSION, options::sort::EXTENSION,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::sort::TIME) Arg::new(options::sort::TIME)
@ -1260,7 +1285,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::sort::NONE, options::sort::NONE,
options::sort::VERSION, options::sort::VERSION,
options::sort::EXTENSION, options::sort::EXTENSION,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::sort::VERSION) Arg::new(options::sort::VERSION)
@ -1273,7 +1299,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::sort::NONE, options::sort::NONE,
options::sort::VERSION, options::sort::VERSION,
options::sort::EXTENSION, options::sort::EXTENSION,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::sort::EXTENSION) Arg::new(options::sort::EXTENSION)
@ -1286,7 +1313,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::sort::NONE, options::sort::NONE,
options::sort::VERSION, options::sort::VERSION,
options::sort::EXTENSION, options::sort::EXTENSION,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::sort::NONE) Arg::new(options::sort::NONE)
@ -1303,7 +1331,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::sort::NONE, options::sort::NONE,
options::sort::VERSION, options::sort::VERSION,
options::sort::EXTENSION, options::sort::EXTENSION,
]), ])
.action(ArgAction::SetTrue),
) )
// Dereferencing // Dereferencing
.arg( .arg(
@ -1318,7 +1347,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::dereference::ALL, options::dereference::ALL,
options::dereference::DIR_ARGS, options::dereference::DIR_ARGS,
options::dereference::ARGS, options::dereference::ARGS,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::dereference::DIR_ARGS) Arg::new(options::dereference::DIR_ARGS)
@ -1331,7 +1361,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::dereference::ALL, options::dereference::ALL,
options::dereference::DIR_ARGS, options::dereference::DIR_ARGS,
options::dereference::ARGS, options::dereference::ARGS,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::dereference::ARGS) Arg::new(options::dereference::ARGS)
@ -1342,40 +1373,42 @@ pub fn uu_app<'a>() -> Command<'a> {
options::dereference::ALL, options::dereference::ALL,
options::dereference::DIR_ARGS, options::dereference::DIR_ARGS,
options::dereference::ARGS, options::dereference::ARGS,
]), ])
.action(ArgAction::SetTrue),
) )
// Long format options // Long format options
.arg( .arg(
Arg::new(options::NO_GROUP) Arg::new(options::NO_GROUP)
.long(options::NO_GROUP) .long(options::NO_GROUP)
.short('G') .short('G')
.help("Do not show group in long format."), .help("Do not show group in long format.")
.action(ArgAction::SetTrue),
) )
.arg(Arg::new(options::AUTHOR).long(options::AUTHOR).help( .arg(Arg::new(options::AUTHOR).long(options::AUTHOR).help(
"Show author in long format. \ "Show author in long format. On the supported platforms, \
On the supported platforms, the author always matches the file owner.", the author always matches the file owner.",
)) ).action(ArgAction::SetTrue))
// Other Flags // Other Flags
.arg( .arg(
Arg::new(options::files::ALL) Arg::new(options::files::ALL)
.short('a') .short('a')
.long(options::files::ALL) .long(options::files::ALL)
// Overrides -A (as the order matters) // Overrides -A (as the order matters)
.overrides_with(options::files::ALMOST_ALL) .overrides_with_all([options::files::ALL, options::files::ALMOST_ALL])
.multiple_occurrences(true) .help("Do not ignore hidden files (files with names that start with '.').")
.help("Do not ignore hidden files (files with names that start with '.')."), .action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::files::ALMOST_ALL) Arg::new(options::files::ALMOST_ALL)
.short('A') .short('A')
.long(options::files::ALMOST_ALL) .long(options::files::ALMOST_ALL)
// Overrides -a (as the order matters) // Overrides -a (as the order matters)
.overrides_with(options::files::ALL) .overrides_with_all([options::files::ALL, options::files::ALMOST_ALL])
.multiple_occurrences(true)
.help( .help(
"In a directory, do not ignore all file names that start with '.', \ "In a directory, do not ignore all file names that start with '.', \
only ignore '.' and '..'.", only ignore '.' and '..'.",
), )
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::DIRECTORY) Arg::new(options::DIRECTORY)
@ -1386,14 +1419,16 @@ pub fn uu_app<'a>() -> Command<'a> {
This will not follow symbolic links unless one of `--dereference-command-line \ This will not follow symbolic links unless one of `--dereference-command-line \
(-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \ (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \
specified.", specified.",
), )
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::size::HUMAN_READABLE) Arg::new(options::size::HUMAN_READABLE)
.short('h') .short('h')
.long(options::size::HUMAN_READABLE) .long(options::size::HUMAN_READABLE)
.help("Print human readable file sizes (e.g. 1K 234M 56G).") .help("Print human readable file sizes (e.g. 1K 234M 56G).")
.overrides_with(options::size::SI), .overrides_with(options::size::SI)
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::size::KIBIBYTES) Arg::new(options::size::KIBIBYTES)
@ -1402,17 +1437,18 @@ pub fn uu_app<'a>() -> Command<'a> {
.help( .help(
"default to 1024-byte blocks for file system usage; used only with -s and per \ "default to 1024-byte blocks for file system usage; used only with -s and per \
directory totals", directory totals",
), )
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::size::SI) Arg::new(options::size::SI)
.long(options::size::SI) .long(options::size::SI)
.help("Print human readable file sizes using powers of 1000 instead of 1024."), .help("Print human readable file sizes using powers of 1000 instead of 1024.")
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::size::BLOCK_SIZE) Arg::new(options::size::BLOCK_SIZE)
.long(options::size::BLOCK_SIZE) .long(options::size::BLOCK_SIZE)
.takes_value(true)
.require_equals(true) .require_equals(true)
.value_name("BLOCK_SIZE") .value_name("BLOCK_SIZE")
.help("scale sizes by BLOCK_SIZE when printing them"), .help("scale sizes by BLOCK_SIZE when printing them"),
@ -1421,7 +1457,8 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(options::INODE) Arg::new(options::INODE)
.short('i') .short('i')
.long(options::INODE) .long(options::INODE)
.help("print the index number of each file"), .help("print the index number of each file")
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::REVERSE) Arg::new(options::REVERSE)
@ -1430,38 +1467,39 @@ pub fn uu_app<'a>() -> Command<'a> {
.help( .help(
"Reverse whatever the sorting method is e.g., list files in reverse \ "Reverse whatever the sorting method is e.g., list files in reverse \
alphabetical order, youngest first, smallest first, or whatever.", alphabetical order, youngest first, smallest first, or whatever.",
), )
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::RECURSIVE) Arg::new(options::RECURSIVE)
.short('R') .short('R')
.long(options::RECURSIVE) .long(options::RECURSIVE)
.help("List the contents of all directories recursively."), .help("List the contents of all directories recursively.")
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::WIDTH) Arg::new(options::WIDTH)
.long(options::WIDTH) .long(options::WIDTH)
.short('w') .short('w')
.help("Assume that the terminal is COLS columns wide.") .help("Assume that the terminal is COLS columns wide.")
.value_name("COLS") .value_name("COLS"),
.takes_value(true),
) )
.arg( .arg(
Arg::new(options::size::ALLOCATION_SIZE) Arg::new(options::size::ALLOCATION_SIZE)
.short('s') .short('s')
.long(options::size::ALLOCATION_SIZE) .long(options::size::ALLOCATION_SIZE)
.help("print the allocated size of each file, in blocks"), .help("print the allocated size of each file, in blocks")
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::COLOR) Arg::new(options::COLOR)
.long(options::COLOR) .long(options::COLOR)
.help("Color output based on file type.") .help("Color output based on file type.")
.takes_value(true)
.value_parser([ .value_parser([
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none",
]) ])
.require_equals(true) .require_equals(true)
.min_values(0), .num_args(0..=1),
) )
.arg( .arg(
Arg::new(options::INDICATOR_STYLE) Arg::new(options::INDICATOR_STYLE)
@ -1470,7 +1508,6 @@ pub fn uu_app<'a>() -> Command<'a> {
"Append indicator with style WORD to entry names: \ "Append indicator with style WORD to entry names: \
none (default), slash (-p), file-type (--file-type), classify (-F)", none (default), slash (-p), file-type (--file-type), classify (-F)",
) )
.takes_value(true)
.value_parser(["none", "slash", "file-type", "classify"]) .value_parser(["none", "slash", "file-type", "classify"])
.overrides_with_all(&[ .overrides_with_all(&[
options::indicator_style::FILE_TYPE, options::indicator_style::FILE_TYPE,
@ -1501,14 +1538,13 @@ pub fn uu_app<'a>() -> Command<'a> {
--dereference-command-line (-H), --dereference (-L), or \ --dereference-command-line (-H), --dereference (-L), or \
--dereference-command-line-symlink-to-dir options are specified.", --dereference-command-line-symlink-to-dir options are specified.",
) )
.takes_value(true)
.value_name("when") .value_name("when")
.value_parser([ .value_parser([
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none",
]) ])
.default_missing_value("always") .default_missing_value("always")
.require_equals(true) .require_equals(true)
.min_values(0) .num_args(0..=1)
.overrides_with_all(&[ .overrides_with_all(&[
options::indicator_style::FILE_TYPE, options::indicator_style::FILE_TYPE,
options::indicator_style::SLASH, options::indicator_style::SLASH,
@ -1525,7 +1561,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::indicator_style::SLASH, options::indicator_style::SLASH,
options::indicator_style::CLASSIFY, options::indicator_style::CLASSIFY,
options::INDICATOR_STYLE, options::INDICATOR_STYLE,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::indicator_style::SLASH) Arg::new(options::indicator_style::SLASH)
@ -1536,7 +1573,8 @@ pub fn uu_app<'a>() -> Command<'a> {
options::indicator_style::SLASH, options::indicator_style::SLASH,
options::indicator_style::CLASSIFY, options::indicator_style::CLASSIFY,
options::INDICATOR_STYLE, options::INDICATOR_STYLE,
]), ])
.action(ArgAction::SetTrue),
) )
.arg( .arg(
//This still needs support for posix-* //This still needs support for posix-*
@ -1552,13 +1590,15 @@ pub fn uu_app<'a>() -> Command<'a> {
Arg::new(options::FULL_TIME) Arg::new(options::FULL_TIME)
.long(options::FULL_TIME) .long(options::FULL_TIME)
.overrides_with(options::FULL_TIME) .overrides_with(options::FULL_TIME)
.help("like -l --time-style=full-iso"), .help("like -l --time-style=full-iso")
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::CONTEXT) Arg::new(options::CONTEXT)
.short('Z') .short('Z')
.long(options::CONTEXT) .long(options::CONTEXT)
.help(CONTEXT_HELP_TEXT), .help(CONTEXT_HELP_TEXT)
.action(ArgAction::SetTrue),
) )
.arg( .arg(
Arg::new(options::GROUP_DIRECTORIES_FIRST) Arg::new(options::GROUP_DIRECTORIES_FIRST)
@ -1566,13 +1606,13 @@ pub fn uu_app<'a>() -> Command<'a> {
.help( .help(
"group directories before files; can be augmented with \ "group directories before files; can be augmented with \
a --sort option, but any use of --sort=none (-U) disables grouping", a --sort option, but any use of --sort=none (-U) disables grouping",
), )
.action(ArgAction::SetTrue),
) )
// Positional arguments // Positional arguments
.arg( .arg(
Arg::new(options::PATHS) Arg::new(options::PATHS)
.multiple_occurrences(true) .action(ArgAction::Append)
.takes_value(true)
.value_hint(clap::ValueHint::AnyPath) .value_hint(clap::ValueHint::AnyPath)
.value_parser(ValueParser::os_string()), .value_parser(ValueParser::os_string()),
) )

View file

@ -3248,6 +3248,7 @@ fn test_ls_multiple_a_A() {
} }
#[test] #[test]
#[cfg(feature = "ln")]
fn test_ls_quoting() { fn test_ls_quoting() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
@ -3267,6 +3268,7 @@ fn test_ls_quoting() {
} }
#[test] #[test]
#[cfg(feature = "ln")]
fn test_ls_quoting_color() { fn test_ls_quoting_color() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());