mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
mv: Fix stderr output mv file into dir and dir into file where both are files (#5464)
* Add tests mv file into dir and dir into file where both are files * Fix test_mv_dir_into_file_where_both_are_files * Fix test_mv_file_into_dir_where_both_are_files * Store String in error instead of PathBuf * Implement path_ends_with_terminator for windows * Fix compilation on windows
This commit is contained in:
parent
5dbbdb4788
commit
5c100dd088
3 changed files with 67 additions and 2 deletions
|
@ -10,6 +10,7 @@ use uucore::error::UError;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum MvError {
|
pub enum MvError {
|
||||||
NoSuchFile(String),
|
NoSuchFile(String),
|
||||||
|
CannotStatNotADirectory(String),
|
||||||
SameFile(String, String),
|
SameFile(String, String),
|
||||||
SelfSubdirectory(String),
|
SelfSubdirectory(String),
|
||||||
SelfTargetSubdirectory(String, String),
|
SelfTargetSubdirectory(String, String),
|
||||||
|
@ -17,6 +18,7 @@ pub enum MvError {
|
||||||
NonDirectoryToDirectory(String, String),
|
NonDirectoryToDirectory(String, String),
|
||||||
NotADirectory(String),
|
NotADirectory(String),
|
||||||
TargetNotADirectory(String),
|
TargetNotADirectory(String),
|
||||||
|
FailedToAccessNotADirectory(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for MvError {}
|
impl Error for MvError {}
|
||||||
|
@ -25,6 +27,7 @@ impl Display for MvError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||||
match self {
|
match self {
|
||||||
Self::NoSuchFile(s) => write!(f, "cannot stat {s}: No such file or directory"),
|
Self::NoSuchFile(s) => write!(f, "cannot stat {s}: No such file or directory"),
|
||||||
|
Self::CannotStatNotADirectory(s) => write!(f, "cannot stat {s}: Not a directory"),
|
||||||
Self::SameFile(s, t) => write!(f, "{s} and {t} are the same file"),
|
Self::SameFile(s, t) => write!(f, "{s} and {t} are the same file"),
|
||||||
Self::SelfSubdirectory(s) => write!(
|
Self::SelfSubdirectory(s) => write!(
|
||||||
f,
|
f,
|
||||||
|
@ -42,6 +45,10 @@ impl Display for MvError {
|
||||||
}
|
}
|
||||||
Self::NotADirectory(t) => write!(f, "target {t}: Not a directory"),
|
Self::NotADirectory(t) => write!(f, "target {t}: Not a directory"),
|
||||||
Self::TargetNotADirectory(t) => write!(f, "target directory {t}: Not a directory"),
|
Self::TargetNotADirectory(t) => write!(f, "target directory {t}: Not a directory"),
|
||||||
|
|
||||||
|
Self::FailedToAccessNotADirectory(t) => {
|
||||||
|
write!(f, "failed to access {t}: Not a directory")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,25 @@ static OPT_VERBOSE: &str = "verbose";
|
||||||
static OPT_PROGRESS: &str = "progress";
|
static OPT_PROGRESS: &str = "progress";
|
||||||
static ARG_FILES: &str = "files";
|
static ARG_FILES: &str = "files";
|
||||||
|
|
||||||
|
/// Returns true if the passed `path` ends with a path terminator.
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn path_ends_with_terminator(path: &Path) -> bool {
|
||||||
|
use std::os::unix::prelude::OsStrExt;
|
||||||
|
path.as_os_str()
|
||||||
|
.as_bytes()
|
||||||
|
.last()
|
||||||
|
.map_or(false, |&byte| byte == b'/' || byte == b'\\')
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn path_ends_with_terminator(path: &Path) -> bool {
|
||||||
|
use std::os::windows::prelude::OsStrExt;
|
||||||
|
path.as_os_str()
|
||||||
|
.encode_wide()
|
||||||
|
.last()
|
||||||
|
.map_or(false, |wide| wide == b'/'.into() || wide == b'\\'.into())
|
||||||
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let mut app = uu_app();
|
let mut app = uu_app();
|
||||||
|
@ -299,7 +318,11 @@ fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()>
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
if source.symlink_metadata().is_err() {
|
if source.symlink_metadata().is_err() {
|
||||||
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
|
return Err(if path_ends_with_terminator(source) {
|
||||||
|
MvError::CannotStatNotADirectory(source.quote().to_string()).into()
|
||||||
|
} else {
|
||||||
|
MvError::NoSuchFile(source.quote().to_string()).into()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.eq(target)
|
if (source.eq(target)
|
||||||
|
@ -316,7 +339,13 @@ fn handle_two_paths(source: &Path, target: &Path, opts: &Options) -> UResult<()>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if target.is_dir() {
|
let target_is_dir = target.is_dir();
|
||||||
|
|
||||||
|
if path_ends_with_terminator(target) && !target_is_dir {
|
||||||
|
return Err(MvError::FailedToAccessNotADirectory(target.quote().to_string()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if target_is_dir {
|
||||||
if opts.no_target_dir {
|
if opts.no_target_dir {
|
||||||
if source.is_dir() {
|
if source.is_dir() {
|
||||||
rename(source, target, opts, None).map_err_context(|| {
|
rename(source, target, opts, None).map_err_context(|| {
|
||||||
|
|
|
@ -1414,6 +1414,35 @@ fn test_mv_directory_into_subdirectory_of_itself_fails() {
|
||||||
"mv: cannot move 'mydir/' to a subdirectory of itself, 'mydir/mydir_2/mydir/'",
|
"mv: cannot move 'mydir/' to a subdirectory of itself, 'mydir/mydir_2/mydir/'",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mv_file_into_dir_where_both_are_files() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
at.touch("a");
|
||||||
|
at.touch("b");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("a")
|
||||||
|
.arg("b/")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("mv: failed to access 'b/': Not a directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mv_dir_into_file_where_both_are_files() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
at.touch("a");
|
||||||
|
at.touch("b");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("a/")
|
||||||
|
.arg("b")
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("mv: cannot stat 'a/': Not a directory");
|
||||||
|
}
|
||||||
|
|
||||||
// Todo:
|
// Todo:
|
||||||
|
|
||||||
// $ at.touch a b
|
// $ at.touch a b
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue