mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
Merge branch 'main' into 6175-env-empty-string
This commit is contained in:
commit
3848e6c271
10 changed files with 870 additions and 665 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
msrv = "1.85.0"
|
||||||
avoid-breaking-exported-api = false
|
avoid-breaking-exported-api = false
|
||||||
check-private-items = true
|
check-private-items = true
|
||||||
cognitive-complexity-threshold = 24
|
cognitive-complexity-threshold = 24
|
||||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3031,6 +3031,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"nix",
|
"nix",
|
||||||
|
"tempfile",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width 0.2.0",
|
"unicode-width 0.2.0",
|
||||||
"uucore",
|
"uucore",
|
||||||
|
|
|
@ -342,7 +342,6 @@ thiserror = "2.0.3"
|
||||||
time = { version = "0.3.36" }
|
time = { version = "0.3.36" }
|
||||||
unicode-segmentation = "1.11.0"
|
unicode-segmentation = "1.11.0"
|
||||||
unicode-width = "0.2.0"
|
unicode-width = "0.2.0"
|
||||||
utf-8 = "0.7.6"
|
|
||||||
utmp-classic = "0.1.6"
|
utmp-classic = "0.1.6"
|
||||||
uutils_term_grid = "0.7"
|
uutils_term_grid = "0.7"
|
||||||
walkdir = "2.5"
|
walkdir = "2.5"
|
||||||
|
|
|
@ -1259,13 +1259,17 @@ fn parse_path_args(
|
||||||
return Err("missing file operand".into());
|
return Err("missing file operand".into());
|
||||||
} else if paths.len() == 1 && options.target_dir.is_none() {
|
} else if paths.len() == 1 && options.target_dir.is_none() {
|
||||||
// Only one file specified
|
// Only one file specified
|
||||||
return Err(format!("missing destination file operand after {:?}", paths[0]).into());
|
return Err(format!(
|
||||||
|
"missing destination file operand after {}",
|
||||||
|
paths[0].display().to_string().quote()
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if the user requested to copy more than one
|
// Return an error if the user requested to copy more than one
|
||||||
// file source to a file target
|
// file source to a file target
|
||||||
if options.no_target_dir && options.target_dir.is_none() && paths.len() > 2 {
|
if options.no_target_dir && options.target_dir.is_none() && paths.len() > 2 {
|
||||||
return Err(format!("extra operand {:?}", paths[2]).into());
|
return Err(format!("extra operand {:}", paths[2].display().to_string().quote()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = match options.target_dir {
|
let target = match options.target_dir {
|
||||||
|
|
|
@ -84,7 +84,12 @@ pub(crate) fn copy_on_write(
|
||||||
// support COW).
|
// support COW).
|
||||||
match reflink_mode {
|
match reflink_mode {
|
||||||
ReflinkMode::Always => {
|
ReflinkMode::Always => {
|
||||||
return Err(format!("failed to clone {source:?} from {dest:?}: {error}").into());
|
return Err(format!(
|
||||||
|
"failed to clone {} from {}: {error}",
|
||||||
|
source.display(),
|
||||||
|
dest.display()
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
copy_debug.reflink = OffloadReflinkDebug::Yes;
|
copy_debug.reflink = OffloadReflinkDebug::Yes;
|
||||||
|
|
|
@ -33,3 +33,6 @@ crossterm = { workspace = true, features = ["use-dev-tty"] }
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "more"
|
name = "more"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = { workspace = true }
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -826,7 +826,7 @@ fn rename_dir_fallback(
|
||||||
io::ErrorKind::PermissionDenied,
|
io::ErrorKind::PermissionDenied,
|
||||||
"Permission denied",
|
"Permission denied",
|
||||||
)),
|
)),
|
||||||
_ => Err(io::Error::new(io::ErrorKind::Other, format!("{err:?}"))),
|
_ => Err(io::Error::other(format!("{err:?}"))),
|
||||||
},
|
},
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,12 @@
|
||||||
//
|
//
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs neve ROOTDIR USERDIR procfs outfile uufs xattrs
|
// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs neve ROOTDIR USERDIR procfs outfile uufs xattrs
|
||||||
// spell-checker:ignore bdfl hlsl IRWXO IRWXG nconfined matchpathcon libselinux-devel
|
// spell-checker:ignore bdfl hlsl IRWXO IRWXG nconfined matchpathcon libselinux-devel
|
||||||
use uutests::at_and_ucmd;
|
use uucore::display::Quotable;
|
||||||
use uutests::new_ucmd;
|
|
||||||
use uutests::path_concat;
|
|
||||||
use uutests::util::TestScenario;
|
use uutests::util::TestScenario;
|
||||||
use uutests::util_name;
|
use uutests::{at_and_ucmd, new_ucmd, path_concat, util_name};
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use std::fs::set_permissions;
|
use std::fs::set_permissions;
|
||||||
|
@ -3946,10 +3945,10 @@ fn test_cp_only_source_no_target() {
|
||||||
let ts = TestScenario::new(util_name!());
|
let ts = TestScenario::new(util_name!());
|
||||||
let at = &ts.fixtures;
|
let at = &ts.fixtures;
|
||||||
at.touch("a");
|
at.touch("a");
|
||||||
ts.ucmd()
|
ts.ucmd().arg("a").fails().stderr_contains(format!(
|
||||||
.arg("a")
|
"missing destination file operand after {}",
|
||||||
.fails()
|
"a".quote()
|
||||||
.stderr_contains("missing destination file operand after \"a\"");
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -2,56 +2,58 @@
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
use std::io::IsTerminal;
|
|
||||||
#[cfg(target_family = "unix")]
|
|
||||||
use uutests::at_and_ucmd;
|
|
||||||
use uutests::new_ucmd;
|
|
||||||
use uutests::util::TestScenario;
|
|
||||||
use uutests::util_name;
|
|
||||||
|
|
||||||
|
use std::io::IsTerminal;
|
||||||
|
|
||||||
|
use uutests::{at_and_ucmd, new_ucmd, util::TestScenario, util_name};
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_more_no_arg() {
|
fn test_no_arg() {
|
||||||
if std::io::stdout().is_terminal() {
|
if std::io::stdout().is_terminal() {
|
||||||
new_ucmd!().fails().stderr_contains("more: bad usage");
|
new_ucmd!()
|
||||||
|
.terminal_simulation(true)
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("more: bad usage");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_valid_arg() {
|
fn test_valid_arg() {
|
||||||
if std::io::stdout().is_terminal() {
|
if std::io::stdout().is_terminal() {
|
||||||
let scene = TestScenario::new(util_name!());
|
let args_list: Vec<&[&str]> = vec![
|
||||||
let at = &scene.fixtures;
|
&["-c"],
|
||||||
|
&["--clean-print"],
|
||||||
|
&["-p"],
|
||||||
|
&["--print-over"],
|
||||||
|
&["-s"],
|
||||||
|
&["--squeeze"],
|
||||||
|
&["-u"],
|
||||||
|
&["--plain"],
|
||||||
|
&["-n", "10"],
|
||||||
|
&["--lines", "0"],
|
||||||
|
&["--number", "0"],
|
||||||
|
&["-F", "10"],
|
||||||
|
&["--from-line", "0"],
|
||||||
|
&["-P", "something"],
|
||||||
|
&["--pattern", "-1"],
|
||||||
|
];
|
||||||
|
for args in args_list {
|
||||||
|
test_alive(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_alive(args: &[&str]) {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
let file = "test_file";
|
let file = "test_file";
|
||||||
at.touch(file);
|
at.touch(file);
|
||||||
|
|
||||||
scene.ucmd().arg(file).arg("-c").succeeds();
|
ucmd.args(args)
|
||||||
scene.ucmd().arg(file).arg("--print-over").succeeds();
|
|
||||||
|
|
||||||
scene.ucmd().arg(file).arg("-p").succeeds();
|
|
||||||
scene.ucmd().arg(file).arg("--clean-print").succeeds();
|
|
||||||
|
|
||||||
scene.ucmd().arg(file).arg("-s").succeeds();
|
|
||||||
scene.ucmd().arg(file).arg("--squeeze").succeeds();
|
|
||||||
|
|
||||||
scene.ucmd().arg(file).arg("-u").succeeds();
|
|
||||||
scene.ucmd().arg(file).arg("--plain").succeeds();
|
|
||||||
|
|
||||||
scene.ucmd().arg(file).arg("-n").arg("10").succeeds();
|
|
||||||
scene.ucmd().arg(file).arg("--lines").arg("0").succeeds();
|
|
||||||
scene.ucmd().arg(file).arg("--number").arg("0").succeeds();
|
|
||||||
|
|
||||||
scene.ucmd().arg(file).arg("-F").arg("10").succeeds();
|
|
||||||
scene
|
|
||||||
.ucmd()
|
|
||||||
.arg(file)
|
.arg(file)
|
||||||
.arg("--from-line")
|
.run_no_wait()
|
||||||
.arg("0")
|
.make_assertion()
|
||||||
.succeeds();
|
.is_alive();
|
||||||
|
|
||||||
scene.ucmd().arg(file).arg("-P").arg("something").succeeds();
|
|
||||||
scene.ucmd().arg(file).arg("--pattern").arg("-1").succeeds();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -67,59 +69,46 @@ fn test_invalid_arg() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_argument_from_file() {
|
fn test_file_arg() {
|
||||||
if std::io::stdout().is_terminal() {
|
|
||||||
let scene = TestScenario::new(util_name!());
|
|
||||||
let at = &scene.fixtures;
|
|
||||||
|
|
||||||
let file = "test_file";
|
|
||||||
|
|
||||||
at.write(file, "1\n2");
|
|
||||||
|
|
||||||
// output all lines
|
|
||||||
scene
|
|
||||||
.ucmd()
|
|
||||||
.arg("-F")
|
|
||||||
.arg("0")
|
|
||||||
.arg(file)
|
|
||||||
.succeeds()
|
|
||||||
.no_stderr()
|
|
||||||
.stdout_contains("1")
|
|
||||||
.stdout_contains("2");
|
|
||||||
|
|
||||||
// output only the second line
|
|
||||||
scene
|
|
||||||
.ucmd()
|
|
||||||
.arg("-F")
|
|
||||||
.arg("2")
|
|
||||||
.arg(file)
|
|
||||||
.succeeds()
|
|
||||||
.no_stderr()
|
|
||||||
.stdout_contains("2")
|
|
||||||
.stdout_does_not_contain("1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_more_dir_arg() {
|
|
||||||
// Run the test only if there's a valid terminal, else do nothing
|
// Run the test only if there's a valid terminal, else do nothing
|
||||||
// Maybe we could capture the error, i.e. "Device not found" in that case
|
// Maybe we could capture the error, i.e. "Device not found" in that case
|
||||||
// but I am leaving this for later
|
// but I am leaving this for later
|
||||||
if std::io::stdout().is_terminal() {
|
if std::io::stdout().is_terminal() {
|
||||||
new_ucmd!()
|
// Directory as argument
|
||||||
.arg(".")
|
let mut ucmd = TestScenario::new(util_name!()).ucmd();
|
||||||
|
ucmd.arg(".")
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stderr_contains("'.' is a directory.");
|
.stderr_contains("'.' is a directory.");
|
||||||
|
|
||||||
|
// Single argument errors
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
at.mkdir_all("folder");
|
||||||
|
ucmd.arg("folder")
|
||||||
|
.succeeds()
|
||||||
|
.stderr_contains("is a directory");
|
||||||
|
|
||||||
|
ucmd = TestScenario::new(util_name!()).ucmd();
|
||||||
|
ucmd.arg("nonexistent_file")
|
||||||
|
.succeeds()
|
||||||
|
.stderr_contains("No such file or directory");
|
||||||
|
|
||||||
|
// Multiple nonexistent files
|
||||||
|
ucmd = TestScenario::new(util_name!()).ucmd();
|
||||||
|
ucmd.arg("file2")
|
||||||
|
.arg("file3")
|
||||||
|
.succeeds()
|
||||||
|
.stderr_contains("file2")
|
||||||
|
.stderr_contains("file3");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
fn test_more_invalid_file_perms() {
|
fn test_invalid_file_perms() {
|
||||||
|
if std::io::stdout().is_terminal() {
|
||||||
use std::fs::{Permissions, set_permissions};
|
use std::fs::{Permissions, set_permissions};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
if std::io::stdout().is_terminal() {
|
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
let permissions = Permissions::from_mode(0o244);
|
let permissions = Permissions::from_mode(0o244);
|
||||||
at.make_file("invalid-perms.txt");
|
at.make_file("invalid-perms.txt");
|
||||||
|
@ -129,89 +118,3 @@ fn test_more_invalid_file_perms() {
|
||||||
.stderr_contains("permission denied");
|
.stderr_contains("permission denied");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_more_error_on_single_arg() {
|
|
||||||
if std::io::stdout().is_terminal() {
|
|
||||||
let ts = TestScenario::new("more");
|
|
||||||
ts.fixtures.mkdir_all("folder");
|
|
||||||
ts.ucmd()
|
|
||||||
.arg("folder")
|
|
||||||
.succeeds()
|
|
||||||
.stderr_contains("is a directory");
|
|
||||||
ts.ucmd()
|
|
||||||
.arg("file1")
|
|
||||||
.succeeds()
|
|
||||||
.stderr_contains("No such file or directory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_more_error_on_multiple_files() {
|
|
||||||
if std::io::stdout().is_terminal() {
|
|
||||||
let ts = TestScenario::new("more");
|
|
||||||
ts.fixtures.mkdir_all("folder");
|
|
||||||
ts.fixtures.make_file("file1");
|
|
||||||
ts.ucmd()
|
|
||||||
.arg("folder")
|
|
||||||
.arg("file2")
|
|
||||||
.arg("file1")
|
|
||||||
.succeeds()
|
|
||||||
.stderr_contains("folder")
|
|
||||||
.stderr_contains("file2")
|
|
||||||
.stdout_contains("file1");
|
|
||||||
ts.ucmd()
|
|
||||||
.arg("file2")
|
|
||||||
.arg("file3")
|
|
||||||
.succeeds()
|
|
||||||
.stderr_contains("file2")
|
|
||||||
.stderr_contains("file3");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_more_pattern_found() {
|
|
||||||
if std::io::stdout().is_terminal() {
|
|
||||||
let scene = TestScenario::new(util_name!());
|
|
||||||
let at = &scene.fixtures;
|
|
||||||
|
|
||||||
let file = "test_file";
|
|
||||||
|
|
||||||
at.write(file, "line1\nline2");
|
|
||||||
|
|
||||||
// output only the second line "line2"
|
|
||||||
scene
|
|
||||||
.ucmd()
|
|
||||||
.arg("-P")
|
|
||||||
.arg("line2")
|
|
||||||
.arg(file)
|
|
||||||
.succeeds()
|
|
||||||
.no_stderr()
|
|
||||||
.stdout_does_not_contain("line1")
|
|
||||||
.stdout_contains("line2");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_more_pattern_not_found() {
|
|
||||||
if std::io::stdout().is_terminal() {
|
|
||||||
let scene = TestScenario::new(util_name!());
|
|
||||||
let at = &scene.fixtures;
|
|
||||||
|
|
||||||
let file = "test_file";
|
|
||||||
|
|
||||||
let file_content = "line1\nline2";
|
|
||||||
at.write(file, file_content);
|
|
||||||
|
|
||||||
scene
|
|
||||||
.ucmd()
|
|
||||||
.arg("-P")
|
|
||||||
.arg("something")
|
|
||||||
.arg(file)
|
|
||||||
.succeeds()
|
|
||||||
.no_stderr()
|
|
||||||
.stdout_contains("Pattern not found")
|
|
||||||
.stdout_contains("line1")
|
|
||||||
.stdout_contains("line2");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue