Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/uu/cp/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ cp-error-missing-destination-operand = missing destination file operand after {
cp-error-extra-operand = extra operand { $operand }
cp-error-same-file = { $source } and { $dest } are the same file
cp-error-backing-up-destroy-source = backing up { $dest } might destroy source; { $source } not copied
cp-error-cannot-open-for-reading = cannot open { $source } for reading
cp-error-cannot-open-for-reading = cannot open { $source } for reading: permission denied

@xtqqczze xtqqczze Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gnu prints "Permission denied" with a capital P; this will be fixed by #13680.

cp-error-not-writing-dangling-symlink = not writing through dangling symlink { $dest }
cp-error-failed-to-clone = failed to clone { $dest } from { $source }: { $error }
cp-error-cannot-change-attribute = cannot change attribute { $dest }: Source file is a non regular file
Expand Down
2 changes: 1 addition & 1 deletion src/uu/cp/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ cp-error-missing-destination-operand = opérande fichier de destination manquant
cp-error-extra-operand = opérande supplémentaire { $operand }
cp-error-same-file = { $source } et { $dest } sont le même fichier
cp-error-backing-up-destroy-source = sauvegarder { $dest } pourrait détruire la source ; { $source } non copié
cp-error-cannot-open-for-reading = impossible d'ouvrir { $source } en lecture
cp-error-cannot-open-for-reading = impossible d'ouvrir { $source } en lecture : permission refusée
cp-error-not-writing-dangling-symlink = ne pas écrire à travers le lien symbolique pendant { $dest }
cp-error-failed-to-clone = échec du clonage de { $dest } depuis { $source } : { $error }
cp-error-cannot-change-attribute = impossible de changer l'attribut { $dest } : Le fichier source n'est pas un fichier régulier
Expand Down
12 changes: 2 additions & 10 deletions src/uu/cp/src/copydir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ use std::path::{Path, PathBuf, StripPrefixError};

use indicatif::ProgressBar;
use uucore::display::Quotable;
use uucore::error::{UIoError, set_exit_code};
use uucore::error::set_exit_code;
use uucore::fs::{
FileInformation, MissingHandling, ResolveMode, canonicalize, path_ends_with_terminator,
};
use uucore::show;
use uucore::translate;
use uucore::uio_error;
use walkdir::{DirEntry, WalkDir};

#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))]
Expand Down Expand Up @@ -323,14 +322,7 @@ fn copy_direntry(
// cause us to continue walking the directory?
match err {
CpError::IoErrContext(e, _) if e.kind() == io::ErrorKind::PermissionDenied => {
show!(uio_error!(
e,
"{}",
translate!(
"cp-error-cannot-open-for-reading",
"source" => entry.source_relative.quote()
),
));
show!(CpError::CannotOpenForReading(entry.source_relative.clone()));
}
e => return Err(e),
}
Expand Down
4 changes: 4 additions & 0 deletions src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ mod platform;

#[derive(Debug, thiserror::Error)]
pub enum CpError {
/// Permission denied.
#[error("{}", translate!("cp-error-cannot-open-for-reading", "source" => .0.quote()))]
CannotOpenForReading(PathBuf),

/// Simple [`io::Error`] wrapper
#[error("{0}")]
IoErr(#[from] io::Error),
Expand Down
79 changes: 2 additions & 77 deletions src/uucore/src/lib/mods/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,8 @@ impl UError for UUsageError {
/// The messages displayed by [`UIoError`] should match the error messages displayed by GNU
/// coreutils.
///
/// There are two ways to construct this type: with [`UIoError::new`] or by calling the
/// [`FromIo::map_err_context`] method on a [`std::io::Result`] or [`std::io::Error`].
/// This type is constructed by calling the [`FromIo::map_err_context`] method on a
/// [`std::io::Result`] or [`std::io::Error`].
/// ```
/// use uucore::{
/// display::Quotable,
Expand All @@ -388,14 +388,6 @@ impl UError for UUsageError {
/// use std::path::Path;
/// let path = Path::new("test.txt");
///
/// // Manual construction
/// let e: Box<dyn UError> = UIoError::new(
/// std::io::ErrorKind::NotFound,
/// format!("cannot access {}", path.quote())
/// );
/// let res: UResult<()> = Err(e.into());
///
/// // Converting from an `std::io::Error`.
/// let res: UResult<File> = File::open(path).map_err_context(|| format!("cannot access {}", path.quote()));
/// ```
#[derive(Debug)]
Expand Down Expand Up @@ -620,73 +612,6 @@ impl From<nix::Error> for Box<dyn UError> {
}
}

/// Shorthand to construct [`UIoError`]-instances.
///
/// This macro serves as a convenience call to quickly construct instances of
/// [`UIoError`]. It takes:
///
/// - An instance of [`std::io::Error`]
/// - A `format!`-compatible string and
/// - An arbitrary number of arguments to the format string
///
/// In exactly this order. It is equivalent to the more verbose code seen in the
/// example.
///
/// # Examples
///
/// ```
/// use uucore::error::UIoError;
/// use uucore::uio_error;
///
/// let io_err = std::io::Error::new(
/// std::io::ErrorKind::PermissionDenied, "fix me please!"
/// );
///
/// let uio_err = UIoError::new(
/// io_err.kind(),
/// format!("Error code: {}", 2)
/// );
///
/// let other_uio_err = uio_error!(io_err, "Error code: {}", 2);
///
/// // prints "fix me please!: Permission denied"
/// println!("{uio_err}");
/// // prints "Error code: 2: Permission denied"
/// println!("{other_uio_err}");
/// ```
///
/// The [`std::fmt::Display`] impl of [`UIoError`] will then ensure that an
/// appropriate error message relating to the actual error kind of the
/// [`std::io::Error`] is appended to whatever error message is defined in
/// addition (as secondary argument).
///
/// If you want to show only the error message for the [`std::io::ErrorKind`]
/// that's contained in [`UIoError`], pass the second argument as empty string:
///
/// ```
/// use uucore::error::UIoError;
/// use uucore::uio_error;
///
/// let io_err = std::io::Error::new(
/// std::io::ErrorKind::PermissionDenied, "fix me please!"
/// );
///
/// let other_uio_err = uio_error!(io_err, "");
///
/// // prints: ": Permission denied"
/// println!("{other_uio_err}");
/// ```
//#[macro_use]
#[macro_export]
macro_rules! uio_error(
($err:expr, $($args:tt)+) => ({
UIoError::new(
$err.kind(),
format!($($args)+)
)
})
);

/// A special error type that does not print any message when returned from
/// `uumain`. Especially useful for porting utilities to using [`UResult`].
///
Expand Down
Loading