1
Fork 0
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:
Sylvestre Ledru 2025-05-19 11:03:02 +02:00 committed by GitHub
commit 3848e6c271
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 870 additions and 665 deletions

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,58 +2,60 @@
// //
// 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"],
let file = "test_file"; &["-p"],
at.touch(file); &["--print-over"],
&["-s"],
scene.ucmd().arg(file).arg("-c").succeeds(); &["--squeeze"],
scene.ucmd().arg(file).arg("--print-over").succeeds(); &["-u"],
&["--plain"],
scene.ucmd().arg(file).arg("-p").succeeds(); &["-n", "10"],
scene.ucmd().arg(file).arg("--clean-print").succeeds(); &["--lines", "0"],
&["--number", "0"],
scene.ucmd().arg(file).arg("-s").succeeds(); &["-F", "10"],
scene.ucmd().arg(file).arg("--squeeze").succeeds(); &["--from-line", "0"],
&["-P", "something"],
scene.ucmd().arg(file).arg("-u").succeeds(); &["--pattern", "-1"],
scene.ucmd().arg(file).arg("--plain").succeeds(); ];
for args in args_list {
scene.ucmd().arg(file).arg("-n").arg("10").succeeds(); test_alive(args);
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("--from-line")
.arg("0")
.succeeds();
scene.ucmd().arg(file).arg("-P").arg("something").succeeds();
scene.ucmd().arg(file).arg("--pattern").arg("-1").succeeds();
} }
} }
fn test_alive(args: &[&str]) {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_file";
at.touch(file);
ucmd.args(args)
.arg(file)
.run_no_wait()
.make_assertion()
.is_alive();
}
#[test] #[test]
fn test_invalid_arg() { fn test_invalid_arg() {
if std::io::stdout().is_terminal() { if std::io::stdout().is_terminal() {
@ -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() {
use std::fs::{Permissions, set_permissions};
use std::os::unix::fs::PermissionsExt;
if std::io::stdout().is_terminal() { if std::io::stdout().is_terminal() {
use std::fs::{Permissions, set_permissions};
use std::os::unix::fs::PermissionsExt;
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");
}
}