1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

ln: change error messages, extract common code

This commit is contained in:
Niyaz Nigmatullin 2022-07-18 23:51:51 +03:00
parent 9402a7b3ba
commit 80ff3b3b40
3 changed files with 62 additions and 92 deletions

View file

@ -12,17 +12,17 @@ extern crate uucore;
use clap::{crate_version, Arg, Command}; use clap::{crate_version, Arg, Command};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UError, UResult}; use uucore::error::{FromIo, UError, UResult};
use uucore::format_usage; use uucore::format_usage;
use uucore::fs::{is_symlink, paths_refer_to_same_file}; use uucore::fs::{is_symlink, make_path_relative_to, paths_refer_to_same_file};
use std::borrow::Cow; use std::borrow::Cow;
use std::error::Error; use std::error::Error;
use std::ffi::{OsStr, OsString}; use std::ffi::OsString;
use std::fmt::Display; use std::fmt::Display;
use std::fs; use std::fs;
use std::io::{stdin, Result}; use std::io::stdin;
#[cfg(any(unix, target_os = "redox"))] #[cfg(any(unix, target_os = "redox"))]
use std::os::unix::fs::symlink; use std::os::unix::fs::symlink;
#[cfg(windows)] #[cfg(windows)]
@ -55,8 +55,7 @@ pub enum OverwriteMode {
enum LnError { enum LnError {
TargetIsDirectory(PathBuf), TargetIsDirectory(PathBuf),
SomeLinksFailed, SomeLinksFailed,
FailedToLink(PathBuf, PathBuf, String), SameFile(PathBuf, PathBuf),
SameFile(),
MissingDestination(PathBuf), MissingDestination(PathBuf),
ExtraOperand(OsString), ExtraOperand(OsString),
} }
@ -65,13 +64,10 @@ impl Display for LnError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::TargetIsDirectory(s) => write!(f, "target {} is not a directory", s.quote()), Self::TargetIsDirectory(s) => write!(f, "target {} is not a directory", s.quote()),
Self::FailedToLink(s, d, e) => { Self::SameFile(s, d) => {
write!(f, "failed to link {} to {}: {}", s.quote(), d.quote(), e) write!(f, "{} and {} are the same file", s.quote(), d.quote())
} }
Self::SameFile() => { Self::SomeLinksFailed => Ok(()),
write!(f, "Same file")
}
Self::SomeLinksFailed => write!(f, "some links failed to create"),
Self::MissingDestination(s) => { Self::MissingDestination(s) => {
write!(f, "missing destination file operand after {}", s.quote()) write!(f, "missing destination file operand after {}", s.quote())
} }
@ -89,14 +85,7 @@ impl Error for LnError {}
impl UError for LnError { impl UError for LnError {
fn code(&self) -> i32 { fn code(&self) -> i32 {
match self { 1
Self::TargetIsDirectory(_)
| Self::SomeLinksFailed
| Self::FailedToLink(_, _, _)
| Self::SameFile()
| Self::MissingDestination(_)
| Self::ExtraOperand(_) => 1,
}
} }
} }
@ -315,15 +304,7 @@ fn exec(files: &[PathBuf], settings: &Settings) -> UResult<()> {
} }
assert!(!files.is_empty()); assert!(!files.is_empty());
match link(&files[0], &files[1], settings) { link(&files[0], &files[1], settings)
Ok(_) => Ok(()),
Err(e) => {
Err(
LnError::FailedToLink(files[0].to_owned(), files[1].to_owned(), e.to_string())
.into(),
)
}
}
} }
fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) -> UResult<()> { fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) -> UResult<()> {
@ -374,12 +355,7 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings)
}; };
if let Err(e) = link(srcpath, &targetpath, settings) { if let Err(e) = link(srcpath, &targetpath, settings) {
show_error!( show_error!("{}", e);
"cannot link {} to {}: {}",
targetpath.quote(),
srcpath.quote(),
e
);
all_successful = false; all_successful = false;
} }
} }
@ -390,38 +366,23 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings)
} }
} }
fn relative_path<'a>(src: &Path, dst: &Path) -> Result<Cow<'a, Path>> { fn relative_path<'a>(src: &'a Path, dst: &Path) -> Cow<'a, Path> {
let src_abs = canonicalize(src, MissingHandling::Normal, ResolveMode::Logical)?; if let Ok(src_abs) = canonicalize(src, MissingHandling::Missing, ResolveMode::Physical) {
let mut dst_abs = canonicalize( if let Ok(dst_abs) = canonicalize(
dst.parent().unwrap(), dst.parent().unwrap(),
MissingHandling::Normal, MissingHandling::Missing,
ResolveMode::Logical, ResolveMode::Physical,
)?; ) {
dst_abs.push(dst.components().last().unwrap()); return make_path_relative_to(src_abs, dst_abs).into();
let suffix_pos = src_abs }
.components()
.zip(dst_abs.components())
.take_while(|(s, d)| s == d)
.count();
let src_iter = src_abs.components().skip(suffix_pos).map(|x| x.as_os_str());
let mut result: PathBuf = dst_abs
.components()
.skip(suffix_pos + 1)
.map(|_| OsStr::new(".."))
.chain(src_iter)
.collect();
if result.as_os_str().is_empty() {
result.push(".");
} }
Ok(result.into()) src.into()
} }
fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> { fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> {
let mut backup_path = None; let mut backup_path = None;
let source: Cow<'_, Path> = if settings.relative { let source: Cow<'_, Path> = if settings.relative {
relative_path(src, dst)? relative_path(src, dst)
} else { } else {
src.into() src.into()
}; };
@ -436,11 +397,11 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> {
if settings.backup == BackupMode::ExistingBackup && !settings.symbolic { if settings.backup == BackupMode::ExistingBackup && !settings.symbolic {
// when ln --backup f f, it should detect that it is the same file // when ln --backup f f, it should detect that it is the same file
if paths_refer_to_same_file(src, dst, true) { if paths_refer_to_same_file(src, dst, true) {
return Err(LnError::SameFile().into()); return Err(LnError::SameFile(src.to_owned(), dst.to_owned()).into());
} }
} }
if let Some(ref p) = backup_path { if let Some(ref p) = backup_path {
fs::rename(dst, p)?; fs::rename(dst, p).map_err_context(|| format!("cannot backup {}", dst.quote()))?;
} }
match settings.overwrite { match settings.overwrite {
OverwriteMode::NoClobber => {} OverwriteMode::NoClobber => {}
@ -455,7 +416,7 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> {
} }
OverwriteMode::Force => { OverwriteMode::Force => {
if !is_symlink(dst) && paths_refer_to_same_file(src, dst, true) { if !is_symlink(dst) && paths_refer_to_same_file(src, dst, true) {
return Err(LnError::SameFile().into()); return Err(LnError::SameFile(src.to_owned(), dst.to_owned()).into());
} }
if fs::remove_file(dst).is_ok() {}; if fs::remove_file(dst).is_ok() {};
// In case of error, don't do anything // In case of error, don't do anything
@ -470,11 +431,18 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> UResult<()> {
// if we want to have an hard link, // if we want to have an hard link,
// source is a symlink and -L is passed // source is a symlink and -L is passed
// we want to resolve the symlink to create the hardlink // we want to resolve the symlink to create the hardlink
std::fs::canonicalize(&source)? fs::canonicalize(&source)
.map_err_context(|| format!("failed to access {}", source.quote()))?
} else { } else {
source.to_path_buf() source.to_path_buf()
}; };
fs::hard_link(&p, dst)?; fs::hard_link(&p, dst).map_err_context(|| {
format!(
"failed to create hard link {} => {}",
source.quote(),
dst.quote()
)
})?;
} }
if settings.verbose { if settings.verbose {

View file

@ -11,12 +11,12 @@
extern crate uucore; extern crate uucore;
use clap::{crate_version, Arg, ArgMatches, Command}; use clap::{crate_version, Arg, ArgMatches, Command};
use std::path::Component;
use std::{ use std::{
io::{stdout, Write}, io::{stdout, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use uucore::error::UClapError; use uucore::error::UClapError;
use uucore::fs::make_path_relative_to;
use uucore::{ use uucore::{
display::{print_verbatim, Quotable}, display::{print_verbatim, Quotable},
error::{FromIo, UResult}, error::{FromIo, UResult},
@ -274,36 +274,13 @@ fn process_relative(
) -> PathBuf { ) -> PathBuf {
if let Some(base) = relative_base { if let Some(base) = relative_base {
if path.starts_with(base) { if path.starts_with(base) {
make_path_relative_to(&path, relative_to.unwrap_or(base)) make_path_relative_to(path.as_path(), relative_to.unwrap_or(base))
} else { } else {
path path
} }
} else if let Some(to) = relative_to { } else if let Some(to) = relative_to {
make_path_relative_to(&path, to) make_path_relative_to(path.as_path(), to)
} else { } else {
path path
} }
} }
/// Converts absolute `path` to be relative to absolute `to` path.
fn make_path_relative_to(path: &Path, to: &Path) -> PathBuf {
let common_prefix_size = path
.components()
.zip(to.components())
.take_while(|(first, second)| first == second)
.count();
let path_suffix = path
.components()
.skip(common_prefix_size)
.map(|x| x.as_os_str());
let mut components: Vec<_> = to
.components()
.skip(common_prefix_size)
.map(|_| Component::ParentDir.as_os_str())
.chain(path_suffix)
.collect();
if components.is_empty() {
components.push(Component::CurDir.as_os_str());
}
components.iter().collect()
}

View file

@ -488,6 +488,31 @@ pub fn paths_refer_to_same_file<P: AsRef<Path>>(p1: P, p2: P, dereference: bool)
false false
} }
/// Converts absolute `path` to be relative to absolute `to` path.
pub fn make_path_relative_to<P: AsRef<Path>>(path: P, to: P) -> PathBuf {
let path = path.as_ref();
let to = to.as_ref();
let common_prefix_size = path
.components()
.zip(to.components())
.take_while(|(first, second)| first == second)
.count();
let path_suffix = path
.components()
.skip(common_prefix_size)
.map(|x| x.as_os_str());
let mut components: Vec<_> = to
.components()
.skip(common_prefix_size)
.map(|_| Component::ParentDir.as_os_str())
.chain(path_suffix)
.collect();
if components.is_empty() {
components.push(Component::CurDir.as_os_str());
}
components.iter().collect()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope. // Note this useful idiom: importing names from outer (for mod tests) scope.