1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

Merge pull request #6174 from BenWiederhake/dev-all-partial-stringly-options

all: Accept shortcuts for stringly-enum options
This commit is contained in:
Terts Diepraam 2024-04-14 21:46:09 +02:00 committed by GitHub
commit 652c65f71a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 565 additions and 177 deletions

View file

@ -75,6 +75,9 @@ number of spaces representing a tab when determining the line length.
GNU `ls` provides two ways to use a long listing format: `-l` and `--format=long`. We support a GNU `ls` provides two ways to use a long listing format: `-l` and `--format=long`. We support a
third way: `--long`. third way: `--long`.
GNU `ls --sort=VALUE` only supports special non-default sort orders.
We support `--sort=name`, which makes it possible to override an earlier value.
## `du` ## `du`
`du` allows `birth` and `creation` as values for the `--time` argument to show the creation time. It `du` allows `birth` and `creation` as values for the `--time` argument to show the creation time. It

View file

@ -37,8 +37,8 @@ use uucore::{backup_control, update_control};
// requires these enum. // requires these enum.
pub use uucore::{backup_control::BackupMode, update_control::UpdateMode}; pub use uucore::{backup_control::BackupMode, update_control::UpdateMode};
use uucore::{ use uucore::{
format_usage, help_about, help_section, help_usage, prompt_yes, show_error, show_warning, format_usage, help_about, help_section, help_usage, prompt_yes,
util_name, shortcut_value_parser::ShortcutValueParser, show_error, show_warning, util_name,
}; };
use crate::copydir::copy_directory; use crate::copydir::copy_directory;
@ -396,22 +396,14 @@ static PRESERVABLE_ATTRIBUTES: &[&str] = &[
"ownership", "ownership",
"timestamps", "timestamps",
"context", "context",
"link",
"links", "links",
"xattr", "xattr",
"all", "all",
]; ];
#[cfg(not(unix))] #[cfg(not(unix))]
static PRESERVABLE_ATTRIBUTES: &[&str] = &[ static PRESERVABLE_ATTRIBUTES: &[&str] =
"mode", &["mode", "timestamps", "context", "links", "xattr", "all"];
"timestamps",
"context",
"link",
"links",
"xattr",
"all",
];
pub fn uu_app() -> Command { pub fn uu_app() -> Command {
const MODE_ARGS: &[&str] = &[ const MODE_ARGS: &[&str] = &[
@ -543,7 +535,7 @@ pub fn uu_app() -> Command {
.overrides_with_all(MODE_ARGS) .overrides_with_all(MODE_ARGS)
.require_equals(true) .require_equals(true)
.default_missing_value("always") .default_missing_value("always")
.value_parser(["auto", "always", "never"]) .value_parser(ShortcutValueParser::new(["auto", "always", "never"]))
.num_args(0..=1) .num_args(0..=1)
.help("control clone/CoW copies. See below"), .help("control clone/CoW copies. See below"),
) )
@ -559,9 +551,7 @@ pub fn uu_app() -> Command {
.long(options::PRESERVE) .long(options::PRESERVE)
.action(ArgAction::Append) .action(ArgAction::Append)
.use_value_delimiter(true) .use_value_delimiter(true)
.value_parser(clap::builder::PossibleValuesParser::new( .value_parser(ShortcutValueParser::new(PRESERVABLE_ATTRIBUTES))
PRESERVABLE_ATTRIBUTES,
))
.num_args(0..) .num_args(0..)
.require_equals(true) .require_equals(true)
.value_name("ATTR_LIST") .value_name("ATTR_LIST")
@ -655,7 +645,7 @@ pub fn uu_app() -> Command {
Arg::new(options::SPARSE) Arg::new(options::SPARSE)
.long(options::SPARSE) .long(options::SPARSE)
.value_name("WHEN") .value_name("WHEN")
.value_parser(["never", "auto", "always"]) .value_parser(ShortcutValueParser::new(["never", "auto", "always"]))
.help("control creation of sparse files. See below"), .help("control creation of sparse files. See below"),
) )
// TODO: implement the following args // TODO: implement the following args

View file

@ -4,7 +4,7 @@
// file that was distributed with this source code. // file that was distributed with this source code.
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command}; use clap::{builder::PossibleValue, crate_version, Arg, ArgAction, ArgMatches, Command};
use glob::Pattern; use glob::Pattern;
use std::collections::HashSet; use std::collections::HashSet;
use std::env; use std::env;
@ -30,6 +30,7 @@ use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError};
use uucore::line_ending::LineEnding; use uucore::line_ending::LineEnding;
use uucore::parse_glob; use uucore::parse_glob;
use uucore::parse_size::{parse_size_u64, ParseSizeError}; use uucore::parse_size::{parse_size_u64, ParseSizeError};
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::{format_usage, help_about, help_section, help_usage, show, show_error, show_warning}; use uucore::{format_usage, help_about, help_section, help_usage, show, show_error, show_warning};
#[cfg(windows)] #[cfg(windows)]
use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::Foundation::HANDLE;
@ -1040,7 +1041,11 @@ pub fn uu_app() -> Command {
.value_name("WORD") .value_name("WORD")
.require_equals(true) .require_equals(true)
.num_args(0..) .num_args(0..)
.value_parser(["atime", "access", "use", "ctime", "status", "birth", "creation"]) .value_parser(ShortcutValueParser::new([
PossibleValue::new("atime").alias("access").alias("use"),
PossibleValue::new("ctime").alias("status"),
PossibleValue::new("creation").alias("birth"),
]))
.help( .help(
"show time of the last modification of any file in the \ "show time of the last modification of any file in the \
directory, or any of its subdirectories. If WORD is given, show time as WORD instead \ directory, or any of its subdirectories. If WORD is given, show time as WORD instead \

View file

@ -6,7 +6,7 @@
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly // spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly
use clap::{ use clap::{
builder::{NonEmptyStringValueParser, ValueParser}, builder::{NonEmptyStringValueParser, PossibleValue, ValueParser},
crate_version, Arg, ArgAction, Command, crate_version, Arg, ArgAction, Command,
}; };
use glob::{MatchOptions, Pattern}; use glob::{MatchOptions, Pattern};
@ -62,6 +62,7 @@ use uucore::{
format_usage, format_usage,
fs::display_permissions, fs::display_permissions,
parse_size::parse_size_u64, parse_size::parse_size_u64,
shortcut_value_parser::ShortcutValueParser,
version_cmp::version_cmp, version_cmp::version_cmp,
}; };
use uucore::{help_about, help_section, help_usage, parse_glob, show, show_error, show_warning}; use uucore::{help_about, help_section, help_usage, parse_glob, show, show_error, show_warning};
@ -1203,7 +1204,7 @@ pub fn uu_app() -> Command {
Arg::new(options::FORMAT) Arg::new(options::FORMAT)
.long(options::FORMAT) .long(options::FORMAT)
.help("Set the display format.") .help("Set the display format.")
.value_parser([ .value_parser(ShortcutValueParser::new([
"long", "long",
"verbose", "verbose",
"single-column", "single-column",
@ -1212,7 +1213,7 @@ pub fn uu_app() -> Command {
"across", "across",
"horizontal", "horizontal",
"commas", "commas",
]) ]))
.hide_possible_values(true) .hide_possible_values(true)
.require_equals(true) .require_equals(true)
.overrides_with_all([ .overrides_with_all([
@ -1303,9 +1304,11 @@ pub fn uu_app() -> Command {
Arg::new(options::HYPERLINK) Arg::new(options::HYPERLINK)
.long(options::HYPERLINK) .long(options::HYPERLINK)
.help("hyperlink file names WHEN") .help("hyperlink file names WHEN")
.value_parser([ .value_parser(ShortcutValueParser::new([
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", PossibleValue::new("always").alias("yes").alias("force"),
]) PossibleValue::new("auto").alias("tty").alias("if-tty"),
PossibleValue::new("never").alias("no").alias("none"),
]))
.require_equals(true) .require_equals(true)
.num_args(0..=1) .num_args(0..=1)
.default_missing_value("always") .default_missing_value("always")
@ -1351,15 +1354,15 @@ pub fn uu_app() -> Command {
Arg::new(options::QUOTING_STYLE) Arg::new(options::QUOTING_STYLE)
.long(options::QUOTING_STYLE) .long(options::QUOTING_STYLE)
.help("Set quoting style.") .help("Set quoting style.")
.value_parser([ .value_parser(ShortcutValueParser::new([
"literal", PossibleValue::new("literal"),
"shell", PossibleValue::new("shell"),
"shell-always", PossibleValue::new("shell-escape"),
"shell-escape", PossibleValue::new("shell-always"),
"shell-escape-always", PossibleValue::new("shell-escape-always"),
"c", PossibleValue::new("c").alias("c-maybe"),
"escape", PossibleValue::new("escape"),
]) ]))
.overrides_with_all([ .overrides_with_all([
options::QUOTING_STYLE, options::QUOTING_STYLE,
options::quoting::LITERAL, options::quoting::LITERAL,
@ -1434,9 +1437,11 @@ pub fn uu_app() -> Command {
\tbirth time: birth, creation;", \tbirth time: birth, creation;",
) )
.value_name("field") .value_name("field")
.value_parser([ .value_parser(ShortcutValueParser::new([
"atime", "access", "use", "ctime", "status", "birth", "creation", PossibleValue::new("atime").alias("access").alias("use"),
]) PossibleValue::new("ctime").alias("status"),
PossibleValue::new("birth").alias("creation"),
]))
.hide_possible_values(true) .hide_possible_values(true)
.require_equals(true) .require_equals(true)
.overrides_with_all([options::TIME, options::time::ACCESS, options::time::CHANGE]), .overrides_with_all([options::TIME, options::time::ACCESS, options::time::CHANGE]),
@ -1496,7 +1501,7 @@ pub fn uu_app() -> Command {
.long(options::SORT) .long(options::SORT)
.help("Sort by <field>: name, none (-U), time (-t), size (-S), extension (-X) or width") .help("Sort by <field>: name, none (-U), time (-t), size (-S), extension (-X) or width")
.value_name("field") .value_name("field")
.value_parser(["name", "none", "time", "size", "version", "extension", "width"]) .value_parser(ShortcutValueParser::new(["name", "none", "time", "size", "version", "extension", "width"]))
.require_equals(true) .require_equals(true)
.overrides_with_all([ .overrides_with_all([
options::SORT, options::SORT,
@ -1744,9 +1749,11 @@ pub fn uu_app() -> Command {
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.")
.value_parser([ .value_parser(ShortcutValueParser::new([
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", PossibleValue::new("always").alias("yes").alias("force"),
]) PossibleValue::new("auto").alias("tty").alias("if-tty"),
PossibleValue::new("never").alias("no").alias("none"),
]))
.require_equals(true) .require_equals(true)
.num_args(0..=1), .num_args(0..=1),
) )
@ -1757,7 +1764,7 @@ pub fn uu_app() -> Command {
"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)",
) )
.value_parser(["none", "slash", "file-type", "classify"]) .value_parser(ShortcutValueParser::new(["none", "slash", "file-type", "classify"]))
.overrides_with_all([ .overrides_with_all([
options::indicator_style::FILE_TYPE, options::indicator_style::FILE_TYPE,
options::indicator_style::SLASH, options::indicator_style::SLASH,
@ -1788,9 +1795,11 @@ pub fn uu_app() -> Command {
--dereference-command-line-symlink-to-dir options are specified.", --dereference-command-line-symlink-to-dir options are specified.",
) )
.value_name("when") .value_name("when")
.value_parser([ .value_parser(ShortcutValueParser::new([
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", PossibleValue::new("always").alias("yes").alias("force"),
]) PossibleValue::new("auto").alias("tty").alias("if-tty"),
PossibleValue::new("never").alias("no").alias("none"),
]))
.default_missing_value("always") .default_missing_value("always")
.require_equals(true) .require_equals(true)
.num_args(0..=1) .num_args(0..=1)

View file

@ -15,6 +15,7 @@ use units::{IEC_BASES, SI_BASES};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::UResult; use uucore::error::UResult;
use uucore::ranges::Range; use uucore::ranges::Range;
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::{format_usage, help_about, help_section, help_usage, show, show_error}; use uucore::{format_usage, help_about, help_section, help_usage, show, show_error};
pub mod errors; pub mod errors;
@ -340,7 +341,13 @@ pub fn uu_app() -> Command {
.help("use METHOD for rounding when scaling") .help("use METHOD for rounding when scaling")
.value_name("METHOD") .value_name("METHOD")
.default_value("from-zero") .default_value("from-zero")
.value_parser(["up", "down", "from-zero", "towards-zero", "nearest"]), .value_parser(ShortcutValueParser::new([
"up",
"down",
"from-zero",
"towards-zero",
"nearest",
])),
) )
.arg( .arg(
Arg::new(options::SUFFIX) Arg::new(options::SUFFIX)

View file

@ -43,6 +43,7 @@ use clap::{crate_version, parser::ValueSource, Arg, ArgMatches, Command};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError}; use uucore::error::{UResult, USimpleError};
use uucore::parse_size::ParseSizeError; use uucore::parse_size::ParseSizeError;
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::{format_usage, help_about, help_section, help_usage, show_error, show_warning}; use uucore::{format_usage, help_about, help_section, help_usage, show_error, show_warning};
const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes
@ -287,7 +288,7 @@ pub fn uu_app() -> Command {
Arg::new(options::ENDIAN) Arg::new(options::ENDIAN)
.long(options::ENDIAN) .long(options::ENDIAN)
.help("byte order to use for multi-byte formats") .help("byte order to use for multi-byte formats")
.value_parser(["big", "little"]) .value_parser(ShortcutValueParser::new(["big", "little"]))
.value_name("big|little"), .value_name("big|little"),
) )
.arg( .arg(

View file

@ -17,6 +17,7 @@ use std::path::{Path, PathBuf};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::parse_size::parse_size_u64; use uucore::parse_size::parse_size_u64;
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::{format_usage, help_about, help_section, help_usage, show_error, show_if_err}; use uucore::{format_usage, help_about, help_section, help_usage, show_error, show_if_err};
const ABOUT: &str = help_about!("shred.md"); const ABOUT: &str = help_about!("shred.md");
@ -315,11 +316,11 @@ pub fn uu_app() -> Command {
Arg::new(options::REMOVE) Arg::new(options::REMOVE)
.long(options::REMOVE) .long(options::REMOVE)
.value_name("HOW") .value_name("HOW")
.value_parser([ .value_parser(ShortcutValueParser::new([
options::remove::UNLINK, options::remove::UNLINK,
options::remove::WIPE, options::remove::WIPE,
options::remove::WIPESYNC, options::remove::WIPESYNC,
]) ]))
.num_args(0..=1) .num_args(0..=1)
.require_equals(true) .require_equals(true)
.default_missing_value(options::remove::WIPESYNC) .default_missing_value(options::remove::WIPESYNC)

View file

@ -43,6 +43,7 @@ use uucore::display::Quotable;
use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError}; use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError};
use uucore::line_ending::LineEnding; use uucore::line_ending::LineEnding;
use uucore::parse_size::{ParseSizeError, Parser}; use uucore::parse_size::{ParseSizeError, Parser};
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::version_cmp::version_cmp; use uucore::version_cmp::version_cmp;
use uucore::{format_usage, help_about, help_section, help_usage}; use uucore::{format_usage, help_about, help_section, help_usage};
@ -1297,14 +1298,14 @@ pub fn uu_app() -> Command {
.arg( .arg(
Arg::new(options::modes::SORT) Arg::new(options::modes::SORT)
.long(options::modes::SORT) .long(options::modes::SORT)
.value_parser([ .value_parser(ShortcutValueParser::new([
"general-numeric", "general-numeric",
"human-numeric", "human-numeric",
"month", "month",
"numeric", "numeric",
"version", "version",
"random", "random",
]) ]))
.conflicts_with_all(options::modes::ALL_SORT_MODES), .conflicts_with_all(options::modes::ALL_SORT_MODES),
) )
.arg(make_sort_mode_arg( .arg(make_sort_mode_arg(
@ -1363,11 +1364,11 @@ pub fn uu_app() -> Command {
.long(options::check::CHECK) .long(options::check::CHECK)
.require_equals(true) .require_equals(true)
.num_args(0..) .num_args(0..)
.value_parser([ .value_parser(ShortcutValueParser::new([
options::check::SILENT, options::check::SILENT,
options::check::QUIET, options::check::QUIET,
options::check::DIAGNOSE_FIRST, options::check::DIAGNOSE_FIRST,
]) ]))
.conflicts_with(options::OUTPUT) .conflicts_with(options::OUTPUT)
.help("check for sorted input; do not sort"), .help("check for sorted input; do not sort"),
) )

View file

@ -16,6 +16,7 @@ use std::io::IsTerminal;
use std::time::Duration; use std::time::Duration;
use uucore::error::{UResult, USimpleError, UUsageError}; use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::parse_size::{parse_size_u64, ParseSizeError}; use uucore::parse_size::{parse_size_u64, ParseSizeError};
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::{format_usage, help_about, help_usage, show_warning}; use uucore::{format_usage, help_about, help_usage, show_warning};
const ABOUT: &str = help_about!("tail.md"); const ABOUT: &str = help_about!("tail.md");
@ -494,7 +495,7 @@ pub fn uu_app() -> Command {
.default_missing_value("descriptor") .default_missing_value("descriptor")
.num_args(0..=1) .num_args(0..=1)
.require_equals(true) .require_equals(true)
.value_parser(["descriptor", "name"]) .value_parser(ShortcutValueParser::new(["descriptor", "name"]))
.overrides_with(options::FOLLOW) .overrides_with(options::FOLLOW)
.help("Print the file as it grows"), .help("Print the file as it grows"),
) )

View file

@ -9,6 +9,7 @@ use std::io::{copy, stdin, stdout, Error, ErrorKind, Read, Result, Write};
use std::path::PathBuf; use std::path::PathBuf;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::UResult; use uucore::error::UResult;
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::{format_usage, help_about, help_section, help_usage, show_error}; use uucore::{format_usage, help_about, help_section, help_usage, show_error};
// spell-checker:ignore nopipe // spell-checker:ignore nopipe
@ -119,7 +120,7 @@ pub fn uu_app() -> Command {
.long(options::OUTPUT_ERROR) .long(options::OUTPUT_ERROR)
.require_equals(true) .require_equals(true)
.num_args(0..=1) .num_args(0..=1)
.value_parser([ .value_parser(ShortcutValueParser::new([
PossibleValue::new("warn") PossibleValue::new("warn")
.help("produce warnings for errors writing to any output"), .help("produce warnings for errors writing to any output"),
PossibleValue::new("warn-nopipe") PossibleValue::new("warn-nopipe")
@ -127,7 +128,7 @@ pub fn uu_app() -> Command {
PossibleValue::new("exit").help("exit on write errors to any output"), PossibleValue::new("exit").help("exit on write errors to any output"),
PossibleValue::new("exit-nopipe") PossibleValue::new("exit-nopipe")
.help("exit on write errors to any output that are not pipe errors (equivalent to exit on non-unix platforms)"), .help("exit on write errors to any output that are not pipe errors (equivalent to exit on non-unix platforms)"),
]) ]))
.help("set write error behavior") .help("set write error behavior")
.conflicts_with(options::IGNORE_PIPE_ERRORS), .conflicts_with(options::IGNORE_PIPE_ERRORS),
) )

View file

@ -10,7 +10,7 @@ use chrono::{
DateTime, Datelike, Duration, Local, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, DateTime, Datelike, Duration, Local, LocalResult, NaiveDate, NaiveDateTime, NaiveTime,
TimeZone, Timelike, TimeZone, Timelike,
}; };
use clap::builder::ValueParser; use clap::builder::{PossibleValue, ValueParser};
use clap::{crate_version, Arg, ArgAction, ArgGroup, ArgMatches, Command}; use clap::{crate_version, Arg, ArgAction, ArgGroup, ArgMatches, Command};
use filetime::{set_file_times, set_symlink_file_times, FileTime}; use filetime::{set_file_times, set_symlink_file_times, FileTime};
use std::ffi::OsString; use std::ffi::OsString;
@ -18,6 +18,7 @@ use std::fs::{self, File};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError}; use uucore::error::{FromIo, UResult, USimpleError};
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::{format_usage, help_about, help_usage, show}; use uucore::{format_usage, help_about, help_usage, show};
const ABOUT: &str = help_about!("touch.md"); const ABOUT: &str = help_about!("touch.md");
@ -216,7 +217,10 @@ pub fn uu_app() -> Command {
equivalent to -m", equivalent to -m",
) )
.value_name("WORD") .value_name("WORD")
.value_parser(["access", "atime", "use", "modify", "mtime"]), .value_parser(ShortcutValueParser::new([
PossibleValue::new("atime").alias("access").alias("use"),
PossibleValue::new("mtime").alias("modify"),
])),
) )
.arg( .arg(
Arg::new(ARG_FILES) Arg::new(ARG_FILES)

View file

@ -14,6 +14,7 @@ use std::num::IntErrorKind;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UError, UResult, USimpleError}; use uucore::error::{FromIo, UError, UResult, USimpleError};
use uucore::posix::{posix_version, OBSOLETE}; use uucore::posix::{posix_version, OBSOLETE};
use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::{format_usage, help_about, help_section, help_usage}; use uucore::{format_usage, help_about, help_section, help_usage};
const ABOUT: &str = help_about!("uniq.md"); const ABOUT: &str = help_about!("uniq.md");
@ -609,11 +610,11 @@ pub fn uu_app() -> Command {
Arg::new(options::ALL_REPEATED) Arg::new(options::ALL_REPEATED)
.short('D') .short('D')
.long(options::ALL_REPEATED) .long(options::ALL_REPEATED)
.value_parser([ .value_parser(ShortcutValueParser::new([
"none", "none",
"prepend", "prepend",
"separate" "separate"
]) ]))
.help("print all duplicate lines. Delimiting is done with blank lines. [default: none]") .help("print all duplicate lines. Delimiting is done with blank lines. [default: none]")
.value_name("delimit-method") .value_name("delimit-method")
.num_args(0..=1) .num_args(0..=1)
@ -623,12 +624,12 @@ pub fn uu_app() -> Command {
.arg( .arg(
Arg::new(options::GROUP) Arg::new(options::GROUP)
.long(options::GROUP) .long(options::GROUP)
.value_parser([ .value_parser(ShortcutValueParser::new([
"separate", "separate",
"prepend", "prepend",
"append", "append",
"both", "both",
]) ]))
.help("show all items, separating groups with an empty line. [default: separate]") .help("show all items, separating groups with an empty line. [default: separate]")
.value_name("group-method") .value_name("group-method")
.num_args(0..=1) .num_args(0..=1)

View file

@ -29,6 +29,7 @@ use uucore::{
error::{FromIo, UError, UResult}, error::{FromIo, UError, UResult},
format_usage, help_about, help_usage, format_usage, help_about, help_usage,
quoting_style::{escape_name, QuotingStyle}, quoting_style::{escape_name, QuotingStyle},
shortcut_value_parser::ShortcutValueParser,
show, show,
}; };
@ -439,7 +440,9 @@ pub fn uu_app() -> Command {
.arg( .arg(
Arg::new(options::TOTAL) Arg::new(options::TOTAL)
.long(options::TOTAL) .long(options::TOTAL)
.value_parser(["auto", "always", "only", "never"]) .value_parser(ShortcutValueParser::new([
"auto", "always", "only", "never",
]))
.value_name("WHEN") .value_name("WHEN")
.hide_possible_values(true) .hide_possible_values(true)
.help(concat!( .help(concat!(

View file

@ -61,6 +61,7 @@ pub enum UpdateMode {
} }
pub mod arguments { pub mod arguments {
use crate::shortcut_value_parser::ShortcutValueParser;
use clap::ArgAction; use clap::ArgAction;
pub static OPT_UPDATE: &str = "update"; pub static OPT_UPDATE: &str = "update";
@ -71,7 +72,7 @@ pub mod arguments {
clap::Arg::new(OPT_UPDATE) clap::Arg::new(OPT_UPDATE)
.long("update") .long("update")
.help("move only when the SOURCE file is newer than the destination file or when the destination file is missing") .help("move only when the SOURCE file is newer than the destination file or when the destination file is missing")
.value_parser(["none", "all", "older"]) .value_parser(ShortcutValueParser::new(["none", "all", "older"]))
.num_args(0..=1) .num_args(0..=1)
.default_missing_value("older") .default_missing_value("older")
.require_equals(true) .require_equals(true)

View file

@ -66,7 +66,7 @@ impl TypedValueParser for ShortcutValueParser {
let matched_values: Vec<_> = self let matched_values: Vec<_> = self
.0 .0
.iter() .iter()
.filter(|x| x.get_name().starts_with(value)) .filter(|x| x.get_name_and_aliases().any(|name| name.starts_with(value)))
.collect(); .collect();
match matched_values.len() { match matched_values.len() {
@ -101,7 +101,7 @@ where
mod tests { mod tests {
use std::ffi::OsStr; use std::ffi::OsStr;
use clap::{builder::TypedValueParser, error::ErrorKind, Command}; use clap::{builder::PossibleValue, builder::TypedValueParser, error::ErrorKind, Command};
use super::ShortcutValueParser; use super::ShortcutValueParser;
@ -166,4 +166,30 @@ mod tests {
let result = parser.parse_ref(&cmd, None, OsStr::from_bytes(&[0xc3, 0x28])); let result = parser.parse_ref(&cmd, None, OsStr::from_bytes(&[0xc3, 0x28]));
assert_eq!(ErrorKind::InvalidUtf8, result.unwrap_err().kind()); assert_eq!(ErrorKind::InvalidUtf8, result.unwrap_err().kind());
} }
#[test]
fn test_ambiguous_word_same_meaning() {
let cmd = Command::new("cmd");
let parser = ShortcutValueParser::new([
PossibleValue::new("atime").alias("access"),
"status".into(),
]);
// Even though "a" is ambiguous (it might mean "atime" or "access"),
// the meaning is uniquely defined, therefore accept it.
let atime_values = [
// spell-checker:disable-next-line
"atime", "atim", "at", "a", "access", "acces", "acce", "acc", "ac",
];
// spell-checker:disable-next-line
let status_values = ["status", "statu", "stat", "sta", "st", "st"];
for value in atime_values {
let result = parser.parse_ref(&cmd, None, OsStr::new(value));
assert_eq!("atime", result.unwrap());
}
for value in status_values {
let result = parser.parse_ref(&cmd, None, OsStr::new(value));
assert_eq!("status", result.unwrap());
}
}
} }

View file

@ -2,7 +2,7 @@
// //
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs xattrs // spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs neve ROOTDIR USERDIR procfs outfile uufs xattrs
use crate::common::util::TestScenario; use crate::common::util::TestScenario;
#[cfg(not(windows))] #[cfg(not(windows))]
@ -286,16 +286,18 @@ fn test_cp_arg_update_interactive_error() {
#[test] #[test]
fn test_cp_arg_update_none() { fn test_cp_arg_update_none() {
let (at, mut ucmd) = at_and_ucmd!(); for argument in ["--update=none", "--update=non", "--update=n"] {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg(TEST_HELLO_WORLD_SOURCE) ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.arg("--update=none") .arg(argument)
.succeeds() .succeeds()
.no_stderr() .no_stderr()
.no_stdout(); .no_stdout();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
}
} }
#[test] #[test]
@ -1402,29 +1404,28 @@ fn test_cp_preserve_no_args_before_opts() {
#[test] #[test]
fn test_cp_preserve_all() { fn test_cp_preserve_all() {
let (at, mut ucmd) = at_and_ucmd!(); for argument in ["--preserve=all", "--preserve=al"] {
let src_file = "a"; let (at, mut ucmd) = at_and_ucmd!();
let dst_file = "b"; let src_file = "a";
let dst_file = "b";
// Prepare the source file // Prepare the source file
at.touch(src_file); at.touch(src_file);
#[cfg(unix)] #[cfg(unix)]
at.set_mode(src_file, 0o0500); at.set_mode(src_file, 0o0500);
// TODO: create a destination that does not allow copying of xattr and context // TODO: create a destination that does not allow copying of xattr and context
// Copy // Copy
ucmd.arg(src_file) ucmd.arg(src_file).arg(dst_file).arg(argument).succeeds();
.arg(dst_file)
.arg("--preserve=all")
.succeeds();
#[cfg(all(unix, not(target_os = "freebsd")))] #[cfg(all(unix, not(target_os = "freebsd")))]
{ {
// Assert that the mode, ownership, and timestamps are preserved // Assert that the mode, ownership, and timestamps are preserved
// NOTICE: the ownership is not modified on the src file, because that requires root permissions // NOTICE: the ownership is not modified on the src file, because that requires root permissions
let metadata_src = at.metadata(src_file); let metadata_src = at.metadata(src_file);
let metadata_dst = at.metadata(dst_file); let metadata_dst = at.metadata(dst_file);
assert_metadata_eq!(metadata_src, metadata_dst); assert_metadata_eq!(metadata_src, metadata_dst);
}
} }
} }
@ -1472,6 +1473,35 @@ fn test_cp_preserve_all_context_fails_on_non_selinux() {
.fails(); .fails();
} }
#[test]
fn test_cp_preserve_link_parses() {
// TODO: Also check whether --preserve=link did the right thing!
for argument in [
"--preserve=links",
"--preserve=link",
"--preserve=li",
"--preserve=l",
] {
new_ucmd!()
.arg(argument)
.arg(TEST_COPY_FROM_FOLDER_FILE)
.arg(TEST_HELLO_WORLD_DEST)
.succeeds()
.no_output();
}
}
#[test]
fn test_cp_preserve_invalid_rejected() {
new_ucmd!()
.arg("--preserve=invalid-value")
.arg(TEST_COPY_FROM_FOLDER_FILE)
.arg(TEST_HELLO_WORLD_DEST)
.fails()
.code_is(1)
.no_stdout();
}
#[test] #[test]
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
#[cfg(disabled_until_fixed)] // FIXME: the test looks to .succeed on android #[cfg(disabled_until_fixed)] // FIXME: the test looks to .succeed on android
@ -2196,14 +2226,16 @@ fn test_cp_reflink_none() {
#[test] #[test]
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
fn test_cp_reflink_never() { fn test_cp_reflink_never() {
let (at, mut ucmd) = at_and_ucmd!(); for argument in ["--reflink=never", "--reflink=neve", "--reflink=n"] {
ucmd.arg("--reflink=never") let (at, mut ucmd) = at_and_ucmd!();
.arg(TEST_HELLO_WORLD_SOURCE) ucmd.arg(argument)
.arg(TEST_EXISTING_FILE) .arg(TEST_HELLO_WORLD_SOURCE)
.succeeds(); .arg(TEST_EXISTING_FILE)
.succeeds();
// Check the content of the destination file // Check the content of the destination file
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
}
} }
#[test] #[test]
@ -2286,19 +2318,21 @@ fn test_cp_sparse_never_empty() {
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
#[test] #[test]
fn test_cp_sparse_always_empty() { fn test_cp_sparse_always_empty() {
let (at, mut ucmd) = at_and_ucmd!(); for argument in ["--sparse=always", "--sparse=alway", "--sparse=al"] {
let (at, mut ucmd) = at_and_ucmd!();
const BUFFER_SIZE: usize = 4096 * 4; const BUFFER_SIZE: usize = 4096 * 4;
let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
at.make_file("src_file1"); at.make_file("src_file1");
at.write_bytes("src_file1", &buf); at.write_bytes("src_file1", &buf);
ucmd.args(&["--sparse=always", "src_file1", "dst_file_sparse"]) ucmd.args(&[argument, "src_file1", "dst_file_sparse"])
.succeeds(); .succeeds();
assert_eq!(at.read_bytes("dst_file_sparse"), buf); assert_eq!(at.read_bytes("dst_file_sparse"), buf);
assert_eq!(at.metadata("dst_file_sparse").blocks(), 0); assert_eq!(at.metadata("dst_file_sparse").blocks(), 0);
}
} }
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]

View file

@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore (paths) sublink subwords azerty azeaze xcwww azeaz amaz azea qzerty tazerty tsublink testfile1 testfile2 filelist testdir testfile // spell-checker:ignore (paths) atim sublink subwords azerty azeaze xcwww azeaz amaz azea qzerty tazerty tsublink testfile1 testfile2 filelist testdir testfile
#[cfg(not(windows))] #[cfg(not(windows))]
use regex::Regex; use regex::Regex;
@ -576,13 +576,15 @@ fn test_du_time() {
.succeeds(); .succeeds();
result.stdout_only("0\t2016-06-16 00:00\tdate_test\n"); result.stdout_only("0\t2016-06-16 00:00\tdate_test\n");
let result = ts for argument in ["--time=atime", "--time=atim", "--time=a"] {
.ucmd() let result = ts
.env("TZ", "UTC") .ucmd()
.arg("--time=atime") .env("TZ", "UTC")
.arg("date_test") .arg(argument)
.succeeds(); .arg("date_test")
result.stdout_only("0\t2015-05-15 00:00\tdate_test\n"); .succeeds();
result.stdout_only("0\t2015-05-15 00:00\tdate_test\n");
}
let result = ts let result = ts
.ucmd() .ucmd()

View file

@ -27,6 +27,7 @@ const LONG_ARGS: &[&str] = &[
"-l", "-l",
"--long", "--long",
"--format=long", "--format=long",
"--format=lon",
"--for=long", "--for=long",
"--format=verbose", "--format=verbose",
"--for=verbose", "--for=verbose",
@ -35,6 +36,7 @@ const LONG_ARGS: &[&str] = &[
const ACROSS_ARGS: &[&str] = &[ const ACROSS_ARGS: &[&str] = &[
"-x", "-x",
"--format=across", "--format=across",
"--format=acr",
"--format=horizontal", "--format=horizontal",
"--for=across", "--for=across",
"--for=horizontal", "--for=horizontal",
@ -999,6 +1001,8 @@ fn test_ls_zero() {
let ignored_opts = [ let ignored_opts = [
"--quoting-style=c", "--quoting-style=c",
"--color=always", "--color=always",
"--color=alway",
"--color=al",
"-m", "-m",
"--hide-control-chars", "--hide-control-chars",
]; ];
@ -1601,6 +1605,24 @@ fn test_ls_deref() {
.succeeds(); .succeeds();
assert!(re.is_match(result.stdout_str().trim())); assert!(re.is_match(result.stdout_str().trim()));
let result = scene
.ucmd()
.arg("-l")
.arg("--color=neve") // spell-checker:disable-line
.arg("test-long")
.arg("test-long.link")
.succeeds();
assert!(re.is_match(result.stdout_str().trim()));
let result = scene
.ucmd()
.arg("-l")
.arg("--color=n")
.arg("test-long")
.arg("test-long.link")
.succeeds();
assert!(re.is_match(result.stdout_str().trim()));
let result = scene let result = scene
.ucmd() .ucmd()
.arg("-L") .arg("-L")
@ -1676,6 +1698,10 @@ fn test_ls_sort_none() {
// Order is not specified so we just check that it doesn't // Order is not specified so we just check that it doesn't
// give any errors. // give any errors.
scene.ucmd().arg("--sort=none").succeeds(); scene.ucmd().arg("--sort=none").succeeds();
scene.ucmd().arg("--sort=non").succeeds();
scene.ucmd().arg("--sort=no").succeeds();
// scene.ucmd().arg("--sort=n").succeeds();
// We refuse to accept "--sort=n", since this is too confusable with "--sort=name", which is our own extension.
scene.ucmd().arg("-U").succeeds(); scene.ucmd().arg("-U").succeeds();
} }
@ -1693,6 +1719,16 @@ fn test_ls_sort_name() {
.arg("--sort=name") .arg("--sort=name")
.succeeds() .succeeds()
.stdout_is("test-1\ntest-2\ntest-3\n"); .stdout_is("test-1\ntest-2\ntest-3\n");
scene
.ucmd()
.arg("--sort=nam")
.succeeds()
.stdout_is("test-1\ntest-2\ntest-3\n");
scene
.ucmd()
.arg("--sort=na")
.succeeds()
.stdout_is("test-1\ntest-2\ntest-3\n");
let scene_dot = TestScenario::new(util_name!()); let scene_dot = TestScenario::new(util_name!());
let at = &scene_dot.fixtures; let at = &scene_dot.fixtures;
@ -1729,6 +1765,16 @@ fn test_ls_sort_width() {
.arg("--sort=width") .arg("--sort=width")
.succeeds() .succeeds()
.stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n"); .stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n");
scene
.ucmd()
.arg("--sort=widt") // spell-checker:disable-line
.succeeds()
.stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n");
scene
.ucmd()
.arg("--sort=w")
.succeeds()
.stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n");
} }
#[test] #[test]
@ -1757,6 +1803,12 @@ fn test_ls_order_size() {
let result = scene.ucmd().arg("--sort=size").succeeds(); let result = scene.ucmd().arg("--sort=size").succeeds();
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n"); result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
let result = scene.ucmd().arg("--sort=siz").succeeds();
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
let result = scene.ucmd().arg("--sort=s").succeeds();
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
let result = scene.ucmd().arg("--sort=size").arg("-r").succeeds(); let result = scene.ucmd().arg("--sort=size").arg("-r").succeeds();
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n"); result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
} }
@ -1961,7 +2013,14 @@ fn test_ls_order_time() {
// 3 was accessed last in the read // 3 was accessed last in the read
// So the order should be 2 3 4 1 // So the order should be 2 3 4 1
for arg in ["-u", "--time=atime", "--time=access", "--time=use"] { for arg in [
"-u",
"--time=atime",
"--time=atim", // spell-checker:disable-line
"--time=a",
"--time=access",
"--time=use",
] {
let result = scene.ucmd().arg("-t").arg(arg).succeeds(); let result = scene.ucmd().arg("-t").arg(arg).succeeds();
at.open("test-3").metadata().unwrap().accessed().unwrap(); at.open("test-3").metadata().unwrap().accessed().unwrap();
at.open("test-4").metadata().unwrap().accessed().unwrap(); at.open("test-4").metadata().unwrap().accessed().unwrap();
@ -2216,12 +2275,16 @@ fn test_ls_indicator_style() {
for opt in [ for opt in [
"--indicator-style=classify", "--indicator-style=classify",
"--ind=classify", "--ind=classify",
"--indicator-style=clas", // spell-checker:disable-line
"--indicator-style=c",
"--indicator-style=file-type", "--indicator-style=file-type",
"--ind=file-type", "--ind=file-type",
"--indicator-style=slash", "--indicator-style=slash",
"--ind=slash", "--ind=slash",
"--classify", "--classify",
"--classify=always", "--classify=always",
"--classify=alway", // spell-checker:disable-line
"--classify=al",
"--classify=yes", "--classify=yes",
"--classify=force", "--classify=force",
"--class", "--class",
@ -2236,10 +2299,13 @@ fn test_ls_indicator_style() {
// Classify, Indicator options should not contain any indicators when value is none. // Classify, Indicator options should not contain any indicators when value is none.
for opt in [ for opt in [
"--indicator-style=none", "--indicator-style=none",
"--indicator-style=n",
"--ind=none", "--ind=none",
"--classify=none", "--classify=none",
"--classify=never", "--classify=never",
"--classify=non",
"--classify=no", "--classify=no",
"--classify=n",
] { ] {
// Verify that there are no indicators for any of the file types. // Verify that there are no indicators for any of the file types.
scene scene
@ -2553,6 +2619,12 @@ fn test_ls_version_sort() {
expected expected
); );
let result = scene.ucmd().arg("-1").arg("--sort=v").succeeds();
assert_eq!(
result.stdout_str().split('\n').collect::<Vec<_>>(),
expected
);
let result = scene.ucmd().arg("-a1v").succeeds(); let result = scene.ucmd().arg("-a1v").succeeds();
expected.insert(expected.len() - 1, ".."); expected.insert(expected.len() - 1, "..");
expected.insert(0, "."); expected.insert(0, ".");
@ -2589,19 +2661,27 @@ fn test_ls_quoting_style() {
for (arg, correct) in [ for (arg, correct) in [
("--quoting-style=literal", "one?two"), ("--quoting-style=literal", "one?two"),
("--quoting-style=litera", "one?two"), // spell-checker:disable-line
("--quoting-style=li", "one?two"),
("-N", "one?two"), ("-N", "one?two"),
("--literal", "one?two"), ("--literal", "one?two"),
("--l", "one?two"), ("--l", "one?two"),
("--quoting-style=c", "\"one\\ntwo\""), ("--quoting-style=c", "\"one\\ntwo\""),
("--quoting-style=c-", "\"one\\ntwo\""),
("--quoting-style=c-maybe", "\"one\\ntwo\""),
("-Q", "\"one\\ntwo\""), ("-Q", "\"one\\ntwo\""),
("--quote-name", "\"one\\ntwo\""), ("--quote-name", "\"one\\ntwo\""),
("--quoting-style=escape", "one\\ntwo"), ("--quoting-style=escape", "one\\ntwo"),
("--quoting-style=escap", "one\\ntwo"), // spell-checker:disable-line
("-b", "one\\ntwo"), ("-b", "one\\ntwo"),
("--escape", "one\\ntwo"), ("--escape", "one\\ntwo"),
("--quoting-style=shell-escape", "'one'$'\\n''two'"), ("--quoting-style=shell-escape", "'one'$'\\n''two'"),
("--quoting-style=shell-escape-always", "'one'$'\\n''two'"), ("--quoting-style=shell-escape-always", "'one'$'\\n''two'"),
("--quoting-style=shell-escape-alway", "'one'$'\\n''two'"),
("--quoting-style=shell-escape-a", "'one'$'\\n''two'"),
("--quoting-style=shell", "one?two"), ("--quoting-style=shell", "one?two"),
("--quoting-style=shell-always", "'one?two'"), ("--quoting-style=shell-always", "'one?two'"),
("--quoting-style=shell-a", "'one?two'"),
] { ] {
scene scene
.ucmd() .ucmd()
@ -4244,11 +4324,18 @@ fn test_ls_hyperlink() {
.stdout_str() .stdout_str()
.contains(&format!("{path}{separator}{file}\x07{file}\x1b]8;;\x07"))); .contains(&format!("{path}{separator}{file}\x07{file}\x1b]8;;\x07")));
scene for argument in [
.ucmd() "--hyperlink=never",
.arg("--hyperlink=never") "--hyperlink=neve", // spell-checker:disable-line
.succeeds() "--hyperlink=ne", // spell-checker:disable-line
.stdout_is(format!("{file}\n")); "--hyperlink=n",
] {
scene
.ucmd()
.arg(argument)
.succeeds()
.stdout_is(format!("{file}\n"));
}
} }
// spell-checker: disable // spell-checker: disable

View file

@ -550,10 +550,14 @@ fn test_delimiter_with_padding_and_fields() {
fn test_round() { fn test_round() {
for (method, exp) in [ for (method, exp) in [
("from-zero", ["9.1K", "-9.1K", "9.1K", "-9.1K"]), ("from-zero", ["9.1K", "-9.1K", "9.1K", "-9.1K"]),
("from-zer", ["9.1K", "-9.1K", "9.1K", "-9.1K"]), // spell-checker:disable-line
("f", ["9.1K", "-9.1K", "9.1K", "-9.1K"]),
("towards-zero", ["9.0K", "-9.0K", "9.0K", "-9.0K"]), ("towards-zero", ["9.0K", "-9.0K", "9.0K", "-9.0K"]),
("up", ["9.1K", "-9.0K", "9.1K", "-9.0K"]), ("up", ["9.1K", "-9.0K", "9.1K", "-9.0K"]),
("down", ["9.0K", "-9.1K", "9.0K", "-9.1K"]), ("down", ["9.0K", "-9.1K", "9.0K", "-9.1K"]),
("nearest", ["9.0K", "-9.0K", "9.1K", "-9.1K"]), ("nearest", ["9.0K", "-9.0K", "9.1K", "-9.1K"]),
("near", ["9.0K", "-9.0K", "9.1K", "-9.1K"]),
("n", ["9.0K", "-9.0K", "9.1K", "-9.1K"]),
] { ] {
new_ucmd!() new_ucmd!()
.args(&[ .args(&[

View file

@ -53,6 +53,20 @@ fn test_file() {
.no_stderr() .no_stderr()
.stdout_is(unindent(ALPHA_OUT)); .stdout_is(unindent(ALPHA_OUT));
new_ucmd!()
.arg("--endian=littl") // spell-checker:disable-line
.arg(file.as_os_str())
.succeeds()
.no_stderr()
.stdout_is(unindent(ALPHA_OUT));
new_ucmd!()
.arg("--endian=l")
.arg(file.as_os_str())
.succeeds()
.no_stderr()
.stdout_is(unindent(ALPHA_OUT));
// Ensure that default format matches `-t o2`, and that `-t` does not absorb file argument // Ensure that default format matches `-t o2`, and that `-t` does not absorb file argument
new_ucmd!() new_ucmd!()
.arg("--endian=little") .arg("--endian=little")
@ -463,6 +477,16 @@ fn test_big_endian() {
.run_piped_stdin(&input[..]) .run_piped_stdin(&input[..])
.no_stderr() .no_stderr()
.success() .success()
.stdout_is(&expected_output);
new_ucmd!()
.arg("--endian=b")
.arg("-F")
.arg("-f")
.arg("-X")
.arg("-x")
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output); .stdout_is(expected_output);
} }

View file

@ -17,6 +17,11 @@ fn test_invalid_remove_arg() {
new_ucmd!().arg("--remove=unknown").fails().code_is(1); new_ucmd!().arg("--remove=unknown").fails().code_is(1);
} }
#[test]
fn test_ambiguous_remove_arg() {
new_ucmd!().arg("--remove=wip").fails().code_is(1);
}
#[test] #[test]
fn test_shred() { fn test_shred() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
@ -49,15 +54,15 @@ fn test_shred_remove() {
#[test] #[test]
fn test_shred_remove_unlink() { fn test_shred_remove_unlink() {
let (at, mut ucmd) = at_and_ucmd!(); // spell-checker:disable-next-line
for argument in ["--remove=unlink", "--remove=unlin", "--remove=u"] {
let file = "test_shred_remove_unlink"; let (at, mut ucmd) = at_and_ucmd!();
at.touch(file); let file = "test_shred_remove_unlink";
at.touch(file);
ucmd.arg("--remove=unlink").arg(file).succeeds(); ucmd.arg(argument).arg(file).succeeds();
// File was deleted
// File was deleted assert!(!at.file_exists(file));
assert!(!at.file_exists(file)); }
} }
#[test] #[test]
@ -75,15 +80,15 @@ fn test_shred_remove_wipe() {
#[test] #[test]
fn test_shred_remove_wipesync() { fn test_shred_remove_wipesync() {
let (at, mut ucmd) = at_and_ucmd!(); // spell-checker:disable-next-line
for argument in ["--remove=wipesync", "--remove=wipesyn", "--remove=wipes"] {
let file = "test_shred_remove_wipesync"; let (at, mut ucmd) = at_and_ucmd!();
at.touch(file); let file = "test_shred_remove_wipesync";
at.touch(file);
ucmd.arg("--remove=wipesync").arg(file).succeeds(); ucmd.arg(argument).arg(file).succeeds();
// File was deleted
// File was deleted assert!(!at.file_exists(file));
assert!(!at.file_exists(file)); }
} }
#[test] #[test]

View file

@ -126,7 +126,16 @@ fn test_ext_sort_zero_terminated() {
#[test] #[test]
fn test_months_whitespace() { fn test_months_whitespace() {
test_helper("months-whitespace", &["-M", "--month-sort", "--sort=month"]); test_helper(
"months-whitespace",
&[
"-M",
"--month-sort",
"--sort=month",
"--sort=mont", // spell-checker:disable-line
"--sort=m",
],
);
} }
#[test] #[test]
@ -141,6 +150,16 @@ fn test_version_sort_unstable() {
.pipe_in("0.1\n0.02\n0.2\n0.002\n0.3\n") .pipe_in("0.1\n0.02\n0.2\n0.002\n0.3\n")
.succeeds() .succeeds()
.stdout_is("0.1\n0.002\n0.02\n0.2\n0.3\n"); .stdout_is("0.1\n0.002\n0.02\n0.2\n0.3\n");
new_ucmd!()
.arg("--sort=versio") // spell-checker:disable-line
.pipe_in("0.1\n0.02\n0.2\n0.002\n0.3\n")
.succeeds()
.stdout_is("0.1\n0.002\n0.02\n0.2\n0.3\n");
new_ucmd!()
.arg("--sort=v")
.pipe_in("0.1\n0.02\n0.2\n0.002\n0.3\n")
.succeeds()
.stdout_is("0.1\n0.002\n0.02\n0.2\n0.3\n");
} }
#[test] #[test]
@ -157,7 +176,14 @@ fn test_version_sort_stable() {
fn test_human_numeric_whitespace() { fn test_human_numeric_whitespace() {
test_helper( test_helper(
"human-numeric-whitespace", "human-numeric-whitespace",
&["-h", "--human-numeric-sort", "--sort=human-numeric"], &[
"-h",
"--human-numeric-sort",
"--sort=human-numeric",
"--sort=human-numeri", // spell-checker:disable-line
"--sort=human",
"--sort=h",
],
); );
} }
@ -177,7 +203,14 @@ fn test_ext_sort_as64_bailout() {
fn test_multiple_decimals_general() { fn test_multiple_decimals_general() {
test_helper( test_helper(
"multiple_decimals_general", "multiple_decimals_general",
&["-g", "--general-numeric-sort", "--sort=general-numeric"], &[
"-g",
"--general-numeric-sort",
"--sort=general-numeric",
"--sort=general-numeri", // spell-checker:disable-line
"--sort=general",
"--sort=g",
],
); );
} }
@ -185,7 +218,7 @@ fn test_multiple_decimals_general() {
fn test_multiple_decimals_numeric() { fn test_multiple_decimals_numeric() {
test_helper( test_helper(
"multiple_decimals_numeric", "multiple_decimals_numeric",
&["-n", "--numeric-sort", "--sort=numeric"], &["-n", "--numeric-sort", "--sort=numeric", "--sort=n"],
); );
} }
@ -784,7 +817,13 @@ fn test_pipe() {
#[test] #[test]
fn test_check() { fn test_check() {
for diagnose_arg in ["-c", "--check", "--check=diagnose-first"] { for diagnose_arg in [
"-c",
"--check",
"--check=diagnose-first",
"--check=diagnose",
"--check=d",
] {
new_ucmd!() new_ucmd!()
.arg(diagnose_arg) .arg(diagnose_arg)
.arg("check_fail.txt") .arg("check_fail.txt")
@ -802,12 +841,25 @@ fn test_check() {
#[test] #[test]
fn test_check_silent() { fn test_check_silent() {
for silent_arg in ["-C", "--check=silent", "--check=quiet"] { for silent_arg in [
"-C",
"--check=silent",
"--check=quiet",
"--check=silen", // spell-checker:disable-line
"--check=quie", // spell-checker:disable-line
"--check=s",
"--check=q",
] {
new_ucmd!() new_ucmd!()
.arg(silent_arg) .arg(silent_arg)
.arg("check_fail.txt") .arg("check_fail.txt")
.fails() .fails()
.stdout_is(""); .stdout_is("");
new_ucmd!()
.arg(silent_arg)
.arg("empty.txt")
.succeeds()
.no_output();
} }
} }

View file

@ -533,37 +533,40 @@ fn test_follow_multiple() {
#[test] #[test]
#[cfg(not(target_os = "windows"))] // FIXME: test times out #[cfg(not(target_os = "windows"))] // FIXME: test times out
fn test_follow_name_multiple() { fn test_follow_name_multiple() {
let (at, mut ucmd) = at_and_ucmd!(); // spell-checker:disable-next-line
let mut child = ucmd for argument in ["--follow=name", "--follo=nam", "--f=n"] {
.arg("--follow=name") let (at, mut ucmd) = at_and_ucmd!();
.arg(FOOBAR_TXT) let mut child = ucmd
.arg(FOOBAR_2_TXT) .arg(argument)
.run_no_wait(); .arg(FOOBAR_TXT)
.arg(FOOBAR_2_TXT)
.run_no_wait();
child child
.make_assertion_with_delay(500) .make_assertion_with_delay(500)
.is_alive() .is_alive()
.with_current_output() .with_current_output()
.stdout_only_fixture("foobar_follow_multiple.expected"); .stdout_only_fixture("foobar_follow_multiple.expected");
let first_append = "trois\n"; let first_append = "trois\n";
at.append(FOOBAR_2_TXT, first_append); at.append(FOOBAR_2_TXT, first_append);
child child
.make_assertion_with_delay(DEFAULT_SLEEP_INTERVAL_MILLIS) .make_assertion_with_delay(DEFAULT_SLEEP_INTERVAL_MILLIS)
.with_current_output() .with_current_output()
.stdout_only(first_append); .stdout_only(first_append);
let second_append = "twenty\nthirty\n"; let second_append = "twenty\nthirty\n";
at.append(FOOBAR_TXT, second_append); at.append(FOOBAR_TXT, second_append);
child child
.make_assertion_with_delay(DEFAULT_SLEEP_INTERVAL_MILLIS) .make_assertion_with_delay(DEFAULT_SLEEP_INTERVAL_MILLIS)
.with_current_output() .with_current_output()
.stdout_only_fixture("foobar_follow_multiple_appended.expected"); .stdout_only_fixture("foobar_follow_multiple_appended.expected");
child.make_assertion().is_alive(); child.make_assertion().is_alive();
child.kill(); child.kill();
}
} }
#[test] #[test]
@ -844,7 +847,7 @@ fn test_follow_missing() {
// Ensure that --follow=name does not imply --retry. // Ensure that --follow=name does not imply --retry.
// Ensure that --follow={descriptor,name} (without --retry) does *not wait* for the // Ensure that --follow={descriptor,name} (without --retry) does *not wait* for the
// file to appear. // file to appear.
for follow_mode in &["--follow=descriptor", "--follow=name"] { for follow_mode in &["--follow=descriptor", "--follow=name", "--fo=d", "--fo=n"] {
new_ucmd!() new_ucmd!()
.arg(follow_mode) .arg(follow_mode)
.arg("missing") .arg("missing")

View file

@ -311,6 +311,23 @@ mod linux_only {
expect_correct(file_out_a, &at, content.as_str()); expect_correct(file_out_a, &at, content.as_str());
} }
#[test]
fn test_pipe_error_warn_nopipe_3_shortcut() {
let (at, mut ucmd) = at_and_ucmd!();
let file_out_a = "tee_file_out_a";
let proc = ucmd
.arg("--output-error=warn-")
.arg(file_out_a)
.set_stdout(make_broken_pipe());
let (content, output) = run_tee(proc);
expect_success(&output);
expect_correct(file_out_a, &at, content.as_str());
}
#[test] #[test]
fn test_pipe_error_warn() { fn test_pipe_error_warn() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
@ -362,6 +379,23 @@ mod linux_only {
expect_correct(file_out_a, &at, content.as_str()); expect_correct(file_out_a, &at, content.as_str());
} }
#[test]
fn test_pipe_error_exit_nopipe_shortcut() {
let (at, mut ucmd) = at_and_ucmd!();
let file_out_a = "tee_file_out_a";
let proc = ucmd
.arg("--output-error=exit-nop")
.arg(file_out_a)
.set_stdout(make_broken_pipe());
let (content, output) = run_tee(proc);
expect_success(&output);
expect_correct(file_out_a, &at, content.as_str());
}
#[test] #[test]
fn test_space_error_default() { fn test_space_error_default() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();

View file

@ -192,7 +192,14 @@ fn test_touch_set_cymdhms_time() {
#[test] #[test]
fn test_touch_set_only_atime() { fn test_touch_set_only_atime() {
let atime_args = ["-a", "--time=access", "--time=atime", "--time=use"]; let atime_args = [
"-a",
"--time=access",
"--time=atime",
"--time=atim", // spell-checker:disable-line
"--time=a",
"--time=use",
];
let file = "test_touch_set_only_atime"; let file = "test_touch_set_only_atime";
for atime_arg in atime_args { for atime_arg in atime_args {
@ -293,7 +300,7 @@ fn test_touch_set_both_time_and_date() {
#[test] #[test]
fn test_touch_set_only_mtime() { fn test_touch_set_only_mtime() {
let mtime_args = ["-m", "--time=modify", "--time=mtime"]; let mtime_args = ["-m", "--time=modify", "--time=mtime", "--time=m"];
let file = "test_touch_set_only_mtime"; let file = "test_touch_set_only_mtime";
for mtime_arg in mtime_args { for mtime_arg in mtime_args {

View file

@ -145,6 +145,21 @@ fn test_stdin_all_repeated() {
.pipe_in_fixture(INPUT) .pipe_in_fixture(INPUT)
.run() .run()
.stdout_is_fixture("sorted-all-repeated.expected"); .stdout_is_fixture("sorted-all-repeated.expected");
new_ucmd!()
.args(&["--all-repeated=none"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("sorted-all-repeated.expected");
new_ucmd!()
.args(&["--all-repeated=non"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("sorted-all-repeated.expected");
new_ucmd!()
.args(&["--all-repeated=n"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("sorted-all-repeated.expected");
} }
#[test] #[test]
@ -167,6 +182,16 @@ fn test_stdin_all_repeated_separate() {
.pipe_in_fixture(INPUT) .pipe_in_fixture(INPUT)
.run() .run()
.stdout_is_fixture("sorted-all-repeated-separate.expected"); .stdout_is_fixture("sorted-all-repeated-separate.expected");
new_ucmd!()
.args(&["--all-repeated=separat"]) // spell-checker:disable-line
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("sorted-all-repeated-separate.expected");
new_ucmd!()
.args(&["--all-repeated=s"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("sorted-all-repeated-separate.expected");
} }
#[test] #[test]
@ -176,6 +201,16 @@ fn test_stdin_all_repeated_prepend() {
.pipe_in_fixture(INPUT) .pipe_in_fixture(INPUT)
.run() .run()
.stdout_is_fixture("sorted-all-repeated-prepend.expected"); .stdout_is_fixture("sorted-all-repeated-prepend.expected");
new_ucmd!()
.args(&["--all-repeated=prepen"]) // spell-checker:disable-line
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("sorted-all-repeated-prepend.expected");
new_ucmd!()
.args(&["--all-repeated=p"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("sorted-all-repeated-prepend.expected");
} }
#[test] #[test]
@ -253,6 +288,11 @@ fn test_group_prepend() {
.pipe_in_fixture(INPUT) .pipe_in_fixture(INPUT)
.run() .run()
.stdout_is_fixture("group-prepend.expected"); .stdout_is_fixture("group-prepend.expected");
new_ucmd!()
.args(&["--group=p"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group-prepend.expected");
} }
#[test] #[test]
@ -262,6 +302,11 @@ fn test_group_append() {
.pipe_in_fixture(INPUT) .pipe_in_fixture(INPUT)
.run() .run()
.stdout_is_fixture("group-append.expected"); .stdout_is_fixture("group-append.expected");
new_ucmd!()
.args(&["--group=a"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group-append.expected");
} }
#[test] #[test]
@ -271,6 +316,16 @@ fn test_group_both() {
.pipe_in_fixture(INPUT) .pipe_in_fixture(INPUT)
.run() .run()
.stdout_is_fixture("group-both.expected"); .stdout_is_fixture("group-both.expected");
new_ucmd!()
.args(&["--group=bot"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group-both.expected");
new_ucmd!()
.args(&["--group=b"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group-both.expected");
} }
#[test] #[test]
@ -280,6 +335,11 @@ fn test_group_separate() {
.pipe_in_fixture(INPUT) .pipe_in_fixture(INPUT)
.run() .run()
.stdout_is_fixture("group.expected"); .stdout_is_fixture("group.expected");
new_ucmd!()
.args(&["--group=s"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group.expected");
} }
#[test] #[test]

View file

@ -531,6 +531,10 @@ fn test_total_auto() {
.args(&["lorem_ipsum.txt", "--total=auto"]) .args(&["lorem_ipsum.txt", "--total=auto"])
.run() .run()
.stdout_is(" 13 109 772 lorem_ipsum.txt\n"); .stdout_is(" 13 109 772 lorem_ipsum.txt\n");
new_ucmd!()
.args(&["lorem_ipsum.txt", "--tot=au"])
.run()
.stdout_is(" 13 109 772 lorem_ipsum.txt\n");
new_ucmd!() new_ucmd!()
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=auto"]) .args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=auto"])
@ -551,6 +555,13 @@ fn test_total_always() {
" 13 109 772 lorem_ipsum.txt\n", " 13 109 772 lorem_ipsum.txt\n",
" 13 109 772 total\n", " 13 109 772 total\n",
)); ));
new_ucmd!()
.args(&["lorem_ipsum.txt", "--total=al"])
.run()
.stdout_is(concat!(
" 13 109 772 lorem_ipsum.txt\n",
" 13 109 772 total\n",
));
new_ucmd!() new_ucmd!()
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=always"]) .args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=always"])
@ -576,6 +587,13 @@ fn test_total_never() {
" 13 109 772 lorem_ipsum.txt\n", " 13 109 772 lorem_ipsum.txt\n",
" 18 204 1115 moby_dick.txt\n", " 18 204 1115 moby_dick.txt\n",
)); ));
new_ucmd!()
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=n"])
.run()
.stdout_is(concat!(
" 13 109 772 lorem_ipsum.txt\n",
" 18 204 1115 moby_dick.txt\n",
));
} }
#[test] #[test]
@ -589,6 +607,10 @@ fn test_total_only() {
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=only"]) .args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=only"])
.run() .run()
.stdout_is("31 313 1887\n"); .stdout_is("31 313 1887\n");
new_ucmd!()
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--t=o"])
.run()
.stdout_is("31 313 1887\n");
} }
#[test] #[test]