Merge pull request #8200 from sylvestre/l10n-cp
Some checks are pending
CICD / Style/cargo-deny (push) Waiting to run
CICD / Style/deps (push) Waiting to run
CICD / Documentation/warnings (push) Waiting to run
CICD / MinRustV (push) Waiting to run
CICD / Dependencies (push) Waiting to run
CICD / Build/Makefile (push) Blocked by required conditions
CICD / Build/stable (push) Blocked by required conditions
CICD / Build/nightly (push) Blocked by required conditions
CICD / Binary sizes (push) Blocked by required conditions
CICD / Build (push) Blocked by required conditions
CICD / Tests/BusyBox test suite (push) Blocked by required conditions
CICD / Tests/Toybox test suite (push) Blocked by required conditions
CICD / Code Coverage (push) Waiting to run
CICD / Separate Builds (push) Waiting to run
CICD / Test all features separately (push) Blocked by required conditions
CICD / Build/SELinux (push) Blocked by required conditions
GnuTests / Run GNU tests (push) Waiting to run
Android / Test builds (push) Waiting to run
Code Quality / Style/format (push) Waiting to run
Code Quality / Style/lint (push) Waiting to run
Code Quality / Style/spelling (push) Waiting to run
Code Quality / Style/toml (push) Waiting to run
Code Quality / Style/Python (push) Waiting to run
Code Quality / Pre-commit hooks (push) Waiting to run
FreeBSD / Style and Lint (push) Waiting to run
FreeBSD / Tests (push) Waiting to run

l10n: port cp for translation + add french
This commit is contained in:
Daniel Hofstetter 2025-06-23 16:08:06 +02:00 committed by GitHub
commit 1675c3e981
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 553 additions and 184 deletions

View file

@ -14,4 +14,103 @@ cp-after-help = Do not copy a non-directory that has an existing destination wit
- all This is the default operation when an --update option is not specified, and results in all existing files in the destination being replaced.
- none This is similar to the --no-clobber option, in that no files in the destination are replaced, but also skipping a file does not induce a failure.
- older This is the default operation when --update is specified, and results in files being replaced if theyre older than the corresponding source file.
- older This is the default operation when --update is specified, and results in files being replaced if they're older than the corresponding source file.
# Help messages
cp-help-target-directory = copy all SOURCE arguments into target-directory
cp-help-no-target-directory = Treat DEST as a regular file and not a directory
cp-help-interactive = ask before overwriting files
cp-help-link = hard-link files instead of copying
cp-help-no-clobber = don't overwrite a file that already exists
cp-help-recursive = copy directories recursively
cp-help-strip-trailing-slashes = remove any trailing slashes from each SOURCE argument
cp-help-debug = explain how a file is copied. Implies -v
cp-help-verbose = explicitly state what is being done
cp-help-symbolic-link = make symbolic links instead of copying
cp-help-force = if an existing destination file cannot be opened, remove it and try again (this option is ignored when the -n option is also used). Currently not implemented for Windows.
cp-help-remove-destination = remove each existing destination file before attempting to open it (contrast with --force). On Windows, currently only works for writeable files.
cp-help-reflink = control clone/CoW copies. See below
cp-help-attributes-only = Don't copy the file data, just the attributes
cp-help-preserve = Preserve the specified attributes (default: mode, ownership (unix only), timestamps), if possible additional attributes: context, links, xattr, all
cp-help-preserve-default = same as --preserve=mode,ownership(unix only),timestamps
cp-help-no-preserve = don't preserve the specified attributes
cp-help-parents = use full source file name under DIRECTORY
cp-help-no-dereference = never follow symbolic links in SOURCE
cp-help-dereference = always follow symbolic links in SOURCE
cp-help-cli-symbolic-links = follow command-line symbolic links in SOURCE
cp-help-archive = Same as -dR --preserve=all
cp-help-no-dereference-preserve-links = same as --no-dereference --preserve=links
cp-help-one-file-system = stay on this file system
cp-help-sparse = control creation of sparse files. See below
cp-help-selinux = set SELinux security context of destination file to default type
cp-help-context = like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX
cp-help-progress = Display a progress bar. Note: this feature is not supported by GNU coreutils.
cp-help-copy-contents = NotImplemented: copy contents of special files when recursive
# Error messages
cp-error-missing-file-operand = missing file operand
cp-error-missing-destination-operand = missing destination file operand after { $source }
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-not-writing-dangling-symlink = not writing through dangling symlink { $dest }
cp-error-failed-to-clone = failed to clone { $source } from { $dest }: { $error }
cp-error-cannot-change-attribute = cannot change attribute { $dest }: Source file is a non regular file
cp-error-cannot-stat = cannot stat { $source }: No such file or directory
cp-error-cannot-create-symlink = cannot create symlink { $dest } to { $source }
cp-error-cannot-create-hard-link = cannot create hard link { $dest } to { $source }
cp-error-omitting-directory = -r not specified; omitting directory { $dir }
cp-error-cannot-copy-directory-into-itself = cannot copy a directory, { $source }, into itself, { $dest }
cp-error-will-not-copy-through-symlink = will not copy { $source } through just-created symlink { $dest }
cp-error-will-not-overwrite-just-created = will not overwrite just-created { $dest } with { $source }
cp-error-target-not-directory = target: { $target } is not a directory
cp-error-cannot-overwrite-directory-with-non-directory = cannot overwrite directory { $dir } with non-directory
cp-error-cannot-overwrite-non-directory-with-directory = cannot overwrite non-directory with directory
cp-error-with-parents-dest-must-be-dir = with --parents, the destination must be a directory
cp-error-not-replacing = not replacing { $file }
cp-error-failed-get-current-dir = failed to get current directory { $error }
cp-error-failed-set-permissions = cannot set permissions { $path }
cp-error-backup-mutually-exclusive = options --backup and --no-clobber are mutually exclusive
cp-error-invalid-argument = invalid argument { $arg } for '{ $option }'
cp-error-option-not-implemented = Option '{ $option }' not yet implemented.
cp-error-not-all-files-copied = Not all files were copied
cp-error-reflink-always-sparse-auto = `--reflink=always` can be used only with --sparse=auto
cp-error-file-exists = { $path }: File exists
cp-error-invalid-backup-argument = --backup is mutually exclusive with -n or --update=none-fail
cp-error-reflink-not-supported = --reflink is only supported on linux and macOS
cp-error-sparse-not-supported = --sparse is only supported on linux
cp-error-not-a-directory = { $path } is not a directory
cp-error-selinux-not-enabled = SELinux was not enabled during the compile time!
cp-error-selinux-set-context = failed to set the security context of { $path }: { $error }
cp-error-selinux-get-context = failed to get security context of { $path }
cp-error-selinux-error = SELinux error: { $error }
cp-error-cannot-create-fifo = cannot create fifo { $path }: File exists
cp-error-invalid-attribute = invalid attribute { $value }
cp-error-failed-to-create-whole-tree = failed to create whole tree
cp-error-failed-to-create-directory = Failed to create directory: { $error }
cp-error-backup-format = cp: { $error }
Try '{ $exec } --help' for more information.
# Debug enum strings
cp-debug-enum-no = no
cp-debug-enum-yes = yes
cp-debug-enum-avoided = avoided
cp-debug-enum-unsupported = unsupported
cp-debug-enum-unknown = unknown
cp-debug-enum-zeros = zeros
cp-debug-enum-seek-hole = SEEK_HOLE
cp-debug-enum-seek-hole-zeros = SEEK_HOLE + zeros
# Warning messages
cp-warning-source-specified-more-than-once = source { $file_type } { $source } specified more than once
# Verbose and debug messages
cp-verbose-copied = { $source } -> { $dest }
cp-debug-skipped = skipped { $path }
cp-verbose-created-directory = { $source } -> { $dest }
cp-debug-copy-offload = copy offload: { $offload }, reflink: { $reflink }, sparse detection: { $sparse }
# Prompts
cp-prompt-overwrite = overwrite { $path }?
cp-prompt-overwrite-with-mode = replace { $path }, overriding mode

116
src/uu/cp/locales/fr-FR.ftl Normal file
View file

@ -0,0 +1,116 @@
cp-about = Copier SOURCE vers DEST, ou plusieurs SOURCE(s) vers RÉPERTOIRE.
cp-usage = cp [OPTION]... [-T] SOURCE DEST
cp [OPTION]... SOURCE... RÉPERTOIRE
cp [OPTION]... -t RÉPERTOIRE SOURCE...
cp-after-help = Ne pas copier un non-répertoire qui a une destination existante avec le même horodatage de modification ou plus récent ;
à la place, ignorer silencieusement le fichier sans échec. Si les horodatages sont préservés, la comparaison est faite avec
l'horodatage source tronqué aux résolutions du système de fichiers de destination et des appels système utilisés pour
mettre à jour les horodatages ; cela évite le travail en double si plusieurs commandes cp -pu sont exécutées avec la même source
et destination. Cette option est ignorée si l'option -n ou --no-clobber est également spécifiée. De plus, si
--preserve=links est également spécifié (comme avec cp -au par exemple), cela aura la priorité ; par conséquent,
selon l'ordre dans lequel les fichiers sont traités depuis la source, les fichiers plus récents dans la destination peuvent être remplacés,
pour refléter les liens durs dans la source. ce qui donne plus de contrôle sur les fichiers existants dans la destination qui sont
remplacés, et sa valeur peut être l'une des suivantes :
- all C'est l'opération par défaut lorsqu'une option --update n'est pas spécifiée, et entraîne le remplacement de tous les fichiers existants dans la destination.
- none Cela est similaire à l'option --no-clobber, en ce sens qu'aucun fichier dans la destination n'est remplacé, mais ignorer un fichier n'induit pas d'échec.
- older C'est l'opération par défaut lorsque --update est spécifié, et entraîne le remplacement des fichiers s'ils sont plus anciens que le fichier source correspondant.
# Messages d'aide
cp-help-target-directory = copier tous les arguments SOURCE dans le répertoire cible
cp-help-no-target-directory = Traiter DEST comme un fichier régulier et non comme un répertoire
cp-help-interactive = demander avant d'écraser les fichiers
cp-help-link = créer des liens durs au lieu de copier
cp-help-no-clobber = ne pas écraser un fichier qui existe déjà
cp-help-recursive = copier les répertoires récursivement
cp-help-strip-trailing-slashes = supprimer les barres obliques finales de chaque argument SOURCE
cp-help-debug = expliquer comment un fichier est copié. Implique -v
cp-help-verbose = indiquer explicitement ce qui est fait
cp-help-symbolic-link = créer des liens symboliques au lieu de copier
cp-help-force = si un fichier de destination existant ne peut pas être ouvert, le supprimer et réessayer (cette option est ignorée lorsque l'option -n est également utilisée). Actuellement non implémenté pour Windows.
cp-help-remove-destination = supprimer chaque fichier de destination existant avant de tenter de l'ouvrir (contraste avec --force). Sur Windows, ne fonctionne actuellement que pour les fichiers inscriptibles.
cp-help-reflink = contrôler les copies clone/CoW. Voir ci-dessous
cp-help-attributes-only = Ne pas copier les données du fichier, juste les attributs
cp-help-preserve = Préserver les attributs spécifiés (par défaut : mode, propriété (unix uniquement), horodatages), si possible attributs supplémentaires : contexte, liens, xattr, all
cp-help-preserve-default = identique à --preserve=mode,ownership(unix uniquement),timestamps
cp-help-no-preserve = ne pas préserver les attributs spécifiés
cp-help-parents = utiliser le nom complet du fichier source sous RÉPERTOIRE
cp-help-no-dereference = ne jamais suivre les liens symboliques dans SOURCE
cp-help-dereference = toujours suivre les liens symboliques dans SOURCE
cp-help-cli-symbolic-links = suivre les liens symboliques de la ligne de commande dans SOURCE
cp-help-archive = Identique à -dR --preserve=all
cp-help-no-dereference-preserve-links = identique à --no-dereference --preserve=links
cp-help-one-file-system = rester sur ce système de fichiers
cp-help-sparse = contrôler la création de fichiers épars. Voir ci-dessous
cp-help-selinux = définir le contexte de sécurité SELinux du fichier de destination au type par défaut
cp-help-context = comme -Z, ou si CTX est spécifié, définir le contexte de sécurité SELinux ou SMACK à CTX
cp-help-progress = Afficher une barre de progression. Note : cette fonctionnalité n'est pas supportée par GNU coreutils.
cp-help-copy-contents = Non implémenté : copier le contenu des fichiers spéciaux lors de la récursion
# Messages d'erreur
cp-error-missing-file-operand = opérande fichier manquant
cp-error-missing-destination-operand = opérande fichier de destination manquant après { $source }
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-not-writing-dangling-symlink = ne pas écrire à travers le lien symbolique pendant { $dest }
cp-error-failed-to-clone = échec du clonage de { $source } depuis { $dest } : { $error }
cp-error-cannot-change-attribute = impossible de changer l'attribut { $dest } : Le fichier source n'est pas un fichier régulier
cp-error-cannot-stat = impossible de faire stat sur { $source } : Aucun fichier ou répertoire de ce type
cp-error-cannot-create-symlink = impossible de créer le lien symbolique { $dest } vers { $source }
cp-error-cannot-create-hard-link = impossible de créer le lien dur { $dest } vers { $source }
cp-error-omitting-directory = -r non spécifié ; répertoire { $dir } omis
cp-error-cannot-copy-directory-into-itself = impossible de copier un répertoire, { $source }, dans lui-même, { $dest }
cp-error-will-not-copy-through-symlink = ne copiera pas { $source } à travers le lien symbolique tout juste créé { $dest }
cp-error-will-not-overwrite-just-created = n'écrasera pas le fichier tout juste créé { $dest } avec { $source }
cp-error-target-not-directory = cible : { $target } n'est pas un répertoire
cp-error-cannot-overwrite-directory-with-non-directory = impossible d'écraser le répertoire { $dir } avec un non-répertoire
cp-error-cannot-overwrite-non-directory-with-directory = impossible d'écraser un non-répertoire avec un répertoire
cp-error-with-parents-dest-must-be-dir = avec --parents, la destination doit être un répertoire
cp-error-not-replacing = ne remplace pas { $file }
cp-error-failed-get-current-dir = échec de l'obtention du répertoire actuel { $error }
cp-error-failed-set-permissions = impossible de définir les permissions { $path }
cp-error-backup-mutually-exclusive = les options --backup et --no-clobber sont mutuellement exclusives
cp-error-invalid-argument = argument invalide { $arg } pour '{ $option }'
cp-error-option-not-implemented = Option '{ $option }' pas encore implémentée.
cp-error-not-all-files-copied = Tous les fichiers n'ont pas été copiés
cp-error-reflink-always-sparse-auto = `--reflink=always` ne peut être utilisé qu'avec --sparse=auto
cp-error-file-exists = { $path } : Le fichier existe
cp-error-invalid-backup-argument = --backup est mutuellement exclusif avec -n ou --update=none-fail
cp-error-reflink-not-supported = --reflink n'est supporté que sur linux et macOS
cp-error-sparse-not-supported = --sparse n'est supporté que sur linux
cp-error-not-a-directory = { $path } n'est pas un répertoire
cp-error-selinux-not-enabled = SELinux n'était pas activé lors de la compilation !
cp-error-selinux-set-context = échec de la définition du contexte de sécurité de { $path } : { $error }
cp-error-selinux-get-context = échec de l'obtention du contexte de sécurité de { $path }
cp-error-selinux-error = Erreur SELinux : { $error }
cp-error-cannot-create-fifo = impossible de créer le fifo { $path } : Le fichier existe
cp-error-invalid-attribute = attribut invalide { $value }
cp-error-failed-to-create-whole-tree = échec de la création de l'arborescence complète
cp-error-failed-to-create-directory = Échec de la création du répertoire : { $error }
cp-error-backup-format = cp : { $error }
Tentez '{ $exec } --help' pour plus d'informations.
# Debug enum strings
cp-debug-enum-no = non
cp-debug-enum-yes = oui
cp-debug-enum-avoided = évité
cp-debug-enum-unsupported = non supporté
cp-debug-enum-unknown = inconnu
cp-debug-enum-zeros = zéros
cp-debug-enum-seek-hole = SEEK_HOLE
cp-debug-enum-seek-hole-zeros = SEEK_HOLE + zéros
# Messages d'avertissement
cp-warning-source-specified-more-than-once = { $file_type } source { $source } spécifié plus d'une fois
# Messages verbeux et de débogage
cp-verbose-copied = { $source } -> { $dest }
cp-debug-skipped = { $path } ignoré
cp-verbose-created-directory = { $source } -> { $dest }
cp-debug-copy-offload = copy offload : { $offload }, reflink : { $reflink }, sparse detection : { $sparse }
# Invites
cp-prompt-overwrite = écraser { $path } ?
cp-prompt-overwrite-with-mode = remplacer { $path }, en écrasant le mode

View file

@ -20,6 +20,7 @@ use uucore::error::UIoError;
use uucore::fs::{
FileInformation, MissingHandling, ResolveMode, canonicalize, path_ends_with_terminator,
};
use uucore::locale::{get_message, get_message_with_args};
use uucore::show;
use uucore::show_error;
use uucore::uio_error;
@ -183,7 +184,13 @@ impl Entry {
let source_is_dir = source.is_dir();
if path_ends_with_terminator(context.target) && source_is_dir {
if let Err(e) = fs::create_dir_all(context.target) {
eprintln!("Failed to create directory: {e}");
eprintln!(
"{}",
get_message_with_args(
"cp-error-failed-to-create-directory",
HashMap::from([("error".to_string(), e.to_string())])
)
);
}
} else {
descendant = descendant.strip_prefix(context.root)?.to_path_buf();
@ -229,7 +236,7 @@ fn copy_direntry(
// exist, ...
if source_absolute.is_dir() && !local_to_target.exists() {
return if target_is_file {
Err("cannot overwrite non-directory with directory".into())
Err(get_message("cp-error-cannot-overwrite-non-directory-with-directory").into())
} else {
build_dir(&local_to_target, false, options, Some(&source_absolute))?;
if options.verbose {
@ -269,8 +276,14 @@ fn copy_direntry(
CpError::IoErrContext(e, _) if e.kind() == io::ErrorKind::PermissionDenied => {
show!(uio_error!(
e,
"cannot open {} for reading",
source_relative.quote(),
"{}",
get_message_with_args(
"cp-error-cannot-open-for-reading",
HashMap::from([(
"source".to_string(),
source_relative.quote().to_string()
)])
),
));
}
e => return Err(e),
@ -315,15 +328,24 @@ pub(crate) fn copy_directory(
}
if !options.recursive {
return Err(format!("-r not specified; omitting directory {}", root.quote()).into());
return Err(get_message_with_args(
"cp-error-omitting-directory",
HashMap::from([("dir".to_string(), root.quote().to_string())]),
)
.into());
}
// check if root is a prefix of target
if path_has_prefix(target, root)? {
return Err(format!(
"cannot copy a directory, {}, into itself, {}",
root.quote(),
target.join(root.file_name().unwrap()).quote()
return Err(get_message_with_args(
"cp-error-cannot-copy-directory-into-itself",
HashMap::from([
("source".to_string(), root.quote().to_string()),
(
"dest".to_string(),
target.join(root.file_name().unwrap()).quote().to_string(),
),
]),
)
.into());
}
@ -368,7 +390,13 @@ pub(crate) fn copy_directory(
// the target directory.
let context = match Context::new(root, target) {
Ok(c) => c,
Err(e) => return Err(format!("failed to get current directory {e}").into()),
Err(e) => {
return Err(get_message_with_args(
"cp-error-failed-get-current-dir",
HashMap::from([("error".to_string(), e.to_string())]),
)
.into());
}
};
// The directory we were in during the previous iteration

View file

@ -41,7 +41,7 @@ use uucore::{
};
use crate::copydir::copy_directory;
use uucore::locale::get_message;
use uucore::locale::{get_message, get_message_with_args};
mod copydir;
mod platform;
@ -62,7 +62,7 @@ pub enum CpError {
/// Represents the state when a non-fatal error has occurred
/// and not all files were copied.
#[error("Not all files were copied")]
#[error("{}", get_message("cp-error-not-all-files-copied"))]
NotAllFilesCopied,
/// Simple walkdir::Error wrapper
@ -80,21 +80,21 @@ pub enum CpError {
#[error("Skipped copying file (exit with error = {0})")]
Skipped(bool),
/// Result of a skipped file
/// Invalid argument error
#[error("{0}")]
InvalidArgument(String),
/// All standard options are included as an an implementation
/// path, but those that are not implemented yet should return
/// a NotImplemented error.
#[error("Option '{0}' not yet implemented.")]
#[error("{}", get_message_with_args("cp-error-option-not-implemented", HashMap::from([("option".to_string(), 0.to_string())])))]
NotImplemented(String),
/// Invalid arguments to backup
#[error(transparent)]
Backup(#[from] BackupError),
#[error("'{}' is not a directory", .0.display())]
#[error("{}", get_message_with_args("cp-error-not-a-directory", HashMap::from([("path".to_string(), .0.quote().to_string())])))]
NotADirectory(PathBuf),
}
@ -118,9 +118,14 @@ impl Display for BackupError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"cp: {}\nTry '{} --help' for more information.",
self.0,
uucore::execution_phrase()
"{}",
get_message_with_args(
"cp-error-backup-format",
HashMap::from([
("error".to_string(), self.0.clone()),
("exec".to_string(), uucore::execution_phrase().to_string())
])
)
)
}
}
@ -415,28 +420,30 @@ struct CopyDebug {
sparse_detection: SparseDebug,
}
impl OffloadReflinkDebug {
fn to_string(&self) -> &'static str {
match self {
Self::No => "no",
Self::Yes => "yes",
Self::Avoided => "avoided",
Self::Unsupported => "unsupported",
Self::Unknown => "unknown",
}
impl Display for OffloadReflinkDebug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match self {
Self::No => get_message("cp-debug-enum-no"),
Self::Yes => get_message("cp-debug-enum-yes"),
Self::Avoided => get_message("cp-debug-enum-avoided"),
Self::Unsupported => get_message("cp-debug-enum-unsupported"),
Self::Unknown => get_message("cp-debug-enum-unknown"),
};
write!(f, "{}", msg)
}
}
impl SparseDebug {
fn to_string(&self) -> &'static str {
match self {
Self::No => "no",
Self::Zeros => "zeros",
Self::SeekHole => "SEEK_HOLE",
Self::SeekHoleZeros => "SEEK_HOLE + zeros",
Self::Unsupported => "unsupported",
Self::Unknown => "unknown",
}
impl Display for SparseDebug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match self {
Self::No => get_message("cp-debug-enum-no"),
Self::Zeros => get_message("cp-debug-enum-zeros"),
Self::SeekHole => get_message("cp-debug-enum-seek-hole"),
Self::SeekHoleZeros => get_message("cp-debug-enum-seek-hole-zeros"),
Self::Unsupported => get_message("cp-debug-enum-unsupported"),
Self::Unknown => get_message("cp-debug-enum-unknown"),
};
write!(f, "{}", msg)
}
}
@ -445,10 +452,18 @@ impl SparseDebug {
/// It prints the debug information of the offload, reflink, and sparse detection actions.
fn show_debug(copy_debug: &CopyDebug) {
println!(
"copy offload: {}, reflink: {}, sparse detection: {}",
copy_debug.offload.to_string(),
copy_debug.reflink.to_string(),
copy_debug.sparse_detection.to_string(),
"{}",
get_message_with_args(
"cp-debug-copy-offload",
HashMap::from([
("offload".to_string(), copy_debug.offload.to_string()),
("reflink".to_string(), copy_debug.reflink.to_string()),
(
"sparse".to_string(),
copy_debug.sparse_detection.to_string()
),
])
)
);
}
@ -537,14 +552,14 @@ pub fn uu_app() -> Command {
.value_name(options::TARGET_DIRECTORY)
.value_hint(clap::ValueHint::DirPath)
.value_parser(ValueParser::path_buf())
.help("copy all SOURCE arguments into target-directory"),
.help(get_message("cp-help-target-directory")),
)
.arg(
Arg::new(options::NO_TARGET_DIRECTORY)
.short('T')
.long(options::NO_TARGET_DIRECTORY)
.conflicts_with(options::TARGET_DIRECTORY)
.help("Treat DEST as a regular file and not a directory")
.help(get_message("cp-help-no-target-directory"))
.action(ArgAction::SetTrue),
)
.arg(
@ -552,7 +567,7 @@ pub fn uu_app() -> Command {
.short('i')
.long(options::INTERACTIVE)
.overrides_with(options::NO_CLOBBER)
.help("ask before overwriting files")
.help(get_message("cp-help-interactive"))
.action(ArgAction::SetTrue),
)
.arg(
@ -560,7 +575,7 @@ pub fn uu_app() -> Command {
.short('l')
.long(options::LINK)
.overrides_with_all(MODE_ARGS)
.help("hard-link files instead of copying")
.help(get_message("cp-help-link"))
.action(ArgAction::SetTrue),
)
.arg(
@ -568,7 +583,7 @@ pub fn uu_app() -> Command {
.short('n')
.long(options::NO_CLOBBER)
.overrides_with(options::INTERACTIVE)
.help("don't overwrite a file that already exists")
.help(get_message("cp-help-no-clobber"))
.action(ArgAction::SetTrue),
)
.arg(
@ -577,26 +592,26 @@ pub fn uu_app() -> Command {
.visible_short_alias('r')
.long(options::RECURSIVE)
// --archive sets this option
.help("copy directories recursively")
.help(get_message("cp-help-recursive"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::STRIP_TRAILING_SLASHES)
.long(options::STRIP_TRAILING_SLASHES)
.help("remove any trailing slashes from each SOURCE argument")
.help(get_message("cp-help-strip-trailing-slashes"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::DEBUG)
.long(options::DEBUG)
.help("explain how a file is copied. Implies -v")
.help(get_message("cp-help-debug"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::VERBOSE)
.short('v')
.long(options::VERBOSE)
.help("explicitly state what is being done")
.help(get_message("cp-help-verbose"))
.action(ArgAction::SetTrue),
)
.arg(
@ -604,29 +619,21 @@ pub fn uu_app() -> Command {
.short('s')
.long(options::SYMBOLIC_LINK)
.overrides_with_all(MODE_ARGS)
.help("make symbolic links instead of copying")
.help(get_message("cp-help-symbolic-link"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::FORCE)
.short('f')
.long(options::FORCE)
.help(
"if an existing destination file cannot be opened, remove it and \
try again (this option is ignored when the -n option is also used). \
Currently not implemented for Windows.",
)
.help(get_message("cp-help-force"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::REMOVE_DESTINATION)
.long(options::REMOVE_DESTINATION)
.overrides_with(options::FORCE)
.help(
"remove each existing destination file before attempting to open it \
(contrast with --force). On Windows, currently only works for \
writeable files.",
)
.help(get_message("cp-help-remove-destination"))
.action(ArgAction::SetTrue),
)
.arg(backup_control::arguments::backup())
@ -643,13 +650,13 @@ pub fn uu_app() -> Command {
.default_missing_value("always")
.value_parser(ShortcutValueParser::new(["auto", "always", "never"]))
.num_args(0..=1)
.help("control clone/CoW copies. See below"),
.help(get_message("cp-help-reflink")),
)
.arg(
Arg::new(options::ATTRIBUTES_ONLY)
.long(options::ATTRIBUTES_ONLY)
.overrides_with_all(MODE_ARGS)
.help("Don't copy the file data, just the attributes")
.help(get_message("cp-help-attributes-only"))
.action(ArgAction::SetTrue),
)
.arg(
@ -664,16 +671,13 @@ pub fn uu_app() -> Command {
.default_missing_value(PRESERVE_DEFAULT_VALUES)
// -d sets this option
// --archive sets this option
.help(
"Preserve the specified attributes (default: mode, ownership (unix only), \
timestamps), if possible additional attributes: context, links, xattr, all",
),
.help(get_message("cp-help-preserve")),
)
.arg(
Arg::new(options::PRESERVE_DEFAULT_ATTRIBUTES)
.short('p')
.long(options::PRESERVE_DEFAULT_ATTRIBUTES)
.help("same as --preserve=mode,ownership(unix only),timestamps")
.help(get_message("cp-help-preserve-default"))
.action(ArgAction::SetTrue),
)
.arg(
@ -685,13 +689,13 @@ pub fn uu_app() -> Command {
.num_args(0..)
.require_equals(true)
.value_name("ATTR_LIST")
.help("don't preserve the specified attributes"),
.help(get_message("cp-help-no-preserve")),
)
.arg(
Arg::new(options::PARENTS)
.long(options::PARENTS)
.alias(options::PARENT)
.help("use full source file name under DIRECTORY")
.help(get_message("cp-help-parents"))
.action(ArgAction::SetTrue),
)
.arg(
@ -700,7 +704,7 @@ pub fn uu_app() -> Command {
.long(options::NO_DEREFERENCE)
.overrides_with(options::DEREFERENCE)
// -d sets this option
.help("never follow symbolic links in SOURCE")
.help(get_message("cp-help-no-dereference"))
.action(ArgAction::SetTrue),
)
.arg(
@ -708,33 +712,33 @@ pub fn uu_app() -> Command {
.short('L')
.long(options::DEREFERENCE)
.overrides_with(options::NO_DEREFERENCE)
.help("always follow symbolic links in SOURCE")
.help(get_message("cp-help-dereference"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::CLI_SYMBOLIC_LINKS)
.short('H')
.help("follow command-line symbolic links in SOURCE")
.help(get_message("cp-help-cli-symbolic-links"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::ARCHIVE)
.short('a')
.long(options::ARCHIVE)
.help("Same as -dR --preserve=all")
.help(get_message("cp-help-archive"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::NO_DEREFERENCE_PRESERVE_LINKS)
.short('d')
.help("same as --no-dereference --preserve=links")
.help(get_message("cp-help-no-dereference-preserve-links"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::ONE_FILE_SYSTEM)
.short('x')
.long(options::ONE_FILE_SYSTEM)
.help("stay on this file system")
.help(get_message("cp-help-one-file-system"))
.action(ArgAction::SetTrue),
)
.arg(
@ -742,12 +746,12 @@ pub fn uu_app() -> Command {
.long(options::SPARSE)
.value_name("WHEN")
.value_parser(ShortcutValueParser::new(["never", "auto", "always"]))
.help("control creation of sparse files. See below"),
.help(get_message("cp-help-sparse")),
)
.arg(
Arg::new(options::SELINUX)
.short('Z')
.help("set SELinux security context of destination file to default type")
.help(get_message("cp-help-selinux"))
.action(ArgAction::SetTrue),
)
.arg(
@ -755,10 +759,7 @@ pub fn uu_app() -> Command {
.long(options::CONTEXT)
.value_name("CTX")
.value_parser(value_parser!(String))
.help(
"like -Z, or if CTX is specified then set the SELinux or SMACK security \
context to CTX",
)
.help(get_message("cp-help-context"))
.num_args(0..=1)
.require_equals(true)
.default_missing_value(""),
@ -770,17 +771,14 @@ pub fn uu_app() -> Command {
.long(options::PROGRESS_BAR)
.short('g')
.action(ArgAction::SetTrue)
.help(
"Display a progress bar. \n\
Note: this feature is not supported by GNU coreutils.",
),
.help(get_message("cp-help-progress")),
)
// TODO: implement the following args
.arg(
Arg::new(options::COPY_CONTENTS)
.long(options::COPY_CONTENTS)
.overrides_with(options::ATTRIBUTES_ONLY)
.help("NotImplemented: copy contents of special files when recursive")
.help(get_message("cp-help-copy-contents"))
.action(ArgAction::SetTrue),
)
// END TODO
@ -803,7 +801,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if options.overwrite == OverwriteMode::NoClobber && options.backup != BackupMode::None {
return Err(UUsageError::new(
EXIT_ERR,
"options --backup and --no-clobber are mutually exclusive",
get_message("cp-error-backup-mutually-exclusive"),
));
}
@ -984,9 +982,9 @@ impl Attributes {
"link" | "links" => &mut new.links,
"xattr" => &mut new.xattr,
_ => {
return Err(CpError::InvalidArgument(format!(
"invalid attribute {}",
value.quote()
return Err(CpError::InvalidArgument(get_message_with_args(
"cp-error-invalid-attribute",
HashMap::from([("value".to_string(), value.quote().to_string())]),
)));
}
};
@ -1030,7 +1028,7 @@ impl Options {
.is_some_and(|v| v == "none" || v == "none-fail")
{
return Err(CpError::InvalidArgument(
"--backup is mutually exclusive with -n or --update=none-fail".to_string(),
get_message("cp-error-invalid-backup-argument").to_string(),
));
}
@ -1134,7 +1132,7 @@ impl Options {
#[cfg(not(feature = "selinux"))]
if let Preserve::Yes { required } = attributes.context {
let selinux_disabled_error =
CpError::Error("SELinux was not enabled during the compile time!".to_owned());
CpError::Error(get_message("cp-error-selinux-not-enabled"));
if required {
return Err(selinux_disabled_error);
}
@ -1176,9 +1174,12 @@ impl Options {
"auto" => ReflinkMode::Auto,
"never" => ReflinkMode::Never,
value => {
return Err(CpError::InvalidArgument(format!(
"invalid argument {} for \'reflink\'",
value.quote()
return Err(CpError::InvalidArgument(get_message_with_args(
"cp-error-invalid-argument",
HashMap::from([
("arg".to_string(), value.quote().to_string()),
("option".to_string(), "reflink".to_string()),
]),
)));
}
}
@ -1193,8 +1194,12 @@ impl Options {
"auto" => SparseMode::Auto,
"never" => SparseMode::Never,
_ => {
return Err(CpError::InvalidArgument(format!(
"invalid argument {val} for \'sparse\'"
return Err(CpError::InvalidArgument(get_message_with_args(
"cp-error-invalid-argument",
HashMap::from([
("arg".to_string(), val.to_string()),
("option".to_string(), "sparse".to_string()),
]),
)));
}
}
@ -1269,12 +1274,15 @@ fn parse_path_args(
) -> CopyResult<(Vec<PathBuf>, PathBuf)> {
if paths.is_empty() {
// No files specified
return Err("missing file operand".into());
return Err(get_message("cp-error-missing-file-operand").into());
} else if paths.len() == 1 && options.target_dir.is_none() {
// Only one file specified
return Err(format!(
"missing destination file operand after {}",
paths[0].display().to_string().quote()
return Err(get_message_with_args(
"cp-error-missing-destination-operand",
HashMap::from([(
"source".to_string(),
paths[0].display().to_string().quote().to_string(),
)]),
)
.into());
}
@ -1282,7 +1290,14 @@ fn parse_path_args(
// Return an error if the user requested to copy more than one
// file source to a file target
if options.no_target_dir && options.target_dir.is_none() && paths.len() > 2 {
return Err(format!("extra operand {:}", paths[2].display().to_string().quote()).into());
return Err(get_message_with_args(
"cp-error-extra-operand",
HashMap::from([(
"operand".to_string(),
paths[2].display().to_string().quote().to_string(),
)]),
)
.into());
}
let target = match options.target_dir {
@ -1377,10 +1392,14 @@ pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult
} else {
"file"
};
show_warning!(
"source {file_type} {} specified more than once",
source.quote()
let msg = get_message_with_args(
"cp-warning-source-specified-more-than-once",
HashMap::from([
("file_type".to_string(), file_type.to_string()),
("source".to_string(), source.quote().to_string()),
]),
);
show_warning!("{}", msg);
} else {
let dest = construct_dest_path(source, target, target_type, options)
.unwrap_or_else(|_| target.to_path_buf());
@ -1395,10 +1414,12 @@ pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult
// There is already a file and it isn't a symlink (managed in a different place)
if copied_destinations.contains(&dest) && options.backup != BackupMode::Numbered {
// If the target file was already created in this cp call, do not overwrite
return Err(CpError::Error(format!(
"will not overwrite just-created '{}' with '{}'",
dest.display(),
source.display()
return Err(CpError::Error(get_message_with_args(
"cp-error-will-not-overwrite-just-created",
HashMap::from([
("dest".to_string(), dest.quote().to_string()),
("source".to_string(), source.quote().to_string()),
]),
)));
}
}
@ -1442,15 +1463,15 @@ fn construct_dest_path(
options: &Options,
) -> CopyResult<PathBuf> {
if options.no_target_dir && target.is_dir() {
return Err(format!(
"cannot overwrite directory {} with non-directory",
target.quote()
return Err(get_message_with_args(
"cp-error-cannot-overwrite-directory-with-non-directory",
HashMap::from([("dir".to_string(), target.quote().to_string())]),
)
.into());
}
if options.parents && !target.is_dir() {
return Err("with --parents, the destination must be a directory".into());
return Err(get_message("cp-error-with-parents-dest-must-be-dir").into());
}
Ok(match target_type {
@ -1575,7 +1596,13 @@ impl OverwriteMode {
match *self {
Self::NoClobber => {
if debug {
println!("skipped {}", path.quote());
println!(
"{}",
get_message_with_args(
"cp-debug-skipped",
HashMap::from([("path".to_string(), path.quote().to_string())])
)
);
}
Err(CpError::Skipped(false))
}
@ -1583,12 +1610,17 @@ impl OverwriteMode {
let prompt_yes_result = if let Some((octal, human_readable)) =
file_mode_for_interactive_overwrite(path)
{
prompt_yes!(
"replace {}, overriding mode {octal} ({human_readable})?",
path.quote()
)
let prompt_msg = get_message_with_args(
"cp-prompt-overwrite-with-mode",
HashMap::from([("path".to_string(), path.quote().to_string())]),
);
prompt_yes!("{} {octal} ({human_readable})?", prompt_msg)
} else {
prompt_yes!("overwrite {}?", path.quote())
let prompt_msg = get_message_with_args(
"cp-prompt-overwrite",
HashMap::from([("path".to_string(), path.quote().to_string())]),
);
prompt_yes!("{}", prompt_msg)
};
if prompt_yes_result {
@ -1621,8 +1653,8 @@ fn handle_preserve<F: Fn() -> CopyResult<()>>(p: &Preserve, f: F) -> CopyResult<
}
/// Copies extended attributes (xattrs) from `source` to `dest`, ensuring that `dest` is temporarily
/// user-writable if needed and restoring its original permissions afterward. This avoids Operation
/// not permitted errors on read-only files. Returns an error if permission or metadata operations fail,
/// user-writable if needed and restoring its original permissions afterward. This avoids "Operation
/// not permitted" errors on read-only files. Returns an error if permission or metadata operations fail,
/// or if xattr copying fails.
#[cfg(all(unix, not(target_os = "android")))]
fn copy_extended_attrs(source: &Path, dest: &Path) -> CopyResult<()> {
@ -1730,16 +1762,19 @@ pub(crate) fn copy_attributes(
if let Ok(context) = selinux::SecurityContext::of_path(source, false, false) {
if let Some(context) = context {
if let Err(e) = context.set_for_path(dest, false, false) {
return Err(CpError::Error(format!(
"failed to set the security context of {}: {e}",
dest.display()
return Err(CpError::Error(get_message_with_args(
"cp-error-selinux-set-context",
HashMap::from([
("path".to_string(), dest.display().to_string()),
("error".to_string(), e.to_string()),
]),
)));
}
}
} else {
return Err(CpError::Error(format!(
"failed to get security context of {}",
source.display()
return Err(CpError::Error(get_message_with_args(
"cp-error-selinux-get-context",
HashMap::from([("path".to_string(), source.display().to_string())]),
)));
}
Ok(())
@ -1780,10 +1815,24 @@ fn symlink_file(
std::os::unix::fs::symlink(source, dest).map_err(|e| {
CpError::IoErrContext(
e,
format!(
"cannot create symlink {} to {}",
get_filename(dest).unwrap_or("invalid file name").quote(),
get_filename(source).unwrap_or("invalid file name").quote()
get_message_with_args(
"cp-error-cannot-create-symlink",
HashMap::from([
(
"dest".to_string(),
get_filename(dest)
.unwrap_or("invalid file name")
.quote()
.to_string(),
),
(
"source".to_string(),
get_filename(source)
.unwrap_or("invalid file name")
.quote()
.to_string(),
),
]),
),
)
})?;
@ -1793,10 +1842,24 @@ fn symlink_file(
std::os::windows::fs::symlink_file(source, dest).map_err(|e| {
CpError::IoErrContext(
e,
format!(
"cannot create symlink {} to {}",
get_filename(dest).unwrap_or("invalid file name").quote(),
get_filename(source).unwrap_or("invalid file name").quote()
get_message_with_args(
"cp-error-cannot-create-symlink",
HashMap::from([
(
"dest".to_string(),
get_filename(dest)
.unwrap_or("invalid file name")
.quote()
.to_string(),
),
(
"source".to_string(),
get_filename(source)
.unwrap_or("invalid file name")
.quote()
.to_string(),
),
]),
),
)
})?;
@ -1887,7 +1950,14 @@ fn handle_existing_dest(
// Disallow copying a file to itself, unless `--force` and
// `--backup` are both specified.
if is_forbidden_to_copy_to_same_file(source, dest, options, source_in_command_line) {
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
return Err(get_message_with_args(
"cp-error-same-file",
HashMap::from([
("source".to_string(), source.quote().to_string()),
("dest".to_string(), dest.quote().to_string()),
]),
)
.into());
}
if options.update == UpdateMode::None {
@ -1905,10 +1975,12 @@ fn handle_existing_dest(
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
if let Some(backup_path) = backup_path {
if paths_refer_to_same_file(source, &backup_path, true) {
return Err(format!(
"backing up {} might destroy source; {} not copied",
dest.quote(),
source.quote()
return Err(get_message_with_args(
"cp-error-backing-up-destroy-source",
HashMap::from([
("dest".to_string(), dest.quote().to_string()),
("source".to_string(), source.quote().to_string()),
]),
)
.into());
}
@ -2063,7 +2135,16 @@ fn print_paths(parents: bool, source: &Path, dest: &Path) {
// a/b -> d/a/b
//
for (x, y) in aligned_ancestors(source, dest) {
println!("{} -> {}", x.display(), y.display());
println!(
"{}",
get_message_with_args(
"cp-verbose-created-directory",
HashMap::from([
("source".to_string(), x.display().to_string()),
("dest".to_string(), y.display().to_string())
])
)
);
}
}
@ -2117,10 +2198,24 @@ fn handle_copy_mode(
.map_err(|e| {
CpError::IoErrContext(
e,
format!(
"cannot create hard link {} to {}",
get_filename(dest).unwrap_or("invalid file name").quote(),
get_filename(source).unwrap_or("invalid file name").quote()
get_message_with_args(
"cp-error-cannot-create-hard-link",
HashMap::from([
(
"dest".to_string(),
get_filename(dest)
.unwrap_or("invalid file name")
.quote()
.to_string(),
),
(
"source".to_string(),
get_filename(source)
.unwrap_or("invalid file name")
.quote()
.to_string(),
),
]),
),
)
})?;
@ -2168,9 +2263,9 @@ fn handle_copy_mode(
return Ok(PerformedAction::Skipped);
}
UpdateMode::NoneFail => {
return Err(CpError::Error(format!(
"not replacing '{}'",
dest.display()
return Err(CpError::Error(get_message_with_args(
"cp-error-not-replacing",
HashMap::from([("file".to_string(), dest.quote().to_string())]),
)));
}
UpdateMode::IfOlder => {
@ -2296,20 +2391,24 @@ fn copy_file(
.map(|info| symlinked_files.contains(&info))
.unwrap_or(false)
{
return Err(CpError::Error(format!(
"will not copy '{}' through just-created symlink '{}'",
source.display(),
dest.display()
return Err(CpError::Error(get_message_with_args(
"cp-error-will-not-copy-through-symlink",
HashMap::from([
("source".to_string(), source.quote().to_string()),
("dest".to_string(), dest.quote().to_string()),
]),
)));
}
// Fail if cp tries to copy two sources of the same name into a single symlink
// Example: "cp file1 dir1/file1 tmp" where "tmp" is a directory containing a symlink "file1" pointing to a file named "foo".
// foo will contain the contents of "file1" and "dir1/file1" will not be copied over to "tmp/file1"
if copied_destinations.contains(dest) {
return Err(CpError::Error(format!(
"will not copy '{}' through just-created symlink '{}'",
source.display(),
dest.display()
return Err(CpError::Error(get_message_with_args(
"cp-error-will-not-copy-through-symlink",
HashMap::from([
("source".to_string(), source.quote().to_string()),
("dest".to_string(), dest.quote().to_string()),
]),
)));
}
@ -2323,9 +2422,9 @@ fn copy_file(
&& !is_symlink_loop(dest)
&& std::env::var_os("POSIXLY_CORRECT").is_none()
{
return Err(CpError::Error(format!(
"not writing through dangling symlink '{}'",
dest.display()
return Err(CpError::Error(get_message_with_args(
"cp-error-not-writing-dangling-symlink",
HashMap::from([("dest".to_string(), dest.quote().to_string())]),
)));
}
if paths_refer_to_same_file(source, dest, true)
@ -2392,9 +2491,9 @@ fn copy_file(
OverwriteMode::Clobber(ClobberMode::RemoveDestination)
)
{
return Err(format!(
"cannot change attribute {}: Source file is a non regular file",
dest.quote()
return Err(get_message_with_args(
"cp-error-cannot-change-attribute",
HashMap::from([("dest".to_string(), dest.quote().to_string())]),
)
.into());
}
@ -2430,7 +2529,10 @@ fn copy_file(
// this is just for gnu tests compatibility
result.map_err(|err| {
if err.to_string().contains("No such file or directory") {
return format!("cannot stat {}: No such file or directory", source.quote());
return get_message_with_args(
"cp-error-cannot-stat",
HashMap::from([("source".to_string(), source.quote().to_string())]),
);
}
err.to_string()
})?
@ -2499,7 +2601,10 @@ fn copy_file(
if let Err(e) =
uucore::selinux::set_selinux_security_context(dest, options.context.as_ref())
{
return Err(CpError::Error(format!("SELinux error: {}", e)));
return Err(CpError::Error(get_message_with_args(
"cp-error-selinux-error",
HashMap::from([("error".to_string(), e.to_string())]),
)));
}
}
@ -2617,7 +2722,13 @@ fn copy_fifo(dest: &Path, overwrite: OverwriteMode, debug: bool) -> CopyResult<(
fs::remove_file(dest)?;
}
make_fifo(dest).map_err(|_| format!("cannot create fifo {}: File exists", dest.quote()).into())
make_fifo(dest).map_err(|_| {
get_message_with_args(
"cp-error-cannot-create-fifo",
HashMap::from([("path".to_string(), dest.quote().to_string())]),
)
.into()
})
}
fn copy_link(
@ -2638,12 +2749,14 @@ fn copy_link(
/// Generate an error message if `target` is not the correct `target_type`
pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> {
match (target_type, target.is_dir()) {
(&TargetType::Directory, false) => {
Err(format!("target: {} is not a directory", target.quote()).into())
}
(&TargetType::File, true) => Err(format!(
"cannot overwrite directory {} with non-directory",
target.quote()
(&TargetType::Directory, false) => Err(get_message_with_args(
"cp-error-target-not-directory",
HashMap::from([("target".to_string(), target.quote().to_string())]),
)
.into()),
(&TargetType::File, true) => Err(get_message_with_args(
"cp-error-cannot-overwrite-directory-with-non-directory",
HashMap::from([("dir".to_string(), target.quote().to_string())]),
)
.into()),
_ => Ok(()),

View file

@ -13,7 +13,7 @@ use std::os::unix::fs::{FileTypeExt, OpenOptionsExt};
use std::os::unix::io::AsRawFd;
use std::path::Path;
use uucore::buf_copy;
use uucore::locale::get_message;
use uucore::mode::get_umask;
use crate::{
@ -401,7 +401,7 @@ pub(crate) fn copy_on_write(
clone(source, dest, CloneFallback::Error)
}
(ReflinkMode::Always, _) => {
return Err("`--reflink=always` can be used only with --sparse=auto".into());
return Err(get_message("cp-error-reflink-always-sparse-auto").into());
}
};
result.map_err(|e| CpError::IoErrContext(e, context.to_owned()))?;

View file

@ -3,6 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore reflink
use std::collections::HashMap;
use std::ffi::CString;
use std::fs::{self, File, OpenOptions};
use std::os::unix::ffi::OsStrExt;
@ -10,6 +11,7 @@ use std::os::unix::fs::OpenOptionsExt;
use std::path::Path;
use uucore::buf_copy;
use uucore::locale::{get_message, get_message_with_args};
use uucore::mode::get_umask;
use crate::{
@ -30,7 +32,9 @@ pub(crate) fn copy_on_write(
source_is_stream: bool,
) -> CopyResult<CopyDebug> {
if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into());
return Err(get_message("cp-error-sparse-not-supported")
.to_string()
.into());
}
let mut copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unknown,
@ -85,10 +89,13 @@ pub(crate) fn copy_on_write(
// support COW).
match reflink_mode {
ReflinkMode::Always => {
return Err(format!(
"failed to clone {} from {}: {error}",
source.display(),
dest.display()
return Err(get_message_with_args(
"cp-error-failed-to-clone",
HashMap::from([
("source".to_string(), source.display().to_string()),
("dest".to_string(), dest.display().to_string()),
("error".to_string(), error.to_string()),
]),
)
.into());
}

View file

@ -5,6 +5,7 @@
// spell-checker:ignore reflink
use std::fs;
use std::path::Path;
use uucore::locale::get_message;
use crate::{
CopyDebug, CopyResult, CpError, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode,
@ -19,12 +20,14 @@ pub(crate) fn copy_on_write(
context: &str,
) -> CopyResult<CopyDebug> {
if reflink_mode != ReflinkMode::Never {
return Err("--reflink is only supported on linux and macOS"
return Err(get_message("cp-error-reflink-not-supported")
.to_string()
.into());
}
if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into());
return Err(get_message("cp-error-sparse-not-supported")
.to_string()
.into());
}
let copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unsupported,

View file

@ -8,6 +8,7 @@ use std::os::unix::fs::OpenOptionsExt;
use std::path::Path;
use uucore::buf_copy;
use uucore::locale::get_message;
use uucore::mode::get_umask;
use crate::{
@ -25,12 +26,14 @@ pub(crate) fn copy_on_write(
source_is_stream: bool,
) -> CopyResult<CopyDebug> {
if reflink_mode != ReflinkMode::Never {
return Err("--reflink is only supported on linux and macOS"
return Err(get_message("cp-error-reflink-not-supported")
.to_string()
.into());
}
if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into());
return Err(get_message("cp-error-sparse-not-supported")
.to_string()
.into());
}
let copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unsupported,