mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge branch 'main' into tail_notify
This commit is contained in:
commit
8ee806a444
16 changed files with 651 additions and 225 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -2085,9 +2085,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "users"
|
name = "users"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486"
|
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
@ -403,7 +403,7 @@ rlimit = "0.8.3"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dev-dependencies]
|
[target.'cfg(unix)'.dev-dependencies]
|
||||||
nix = { version = "0.24.1", default-features = false, features = ["process", "signal", "user"] }
|
nix = { version = "0.24.1", default-features = false, features = ["process", "signal", "user"] }
|
||||||
rust-users = { version="0.10", package="users" }
|
rust-users = { version="0.11", package="users" }
|
||||||
unix_socket = "0.5.0"
|
unix_socket = "0.5.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -204,12 +204,18 @@ pub(crate) struct RowFormatter<'a> {
|
||||||
// numbers. We could split the options up into those groups to
|
// numbers. We could split the options up into those groups to
|
||||||
// reduce the coupling between this `table.rs` module and the main
|
// reduce the coupling between this `table.rs` module and the main
|
||||||
// `df.rs` module.
|
// `df.rs` module.
|
||||||
|
/// Whether to use the special rules for displaying the total row.
|
||||||
|
is_total_row: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RowFormatter<'a> {
|
impl<'a> RowFormatter<'a> {
|
||||||
/// Instantiate this struct.
|
/// Instantiate this struct.
|
||||||
pub(crate) fn new(row: &'a Row, options: &'a Options) -> Self {
|
pub(crate) fn new(row: &'a Row, options: &'a Options, is_total_row: bool) -> Self {
|
||||||
Self { row, options }
|
Self {
|
||||||
|
row,
|
||||||
|
options,
|
||||||
|
is_total_row,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a string giving the scaled version of the input number.
|
/// Get a string giving the scaled version of the input number.
|
||||||
|
@ -251,13 +257,25 @@ impl<'a> RowFormatter<'a> {
|
||||||
|
|
||||||
for column in &self.options.columns {
|
for column in &self.options.columns {
|
||||||
let string = match column {
|
let string = match column {
|
||||||
Column::Source => self.row.fs_device.to_string(),
|
Column::Source => {
|
||||||
|
if self.is_total_row {
|
||||||
|
"total".to_string()
|
||||||
|
} else {
|
||||||
|
self.row.fs_device.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
Column::Size => self.scaled_bytes(self.row.bytes),
|
Column::Size => self.scaled_bytes(self.row.bytes),
|
||||||
Column::Used => self.scaled_bytes(self.row.bytes_used),
|
Column::Used => self.scaled_bytes(self.row.bytes_used),
|
||||||
Column::Avail => self.scaled_bytes(self.row.bytes_avail),
|
Column::Avail => self.scaled_bytes(self.row.bytes_avail),
|
||||||
Column::Pcent => Self::percentage(self.row.bytes_usage),
|
Column::Pcent => Self::percentage(self.row.bytes_usage),
|
||||||
|
|
||||||
Column::Target => self.row.fs_mount.to_string(),
|
Column::Target => {
|
||||||
|
if self.is_total_row && !self.options.columns.contains(&Column::Source) {
|
||||||
|
"total".to_string()
|
||||||
|
} else {
|
||||||
|
self.row.fs_mount.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
Column::Itotal => self.scaled_inodes(self.row.inodes),
|
Column::Itotal => self.scaled_inodes(self.row.inodes),
|
||||||
Column::Iused => self.scaled_inodes(self.row.inodes_used),
|
Column::Iused => self.scaled_inodes(self.row.inodes_used),
|
||||||
Column::Iavail => self.scaled_inodes(self.row.inodes_free),
|
Column::Iavail => self.scaled_inodes(self.row.inodes_free),
|
||||||
|
@ -371,7 +389,7 @@ impl Table {
|
||||||
// the output table.
|
// the output table.
|
||||||
if options.show_all_fs || filesystem.usage.blocks > 0 {
|
if options.show_all_fs || filesystem.usage.blocks > 0 {
|
||||||
let row = Row::from(filesystem);
|
let row = Row::from(filesystem);
|
||||||
let fmt = RowFormatter::new(&row, options);
|
let fmt = RowFormatter::new(&row, options, false);
|
||||||
let values = fmt.get_values();
|
let values = fmt.get_values();
|
||||||
total += row;
|
total += row;
|
||||||
|
|
||||||
|
@ -386,7 +404,7 @@ impl Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.show_total {
|
if options.show_total {
|
||||||
let total_row = RowFormatter::new(&total, options);
|
let total_row = RowFormatter::new(&total, options, true);
|
||||||
rows.push(total_row.get_values());
|
rows.push(total_row.get_values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,7 +643,7 @@ mod tests {
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let fmt = RowFormatter::new(&row, &options);
|
let fmt = RowFormatter::new(&row, &options, false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fmt.get_values(),
|
fmt.get_values(),
|
||||||
vec!("my_device", "100", "25", "75", "25%", "my_mount")
|
vec!("my_device", "100", "25", "75", "25%", "my_mount")
|
||||||
|
@ -651,7 +669,7 @@ mod tests {
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let fmt = RowFormatter::new(&row, &options);
|
let fmt = RowFormatter::new(&row, &options, false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fmt.get_values(),
|
fmt.get_values(),
|
||||||
vec!("my_device", "my_type", "100", "25", "75", "25%", "my_mount")
|
vec!("my_device", "my_type", "100", "25", "75", "25%", "my_mount")
|
||||||
|
@ -676,7 +694,7 @@ mod tests {
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let fmt = RowFormatter::new(&row, &options);
|
let fmt = RowFormatter::new(&row, &options, false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fmt.get_values(),
|
fmt.get_values(),
|
||||||
vec!("my_device", "10", "2", "8", "20%", "my_mount")
|
vec!("my_device", "10", "2", "8", "20%", "my_mount")
|
||||||
|
@ -695,7 +713,7 @@ mod tests {
|
||||||
inodes: 10,
|
inodes: 10,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let fmt = RowFormatter::new(&row, &options);
|
let fmt = RowFormatter::new(&row, &options, false);
|
||||||
assert_eq!(fmt.get_values(), vec!("1", "10"));
|
assert_eq!(fmt.get_values(), vec!("1", "10"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,7 +736,7 @@ mod tests {
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let fmt = RowFormatter::new(&row, &options);
|
let fmt = RowFormatter::new(&row, &options, false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fmt.get_values(),
|
fmt.get_values(),
|
||||||
vec!("my_device", "my_type", "4k", "1k", "3k", "25%", "my_mount")
|
vec!("my_device", "my_type", "4k", "1k", "3k", "25%", "my_mount")
|
||||||
|
@ -744,7 +762,7 @@ mod tests {
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let fmt = RowFormatter::new(&row, &options);
|
let fmt = RowFormatter::new(&row, &options, false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fmt.get_values(),
|
fmt.get_values(),
|
||||||
vec!("my_device", "my_type", "4K", "1K", "3K", "25%", "my_mount")
|
vec!("my_device", "my_type", "4K", "1K", "3K", "25%", "my_mount")
|
||||||
|
@ -761,7 +779,7 @@ mod tests {
|
||||||
bytes_usage: Some(0.251),
|
bytes_usage: Some(0.251),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let fmt = RowFormatter::new(&row, &options);
|
let fmt = RowFormatter::new(&row, &options, false);
|
||||||
assert_eq!(fmt.get_values(), vec!("26%"));
|
assert_eq!(fmt.get_values(), vec!("26%"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,7 +798,7 @@ mod tests {
|
||||||
bytes_avail,
|
bytes_avail,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
RowFormatter::new(&row, &options).get_values()
|
RowFormatter::new(&row, &options, false).get_values()
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(get_formatted_values(100, 100, 0), vec!("1", "1", "0"));
|
assert_eq!(get_formatted_values(100, 100, 0), vec!("1", "1", "0"));
|
||||||
|
|
|
@ -4,6 +4,7 @@ Create the test fixtures by writing the output of the GNU dircolors commands to
|
||||||
|
|
||||||
```
|
```
|
||||||
$ dircolors --print-database > /PATH_TO_COREUTILS/tests/fixtures/dircolors/internal.expected
|
$ dircolors --print-database > /PATH_TO_COREUTILS/tests/fixtures/dircolors/internal.expected
|
||||||
|
$ dircolors --print-ls-colors > /PATH_TO_COREUTILS/tests/fixtures/dircolors/ls_colors.expected
|
||||||
$ dircolors -b > /PATH_TO_COREUTILS/tests/fixtures/dircolors/bash_def.expected
|
$ dircolors -b > /PATH_TO_COREUTILS/tests/fixtures/dircolors/bash_def.expected
|
||||||
$ dircolors -c > /PATH_TO_COREUTILS/tests/fixtures/dircolors/csh_def.expected
|
$ dircolors -c > /PATH_TO_COREUTILS/tests/fixtures/dircolors/csh_def.expected
|
||||||
```
|
```
|
||||||
|
|
|
@ -21,6 +21,7 @@ mod options {
|
||||||
pub const BOURNE_SHELL: &str = "bourne-shell";
|
pub const BOURNE_SHELL: &str = "bourne-shell";
|
||||||
pub const C_SHELL: &str = "c-shell";
|
pub const C_SHELL: &str = "c-shell";
|
||||||
pub const PRINT_DATABASE: &str = "print-database";
|
pub const PRINT_DATABASE: &str = "print-database";
|
||||||
|
pub const PRINT_LS_COLORS: &str = "print-ls-colors";
|
||||||
pub const FILE: &str = "FILE";
|
pub const FILE: &str = "FILE";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ use self::colors::INTERNAL_DB;
|
||||||
pub enum OutputFmt {
|
pub enum OutputFmt {
|
||||||
Shell,
|
Shell,
|
||||||
CShell,
|
CShell,
|
||||||
|
Display,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +78,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
// clap provides .conflicts_with / .conflicts_with_all, but we want to
|
// clap provides .conflicts_with / .conflicts_with_all, but we want to
|
||||||
// manually handle conflicts so we can match the output of GNU coreutils
|
// manually handle conflicts so we can match the output of GNU coreutils
|
||||||
if (matches.is_present(options::C_SHELL) || matches.is_present(options::BOURNE_SHELL))
|
if (matches.is_present(options::C_SHELL) || matches.is_present(options::BOURNE_SHELL))
|
||||||
&& matches.is_present(options::PRINT_DATABASE)
|
&& (matches.is_present(options::PRINT_DATABASE)
|
||||||
|
|| matches.is_present(options::PRINT_LS_COLORS))
|
||||||
{
|
{
|
||||||
return Err(UUsageError::new(
|
return Err(UUsageError::new(
|
||||||
1,
|
1,
|
||||||
|
@ -85,6 +88,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if matches.is_present(options::PRINT_DATABASE) && matches.is_present(options::PRINT_LS_COLORS) {
|
||||||
|
return Err(UUsageError::new(
|
||||||
|
1,
|
||||||
|
"options --print-database and --print-ls-colors are mutually exclusive",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if matches.is_present(options::PRINT_DATABASE) {
|
if matches.is_present(options::PRINT_DATABASE) {
|
||||||
if !files.is_empty() {
|
if !files.is_empty() {
|
||||||
return Err(UUsageError::new(
|
return Err(UUsageError::new(
|
||||||
|
@ -100,12 +110,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut out_format = OutputFmt::Unknown;
|
let mut out_format = if matches.is_present(options::C_SHELL) {
|
||||||
if matches.is_present(options::C_SHELL) {
|
OutputFmt::CShell
|
||||||
out_format = OutputFmt::CShell;
|
|
||||||
} else if matches.is_present(options::BOURNE_SHELL) {
|
} else if matches.is_present(options::BOURNE_SHELL) {
|
||||||
out_format = OutputFmt::Shell;
|
OutputFmt::Shell
|
||||||
}
|
} else if matches.is_present(options::PRINT_LS_COLORS) {
|
||||||
|
OutputFmt::Display
|
||||||
|
} else {
|
||||||
|
OutputFmt::Unknown
|
||||||
|
};
|
||||||
|
|
||||||
if out_format == OutputFmt::Unknown {
|
if out_format == OutputFmt::Unknown {
|
||||||
match guess_syntax() {
|
match guess_syntax() {
|
||||||
|
@ -186,6 +199,12 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.help("print the byte counts")
|
.help("print the byte counts")
|
||||||
.display_order(3),
|
.display_order(3),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::PRINT_LS_COLORS)
|
||||||
|
.long("print-ls-colors")
|
||||||
|
.help("output fully escaped colors for display")
|
||||||
|
.display_order(4),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::FILE)
|
Arg::new(options::FILE)
|
||||||
.hide(true)
|
.hide(true)
|
||||||
|
@ -254,6 +273,7 @@ enum ParseState {
|
||||||
Continue,
|
Continue,
|
||||||
Pass,
|
Pass,
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use uucore::{format_usage, InvalidEncodingHandling};
|
use uucore::{format_usage, InvalidEncodingHandling};
|
||||||
|
|
||||||
|
@ -262,11 +282,12 @@ where
|
||||||
T: IntoIterator,
|
T: IntoIterator,
|
||||||
T::Item: Borrow<str>,
|
T::Item: Borrow<str>,
|
||||||
{
|
{
|
||||||
// 1440 > $(dircolors | wc -m)
|
// 1790 > $(dircolors | wc -m)
|
||||||
let mut result = String::with_capacity(1440);
|
let mut result = String::with_capacity(1790);
|
||||||
match fmt {
|
match fmt {
|
||||||
OutputFmt::Shell => result.push_str("LS_COLORS='"),
|
OutputFmt::Shell => result.push_str("LS_COLORS='"),
|
||||||
OutputFmt::CShell => result.push_str("setenv LS_COLORS '"),
|
OutputFmt::CShell => result.push_str("setenv LS_COLORS '"),
|
||||||
|
OutputFmt::Display => (),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,13 +366,25 @@ where
|
||||||
}
|
}
|
||||||
if state != ParseState::Pass {
|
if state != ParseState::Pass {
|
||||||
if key.starts_with('.') {
|
if key.starts_with('.') {
|
||||||
|
if *fmt == OutputFmt::Display {
|
||||||
|
result.push_str(format!("\x1b[{1}m*{0}\t{1}\x1b[0m\n", key, val).as_str());
|
||||||
|
} else {
|
||||||
result.push_str(format!("*{}={}:", key, val).as_str());
|
result.push_str(format!("*{}={}:", key, val).as_str());
|
||||||
|
}
|
||||||
} else if key.starts_with('*') {
|
} else if key.starts_with('*') {
|
||||||
|
if *fmt == OutputFmt::Display {
|
||||||
|
result.push_str(format!("\x1b[{1}m{0}\t{1}\x1b[0m\n", key, val).as_str());
|
||||||
|
} else {
|
||||||
result.push_str(format!("{}={}:", key, val).as_str());
|
result.push_str(format!("{}={}:", key, val).as_str());
|
||||||
|
}
|
||||||
} else if lower == "options" || lower == "color" || lower == "eightbit" {
|
} else if lower == "options" || lower == "color" || lower == "eightbit" {
|
||||||
// Slackware only. Ignore
|
// Slackware only. Ignore
|
||||||
} else if let Some(s) = table.get(lower.as_str()) {
|
} else if let Some(s) = table.get(lower.as_str()) {
|
||||||
|
if *fmt == OutputFmt::Display {
|
||||||
|
result.push_str(format!("\x1b[{1}m{0}\t{1}\x1b[0m\n", s, val).as_str());
|
||||||
|
} else {
|
||||||
result.push_str(format!("{}={}:", s, val).as_str());
|
result.push_str(format!("{}={}:", s, val).as_str());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"{}:{}: unrecognized keyword {}",
|
"{}:{}: unrecognized keyword {}",
|
||||||
|
@ -367,6 +400,10 @@ where
|
||||||
match fmt {
|
match fmt {
|
||||||
OutputFmt::Shell => result.push_str("';\nexport LS_COLORS"),
|
OutputFmt::Shell => result.push_str("';\nexport LS_COLORS"),
|
||||||
OutputFmt::CShell => result.push('\''),
|
OutputFmt::CShell => result.push('\''),
|
||||||
|
OutputFmt::Display => {
|
||||||
|
// remove latest "\n"
|
||||||
|
result.pop();
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ fn tabstops_parse(s: &str) -> (RemainingMode, Vec<usize>) {
|
||||||
|
|
||||||
// Tab size must be positive.
|
// Tab size must be positive.
|
||||||
if num == 0 {
|
if num == 0 {
|
||||||
crash!(1, "{}\n", "tab size cannot be 0");
|
crash!(1, "tab size cannot be 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab sizes must be ascending.
|
// Tab sizes must be ascending.
|
||||||
|
@ -132,8 +132,8 @@ struct Options {
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
fn new(matches: &ArgMatches) -> Self {
|
fn new(matches: &ArgMatches) -> Self {
|
||||||
let (remaining_mode, tabstops) = match matches.value_of(options::TABS) {
|
let (remaining_mode, tabstops) = match matches.values_of(options::TABS) {
|
||||||
Some(s) => tabstops_parse(s),
|
Some(s) => tabstops_parse(&s.collect::<Vec<&str>>().join(",")),
|
||||||
None => (RemainingMode::None, vec![DEFAULT_TABSTOP]),
|
None => (RemainingMode::None, vec![DEFAULT_TABSTOP]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -195,6 +195,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.short('t')
|
.short('t')
|
||||||
.value_name("N, LIST")
|
.value_name("N, LIST")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.multiple_occurrences(true)
|
||||||
.help("have tabs N characters apart, not 8 or use comma separated list of explicit tab positions"),
|
.help("have tabs N characters apart, not 8 or use comma separated list of explicit tab positions"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
|
|
@ -36,6 +36,7 @@ pub struct Settings {
|
||||||
suffix: String,
|
suffix: String,
|
||||||
symbolic: bool,
|
symbolic: bool,
|
||||||
relative: bool,
|
relative: bool,
|
||||||
|
logical: bool,
|
||||||
target_dir: Option<String>,
|
target_dir: Option<String>,
|
||||||
no_target_dir: bool,
|
no_target_dir: bool,
|
||||||
no_dereference: bool,
|
no_dereference: bool,
|
||||||
|
@ -121,9 +122,12 @@ const USAGE: &str = "\
|
||||||
|
|
||||||
mod options {
|
mod options {
|
||||||
pub const FORCE: &str = "force";
|
pub const FORCE: &str = "force";
|
||||||
|
//pub const DIRECTORY: &str = "directory";
|
||||||
pub const INTERACTIVE: &str = "interactive";
|
pub const INTERACTIVE: &str = "interactive";
|
||||||
pub const NO_DEREFERENCE: &str = "no-dereference";
|
pub const NO_DEREFERENCE: &str = "no-dereference";
|
||||||
pub const SYMBOLIC: &str = "symbolic";
|
pub const SYMBOLIC: &str = "symbolic";
|
||||||
|
pub const LOGICAL: &str = "logical";
|
||||||
|
pub const PHYSICAL: &str = "physical";
|
||||||
pub const TARGET_DIRECTORY: &str = "target-directory";
|
pub const TARGET_DIRECTORY: &str = "target-directory";
|
||||||
pub const NO_TARGET_DIRECTORY: &str = "no-target-directory";
|
pub const NO_TARGET_DIRECTORY: &str = "no-target-directory";
|
||||||
pub const RELATIVE: &str = "relative";
|
pub const RELATIVE: &str = "relative";
|
||||||
|
@ -152,6 +156,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let symbolic = matches.is_present(options::SYMBOLIC);
|
||||||
|
|
||||||
let overwrite_mode = if matches.is_present(options::FORCE) {
|
let overwrite_mode = if matches.is_present(options::FORCE) {
|
||||||
OverwriteMode::Force
|
OverwriteMode::Force
|
||||||
} else if matches.is_present(options::INTERACTIVE) {
|
} else if matches.is_present(options::INTERACTIVE) {
|
||||||
|
@ -163,11 +169,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let backup_mode = backup_control::determine_backup_mode(&matches)?;
|
let backup_mode = backup_control::determine_backup_mode(&matches)?;
|
||||||
let backup_suffix = backup_control::determine_backup_suffix(&matches);
|
let backup_suffix = backup_control::determine_backup_suffix(&matches);
|
||||||
|
|
||||||
|
// When we have "-L" or "-L -P", false otherwise
|
||||||
|
let logical = matches.is_present(options::LOGICAL);
|
||||||
|
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
overwrite: overwrite_mode,
|
overwrite: overwrite_mode,
|
||||||
backup: backup_mode,
|
backup: backup_mode,
|
||||||
suffix: backup_suffix,
|
suffix: backup_suffix,
|
||||||
symbolic: matches.is_present(options::SYMBOLIC),
|
symbolic,
|
||||||
|
logical,
|
||||||
relative: matches.is_present(options::RELATIVE),
|
relative: matches.is_present(options::RELATIVE),
|
||||||
target_dir: matches
|
target_dir: matches
|
||||||
.value_of(options::TARGET_DIRECTORY)
|
.value_of(options::TARGET_DIRECTORY)
|
||||||
|
@ -188,9 +198,12 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.infer_long_args(true)
|
.infer_long_args(true)
|
||||||
.arg(backup_control::arguments::backup())
|
.arg(backup_control::arguments::backup())
|
||||||
.arg(backup_control::arguments::backup_no_args())
|
.arg(backup_control::arguments::backup_no_args())
|
||||||
// TODO: opts.arg(
|
/*.arg(
|
||||||
// Arg::new(("d", "directory", "allow users with appropriate privileges to attempt \
|
Arg::new(options::DIRECTORY)
|
||||||
// to make hard links to directories");
|
.short('d')
|
||||||
|
.long(options::DIRECTORY)
|
||||||
|
.help("allow users with appropriate privileges to attempt to make hard links to directories")
|
||||||
|
)*/
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::FORCE)
|
Arg::new(options::FORCE)
|
||||||
.short('f')
|
.short('f')
|
||||||
|
@ -212,15 +225,24 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
symbolic link to a directory",
|
symbolic link to a directory",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
// TODO: opts.arg(
|
.arg(
|
||||||
// Arg::new(("L", "logical", "dereference TARGETs that are symbolic links");
|
Arg::new(options::LOGICAL)
|
||||||
//
|
.short('L')
|
||||||
// TODO: opts.arg(
|
.long(options::LOGICAL)
|
||||||
// Arg::new(("P", "physical", "make hard links directly to symbolic links");
|
.help("dereference TARGETs that are symbolic links")
|
||||||
|
.overrides_with(options::PHYSICAL),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
// Not implemented yet
|
||||||
|
Arg::new(options::PHYSICAL)
|
||||||
|
.short('P')
|
||||||
|
.long(options::PHYSICAL)
|
||||||
|
.help("make hard links directly to symbolic links"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::SYMBOLIC)
|
Arg::new(options::SYMBOLIC)
|
||||||
.short('s')
|
.short('s')
|
||||||
.long("symbolic")
|
.long(options::SYMBOLIC)
|
||||||
.help("make symbolic links instead of hard links")
|
.help("make symbolic links instead of hard links")
|
||||||
// override added for https://github.com/uutils/coreutils/issues/2359
|
// override added for https://github.com/uutils/coreutils/issues/2359
|
||||||
.overrides_with(options::SYMBOLIC),
|
.overrides_with(options::SYMBOLIC),
|
||||||
|
@ -446,7 +468,15 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> {
|
||||||
if settings.symbolic {
|
if settings.symbolic {
|
||||||
symlink(&source, dst)?;
|
symlink(&source, dst)?;
|
||||||
} else {
|
} else {
|
||||||
fs::hard_link(&source, dst)?;
|
let p = if settings.logical && is_symlink(&source) {
|
||||||
|
// if we want to have an hard link,
|
||||||
|
// source is a symlink and -L is passed
|
||||||
|
// we want to resolve the symlink to create the hardlink
|
||||||
|
std::fs::canonicalize(&source)?
|
||||||
|
} else {
|
||||||
|
source.to_path_buf()
|
||||||
|
};
|
||||||
|
fs::hard_link(&p, dst)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.verbose {
|
if settings.verbose {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
// spell-checker:ignore (paths) GPGHome
|
// spell-checker:ignore (paths) GPGHome
|
||||||
|
|
||||||
use clap::{crate_version, Arg, Command};
|
use clap::{crate_version, Arg, ArgMatches, Command};
|
||||||
use uucore::display::{println_verbatim, Quotable};
|
use uucore::display::{println_verbatim, Quotable};
|
||||||
use uucore::error::{FromIo, UError, UResult};
|
use uucore::error::{FromIo, UError, UResult};
|
||||||
use uucore::format_usage;
|
use uucore::format_usage;
|
||||||
|
@ -17,7 +17,7 @@ use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::path::{is_separator, Path, PathBuf, MAIN_SEPARATOR};
|
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
@ -89,86 +89,229 @@ impl Display for MkTempError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
/// Options parsed from the command-line.
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
///
|
||||||
let matches = uu_app().get_matches_from(args);
|
/// This provides a layer of indirection between the application logic
|
||||||
|
/// and the argument parsing library `clap`, allowing each to vary
|
||||||
|
/// independently.
|
||||||
|
struct Options {
|
||||||
|
/// Whether to create a temporary directory instead of a file.
|
||||||
|
directory: bool,
|
||||||
|
|
||||||
let template = matches.value_of(ARG_TEMPLATE).unwrap();
|
/// Whether to just print the name of a file that would have been created.
|
||||||
let tmpdir = matches.value_of(OPT_TMPDIR).unwrap_or_default();
|
dry_run: bool,
|
||||||
|
|
||||||
// Treat the template string as a path to get the directory
|
/// Whether to suppress file creation error messages.
|
||||||
// containing the last component.
|
quiet: bool,
|
||||||
let path = PathBuf::from(template);
|
|
||||||
|
|
||||||
let (template, tmpdir) = if matches.is_present(OPT_TMPDIR)
|
/// The directory in which to create the temporary file.
|
||||||
&& !PathBuf::from(tmpdir).is_dir() // if a temp dir is provided, it must be an actual path
|
///
|
||||||
&& tmpdir.contains("XXX")
|
/// If `None`, the file will be created in the current directory.
|
||||||
// If this is a template, it has to contain at least 3 X
|
tmpdir: Option<String>,
|
||||||
&& template == DEFAULT_TEMPLATE
|
|
||||||
// That means that clap does not think we provided a template
|
/// The suffix to append to the temporary file, if any.
|
||||||
{
|
suffix: Option<String>,
|
||||||
// Special case to workaround a limitation of clap when doing
|
|
||||||
// mktemp --tmpdir apt-key-gpghome.XXX
|
/// Whether to treat the template argument as a single file path component.
|
||||||
// The behavior should be
|
treat_as_template: bool,
|
||||||
// mktemp --tmpdir $TMPDIR apt-key-gpghome.XX
|
|
||||||
// As --tmpdir is empty
|
/// The template to use for the name of the temporary file.
|
||||||
|
template: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decide whether the argument to `--tmpdir` should actually be the template.
|
||||||
|
///
|
||||||
|
/// This function is required to work around a limitation of `clap`,
|
||||||
|
/// the command-line argument parsing library. In case the command
|
||||||
|
/// line is
|
||||||
|
///
|
||||||
|
/// ```sh
|
||||||
|
/// mktemp --tmpdir XXX
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// the program should behave like
|
||||||
|
///
|
||||||
|
/// ```sh
|
||||||
|
/// mktemp --tmpdir=${TMPDIR:-/tmp} XXX
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// However, `clap` thinks that `XXX` is the value of the `--tmpdir`
|
||||||
|
/// option. This function returns `true` in this case and `false`
|
||||||
|
/// in all other cases.
|
||||||
|
fn is_tmpdir_argument_actually_the_template(matches: &ArgMatches) -> bool {
|
||||||
|
if !matches.is_present(ARG_TEMPLATE) {
|
||||||
|
if let Some(tmpdir) = matches.value_of(OPT_TMPDIR) {
|
||||||
|
if !Path::new(tmpdir).is_dir() && tmpdir.contains("XXX") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Options {
|
||||||
|
fn from(matches: &ArgMatches) -> Self {
|
||||||
|
// Special case to work around a limitation of `clap`; see
|
||||||
|
// `is_tmpdir_argument_actually_the_template()` for more
|
||||||
|
// information.
|
||||||
//
|
//
|
||||||
// Fixed in clap 3
|
// Fixed in clap 3
|
||||||
// See https://github.com/clap-rs/clap/pull/1587
|
// See https://github.com/clap-rs/clap/pull/1587
|
||||||
let tmp = env::temp_dir();
|
let (tmpdir, template) = if is_tmpdir_argument_actually_the_template(matches) {
|
||||||
(tmpdir, tmp)
|
let tmpdir = Some(env::temp_dir().display().to_string());
|
||||||
} else if !matches.is_present(OPT_TMPDIR) {
|
let template = matches.value_of(OPT_TMPDIR).unwrap().to_string();
|
||||||
// In this case, the command line was `mktemp -t XXX`, so we
|
(tmpdir, template)
|
||||||
// treat the argument `XXX` as though it were a filename
|
|
||||||
// regardless of whether it has path separators in it.
|
|
||||||
if matches.is_present(OPT_T) {
|
|
||||||
let tmp = env::temp_dir();
|
|
||||||
(template, tmp)
|
|
||||||
// In this case, the command line was `mktemp XXX`, so we need
|
|
||||||
// to parse out the parent directory and the filename from the
|
|
||||||
// argument `XXX`, since it may be include path separators.
|
|
||||||
} else {
|
} else {
|
||||||
let tmp = match path.parent() {
|
let tmpdir = matches.value_of(OPT_TMPDIR).map(String::from);
|
||||||
None => PathBuf::from("."),
|
let template = matches
|
||||||
Some(d) => PathBuf::from(d),
|
.value_of(ARG_TEMPLATE)
|
||||||
|
.unwrap_or(DEFAULT_TEMPLATE)
|
||||||
|
.to_string();
|
||||||
|
(tmpdir, template)
|
||||||
};
|
};
|
||||||
let filename = path.file_name();
|
Self {
|
||||||
let template = filename.unwrap().to_str().unwrap();
|
directory: matches.is_present(OPT_DIRECTORY),
|
||||||
// If the command line was `mktemp aXXX/b`, then we will
|
dry_run: matches.is_present(OPT_DRY_RUN),
|
||||||
// find that `tmp`, which is the result of getting the
|
quiet: matches.is_present(OPT_QUIET),
|
||||||
// parent when treating the argument as a path, contains
|
tmpdir,
|
||||||
// at least three consecutive Xs. This means that there
|
suffix: matches.value_of(OPT_SUFFIX).map(String::from),
|
||||||
// was a path separator in the suffix, which is not
|
treat_as_template: matches.is_present(OPT_T),
|
||||||
// allowed.
|
template,
|
||||||
if tmp.display().to_string().contains("XXX") {
|
|
||||||
return Err(MkTempError::SuffixContainsDirSeparator(format!(
|
|
||||||
"{}{}",
|
|
||||||
MAIN_SEPARATOR, template
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
(template, tmp)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters that control the path to and name of the temporary file.
|
||||||
|
///
|
||||||
|
/// The temporary file will be created at
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// {directory}/{prefix}{XXX}{suffix}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// where `{XXX}` is a sequence of random characters whose length is
|
||||||
|
/// `num_rand_chars`.
|
||||||
|
struct Params {
|
||||||
|
/// The directory that will contain the temporary file.
|
||||||
|
directory: String,
|
||||||
|
|
||||||
|
/// The (non-random) prefix of the temporary file.
|
||||||
|
prefix: String,
|
||||||
|
|
||||||
|
/// The number of random characters in the name of the temporary file.
|
||||||
|
num_rand_chars: usize,
|
||||||
|
|
||||||
|
/// The (non-random) suffix of the temporary file.
|
||||||
|
suffix: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Params {
|
||||||
|
fn from(options: Options) -> Result<Self, MkTempError> {
|
||||||
|
// Get the start and end indices of the randomized part of the template.
|
||||||
|
//
|
||||||
|
// For example, if the template is "abcXXXXyz", then `i` is 3 and `j` is 7.
|
||||||
|
let i = match options.template.find("XXX") {
|
||||||
|
None => {
|
||||||
|
let s = match options.suffix {
|
||||||
|
None => options.template,
|
||||||
|
Some(s) => format!("{}{}", options.template, s),
|
||||||
|
};
|
||||||
|
return Err(MkTempError::TooFewXs(s));
|
||||||
|
}
|
||||||
|
Some(i) => i,
|
||||||
|
};
|
||||||
|
let j = options.template.rfind("XXX").unwrap() + 3;
|
||||||
|
|
||||||
|
// Combine the directory given as an option and the prefix of the template.
|
||||||
|
//
|
||||||
|
// For example, if `tmpdir` is "a/b" and the template is "c/dXXX",
|
||||||
|
// then `prefix` is "a/b/c/d".
|
||||||
|
let tmpdir = options.tmpdir;
|
||||||
|
let prefix_from_option = tmpdir.clone().unwrap_or_else(|| "".to_string());
|
||||||
|
let prefix_from_template = &options.template[..i];
|
||||||
|
let prefix = Path::new(&prefix_from_option)
|
||||||
|
.join(prefix_from_template)
|
||||||
|
.display()
|
||||||
|
.to_string();
|
||||||
|
if options.treat_as_template && prefix.contains(MAIN_SEPARATOR) {
|
||||||
|
return Err(MkTempError::PrefixContainsDirSeparator(options.template));
|
||||||
|
}
|
||||||
|
if tmpdir.is_some() && Path::new(prefix_from_template).is_absolute() {
|
||||||
|
return Err(MkTempError::InvalidTemplate(options.template));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the parent directory from the file part of the prefix.
|
||||||
|
//
|
||||||
|
// For example, if `prefix` is "a/b/c/d", then `directory` is
|
||||||
|
// "a/b/c" is `prefix` gets reassigned to "d".
|
||||||
|
let (directory, prefix) = if prefix.ends_with(MAIN_SEPARATOR) {
|
||||||
|
(prefix, "".to_string())
|
||||||
} else {
|
} else {
|
||||||
(template, PathBuf::from(tmpdir))
|
let path = Path::new(&prefix);
|
||||||
|
let directory = match path.parent() {
|
||||||
|
None => String::new(),
|
||||||
|
Some(d) => d.display().to_string(),
|
||||||
|
};
|
||||||
|
let prefix = match path.file_name() {
|
||||||
|
None => String::new(),
|
||||||
|
Some(f) => f.to_str().unwrap().to_string(),
|
||||||
|
};
|
||||||
|
(directory, prefix)
|
||||||
};
|
};
|
||||||
|
|
||||||
let make_dir = matches.is_present(OPT_DIRECTORY);
|
// Combine the suffix from the template with the suffix given as an option.
|
||||||
let dry_run = matches.is_present(OPT_DRY_RUN);
|
//
|
||||||
let suppress_file_err = matches.is_present(OPT_QUIET);
|
// For example, if the suffix command-line argument is ".txt" and
|
||||||
|
// the template is "XXXabc", then `suffix` is "abc.txt".
|
||||||
// If `--tmpdir` is given, the template cannot be an absolute
|
let suffix_from_option = options.suffix.unwrap_or_else(|| "".to_string());
|
||||||
// path. For example, `mktemp --tmpdir=a /XXX` is not allowed.
|
let suffix_from_template = &options.template[j..];
|
||||||
if matches.is_present(OPT_TMPDIR) && PathBuf::from(template).is_absolute() {
|
let suffix = format!("{}{}", suffix_from_template, suffix_from_option);
|
||||||
return Err(MkTempError::InvalidTemplate(template.into()).into());
|
if suffix.contains(MAIN_SEPARATOR) {
|
||||||
|
return Err(MkTempError::SuffixContainsDirSeparator(suffix));
|
||||||
|
}
|
||||||
|
if !suffix_from_template.is_empty() && !suffix_from_option.is_empty() {
|
||||||
|
return Err(MkTempError::MustEndInX(options.template));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (prefix, rand, suffix) = parse_template(template, matches.value_of(OPT_SUFFIX))?;
|
// The number of random characters in the template.
|
||||||
|
//
|
||||||
|
// For example, if the template is "abcXXXXyz", then the number of
|
||||||
|
// random characters is four.
|
||||||
|
let num_rand_chars = j - i;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
directory,
|
||||||
|
prefix,
|
||||||
|
num_rand_chars,
|
||||||
|
suffix,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[uucore::main]
|
||||||
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
let matches = uu_app().try_get_matches_from(args)?;
|
||||||
|
|
||||||
|
// Parse command-line options into a format suitable for the
|
||||||
|
// application logic.
|
||||||
|
let options = Options::from(&matches);
|
||||||
|
let dry_run = options.dry_run;
|
||||||
|
let suppress_file_err = options.quiet;
|
||||||
|
let make_dir = options.directory;
|
||||||
|
|
||||||
|
// Parse file path parameters from the command-line options.
|
||||||
|
let Params {
|
||||||
|
directory: tmpdir,
|
||||||
|
prefix,
|
||||||
|
num_rand_chars: rand,
|
||||||
|
suffix,
|
||||||
|
} = Params::from(options)?;
|
||||||
|
|
||||||
|
// Create the temporary file or directory, or simulate creating it.
|
||||||
let res = if dry_run {
|
let res = if dry_run {
|
||||||
dry_exec(tmpdir, prefix, rand, suffix)
|
dry_exec(&tmpdir, &prefix, rand, &suffix)
|
||||||
} else {
|
} else {
|
||||||
exec(&tmpdir, prefix, rand, suffix, make_dir)
|
exec(&tmpdir, &prefix, rand, &suffix, make_dir)
|
||||||
};
|
};
|
||||||
|
|
||||||
if suppress_file_err {
|
if suppress_file_err {
|
||||||
|
@ -234,73 +377,10 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.multiple_occurrences(false)
|
.multiple_occurrences(false)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.max_values(1)
|
.max_values(1)
|
||||||
.default_value(DEFAULT_TEMPLATE),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a template string into prefix, suffix, and random components.
|
pub fn dry_exec(tmpdir: &str, prefix: &str, rand: usize, suffix: &str) -> UResult<()> {
|
||||||
///
|
|
||||||
/// `temp` is the template string, with three or more consecutive `X`s
|
|
||||||
/// representing a placeholder for randomly generated characters (for
|
|
||||||
/// example, `"abc_XXX.txt"`). If `temp` ends in an `X`, then a suffix
|
|
||||||
/// can be specified by `suffix` instead.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// * If there are fewer than three consecutive `X`s in `temp`.
|
|
||||||
/// * If `suffix` is a [`Some`] object but `temp` does not end in `X`.
|
|
||||||
/// * If the suffix (specified either way) contains a path separator.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// assert_eq!(parse_template("XXX", None).unwrap(), ("", 3, ""));
|
|
||||||
/// assert_eq!(parse_template("abcXXX", None).unwrap(), ("abc", 3, ""));
|
|
||||||
/// assert_eq!(parse_template("XXXdef", None).unwrap(), ("", 3, "def"));
|
|
||||||
/// assert_eq!(parse_template("abcXXXdef", None).unwrap(), ("abc", 3, "def"));
|
|
||||||
/// ```
|
|
||||||
fn parse_template<'a>(
|
|
||||||
temp: &'a str,
|
|
||||||
suffix: Option<&'a str>,
|
|
||||||
) -> Result<(&'a str, usize, &'a str), MkTempError> {
|
|
||||||
let right = match temp.rfind('X') {
|
|
||||||
Some(r) => r + 1,
|
|
||||||
None => return Err(MkTempError::TooFewXs(temp.into())),
|
|
||||||
};
|
|
||||||
let left = temp[..right].rfind(|c| c != 'X').map_or(0, |i| i + 1);
|
|
||||||
let prefix = &temp[..left];
|
|
||||||
let rand = right - left;
|
|
||||||
|
|
||||||
if rand < 3 {
|
|
||||||
let s = match suffix {
|
|
||||||
None => temp.into(),
|
|
||||||
Some(s) => format!("{}{}", temp, s),
|
|
||||||
};
|
|
||||||
return Err(MkTempError::TooFewXs(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut suf = &temp[right..];
|
|
||||||
|
|
||||||
if let Some(s) = suffix {
|
|
||||||
if suf.is_empty() {
|
|
||||||
suf = s;
|
|
||||||
} else {
|
|
||||||
return Err(MkTempError::MustEndInX(temp.into()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if prefix.chars().any(is_separator) {
|
|
||||||
return Err(MkTempError::PrefixContainsDirSeparator(temp.into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if suf.chars().any(is_separator) {
|
|
||||||
return Err(MkTempError::SuffixContainsDirSeparator(suf.into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((prefix, rand, suf))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> UResult<()> {
|
|
||||||
let len = prefix.len() + suffix.len() + rand;
|
let len = prefix.len() + suffix.len() + rand;
|
||||||
let mut buf = Vec::with_capacity(len);
|
let mut buf = Vec::with_capacity(len);
|
||||||
buf.extend(prefix.as_bytes());
|
buf.extend(prefix.as_bytes());
|
||||||
|
@ -320,11 +400,11 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) ->
|
||||||
}
|
}
|
||||||
// We guarantee utf8.
|
// We guarantee utf8.
|
||||||
let buf = String::from_utf8(buf).unwrap();
|
let buf = String::from_utf8(buf).unwrap();
|
||||||
tmpdir.push(buf);
|
let tmpdir = Path::new(tmpdir).join(buf);
|
||||||
println_verbatim(tmpdir).map_err_context(|| "failed to print directory name".to_owned())
|
println_verbatim(tmpdir).map_err_context(|| "failed to print directory name".to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(dir: &Path, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> {
|
fn exec(dir: &str, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> {
|
||||||
let context = || {
|
let context = || {
|
||||||
format!(
|
format!(
|
||||||
"failed to create file via template '{}{}{}'",
|
"failed to create file via template '{}{}{}'",
|
||||||
|
@ -366,42 +446,7 @@ fn exec(dir: &Path, prefix: &str, rand: usize, suffix: &str, make_dir: bool) ->
|
||||||
// the absolute path and we need to return a filename that matches
|
// the absolute path and we need to return a filename that matches
|
||||||
// the template given on the command-line which might be a
|
// the template given on the command-line which might be a
|
||||||
// relative path.
|
// relative path.
|
||||||
let mut path = dir.to_path_buf();
|
let path = Path::new(dir).join(filename);
|
||||||
path.push(filename);
|
|
||||||
|
|
||||||
println_verbatim(path).map_err_context(|| "failed to print directory name".to_owned())
|
println_verbatim(path).map_err_context(|| "failed to print directory name".to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::parse_template;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_template_no_suffix() {
|
|
||||||
assert_eq!(parse_template("XXX", None).unwrap(), ("", 3, ""));
|
|
||||||
assert_eq!(parse_template("abcXXX", None).unwrap(), ("abc", 3, ""));
|
|
||||||
assert_eq!(parse_template("XXXdef", None).unwrap(), ("", 3, "def"));
|
|
||||||
assert_eq!(
|
|
||||||
parse_template("abcXXXdef", None).unwrap(),
|
|
||||||
("abc", 3, "def")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_template_suffix() {
|
|
||||||
assert_eq!(parse_template("XXX", Some("def")).unwrap(), ("", 3, "def"));
|
|
||||||
assert_eq!(
|
|
||||||
parse_template("abcXXX", Some("def")).unwrap(),
|
|
||||||
("abc", 3, "def")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_template_errors() {
|
|
||||||
assert!(parse_template("a/bXXX", None).is_err());
|
|
||||||
assert!(parse_template("XXXa/b", None).is_err());
|
|
||||||
assert!(parse_template("XX", None).is_err());
|
|
||||||
assert!(parse_template("XXXabc", Some("def")).is_err());
|
|
||||||
assert!(parse_template("XXX", Some("a/b")).is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// * For the full copyright and license information, please view the LICENSE
|
// * For the full copyright and license information, please view the LICENSE
|
||||||
// * file that was distributed with this source code.
|
// * file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) PREFIXaa nbbbb ncccc
|
// spell-checker:ignore (ToDO) PREFIXaa PREFIXab nbbbb ncccc
|
||||||
|
|
||||||
mod filenames;
|
mod filenames;
|
||||||
mod number;
|
mod number;
|
||||||
|
@ -47,7 +47,7 @@ static ARG_PREFIX: &str = "prefix";
|
||||||
|
|
||||||
const USAGE: &str = "{} [OPTION]... [INPUT [PREFIX]]";
|
const USAGE: &str = "{} [OPTION]... [INPUT [PREFIX]]";
|
||||||
const AFTER_HELP: &str = "\
|
const AFTER_HELP: &str = "\
|
||||||
Output fixed-size pieces of INPUT to PREFIXaa, PREFIX ab, ...; default \
|
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default \
|
||||||
size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is \
|
size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is \
|
||||||
-, read standard input.";
|
-, read standard input.";
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.short('b')
|
.short('b')
|
||||||
.long(OPT_BYTES)
|
.long(OPT_BYTES)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.value_name("SIZE")
|
||||||
.help("put SIZE bytes per output file"),
|
.help("put SIZE bytes per output file"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -81,6 +82,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.short('C')
|
.short('C')
|
||||||
.long(OPT_LINE_BYTES)
|
.long(OPT_LINE_BYTES)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.value_name("SIZE")
|
||||||
.default_value("2")
|
.default_value("2")
|
||||||
.help("put at most SIZE bytes of lines per output file"),
|
.help("put at most SIZE bytes of lines per output file"),
|
||||||
)
|
)
|
||||||
|
@ -89,6 +91,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.short('l')
|
.short('l')
|
||||||
.long(OPT_LINES)
|
.long(OPT_LINES)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.value_name("NUMBER")
|
||||||
.default_value("1000")
|
.default_value("1000")
|
||||||
.help("put NUMBER lines/records per output file"),
|
.help("put NUMBER lines/records per output file"),
|
||||||
)
|
)
|
||||||
|
@ -97,6 +100,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.short('n')
|
.short('n')
|
||||||
.long(OPT_NUMBER)
|
.long(OPT_NUMBER)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.value_name("CHUNKS")
|
||||||
.help("generate CHUNKS output files; see explanation below"),
|
.help("generate CHUNKS output files; see explanation below"),
|
||||||
)
|
)
|
||||||
// rest of the arguments
|
// rest of the arguments
|
||||||
|
@ -104,8 +108,9 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
Arg::new(OPT_ADDITIONAL_SUFFIX)
|
Arg::new(OPT_ADDITIONAL_SUFFIX)
|
||||||
.long(OPT_ADDITIONAL_SUFFIX)
|
.long(OPT_ADDITIONAL_SUFFIX)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.value_name("SUFFIX")
|
||||||
.default_value("")
|
.default_value("")
|
||||||
.help("additional suffix to append to output file names"),
|
.help("additional SUFFIX to append to output file names"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(OPT_FILTER)
|
Arg::new(OPT_FILTER)
|
||||||
|
@ -137,6 +142,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.short('a')
|
.short('a')
|
||||||
.long(OPT_SUFFIX_LENGTH)
|
.long(OPT_SUFFIX_LENGTH)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.value_name("N")
|
||||||
.default_value(OPT_DEFAULT_SUFFIX_LENGTH)
|
.default_value(OPT_DEFAULT_SUFFIX_LENGTH)
|
||||||
.help("use suffixes of length N (default 2)"),
|
.help("use suffixes of length N (default 2)"),
|
||||||
)
|
)
|
||||||
|
|
|
@ -360,6 +360,47 @@ fn test_total() {
|
||||||
assert_eq!(computed_total_avail, reported_total_avail);
|
assert_eq!(computed_total_avail, reported_total_avail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test that the "total" label appears in the correct column.
|
||||||
|
///
|
||||||
|
/// The "total" label should appear in the "source" column, or in the
|
||||||
|
/// "target" column if "source" is not visible.
|
||||||
|
#[test]
|
||||||
|
fn test_total_label_in_correct_column() {
|
||||||
|
let output = new_ucmd!()
|
||||||
|
.args(&["--output=source", "--total", "."])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_move_str();
|
||||||
|
let last_line = output.lines().last().unwrap();
|
||||||
|
assert_eq!(last_line.trim(), "total");
|
||||||
|
|
||||||
|
let output = new_ucmd!()
|
||||||
|
.args(&["--output=target", "--total", "."])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_move_str();
|
||||||
|
let last_line = output.lines().last().unwrap();
|
||||||
|
assert_eq!(last_line.trim(), "total");
|
||||||
|
|
||||||
|
let output = new_ucmd!()
|
||||||
|
.args(&["--output=source,target", "--total", "."])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_move_str();
|
||||||
|
let last_line = output.lines().last().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
last_line.split_whitespace().collect::<Vec<&str>>(),
|
||||||
|
vec!["total", "-"]
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = new_ucmd!()
|
||||||
|
.args(&["--output=target,source", "--total", "."])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_move_str();
|
||||||
|
let last_line = output.lines().last().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
last_line.split_whitespace().collect::<Vec<&str>>(),
|
||||||
|
vec!["-", "total"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_use_percentage() {
|
fn test_use_percentage() {
|
||||||
let output = new_ucmd!()
|
let output = new_ucmd!()
|
||||||
|
@ -421,7 +462,7 @@ fn test_default_block_size() {
|
||||||
.arg("--output=size")
|
.arg("--output=size")
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
let header = output.lines().next().unwrap().to_string();
|
let header = output.lines().next().unwrap().trim().to_string();
|
||||||
|
|
||||||
assert_eq!(header, "1K-blocks");
|
assert_eq!(header, "1K-blocks");
|
||||||
|
|
||||||
|
@ -430,7 +471,7 @@ fn test_default_block_size() {
|
||||||
.env("POSIXLY_CORRECT", "1")
|
.env("POSIXLY_CORRECT", "1")
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
let header = output.lines().next().unwrap().to_string();
|
let header = output.lines().next().unwrap().trim().to_string();
|
||||||
|
|
||||||
assert_eq!(header, "512B-blocks");
|
assert_eq!(header, "512B-blocks");
|
||||||
}
|
}
|
||||||
|
@ -445,6 +486,7 @@ fn test_default_block_size_in_posix_portability_mode() {
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.trim()
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +508,7 @@ fn test_block_size_1024() {
|
||||||
.args(&["-B", &format!("{}", block_size), "--output=size"])
|
.args(&["-B", &format!("{}", block_size), "--output=size"])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
output.lines().next().unwrap().to_string()
|
output.lines().next().unwrap().trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(get_header(1024), "1K-blocks");
|
assert_eq!(get_header(1024), "1K-blocks");
|
||||||
|
@ -490,7 +532,7 @@ fn test_block_size_with_suffix() {
|
||||||
.args(&["-B", block_size, "--output=size"])
|
.args(&["-B", block_size, "--output=size"])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
output.lines().next().unwrap().to_string()
|
output.lines().next().unwrap().trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(get_header("K"), "1K-blocks");
|
assert_eq!(get_header("K"), "1K-blocks");
|
||||||
|
@ -522,6 +564,7 @@ fn test_block_size_in_posix_portability_mode() {
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.trim()
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,7 +583,7 @@ fn test_block_size_from_env() {
|
||||||
.env(env_var, env_value)
|
.env(env_var, env_value)
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
output.lines().next().unwrap().to_string()
|
output.lines().next().unwrap().trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(get_header("DF_BLOCK_SIZE", "111"), "111B-blocks");
|
assert_eq!(get_header("DF_BLOCK_SIZE", "111"), "111B-blocks");
|
||||||
|
@ -559,7 +602,7 @@ fn test_block_size_from_env_precedences() {
|
||||||
.env(k2, v2)
|
.env(k2, v2)
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
output.lines().next().unwrap().to_string()
|
output.lines().next().unwrap().trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
let df_block_size = ("DF_BLOCK_SIZE", "111");
|
let df_block_size = ("DF_BLOCK_SIZE", "111");
|
||||||
|
@ -578,7 +621,7 @@ fn test_precedence_of_block_size_arg_over_env() {
|
||||||
.env("DF_BLOCK_SIZE", "111")
|
.env("DF_BLOCK_SIZE", "111")
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
let header = output.lines().next().unwrap().to_string();
|
let header = output.lines().next().unwrap().trim().to_string();
|
||||||
|
|
||||||
assert_eq!(header, "999B-blocks");
|
assert_eq!(header, "999B-blocks");
|
||||||
}
|
}
|
||||||
|
@ -592,7 +635,7 @@ fn test_invalid_block_size_from_env() {
|
||||||
.env("DF_BLOCK_SIZE", "invalid")
|
.env("DF_BLOCK_SIZE", "invalid")
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
let header = output.lines().next().unwrap().to_string();
|
let header = output.lines().next().unwrap().trim().to_string();
|
||||||
|
|
||||||
assert_eq!(header, default_block_size_header);
|
assert_eq!(header, default_block_size_header);
|
||||||
|
|
||||||
|
@ -602,7 +645,7 @@ fn test_invalid_block_size_from_env() {
|
||||||
.env("BLOCK_SIZE", "222")
|
.env("BLOCK_SIZE", "222")
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_move_str();
|
.stdout_move_str();
|
||||||
let header = output.lines().next().unwrap().to_string();
|
let header = output.lines().next().unwrap().trim().to_string();
|
||||||
|
|
||||||
assert_eq!(header, default_block_size_header);
|
assert_eq!(header, default_block_size_header);
|
||||||
}
|
}
|
||||||
|
@ -626,6 +669,7 @@ fn test_ignore_block_size_from_env_in_posix_portability_mode() {
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.trim()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
assert_eq!(header, default_block_size_header);
|
assert_eq!(header, default_block_size_header);
|
||||||
|
|
|
@ -62,6 +62,14 @@ fn test_internal_db() {
|
||||||
.stdout_is_fixture("internal.expected");
|
.stdout_is_fixture("internal.expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ls_colors() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("--print-ls-colors")
|
||||||
|
.run()
|
||||||
|
.stdout_is_fixture("ls_colors.expected");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bash_default() {
|
fn test_bash_default() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
@ -109,6 +117,18 @@ fn test_exclusive_option() {
|
||||||
.arg("-cp")
|
.arg("-cp")
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_contains("mutually exclusive");
|
.stderr_contains("mutually exclusive");
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-b", "--print-ls-colors"])
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("mutually exclusive");
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-c", "--print-ls-colors"])
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("mutually exclusive");
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-p", "--print-ls-colors"])
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("mutually exclusive");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_helper(file_name: &str, term: &str) {
|
fn test_helper(file_name: &str, term: &str) {
|
||||||
|
|
|
@ -73,6 +73,15 @@ fn test_tabs_mixed_style_list() {
|
||||||
.stdout_is("a b c d e");
|
.stdout_is("a b c d e");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiple_tabs_args() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--tabs=3", "--tabs=6", "--tabs=9"])
|
||||||
|
.pipe_in("a\tb\tc\td\te")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("a b c d e");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tabs_empty_string() {
|
fn test_tabs_empty_string() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
|
|
@ -650,3 +650,55 @@ fn test_backup_force() {
|
||||||
// we should have the same content as b as we had time to do a backup
|
// we should have the same content as b as we had time to do a backup
|
||||||
assert_eq!(at.read("b~"), "b2\n");
|
assert_eq!(at.read("b~"), "b2\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hard_logical() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let file_a = "file1";
|
||||||
|
let link = "symlink1";
|
||||||
|
let target = "hard-to-a";
|
||||||
|
let target2 = "hard-to-a2";
|
||||||
|
at.touch(file_a);
|
||||||
|
at.symlink_file(file_a, link);
|
||||||
|
|
||||||
|
ucmd.args(&["-P", "-L", link, target]);
|
||||||
|
assert!(!at.is_symlink(target));
|
||||||
|
|
||||||
|
ucmd.args(&["-P", "-L", "-s", link, target2]);
|
||||||
|
assert!(!at.is_symlink(target2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hard_logical_non_exit_fail() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
|
let file_a = "/no-such-dir";
|
||||||
|
let link = "hard-to-dangle";
|
||||||
|
|
||||||
|
scene.ucmd().args(&["-s", file_a]);
|
||||||
|
assert!(!at.is_symlink("no-such-dir"));
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-L", "no-such-dir", link])
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("failed to link 'no-such-dir'");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hard_logical_dir_fail() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
let dir = "d";
|
||||||
|
at.mkdir(dir);
|
||||||
|
let target = "link-to-dir";
|
||||||
|
|
||||||
|
scene.ucmd().args(&["-s", dir, target]);
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.args(&["-L", target, "hard-to-dir-link"])
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("failed to link 'link-to-dir'");
|
||||||
|
}
|
||||||
|
|
|
@ -519,6 +519,7 @@ fn test_directory_permissions() {
|
||||||
/// Test that a template with a path separator is invalid.
|
/// Test that a template with a path separator is invalid.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_template_path_separator() {
|
fn test_template_path_separator() {
|
||||||
|
#[cfg(not(windows))]
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["-t", "a/bXXX"])
|
.args(&["-t", "a/bXXX"])
|
||||||
.fails()
|
.fails()
|
||||||
|
@ -526,6 +527,14 @@ fn test_template_path_separator() {
|
||||||
"mktemp: invalid template, {}, contains directory separator\n",
|
"mktemp: invalid template, {}, contains directory separator\n",
|
||||||
"a/bXXX".quote()
|
"a/bXXX".quote()
|
||||||
));
|
));
|
||||||
|
#[cfg(windows)]
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-t", r"a\bXXX"])
|
||||||
|
.fails()
|
||||||
|
.stderr_only(format!(
|
||||||
|
"mktemp: invalid template, {}, contains directory separator\n",
|
||||||
|
r"a\bXXX".quote()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test that a suffix with a path separator is invalid.
|
/// Test that a suffix with a path separator is invalid.
|
||||||
|
@ -558,3 +567,8 @@ fn test_too_few_xs_suffix_directory() {
|
||||||
.fails()
|
.fails()
|
||||||
.stderr_only("mktemp: too few X's in template 'aXXX'\n");
|
.stderr_only("mktemp: too few X's in template 'aXXX'\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_too_many_arguments() {
|
||||||
|
new_ucmd!().args(&["-q", "a", "b"]).fails().code_is(1);
|
||||||
|
}
|
||||||
|
|
148
tests/fixtures/dircolors/ls_colors.expected
vendored
Normal file
148
tests/fixtures/dircolors/ls_colors.expected
vendored
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
[0mrs 0[0m
|
||||||
|
[01;34mdi 01;34[0m
|
||||||
|
[01;36mln 01;36[0m
|
||||||
|
[00mmh 00[0m
|
||||||
|
[40;33mpi 40;33[0m
|
||||||
|
[01;35mso 01;35[0m
|
||||||
|
[01;35mdo 01;35[0m
|
||||||
|
[40;33;01mbd 40;33;01[0m
|
||||||
|
[40;33;01mcd 40;33;01[0m
|
||||||
|
[40;31;01mor 40;31;01[0m
|
||||||
|
[00mmi 00[0m
|
||||||
|
[37;41msu 37;41[0m
|
||||||
|
[30;43msg 30;43[0m
|
||||||
|
[00mca 00[0m
|
||||||
|
[30;42mtw 30;42[0m
|
||||||
|
[34;42mow 34;42[0m
|
||||||
|
[37;44mst 37;44[0m
|
||||||
|
[01;32mex 01;32[0m
|
||||||
|
[01;31m*.tar 01;31[0m
|
||||||
|
[01;31m*.tgz 01;31[0m
|
||||||
|
[01;31m*.arc 01;31[0m
|
||||||
|
[01;31m*.arj 01;31[0m
|
||||||
|
[01;31m*.taz 01;31[0m
|
||||||
|
[01;31m*.lha 01;31[0m
|
||||||
|
[01;31m*.lz4 01;31[0m
|
||||||
|
[01;31m*.lzh 01;31[0m
|
||||||
|
[01;31m*.lzma 01;31[0m
|
||||||
|
[01;31m*.tlz 01;31[0m
|
||||||
|
[01;31m*.txz 01;31[0m
|
||||||
|
[01;31m*.tzo 01;31[0m
|
||||||
|
[01;31m*.t7z 01;31[0m
|
||||||
|
[01;31m*.zip 01;31[0m
|
||||||
|
[01;31m*.z 01;31[0m
|
||||||
|
[01;31m*.dz 01;31[0m
|
||||||
|
[01;31m*.gz 01;31[0m
|
||||||
|
[01;31m*.lrz 01;31[0m
|
||||||
|
[01;31m*.lz 01;31[0m
|
||||||
|
[01;31m*.lzo 01;31[0m
|
||||||
|
[01;31m*.xz 01;31[0m
|
||||||
|
[01;31m*.zst 01;31[0m
|
||||||
|
[01;31m*.tzst 01;31[0m
|
||||||
|
[01;31m*.bz2 01;31[0m
|
||||||
|
[01;31m*.bz 01;31[0m
|
||||||
|
[01;31m*.tbz 01;31[0m
|
||||||
|
[01;31m*.tbz2 01;31[0m
|
||||||
|
[01;31m*.tz 01;31[0m
|
||||||
|
[01;31m*.deb 01;31[0m
|
||||||
|
[01;31m*.rpm 01;31[0m
|
||||||
|
[01;31m*.jar 01;31[0m
|
||||||
|
[01;31m*.war 01;31[0m
|
||||||
|
[01;31m*.ear 01;31[0m
|
||||||
|
[01;31m*.sar 01;31[0m
|
||||||
|
[01;31m*.rar 01;31[0m
|
||||||
|
[01;31m*.alz 01;31[0m
|
||||||
|
[01;31m*.ace 01;31[0m
|
||||||
|
[01;31m*.zoo 01;31[0m
|
||||||
|
[01;31m*.cpio 01;31[0m
|
||||||
|
[01;31m*.7z 01;31[0m
|
||||||
|
[01;31m*.rz 01;31[0m
|
||||||
|
[01;31m*.cab 01;31[0m
|
||||||
|
[01;31m*.wim 01;31[0m
|
||||||
|
[01;31m*.swm 01;31[0m
|
||||||
|
[01;31m*.dwm 01;31[0m
|
||||||
|
[01;31m*.esd 01;31[0m
|
||||||
|
[01;35m*.avif 01;35[0m
|
||||||
|
[01;35m*.jpg 01;35[0m
|
||||||
|
[01;35m*.jpeg 01;35[0m
|
||||||
|
[01;35m*.mjpg 01;35[0m
|
||||||
|
[01;35m*.mjpeg 01;35[0m
|
||||||
|
[01;35m*.gif 01;35[0m
|
||||||
|
[01;35m*.bmp 01;35[0m
|
||||||
|
[01;35m*.pbm 01;35[0m
|
||||||
|
[01;35m*.pgm 01;35[0m
|
||||||
|
[01;35m*.ppm 01;35[0m
|
||||||
|
[01;35m*.tga 01;35[0m
|
||||||
|
[01;35m*.xbm 01;35[0m
|
||||||
|
[01;35m*.xpm 01;35[0m
|
||||||
|
[01;35m*.tif 01;35[0m
|
||||||
|
[01;35m*.tiff 01;35[0m
|
||||||
|
[01;35m*.png 01;35[0m
|
||||||
|
[01;35m*.svg 01;35[0m
|
||||||
|
[01;35m*.svgz 01;35[0m
|
||||||
|
[01;35m*.mng 01;35[0m
|
||||||
|
[01;35m*.pcx 01;35[0m
|
||||||
|
[01;35m*.mov 01;35[0m
|
||||||
|
[01;35m*.mpg 01;35[0m
|
||||||
|
[01;35m*.mpeg 01;35[0m
|
||||||
|
[01;35m*.m2v 01;35[0m
|
||||||
|
[01;35m*.mkv 01;35[0m
|
||||||
|
[01;35m*.webm 01;35[0m
|
||||||
|
[01;35m*.webp 01;35[0m
|
||||||
|
[01;35m*.ogm 01;35[0m
|
||||||
|
[01;35m*.mp4 01;35[0m
|
||||||
|
[01;35m*.m4v 01;35[0m
|
||||||
|
[01;35m*.mp4v 01;35[0m
|
||||||
|
[01;35m*.vob 01;35[0m
|
||||||
|
[01;35m*.qt 01;35[0m
|
||||||
|
[01;35m*.nuv 01;35[0m
|
||||||
|
[01;35m*.wmv 01;35[0m
|
||||||
|
[01;35m*.asf 01;35[0m
|
||||||
|
[01;35m*.rm 01;35[0m
|
||||||
|
[01;35m*.rmvb 01;35[0m
|
||||||
|
[01;35m*.flc 01;35[0m
|
||||||
|
[01;35m*.avi 01;35[0m
|
||||||
|
[01;35m*.fli 01;35[0m
|
||||||
|
[01;35m*.flv 01;35[0m
|
||||||
|
[01;35m*.gl 01;35[0m
|
||||||
|
[01;35m*.dl 01;35[0m
|
||||||
|
[01;35m*.xcf 01;35[0m
|
||||||
|
[01;35m*.xwd 01;35[0m
|
||||||
|
[01;35m*.yuv 01;35[0m
|
||||||
|
[01;35m*.cgm 01;35[0m
|
||||||
|
[01;35m*.emf 01;35[0m
|
||||||
|
[01;35m*.ogv 01;35[0m
|
||||||
|
[01;35m*.ogx 01;35[0m
|
||||||
|
[00;36m*.aac 00;36[0m
|
||||||
|
[00;36m*.au 00;36[0m
|
||||||
|
[00;36m*.flac 00;36[0m
|
||||||
|
[00;36m*.m4a 00;36[0m
|
||||||
|
[00;36m*.mid 00;36[0m
|
||||||
|
[00;36m*.midi 00;36[0m
|
||||||
|
[00;36m*.mka 00;36[0m
|
||||||
|
[00;36m*.mp3 00;36[0m
|
||||||
|
[00;36m*.mpc 00;36[0m
|
||||||
|
[00;36m*.ogg 00;36[0m
|
||||||
|
[00;36m*.ra 00;36[0m
|
||||||
|
[00;36m*.wav 00;36[0m
|
||||||
|
[00;36m*.oga 00;36[0m
|
||||||
|
[00;36m*.opus 00;36[0m
|
||||||
|
[00;36m*.spx 00;36[0m
|
||||||
|
[00;36m*.xspf 00;36[0m
|
||||||
|
[00;90m*~ 00;90[0m
|
||||||
|
[00;90m*# 00;90[0m
|
||||||
|
[00;90m*.bak 00;90[0m
|
||||||
|
[00;90m*.old 00;90[0m
|
||||||
|
[00;90m*.orig 00;90[0m
|
||||||
|
[00;90m*.part 00;90[0m
|
||||||
|
[00;90m*.rej 00;90[0m
|
||||||
|
[00;90m*.swp 00;90[0m
|
||||||
|
[00;90m*.tmp 00;90[0m
|
||||||
|
[00;90m*.dpkg-dist 00;90[0m
|
||||||
|
[00;90m*.dpkg-old 00;90[0m
|
||||||
|
[00;90m*.ucf-dist 00;90[0m
|
||||||
|
[00;90m*.ucf-new 00;90[0m
|
||||||
|
[00;90m*.ucf-old 00;90[0m
|
||||||
|
[00;90m*.rpmnew 00;90[0m
|
||||||
|
[00;90m*.rpmorig 00;90[0m
|
||||||
|
[00;90m*.rpmsave 00;90[0m
|
|
@ -199,3 +199,4 @@ sed -i -e "s/provoked error./provoked error\ncat pat |sort -u > pat/" tests/misc
|
||||||
|
|
||||||
# Update the GNU error message to match ours
|
# Update the GNU error message to match ours
|
||||||
sed -i -e "s/ln: 'f' and 'f' are the same file/ln: failed to link 'f' to 'f': Same file/g" tests/ln/hard-backup.sh
|
sed -i -e "s/ln: 'f' and 'f' are the same file/ln: failed to link 'f' to 'f': Same file/g" tests/ln/hard-backup.sh
|
||||||
|
sed -i -e "s/failed to access 'no-such-dir'\":/failed to link 'no-such-dir'\"/" -e "s/link-to-dir: hard link not allowed for directory/failed to link 'link-to-dir' to/" -e "s|link-to-dir/: hard link not allowed for directory|failed to link 'link-to-dir/' to|" tests/ln/hard-to-sym.sh
|
Loading…
Add table
Add a link
Reference in a new issue