mirror of
https://github.com/uutils/coreutils.git
synced 2025-07-07 21:45:01 +00:00
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
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:
commit
1675c3e981
8 changed files with 553 additions and 184 deletions
|
@ -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 they’re 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
116
src/uu/cp/locales/fr-FR.ftl
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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(()),
|
||||
|
|
|
@ -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()))?;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue