Merge branch 'master' into hostname-uresult

This commit is contained in:
Terts Diepraam 2021-07-06 11:21:42 +02:00
commit e253fb89db
20 changed files with 910 additions and 177 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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]

View file

@ -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;

View file

@ -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"
)

View file

@ -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(())

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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]

View file

@ -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;

View file

@ -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};

View file

@ -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!());
}
})
);

View file

@ -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) {

View file

@ -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 {

View file

@ -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()
}
}

View file

@ -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");
}

View file

@ -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!()

View file

@ -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)));
}