mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 23:32:39 +00:00
Merge branch 'master' into hostname-uresult
This commit is contained in:
commit
e253fb89db
20 changed files with 910 additions and 177 deletions
|
@ -6,6 +6,7 @@
|
|||
// file that was distributed with this source code.
|
||||
|
||||
use clap::App;
|
||||
use clap::Arg;
|
||||
use clap::Shell;
|
||||
use std::cmp;
|
||||
use std::collections::hash_map::HashMap;
|
||||
|
@ -122,31 +123,38 @@ fn main() {
|
|||
|
||||
/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout
|
||||
fn gen_completions<T: uucore::Args>(
|
||||
mut args: impl Iterator<Item = OsString>,
|
||||
args: impl Iterator<Item = OsString>,
|
||||
util_map: UtilityMap<T>,
|
||||
) -> ! {
|
||||
let utility = args
|
||||
.next()
|
||||
.expect("expected utility as the first parameter")
|
||||
.to_str()
|
||||
.expect("utility name was not valid utf-8")
|
||||
.to_owned();
|
||||
let shell = args
|
||||
.next()
|
||||
.expect("expected shell as the second parameter")
|
||||
.to_str()
|
||||
.expect("shell name was not valid utf-8")
|
||||
.to_owned();
|
||||
let all_utilities: Vec<_> = std::iter::once("coreutils")
|
||||
.chain(util_map.keys().copied())
|
||||
.collect();
|
||||
|
||||
let matches = App::new("completion")
|
||||
.about("Prints completions to stdout")
|
||||
.arg(
|
||||
Arg::with_name("utility")
|
||||
.possible_values(&all_utilities)
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("shell")
|
||||
.possible_values(&Shell::variants())
|
||||
.required(true),
|
||||
)
|
||||
.get_matches_from(std::iter::once(OsString::from("completion")).chain(args));
|
||||
|
||||
let utility = matches.value_of("utility").unwrap();
|
||||
let shell = matches.value_of("shell").unwrap();
|
||||
|
||||
let mut app = if utility == "coreutils" {
|
||||
gen_coreutils_app(util_map)
|
||||
} else if let Some((_, app)) = util_map.get(utility.as_str()) {
|
||||
app()
|
||||
} else {
|
||||
eprintln!("{} is not a valid utility", utility);
|
||||
process::exit(1)
|
||||
util_map.get(utility).unwrap().1()
|
||||
};
|
||||
let shell: Shell = shell.parse().unwrap();
|
||||
let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + &utility;
|
||||
let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + utility;
|
||||
|
||||
app.gen_completions_to(bin_name, shell, &mut io::stdout());
|
||||
io::stdout().flush().unwrap();
|
||||
process::exit(0);
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
// spell-checker:ignore (ToDO) nonprint nonblank nonprinting
|
||||
|
||||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate unix_socket;
|
||||
#[macro_use]
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ extern crate uucore;
|
|||
|
||||
use chrono::prelude::DateTime;
|
||||
use chrono::Local;
|
||||
use clap::ArgMatches;
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -63,6 +64,7 @@ mod options {
|
|||
pub const TIME_STYLE: &str = "time-style";
|
||||
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
|
||||
pub const DEREFERENCE: &str = "dereference";
|
||||
pub const INODES: &str = "inodes";
|
||||
pub const FILE: &str = "FILE";
|
||||
}
|
||||
|
||||
|
@ -89,6 +91,7 @@ struct Options {
|
|||
separate_dirs: bool,
|
||||
one_file_system: bool,
|
||||
dereference: bool,
|
||||
inodes: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||
|
@ -102,6 +105,7 @@ struct Stat {
|
|||
is_dir: bool,
|
||||
size: u64,
|
||||
blocks: u64,
|
||||
inodes: u64,
|
||||
inode: Option<FileInfo>,
|
||||
created: Option<u64>,
|
||||
accessed: u64,
|
||||
|
@ -127,6 +131,7 @@ impl Stat {
|
|||
is_dir: metadata.is_dir(),
|
||||
size: metadata.len(),
|
||||
blocks: metadata.blocks() as u64,
|
||||
inodes: 1,
|
||||
inode: Some(file_info),
|
||||
created: birth_u64(&metadata),
|
||||
accessed: metadata.atime() as u64,
|
||||
|
@ -144,6 +149,7 @@ impl Stat {
|
|||
size: metadata.len(),
|
||||
blocks: size_on_disk / 1024 * 2,
|
||||
inode: file_info,
|
||||
inodes: 1,
|
||||
created: windows_creation_time_to_unix_time(metadata.creation_time()),
|
||||
accessed: windows_time_to_unix_time(metadata.last_access_time()),
|
||||
modified: windows_time_to_unix_time(metadata.last_write_time()),
|
||||
|
@ -257,6 +263,18 @@ fn read_block_size(s: Option<&str>) -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
fn choose_size(matches: &ArgMatches, stat: &Stat) -> u64 {
|
||||
if matches.is_present(options::INODES) {
|
||||
stat.inodes
|
||||
} else if matches.is_present(options::APPARENT_SIZE) || matches.is_present(options::BYTES) {
|
||||
stat.size
|
||||
} else {
|
||||
// The st_blocks field indicates the number of blocks allocated to the file, 512-byte units.
|
||||
// See: http://linux.die.net/man/2/stat
|
||||
stat.blocks * 512
|
||||
}
|
||||
}
|
||||
|
||||
// this takes `my_stat` to avoid having to stat files multiple times.
|
||||
// XXX: this should use the impl Trait return type when it is stabilized
|
||||
fn du(
|
||||
|
@ -307,6 +325,7 @@ fn du(
|
|||
} else {
|
||||
my_stat.size += this_stat.size;
|
||||
my_stat.blocks += this_stat.blocks;
|
||||
my_stat.inodes += 1;
|
||||
if options.all {
|
||||
stats.push(this_stat);
|
||||
}
|
||||
|
@ -330,6 +349,7 @@ fn du(
|
|||
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
|
||||
my_stat.size += stat.size;
|
||||
my_stat.blocks += stat.blocks;
|
||||
my_stat.inodes += stat.inodes;
|
||||
}
|
||||
options
|
||||
.max_depth
|
||||
|
@ -413,6 +433,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
separate_dirs: matches.is_present(options::SEPARATE_DIRS),
|
||||
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
|
||||
dereference: matches.is_present(options::DEREFERENCE),
|
||||
inodes: matches.is_present(options::INODES),
|
||||
};
|
||||
|
||||
let files = match matches.value_of(options::FILE) {
|
||||
|
@ -420,6 +441,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
None => vec!["."],
|
||||
};
|
||||
|
||||
if options.inodes
|
||||
&& (matches.is_present(options::APPARENT_SIZE) || matches.is_present(options::BYTES))
|
||||
{
|
||||
show_warning!("options --apparent-size and -b are ineffective with --inodes")
|
||||
}
|
||||
|
||||
let block_size = u64::try_from(read_block_size(matches.value_of(options::BLOCK_SIZE))).unwrap();
|
||||
|
||||
let threshold = matches.value_of(options::THRESHOLD).map(|s| {
|
||||
|
@ -445,7 +472,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
convert_size_other
|
||||
}
|
||||
};
|
||||
let convert_size = |size| convert_size_fn(size, multiplier, block_size);
|
||||
let convert_size = |size: u64| {
|
||||
if options.inodes {
|
||||
size.to_string()
|
||||
} else {
|
||||
convert_size_fn(size, multiplier, block_size)
|
||||
}
|
||||
};
|
||||
|
||||
let time_format_str = match matches.value_of("time-style") {
|
||||
Some(s) => match s {
|
||||
|
@ -488,15 +521,7 @@ Try '{} --help' for more information.",
|
|||
let (_, len) = iter.size_hint();
|
||||
let len = len.unwrap();
|
||||
for (index, stat) in iter.enumerate() {
|
||||
let size = if matches.is_present(options::APPARENT_SIZE)
|
||||
|| matches.is_present(options::BYTES)
|
||||
{
|
||||
stat.size
|
||||
} else {
|
||||
// C's stat is such that each block is assume to be 512 bytes
|
||||
// See: http://linux.die.net/man/2/stat
|
||||
stat.blocks * 512
|
||||
};
|
||||
let size = choose_size(&matches, &stat);
|
||||
|
||||
if threshold.map_or(false, |threshold| threshold.should_exclude(size)) {
|
||||
continue;
|
||||
|
@ -629,8 +654,8 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.help("print sizes in human readable format (e.g., 1K 234M 2G)")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("inodes")
|
||||
.long("inodes")
|
||||
Arg::with_name(options::INODES)
|
||||
.long(options::INODES)
|
||||
.help(
|
||||
"list inode usage information instead of block usage like --block-size=1K"
|
||||
)
|
||||
|
|
|
@ -15,6 +15,7 @@ extern crate uucore;
|
|||
use clap::{crate_version, App, Arg, ArgMatches};
|
||||
use file_diff::diff;
|
||||
use filetime::{set_file_times, FileTime};
|
||||
use uucore::backup_control::{self, BackupMode};
|
||||
use uucore::entries::{grp2gid, usr2uid};
|
||||
use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity};
|
||||
|
||||
|
@ -33,6 +34,7 @@ const DEFAULT_STRIP_PROGRAM: &str = "strip";
|
|||
pub struct Behavior {
|
||||
main_function: MainFunction,
|
||||
specified_mode: Option<u32>,
|
||||
backup_mode: BackupMode,
|
||||
suffix: String,
|
||||
owner: String,
|
||||
group: String,
|
||||
|
@ -68,7 +70,7 @@ static ABOUT: &str = "Copy SOURCE to DEST or multiple SOURCE(s) to the existing
|
|||
|
||||
static OPT_COMPARE: &str = "compare";
|
||||
static OPT_BACKUP: &str = "backup";
|
||||
static OPT_BACKUP_2: &str = "backup2";
|
||||
static OPT_BACKUP_NO_ARG: &str = "backup2";
|
||||
static OPT_DIRECTORY: &str = "directory";
|
||||
static OPT_IGNORED: &str = "ignored";
|
||||
static OPT_CREATE_LEADING: &str = "create-leading";
|
||||
|
@ -130,14 +132,17 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.arg(
|
||||
Arg::with_name(OPT_BACKUP)
|
||||
.long(OPT_BACKUP)
|
||||
.help("(unimplemented) make a backup of each existing destination file")
|
||||
.help("make a backup of each existing destination file")
|
||||
.takes_value(true)
|
||||
.require_equals(true)
|
||||
.min_values(0)
|
||||
.value_name("CONTROL")
|
||||
)
|
||||
.arg(
|
||||
// TODO implement flag
|
||||
Arg::with_name(OPT_BACKUP_2)
|
||||
Arg::with_name(OPT_BACKUP_NO_ARG)
|
||||
.short("b")
|
||||
.help("(unimplemented) like --backup but does not accept an argument")
|
||||
.help("like --backup but does not accept an argument")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_IGNORED)
|
||||
|
@ -210,7 +215,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
Arg::with_name(OPT_SUFFIX)
|
||||
.short("S")
|
||||
.long(OPT_SUFFIX)
|
||||
.help("(unimplemented) override the usual backup suffix")
|
||||
.help("override the usual backup suffix")
|
||||
.value_name("SUFFIX")
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
|
@ -265,13 +270,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
///
|
||||
///
|
||||
fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
||||
if matches.is_present(OPT_BACKUP) {
|
||||
Err("--backup")
|
||||
} else if matches.is_present(OPT_BACKUP_2) {
|
||||
Err("-b")
|
||||
} else if matches.is_present(OPT_SUFFIX) {
|
||||
Err("--suffix, -S")
|
||||
} else if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
|
||||
if matches.is_present(OPT_NO_TARGET_DIRECTORY) {
|
||||
Err("--no-target-directory, -T")
|
||||
} else if matches.is_present(OPT_PRESERVE_CONTEXT) {
|
||||
Err("--preserve-context, -P")
|
||||
|
@ -309,18 +308,16 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
|||
None
|
||||
};
|
||||
|
||||
let backup_suffix = if matches.is_present(OPT_SUFFIX) {
|
||||
matches.value_of(OPT_SUFFIX).ok_or(1)?
|
||||
} else {
|
||||
"~"
|
||||
};
|
||||
|
||||
let target_dir = matches.value_of(OPT_TARGET_DIRECTORY).map(|d| d.to_owned());
|
||||
|
||||
Ok(Behavior {
|
||||
main_function,
|
||||
specified_mode,
|
||||
suffix: backup_suffix.to_string(),
|
||||
backup_mode: backup_control::determine_backup_mode(
|
||||
matches.is_present(OPT_BACKUP_NO_ARG) || matches.is_present(OPT_BACKUP),
|
||||
matches.value_of(OPT_BACKUP),
|
||||
),
|
||||
suffix: backup_control::determine_backup_suffix(matches.value_of(OPT_SUFFIX)),
|
||||
owner: matches.value_of(OPT_OWNER).unwrap_or("").to_string(),
|
||||
group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(),
|
||||
verbose: matches.is_present(OPT_VERBOSE),
|
||||
|
@ -517,6 +514,28 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
|
|||
if b.compare && !need_copy(from, to, b) {
|
||||
return Ok(());
|
||||
}
|
||||
// Declare the path here as we may need it for the verbose output below.
|
||||
let mut backup_path = None;
|
||||
|
||||
// Perform backup, if any, before overwriting 'to'
|
||||
//
|
||||
// The codes actually making use of the backup process don't seem to agree
|
||||
// on how best to approach the issue. (mv and ln, for example)
|
||||
if to.exists() {
|
||||
backup_path = backup_control::get_backup_path(b.backup_mode, to, &b.suffix);
|
||||
if let Some(ref backup_path) = backup_path {
|
||||
// TODO!!
|
||||
if let Err(err) = fs::rename(to, backup_path) {
|
||||
show_error!(
|
||||
"install: cannot backup file '{}' to '{}': {}",
|
||||
to.display(),
|
||||
backup_path.display(),
|
||||
err
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if from.to_string_lossy() == "/dev/null" {
|
||||
/* workaround a limitation of fs::copy
|
||||
|
@ -624,7 +643,11 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
|
|||
}
|
||||
|
||||
if b.verbose {
|
||||
show_error!("'{}' -> '{}'", from.display(), to.display());
|
||||
print!("'{}' -> '{}'", from.display(), to.display());
|
||||
match backup_path {
|
||||
Some(path) => println!(" (backup: '{}')", path.display()),
|
||||
None => println!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf
|
||||
|
||||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
#[cfg(unix)]
|
||||
|
@ -46,19 +49,12 @@ use unicode_width::UnicodeWidthStr;
|
|||
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
|
||||
use uucore::{fs::display_permissions, version_cmp::version_cmp};
|
||||
|
||||
static ABOUT: &str = "
|
||||
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 '.'
|
||||
";
|
||||
static AFTER_HELP: &str = "The TIME_STYLE argument can be full-iso, long-iso, iso.
|
||||
Also the TIME_STYLE environment variable sets the default style to use.";
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{0} [OPTION]... [FILE]...", executable!())
|
||||
}
|
||||
|
||||
pub mod options {
|
||||
|
||||
pub mod format {
|
||||
pub static ONE_LINE: &str = "1";
|
||||
pub static LONG: &str = "long";
|
||||
|
@ -69,10 +65,12 @@ pub mod options {
|
|||
pub static LONG_NO_GROUP: &str = "o";
|
||||
pub static LONG_NUMERIC_UID_GID: &str = "numeric-uid-gid";
|
||||
}
|
||||
|
||||
pub mod files {
|
||||
pub static ALL: &str = "all";
|
||||
pub static ALMOST_ALL: &str = "almost-all";
|
||||
}
|
||||
|
||||
pub mod sort {
|
||||
pub static SIZE: &str = "S";
|
||||
pub static TIME: &str = "t";
|
||||
|
@ -80,30 +78,36 @@ pub mod options {
|
|||
pub static VERSION: &str = "v";
|
||||
pub static EXTENSION: &str = "X";
|
||||
}
|
||||
|
||||
pub mod time {
|
||||
pub static ACCESS: &str = "u";
|
||||
pub static CHANGE: &str = "c";
|
||||
}
|
||||
|
||||
pub mod size {
|
||||
pub static HUMAN_READABLE: &str = "human-readable";
|
||||
pub static SI: &str = "si";
|
||||
}
|
||||
|
||||
pub mod quoting {
|
||||
pub static ESCAPE: &str = "escape";
|
||||
pub static LITERAL: &str = "literal";
|
||||
pub static C: &str = "quote-name";
|
||||
}
|
||||
pub static QUOTING_STYLE: &str = "quoting-style";
|
||||
|
||||
pub mod indicator_style {
|
||||
pub static SLASH: &str = "p";
|
||||
pub static FILE_TYPE: &str = "file-type";
|
||||
pub static CLASSIFY: &str = "classify";
|
||||
}
|
||||
|
||||
pub mod dereference {
|
||||
pub static ALL: &str = "dereference";
|
||||
pub static ARGS: &str = "dereference-command-line";
|
||||
pub static DIR_ARGS: &str = "dereference-command-line-symlink-to-dir";
|
||||
}
|
||||
|
||||
pub static QUOTING_STYLE: &str = "quoting-style";
|
||||
pub static HIDE_CONTROL_CHARS: &str = "hide-control-chars";
|
||||
pub static SHOW_CONTROL_CHARS: &str = "show-control-chars";
|
||||
pub static WIDTH: &str = "width";
|
||||
|
@ -599,15 +603,27 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
pub fn uu_app() -> App<'static, 'static> {
|
||||
App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
|
||||
.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 '.'.",
|
||||
)
|
||||
// Format arguments
|
||||
.arg(
|
||||
Arg::with_name(options::FORMAT)
|
||||
.long(options::FORMAT)
|
||||
.help("Set the display format.")
|
||||
.takes_value(true)
|
||||
.possible_values(&["long", "verbose", "single-column", "columns", "vertical", "across", "horizontal", "commas"])
|
||||
.possible_values(&[
|
||||
"long",
|
||||
"verbose",
|
||||
"single-column",
|
||||
"columns",
|
||||
"vertical",
|
||||
"across",
|
||||
"horizontal",
|
||||
"commas",
|
||||
])
|
||||
.hide_possible_values(true)
|
||||
.require_equals(true)
|
||||
.overrides_with_all(&[
|
||||
|
@ -677,41 +693,51 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
Arg::with_name(options::format::ONE_LINE)
|
||||
.short(options::format::ONE_LINE)
|
||||
.help("List one file per line.")
|
||||
.multiple(true)
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::format::LONG_NO_GROUP)
|
||||
.short(options::format::LONG_NO_GROUP)
|
||||
.help("Long format without group information. Identical to --format=long with --no-group.")
|
||||
.multiple(true)
|
||||
.help(
|
||||
"Long format without group information. \
|
||||
Identical to --format=long with --no-group.",
|
||||
)
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::format::LONG_NO_OWNER)
|
||||
.short(options::format::LONG_NO_OWNER)
|
||||
.help("Long format without owner information.")
|
||||
.multiple(true)
|
||||
.multiple(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::format::LONG_NUMERIC_UID_GID)
|
||||
.short("n")
|
||||
.long(options::format::LONG_NUMERIC_UID_GID)
|
||||
.help("-l with numeric UIDs and GIDs.")
|
||||
.multiple(true)
|
||||
.multiple(true),
|
||||
)
|
||||
|
||||
// Quoting style
|
||||
.arg(
|
||||
Arg::with_name(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"])
|
||||
.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::with_name(options::quoting::LITERAL)
|
||||
|
@ -723,7 +749,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::quoting::ESCAPE)
|
||||
|
@ -735,7 +761,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::quoting::C)
|
||||
|
@ -747,76 +773,63 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::quoting::LITERAL,
|
||||
options::quoting::ESCAPE,
|
||||
options::quoting::C,
|
||||
])
|
||||
]),
|
||||
)
|
||||
|
||||
// Control characters
|
||||
.arg(
|
||||
Arg::with_name(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,
|
||||
])
|
||||
.overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(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,
|
||||
])
|
||||
.overrides_with_all(&[options::HIDE_CONTROL_CHARS, options::SHOW_CONTROL_CHARS]),
|
||||
)
|
||||
|
||||
// Time arguments
|
||||
.arg(
|
||||
Arg::with_name(options::TIME)
|
||||
.long(options::TIME)
|
||||
.help("Show time in <field>:\n\
|
||||
.help(
|
||||
"Show time in <field>:\n\
|
||||
\taccess time (-u): atime, access, use;\n\
|
||||
\tchange time (-t): ctime, status.\n\
|
||||
\tbirth time: birth, creation;")
|
||||
\tbirth time: birth, creation;",
|
||||
)
|
||||
.value_name("field")
|
||||
.takes_value(true)
|
||||
.possible_values(&["atime", "access", "use", "ctime", "status", "birth", "creation"])
|
||||
.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,
|
||||
])
|
||||
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::time::CHANGE)
|
||||
.short(options::time::CHANGE)
|
||||
.help("If the long listing format (e.g., -l, -o) is being used, print the status \
|
||||
.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,
|
||||
])
|
||||
format, sort according to the status change time.",
|
||||
)
|
||||
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::time::ACCESS)
|
||||
.short(options::time::ACCESS)
|
||||
.help("If the long listing format (e.g., -l, -o) is being used, print the status \
|
||||
.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,
|
||||
])
|
||||
access time.",
|
||||
)
|
||||
.overrides_with_all(&[options::TIME, options::time::ACCESS, options::time::CHANGE]),
|
||||
)
|
||||
|
||||
// Hide and ignore
|
||||
.arg(
|
||||
Arg::with_name(options::HIDE)
|
||||
|
@ -824,7 +837,9 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.value_name("PATTERN")
|
||||
.help("do not list implied entries matching shell PATTERN (overridden by -a or -A)")
|
||||
.help(
|
||||
"do not list implied entries matching shell PATTERN (overridden by -a or -A)",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::IGNORE)
|
||||
|
@ -833,7 +848,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.takes_value(true)
|
||||
.multiple(true)
|
||||
.value_name("PATTERN")
|
||||
.help("do not list implied entries matching shell PATTERN")
|
||||
.help("do not list implied entries matching shell PATTERN"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::IGNORE_BACKUPS)
|
||||
|
@ -841,7 +856,6 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.long(options::IGNORE_BACKUPS)
|
||||
.help("Ignore entries which end with ~."),
|
||||
)
|
||||
|
||||
// Sort arguments
|
||||
.arg(
|
||||
Arg::with_name(options::SORT)
|
||||
|
@ -858,7 +872,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::SIZE)
|
||||
|
@ -871,7 +885,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::TIME)
|
||||
|
@ -884,7 +898,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::VERSION)
|
||||
|
@ -897,7 +911,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::EXTENSION)
|
||||
|
@ -910,14 +924,16 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::sort::NONE)
|
||||
.short(options::sort::NONE)
|
||||
.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.")
|
||||
.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,
|
||||
|
@ -925,9 +941,8 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::sort::NONE,
|
||||
options::sort::VERSION,
|
||||
options::sort::EXTENSION,
|
||||
])
|
||||
]),
|
||||
)
|
||||
|
||||
// Dereferencing
|
||||
.arg(
|
||||
Arg::with_name(options::dereference::ALL)
|
||||
|
@ -941,48 +956,43 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::dereference::ALL,
|
||||
options::dereference::DIR_ARGS,
|
||||
options::dereference::ARGS,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(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.",
|
||||
given as command line arguments.",
|
||||
)
|
||||
.overrides_with_all(&[
|
||||
options::dereference::ALL,
|
||||
options::dereference::DIR_ARGS,
|
||||
options::dereference::ARGS,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::dereference::ARGS)
|
||||
.short("H")
|
||||
.long(options::dereference::ARGS)
|
||||
.help(
|
||||
"Do not dereference symlinks except when given as command line arguments.",
|
||||
)
|
||||
.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::with_name(options::NO_GROUP)
|
||||
.long(options::NO_GROUP)
|
||||
.short("-G")
|
||||
.help("Do not show group in long format.")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::AUTHOR)
|
||||
.long(options::AUTHOR)
|
||||
.help("Show author in long format. On the supported platforms, the author \
|
||||
always matches the file owner.")
|
||||
.help("Do not show group in long format."),
|
||||
)
|
||||
.arg(Arg::with_name(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::with_name(options::files::ALL)
|
||||
|
@ -995,9 +1005,9 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.short("A")
|
||||
.long(options::files::ALMOST_ALL)
|
||||
.help(
|
||||
"In a directory, do not ignore all file names that start with '.', only ignore \
|
||||
'.' and '..'.",
|
||||
),
|
||||
"In a directory, do not ignore all file names that start with '.', \
|
||||
only ignore '.' and '..'.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::DIRECTORY)
|
||||
|
@ -1020,7 +1030,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.arg(
|
||||
Arg::with_name(options::size::SI)
|
||||
.long(options::size::SI)
|
||||
.help("Print human readable file sizes using powers of 1000 instead of 1024.")
|
||||
.help("Print human readable file sizes using powers of 1000 instead of 1024."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::INODE)
|
||||
|
@ -1032,9 +1042,11 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
Arg::with_name(options::REVERSE)
|
||||
.short("r")
|
||||
.long(options::REVERSE)
|
||||
.help("Reverse whatever the sorting method is--e.g., list files in reverse \
|
||||
.help(
|
||||
"Reverse whatever the sorting method is e.g., list files in reverse \
|
||||
alphabetical order, youngest first, smallest first, or whatever.",
|
||||
))
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::RECURSIVE)
|
||||
.short("R")
|
||||
|
@ -1047,7 +1059,7 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.short("w")
|
||||
.help("Assume that the terminal is COLS columns wide.")
|
||||
.value_name("COLS")
|
||||
.takes_value(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::COLOR)
|
||||
|
@ -1060,8 +1072,10 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.arg(
|
||||
Arg::with_name(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)")
|
||||
.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(&[
|
||||
|
@ -1069,21 +1083,24 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::indicator_style::SLASH,
|
||||
options::indicator_style::CLASSIFY,
|
||||
options::INDICATOR_STYLE,
|
||||
]))
|
||||
.arg(
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(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.")
|
||||
.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.",
|
||||
)
|
||||
.overrides_with_all(&[
|
||||
options::indicator_style::FILE_TYPE,
|
||||
options::indicator_style::SLASH,
|
||||
options::indicator_style::CLASSIFY,
|
||||
options::INDICATOR_STYLE,
|
||||
])
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::indicator_style::FILE_TYPE)
|
||||
|
@ -1094,18 +1111,19 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
options::indicator_style::SLASH,
|
||||
options::indicator_style::CLASSIFY,
|
||||
options::INDICATOR_STYLE,
|
||||
]))
|
||||
]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::indicator_style::SLASH)
|
||||
.short(options::indicator_style::SLASH)
|
||||
.help("Append / indicator to directories."
|
||||
)
|
||||
.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::with_name(options::TIME_STYLE)
|
||||
|
@ -1113,27 +1131,25 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.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
|
||||
])
|
||||
.possible_values(&["full-iso", "long-iso", "iso", "locale"])
|
||||
.overrides_with_all(&[options::TIME_STYLE]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::FULL_TIME)
|
||||
.long(options::FULL_TIME)
|
||||
.overrides_with(options::FULL_TIME)
|
||||
.help("like -l --time-style=full-iso")
|
||||
.long(options::FULL_TIME)
|
||||
.overrides_with(options::FULL_TIME)
|
||||
.help("like -l --time-style=full-iso"),
|
||||
)
|
||||
// Positional arguments
|
||||
.arg(
|
||||
Arg::with_name(options::PATHS)
|
||||
.multiple(true)
|
||||
.takes_value(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.",
|
||||
)
|
||||
|
||||
// Positional arguments
|
||||
.arg(Arg::with_name(options::PATHS).multiple(true).takes_value(true))
|
||||
|
||||
.after_help(AFTER_HELP)
|
||||
}
|
||||
|
||||
/// Represents a Path along with it's associated data
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
|
||||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
// spell-checker:ignore (paths) GPGHome
|
||||
|
||||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
|
|
|
@ -349,12 +349,10 @@ impl<'a> Pager<'a> {
|
|||
let status = format!("--More--({})", status_inner);
|
||||
|
||||
let banner = match (self.silent, wrong_key) {
|
||||
(true, Some(key)) => {
|
||||
format!(
|
||||
"{} [Unknown key: '{}'. Press 'h' for instructions. (unimplemented)]",
|
||||
status, key
|
||||
)
|
||||
}
|
||||
(true, Some(key)) => format!(
|
||||
"{} [Unknown key: '{}'. Press 'h' for instructions. (unimplemented)]",
|
||||
status, key
|
||||
),
|
||||
(true, None) => format!("{}[Press space to continue, 'q' to quit.]", status),
|
||||
(false, Some(_)) => format!("{}{}", status, BELL),
|
||||
(false, None) => status,
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
// spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm
|
||||
|
||||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
pub extern crate filetime;
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
// spell-checker:ignore (strings) ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
|
||||
// clippy bug https://github.com/rust-lang/rust-clippy/issues/7422
|
||||
#![allow(clippy::nonstandard_macro_braces)]
|
||||
|
||||
extern crate data_encoding;
|
||||
|
||||
use self::data_encoding::{DecodeError, BASE32, BASE64};
|
||||
|
|
|
@ -27,6 +27,9 @@ macro_rules! show(
|
|||
let e = $err;
|
||||
uucore::error::set_exit_code(e.code());
|
||||
eprintln!("{}: {}", executable!(), e);
|
||||
if e.usage() {
|
||||
eprintln!("Try '{} --help' for more information.", executable!());
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -37,6 +37,19 @@ pub fn determine_backup_suffix(supplied_suffix: Option<&str>) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// # TODO
|
||||
///
|
||||
/// This function currently deviates slightly from how the [manual][1] describes
|
||||
/// that it should work. In particular, the current implementation:
|
||||
///
|
||||
/// 1. Doesn't strictly respect the order in which to determine the backup type,
|
||||
/// which is (in order of precedence)
|
||||
/// 1. Take a valid value to the '--backup' option
|
||||
/// 2. Take the value of the `VERSION_CONTROL` env var
|
||||
/// 3. default to 'existing'
|
||||
/// 2. Doesn't accept abbreviations to the 'backup_option' parameter
|
||||
///
|
||||
/// [1]: https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html
|
||||
pub fn determine_backup_mode(backup_opt_exists: bool, backup_opt: Option<&str>) -> BackupMode {
|
||||
if backup_opt_exists {
|
||||
match backup_opt.map(String::from) {
|
||||
|
|
|
@ -144,6 +144,13 @@ impl UError {
|
|||
UError::Custom(e) => e.code(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn usage(&self) -> bool {
|
||||
match self {
|
||||
UError::Common(e) => e.usage(),
|
||||
UError::Custom(e) => e.usage(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UCommonError> for UError {
|
||||
|
@ -220,6 +227,10 @@ pub trait UCustomError: Error {
|
|||
fn code(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn usage(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<dyn UCustomError>> for i32 {
|
||||
|
@ -291,6 +302,37 @@ impl UCustomError for USimpleError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UUsageError {
|
||||
pub code: i32,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl UUsageError {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(code: i32, message: String) -> UError {
|
||||
UError::Custom(Box::new(Self { code, message }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for UUsageError {}
|
||||
|
||||
impl Display for UUsageError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
self.message.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl UCustomError for UUsageError {
|
||||
fn code(&self) -> i32 {
|
||||
self.code
|
||||
}
|
||||
|
||||
fn usage(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type around [`std::io::Error`].
|
||||
///
|
||||
/// The messages displayed by [`UIoError`] should match the error messages displayed by GNU
|
||||
|
@ -331,6 +373,10 @@ impl UIoError {
|
|||
pub fn code(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
pub fn usage(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for UIoError {}
|
||||
|
@ -427,6 +473,10 @@ impl UCommonError {
|
|||
pub fn code(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
pub fn usage(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UCommonError> for i32 {
|
||||
|
|
|
@ -103,6 +103,9 @@ pub fn gen_uumain(_args: TokenStream, stream: TokenStream) -> TokenStream {
|
|||
if s != "" {
|
||||
show_error!("{}", s);
|
||||
}
|
||||
if e.usage() {
|
||||
eprintln!("Try '{} --help' for more information.", executable!());
|
||||
}
|
||||
e.code()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -292,6 +292,77 @@ fn _du_dereference(s: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_du_inodes_basic() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.ucmd().arg("--inodes").succeeds();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let result_reference = scene.cmd("du").arg("--inodes").run();
|
||||
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
_du_inodes_basic(result.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn _du_inodes_basic(s: &str) {
|
||||
assert_eq!(
|
||||
s,
|
||||
"2\t.\\subdir\\deeper\\deeper_dir
|
||||
4\t.\\subdir\\deeper
|
||||
3\t.\\subdir\\links
|
||||
8\t.\\subdir
|
||||
11\t.
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn _du_inodes_basic(s: &str) {
|
||||
assert_eq!(
|
||||
s,
|
||||
"2\t./subdir/deeper/deeper_dir
|
||||
4\t./subdir/deeper
|
||||
3\t./subdir/links
|
||||
8\t./subdir
|
||||
11\t.
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_du_inodes() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--summarize")
|
||||
.arg("--inodes")
|
||||
.succeeds()
|
||||
.stdout_only("11\t.\n");
|
||||
|
||||
let result = scene
|
||||
.ucmd()
|
||||
.arg("--separate-dirs")
|
||||
.arg("--inodes")
|
||||
.succeeds();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
result.stdout_contains("3\t.\\subdir\\links\n");
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
result.stdout_contains("3\t./subdir/links\n");
|
||||
result.stdout_contains("3\t.\n");
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let result_reference = scene.cmd("du").arg("--separate-dirs").arg("--inodes").run();
|
||||
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_du_h_flag_empty_file() {
|
||||
new_ucmd!()
|
||||
|
@ -419,3 +490,96 @@ fn test_du_threshold() {
|
|||
.stdout_does_not_contain("links")
|
||||
.stdout_contains("deeper_dir");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_du_apparent_size() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.ucmd().arg("--apparent-size").succeeds();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let result_reference = scene.cmd("du").arg("--apparent-size").run();
|
||||
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
_du_apparent_size(result.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn _du_apparent_size(s: &str) {
|
||||
assert_eq!(
|
||||
s,
|
||||
"1\t.\\subdir\\deeper\\deeper_dir
|
||||
1\t.\\subdir\\deeper
|
||||
6\t.\\subdir\\links
|
||||
6\t.\\subdir
|
||||
6\t.
|
||||
"
|
||||
);
|
||||
}
|
||||
#[cfg(target_vendor = "apple")]
|
||||
fn _du_apparent_size(s: &str) {
|
||||
assert_eq!(
|
||||
s,
|
||||
"1\t./subdir/deeper/deeper_dir
|
||||
1\t./subdir/deeper
|
||||
6\t./subdir/links
|
||||
6\t./subdir
|
||||
6\t.
|
||||
"
|
||||
);
|
||||
}
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn _du_apparent_size(s: &str) {
|
||||
assert_eq!(
|
||||
s,
|
||||
"1\t./subdir/deeper/deeper_dir
|
||||
2\t./subdir/deeper
|
||||
6\t./subdir/links
|
||||
8\t./subdir
|
||||
8\t.
|
||||
"
|
||||
);
|
||||
}
|
||||
#[cfg(all(
|
||||
not(target_vendor = "apple"),
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "freebsd")
|
||||
))]
|
||||
fn _du_apparent_size(s: &str) {
|
||||
assert_eq!(
|
||||
s,
|
||||
"5\t./subdir/deeper/deeper_dir
|
||||
9\t./subdir/deeper
|
||||
10\t./subdir/links
|
||||
22\t./subdir
|
||||
26\t.
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_du_bytes() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.ucmd().arg("--bytes").succeeds();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let result_reference = scene.cmd("du").arg("--bytes").run();
|
||||
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
result.stdout_contains("5145\t.\\subdir\n");
|
||||
#[cfg(target_vendor = "apple")]
|
||||
result.stdout_contains("5625\t./subdir\n");
|
||||
#[cfg(target_os = "freebsd")]
|
||||
result.stdout_contains("7193\t./subdir\n");
|
||||
#[cfg(all(
|
||||
not(target_vendor = "apple"),
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "freebsd")
|
||||
))]
|
||||
result.stdout_contains("21529\t./subdir\n");
|
||||
}
|
||||
|
|
|
@ -250,6 +250,28 @@ hello
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_utf8() {
|
||||
let bytes: &[u8] = b"\xfc\x80\x80\x80\x80\xaf";
|
||||
new_ucmd!()
|
||||
.args(&["-c", "6"])
|
||||
.pipe_in(bytes)
|
||||
.succeeds()
|
||||
.stdout_is_bytes(bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_utf8_lines() {
|
||||
let input: &[u8] =
|
||||
b"\xfc\x80\x80\x80\x80\xaf\nb\xfc\x80\x80\x80\x80\xaf\nb\xfc\x80\x80\x80\x80\xaf";
|
||||
let output = b"\xfc\x80\x80\x80\x80\xaf\nb\xfc\x80\x80\x80\x80\xaf\n";
|
||||
new_ucmd!()
|
||||
.args(&["-n", "2"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_is_bytes(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_head_invalid_num() {
|
||||
new_ucmd!()
|
||||
|
|
|
@ -696,3 +696,388 @@ fn test_install_dir() {
|
|||
assert!(at.file_exists(&format!("{}/{}", dir, file1)));
|
||||
assert!(at.file_exists(&format!("{}/{}", dir, file2)));
|
||||
}
|
||||
//
|
||||
// test backup functionality
|
||||
#[test]
|
||||
fn test_install_backup_short_no_args_files() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_simple_backup_file_a";
|
||||
let file_b = "test_install_simple_backup_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-b")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_short_no_args_file_to_dir() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file = "test_install_simple_backup_file_a";
|
||||
let dest_dir = "test_install_dest/";
|
||||
let expect = format!("{}{}", dest_dir, file);
|
||||
|
||||
at.touch(file);
|
||||
at.mkdir(dest_dir);
|
||||
at.touch(&expect);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-b")
|
||||
.arg(file)
|
||||
.arg(dest_dir)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file));
|
||||
assert!(at.file_exists(&expect));
|
||||
assert!(at.file_exists(&format!("{}~", expect)));
|
||||
}
|
||||
|
||||
// Long --backup option is tested separately as it requires a slightly different
|
||||
// handling than '-b' does.
|
||||
#[test]
|
||||
fn test_install_backup_long_no_args_files() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_simple_backup_file_a";
|
||||
let file_b = "test_install_simple_backup_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_long_no_args_file_to_dir() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file = "test_install_simple_backup_file_a";
|
||||
let dest_dir = "test_install_dest/";
|
||||
let expect = format!("{}{}", dest_dir, file);
|
||||
|
||||
at.touch(file);
|
||||
at.mkdir(dest_dir);
|
||||
at.touch(&expect);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup")
|
||||
.arg(file)
|
||||
.arg(dest_dir)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file));
|
||||
assert!(at.file_exists(&expect));
|
||||
assert!(at.file_exists(&format!("{}~", expect)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_short_custom_suffix() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_custom_suffix_file_a";
|
||||
let file_b = "test_install_backup_custom_suffix_file_b";
|
||||
let suffix = "super-suffix-of-the-century";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-b")
|
||||
.arg(format!("--suffix={}", suffix))
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}{}", file_b, suffix)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_custom_suffix_via_env() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_custom_suffix_file_a";
|
||||
let file_b = "test_install_backup_custom_suffix_file_b";
|
||||
let suffix = "super-suffix-of-the-century";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-b")
|
||||
.env("SIMPLE_BACKUP_SUFFIX", suffix)
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}{}", file_b, suffix)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_numbered_with_t() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=t")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}.~1~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_numbered_with_numbered() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=numbered")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}.~1~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_existing() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=existing")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_nil() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=nil")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_numbered_if_existing_backup_existing() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
let file_b_backup = "test_install_backup_numbering_file_b.~1~";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
at.touch(file_b_backup);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=existing")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(file_b_backup));
|
||||
assert!(at.file_exists(&*format!("{}.~2~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_numbered_if_existing_backup_nil() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
let file_b_backup = "test_install_backup_numbering_file_b.~1~";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
at.touch(file_b_backup);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=nil")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(file_b_backup));
|
||||
assert!(at.file_exists(&*format!("{}.~2~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_simple() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=simple")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_never() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=never")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(at.file_exists(&format!("{}~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_none() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=none")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(!at.file_exists(&format!("{}~", file_b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_backup_off() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_install_backup_numbering_file_a";
|
||||
let file_b = "test_install_backup_numbering_file_b";
|
||||
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("--backup=off")
|
||||
.arg(file_a)
|
||||
.arg(file_b)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
assert!(!at.file_exists(&format!("{}~", file_b)));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue