mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
l10n: port dd for translation + add french
This commit is contained in:
parent
411874f34b
commit
688215725c
6 changed files with 435 additions and 95 deletions
|
@ -113,3 +113,48 @@ dd-after-help = ### Operands
|
|||
- nocache : request that OS drop cache.
|
||||
- noctty : do not assign a controlling tty.
|
||||
- nofollow : do not follow system links.
|
||||
|
||||
# Error messages
|
||||
dd-error-failed-to-open = failed to open { $path }
|
||||
dd-error-write-error = write error
|
||||
dd-error-failed-to-seek = failed to seek in output file
|
||||
dd-error-io-error = IO error
|
||||
dd-error-cannot-skip-offset = '{ $file }': cannot skip to specified offset
|
||||
dd-error-cannot-skip-invalid = '{ $file }': cannot skip: Invalid argument
|
||||
dd-error-cannot-seek-invalid = '{ $output }': cannot seek: Invalid argument
|
||||
dd-error-not-directory = setting flags for '{ $file }': Not a directory
|
||||
dd-error-failed-discard-cache-input = failed to discard cache for: 'standard input'
|
||||
dd-error-failed-discard-cache-output = failed to discard cache for: 'standard output'
|
||||
|
||||
# Parse errors
|
||||
dd-error-unrecognized-operand = Unrecognized operand '{ $operand }'
|
||||
dd-error-multiple-format-table = Only one of conv=ascii conv=ebcdic or conv=ibm may be specified
|
||||
dd-error-multiple-case = Only one of conv=lcase or conv=ucase may be specified
|
||||
dd-error-multiple-block = Only one of conv=block or conv=unblock may be specified
|
||||
dd-error-multiple-excl = Only one ov conv=excl or conv=nocreat may be specified
|
||||
dd-error-invalid-flag = invalid input flag: ‘{ $flag }’
|
||||
Try '{ $cmd } --help' for more information.
|
||||
dd-error-conv-flag-no-match = Unrecognized conv=CONV -> { $flag }
|
||||
dd-error-multiplier-parse-failure = invalid number: '{ $input }'
|
||||
dd-error-multiplier-overflow = Multiplier string would overflow on current system -> { $input }
|
||||
dd-error-block-without-cbs = conv=block or conv=unblock specified without cbs=N
|
||||
dd-error-status-not-recognized = status=LEVEL not recognized -> { $level }
|
||||
dd-error-unimplemented = feature not implemented on this system -> { $feature }
|
||||
dd-error-bs-out-of-range = { $param }=N cannot fit into memory
|
||||
dd-error-invalid-number = invalid number: ‘{ $input }’
|
||||
|
||||
# Progress messages
|
||||
dd-progress-records-in = { $complete }+{ $partial } records in
|
||||
dd-progress-records-out = { $complete }+{ $partial } records out
|
||||
dd-progress-truncated-record = { $count ->
|
||||
[one] { $count } truncated record
|
||||
*[other] { $count } truncated records
|
||||
}
|
||||
dd-progress-byte-copied = { $bytes } byte copied, { $duration } s, { $rate }/s
|
||||
dd-progress-bytes-copied = { $bytes } bytes copied, { $duration } s, { $rate }/s
|
||||
dd-progress-bytes-copied-si = { $bytes } bytes ({ $si }) copied, { $duration } s, { $rate }/s
|
||||
dd-progress-bytes-copied-si-iec = { $bytes } bytes ({ $si }, { $iec }) copied, { $duration } s, { $rate }/s
|
||||
|
||||
# Warnings
|
||||
dd-warning-zero-multiplier = { $zero } is a zero multiplier; use { $alternative } if that is intended
|
||||
dd-warning-signal-handler = Internal dd Warning: Unable to register signal handler
|
||||
|
|
160
src/uu/dd/locales/fr-FR.ftl
Normal file
160
src/uu/dd/locales/fr-FR.ftl
Normal file
|
@ -0,0 +1,160 @@
|
|||
dd-about = Copier, et optionnellement convertir, une ressource du système de fichiers
|
||||
dd-usage = dd [OPÉRANDE]...
|
||||
dd OPTION
|
||||
dd-after-help = ### Opérandes
|
||||
|
||||
- bs=OCTETS : lire et écrire jusqu'à OCTETS octets à la fois (par défaut : 512) ;
|
||||
remplace ibs et obs.
|
||||
- cbs=OCTETS : la 'taille de bloc de conversion' en octets. S'applique aux
|
||||
opérations conv=block et conv=unblock.
|
||||
- conv=CONVS : une liste séparée par des virgules d'options de conversion ou (pour des
|
||||
raisons historiques) d'indicateurs de fichier.
|
||||
- count=N : arrêter la lecture de l'entrée après N opérations de lecture de taille ibs
|
||||
plutôt que de continuer jusqu'à EOF. Voir iflag=count_bytes si l'arrêt après N octets
|
||||
est préféré
|
||||
- ibs=N : la taille du tampon utilisé pour les lectures (par défaut : 512)
|
||||
- if=FICHIER : le fichier utilisé pour l'entrée. Quand non spécifié, stdin est utilisé à la place
|
||||
- iflag=INDICATEURS : une liste séparée par des virgules d'indicateurs d'entrée qui spécifient comment
|
||||
la source d'entrée est traitée. INDICATEURS peut être n'importe lequel des indicateurs d'entrée ou
|
||||
indicateurs généraux spécifiés ci-dessous.
|
||||
- skip=N (ou iseek=N) : ignorer N enregistrements de taille ibs dans l'entrée avant de commencer
|
||||
les opérations de copie/conversion. Voir iflag=seek_bytes si la recherche de N octets est préférée.
|
||||
- obs=N : la taille du tampon utilisé pour les écritures (par défaut : 512)
|
||||
- of=FICHIER : le fichier utilisé pour la sortie. Quand non spécifié, stdout est utilisé
|
||||
à la place
|
||||
- oflag=INDICATEURS : liste séparée par des virgules d'indicateurs de sortie qui spécifient comment la
|
||||
source de sortie est traitée. INDICATEURS peut être n'importe lequel des indicateurs de sortie ou
|
||||
indicateurs généraux spécifiés ci-dessous
|
||||
- seek=N (ou oseek=N) : recherche N enregistrements de taille obs dans la sortie avant de
|
||||
commencer les opérations de copie/conversion. Voir oflag=seek_bytes si la recherche de N octets est
|
||||
préférée
|
||||
- status=NIVEAU : contrôle si les statistiques de volume et de performance sont écrites sur
|
||||
stderr.
|
||||
|
||||
Quand non spécifié, dd affichera les statistiques à la fin. Un exemple est ci-dessous.
|
||||
|
||||
```plain
|
||||
6+0 enregistrements en entrée
|
||||
16+0 enregistrements en sortie
|
||||
8192 octets (8.2 kB, 8.0 KiB) copiés, 0.00057009 s,
|
||||
14.4 MB/s
|
||||
|
||||
Les deux premières lignes sont les statistiques de 'volume' et la dernière ligne est les
|
||||
statistiques de 'performance'.
|
||||
Les statistiques de volume indiquent le nombre de lectures complètes et partielles de taille ibs,
|
||||
ou d'écritures de taille obs qui ont eu lieu pendant la copie. Le format des statistiques de
|
||||
volume est <complètes>+<partielles>. Si des enregistrements ont été tronqués (voir
|
||||
conv=block), les statistiques de volume contiendront le nombre d'enregistrements tronqués.
|
||||
|
||||
Les valeurs possibles de NIVEAU sont :
|
||||
- progress : Afficher les statistiques de performance périodiques pendant la copie.
|
||||
- noxfer : Afficher les statistiques de volume finales, mais pas les statistiques de performance.
|
||||
- none : N'afficher aucune statistique.
|
||||
|
||||
L'affichage des statistiques de performance est aussi déclenché par le signal INFO (quand supporté),
|
||||
ou le signal USR1. Définir la variable d'environnement POSIXLY_CORRECT à n'importe quelle valeur
|
||||
(y compris une valeur vide) fera ignorer le signal USR1.
|
||||
|
||||
### Options de conversion
|
||||
|
||||
- ascii : convertir d'EBCDIC vers ASCII. C'est l'inverse de l'option ebcdic.
|
||||
Implique conv=unblock.
|
||||
- ebcdic : convertir d'ASCII vers EBCDIC. C'est l'inverse de l'option ascii.
|
||||
Implique conv=block.
|
||||
- ibm : convertir d'ASCII vers EBCDIC, en appliquant les conventions pour [, ]
|
||||
et ~ spécifiées dans POSIX. Implique conv=block.
|
||||
|
||||
- ucase : convertir de minuscules vers majuscules.
|
||||
- lcase : convertir de majuscules vers minuscules.
|
||||
|
||||
- block : pour chaque nouvelle ligne inférieure à la taille indiquée par cbs=OCTETS, supprimer
|
||||
la nouvelle ligne et remplir avec des espaces jusqu'à cbs. Les lignes plus longues que cbs sont tronquées.
|
||||
- unblock : pour chaque bloc d'entrée de la taille indiquée par cbs=OCTETS, supprimer
|
||||
les espaces de fin à droite et remplacer par un caractère de nouvelle ligne.
|
||||
|
||||
- sparse : tente de rechercher la sortie quand un bloc de taille obs ne contient que
|
||||
des zéros.
|
||||
- swab : échange chaque paire d'octets adjacents. Si un nombre impair d'octets est
|
||||
présent, l'octet final est omis.
|
||||
- sync : remplit chaque bloc de taille ibs avec des zéros. Si block ou unblock est
|
||||
spécifié, remplit avec des espaces à la place.
|
||||
- excl : le fichier de sortie doit être créé. Échoue si le fichier de sortie est déjà
|
||||
présent.
|
||||
- nocreat : le fichier de sortie ne sera pas créé. Échoue si le fichier de sortie n'est
|
||||
pas déjà présent.
|
||||
- notrunc : le fichier de sortie ne sera pas tronqué. Si cette option n'est pas
|
||||
présente, la sortie sera tronquée à l'ouverture.
|
||||
- noerror : toutes les erreurs de lecture seront ignorées. Si cette option n'est pas présente,
|
||||
dd n'ignorera que Error::Interrupted.
|
||||
- fdatasync : les données seront écrites avant la fin.
|
||||
- fsync : les données et les métadonnées seront écrites avant la fin.
|
||||
|
||||
### Indicateurs d'entrée
|
||||
|
||||
- count_bytes : une valeur pour count=N sera interprétée comme des octets.
|
||||
- skip_bytes : une valeur pour skip=N sera interprétée comme des octets.
|
||||
- fullblock : attendre ibs octets de chaque lecture. les lectures de longueur zéro sont toujours
|
||||
considérées comme EOF.
|
||||
|
||||
### Indicateurs de sortie
|
||||
|
||||
- append : ouvrir le fichier en mode ajout. Considérez définir conv=notrunc aussi.
|
||||
- seek_bytes : une valeur pour seek=N sera interprétée comme des octets.
|
||||
|
||||
### Indicateurs généraux
|
||||
|
||||
- direct : utiliser les E/S directes pour les données.
|
||||
- directory : échouer sauf si l'entrée donnée (si utilisée comme iflag) ou
|
||||
la sortie (si utilisée comme oflag) est un répertoire.
|
||||
- dsync : utiliser les E/S synchronisées pour les données.
|
||||
- sync : utiliser les E/S synchronisées pour les données et les métadonnées.
|
||||
- nonblock : utiliser les E/S non-bloquantes.
|
||||
- noatime : ne pas mettre à jour l'heure d'accès.
|
||||
- nocache : demander au système d'exploitation de supprimer le cache.
|
||||
- noctty : ne pas assigner un tty de contrôle.
|
||||
- nofollow : ne pas suivre les liens système.
|
||||
|
||||
# Error messages
|
||||
dd-error-failed-to-open = échec de l'ouverture de { $path }
|
||||
dd-error-write-error = erreur d'écriture
|
||||
dd-error-failed-to-seek = échec de la recherche dans le fichier de sortie
|
||||
dd-error-io-error = erreur E/S
|
||||
dd-error-cannot-skip-offset = '{ $file }' : impossible d'ignorer jusqu'au décalage spécifié
|
||||
dd-error-cannot-skip-invalid = '{ $file }' : impossible d'ignorer : Argument invalide
|
||||
dd-error-cannot-seek-invalid = '{ $output }' : impossible de rechercher : Argument invalide
|
||||
dd-error-not-directory = définir les indicateurs pour '{ $file }' : N'est pas un répertoire
|
||||
dd-error-failed-discard-cache-input = échec de la suppression du cache pour : 'entrée standard'
|
||||
dd-error-failed-discard-cache-output = échec de la suppression du cache pour : 'sortie standard'
|
||||
|
||||
# Parse errors
|
||||
dd-error-unrecognized-operand = Opérande non reconnue '{ $operand }'
|
||||
dd-error-multiple-format-table = Seul un seul de conv=ascii conv=ebcdic ou conv=ibm peut être spécifié
|
||||
dd-error-multiple-case = Seul un seul de conv=lcase ou conv=ucase peut être spécifié
|
||||
dd-error-multiple-block = Seul un seul de conv=block ou conv=unblock peut être spécifié
|
||||
dd-error-multiple-excl = Seul un seul de conv=excl ou conv=nocreat peut être spécifié
|
||||
dd-error-invalid-flag = indicateur d'entrée invalide : '{ $flag }'
|
||||
Essayez '{ $cmd } --help' pour plus d'informations.
|
||||
dd-error-conv-flag-no-match = conv=CONV non reconnu -> { $flag }
|
||||
dd-error-multiplier-parse-failure = nombre invalide : ‘{ $input }‘
|
||||
dd-error-multiplier-overflow = La chaîne de multiplicateur déborderait sur le système actuel -> { $input }
|
||||
dd-error-block-without-cbs = conv=block ou conv=unblock spécifié sans cbs=N
|
||||
dd-error-status-not-recognized = status=NIVEAU non reconnu -> { $level }
|
||||
dd-error-unimplemented = fonctionnalité non implémentée sur ce système -> { $feature }
|
||||
dd-error-bs-out-of-range = { $param }=N ne peut pas tenir en mémoire
|
||||
dd-error-invalid-number = nombre invalide : ‘{ $input }‘
|
||||
|
||||
# Progress messages
|
||||
dd-progress-records-in = { $complete }+{ $partial } enregistrements en entrée
|
||||
dd-progress-records-out = { $complete }+{ $partial } enregistrements en sortie
|
||||
dd-progress-truncated-record = { $count ->
|
||||
[one] { $count } enregistrement tronqué
|
||||
*[other] { $count } enregistrements tronqués
|
||||
}
|
||||
dd-progress-byte-copied = { $bytes } octet copié, { $duration } s, { $rate }/s
|
||||
dd-progress-bytes-copied = { $bytes } octets copiés, { $duration } s, { $rate }/s
|
||||
dd-progress-bytes-copied-si = { $bytes } octets ({ $si }) copiés, { $duration } s, { $rate }/s
|
||||
dd-progress-bytes-copied-si-iec = { $bytes } octets ({ $si }, { $iec }) copiés, { $duration } s, { $rate }/s
|
||||
|
||||
# Warnings
|
||||
dd-warning-zero-multiplier = { $zero } est un multiplicateur zéro ; utilisez { $alternative } si c'est voulu
|
||||
dd-warning-signal-handler = Avertissement dd interne : Impossible d'enregistrer le gestionnaire de signal
|
|
@ -26,6 +26,7 @@ use progress::{ProgUpdate, ReadStat, StatusLevel, WriteStat, gen_prog_updater};
|
|||
use uucore::io::OwnedFileDescriptorOrHandle;
|
||||
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{File, OpenOptions};
|
||||
|
@ -62,7 +63,7 @@ use uucore::error::{USimpleError, set_exit_code};
|
|||
use uucore::show_if_err;
|
||||
use uucore::{format_usage, show_error};
|
||||
|
||||
use uucore::locale::get_message;
|
||||
use uucore::locale::{get_message, get_message_with_args};
|
||||
const BUF_INIT_BYTE: u8 = 0xDD;
|
||||
|
||||
/// Final settings after parsing
|
||||
|
@ -235,7 +236,13 @@ impl Source {
|
|||
#[cfg(not(unix))]
|
||||
Self::Stdin(stdin) => match io::copy(&mut stdin.take(n), &mut io::sink()) {
|
||||
Ok(m) if m < n => {
|
||||
show_error!("'standard input': cannot skip to specified offset");
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"dd-error-cannot-skip-offset",
|
||||
HashMap::from([("file".to_string(), "standard input".to_string())])
|
||||
)
|
||||
);
|
||||
Ok(m)
|
||||
}
|
||||
Ok(m) => Ok(m),
|
||||
|
@ -247,14 +254,26 @@ impl Source {
|
|||
if len < n {
|
||||
// GNU compatibility:
|
||||
// this case prints the stats but sets the exit code to 1
|
||||
show_error!("'standard input': cannot skip: Invalid argument");
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"dd-error-cannot-skip-invalid",
|
||||
HashMap::from([("file".to_string(), "standard input".to_string())])
|
||||
)
|
||||
);
|
||||
set_exit_code(1);
|
||||
return Ok(len);
|
||||
}
|
||||
}
|
||||
match io::copy(&mut f.take(n), &mut io::sink()) {
|
||||
Ok(m) if m < n => {
|
||||
show_error!("'standard input': cannot skip to specified offset");
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"dd-error-cannot-skip-offset",
|
||||
HashMap::from([("file".to_string(), "standard input".to_string())])
|
||||
)
|
||||
);
|
||||
Ok(m)
|
||||
}
|
||||
Ok(m) => Ok(m),
|
||||
|
@ -343,7 +362,10 @@ impl<'a> Input<'a> {
|
|||
if settings.iflags.directory && !f.metadata()?.is_dir() {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
"setting flags for 'standard input': Not a directory",
|
||||
get_message_with_args(
|
||||
"dd-error-not-directory",
|
||||
HashMap::from([("file".to_string(), "standard input".to_string())]),
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
@ -364,8 +386,12 @@ impl<'a> Input<'a> {
|
|||
opts.custom_flags(libc_flags);
|
||||
}
|
||||
|
||||
opts.open(filename)
|
||||
.map_err_context(|| format!("failed to open {}", filename.quote()))?
|
||||
opts.open(filename).map_err_context(|| {
|
||||
get_message_with_args(
|
||||
"dd-error-failed-to-open",
|
||||
HashMap::from([("path".to_string(), filename.quote().to_string())]),
|
||||
)
|
||||
})?
|
||||
};
|
||||
|
||||
let mut src = Source::File(src);
|
||||
|
@ -457,10 +483,11 @@ impl Input<'_> {
|
|||
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
show_if_err!(self
|
||||
.src
|
||||
show_if_err!(
|
||||
self.src
|
||||
.discard_cache(offset, len)
|
||||
.map_err_context(|| "failed to discard cache for: 'standard input'".to_string()));
|
||||
.map_err_context(|| get_message("dd-error-failed-discard-cache-input"))
|
||||
);
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
|
@ -609,7 +636,16 @@ impl Dest {
|
|||
if len < n {
|
||||
// GNU compatibility:
|
||||
// this case prints the stats but sets the exit code to 1
|
||||
show_error!("'standard output': cannot seek: Invalid argument");
|
||||
show_error!(
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"dd-error-cannot-seek-invalid",
|
||||
HashMap::from([(
|
||||
"output".to_string(),
|
||||
"standard output".to_string()
|
||||
)])
|
||||
)
|
||||
);
|
||||
set_exit_code(1);
|
||||
return Ok(len);
|
||||
}
|
||||
|
@ -723,7 +759,7 @@ impl<'a> Output<'a> {
|
|||
fn new_stdout(settings: &'a Settings) -> UResult<Self> {
|
||||
let mut dst = Dest::Stdout(io::stdout());
|
||||
dst.seek(settings.seek)
|
||||
.map_err_context(|| "write error".to_string())?;
|
||||
.map_err_context(|| get_message("dd-error-write-error"))?;
|
||||
Ok(Self { dst, settings })
|
||||
}
|
||||
|
||||
|
@ -744,8 +780,12 @@ impl<'a> Output<'a> {
|
|||
opts.open(path)
|
||||
}
|
||||
|
||||
let dst = open_dst(filename, &settings.oconv, &settings.oflags)
|
||||
.map_err_context(|| format!("failed to open {}", filename.quote()))?;
|
||||
let dst = open_dst(filename, &settings.oconv, &settings.oflags).map_err_context(|| {
|
||||
get_message_with_args(
|
||||
"dd-error-failed-to-open",
|
||||
HashMap::from([("path".to_string(), filename.quote().to_string())]),
|
||||
)
|
||||
})?;
|
||||
|
||||
// Seek to the index in the output file, truncating if requested.
|
||||
//
|
||||
|
@ -770,7 +810,7 @@ impl<'a> Output<'a> {
|
|||
};
|
||||
let mut dst = Dest::File(dst, density);
|
||||
dst.seek(settings.seek)
|
||||
.map_err_context(|| "failed to seek in output file".to_string())?;
|
||||
.map_err_context(|| get_message("dd-error-failed-to-seek"))?;
|
||||
Ok(Self { dst, settings })
|
||||
}
|
||||
|
||||
|
@ -832,9 +872,11 @@ impl<'a> Output<'a> {
|
|||
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
show_if_err!(self.dst.discard_cache(offset, len).map_err_context(|| {
|
||||
"failed to discard cache for: 'standard output'".to_string()
|
||||
}));
|
||||
show_if_err!(
|
||||
self.dst
|
||||
.discard_cache(offset, len)
|
||||
.map_err_context(|| { get_message("dd-error-failed-discard-cache-output") })
|
||||
);
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
|
@ -1083,7 +1125,7 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
|
|||
#[cfg(target_os = "linux")]
|
||||
if let Err(e) = &signal_handler {
|
||||
if Some(StatusLevel::None) != i.settings.status {
|
||||
eprintln!("Internal dd Warning: Unable to register signal handler \n\t{e}");
|
||||
eprintln!("{}\n\t{e}", get_message("dd-warning-signal-handler"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1419,7 +1461,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
None if is_stdout_redirected_to_seekable_file() => Output::new_file_from_stdout(&settings)?,
|
||||
None => Output::new_stdout(&settings)?,
|
||||
};
|
||||
dd_copy(i, o).map_err_context(|| "IO error".to_string())
|
||||
dd_copy(i, o).map_err_context(|| get_message("dd-error-io-error"))
|
||||
}
|
||||
|
||||
pub fn uu_app() -> Command {
|
||||
|
|
|
@ -9,42 +9,53 @@ mod unit_tests;
|
|||
|
||||
use super::{ConversionMode, IConvFlags, IFlags, Num, OConvFlags, OFlags, Settings, StatusLevel};
|
||||
use crate::conversion_tables::ConversionTable;
|
||||
use std::collections::HashMap;
|
||||
use thiserror::Error;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::UError;
|
||||
use uucore::locale::{get_message, get_message_with_args};
|
||||
use uucore::parser::parse_size::{ParseSizeError, Parser as SizeParser};
|
||||
use uucore::show_warning;
|
||||
|
||||
/// Parser Errors describe errors with parser input
|
||||
#[derive(Debug, PartialEq, Eq, Error)]
|
||||
pub enum ParseError {
|
||||
#[error("Unrecognized operand '{0}'")]
|
||||
#[error("{}", get_message_with_args("dd-error-unrecognized-operand",
|
||||
HashMap::from([("operand".to_string(), .0.clone())])))]
|
||||
UnrecognizedOperand(String),
|
||||
#[error("Only one of conv=ascii conv=ebcdic or conv=ibm may be specified")]
|
||||
#[error("{}", get_message("dd-error-multiple-format-table"))]
|
||||
MultipleFmtTable,
|
||||
#[error("Only one of conv=lcase or conv=ucase may be specified")]
|
||||
#[error("{}", get_message("dd-error-multiple-case"))]
|
||||
MultipleUCaseLCase,
|
||||
#[error("Only one of conv=block or conv=unblock may be specified")]
|
||||
#[error("{}", get_message("dd-error-multiple-block"))]
|
||||
MultipleBlockUnblock,
|
||||
#[error("Only one ov conv=excl or conv=nocreat may be specified")]
|
||||
#[error("{}", get_message("dd-error-multiple-excl"))]
|
||||
MultipleExclNoCreate,
|
||||
#[error("invalid input flag: ‘{}’\nTry '{} --help' for more information.", .0, uucore::execution_phrase())]
|
||||
#[error("{}", get_message_with_args("dd-error-invalid-flag",
|
||||
HashMap::from([("flag".to_string(), .0.clone()),("cmd".to_string(), uucore::execution_phrase().to_string())])))]
|
||||
FlagNoMatch(String),
|
||||
#[error("Unrecognized conv=CONV -> {0}")]
|
||||
#[error("{}", get_message_with_args("dd-error-conv-flag-no-match",
|
||||
HashMap::from([("flag".to_string(), .0.clone())])))]
|
||||
ConvFlagNoMatch(String),
|
||||
#[error("invalid number: ‘{0}’")]
|
||||
#[error("{}", get_message_with_args("dd-error-multiplier-parse-failure",
|
||||
HashMap::from([("input".to_string(), .0.clone())])))]
|
||||
MultiplierStringParseFailure(String),
|
||||
#[error("Multiplier string would overflow on current system -> {0}")]
|
||||
#[error("{}", get_message_with_args("dd-error-multiplier-overflow",
|
||||
HashMap::from([("input".to_string(), .0.clone())])))]
|
||||
MultiplierStringOverflow(String),
|
||||
#[error("conv=block or conv=unblock specified without cbs=N")]
|
||||
#[error("{}", get_message("dd-error-block-without-cbs"))]
|
||||
BlockUnblockWithoutCBS,
|
||||
#[error("status=LEVEL not recognized -> {0}")]
|
||||
#[error("{}", get_message_with_args("dd-error-status-not-recognized",
|
||||
HashMap::from([("level".to_string(), .0.clone())])))]
|
||||
StatusLevelNotRecognized(String),
|
||||
#[error("feature not implemented on this system -> {0}")]
|
||||
#[error("{}", get_message_with_args("dd-error-unimplemented",
|
||||
HashMap::from([("feature".to_string(), .0.clone())])))]
|
||||
Unimplemented(String),
|
||||
#[error("{0}=N cannot fit into memory")]
|
||||
#[error("{}", get_message_with_args("dd-error-bs-out-of-range",
|
||||
HashMap::from([("param".to_string(), .0.clone())])))]
|
||||
BsOutOfRange(String),
|
||||
#[error("invalid number: ‘{0}’")]
|
||||
#[error("{}", get_message_with_args("dd-error-invalid-number",
|
||||
HashMap::from([("input".to_string(), .0.clone())])))]
|
||||
InvalidNumber(String),
|
||||
}
|
||||
|
||||
|
@ -424,9 +435,14 @@ impl UError for ParseError {
|
|||
|
||||
fn show_zero_multiplier_warning() {
|
||||
show_warning!(
|
||||
"{} is a zero multiplier; use {} if that is intended",
|
||||
"0x".quote(),
|
||||
"00x".quote()
|
||||
"{}",
|
||||
get_message_with_args(
|
||||
"dd-warning-zero-multiplier",
|
||||
HashMap::from([
|
||||
("zero".to_string(), "0x".quote().to_string()),
|
||||
("alternative".to_string(), "00x".quote().to_string())
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
//! read and write progress of a running `dd` process. The
|
||||
//! [`gen_prog_updater`] function can be used to implement a progress
|
||||
//! updater that runs in its own thread.
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::sync::mpsc;
|
||||
#[cfg(target_os = "linux")]
|
||||
|
@ -20,6 +21,8 @@ use signal_hook::iterator::Handle;
|
|||
use uucore::{
|
||||
error::UResult,
|
||||
format::num_format::{FloatVariant, Formatter},
|
||||
locale::get_message_with_args,
|
||||
locale::setup_localization,
|
||||
};
|
||||
|
||||
use crate::numbers::{SuffixType, to_magnitude_and_suffix};
|
||||
|
@ -102,8 +105,13 @@ impl ProgUpdate {
|
|||
self.write_stat.report(w)?;
|
||||
match self.read_stat.records_truncated {
|
||||
0 => {}
|
||||
1 => writeln!(w, "1 truncated record")?,
|
||||
n => writeln!(w, "{n} truncated records")?,
|
||||
count => {
|
||||
let message = get_message_with_args(
|
||||
"dd-progress-truncated-record",
|
||||
HashMap::from([("count".to_string(), count.to_string())]),
|
||||
);
|
||||
writeln!(w, "{}", message)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -164,24 +172,45 @@ impl ProgUpdate {
|
|||
// If the number of bytes written is sufficiently large, then
|
||||
// print a more concise representation of the number, like
|
||||
// "1.2 kB" and "1.0 KiB".
|
||||
match btotal {
|
||||
1 => write!(
|
||||
w,
|
||||
"{carriage_return}{btotal} byte copied, {duration_str} s, {transfer_rate}/s{newline}",
|
||||
)?,
|
||||
0..=999 => write!(
|
||||
w,
|
||||
"{carriage_return}{btotal} bytes copied, {duration_str} s, {transfer_rate}/s{newline}",
|
||||
)?,
|
||||
1000..=1023 => write!(
|
||||
w,
|
||||
"{carriage_return}{btotal} bytes ({btotal_metric}) copied, {duration_str} s, {transfer_rate}/s{newline}",
|
||||
)?,
|
||||
_ => write!(
|
||||
w,
|
||||
"{carriage_return}{btotal} bytes ({btotal_metric}, {btotal_bin}) copied, {duration_str} s, {transfer_rate}/s{newline}",
|
||||
)?,
|
||||
let message = match btotal {
|
||||
1 => get_message_with_args(
|
||||
"dd-progress-byte-copied",
|
||||
HashMap::from([
|
||||
("bytes".to_string(), btotal.to_string()),
|
||||
("duration".to_string(), duration_str.to_string()),
|
||||
("rate".to_string(), transfer_rate.to_string()),
|
||||
]),
|
||||
),
|
||||
0..=999 => get_message_with_args(
|
||||
"dd-progress-bytes-copied",
|
||||
HashMap::from([
|
||||
("bytes".to_string(), btotal.to_string()),
|
||||
("duration".to_string(), duration_str.to_string()),
|
||||
("rate".to_string(), transfer_rate.to_string()),
|
||||
]),
|
||||
),
|
||||
1000..=1023 => get_message_with_args(
|
||||
"dd-progress-bytes-copied-si",
|
||||
HashMap::from([
|
||||
("bytes".to_string(), btotal.to_string()),
|
||||
("si".to_string(), btotal_metric.to_string()),
|
||||
("duration".to_string(), duration_str.to_string()),
|
||||
("rate".to_string(), transfer_rate.to_string()),
|
||||
]),
|
||||
),
|
||||
_ => get_message_with_args(
|
||||
"dd-progress-bytes-copied-si-iec",
|
||||
HashMap::from([
|
||||
("bytes".to_string(), btotal.to_string()),
|
||||
("si".to_string(), btotal_metric.to_string()),
|
||||
("iec".to_string(), btotal_bin.to_string()),
|
||||
("duration".to_string(), duration_str.to_string()),
|
||||
("rate".to_string(), transfer_rate.to_string()),
|
||||
]),
|
||||
),
|
||||
};
|
||||
|
||||
write!(w, "{carriage_return}{message}{newline}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -311,11 +340,14 @@ impl ReadStat {
|
|||
///
|
||||
/// If there is a problem writing to `w`.
|
||||
fn report(&self, w: &mut impl Write) -> std::io::Result<()> {
|
||||
writeln!(
|
||||
w,
|
||||
"{}+{} records in",
|
||||
self.reads_complete, self.reads_partial
|
||||
)?;
|
||||
let message = get_message_with_args(
|
||||
"dd-progress-records-in",
|
||||
HashMap::from([
|
||||
("complete".to_string(), self.reads_complete.to_string()),
|
||||
("partial".to_string(), self.reads_partial.to_string()),
|
||||
]),
|
||||
);
|
||||
writeln!(w, "{}", message)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -368,11 +400,14 @@ impl WriteStat {
|
|||
///
|
||||
/// If there is a problem writing to `w`.
|
||||
fn report(&self, w: &mut impl Write) -> std::io::Result<()> {
|
||||
writeln!(
|
||||
w,
|
||||
"{}+{} records out",
|
||||
self.writes_complete, self.writes_partial
|
||||
)
|
||||
let message = get_message_with_args(
|
||||
"dd-progress-records-out",
|
||||
HashMap::from([
|
||||
("complete".to_string(), self.writes_complete.to_string()),
|
||||
("partial".to_string(), self.writes_partial.to_string()),
|
||||
]),
|
||||
);
|
||||
writeln!(w, "{}", message)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -428,6 +463,9 @@ pub(crate) fn gen_prog_updater(
|
|||
print_level: Option<StatusLevel>,
|
||||
) -> impl Fn() {
|
||||
move || {
|
||||
// As we are in a thread, we need to set up localization independently.
|
||||
let _ = setup_localization("dd");
|
||||
|
||||
let mut progress_printed = false;
|
||||
while let Ok(update) = rx.recv() {
|
||||
// Print the final read/write statistics.
|
||||
|
@ -502,6 +540,9 @@ pub(crate) fn gen_prog_updater(
|
|||
) -> impl Fn() {
|
||||
// --------------------------------------------------------------
|
||||
move || {
|
||||
// As we are in a thread, we need to set up localization independently.
|
||||
let _ = setup_localization("dd");
|
||||
|
||||
// Holds the state of whether we have printed the current progress.
|
||||
// This is needed so that we know whether or not to print a newline
|
||||
// character before outputting non-progress data.
|
||||
|
@ -532,11 +573,18 @@ pub(crate) fn gen_prog_updater(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::env;
|
||||
use std::io::Cursor;
|
||||
use std::time::Duration;
|
||||
use uucore::locale::setup_localization;
|
||||
|
||||
use super::{ProgUpdate, ReadStat, WriteStat};
|
||||
fn init() {
|
||||
unsafe {
|
||||
env::set_var("LANG", "C");
|
||||
}
|
||||
let _ = setup_localization("dd");
|
||||
}
|
||||
|
||||
fn prog_update_write(n: u128) -> ProgUpdate {
|
||||
ProgUpdate {
|
||||
|
@ -561,22 +609,31 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_read_stat_report() {
|
||||
init();
|
||||
let read_stat = ReadStat::new(1, 2, 3, 4);
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
read_stat.report(&mut cursor).unwrap();
|
||||
assert_eq!(cursor.get_ref(), b"1+2 records in\n");
|
||||
assert_eq!(
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"1+2 records in\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_stat_report() {
|
||||
init();
|
||||
let write_stat = WriteStat::new(1, 2, 3);
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
write_stat.report(&mut cursor).unwrap();
|
||||
assert_eq!(cursor.get_ref(), b"1+2 records out\n");
|
||||
assert_eq!(
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"1+2 records out\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prog_update_write_io_lines() {
|
||||
init();
|
||||
let read_stat = ReadStat::new(1, 2, 3, 4);
|
||||
let write_stat = WriteStat::new(4, 5, 6);
|
||||
let duration = Duration::new(789, 0);
|
||||
|
@ -591,13 +648,14 @@ mod tests {
|
|||
let mut cursor = Cursor::new(vec![]);
|
||||
prog_update.write_io_lines(&mut cursor).unwrap();
|
||||
assert_eq!(
|
||||
cursor.get_ref(),
|
||||
b"1+2 records in\n4+5 records out\n3 truncated records\n"
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"1+2 records in\n4+5 records out\n3 truncated records\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prog_update_write_prog_line() {
|
||||
init();
|
||||
let prog_update = ProgUpdate {
|
||||
read_stat: ReadStat::default(),
|
||||
write_stat: WriteStat::default(),
|
||||
|
@ -615,45 +673,55 @@ mod tests {
|
|||
// 0 bytes copied, 7.9151e-05 s, 0.0 kB/s
|
||||
//
|
||||
// The throughput still does not match GNU dd.
|
||||
assert_eq!(cursor.get_ref(), b"0 bytes copied, 1 s, 0.0 B/s\n");
|
||||
assert_eq!(
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"0 bytes copied, 1 s, 0.0 B/s\n"
|
||||
);
|
||||
|
||||
let prog_update = prog_update_write(1);
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
prog_update.write_prog_line(&mut cursor, rewrite).unwrap();
|
||||
assert_eq!(cursor.get_ref(), b"1 byte copied, 1 s, 0.0 B/s\n");
|
||||
assert_eq!(
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"1 byte copied, 1 s, 0.0 B/s\n"
|
||||
);
|
||||
|
||||
let prog_update = prog_update_write(999);
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
prog_update.write_prog_line(&mut cursor, rewrite).unwrap();
|
||||
assert_eq!(cursor.get_ref(), b"999 bytes copied, 1 s, 0.0 B/s\n");
|
||||
assert_eq!(
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"999 bytes copied, 1 s, 0.0 B/s\n"
|
||||
);
|
||||
|
||||
let prog_update = prog_update_write(1000);
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
prog_update.write_prog_line(&mut cursor, rewrite).unwrap();
|
||||
assert_eq!(
|
||||
cursor.get_ref(),
|
||||
b"1000 bytes (1.0 kB) copied, 1 s, 1.0 kB/s\n"
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"1000 bytes (1.0 kB) copied, 1 s, 1.0 kB/s\n"
|
||||
);
|
||||
|
||||
let prog_update = prog_update_write(1023);
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
prog_update.write_prog_line(&mut cursor, rewrite).unwrap();
|
||||
assert_eq!(
|
||||
cursor.get_ref(),
|
||||
b"1023 bytes (1.0 kB) copied, 1 s, 1.0 kB/s\n"
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"1023 bytes (1.0 kB) copied, 1 s, 1.0 kB/s\n"
|
||||
);
|
||||
|
||||
let prog_update = prog_update_write(1024);
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
prog_update.write_prog_line(&mut cursor, rewrite).unwrap();
|
||||
assert_eq!(
|
||||
cursor.get_ref(),
|
||||
b"1024 bytes (1.0 kB, 1.0 KiB) copied, 1 s, 1.0 kB/s\n"
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"1024 bytes (1.0 kB, 1.0 KiB) copied, 1 s, 1.0 kB/s\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_transfer_stats() {
|
||||
init();
|
||||
let prog_update = ProgUpdate {
|
||||
read_stat: ReadStat::default(),
|
||||
write_stat: WriteStat::default(),
|
||||
|
@ -664,16 +732,18 @@ mod tests {
|
|||
prog_update
|
||||
.write_transfer_stats(&mut cursor, false)
|
||||
.unwrap();
|
||||
let mut iter = cursor.get_ref().split(|v| *v == b'\n');
|
||||
assert_eq!(iter.next().unwrap(), b"0+0 records in");
|
||||
assert_eq!(iter.next().unwrap(), b"0+0 records out");
|
||||
assert_eq!(iter.next().unwrap(), b"0 bytes copied, 1 s, 0.0 B/s");
|
||||
assert_eq!(iter.next().unwrap(), b"");
|
||||
let output_str = std::str::from_utf8(cursor.get_ref()).unwrap();
|
||||
let mut iter = output_str.split('\n');
|
||||
assert_eq!(iter.next().unwrap(), "0+0 records in");
|
||||
assert_eq!(iter.next().unwrap(), "0+0 records out");
|
||||
assert_eq!(iter.next().unwrap(), "0 bytes copied, 1 s, 0.0 B/s");
|
||||
assert_eq!(iter.next().unwrap(), "");
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_final_transfer_stats() {
|
||||
init();
|
||||
// Tests the formatting of the final statistics written after a progress line.
|
||||
let prog_update = ProgUpdate {
|
||||
read_stat: ReadStat::default(),
|
||||
|
@ -685,21 +755,26 @@ mod tests {
|
|||
let rewrite = true;
|
||||
prog_update.write_prog_line(&mut cursor, rewrite).unwrap();
|
||||
prog_update.write_transfer_stats(&mut cursor, true).unwrap();
|
||||
let mut iter = cursor.get_ref().split(|v| *v == b'\n');
|
||||
assert_eq!(iter.next().unwrap(), b"\r0 bytes copied, 1 s, 0.0 B/s");
|
||||
assert_eq!(iter.next().unwrap(), b"0+0 records in");
|
||||
assert_eq!(iter.next().unwrap(), b"0+0 records out");
|
||||
assert_eq!(iter.next().unwrap(), b"0 bytes copied, 1 s, 0.0 B/s");
|
||||
assert_eq!(iter.next().unwrap(), b"");
|
||||
let output_str = std::str::from_utf8(cursor.get_ref()).unwrap();
|
||||
let mut iter = output_str.split('\n');
|
||||
assert_eq!(iter.next().unwrap(), "\r0 bytes copied, 1 s, 0.0 B/s");
|
||||
assert_eq!(iter.next().unwrap(), "0+0 records in");
|
||||
assert_eq!(iter.next().unwrap(), "0+0 records out");
|
||||
assert_eq!(iter.next().unwrap(), "0 bytes copied, 1 s, 0.0 B/s");
|
||||
assert_eq!(iter.next().unwrap(), "");
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_precision() {
|
||||
init();
|
||||
let prog_update = prog_update_duration(Duration::from_nanos(123));
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
let rewrite = false;
|
||||
prog_update.write_prog_line(&mut cursor, rewrite).unwrap();
|
||||
assert_eq!(cursor.get_ref(), b"0 bytes copied, 1.23e-07 s, 0.0 B/s\n");
|
||||
assert_eq!(
|
||||
std::str::from_utf8(cursor.get_ref()).unwrap(),
|
||||
"0 bytes copied, 0.000000123 s, 0.0 B/s\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1617,6 +1617,7 @@ fn test_reading_partial_blocks_from_fifo() {
|
|||
.args(["dd", "ibs=3", "obs=3", &format!("if={fifoname}")])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.env("LANG", "C")
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
|
@ -1661,6 +1662,7 @@ fn test_reading_partial_blocks_from_fifo_unbuffered() {
|
|||
.args(["dd", "bs=3", "ibs=1", "obs=1", &format!("if={fifoname}")])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.env("LANG", "C")
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
|
@ -1767,10 +1769,10 @@ fn test_wrong_number_err_msg() {
|
|||
new_ucmd!()
|
||||
.args(&["count=kBb"])
|
||||
.fails()
|
||||
.stderr_contains("dd: invalid number: ‘kBb’\n");
|
||||
.stderr_contains("dd: invalid number: 'kBb'\n");
|
||||
|
||||
new_ucmd!()
|
||||
.args(&["count=1kBb555"])
|
||||
.fails()
|
||||
.stderr_contains("dd: invalid number: ‘1kBb555’\n");
|
||||
.stderr_contains("dd: invalid number: '1kBb555'\n");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue