From 7d08884436ba04e1d1b6d8dc97ce5550ae28b9c1 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 05:34:53 -0700 Subject: [PATCH 01/20] uutils: add dir util --- Cargo.lock | 21 ++ Cargo.toml | 2 + src/uu/dir/Cargo.toml | 40 +++ src/uu/dir/LICENSE | 1 + src/uu/dir/src/dir.rs | 25 ++ src/uu/dir/src/main.rs | 1 + src/uu/ls/src/ls.rs | 593 ++++++++++++++++++++++++++++++++++++++++- 7 files changed, 681 insertions(+), 2 deletions(-) create mode 100644 src/uu/dir/Cargo.toml create mode 120000 src/uu/dir/LICENSE create mode 100644 src/uu/dir/src/dir.rs create mode 100644 src/uu/dir/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index bc7431baaf3..47f14200641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -368,6 +368,7 @@ dependencies = [ "uu_date", "uu_dd", "uu_df", + "uu_dir", "uu_dircolors", "uu_dirname", "uu_du", @@ -2376,6 +2377,26 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_dir" +version = "0.0.13" +dependencies = [ + "atty", + "chrono", + "clap 3.1.6", + "glob", + "lazy_static", + "lscolors", + "number_prefix", + "once_cell", + "selinux", + "term_grid", + "termsize", + "unicode-width", + "uu_ls", + "uucore", +] + [[package]] name = "uu_dircolors" version = "0.0.13" diff --git a/Cargo.toml b/Cargo.toml index 12959ad35ad..3c904cd163d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ feat_common_core = [ "cut", "date", "df", + "dir", "dircolors", "dirname", "dd", @@ -276,6 +277,7 @@ cut = { optional=true, version="0.0.13", package="uu_cut", path="src/uu/cut date = { optional=true, version="0.0.13", package="uu_date", path="src/uu/date" } dd = { optional=true, version="0.0.13", package="uu_dd", path="src/uu/dd" } df = { optional=true, version="0.0.13", package="uu_df", path="src/uu/df" } +dir = { optional=true, version="0.0.13", package="uu_dir", path="src/uu/dir" } dircolors= { optional=true, version="0.0.13", package="uu_dircolors", path="src/uu/dircolors" } dirname = { optional=true, version="0.0.13", package="uu_dirname", path="src/uu/dirname" } du = { optional=true, version="0.0.13", package="uu_du", path="src/uu/du" } diff --git a/src/uu/dir/Cargo.toml b/src/uu/dir/Cargo.toml new file mode 100644 index 00000000000..8da834ac3c4 --- /dev/null +++ b/src/uu/dir/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "uu_dir" +version = "0.0.13" +authors = ["uutils developers"] +license = "MIT" +description = "shortcut to ls -C -b" + +homepage = "https://github.com/uutils/coreutils" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls" +keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] +categories = ["command-line-utilities"] +edition = "2021" + +[lib] +path = "src/dir.rs" + +[dependencies] +chrono = "0.4.19" +clap = { version = "3.1", features = ["wrap_help", "cargo", "env"] } +unicode-width = "0.1.8" +number_prefix = "0.4" +term_grid = "0.1.5" +termsize = "0.1.6" +glob = "0.3.0" +lscolors = { version = "0.9.0", features = ["ansi_term"] } +uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } +once_cell = "1.10.0" +atty = "0.2" +selinux = { version="0.2", optional = true } +uu_ls = {path="../ls"} + +[target.'cfg(unix)'.dependencies] +lazy_static = "1.4.0" + +[[bin]] +name = "dir" +path = "src/main.rs" + +[features] +feat_selinux = ["selinux"] diff --git a/src/uu/dir/LICENSE b/src/uu/dir/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/src/uu/dir/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/dir/src/dir.rs b/src/uu/dir/src/dir.rs new file mode 100644 index 00000000000..56be6b3b247 --- /dev/null +++ b/src/uu/dir/src/dir.rs @@ -0,0 +1,25 @@ +// * This file is part of the uutils coreutils package. +// * +// * (c) gmnsii +// * +// * For the full copyright and license information, please view the LICENSE file +// * that was distributed with this source code. + +use clap::Command; +use uucore::error::UResult; + +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + uu_ls::dir_main(args) +} + +// Coreutils won't compile if not every util have an uu_app function. +// However, we can't put it here since it needs to be in the same place as +// the entry point for the dir util, which is in ls/ls.rs. We could put the +// entry point here, but we would need a lot of refactoring and to make a lot of +// ls functions and structs public. +// +// To make our life easier, we use this dummy function. +pub fn uu_app<'a>() -> Command<'a> { + Command::new(uucore::util_name()) +} diff --git a/src/uu/dir/src/main.rs b/src/uu/dir/src/main.rs new file mode 100644 index 00000000000..c5b5b6363d8 --- /dev/null +++ b/src/uu/dir/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_dir); diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 7782830393b..d1666c0dd4a 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -20,7 +20,7 @@ use glob::Pattern; use lscolors::LsColors; use number_prefix::NumberPrefix; use once_cell::unsync::OnceCell; -use quoting_style::{escape_name, QuotingStyle}; +use quoting_style::{escape_name, Quotes, QuotingStyle}; #[cfg(windows)] use std::os::windows::fs::MetadataExt; use std::{ @@ -304,7 +304,7 @@ enum IndicatorStyle { Classify, } -struct Config { +pub struct Config { format: Format, files: Files, sort: Sort, @@ -1411,6 +1411,595 @@ only ignore '.' and '..'.", ) } +/// The entry point for the dir coreutil +pub fn dir_main(args: impl uucore::Args) -> UResult<()> { + let command = dir_uu_app(); + let matches = command.get_matches_from(args); + let mut config = Config::from(&matches)?; + config.quoting_style = QuotingStyle::C { + quotes: Quotes::None, + }; + config.format = Format::Columns; + let locs = matches + .values_of_os(options::PATHS) + .map(|v| v.map(Path::new).collect()) + .unwrap_or_else(|| vec![Path::new(".")]); + list(locs, &config) +} + +pub fn dir_uu_app<'a>() -> Command<'a> { + Command::new(uucore::util_name()) + .version(crate_version!()) + .override_usage(format_usage(USAGE)) + .about( + "Shortcut to ls -C -b", + ) + .infer_long_args(true) + .arg( + Arg::new(options::HELP) + .long(options::HELP) + .help("Print help information.") + ) + // Format arguments + .arg( + Arg::new(options::FORMAT) + .long(options::FORMAT) + .help("Set the display format.") + .takes_value(true) + .possible_values(&[ + "long", + "verbose", + "single-column", + "vertical", + "across", + "horizontal", + "commas", + ]) + .hide_possible_values(true) + .require_equals(true) + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::LONG) + .short('l') + .long(options::format::LONG) + .help("Display detailed information.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::ACROSS) + .short('x') + .help("List entries in rows instead of in columns.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::COMMAS) + .short('m') + .help("List entries separated by commas.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + // The next four arguments do not override with the other format + // options, see the comment in Config::from for the reason. + // Ideally, they would use Arg::override_with, with their own name + // but that doesn't seem to work in all cases. Example: + // ls -1g1 + // even though `ls -11` and `ls -1 -g -1` work. + .arg( + Arg::new(options::format::ONE_LINE) + .short('1') + .help("List one file per line.") + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NO_GROUP) + .short('o') + .help( + "Long format without group information. \ + Identical to --format=long with --no-group.", + ) + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NO_OWNER) + .short('g') + .help("Long format without owner information.") + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NUMERIC_UID_GID) + .short('n') + .long(options::format::LONG_NUMERIC_UID_GID) + .help("-l with numeric UIDs and GIDs.") + .multiple_occurrences(true), + ) + // Quoting style + .arg( + Arg::new(options::QUOTING_STYLE) + .long(options::QUOTING_STYLE) + .takes_value(true) + .help("Set quoting style.") + .possible_values(&[ + "literal", + "shell", + "shell-always", + "shell-escape", + "shell-escape-always", + ]) + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + .arg( + Arg::new(options::quoting::LITERAL) + .short('N') + .long(options::quoting::LITERAL) + .help("Use literal quoting style. Equivalent to `--quoting-style=literal`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + // Control characters + .arg( + Arg::new(options::HIDE_CONTROL_CHARS) + .short('q') + .long(options::HIDE_CONTROL_CHARS) + .help("Replace control characters with '?' if they are not escaped.") + .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), + ) + .arg( + Arg::new(options::SHOW_CONTROL_CHARS) + .long(options::SHOW_CONTROL_CHARS) + .help("Show control characters 'as is' if they are not escaped.") + .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), + ) + // Time arguments + .arg( + Arg::new(options::TIME) + .long(options::TIME) + .help( + "Show time in :\n\ + \taccess time (-u): atime, access, use;\n\ + \tchange time (-t): ctime, status.\n\ + \tbirth time: birth, creation;", + ) + .value_name("field") + .takes_value(true) + .possible_values(&[ + "atime", "access", "use", "ctime", "status", "birth", "creation", + ]) + .hide_possible_values(true) + .require_equals(true) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + .arg( + Arg::new(options::time::CHANGE) + .short('c') + .help( + "If the long listing format (e.g., -l, -o) is being used, print the status \ + change time (the 'ctime' in the inode) instead of the modification 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.", + ) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + .arg( + Arg::new(options::time::ACCESS) + .short('u') + .help( + "If the long listing format (e.g., -l, -o) is being used, print the status \ + access time instead of the modification time. When explicitly sorting by time \ + (--sort=time or -t) or when not using a long listing format, sort according to the \ + access time.", + ) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + // Hide and ignore + .arg( + Arg::new(options::HIDE) + .long(options::HIDE) + .takes_value(true) + .multiple_occurrences(true) + .value_name("PATTERN") + .help( + "do not list implied entries matching shell PATTERN (overridden by -a or -A)", + ), + ) + .arg( + Arg::new(options::IGNORE) + .short('I') + .long(options::IGNORE) + .takes_value(true) + .multiple_occurrences(true) + .value_name("PATTERN") + .help("do not list implied entries matching shell PATTERN"), + ) + .arg( + Arg::new(options::IGNORE_BACKUPS) + .short('B') + .long(options::IGNORE_BACKUPS) + .help("Ignore entries which end with ~."), + ) + // Sort arguments + .arg( + Arg::new(options::SORT) + .long(options::SORT) + .help("Sort by : name, none (-U), time (-t), size (-S) or extension (-X)") + .value_name("field") + .takes_value(true) + .possible_values(&["name", "none", "time", "size", "version", "extension"]) + .require_equals(true) + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::SIZE) + .short('S') + .help("Sort by file size, largest first.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::TIME) + .short('t') + .help("Sort by modification time (the 'mtime' in the inode), newest first.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::VERSION) + .short('v') + .help("Natural sort of (version) numbers in the filenames.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::EXTENSION) + .short('X') + .help("Sort alphabetically by entry extension.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::NONE) + .short('U') + .help( + "Do not sort; list the files in whatever order they are stored in the \ + directory. This is especially useful when listing very large directories, \ + since not doing any sorting can be noticeably faster.", + ) + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + // Dereferencing + .arg( + Arg::new(options::dereference::ALL) + .short('L') + .long(options::dereference::ALL) + .help( + "When showing file information for a symbolic link, show information for the \ + file the link references rather than the link itself.", + ) + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + .arg( + Arg::new(options::dereference::DIR_ARGS) + .long(options::dereference::DIR_ARGS) + .help( + "Do not dereference symlinks except when they link to directories and are \ + given as command line arguments.", + ) + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + .arg( + Arg::new(options::dereference::ARGS) + .short('H') + .long(options::dereference::ARGS) + .help("Do not dereference symlinks except when given as command line arguments.") + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + // Long format options + .arg( + Arg::new(options::NO_GROUP) + .long(options::NO_GROUP) + .short('G') + .help("Do not show group in long format."), + ) + .arg(Arg::new(options::AUTHOR).long(options::AUTHOR).help( + "Show author in long format. \ + On the supported platforms, the author always matches the file owner.", + )) + // Other Flags + .arg( + Arg::new(options::files::ALL) + .short('a') + .long(options::files::ALL) + // Overrides -A (as the order matters) + .overrides_with(options::files::ALMOST_ALL) + .multiple_occurrences(true) + .help("Do not ignore hidden files (files with names that start with '.')."), + ) + .arg( + Arg::new(options::files::ALMOST_ALL) + .short('A') + .long(options::files::ALMOST_ALL) + // Overrides -a (as the order matters) + .overrides_with(options::files::ALL) + .multiple_occurrences(true) + .help( + "In a directory, do not ignore all file names that start with '.', \ +only ignore '.' and '..'.", + ), + ) + .arg( + Arg::new(options::DIRECTORY) + .short('d') + .long(options::DIRECTORY) + .help( + "Only list the names of directories, rather than listing directory contents. \ + This will not follow symbolic links unless one of `--dereference-command-line \ + (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \ + specified.", + ), + ) + .arg( + Arg::new(options::size::HUMAN_READABLE) + .short('h') + .long(options::size::HUMAN_READABLE) + .help("Print human readable file sizes (e.g. 1K 234M 56G).") + .overrides_with(options::size::SI), + ) + .arg( + Arg::new(options::size::KIBIBYTES) + .short('k') + .long(options::size::KIBIBYTES) + .help("default to 1024-byte blocks for file system usage; used only with -s and per directory totals"), + ) + .arg( + Arg::new(options::size::SI) + .long(options::size::SI) + .help("Print human readable file sizes using powers of 1000 instead of 1024."), + ) + .arg( + Arg::new(options::size::BLOCK_SIZE) + .long(options::size::BLOCK_SIZE) + .takes_value(true) + .require_equals(true) + .value_name("BLOCK_SIZE") + .help("scale sizes by BLOCK_SIZE when printing them"), + ) + .arg( + Arg::new(options::INODE) + .short('i') + .long(options::INODE) + .help("print the index number of each file"), + ) + .arg( + Arg::new(options::REVERSE) + .short('r') + .long(options::REVERSE) + .help( + "Reverse whatever the sorting method is e.g., list files in reverse \ + alphabetical order, youngest first, smallest first, or whatever.", + ), + ) + .arg( + Arg::new(options::RECURSIVE) + .short('R') + .long(options::RECURSIVE) + .help("List the contents of all directories recursively."), + ) + .arg( + Arg::new(options::WIDTH) + .long(options::WIDTH) + .short('w') + .help("Assume that the terminal is COLS columns wide.") + .value_name("COLS") + .takes_value(true), + ) + .arg( + Arg::new(options::size::ALLOCATION_SIZE) + .short('s') + .long(options::size::ALLOCATION_SIZE) + .help("print the allocated size of each file, in blocks"), + ) + .arg( + Arg::new(options::COLOR) + .long(options::COLOR) + .help("Color output based on file type.") + .takes_value(true) + .possible_values(&[ + "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", + ]) + .require_equals(true) + .min_values(0), + ) + .arg( + Arg::new(options::INDICATOR_STYLE) + .long(options::INDICATOR_STYLE) + .help( + "Append indicator with style WORD to entry names: \ + none (default), slash (-p), file-type (--file-type), classify (-F)", + ) + .takes_value(true) + .possible_values(&["none", "slash", "file-type", "classify"]) + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + // The --classify flag can take an optional when argument to + // control its behavior from version 9 of GNU coreutils. + // There is currently an inconsistency where GNU coreutils allows only + // the long form of the flag to take the argument while we allow it + // for both the long and short form of the flag. + Arg::new(options::indicator_style::CLASSIFY) + .short('F') + .long(options::indicator_style::CLASSIFY) + .help( + "Append a character to each file name indicating the file type. Also, for \ + regular files that are executable, append '*'. The file type indicators are \ + '/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \ + '>' for doors, and nothing for regular files. when may be omitted, or one of:\n\ + \tnone - Do not classify. This is the default.\n\ + \tauto - Only classify if standard output is a terminal.\n\ + \talways - Always classify.\n\ + Specifying --classify and no when is equivalent to --classify=always. This will not follow\ + symbolic links listed on the command line unless the --dereference-command-line (-H),\ + --dereference (-L), or --dereference-command-line-symlink-to-dir options are specified.", + ) + .takes_value(true) + .value_name("when") + .possible_values(&[ + "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", + ]) + .default_missing_value("always") + .require_equals(true) + .min_values(0) + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + Arg::new(options::indicator_style::FILE_TYPE) + .long(options::indicator_style::FILE_TYPE) + .help("Same as --classify, but do not append '*'") + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + Arg::new(options::indicator_style::SLASH) + .short('p') + .help("Append / indicator to directories.") + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + //This still needs support for posix-*, +FORMAT + Arg::new(options::TIME_STYLE) + .long(options::TIME_STYLE) + .help("time/date format with -l; see TIME_STYLE below") + .value_name("TIME_STYLE") + .env("TIME_STYLE") + .possible_values(&["full-iso", "long-iso", "iso", "locale"]) + .overrides_with_all(&[options::TIME_STYLE]), + ) + .arg( + Arg::new(options::FULL_TIME) + .long(options::FULL_TIME) + .overrides_with(options::FULL_TIME) + .help("like -l --time-style=full-iso"), + ) + .arg( + Arg::new(options::CONTEXT) + .short('Z') + .long(options::CONTEXT) + .help(CONTEXT_HELP_TEXT), + ) + // Positional arguments + .arg( + Arg::new(options::PATHS) + .multiple_occurrences(true) + .takes_value(true) + .allow_invalid_utf8(true), + ) + .after_help( + "The TIME_STYLE argument can be full-iso, long-iso, iso. \ + Also the TIME_STYLE environment variable sets the default style to use.", + ) +} + /// Represents a Path along with it's associated data. /// Any data that will be reused several times makes sense to be added to this structure. /// Caching data here helps eliminate redundant syscalls to fetch same information. From b2c9b39288defe2d37b2fb4b59d8fe454d03b296 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 05:36:17 -0700 Subject: [PATCH 02/20] Update Cargo.lock --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 47f14200641..f9570807685 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2383,7 +2383,7 @@ version = "0.0.13" dependencies = [ "atty", "chrono", - "clap 3.1.6", + "clap 3.1.8", "glob", "lazy_static", "lscolors", From 5c199a77a87095ee38e324a32f7ed9c1c0afdbac Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 06:35:29 -0700 Subject: [PATCH 03/20] Made dir base opions overwrittables --- src/uu/ls/src/ls.rs | 95 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index d1666c0dd4a..171c8283cfe 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -15,7 +15,7 @@ extern crate lazy_static; mod quoting_style; -use clap::{crate_version, Arg, Command}; +use clap::{crate_version, Arg, ArgMatches, Command}; use glob::Pattern; use lscolors::LsColors; use number_prefix::NumberPrefix; @@ -30,6 +30,7 @@ use std::{ fmt::Display, fs::{self, DirEntry, FileType, Metadata, ReadDir}, io::{stdout, BufWriter, ErrorKind, Stdout, Write}, + option, path::{Path, PathBuf}, time::{SystemTime, UNIX_EPOCH}, }; @@ -802,9 +803,7 @@ pub fn uu_app<'a>() -> Command<'a> { .version(crate_version!()) .override_usage(format_usage(USAGE)) .about( - "By default, ls will list the files and contents of any directories on \ - the command line, expect that it will ignore files and directories \ - whose names start with '.'.", + "dir is a shortcut for ls -C -b", ) .infer_long_args(true) .arg( @@ -1415,11 +1414,45 @@ only ignore '.' and '..'.", pub fn dir_main(args: impl uucore::Args) -> UResult<()> { let command = dir_uu_app(); let matches = command.get_matches_from(args); + + let mut default_quoting_style = false; + let mut default_format_style = false; + + if !matches.is_present(options::QUOTING_STYLE) + && !matches.is_present(options::quoting::C) + && !matches.is_present(options::quoting::ESCAPE) + && !matches.is_present(options::quoting::LITERAL) + { + default_quoting_style = true; + } + if !matches.is_present(options::FORMAT) + && !matches.is_present(options::format::ACROSS) + && !matches.is_present(options::format::COLUMNS) + && !matches.is_present(options::format::COMMAS) + && !matches.is_present(options::format::LONG) + && !matches.is_present(options::format::LONG_NO_GROUP) + && !matches.is_present(options::format::LONG_NO_OWNER) + && !matches.is_present(options::format::LONG_NUMERIC_UID_GID) + && !matches.is_present(options::format::ONE_LINE) + { + default_format_style = true; + } + let mut config = Config::from(&matches)?; - config.quoting_style = QuotingStyle::C { - quotes: Quotes::None, - }; - config.format = Format::Columns; + // config.quoting_style = QuotingStyle::C { + // quotes: Quotes::None, + // }; + // config.format = Format::Columns; + + if default_quoting_style { + config.quoting_style = QuotingStyle::C { + quotes: Quotes::None, + }; + } + if default_format_style { + config.format = Format::Columns; + } + let locs = matches .values_of_os(options::PATHS) .map(|v| v.map(Path::new).collect()) @@ -1427,12 +1460,14 @@ pub fn dir_main(args: impl uucore::Args) -> UResult<()> { list(locs, &config) } -pub fn dir_uu_app<'a>() -> Command<'a> { +fn dir_uu_app<'a>() -> Command<'a> { Command::new(uucore::util_name()) .version(crate_version!()) .override_usage(format_usage(USAGE)) .about( - "Shortcut to ls -C -b", + "By default, ls will list the files and contents of any directories on \ + the command line, expect that it will ignore files and directories \ + whose names start with '.'.", ) .infer_long_args(true) .arg( @@ -1450,6 +1485,7 @@ pub fn dir_uu_app<'a>() -> Command<'a> { "long", "verbose", "single-column", + "columns", "vertical", "across", "horizontal", @@ -1465,6 +1501,18 @@ pub fn dir_uu_app<'a>() -> Command<'a> { options::format::COLUMNS, ]), ) + .arg( + Arg::new(options::format::COLUMNS) + .short('C') + .help("Display the files in columns.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) .arg( Arg::new(options::format::LONG) .short('l') @@ -1548,6 +1596,8 @@ pub fn dir_uu_app<'a>() -> Command<'a> { "shell-always", "shell-escape", "shell-escape-always", + "c", + "escape", ]) .overrides_with_all(&[ options::QUOTING_STYLE, @@ -1568,6 +1618,30 @@ pub fn dir_uu_app<'a>() -> Command<'a> { options::quoting::C, ]), ) + .arg( + Arg::new(options::quoting::ESCAPE) + .short('b') + .long(options::quoting::ESCAPE) + .help("Use escape quoting style. Equivalent to `--quoting-style=escape`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + .arg( + Arg::new(options::quoting::C) + .short('Q') + .long(options::quoting::C) + .help("Use C quoting style. Equivalent to `--quoting-style=c`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) // Control characters .arg( Arg::new(options::HIDE_CONTROL_CHARS) @@ -1999,7 +2073,6 @@ only ignore '.' and '..'.", Also the TIME_STYLE environment variable sets the default style to use.", ) } - /// Represents a Path along with it's associated data. /// Any data that will be reused several times makes sense to be added to this structure. /// Caching data here helps eliminate redundant syscalls to fetch same information. From d0cbf76be2ef89e3975766d2dc2c454235905204 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 06:50:21 -0700 Subject: [PATCH 04/20] add vdir --- Cargo.lock | 21 ++ Cargo.toml | 2 + src/uu/ls/src/ls.rs | 672 +++++++++++++++++++++++++++++++++++++++- src/uu/vdir/Cargo.toml | 40 +++ src/uu/vdir/src/main.rs | 1 + src/uu/vdir/src/vdir.rs | 25 ++ 6 files changed, 755 insertions(+), 6 deletions(-) create mode 100644 src/uu/vdir/Cargo.toml create mode 100644 src/uu/vdir/src/main.rs create mode 100644 src/uu/vdir/src/vdir.rs diff --git a/Cargo.lock b/Cargo.lock index f9570807685..46f3ba8309b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -446,6 +446,7 @@ dependencies = [ "uu_unlink", "uu_uptime", "uu_users", + "uu_vdir", "uu_wc", "uu_who", "uu_whoami", @@ -3148,6 +3149,26 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_vdir" +version = "0.0.13" +dependencies = [ + "atty", + "chrono", + "clap 3.1.8", + "glob", + "lazy_static", + "lscolors", + "number_prefix", + "once_cell", + "selinux", + "term_grid", + "termsize", + "unicode-width", + "uu_ls", + "uucore", +] + [[package]] name = "uu_wc" version = "0.0.13" diff --git a/Cargo.toml b/Cargo.toml index 3c904cd163d..f5932b1cf6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,6 +101,7 @@ feat_common_core = [ "unexpand", "uniq", "unlink", + "vdir", "wc", "yes", ] @@ -354,6 +355,7 @@ uniq = { optional=true, version="0.0.13", package="uu_uniq", path="src/uu/un unlink = { optional=true, version="0.0.13", package="uu_unlink", path="src/uu/unlink" } uptime = { optional=true, version="0.0.13", package="uu_uptime", path="src/uu/uptime" } users = { optional=true, version="0.0.13", package="uu_users", path="src/uu/users" } +vdir = { optional=true, version="0.0.13", package="uu_vdir", path="src/uu/vdir" } wc = { optional=true, version="0.0.13", package="uu_wc", path="src/uu/wc" } who = { optional=true, version="0.0.13", package="uu_who", path="src/uu/who" } whoami = { optional=true, version="0.0.13", package="uu_whoami", path="src/uu/whoami" } diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 171c8283cfe..5e8db0513ea 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -15,7 +15,7 @@ extern crate lazy_static; mod quoting_style; -use clap::{crate_version, Arg, ArgMatches, Command}; +use clap::{crate_version, Arg, Command}; use glob::Pattern; use lscolors::LsColors; use number_prefix::NumberPrefix; @@ -30,7 +30,6 @@ use std::{ fmt::Display, fs::{self, DirEntry, FileType, Metadata, ReadDir}, io::{stdout, BufWriter, ErrorKind, Stdout, Write}, - option, path::{Path, PathBuf}, time::{SystemTime, UNIX_EPOCH}, }; @@ -1418,6 +1417,9 @@ pub fn dir_main(args: impl uucore::Args) -> UResult<()> { let mut default_quoting_style = false; let mut default_format_style = false; + // We check if any options on formatting or quoting style have been given. + // If not, we will use dir default formatting and quoting style options + if !matches.is_present(options::QUOTING_STYLE) && !matches.is_present(options::quoting::C) && !matches.is_present(options::quoting::ESCAPE) @@ -1439,10 +1441,6 @@ pub fn dir_main(args: impl uucore::Args) -> UResult<()> { } let mut config = Config::from(&matches)?; - // config.quoting_style = QuotingStyle::C { - // quotes: Quotes::None, - // }; - // config.format = Format::Columns; if default_quoting_style { config.quoting_style = QuotingStyle::C { @@ -2073,6 +2071,668 @@ only ignore '.' and '..'.", Also the TIME_STYLE environment variable sets the default style to use.", ) } + +// The entry point for the vdir coreutil +pub fn vdir_main(args: impl uucore::Args) -> UResult<()> { + let command = vdir_uu_app(); + let matches = command.get_matches_from(args); + + let mut default_quoting_style = false; + let mut default_format_style = false; + + // We check if any options on formatting or quoting style have been given. + // If not, we will use dir default formatting and quoting style options + + if !matches.is_present(options::QUOTING_STYLE) + && !matches.is_present(options::quoting::C) + && !matches.is_present(options::quoting::ESCAPE) + && !matches.is_present(options::quoting::LITERAL) + { + default_quoting_style = true; + } + if !matches.is_present(options::FORMAT) + && !matches.is_present(options::format::ACROSS) + && !matches.is_present(options::format::COLUMNS) + && !matches.is_present(options::format::COMMAS) + && !matches.is_present(options::format::LONG) + && !matches.is_present(options::format::LONG_NO_GROUP) + && !matches.is_present(options::format::LONG_NO_OWNER) + && !matches.is_present(options::format::LONG_NUMERIC_UID_GID) + && !matches.is_present(options::format::ONE_LINE) + { + default_format_style = true; + } + + let mut config = Config::from(&matches)?; + + if default_quoting_style { + config.quoting_style = QuotingStyle::C { + quotes: Quotes::None, + }; + } + if default_format_style { + config.format = Format::Long; + } + + let locs = matches + .values_of_os(options::PATHS) + .map(|v| v.map(Path::new).collect()) + .unwrap_or_else(|| vec![Path::new(".")]); + list(locs, &config) +} + +fn vdir_uu_app<'a>() -> Command<'a> { + Command::new(uucore::util_name()) + .version(crate_version!()) + .override_usage(format_usage(USAGE)) + .about( + "vdir is a shortcut to ls -l -b", + ) + .infer_long_args(true) + .arg( + Arg::new(options::HELP) + .long(options::HELP) + .help("Print help information.") + ) + // Format arguments + .arg( + Arg::new(options::FORMAT) + .long(options::FORMAT) + .help("Set the display format.") + .takes_value(true) + .possible_values(&[ + "long", + "verbose", + "single-column", + "columns", + "vertical", + "across", + "horizontal", + "commas", + ]) + .hide_possible_values(true) + .require_equals(true) + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::COLUMNS) + .short('C') + .help("Display the files in columns.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::LONG) + .short('l') + .long(options::format::LONG) + .help("Display detailed information.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::ACROSS) + .short('x') + .help("List entries in rows instead of in columns.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::COMMAS) + .short('m') + .help("List entries separated by commas.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + // The next four arguments do not override with the other format + // options, see the comment in Config::from for the reason. + // Ideally, they would use Arg::override_with, with their own name + // but that doesn't seem to work in all cases. Example: + // ls -1g1 + // even though `ls -11` and `ls -1 -g -1` work. + .arg( + Arg::new(options::format::ONE_LINE) + .short('1') + .help("List one file per line.") + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NO_GROUP) + .short('o') + .help( + "Long format without group information. \ + Identical to --format=long with --no-group.", + ) + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NO_OWNER) + .short('g') + .help("Long format without owner information.") + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NUMERIC_UID_GID) + .short('n') + .long(options::format::LONG_NUMERIC_UID_GID) + .help("-l with numeric UIDs and GIDs.") + .multiple_occurrences(true), + ) + // Quoting style + .arg( + Arg::new(options::QUOTING_STYLE) + .long(options::QUOTING_STYLE) + .takes_value(true) + .help("Set quoting style.") + .possible_values(&[ + "literal", + "shell", + "shell-always", + "shell-escape", + "shell-escape-always", + "c", + "escape", + ]) + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + .arg( + Arg::new(options::quoting::LITERAL) + .short('N') + .long(options::quoting::LITERAL) + .help("Use literal quoting style. Equivalent to `--quoting-style=literal`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + .arg( + Arg::new(options::quoting::ESCAPE) + .short('b') + .long(options::quoting::ESCAPE) + .help("Use escape quoting style. Equivalent to `--quoting-style=escape`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + .arg( + Arg::new(options::quoting::C) + .short('Q') + .long(options::quoting::C) + .help("Use C quoting style. Equivalent to `--quoting-style=c`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + // Control characters + .arg( + Arg::new(options::HIDE_CONTROL_CHARS) + .short('q') + .long(options::HIDE_CONTROL_CHARS) + .help("Replace control characters with '?' if they are not escaped.") + .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), + ) + .arg( + Arg::new(options::SHOW_CONTROL_CHARS) + .long(options::SHOW_CONTROL_CHARS) + .help("Show control characters 'as is' if they are not escaped.") + .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), + ) + // Time arguments + .arg( + Arg::new(options::TIME) + .long(options::TIME) + .help( + "Show time in :\n\ + \taccess time (-u): atime, access, use;\n\ + \tchange time (-t): ctime, status.\n\ + \tbirth time: birth, creation;", + ) + .value_name("field") + .takes_value(true) + .possible_values(&[ + "atime", "access", "use", "ctime", "status", "birth", "creation", + ]) + .hide_possible_values(true) + .require_equals(true) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + .arg( + Arg::new(options::time::CHANGE) + .short('c') + .help( + "If the long listing format (e.g., -l, -o) is being used, print the status \ + change time (the 'ctime' in the inode) instead of the modification 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.", + ) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + .arg( + Arg::new(options::time::ACCESS) + .short('u') + .help( + "If the long listing format (e.g., -l, -o) is being used, print the status \ + access time instead of the modification time. When explicitly sorting by time \ + (--sort=time or -t) or when not using a long listing format, sort according to the \ + access time.", + ) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + // Hide and ignore + .arg( + Arg::new(options::HIDE) + .long(options::HIDE) + .takes_value(true) + .multiple_occurrences(true) + .value_name("PATTERN") + .help( + "do not list implied entries matching shell PATTERN (overridden by -a or -A)", + ), + ) + .arg( + Arg::new(options::IGNORE) + .short('I') + .long(options::IGNORE) + .takes_value(true) + .multiple_occurrences(true) + .value_name("PATTERN") + .help("do not list implied entries matching shell PATTERN"), + ) + .arg( + Arg::new(options::IGNORE_BACKUPS) + .short('B') + .long(options::IGNORE_BACKUPS) + .help("Ignore entries which end with ~."), + ) + // Sort arguments + .arg( + Arg::new(options::SORT) + .long(options::SORT) + .help("Sort by : name, none (-U), time (-t), size (-S) or extension (-X)") + .value_name("field") + .takes_value(true) + .possible_values(&["name", "none", "time", "size", "version", "extension"]) + .require_equals(true) + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::SIZE) + .short('S') + .help("Sort by file size, largest first.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::TIME) + .short('t') + .help("Sort by modification time (the 'mtime' in the inode), newest first.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::VERSION) + .short('v') + .help("Natural sort of (version) numbers in the filenames.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::EXTENSION) + .short('X') + .help("Sort alphabetically by entry extension.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::NONE) + .short('U') + .help( + "Do not sort; list the files in whatever order they are stored in the \ + directory. This is especially useful when listing very large directories, \ + since not doing any sorting can be noticeably faster.", + ) + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + // Dereferencing + .arg( + Arg::new(options::dereference::ALL) + .short('L') + .long(options::dereference::ALL) + .help( + "When showing file information for a symbolic link, show information for the \ + file the link references rather than the link itself.", + ) + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + .arg( + Arg::new(options::dereference::DIR_ARGS) + .long(options::dereference::DIR_ARGS) + .help( + "Do not dereference symlinks except when they link to directories and are \ + given as command line arguments.", + ) + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + .arg( + Arg::new(options::dereference::ARGS) + .short('H') + .long(options::dereference::ARGS) + .help("Do not dereference symlinks except when given as command line arguments.") + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + // Long format options + .arg( + Arg::new(options::NO_GROUP) + .long(options::NO_GROUP) + .short('G') + .help("Do not show group in long format."), + ) + .arg(Arg::new(options::AUTHOR).long(options::AUTHOR).help( + "Show author in long format. \ + On the supported platforms, the author always matches the file owner.", + )) + // Other Flags + .arg( + Arg::new(options::files::ALL) + .short('a') + .long(options::files::ALL) + // Overrides -A (as the order matters) + .overrides_with(options::files::ALMOST_ALL) + .multiple_occurrences(true) + .help("Do not ignore hidden files (files with names that start with '.')."), + ) + .arg( + Arg::new(options::files::ALMOST_ALL) + .short('A') + .long(options::files::ALMOST_ALL) + // Overrides -a (as the order matters) + .overrides_with(options::files::ALL) + .multiple_occurrences(true) + .help( + "In a directory, do not ignore all file names that start with '.', \ +only ignore '.' and '..'.", + ), + ) + .arg( + Arg::new(options::DIRECTORY) + .short('d') + .long(options::DIRECTORY) + .help( + "Only list the names of directories, rather than listing directory contents. \ + This will not follow symbolic links unless one of `--dereference-command-line \ + (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \ + specified.", + ), + ) + .arg( + Arg::new(options::size::HUMAN_READABLE) + .short('h') + .long(options::size::HUMAN_READABLE) + .help("Print human readable file sizes (e.g. 1K 234M 56G).") + .overrides_with(options::size::SI), + ) + .arg( + Arg::new(options::size::KIBIBYTES) + .short('k') + .long(options::size::KIBIBYTES) + .help("default to 1024-byte blocks for file system usage; used only with -s and per directory totals"), + ) + .arg( + Arg::new(options::size::SI) + .long(options::size::SI) + .help("Print human readable file sizes using powers of 1000 instead of 1024."), + ) + .arg( + Arg::new(options::size::BLOCK_SIZE) + .long(options::size::BLOCK_SIZE) + .takes_value(true) + .require_equals(true) + .value_name("BLOCK_SIZE") + .help("scale sizes by BLOCK_SIZE when printing them"), + ) + .arg( + Arg::new(options::INODE) + .short('i') + .long(options::INODE) + .help("print the index number of each file"), + ) + .arg( + Arg::new(options::REVERSE) + .short('r') + .long(options::REVERSE) + .help( + "Reverse whatever the sorting method is e.g., list files in reverse \ + alphabetical order, youngest first, smallest first, or whatever.", + ), + ) + .arg( + Arg::new(options::RECURSIVE) + .short('R') + .long(options::RECURSIVE) + .help("List the contents of all directories recursively."), + ) + .arg( + Arg::new(options::WIDTH) + .long(options::WIDTH) + .short('w') + .help("Assume that the terminal is COLS columns wide.") + .value_name("COLS") + .takes_value(true), + ) + .arg( + Arg::new(options::size::ALLOCATION_SIZE) + .short('s') + .long(options::size::ALLOCATION_SIZE) + .help("print the allocated size of each file, in blocks"), + ) + .arg( + Arg::new(options::COLOR) + .long(options::COLOR) + .help("Color output based on file type.") + .takes_value(true) + .possible_values(&[ + "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", + ]) + .require_equals(true) + .min_values(0), + ) + .arg( + Arg::new(options::INDICATOR_STYLE) + .long(options::INDICATOR_STYLE) + .help( + "Append indicator with style WORD to entry names: \ + none (default), slash (-p), file-type (--file-type), classify (-F)", + ) + .takes_value(true) + .possible_values(&["none", "slash", "file-type", "classify"]) + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + // The --classify flag can take an optional when argument to + // control its behavior from version 9 of GNU coreutils. + // There is currently an inconsistency where GNU coreutils allows only + // the long form of the flag to take the argument while we allow it + // for both the long and short form of the flag. + Arg::new(options::indicator_style::CLASSIFY) + .short('F') + .long(options::indicator_style::CLASSIFY) + .help( + "Append a character to each file name indicating the file type. Also, for \ + regular files that are executable, append '*'. The file type indicators are \ + '/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \ + '>' for doors, and nothing for regular files. when may be omitted, or one of:\n\ + \tnone - Do not classify. This is the default.\n\ + \tauto - Only classify if standard output is a terminal.\n\ + \talways - Always classify.\n\ + Specifying --classify and no when is equivalent to --classify=always. This will not follow\ + symbolic links listed on the command line unless the --dereference-command-line (-H),\ + --dereference (-L), or --dereference-command-line-symlink-to-dir options are specified.", + ) + .takes_value(true) + .value_name("when") + .possible_values(&[ + "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", + ]) + .default_missing_value("always") + .require_equals(true) + .min_values(0) + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + Arg::new(options::indicator_style::FILE_TYPE) + .long(options::indicator_style::FILE_TYPE) + .help("Same as --classify, but do not append '*'") + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + Arg::new(options::indicator_style::SLASH) + .short('p') + .help("Append / indicator to directories.") + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + //This still needs support for posix-*, +FORMAT + Arg::new(options::TIME_STYLE) + .long(options::TIME_STYLE) + .help("time/date format with -l; see TIME_STYLE below") + .value_name("TIME_STYLE") + .env("TIME_STYLE") + .possible_values(&["full-iso", "long-iso", "iso", "locale"]) + .overrides_with_all(&[options::TIME_STYLE]), + ) + .arg( + Arg::new(options::FULL_TIME) + .long(options::FULL_TIME) + .overrides_with(options::FULL_TIME) + .help("like -l --time-style=full-iso"), + ) + .arg( + Arg::new(options::CONTEXT) + .short('Z') + .long(options::CONTEXT) + .help(CONTEXT_HELP_TEXT), + ) + // Positional arguments + .arg( + Arg::new(options::PATHS) + .multiple_occurrences(true) + .takes_value(true) + .allow_invalid_utf8(true), + ) + .after_help( + "The TIME_STYLE argument can be full-iso, long-iso, iso. \ + Also the TIME_STYLE environment variable sets the default style to use.", + ) +} + /// Represents a Path along with it's associated data. /// Any data that will be reused several times makes sense to be added to this structure. /// Caching data here helps eliminate redundant syscalls to fetch same information. diff --git a/src/uu/vdir/Cargo.toml b/src/uu/vdir/Cargo.toml new file mode 100644 index 00000000000..78b9569bb7f --- /dev/null +++ b/src/uu/vdir/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "uu_vdir" +version = "0.0.13" +authors = ["uutils developers"] +license = "MIT" +description = "shortcut to ls -l -b" + +homepage = "https://github.com/uutils/coreutils" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls" +keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] +categories = ["command-line-utilities"] +edition = "2021" + +[lib] +path = "src/vdir.rs" + +[dependencies] +chrono = "0.4.19" +clap = { version = "3.1", features = ["wrap_help", "cargo", "env"] } +unicode-width = "0.1.8" +number_prefix = "0.4" +term_grid = "0.1.5" +termsize = "0.1.6" +glob = "0.3.0" +lscolors = { version = "0.9.0", features = ["ansi_term"] } +uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } +once_cell = "1.10.0" +atty = "0.2" +selinux = { version="0.2", optional = true } +uu_ls = {path="../ls"} + +[target.'cfg(unix)'.dependencies] +lazy_static = "1.4.0" + +[[bin]] +name = "dir" +path = "src/main.rs" + +[features] +feat_selinux = ["selinux"] diff --git a/src/uu/vdir/src/main.rs b/src/uu/vdir/src/main.rs new file mode 100644 index 00000000000..9d9ba9de0c3 --- /dev/null +++ b/src/uu/vdir/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_vdir); diff --git a/src/uu/vdir/src/vdir.rs b/src/uu/vdir/src/vdir.rs new file mode 100644 index 00000000000..f3462d31797 --- /dev/null +++ b/src/uu/vdir/src/vdir.rs @@ -0,0 +1,25 @@ +// * This file is part of the uutils coreutils package. +// * +// * (c) gmnsii +// * +// * For the full copyright and license information, please view the LICENSE file +// * that was distributed with this source code. + +use clap::Command; +use uucore::error::UResult; + +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + uu_ls::vdir_main(args) +} + +// Coreutils won't compile if not every util have an uu_app function. +// However, we can't put it here since it needs to be in the same place as +// the entry point for the vdir util, which is in ls/ls.rs. We could put the +// entry point here, but we would need a lot of refactoring and to make a lot of +// ls functions and structs public. +// +// To make our life easier, we use this dummy function. +pub fn uu_app<'a>() -> Command<'a> { + Command::new(uucore::util_name()) +} From 965d8ba939295dbc6cc19735c51128674df5d8d9 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 06:55:28 -0700 Subject: [PATCH 05/20] accidentally make Config struct public --- src/uu/ls/src/ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 5e8db0513ea..94a024287b8 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -304,7 +304,7 @@ enum IndicatorStyle { Classify, } -pub struct Config { +struct Config { format: Format, files: Files, sort: Sort, From e8f63bc36c0383ebed26d87ff19088f7aee695a5 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 07:09:07 -0700 Subject: [PATCH 06/20] Add symlink to licenses for dir and vdir --- src/uu/vdir/LICENSE | 1 + 1 file changed, 1 insertion(+) create mode 120000 src/uu/vdir/LICENSE diff --git a/src/uu/vdir/LICENSE b/src/uu/vdir/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/src/uu/vdir/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file From bb4418a425b0b12184d62d34bdcac3b8d5a367dd Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 08:14:23 -0700 Subject: [PATCH 07/20] Add tests --- tests/by-util/test_dir.rs | 59 ++++++++++++++++++++++++++++++++++++++ tests/by-util/test_vdir.rs | 59 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 tests/by-util/test_dir.rs create mode 100644 tests/by-util/test_vdir.rs diff --git a/tests/by-util/test_dir.rs b/tests/by-util/test_dir.rs new file mode 100644 index 00000000000..4c57356c5da --- /dev/null +++ b/tests/by-util/test_dir.rs @@ -0,0 +1,59 @@ +#[cfg(not(windows))] +extern crate libc; +extern crate regex; +#[cfg(not(windows))] +extern crate tempfile; +#[cfg(unix)] +extern crate unix_socket; + +use self::regex::Regex; +use crate::common::util::*; +#[cfg(all(unix, feature = "chmod"))] +use nix::unistd::{close, dup}; +#[cfg(all(unix, feature = "chmod"))] +use std::os::unix::io::IntoRawFd; + +/* + * As dir use the same functions than ls, we don't have to retest them here. + * We just test the default and the long output +*/ + +#[test] +fn test_dir() { + new_ucmd!().succeeds(); +} + +#[test] +fn test_default_output() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkdir("some-dir1"); + at.touch("some-file1"); + + scene.ucmd().succeeds().stdout_contains("some-file1"); + + scene + .ucmd() + .succeeds() + .stdout_does_not_match(&Regex::new("[rwx][^some-file1]").unwrap()); +} + +#[test] +fn test_long_output() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkdir("some-dir1"); + at.touch("some-file1"); + + scene + .ucmd() + .arg("-l") + .succeeds() + .stdout_contains("some-file1"); + + scene + .ucmd() + .arg("-l") + .succeeds() + .stdout_matches(&Regex::new("[rwx][^some-file1]").unwrap()); +} diff --git a/tests/by-util/test_vdir.rs b/tests/by-util/test_vdir.rs new file mode 100644 index 00000000000..bb2e4b80177 --- /dev/null +++ b/tests/by-util/test_vdir.rs @@ -0,0 +1,59 @@ +#[cfg(not(windows))] +extern crate libc; +extern crate regex; +#[cfg(not(windows))] +extern crate tempfile; +#[cfg(unix)] +extern crate unix_socket; + +use self::regex::Regex; +use crate::common::util::*; +#[cfg(all(unix, feature = "chmod"))] +use nix::unistd::{close, dup}; +#[cfg(all(unix, feature = "chmod"))] +use std::os::unix::io::IntoRawFd; + +/* + * As vdir use the same functions than ls, we don't have to retest them here. + * We just test the default and the column output +*/ + +#[test] +fn test_vdir() { + new_ucmd!().succeeds(); +} + +#[test] +fn test_default_output() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkdir("some-dir1"); + at.touch("some-file1"); + + scene.ucmd().succeeds().stdout_contains("some-file1"); + + scene + .ucmd() + .succeeds() + .stdout_matches(&Regex::new("[rwx][^some-file1]").unwrap()); +} + +#[test] +fn test_column_output() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkdir("some-dir1"); + at.touch("some-file1"); + + scene + .ucmd() + .arg("-C") + .succeeds() + .stdout_contains("some-file1"); + + scene + .ucmd() + .arg("-C") + .succeeds() + .stdout_does_not_match(&Regex::new("[rwx][^some-file1]").unwrap()); +} From d486319d34545c3152c3a2ff1a860af3e27f5839 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 09:50:41 -0700 Subject: [PATCH 08/20] Remove unused dependencies and imports --- Cargo.lock | 18 ------------------ src/uu/dir/Cargo.toml | 9 --------- src/uu/vdir/Cargo.toml | 20 ++++++++++---------- tests/by-util/test_dir.rs | 4 ---- tests/by-util/test_vdir.rs | 4 ---- 5 files changed, 10 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46f3ba8309b..7c1d0b58132 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2382,18 +2382,9 @@ dependencies = [ name = "uu_dir" version = "0.0.13" dependencies = [ - "atty", - "chrono", "clap 3.1.8", - "glob", "lazy_static", - "lscolors", - "number_prefix", - "once_cell", "selinux", - "term_grid", - "termsize", - "unicode-width", "uu_ls", "uucore", ] @@ -3153,18 +3144,9 @@ dependencies = [ name = "uu_vdir" version = "0.0.13" dependencies = [ - "atty", - "chrono", "clap 3.1.8", - "glob", "lazy_static", - "lscolors", - "number_prefix", - "once_cell", "selinux", - "term_grid", - "termsize", - "unicode-width", "uu_ls", "uucore", ] diff --git a/src/uu/dir/Cargo.toml b/src/uu/dir/Cargo.toml index 8da834ac3c4..91518485cae 100644 --- a/src/uu/dir/Cargo.toml +++ b/src/uu/dir/Cargo.toml @@ -15,17 +15,8 @@ edition = "2021" path = "src/dir.rs" [dependencies] -chrono = "0.4.19" clap = { version = "3.1", features = ["wrap_help", "cargo", "env"] } -unicode-width = "0.1.8" -number_prefix = "0.4" -term_grid = "0.1.5" -termsize = "0.1.6" -glob = "0.3.0" -lscolors = { version = "0.9.0", features = ["ansi_term"] } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } -once_cell = "1.10.0" -atty = "0.2" selinux = { version="0.2", optional = true } uu_ls = {path="../ls"} diff --git a/src/uu/vdir/Cargo.toml b/src/uu/vdir/Cargo.toml index 78b9569bb7f..72bd00ce304 100644 --- a/src/uu/vdir/Cargo.toml +++ b/src/uu/vdir/Cargo.toml @@ -15,17 +15,17 @@ edition = "2021" path = "src/vdir.rs" [dependencies] -chrono = "0.4.19" +#chrono = "0.4.19" clap = { version = "3.1", features = ["wrap_help", "cargo", "env"] } -unicode-width = "0.1.8" -number_prefix = "0.4" -term_grid = "0.1.5" -termsize = "0.1.6" -glob = "0.3.0" -lscolors = { version = "0.9.0", features = ["ansi_term"] } +#unicode-width = "0.1.8" +#number_prefix = "0.4" +##term_grid = "0.1.5" +#termsize = "0.1.6" +#glob = "0.3.0" +#lscolors = { version = "0.9.0", features = ["ansi_term"] } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } -once_cell = "1.10.0" -atty = "0.2" +#once_cell = "1.10.0" +#atty = "0.2" selinux = { version="0.2", optional = true } uu_ls = {path="../ls"} @@ -33,7 +33,7 @@ uu_ls = {path="../ls"} lazy_static = "1.4.0" [[bin]] -name = "dir" +name = "vdir" path = "src/main.rs" [features] diff --git a/tests/by-util/test_dir.rs b/tests/by-util/test_dir.rs index 4c57356c5da..3ec416bb2ac 100644 --- a/tests/by-util/test_dir.rs +++ b/tests/by-util/test_dir.rs @@ -8,10 +8,6 @@ extern crate unix_socket; use self::regex::Regex; use crate::common::util::*; -#[cfg(all(unix, feature = "chmod"))] -use nix::unistd::{close, dup}; -#[cfg(all(unix, feature = "chmod"))] -use std::os::unix::io::IntoRawFd; /* * As dir use the same functions than ls, we don't have to retest them here. diff --git a/tests/by-util/test_vdir.rs b/tests/by-util/test_vdir.rs index bb2e4b80177..01c54009595 100644 --- a/tests/by-util/test_vdir.rs +++ b/tests/by-util/test_vdir.rs @@ -8,10 +8,6 @@ extern crate unix_socket; use self::regex::Regex; use crate::common::util::*; -#[cfg(all(unix, feature = "chmod"))] -use nix::unistd::{close, dup}; -#[cfg(all(unix, feature = "chmod"))] -use std::os::unix::io::IntoRawFd; /* * As vdir use the same functions than ls, we don't have to retest them here. From 6887161108ca85d6af4f997db8ce67fad3bf5a7e Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 09:51:22 -0700 Subject: [PATCH 09/20] Add cspell words --- .vscode/cspell.dictionaries/jargon.wordlist.txt | 1 + .vscode/cspell.dictionaries/people.wordlist.txt | 1 + .vscode/cspell.dictionaries/shell.wordlist.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 13f340f6aa3..dcd0ac7b5b5 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -17,6 +17,7 @@ colorize coprime consts conv +coreutil cyclomatic dedup deduplication diff --git a/.vscode/cspell.dictionaries/people.wordlist.txt b/.vscode/cspell.dictionaries/people.wordlist.txt index 40573383613..8fe38d88538 100644 --- a/.vscode/cspell.dictionaries/people.wordlist.txt +++ b/.vscode/cspell.dictionaries/people.wordlist.txt @@ -179,3 +179,4 @@ Smigle00 anonymousknight kwantam nicoo +gmnsii diff --git a/.vscode/cspell.dictionaries/shell.wordlist.txt b/.vscode/cspell.dictionaries/shell.wordlist.txt index 4ed281efbff..16d7b25e997 100644 --- a/.vscode/cspell.dictionaries/shell.wordlist.txt +++ b/.vscode/cspell.dictionaries/shell.wordlist.txt @@ -93,6 +93,7 @@ rollup sed selinuxenabled sestatus +vdir wslpath xargs From e2b429e776ecacbd027a7d9d62a9c8c3d68c3c6d Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 10:00:14 -0700 Subject: [PATCH 10/20] Update comments and help context --- src/uu/dir/src/dir.rs | 5 ++--- src/uu/ls/src/ls.rs | 10 +++++----- src/uu/vdir/src/vdir.rs | 5 ++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/uu/dir/src/dir.rs b/src/uu/dir/src/dir.rs index 56be6b3b247..dbeac6d0985 100644 --- a/src/uu/dir/src/dir.rs +++ b/src/uu/dir/src/dir.rs @@ -14,10 +14,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } // Coreutils won't compile if not every util have an uu_app function. -// However, we can't put it here since it needs to be in the same place as +// However, we can't put it here since it needs to be in the same place as // the entry point for the dir util, which is in ls/ls.rs. We could put the -// entry point here, but we would need a lot of refactoring and to make a lot of -// ls functions and structs public. +// entry point here, but we would need a lot of refactoring. // // To make our life easier, we use this dummy function. pub fn uu_app<'a>() -> Command<'a> { diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 94a024287b8..621fd56e8ef 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -802,7 +802,9 @@ pub fn uu_app<'a>() -> Command<'a> { .version(crate_version!()) .override_usage(format_usage(USAGE)) .about( - "dir is a shortcut for ls -C -b", + "By default, ls will list the files and contents of any directories on \ + the command line, expect that it will ignore files and directories \ + whose names start with '.'.", ) .infer_long_args(true) .arg( @@ -1463,9 +1465,7 @@ fn dir_uu_app<'a>() -> Command<'a> { .version(crate_version!()) .override_usage(format_usage(USAGE)) .about( - "By default, ls will list the files and contents of any directories on \ - the command line, expect that it will ignore files and directories \ - whose names start with '.'.", + "dir is a shortcut for ls -C -b. All valid ls options are also valid dir options", ) .infer_long_args(true) .arg( @@ -2126,7 +2126,7 @@ fn vdir_uu_app<'a>() -> Command<'a> { .version(crate_version!()) .override_usage(format_usage(USAGE)) .about( - "vdir is a shortcut to ls -l -b", + "dir is a shortcut for ls -l -b. All valid ls options are also valid dir options", ) .infer_long_args(true) .arg( diff --git a/src/uu/vdir/src/vdir.rs b/src/uu/vdir/src/vdir.rs index f3462d31797..329be198309 100644 --- a/src/uu/vdir/src/vdir.rs +++ b/src/uu/vdir/src/vdir.rs @@ -14,10 +14,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } // Coreutils won't compile if not every util have an uu_app function. -// However, we can't put it here since it needs to be in the same place as +// However, we can't put it here since it needs to be in the same place as // the entry point for the vdir util, which is in ls/ls.rs. We could put the -// entry point here, but we would need a lot of refactoring and to make a lot of -// ls functions and structs public. +// entry point here, but we would need a lot of refactoring. // // To make our life easier, we use this dummy function. pub fn uu_app<'a>() -> Command<'a> { From cc460b77c8447cf62b360887f2db35ddd7574f76 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 10:07:52 -0700 Subject: [PATCH 11/20] Update comments and help context --- src/uu/dir/src/dir.rs | 1 - src/uu/ls/src/ls.rs | 12 ++++++------ src/uu/vdir/src/vdir.rs | 1 - 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/uu/dir/src/dir.rs b/src/uu/dir/src/dir.rs index dbeac6d0985..53be923b06a 100644 --- a/src/uu/dir/src/dir.rs +++ b/src/uu/dir/src/dir.rs @@ -17,7 +17,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // However, we can't put it here since it needs to be in the same place as // the entry point for the dir util, which is in ls/ls.rs. We could put the // entry point here, but we would need a lot of refactoring. -// // To make our life easier, we use this dummy function. pub fn uu_app<'a>() -> Command<'a> { Command::new(uucore::util_name()) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 621fd56e8ef..1c86573de04 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1552,8 +1552,8 @@ fn dir_uu_app<'a>() -> Command<'a> { // options, see the comment in Config::from for the reason. // Ideally, they would use Arg::override_with, with their own name // but that doesn't seem to work in all cases. Example: - // ls -1g1 - // even though `ls -11` and `ls -1 -g -1` work. + // dir -1g1 + // even though `dir -11` and `dir -1 -g -1` work. .arg( Arg::new(options::format::ONE_LINE) .short('1') @@ -2072,7 +2072,7 @@ only ignore '.' and '..'.", ) } -// The entry point for the vdir coreutil +/// The entry point for the vdir coreutil pub fn vdir_main(args: impl uucore::Args) -> UResult<()> { let command = vdir_uu_app(); let matches = command.get_matches_from(args); @@ -2126,7 +2126,7 @@ fn vdir_uu_app<'a>() -> Command<'a> { .version(crate_version!()) .override_usage(format_usage(USAGE)) .about( - "dir is a shortcut for ls -l -b. All valid ls options are also valid dir options", + "vdir is a shortcut for ls -l -b. All valid ls options are also valid vdir options", ) .infer_long_args(true) .arg( @@ -2213,8 +2213,8 @@ fn vdir_uu_app<'a>() -> Command<'a> { // options, see the comment in Config::from for the reason. // Ideally, they would use Arg::override_with, with their own name // but that doesn't seem to work in all cases. Example: - // ls -1g1 - // even though `ls -11` and `ls -1 -g -1` work. + // vdir -1g1 + // even though `vdir -11` and `vdir -1 -g -1` work. .arg( Arg::new(options::format::ONE_LINE) .short('1') diff --git a/src/uu/vdir/src/vdir.rs b/src/uu/vdir/src/vdir.rs index 329be198309..c6b07a167db 100644 --- a/src/uu/vdir/src/vdir.rs +++ b/src/uu/vdir/src/vdir.rs @@ -17,7 +17,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // However, we can't put it here since it needs to be in the same place as // the entry point for the vdir util, which is in ls/ls.rs. We could put the // entry point here, but we would need a lot of refactoring. -// // To make our life easier, we use this dummy function. pub fn uu_app<'a>() -> Command<'a> { Command::new(uucore::util_name()) From ba8b3f74cc7763aee46590aa809aa2e522f5bb07 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 10:09:16 -0700 Subject: [PATCH 12/20] Update Cargo.toml --- src/uu/vdir/Cargo.toml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/uu/vdir/Cargo.toml b/src/uu/vdir/Cargo.toml index 72bd00ce304..ab2e137b5d3 100644 --- a/src/uu/vdir/Cargo.toml +++ b/src/uu/vdir/Cargo.toml @@ -15,17 +15,8 @@ edition = "2021" path = "src/vdir.rs" [dependencies] -#chrono = "0.4.19" clap = { version = "3.1", features = ["wrap_help", "cargo", "env"] } -#unicode-width = "0.1.8" -#number_prefix = "0.4" -##term_grid = "0.1.5" -#termsize = "0.1.6" -#glob = "0.3.0" -#lscolors = { version = "0.9.0", features = ["ansi_term"] } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } -#once_cell = "1.10.0" -#atty = "0.2" selinux = { version="0.2", optional = true } uu_ls = {path="../ls"} From 7e5de7e9c4bd21b8a4e9c7153ce6c638a46de830 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 10:18:42 -0700 Subject: [PATCH 13/20] Remove unused dir and vdir dependencies --- Cargo.lock | 2 -- src/uu/dir/Cargo.toml | 6 ------ src/uu/vdir/Cargo.toml | 6 ------ 3 files changed, 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c1d0b58132..c1dec695dfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2383,7 +2383,6 @@ name = "uu_dir" version = "0.0.13" dependencies = [ "clap 3.1.8", - "lazy_static", "selinux", "uu_ls", "uucore", @@ -3145,7 +3144,6 @@ name = "uu_vdir" version = "0.0.13" dependencies = [ "clap 3.1.8", - "lazy_static", "selinux", "uu_ls", "uucore", diff --git a/src/uu/dir/Cargo.toml b/src/uu/dir/Cargo.toml index 91518485cae..542abdc630e 100644 --- a/src/uu/dir/Cargo.toml +++ b/src/uu/dir/Cargo.toml @@ -20,12 +20,6 @@ uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", featu selinux = { version="0.2", optional = true } uu_ls = {path="../ls"} -[target.'cfg(unix)'.dependencies] -lazy_static = "1.4.0" - [[bin]] name = "dir" path = "src/main.rs" - -[features] -feat_selinux = ["selinux"] diff --git a/src/uu/vdir/Cargo.toml b/src/uu/vdir/Cargo.toml index ab2e137b5d3..10dc07a255d 100644 --- a/src/uu/vdir/Cargo.toml +++ b/src/uu/vdir/Cargo.toml @@ -20,12 +20,6 @@ uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", featu selinux = { version="0.2", optional = true } uu_ls = {path="../ls"} -[target.'cfg(unix)'.dependencies] -lazy_static = "1.4.0" - [[bin]] name = "vdir" path = "src/main.rs" - -[features] -feat_selinux = ["selinux"] From 2b4ecfe85536167f6e29e02fd1bd2498bebdeb15 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 12:48:34 -0700 Subject: [PATCH 14/20] Update ABOUT context --- src/uu/ls/src/ls.rs | 2445 +++++++++++-------------------------------- 1 file changed, 609 insertions(+), 1836 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 1c86573de04..61a4fb4c0e7 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -797,623 +797,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { list(locs, &config) } -pub fn uu_app<'a>() -> Command<'a> { - Command::new(uucore::util_name()) - .version(crate_version!()) - .override_usage(format_usage(USAGE)) - .about( - "By default, ls will list the files and contents of any directories on \ - the command line, expect that it will ignore files and directories \ - whose names start with '.'.", - ) - .infer_long_args(true) - .arg( - Arg::new(options::HELP) - .long(options::HELP) - .help("Print help information.") - ) - // Format arguments - .arg( - Arg::new(options::FORMAT) - .long(options::FORMAT) - .help("Set the display format.") - .takes_value(true) - .possible_values(&[ - "long", - "verbose", - "single-column", - "columns", - "vertical", - "across", - "horizontal", - "commas", - ]) - .hide_possible_values(true) - .require_equals(true) - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::COLUMNS) - .short('C') - .help("Display the files in columns.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::LONG) - .short('l') - .long(options::format::LONG) - .help("Display detailed information.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::ACROSS) - .short('x') - .help("List entries in rows instead of in columns.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::COMMAS) - .short('m') - .help("List entries separated by commas.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - // The next four arguments do not override with the other format - // options, see the comment in Config::from for the reason. - // Ideally, they would use Arg::override_with, with their own name - // but that doesn't seem to work in all cases. Example: - // ls -1g1 - // even though `ls -11` and `ls -1 -g -1` work. - .arg( - Arg::new(options::format::ONE_LINE) - .short('1') - .help("List one file per line.") - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NO_GROUP) - .short('o') - .help( - "Long format without group information. \ - Identical to --format=long with --no-group.", - ) - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NO_OWNER) - .short('g') - .help("Long format without owner information.") - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NUMERIC_UID_GID) - .short('n') - .long(options::format::LONG_NUMERIC_UID_GID) - .help("-l with numeric UIDs and GIDs.") - .multiple_occurrences(true), - ) - // Quoting style - .arg( - Arg::new(options::QUOTING_STYLE) - .long(options::QUOTING_STYLE) - .takes_value(true) - .help("Set quoting style.") - .possible_values(&[ - "literal", - "shell", - "shell-always", - "shell-escape", - "shell-escape-always", - "c", - "escape", - ]) - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::LITERAL) - .short('N') - .long(options::quoting::LITERAL) - .help("Use literal quoting style. Equivalent to `--quoting-style=literal`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::ESCAPE) - .short('b') - .long(options::quoting::ESCAPE) - .help("Use escape quoting style. Equivalent to `--quoting-style=escape`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::C) - .short('Q') - .long(options::quoting::C) - .help("Use C quoting style. Equivalent to `--quoting-style=c`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - // Control characters - .arg( - Arg::new(options::HIDE_CONTROL_CHARS) - .short('q') - .long(options::HIDE_CONTROL_CHARS) - .help("Replace control characters with '?' if they are not escaped.") - .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), - ) - .arg( - Arg::new(options::SHOW_CONTROL_CHARS) - .long(options::SHOW_CONTROL_CHARS) - .help("Show control characters 'as is' if they are not escaped.") - .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), - ) - // Time arguments - .arg( - Arg::new(options::TIME) - .long(options::TIME) - .help( - "Show time in :\n\ - \taccess time (-u): atime, access, use;\n\ - \tchange time (-t): ctime, status.\n\ - \tbirth time: birth, creation;", - ) - .value_name("field") - .takes_value(true) - .possible_values(&[ - "atime", "access", "use", "ctime", "status", "birth", "creation", - ]) - .hide_possible_values(true) - .require_equals(true) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - .arg( - Arg::new(options::time::CHANGE) - .short('c') - .help( - "If the long listing format (e.g., -l, -o) is being used, print the status \ - change time (the 'ctime' in the inode) instead of the modification 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.", - ) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - .arg( - Arg::new(options::time::ACCESS) - .short('u') - .help( - "If the long listing format (e.g., -l, -o) is being used, print the status \ - access time instead of the modification time. When explicitly sorting by time \ - (--sort=time or -t) or when not using a long listing format, sort according to the \ - access time.", - ) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - // Hide and ignore - .arg( - Arg::new(options::HIDE) - .long(options::HIDE) - .takes_value(true) - .multiple_occurrences(true) - .value_name("PATTERN") - .help( - "do not list implied entries matching shell PATTERN (overridden by -a or -A)", - ), - ) - .arg( - Arg::new(options::IGNORE) - .short('I') - .long(options::IGNORE) - .takes_value(true) - .multiple_occurrences(true) - .value_name("PATTERN") - .help("do not list implied entries matching shell PATTERN"), - ) - .arg( - Arg::new(options::IGNORE_BACKUPS) - .short('B') - .long(options::IGNORE_BACKUPS) - .help("Ignore entries which end with ~."), - ) - // Sort arguments - .arg( - Arg::new(options::SORT) - .long(options::SORT) - .help("Sort by : name, none (-U), time (-t), size (-S) or extension (-X)") - .value_name("field") - .takes_value(true) - .possible_values(&["name", "none", "time", "size", "version", "extension"]) - .require_equals(true) - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::SIZE) - .short('S') - .help("Sort by file size, largest first.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::TIME) - .short('t') - .help("Sort by modification time (the 'mtime' in the inode), newest first.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::VERSION) - .short('v') - .help("Natural sort of (version) numbers in the filenames.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::EXTENSION) - .short('X') - .help("Sort alphabetically by entry extension.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::NONE) - .short('U') - .help( - "Do not sort; list the files in whatever order they are stored in the \ - directory. This is especially useful when listing very large directories, \ - since not doing any sorting can be noticeably faster.", - ) - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - // Dereferencing - .arg( - Arg::new(options::dereference::ALL) - .short('L') - .long(options::dereference::ALL) - .help( - "When showing file information for a symbolic link, show information for the \ - file the link references rather than the link itself.", - ) - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - .arg( - Arg::new(options::dereference::DIR_ARGS) - .long(options::dereference::DIR_ARGS) - .help( - "Do not dereference symlinks except when they link to directories and are \ - given as command line arguments.", - ) - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - .arg( - Arg::new(options::dereference::ARGS) - .short('H') - .long(options::dereference::ARGS) - .help("Do not dereference symlinks except when given as command line arguments.") - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - // Long format options - .arg( - Arg::new(options::NO_GROUP) - .long(options::NO_GROUP) - .short('G') - .help("Do not show group in long format."), - ) - .arg(Arg::new(options::AUTHOR).long(options::AUTHOR).help( - "Show author in long format. \ - On the supported platforms, the author always matches the file owner.", - )) - // Other Flags - .arg( - Arg::new(options::files::ALL) - .short('a') - .long(options::files::ALL) - // Overrides -A (as the order matters) - .overrides_with(options::files::ALMOST_ALL) - .multiple_occurrences(true) - .help("Do not ignore hidden files (files with names that start with '.')."), - ) - .arg( - Arg::new(options::files::ALMOST_ALL) - .short('A') - .long(options::files::ALMOST_ALL) - // Overrides -a (as the order matters) - .overrides_with(options::files::ALL) - .multiple_occurrences(true) - .help( - "In a directory, do not ignore all file names that start with '.', \ -only ignore '.' and '..'.", - ), - ) - .arg( - Arg::new(options::DIRECTORY) - .short('d') - .long(options::DIRECTORY) - .help( - "Only list the names of directories, rather than listing directory contents. \ - This will not follow symbolic links unless one of `--dereference-command-line \ - (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \ - specified.", - ), - ) - .arg( - Arg::new(options::size::HUMAN_READABLE) - .short('h') - .long(options::size::HUMAN_READABLE) - .help("Print human readable file sizes (e.g. 1K 234M 56G).") - .overrides_with(options::size::SI), - ) - .arg( - Arg::new(options::size::KIBIBYTES) - .short('k') - .long(options::size::KIBIBYTES) - .help("default to 1024-byte blocks for file system usage; used only with -s and per directory totals"), - ) - .arg( - Arg::new(options::size::SI) - .long(options::size::SI) - .help("Print human readable file sizes using powers of 1000 instead of 1024."), - ) - .arg( - Arg::new(options::size::BLOCK_SIZE) - .long(options::size::BLOCK_SIZE) - .takes_value(true) - .require_equals(true) - .value_name("BLOCK_SIZE") - .help("scale sizes by BLOCK_SIZE when printing them"), - ) - .arg( - Arg::new(options::INODE) - .short('i') - .long(options::INODE) - .help("print the index number of each file"), - ) - .arg( - Arg::new(options::REVERSE) - .short('r') - .long(options::REVERSE) - .help( - "Reverse whatever the sorting method is e.g., list files in reverse \ - alphabetical order, youngest first, smallest first, or whatever.", - ), - ) - .arg( - Arg::new(options::RECURSIVE) - .short('R') - .long(options::RECURSIVE) - .help("List the contents of all directories recursively."), - ) - .arg( - Arg::new(options::WIDTH) - .long(options::WIDTH) - .short('w') - .help("Assume that the terminal is COLS columns wide.") - .value_name("COLS") - .takes_value(true), - ) - .arg( - Arg::new(options::size::ALLOCATION_SIZE) - .short('s') - .long(options::size::ALLOCATION_SIZE) - .help("print the allocated size of each file, in blocks"), - ) - .arg( - Arg::new(options::COLOR) - .long(options::COLOR) - .help("Color output based on file type.") - .takes_value(true) - .possible_values(&[ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) - .require_equals(true) - .min_values(0), - ) - .arg( - Arg::new(options::INDICATOR_STYLE) - .long(options::INDICATOR_STYLE) - .help( - "Append indicator with style WORD to entry names: \ - none (default), slash (-p), file-type (--file-type), classify (-F)", - ) - .takes_value(true) - .possible_values(&["none", "slash", "file-type", "classify"]) - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - // The --classify flag can take an optional when argument to - // control its behavior from version 9 of GNU coreutils. - // There is currently an inconsistency where GNU coreutils allows only - // the long form of the flag to take the argument while we allow it - // for both the long and short form of the flag. - Arg::new(options::indicator_style::CLASSIFY) - .short('F') - .long(options::indicator_style::CLASSIFY) - .help( - "Append a character to each file name indicating the file type. Also, for \ - regular files that are executable, append '*'. The file type indicators are \ - '/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \ - '>' for doors, and nothing for regular files. when may be omitted, or one of:\n\ - \tnone - Do not classify. This is the default.\n\ - \tauto - Only classify if standard output is a terminal.\n\ - \talways - Always classify.\n\ - Specifying --classify and no when is equivalent to --classify=always. This will not follow\ - symbolic links listed on the command line unless the --dereference-command-line (-H),\ - --dereference (-L), or --dereference-command-line-symlink-to-dir options are specified.", - ) - .takes_value(true) - .value_name("when") - .possible_values(&[ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) - .default_missing_value("always") - .require_equals(true) - .min_values(0) - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - Arg::new(options::indicator_style::FILE_TYPE) - .long(options::indicator_style::FILE_TYPE) - .help("Same as --classify, but do not append '*'") - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - Arg::new(options::indicator_style::SLASH) - .short('p') - .help("Append / indicator to directories.") - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - //This still needs support for posix-*, +FORMAT - Arg::new(options::TIME_STYLE) - .long(options::TIME_STYLE) - .help("time/date format with -l; see TIME_STYLE below") - .value_name("TIME_STYLE") - .env("TIME_STYLE") - .possible_values(&["full-iso", "long-iso", "iso", "locale"]) - .overrides_with_all(&[options::TIME_STYLE]), - ) - .arg( - Arg::new(options::FULL_TIME) - .long(options::FULL_TIME) - .overrides_with(options::FULL_TIME) - .help("like -l --time-style=full-iso"), - ) - .arg( - Arg::new(options::CONTEXT) - .short('Z') - .long(options::CONTEXT) - .help(CONTEXT_HELP_TEXT), - ) - // Positional arguments - .arg( - Arg::new(options::PATHS) - .multiple_occurrences(true) - .takes_value(true) - .allow_invalid_utf8(true), - ) - .after_help( - "The TIME_STYLE argument can be full-iso, long-iso, iso. \ - Also the TIME_STYLE environment variable sets the default style to use.", - ) -} - /// The entry point for the dir coreutil pub fn dir_main(args: impl uucore::Args) -> UResult<()> { - let command = dir_uu_app(); + let command = uu_app(); + let matches = command.get_matches_from(args); let mut default_quoting_style = false; @@ -1460,621 +847,9 @@ pub fn dir_main(args: impl uucore::Args) -> UResult<()> { list(locs, &config) } -fn dir_uu_app<'a>() -> Command<'a> { - Command::new(uucore::util_name()) - .version(crate_version!()) - .override_usage(format_usage(USAGE)) - .about( - "dir is a shortcut for ls -C -b. All valid ls options are also valid dir options", - ) - .infer_long_args(true) - .arg( - Arg::new(options::HELP) - .long(options::HELP) - .help("Print help information.") - ) - // Format arguments - .arg( - Arg::new(options::FORMAT) - .long(options::FORMAT) - .help("Set the display format.") - .takes_value(true) - .possible_values(&[ - "long", - "verbose", - "single-column", - "columns", - "vertical", - "across", - "horizontal", - "commas", - ]) - .hide_possible_values(true) - .require_equals(true) - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::COLUMNS) - .short('C') - .help("Display the files in columns.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::LONG) - .short('l') - .long(options::format::LONG) - .help("Display detailed information.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::ACROSS) - .short('x') - .help("List entries in rows instead of in columns.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::COMMAS) - .short('m') - .help("List entries separated by commas.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - // The next four arguments do not override with the other format - // options, see the comment in Config::from for the reason. - // Ideally, they would use Arg::override_with, with their own name - // but that doesn't seem to work in all cases. Example: - // dir -1g1 - // even though `dir -11` and `dir -1 -g -1` work. - .arg( - Arg::new(options::format::ONE_LINE) - .short('1') - .help("List one file per line.") - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NO_GROUP) - .short('o') - .help( - "Long format without group information. \ - Identical to --format=long with --no-group.", - ) - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NO_OWNER) - .short('g') - .help("Long format without owner information.") - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NUMERIC_UID_GID) - .short('n') - .long(options::format::LONG_NUMERIC_UID_GID) - .help("-l with numeric UIDs and GIDs.") - .multiple_occurrences(true), - ) - // Quoting style - .arg( - Arg::new(options::QUOTING_STYLE) - .long(options::QUOTING_STYLE) - .takes_value(true) - .help("Set quoting style.") - .possible_values(&[ - "literal", - "shell", - "shell-always", - "shell-escape", - "shell-escape-always", - "c", - "escape", - ]) - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::LITERAL) - .short('N') - .long(options::quoting::LITERAL) - .help("Use literal quoting style. Equivalent to `--quoting-style=literal`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::ESCAPE) - .short('b') - .long(options::quoting::ESCAPE) - .help("Use escape quoting style. Equivalent to `--quoting-style=escape`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::C) - .short('Q') - .long(options::quoting::C) - .help("Use C quoting style. Equivalent to `--quoting-style=c`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - // Control characters - .arg( - Arg::new(options::HIDE_CONTROL_CHARS) - .short('q') - .long(options::HIDE_CONTROL_CHARS) - .help("Replace control characters with '?' if they are not escaped.") - .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), - ) - .arg( - Arg::new(options::SHOW_CONTROL_CHARS) - .long(options::SHOW_CONTROL_CHARS) - .help("Show control characters 'as is' if they are not escaped.") - .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), - ) - // Time arguments - .arg( - Arg::new(options::TIME) - .long(options::TIME) - .help( - "Show time in :\n\ - \taccess time (-u): atime, access, use;\n\ - \tchange time (-t): ctime, status.\n\ - \tbirth time: birth, creation;", - ) - .value_name("field") - .takes_value(true) - .possible_values(&[ - "atime", "access", "use", "ctime", "status", "birth", "creation", - ]) - .hide_possible_values(true) - .require_equals(true) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - .arg( - Arg::new(options::time::CHANGE) - .short('c') - .help( - "If the long listing format (e.g., -l, -o) is being used, print the status \ - change time (the 'ctime' in the inode) instead of the modification 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.", - ) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - .arg( - Arg::new(options::time::ACCESS) - .short('u') - .help( - "If the long listing format (e.g., -l, -o) is being used, print the status \ - access time instead of the modification time. When explicitly sorting by time \ - (--sort=time or -t) or when not using a long listing format, sort according to the \ - access time.", - ) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - // Hide and ignore - .arg( - Arg::new(options::HIDE) - .long(options::HIDE) - .takes_value(true) - .multiple_occurrences(true) - .value_name("PATTERN") - .help( - "do not list implied entries matching shell PATTERN (overridden by -a or -A)", - ), - ) - .arg( - Arg::new(options::IGNORE) - .short('I') - .long(options::IGNORE) - .takes_value(true) - .multiple_occurrences(true) - .value_name("PATTERN") - .help("do not list implied entries matching shell PATTERN"), - ) - .arg( - Arg::new(options::IGNORE_BACKUPS) - .short('B') - .long(options::IGNORE_BACKUPS) - .help("Ignore entries which end with ~."), - ) - // Sort arguments - .arg( - Arg::new(options::SORT) - .long(options::SORT) - .help("Sort by : name, none (-U), time (-t), size (-S) or extension (-X)") - .value_name("field") - .takes_value(true) - .possible_values(&["name", "none", "time", "size", "version", "extension"]) - .require_equals(true) - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::SIZE) - .short('S') - .help("Sort by file size, largest first.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::TIME) - .short('t') - .help("Sort by modification time (the 'mtime' in the inode), newest first.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::VERSION) - .short('v') - .help("Natural sort of (version) numbers in the filenames.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::EXTENSION) - .short('X') - .help("Sort alphabetically by entry extension.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::NONE) - .short('U') - .help( - "Do not sort; list the files in whatever order they are stored in the \ - directory. This is especially useful when listing very large directories, \ - since not doing any sorting can be noticeably faster.", - ) - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - // Dereferencing - .arg( - Arg::new(options::dereference::ALL) - .short('L') - .long(options::dereference::ALL) - .help( - "When showing file information for a symbolic link, show information for the \ - file the link references rather than the link itself.", - ) - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - .arg( - Arg::new(options::dereference::DIR_ARGS) - .long(options::dereference::DIR_ARGS) - .help( - "Do not dereference symlinks except when they link to directories and are \ - given as command line arguments.", - ) - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - .arg( - Arg::new(options::dereference::ARGS) - .short('H') - .long(options::dereference::ARGS) - .help("Do not dereference symlinks except when given as command line arguments.") - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - // Long format options - .arg( - Arg::new(options::NO_GROUP) - .long(options::NO_GROUP) - .short('G') - .help("Do not show group in long format."), - ) - .arg(Arg::new(options::AUTHOR).long(options::AUTHOR).help( - "Show author in long format. \ - On the supported platforms, the author always matches the file owner.", - )) - // Other Flags - .arg( - Arg::new(options::files::ALL) - .short('a') - .long(options::files::ALL) - // Overrides -A (as the order matters) - .overrides_with(options::files::ALMOST_ALL) - .multiple_occurrences(true) - .help("Do not ignore hidden files (files with names that start with '.')."), - ) - .arg( - Arg::new(options::files::ALMOST_ALL) - .short('A') - .long(options::files::ALMOST_ALL) - // Overrides -a (as the order matters) - .overrides_with(options::files::ALL) - .multiple_occurrences(true) - .help( - "In a directory, do not ignore all file names that start with '.', \ -only ignore '.' and '..'.", - ), - ) - .arg( - Arg::new(options::DIRECTORY) - .short('d') - .long(options::DIRECTORY) - .help( - "Only list the names of directories, rather than listing directory contents. \ - This will not follow symbolic links unless one of `--dereference-command-line \ - (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \ - specified.", - ), - ) - .arg( - Arg::new(options::size::HUMAN_READABLE) - .short('h') - .long(options::size::HUMAN_READABLE) - .help("Print human readable file sizes (e.g. 1K 234M 56G).") - .overrides_with(options::size::SI), - ) - .arg( - Arg::new(options::size::KIBIBYTES) - .short('k') - .long(options::size::KIBIBYTES) - .help("default to 1024-byte blocks for file system usage; used only with -s and per directory totals"), - ) - .arg( - Arg::new(options::size::SI) - .long(options::size::SI) - .help("Print human readable file sizes using powers of 1000 instead of 1024."), - ) - .arg( - Arg::new(options::size::BLOCK_SIZE) - .long(options::size::BLOCK_SIZE) - .takes_value(true) - .require_equals(true) - .value_name("BLOCK_SIZE") - .help("scale sizes by BLOCK_SIZE when printing them"), - ) - .arg( - Arg::new(options::INODE) - .short('i') - .long(options::INODE) - .help("print the index number of each file"), - ) - .arg( - Arg::new(options::REVERSE) - .short('r') - .long(options::REVERSE) - .help( - "Reverse whatever the sorting method is e.g., list files in reverse \ - alphabetical order, youngest first, smallest first, or whatever.", - ), - ) - .arg( - Arg::new(options::RECURSIVE) - .short('R') - .long(options::RECURSIVE) - .help("List the contents of all directories recursively."), - ) - .arg( - Arg::new(options::WIDTH) - .long(options::WIDTH) - .short('w') - .help("Assume that the terminal is COLS columns wide.") - .value_name("COLS") - .takes_value(true), - ) - .arg( - Arg::new(options::size::ALLOCATION_SIZE) - .short('s') - .long(options::size::ALLOCATION_SIZE) - .help("print the allocated size of each file, in blocks"), - ) - .arg( - Arg::new(options::COLOR) - .long(options::COLOR) - .help("Color output based on file type.") - .takes_value(true) - .possible_values(&[ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) - .require_equals(true) - .min_values(0), - ) - .arg( - Arg::new(options::INDICATOR_STYLE) - .long(options::INDICATOR_STYLE) - .help( - "Append indicator with style WORD to entry names: \ - none (default), slash (-p), file-type (--file-type), classify (-F)", - ) - .takes_value(true) - .possible_values(&["none", "slash", "file-type", "classify"]) - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - // The --classify flag can take an optional when argument to - // control its behavior from version 9 of GNU coreutils. - // There is currently an inconsistency where GNU coreutils allows only - // the long form of the flag to take the argument while we allow it - // for both the long and short form of the flag. - Arg::new(options::indicator_style::CLASSIFY) - .short('F') - .long(options::indicator_style::CLASSIFY) - .help( - "Append a character to each file name indicating the file type. Also, for \ - regular files that are executable, append '*'. The file type indicators are \ - '/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \ - '>' for doors, and nothing for regular files. when may be omitted, or one of:\n\ - \tnone - Do not classify. This is the default.\n\ - \tauto - Only classify if standard output is a terminal.\n\ - \talways - Always classify.\n\ - Specifying --classify and no when is equivalent to --classify=always. This will not follow\ - symbolic links listed on the command line unless the --dereference-command-line (-H),\ - --dereference (-L), or --dereference-command-line-symlink-to-dir options are specified.", - ) - .takes_value(true) - .value_name("when") - .possible_values(&[ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) - .default_missing_value("always") - .require_equals(true) - .min_values(0) - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - Arg::new(options::indicator_style::FILE_TYPE) - .long(options::indicator_style::FILE_TYPE) - .help("Same as --classify, but do not append '*'") - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - Arg::new(options::indicator_style::SLASH) - .short('p') - .help("Append / indicator to directories.") - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - //This still needs support for posix-*, +FORMAT - Arg::new(options::TIME_STYLE) - .long(options::TIME_STYLE) - .help("time/date format with -l; see TIME_STYLE below") - .value_name("TIME_STYLE") - .env("TIME_STYLE") - .possible_values(&["full-iso", "long-iso", "iso", "locale"]) - .overrides_with_all(&[options::TIME_STYLE]), - ) - .arg( - Arg::new(options::FULL_TIME) - .long(options::FULL_TIME) - .overrides_with(options::FULL_TIME) - .help("like -l --time-style=full-iso"), - ) - .arg( - Arg::new(options::CONTEXT) - .short('Z') - .long(options::CONTEXT) - .help(CONTEXT_HELP_TEXT), - ) - // Positional arguments - .arg( - Arg::new(options::PATHS) - .multiple_occurrences(true) - .takes_value(true) - .allow_invalid_utf8(true), - ) - .after_help( - "The TIME_STYLE argument can be full-iso, long-iso, iso. \ - Also the TIME_STYLE environment variable sets the default style to use.", - ) -} - /// The entry point for the vdir coreutil pub fn vdir_main(args: impl uucore::Args) -> UResult<()> { - let command = vdir_uu_app(); + let command = uu_app(); let matches = command.get_matches_from(args); let mut default_quoting_style = false; @@ -2121,616 +896,614 @@ pub fn vdir_main(args: impl uucore::Args) -> UResult<()> { list(locs, &config) } -fn vdir_uu_app<'a>() -> Command<'a> { +pub fn uu_app<'a>() -> Command<'a> { Command::new(uucore::util_name()) .version(crate_version!()) - .override_usage(format_usage(USAGE)) - .about( - "vdir is a shortcut for ls -l -b. All valid ls options are also valid vdir options", - ) - .infer_long_args(true) - .arg( - Arg::new(options::HELP) - .long(options::HELP) - .help("Print help information.") - ) - // Format arguments - .arg( - Arg::new(options::FORMAT) - .long(options::FORMAT) - .help("Set the display format.") - .takes_value(true) - .possible_values(&[ - "long", - "verbose", - "single-column", - "columns", - "vertical", - "across", - "horizontal", - "commas", - ]) - .hide_possible_values(true) - .require_equals(true) - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::COLUMNS) - .short('C') - .help("Display the files in columns.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::LONG) - .short('l') - .long(options::format::LONG) - .help("Display detailed information.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::ACROSS) - .short('x') - .help("List entries in rows instead of in columns.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - .arg( - Arg::new(options::format::COMMAS) - .short('m') - .help("List entries separated by commas.") - .overrides_with_all(&[ - options::FORMAT, - options::format::COLUMNS, - options::format::LONG, - options::format::ACROSS, - options::format::COLUMNS, - ]), - ) - // The next four arguments do not override with the other format - // options, see the comment in Config::from for the reason. - // Ideally, they would use Arg::override_with, with their own name - // but that doesn't seem to work in all cases. Example: - // vdir -1g1 - // even though `vdir -11` and `vdir -1 -g -1` work. - .arg( - Arg::new(options::format::ONE_LINE) - .short('1') - .help("List one file per line.") - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NO_GROUP) - .short('o') - .help( - "Long format without group information. \ - Identical to --format=long with --no-group.", - ) - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NO_OWNER) - .short('g') - .help("Long format without owner information.") - .multiple_occurrences(true), - ) - .arg( - Arg::new(options::format::LONG_NUMERIC_UID_GID) - .short('n') - .long(options::format::LONG_NUMERIC_UID_GID) - .help("-l with numeric UIDs and GIDs.") - .multiple_occurrences(true), - ) - // Quoting style - .arg( - Arg::new(options::QUOTING_STYLE) - .long(options::QUOTING_STYLE) - .takes_value(true) - .help("Set quoting style.") - .possible_values(&[ - "literal", - "shell", - "shell-always", - "shell-escape", - "shell-escape-always", - "c", - "escape", - ]) - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::LITERAL) - .short('N') - .long(options::quoting::LITERAL) - .help("Use literal quoting style. Equivalent to `--quoting-style=literal`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::ESCAPE) - .short('b') - .long(options::quoting::ESCAPE) - .help("Use escape quoting style. Equivalent to `--quoting-style=escape`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - .arg( - Arg::new(options::quoting::C) - .short('Q') - .long(options::quoting::C) - .help("Use C quoting style. Equivalent to `--quoting-style=c`") - .overrides_with_all(&[ - options::QUOTING_STYLE, - options::quoting::LITERAL, - options::quoting::ESCAPE, - options::quoting::C, - ]), - ) - // Control characters - .arg( - Arg::new(options::HIDE_CONTROL_CHARS) - .short('q') - .long(options::HIDE_CONTROL_CHARS) - .help("Replace control characters with '?' if they are not escaped.") - .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), - ) - .arg( - Arg::new(options::SHOW_CONTROL_CHARS) - .long(options::SHOW_CONTROL_CHARS) - .help("Show control characters 'as is' if they are not escaped.") - .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), - ) - // Time arguments - .arg( - Arg::new(options::TIME) - .long(options::TIME) - .help( - "Show time in :\n\ - \taccess time (-u): atime, access, use;\n\ - \tchange time (-t): ctime, status.\n\ - \tbirth time: birth, creation;", - ) - .value_name("field") - .takes_value(true) - .possible_values(&[ - "atime", "access", "use", "ctime", "status", "birth", "creation", - ]) - .hide_possible_values(true) - .require_equals(true) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - .arg( - Arg::new(options::time::CHANGE) - .short('c') - .help( - "If the long listing format (e.g., -l, -o) is being used, print the status \ - change time (the 'ctime' in the inode) instead of the modification 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.", - ) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - .arg( - Arg::new(options::time::ACCESS) - .short('u') - .help( - "If the long listing format (e.g., -l, -o) is being used, print the status \ - access time instead of the modification time. When explicitly sorting by time \ - (--sort=time or -t) or when not using a long listing format, sort according to the \ - access time.", - ) - .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), - ) - // Hide and ignore - .arg( - Arg::new(options::HIDE) - .long(options::HIDE) - .takes_value(true) - .multiple_occurrences(true) - .value_name("PATTERN") - .help( - "do not list implied entries matching shell PATTERN (overridden by -a or -A)", - ), - ) - .arg( - Arg::new(options::IGNORE) - .short('I') - .long(options::IGNORE) - .takes_value(true) - .multiple_occurrences(true) - .value_name("PATTERN") - .help("do not list implied entries matching shell PATTERN"), - ) - .arg( - Arg::new(options::IGNORE_BACKUPS) - .short('B') - .long(options::IGNORE_BACKUPS) - .help("Ignore entries which end with ~."), - ) - // Sort arguments - .arg( - Arg::new(options::SORT) - .long(options::SORT) - .help("Sort by : name, none (-U), time (-t), size (-S) or extension (-X)") - .value_name("field") - .takes_value(true) - .possible_values(&["name", "none", "time", "size", "version", "extension"]) - .require_equals(true) - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::SIZE) - .short('S') - .help("Sort by file size, largest first.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::TIME) - .short('t') - .help("Sort by modification time (the 'mtime' in the inode), newest first.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::VERSION) - .short('v') - .help("Natural sort of (version) numbers in the filenames.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::EXTENSION) - .short('X') - .help("Sort alphabetically by entry extension.") - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - .arg( - Arg::new(options::sort::NONE) - .short('U') - .help( - "Do not sort; list the files in whatever order they are stored in the \ - directory. This is especially useful when listing very large directories, \ - since not doing any sorting can be noticeably faster.", - ) - .overrides_with_all(&[ - options::SORT, - options::sort::SIZE, - options::sort::TIME, - options::sort::NONE, - options::sort::VERSION, - options::sort::EXTENSION, - ]), - ) - // Dereferencing - .arg( - Arg::new(options::dereference::ALL) - .short('L') - .long(options::dereference::ALL) - .help( - "When showing file information for a symbolic link, show information for the \ - file the link references rather than the link itself.", - ) - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - .arg( - Arg::new(options::dereference::DIR_ARGS) - .long(options::dereference::DIR_ARGS) - .help( - "Do not dereference symlinks except when they link to directories and are \ - given as command line arguments.", - ) - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - .arg( - Arg::new(options::dereference::ARGS) - .short('H') - .long(options::dereference::ARGS) - .help("Do not dereference symlinks except when given as command line arguments.") - .overrides_with_all(&[ - options::dereference::ALL, - options::dereference::DIR_ARGS, - options::dereference::ARGS, - ]), - ) - // Long format options - .arg( - Arg::new(options::NO_GROUP) - .long(options::NO_GROUP) - .short('G') - .help("Do not show group in long format."), - ) - .arg(Arg::new(options::AUTHOR).long(options::AUTHOR).help( - "Show author in long format. \ - On the supported platforms, the author always matches the file owner.", - )) - // Other Flags - .arg( - Arg::new(options::files::ALL) - .short('a') - .long(options::files::ALL) - // Overrides -A (as the order matters) - .overrides_with(options::files::ALMOST_ALL) - .multiple_occurrences(true) - .help("Do not ignore hidden files (files with names that start with '.')."), - ) - .arg( - Arg::new(options::files::ALMOST_ALL) - .short('A') - .long(options::files::ALMOST_ALL) - // Overrides -a (as the order matters) - .overrides_with(options::files::ALL) - .multiple_occurrences(true) - .help( - "In a directory, do not ignore all file names that start with '.', \ -only ignore '.' and '..'.", - ), - ) - .arg( - Arg::new(options::DIRECTORY) - .short('d') - .long(options::DIRECTORY) - .help( - "Only list the names of directories, rather than listing directory contents. \ - This will not follow symbolic links unless one of `--dereference-command-line \ - (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \ - specified.", - ), - ) - .arg( - Arg::new(options::size::HUMAN_READABLE) - .short('h') - .long(options::size::HUMAN_READABLE) - .help("Print human readable file sizes (e.g. 1K 234M 56G).") - .overrides_with(options::size::SI), - ) - .arg( - Arg::new(options::size::KIBIBYTES) - .short('k') - .long(options::size::KIBIBYTES) - .help("default to 1024-byte blocks for file system usage; used only with -s and per directory totals"), - ) - .arg( - Arg::new(options::size::SI) - .long(options::size::SI) - .help("Print human readable file sizes using powers of 1000 instead of 1024."), - ) - .arg( - Arg::new(options::size::BLOCK_SIZE) - .long(options::size::BLOCK_SIZE) - .takes_value(true) - .require_equals(true) - .value_name("BLOCK_SIZE") - .help("scale sizes by BLOCK_SIZE when printing them"), - ) - .arg( - Arg::new(options::INODE) - .short('i') - .long(options::INODE) - .help("print the index number of each file"), - ) - .arg( - Arg::new(options::REVERSE) - .short('r') - .long(options::REVERSE) - .help( - "Reverse whatever the sorting method is e.g., list files in reverse \ - alphabetical order, youngest first, smallest first, or whatever.", - ), - ) - .arg( - Arg::new(options::RECURSIVE) - .short('R') - .long(options::RECURSIVE) - .help("List the contents of all directories recursively."), - ) - .arg( - Arg::new(options::WIDTH) - .long(options::WIDTH) - .short('w') - .help("Assume that the terminal is COLS columns wide.") - .value_name("COLS") - .takes_value(true), - ) - .arg( - Arg::new(options::size::ALLOCATION_SIZE) - .short('s') - .long(options::size::ALLOCATION_SIZE) - .help("print the allocated size of each file, in blocks"), - ) - .arg( - Arg::new(options::COLOR) - .long(options::COLOR) - .help("Color output based on file type.") - .takes_value(true) - .possible_values(&[ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) - .require_equals(true) - .min_values(0), - ) - .arg( - Arg::new(options::INDICATOR_STYLE) - .long(options::INDICATOR_STYLE) - .help( - "Append indicator with style WORD to entry names: \ - none (default), slash (-p), file-type (--file-type), classify (-F)", - ) - .takes_value(true) - .possible_values(&["none", "slash", "file-type", "classify"]) - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - // The --classify flag can take an optional when argument to - // control its behavior from version 9 of GNU coreutils. - // There is currently an inconsistency where GNU coreutils allows only - // the long form of the flag to take the argument while we allow it - // for both the long and short form of the flag. - Arg::new(options::indicator_style::CLASSIFY) - .short('F') - .long(options::indicator_style::CLASSIFY) - .help( - "Append a character to each file name indicating the file type. Also, for \ - regular files that are executable, append '*'. The file type indicators are \ - '/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \ - '>' for doors, and nothing for regular files. when may be omitted, or one of:\n\ - \tnone - Do not classify. This is the default.\n\ - \tauto - Only classify if standard output is a terminal.\n\ - \talways - Always classify.\n\ - Specifying --classify and no when is equivalent to --classify=always. This will not follow\ - symbolic links listed on the command line unless the --dereference-command-line (-H),\ - --dereference (-L), or --dereference-command-line-symlink-to-dir options are specified.", - ) - .takes_value(true) - .value_name("when") - .possible_values(&[ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) - .default_missing_value("always") - .require_equals(true) - .min_values(0) - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - Arg::new(options::indicator_style::FILE_TYPE) - .long(options::indicator_style::FILE_TYPE) - .help("Same as --classify, but do not append '*'") - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - Arg::new(options::indicator_style::SLASH) - .short('p') - .help("Append / indicator to directories.") - .overrides_with_all(&[ - options::indicator_style::FILE_TYPE, - options::indicator_style::SLASH, - options::indicator_style::CLASSIFY, - options::INDICATOR_STYLE, - ]), - ) - .arg( - //This still needs support for posix-*, +FORMAT - Arg::new(options::TIME_STYLE) - .long(options::TIME_STYLE) - .help("time/date format with -l; see TIME_STYLE below") - .value_name("TIME_STYLE") - .env("TIME_STYLE") - .possible_values(&["full-iso", "long-iso", "iso", "locale"]) - .overrides_with_all(&[options::TIME_STYLE]), - ) - .arg( - Arg::new(options::FULL_TIME) - .long(options::FULL_TIME) - .overrides_with(options::FULL_TIME) - .help("like -l --time-style=full-iso"), - ) - .arg( - Arg::new(options::CONTEXT) - .short('Z') - .long(options::CONTEXT) - .help(CONTEXT_HELP_TEXT), - ) - // Positional arguments - .arg( - Arg::new(options::PATHS) - .multiple_occurrences(true) - .takes_value(true) - .allow_invalid_utf8(true), - ) - .after_help( - "The TIME_STYLE argument can be full-iso, long-iso, iso. \ - Also the TIME_STYLE environment variable sets the default style to use.", - ) + .override_usage(format_usage(USAGE)) + .about("List directory contents. Ignore files and directories starting with a '.' by default") + .infer_long_args(true) + .arg( + Arg::new(options::HELP) + .long(options::HELP) + .help("Print help information.") + ) + // Format arguments + .arg( + Arg::new(options::FORMAT) + .long(options::FORMAT) + .help("Set the display format.") + .takes_value(true) + .possible_values(&[ + "long", + "verbose", + "single-column", + "columns", + "vertical", + "across", + "horizontal", + "commas", + ]) + .hide_possible_values(true) + .require_equals(true) + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::COLUMNS) + .short('C') + .help("Display the files in columns.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::LONG) + .short('l') + .long(options::format::LONG) + .help("Display detailed information.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::ACROSS) + .short('x') + .help("List entries in rows instead of in columns.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + .arg( + Arg::new(options::format::COMMAS) + .short('m') + .help("List entries separated by commas.") + .overrides_with_all(&[ + options::FORMAT, + options::format::COLUMNS, + options::format::LONG, + options::format::ACROSS, + options::format::COLUMNS, + ]), + ) + // The next four arguments do not override with the other format + // options, see the comment in Config::from for the reason. + // Ideally, they would use Arg::override_with, with their own name + // but that doesn't seem to work in all cases. Example: + // ls -1g1 + // even though `ls -11` and `ls -1 -g -1` work. + .arg( + Arg::new(options::format::ONE_LINE) + .short('1') + .help("List one file per line.") + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NO_GROUP) + .short('o') + .help( + "Long format without group information. \ + Identical to --format=long with --no-group.", + ) + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NO_OWNER) + .short('g') + .help("Long format without owner information.") + .multiple_occurrences(true), + ) + .arg( + Arg::new(options::format::LONG_NUMERIC_UID_GID) + .short('n') + .long(options::format::LONG_NUMERIC_UID_GID) + .help("-l with numeric UIDs and GIDs.") + .multiple_occurrences(true), + ) + // Quoting style + .arg( + Arg::new(options::QUOTING_STYLE) + .long(options::QUOTING_STYLE) + .takes_value(true) + .help("Set quoting style.") + .possible_values(&[ + "literal", + "shell", + "shell-always", + "shell-escape", + "shell-escape-always", + "c", + "escape", + ]) + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + .arg( + Arg::new(options::quoting::LITERAL) + .short('N') + .long(options::quoting::LITERAL) + .help("Use literal quoting style. Equivalent to `--quoting-style=literal`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + .arg( + Arg::new(options::quoting::ESCAPE) + .short('b') + .long(options::quoting::ESCAPE) + .help("Use escape quoting style. Equivalent to `--quoting-style=escape`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + .arg( + Arg::new(options::quoting::C) + .short('Q') + .long(options::quoting::C) + .help("Use C quoting style. Equivalent to `--quoting-style=c`") + .overrides_with_all(&[ + options::QUOTING_STYLE, + options::quoting::LITERAL, + options::quoting::ESCAPE, + options::quoting::C, + ]), + ) + // Control characters + .arg( + Arg::new(options::HIDE_CONTROL_CHARS) + .short('q') + .long(options::HIDE_CONTROL_CHARS) + .help("Replace control characters with '?' if they are not escaped.") + .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), + ) + .arg( + Arg::new(options::SHOW_CONTROL_CHARS) + .long(options::SHOW_CONTROL_CHARS) + .help("Show control characters 'as is' if they are not escaped.") + .overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]), + ) + // Time arguments + .arg( + Arg::new(options::TIME) + .long(options::TIME) + .help( + "Show time in :\n\ + \taccess time (-u): atime, access, use;\n\ + \tchange time (-t): ctime, status.\n\ + \tbirth time: birth, creation;", + ) + .value_name("field") + .takes_value(true) + .possible_values(&[ + "atime", "access", "use", "ctime", "status", "birth", "creation", + ]) + .hide_possible_values(true) + .require_equals(true) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + .arg( + Arg::new(options::time::CHANGE) + .short('c') + .help( + "If the long listing format (e.g., -l, -o) is being used, print the status \ + change time (the 'ctime' in the inode) instead of the modification 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.", + ) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + .arg( + Arg::new(options::time::ACCESS) + .short('u') + .help( + "If the long listing format (e.g., -l, -o) is being used, print the status \ + access time instead of the modification time. When explicitly sorting by time \ + (--sort=time or -t) or when not using a long listing format, sort according to the \ + access time.", + ) + .overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]), + ) + // Hide and ignore + .arg( + Arg::new(options::HIDE) + .long(options::HIDE) + .takes_value(true) + .multiple_occurrences(true) + .value_name("PATTERN") + .help( + "do not list implied entries matching shell PATTERN (overridden by -a or -A)", + ), + ) + .arg( + Arg::new(options::IGNORE) + .short('I') + .long(options::IGNORE) + .takes_value(true) + .multiple_occurrences(true) + .value_name("PATTERN") + .help("do not list implied entries matching shell PATTERN"), + ) + .arg( + Arg::new(options::IGNORE_BACKUPS) + .short('B') + .long(options::IGNORE_BACKUPS) + .help("Ignore entries which end with ~."), + ) + // Sort arguments + .arg( + Arg::new(options::SORT) + .long(options::SORT) + .help("Sort by : name, none (-U), time (-t), size (-S) or extension (-X)") + .value_name("field") + .takes_value(true) + .possible_values(&["name", "none", "time", "size", "version", "extension"]) + .require_equals(true) + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::SIZE) + .short('S') + .help("Sort by file size, largest first.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::TIME) + .short('t') + .help("Sort by modification time (the 'mtime' in the inode), newest first.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::VERSION) + .short('v') + .help("Natural sort of (version) numbers in the filenames.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::EXTENSION) + .short('X') + .help("Sort alphabetically by entry extension.") + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + .arg( + Arg::new(options::sort::NONE) + .short('U') + .help( + "Do not sort; list the files in whatever order they are stored in the \ + directory. This is especially useful when listing very large directories, \ + since not doing any sorting can be noticeably faster.", + ) + .overrides_with_all(&[ + options::SORT, + options::sort::SIZE, + options::sort::TIME, + options::sort::NONE, + options::sort::VERSION, + options::sort::EXTENSION, + ]), + ) + // Dereferencing + .arg( + Arg::new(options::dereference::ALL) + .short('L') + .long(options::dereference::ALL) + .help( + "When showing file information for a symbolic link, show information for the \ + file the link references rather than the link itself.", + ) + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + .arg( + Arg::new(options::dereference::DIR_ARGS) + .long(options::dereference::DIR_ARGS) + .help( + "Do not dereference symlinks except when they link to directories and are \ + given as command line arguments.", + ) + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + .arg( + Arg::new(options::dereference::ARGS) + .short('H') + .long(options::dereference::ARGS) + .help("Do not dereference symlinks except when given as command line arguments.") + .overrides_with_all(&[ + options::dereference::ALL, + options::dereference::DIR_ARGS, + options::dereference::ARGS, + ]), + ) + // Long format options + .arg( + Arg::new(options::NO_GROUP) + .long(options::NO_GROUP) + .short('G') + .help("Do not show group in long format."), + ) + .arg(Arg::new(options::AUTHOR).long(options::AUTHOR).help( + "Show author in long format. \ + On the supported platforms, the author always matches the file owner.", + )) + // Other Flags + .arg( + Arg::new(options::files::ALL) + .short('a') + .long(options::files::ALL) + // Overrides -A (as the order matters) + .overrides_with(options::files::ALMOST_ALL) + .multiple_occurrences(true) + .help("Do not ignore hidden files (files with names that start with '.')."), + ) + .arg( + Arg::new(options::files::ALMOST_ALL) + .short('A') + .long(options::files::ALMOST_ALL) + // Overrides -a (as the order matters) + .overrides_with(options::files::ALL) + .multiple_occurrences(true) + .help( + "In a directory, do not ignore all file names that start with '.', \ + only ignore '.' and '..'.", + ), + ) + .arg( + Arg::new(options::DIRECTORY) + .short('d') + .long(options::DIRECTORY) + .help( + "Only list the names of directories, rather than listing directory contents. \ + This will not follow symbolic links unless one of `--dereference-command-line \ + (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \ + specified.", + ), + ) + .arg( + Arg::new(options::size::HUMAN_READABLE) + .short('h') + .long(options::size::HUMAN_READABLE) + .help("Print human readable file sizes (e.g. 1K 234M 56G).") + .overrides_with(options::size::SI), + ) + .arg( + Arg::new(options::size::KIBIBYTES) + .short('k') + .long(options::size::KIBIBYTES) + .help("default to 1024-byte blocks for file system usage; used only with -s and per directory totals"), + ) + .arg( + Arg::new(options::size::SI) + .long(options::size::SI) + .help("Print human readable file sizes using powers of 1000 instead of 1024."), + ) + .arg( + Arg::new(options::size::BLOCK_SIZE) + .long(options::size::BLOCK_SIZE) + .takes_value(true) + .require_equals(true) + .value_name("BLOCK_SIZE") + .help("scale sizes by BLOCK_SIZE when printing them"), + ) + .arg( + Arg::new(options::INODE) + .short('i') + .long(options::INODE) + .help("print the index number of each file"), + ) + .arg( + Arg::new(options::REVERSE) + .short('r') + .long(options::REVERSE) + .help( + "Reverse whatever the sorting method is e.g., list files in reverse \ + alphabetical order, youngest first, smallest first, or whatever.", + ), + ) + .arg( + Arg::new(options::RECURSIVE) + .short('R') + .long(options::RECURSIVE) + .help("List the contents of all directories recursively."), + ) + .arg( + Arg::new(options::WIDTH) + .long(options::WIDTH) + .short('w') + .help("Assume that the terminal is COLS columns wide.") + .value_name("COLS") + .takes_value(true), + ) + .arg( + Arg::new(options::size::ALLOCATION_SIZE) + .short('s') + .long(options::size::ALLOCATION_SIZE) + .help("print the allocated size of each file, in blocks"), + ) + .arg( + Arg::new(options::COLOR) + .long(options::COLOR) + .help("Color output based on file type.") + .takes_value(true) + .possible_values(&[ + "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", + ]) + .require_equals(true) + .min_values(0), + ) + .arg( + Arg::new(options::INDICATOR_STYLE) + .long(options::INDICATOR_STYLE) + .help( + "Append indicator with style WORD to entry names: \ + none (default), slash (-p), file-type (--file-type), classify (-F)", + ) + .takes_value(true) + .possible_values(&["none", "slash", "file-type", "classify"]) + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + // The --classify flag can take an optional when argument to + // control its behavior from version 9 of GNU coreutils. + // There is currently an inconsistency where GNU coreutils allows only + // the long form of the flag to take the argument while we allow it + // for both the long and short form of the flag. + Arg::new(options::indicator_style::CLASSIFY) + .short('F') + .long(options::indicator_style::CLASSIFY) + .help( + "Append a character to each file name indicating the file type. Also, for \ + regular files that are executable, append '*'. The file type indicators are \ + '/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \ + '>' for doors, and nothing for regular files. when may be omitted, or one of:\n\ + \tnone - Do not classify. This is the default.\n\ + \tauto - Only classify if standard output is a terminal.\n\ + \talways - Always classify.\n\ + Specifying --classify and no when is equivalent to --classify=always. This will not follow\ + symbolic links listed on the command line unless the --dereference-command-line (-H),\ + --dereference (-L), or --dereference-command-line-symlink-to-dir options are specified.", + ) + .takes_value(true) + .value_name("when") + .possible_values(&[ + "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", + ]) + .default_missing_value("always") + .require_equals(true) + .min_values(0) + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + Arg::new(options::indicator_style::FILE_TYPE) + .long(options::indicator_style::FILE_TYPE) + .help("Same as --classify, but do not append '*'") + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + Arg::new(options::indicator_style::SLASH) + .short('p') + .help("Append / indicator to directories.") + .overrides_with_all(&[ + options::indicator_style::FILE_TYPE, + options::indicator_style::SLASH, + options::indicator_style::CLASSIFY, + options::INDICATOR_STYLE, + ]), + ) + .arg( + //This still needs support for posix-*, +FORMAT + Arg::new(options::TIME_STYLE) + .long(options::TIME_STYLE) + .help("time/date format with -l; see TIME_STYLE below") + .value_name("TIME_STYLE") + .env("TIME_STYLE") + .possible_values(&["full-iso", "long-iso", "iso", "locale"]) + .overrides_with_all(&[options::TIME_STYLE]), + ) + .arg( + Arg::new(options::FULL_TIME) + .long(options::FULL_TIME) + .overrides_with(options::FULL_TIME) + .help("like -l --time-style=full-iso"), + ) + .arg( + Arg::new(options::CONTEXT) + .short('Z') + .long(options::CONTEXT) + .help(CONTEXT_HELP_TEXT), + ) + // Positional arguments + .arg( + Arg::new(options::PATHS) + .multiple_occurrences(true) + .takes_value(true) + .allow_invalid_utf8(true), + ) + .after_help( + "The TIME_STYLE argument can be full-iso, long-iso, iso. \ + Also the TIME_STYLE environment variable sets the default style to use.", + ) } /// Represents a Path along with it's associated data. From 923f6d8b964a6c8b9bb1b0850db74c4767a708ff Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 12:57:55 -0700 Subject: [PATCH 15/20] Update src/uu/ls/src/ls.rs Co-authored-by: Sylvestre Ledru --- src/uu/ls/src/ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 61a4fb4c0e7..432a56b8372 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -847,7 +847,7 @@ pub fn dir_main(args: impl uucore::Args) -> UResult<()> { list(locs, &config) } -/// The entry point for the vdir coreutil +/// The entry point for the vdir coreutils pub fn vdir_main(args: impl uucore::Args) -> UResult<()> { let command = uu_app(); let matches = command.get_matches_from(args); From 437bb88225a6cb611ada8c7aaa5f25487da04551 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 12:57:59 -0700 Subject: [PATCH 16/20] Update .vscode/cspell.dictionaries/jargon.wordlist.txt Co-authored-by: Sylvestre Ledru --- .vscode/cspell.dictionaries/jargon.wordlist.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index dcd0ac7b5b5..13f340f6aa3 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -17,7 +17,6 @@ colorize coprime consts conv -coreutil cyclomatic dedup deduplication From eefcafe3c52ff653c2c120fc6b80cf105f3c683a Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 12:58:04 -0700 Subject: [PATCH 17/20] Update src/uu/ls/src/ls.rs Co-authored-by: Sylvestre Ledru --- src/uu/ls/src/ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 432a56b8372..f2782267b2d 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -797,7 +797,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { list(locs, &config) } -/// The entry point for the dir coreutil +/// The entry point for the dir coreutils pub fn dir_main(args: impl uucore::Args) -> UResult<()> { let command = uu_app(); From 6af0ec7e4457daf71a5bcfad2e67bf03a8b469f7 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Thu, 14 Apr 2022 14:22:36 -0700 Subject: [PATCH 18/20] Add dir and vdir to GNUmakefile --- GNUmakefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GNUmakefile b/GNUmakefile index ade50214068..d3c26ce8026 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -65,6 +65,7 @@ PROGS := \ date \ dd \ df \ + dir \ dircolors \ dirname \ echo \ @@ -118,6 +119,7 @@ PROGS := \ tsort \ unexpand \ uniq \ + vdir \ wc \ whoami \ yes From b2a9f81419adcc096b2c1788db5b0c19633561db Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Fri, 15 Apr 2022 00:01:25 -0700 Subject: [PATCH 19/20] removed dir and vdir from the ignored binaries --- util/build-gnu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index a4a33e8604f..0aad35ff1d9 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -180,7 +180,7 @@ sed -i -e "s~ sed -n \"1s/'\\\/'/'OPT'/p\" < err >> pat || framework_failure_~ # see issue #3331 (clap limitation). # Upstream returns 1 for most of the program. We do for cp, truncate & pr # So, keep it as it -sed -i -e "s/rcexp=1$/rcexp=2\n case \"\$prg\" in chcon|dir|runcon|vdir) return;; esac/" -e "s/rcexp=125 ;;/rcexp=2 ;;\ncp|truncate|pr) rcexp=1;;/" tests/misc/usage_vs_getopt.sh +sed -i -e "s/rcexp=1$/rcexp=2\n case \"\$prg\" in chcon|runcon) return;; esac/" -e "s/rcexp=125 ;;/rcexp=2 ;;\ncp|truncate|pr) rcexp=1;;/" tests/misc/usage_vs_getopt.sh # GNU has option=[SUFFIX], clap is sed -i -e "s/cat opts/sed -i -e \"s| <.\*>$||g\" opts/" tests/misc/usage_vs_getopt.sh # Strip empty lines for the diff - see https://github.com/uutils/coreutils/issues/3370 From 371edf63fc3d2cfa6212973bf39431c8931dc937 Mon Sep 17 00:00:00 2001 From: gmnsii <95436780+gmnsii@users.noreply.github.com> Date: Fri, 15 Apr 2022 05:49:19 -0700 Subject: [PATCH 20/20] Refactor dir and vdir code --- src/uu/dir/src/dir.rs | 59 +++++++++++++++-- src/uu/ls/src/ls.rs | 118 +++------------------------------ src/uu/ls/src/quoting_style.rs | 4 +- src/uu/vdir/src/vdir.rs | 57 ++++++++++++++-- 4 files changed, 117 insertions(+), 121 deletions(-) diff --git a/src/uu/dir/src/dir.rs b/src/uu/dir/src/dir.rs index 53be923b06a..74907472668 100644 --- a/src/uu/dir/src/dir.rs +++ b/src/uu/dir/src/dir.rs @@ -6,18 +6,65 @@ // * that was distributed with this source code. use clap::Command; +use std::path::Path; +use uu_ls::quoting_style::{Quotes, QuotingStyle}; +use uu_ls::{options, Config, Format}; use uucore::error::UResult; #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - uu_ls::dir_main(args) + let command = uu_ls::uu_app(); + + let matches = command.get_matches_from(args); + + let mut default_quoting_style = false; + let mut default_format_style = false; + + // We check if any options on formatting or quoting style have been given. + // If not, we will use dir default formatting and quoting style options + + if !matches.is_present(options::QUOTING_STYLE) + && !matches.is_present(options::quoting::C) + && !matches.is_present(options::quoting::ESCAPE) + && !matches.is_present(options::quoting::LITERAL) + { + default_quoting_style = true; + } + if !matches.is_present(options::FORMAT) + && !matches.is_present(options::format::ACROSS) + && !matches.is_present(options::format::COLUMNS) + && !matches.is_present(options::format::COMMAS) + && !matches.is_present(options::format::LONG) + && !matches.is_present(options::format::LONG_NO_GROUP) + && !matches.is_present(options::format::LONG_NO_OWNER) + && !matches.is_present(options::format::LONG_NUMERIC_UID_GID) + && !matches.is_present(options::format::ONE_LINE) + { + default_format_style = true; + } + + let mut config = Config::from(&matches)?; + + if default_quoting_style { + config.quoting_style = QuotingStyle::C { + quotes: Quotes::None, + }; + } + if default_format_style { + config.format = Format::Columns; + } + + let locs = matches + .values_of_os(options::PATHS) + .map(|v| v.map(Path::new).collect()) + .unwrap_or_else(|| vec![Path::new(".")]); + + uu_ls::list(locs, &config) } -// Coreutils won't compile if not every util have an uu_app function. -// However, we can't put it here since it needs to be in the same place as -// the entry point for the dir util, which is in ls/ls.rs. We could put the -// entry point here, but we would need a lot of refactoring. -// To make our life easier, we use this dummy function. +// To avoid code duplication, we reuse ls uu_app function which has the same +// arguments. However, coreutils won't compile if one of the utils is missing +// an uu_app function, so we need this dummy one. pub fn uu_app<'a>() -> Command<'a> { Command::new(uucore::util_name()) } diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index f2782267b2d..a55f29e238d 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -13,14 +13,15 @@ extern crate uucore; #[macro_use] extern crate lazy_static; -mod quoting_style; +// dir and vdir also need access to the quoting_style module +pub mod quoting_style; use clap::{crate_version, Arg, Command}; use glob::Pattern; use lscolors::LsColors; use number_prefix::NumberPrefix; use once_cell::unsync::OnceCell; -use quoting_style::{escape_name, Quotes, QuotingStyle}; +use quoting_style::{escape_name, QuotingStyle}; #[cfg(windows)] use std::os::windows::fs::MetadataExt; use std::{ @@ -242,7 +243,7 @@ impl Display for LsError { } #[derive(PartialEq, Eq)] -enum Format { +pub enum Format { Columns, Long, OneLine, @@ -304,8 +305,9 @@ enum IndicatorStyle { Classify, } -struct Config { - format: Format, +pub struct Config { + // Dir and vdir needs access to this field + pub format: Format, files: Files, sort: Sort, recursive: bool, @@ -322,7 +324,8 @@ struct Config { alloc_size: bool, block_size: Option, width: u16, - quoting_style: QuotingStyle, + // Dir and vdir needs access to this field + pub quoting_style: QuotingStyle, indicator_style: IndicatorStyle, time_style: TimeStyle, context: bool, @@ -355,7 +358,7 @@ struct PaddingCollection { impl Config { #[allow(clippy::cognitive_complexity)] - fn from(options: &clap::ArgMatches) -> UResult { + pub fn from(options: &clap::ArgMatches) -> UResult { let context = options.is_present(options::CONTEXT); let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) { ( @@ -797,105 +800,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { list(locs, &config) } -/// The entry point for the dir coreutils -pub fn dir_main(args: impl uucore::Args) -> UResult<()> { - let command = uu_app(); - - let matches = command.get_matches_from(args); - - let mut default_quoting_style = false; - let mut default_format_style = false; - - // We check if any options on formatting or quoting style have been given. - // If not, we will use dir default formatting and quoting style options - - if !matches.is_present(options::QUOTING_STYLE) - && !matches.is_present(options::quoting::C) - && !matches.is_present(options::quoting::ESCAPE) - && !matches.is_present(options::quoting::LITERAL) - { - default_quoting_style = true; - } - if !matches.is_present(options::FORMAT) - && !matches.is_present(options::format::ACROSS) - && !matches.is_present(options::format::COLUMNS) - && !matches.is_present(options::format::COMMAS) - && !matches.is_present(options::format::LONG) - && !matches.is_present(options::format::LONG_NO_GROUP) - && !matches.is_present(options::format::LONG_NO_OWNER) - && !matches.is_present(options::format::LONG_NUMERIC_UID_GID) - && !matches.is_present(options::format::ONE_LINE) - { - default_format_style = true; - } - - let mut config = Config::from(&matches)?; - - if default_quoting_style { - config.quoting_style = QuotingStyle::C { - quotes: Quotes::None, - }; - } - if default_format_style { - config.format = Format::Columns; - } - - let locs = matches - .values_of_os(options::PATHS) - .map(|v| v.map(Path::new).collect()) - .unwrap_or_else(|| vec![Path::new(".")]); - list(locs, &config) -} - -/// The entry point for the vdir coreutils -pub fn vdir_main(args: impl uucore::Args) -> UResult<()> { - let command = uu_app(); - let matches = command.get_matches_from(args); - - let mut default_quoting_style = false; - let mut default_format_style = false; - - // We check if any options on formatting or quoting style have been given. - // If not, we will use dir default formatting and quoting style options - - if !matches.is_present(options::QUOTING_STYLE) - && !matches.is_present(options::quoting::C) - && !matches.is_present(options::quoting::ESCAPE) - && !matches.is_present(options::quoting::LITERAL) - { - default_quoting_style = true; - } - if !matches.is_present(options::FORMAT) - && !matches.is_present(options::format::ACROSS) - && !matches.is_present(options::format::COLUMNS) - && !matches.is_present(options::format::COMMAS) - && !matches.is_present(options::format::LONG) - && !matches.is_present(options::format::LONG_NO_GROUP) - && !matches.is_present(options::format::LONG_NO_OWNER) - && !matches.is_present(options::format::LONG_NUMERIC_UID_GID) - && !matches.is_present(options::format::ONE_LINE) - { - default_format_style = true; - } - - let mut config = Config::from(&matches)?; - - if default_quoting_style { - config.quoting_style = QuotingStyle::C { - quotes: Quotes::None, - }; - } - if default_format_style { - config.format = Format::Long; - } - - let locs = matches - .values_of_os(options::PATHS) - .map(|v| v.map(Path::new).collect()) - .unwrap_or_else(|| vec![Path::new(".")]); - list(locs, &config) -} - pub fn uu_app<'a>() -> Command<'a> { Command::new(uucore::util_name()) .version(crate_version!()) @@ -1638,7 +1542,7 @@ impl PathData { } } -fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { +pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> { let mut files = Vec::::new(); let mut dirs = Vec::::new(); let mut out = BufWriter::new(stdout()); diff --git a/src/uu/ls/src/quoting_style.rs b/src/uu/ls/src/quoting_style.rs index 4900dc566b7..9ff68927302 100644 --- a/src/uu/ls/src/quoting_style.rs +++ b/src/uu/ls/src/quoting_style.rs @@ -6,7 +6,7 @@ use std::ffi::OsStr; const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#']; const SPECIAL_SHELL_CHARS: &str = "`$&*()|[]{};\\'\"<>?! "; -pub(super) enum QuotingStyle { +pub enum QuotingStyle { Shell { escape: bool, always_quote: bool, @@ -21,7 +21,7 @@ pub(super) enum QuotingStyle { } #[derive(Clone, Copy)] -pub(super) enum Quotes { +pub enum Quotes { None, Single, Double, diff --git a/src/uu/vdir/src/vdir.rs b/src/uu/vdir/src/vdir.rs index c6b07a167db..b2af0b332ad 100644 --- a/src/uu/vdir/src/vdir.rs +++ b/src/uu/vdir/src/vdir.rs @@ -6,18 +6,63 @@ // * that was distributed with this source code. use clap::Command; +use std::path::Path; +use uu_ls::quoting_style::{Quotes, QuotingStyle}; +use uu_ls::{options, Config, Format}; use uucore::error::UResult; #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - uu_ls::vdir_main(args) + let command = uu_ls::uu_app(); + let matches = command.get_matches_from(args); + + let mut default_quoting_style = false; + let mut default_format_style = false; + + // We check if any options on formatting or quoting style have been given. + // If not, we will use dir default formatting and quoting style options + + if !matches.is_present(options::QUOTING_STYLE) + && !matches.is_present(options::quoting::C) + && !matches.is_present(options::quoting::ESCAPE) + && !matches.is_present(options::quoting::LITERAL) + { + default_quoting_style = true; + } + if !matches.is_present(options::FORMAT) + && !matches.is_present(options::format::ACROSS) + && !matches.is_present(options::format::COLUMNS) + && !matches.is_present(options::format::COMMAS) + && !matches.is_present(options::format::LONG) + && !matches.is_present(options::format::LONG_NO_GROUP) + && !matches.is_present(options::format::LONG_NO_OWNER) + && !matches.is_present(options::format::LONG_NUMERIC_UID_GID) + && !matches.is_present(options::format::ONE_LINE) + { + default_format_style = true; + } + + let mut config = Config::from(&matches)?; + + if default_quoting_style { + config.quoting_style = QuotingStyle::C { + quotes: Quotes::None, + }; + } + if default_format_style { + config.format = Format::Long; + } + + let locs = matches + .values_of_os(options::PATHS) + .map(|v| v.map(Path::new).collect()) + .unwrap_or_else(|| vec![Path::new(".")]); + uu_ls::list(locs, &config) } -// Coreutils won't compile if not every util have an uu_app function. -// However, we can't put it here since it needs to be in the same place as -// the entry point for the vdir util, which is in ls/ls.rs. We could put the -// entry point here, but we would need a lot of refactoring. -// To make our life easier, we use this dummy function. +// To avoid code duplication, we reuse ls uu_app function which has the same +// arguments. However, coreutils won't compile if one of the utils is missing +// an uu_app function, so we need this dummy one. pub fn uu_app<'a>() -> Command<'a> { Command::new(uucore::util_name()) }