diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index dfc1129cb..5bbf0eada 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -238,93 +238,88 @@ fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { } } -fn exec(files: &[OsString], b: &Behavior) -> UResult<()> { - let paths: Vec = { - let paths = files.iter().map(Path::new); +fn parse_paths(files: &[OsString], b: &Behavior) -> Vec { + let paths = files.iter().map(Path::new); - // Strip slashes from path, if strip opt present - if b.strip_slashes { - paths - .map(|p| p.components().as_path().to_owned()) - .collect::>() + if b.strip_slashes { + paths + .map(|p| p.components().as_path().to_owned()) + .collect::>() + } else { + paths.map(|p| p.to_owned()).collect::>() + } +} + +fn handle_two_paths(source: &Path, target: &Path, b: &Behavior) -> UResult<()> { + if source.symlink_metadata().is_err() { + return Err(MvError::NoSuchFile(source.quote().to_string()).into()); + } + + if source.eq(target) || are_hardlinks_to_same_file(source, target) { + if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() { + return Err( + MvError::SameFile(source.quote().to_string(), target.quote().to_string()).into(), + ); } else { - paths.map(|p| p.to_owned()).collect::>() + return Err(MvError::SelfSubdirectory(source.display().to_string()).into()); } - }; + } + + if target.is_dir() { + if b.no_target_dir { + if source.is_dir() { + rename(source, target, b, None).map_err_context(|| { + format!("cannot move {} to {}", source.quote(), target.quote()) + }) + } else { + Err(MvError::DirectoryToNonDirectory(target.quote().to_string()).into()) + } + } else { + move_files_into_dir(&[source.to_path_buf()], target, b) + } + } else if target.exists() && source.is_dir() { + match b.overwrite { + OverwriteMode::NoClobber => return Ok(()), + OverwriteMode::Interactive => { + if !prompt_yes!("overwrite {}? ", target.quote()) { + return Err(io::Error::new(io::ErrorKind::Other, "").into()); + } + } + OverwriteMode::Force => {} + }; + Err(MvError::NonDirectoryToDirectory( + source.quote().to_string(), + target.quote().to_string(), + ) + .into()) + } else { + rename(source, target, b, None).map_err(|e| USimpleError::new(1, format!("{e}"))) + } +} + +fn handle_multiple_paths(paths: &[PathBuf], b: &Behavior) -> UResult<()> { + if b.no_target_dir { + return Err(UUsageError::new( + 1, + format!("mv: extra operand {}", paths[2].quote()), + )); + } + let target_dir = paths.last().unwrap(); + let sources = &paths[..paths.len() - 1]; + + move_files_into_dir(sources, target_dir, b) +} + +fn exec(files: &[OsString], b: &Behavior) -> UResult<()> { + let paths = parse_paths(files, b); if let Some(ref name) = b.target_dir { return move_files_into_dir(&paths, &PathBuf::from(name), b); } + match paths.len() { - /* case 0/1 are not possible thanks to clap */ - 2 => { - let source = &paths[0]; - let target = &paths[1]; - // Here we use the `symlink_metadata()` method instead of `exists()`, - // since it handles dangling symlinks correctly. The method gives an - // `Ok()` results unless the source does not exist, or the user - // lacks permission to access metadata. - if source.symlink_metadata().is_err() { - return Err(MvError::NoSuchFile(source.quote().to_string()).into()); - } - - // GNU semantics are: if the source and target are the same, no move occurs and we print an error - if source.eq(target) || are_hardlinks_to_same_file(source, target) { - // Done to match GNU semantics for the dot file - if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() { - return Err(MvError::SameFile( - source.quote().to_string(), - target.quote().to_string(), - ) - .into()); - } else { - return Err(MvError::SelfSubdirectory(source.display().to_string()).into()); - } - } - - if target.is_dir() { - if b.no_target_dir { - if source.is_dir() { - rename(source, target, b, None).map_err_context(|| { - format!("cannot move {} to {}", source.quote(), target.quote()) - }) - } else { - Err(MvError::DirectoryToNonDirectory(target.quote().to_string()).into()) - } - } else { - move_files_into_dir(&[source.clone()], target, b) - } - } else if target.exists() && source.is_dir() { - match b.overwrite { - OverwriteMode::NoClobber => return Ok(()), - OverwriteMode::Interactive => { - if !prompt_yes!("overwrite {}? ", target.quote()) { - return Err(io::Error::new(io::ErrorKind::Other, "").into()); - } - } - OverwriteMode::Force => {} - }; - Err(MvError::NonDirectoryToDirectory( - source.quote().to_string(), - target.quote().to_string(), - ) - .into()) - } else { - rename(source, target, b, None).map_err(|e| USimpleError::new(1, format!("{e}"))) - } - } - _ => { - if b.no_target_dir { - return Err(UUsageError::new( - 1, - format!("mv: extra operand {}", files[2].quote()), - )); - } - let target_dir = paths.last().unwrap(); - let sources = &paths[..paths.len() - 1]; - - move_files_into_dir(sources, target_dir, b) - } + 2 => handle_two_paths(&paths[0], &paths[1], b), + _ => handle_multiple_paths(&paths, b), } }