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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/uu/truncate/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,18 @@ truncate-after-help = SIZE is an integer with an optional prefix and optional un
'>' => at least
'/' => round down to multiple of
'%' => round up to multiple of

# Help messages
truncate-help-io-blocks = treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)
truncate-help-no-create = do not create files that do not exist
truncate-help-reference = base the size of each file on the size of RFILE
truncate-help-size = set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified

# Error messages
truncate-error-missing-file-operand = missing file operand
truncate-error-cannot-open-no-device = cannot open { $filename } for writing: No such device or address
truncate-error-cannot-open-for-writing = cannot open { $filename } for writing
truncate-error-invalid-number = Invalid number: { $error }
truncate-error-must-specify-relative-size = you must specify a relative '--size' with '--reference'
truncate-error-division-by-zero = division by zero
truncate-error-cannot-stat-no-such-file = cannot stat { $filename }: No such file or directory
33 changes: 33 additions & 0 deletions src/uu/truncate/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
truncate-about = Réduire ou étendre la taille de chaque fichier à la taille spécifiée.
truncate-usage = truncate [OPTION]... [FICHIER]...
truncate-after-help = TAILLE est un entier avec un préfixe optionnel et une unité optionnelle.
Les unités disponibles (K, M, G, T, P, E, Z, et Y) utilisent le format suivant :
'KB' => 1000 (kilooctets)
'K' => 1024 (kibioctets)
'MB' => 1000*1000 (mégaoctets)
'M' => 1024*1024 (mébioctets)
'GB' => 1000*1000*1000 (gigaoctets)
'G' => 1024*1024*1024 (gibioctets)
TAILLE peut aussi être préfixée par l'un des éléments suivants pour ajuster la taille de chaque
fichier basé sur sa taille actuelle :
'+' => étendre de
'-' => réduire de
'<' => au maximum
'>' => au minimum
'/' => arrondir vers le bas au multiple de
'%' => arrondir vers le haut au multiple de

# Messages d'aide
truncate-help-io-blocks = traiter TAILLE comme le nombre de blocs I/O du fichier plutôt que des octets (NON IMPLÉMENTÉ)
truncate-help-no-create = ne pas créer les fichiers qui n'existent pas
truncate-help-reference = baser la taille de chaque fichier sur la taille de RFICHIER
truncate-help-size = définir ou ajuster la taille de chaque fichier selon TAILLE, qui est en octets sauf si --io-blocks est spécifié

# Messages d'erreur
truncate-error-missing-file-operand = opérande de fichier manquant
truncate-error-cannot-open-no-device = impossible d'ouvrir { $filename } en écriture : Aucun périphérique ou adresse de ce type
truncate-error-cannot-open-for-writing = impossible d'ouvrir { $filename } en écriture
truncate-error-invalid-number = Nombre invalide : { $error }
truncate-error-must-specify-relative-size = vous devez spécifier une '--size' relative avec '--reference'
truncate-error-division-by-zero = division par zéro
truncate-error-cannot-stat-no-such-file = impossible d'obtenir les informations de { $filename } : Aucun fichier ou répertoire de ce type
99 changes: 68 additions & 31 deletions src/uu/truncate/src/truncate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize
use clap::{Arg, ArgAction, Command};
use std::collections::HashMap;
use std::fs::{OpenOptions, metadata};
use std::io::ErrorKind;
#[cfg(unix)]
Expand All @@ -13,7 +14,7 @@ use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::format_usage;
use uucore::locale::get_message;
use uucore::locale::{get_message, get_message_with_args};
use uucore::parser::parse_size::{ParseSizeError, parse_size_u64};

#[derive(Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -99,7 +100,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
.unwrap_or_default();

if files.is_empty() {
Err(UUsageError::new(1, "missing file operand"))
Err(UUsageError::new(
1,
get_message("truncate-error-missing-file-operand"),
))
} else {
let io_blocks = matches.get_flag(options::IO_BLOCKS);
let no_create = matches.get_flag(options::NO_CREATE);
Expand All @@ -121,25 +125,22 @@ pub fn uu_app() -> Command {
Arg::new(options::IO_BLOCKS)
.short('o')
.long(options::IO_BLOCKS)
.help(
"treat SIZE as the number of I/O blocks of the file rather than bytes \
(NOT IMPLEMENTED)",
)
.help(get_message("truncate-help-io-blocks"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::NO_CREATE)
.short('c')
.long(options::NO_CREATE)
.help("do not create files that do not exist")
.help(get_message("truncate-help-no-create"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::REFERENCE)
.short('r')
.long(options::REFERENCE)
.required_unless_present(options::SIZE)
.help("base the size of each file on the size of RFILE")
.help(get_message("truncate-help-reference"))
.value_name("RFILE")
.value_hint(clap::ValueHint::FilePath),
)
Expand All @@ -148,10 +149,7 @@ pub fn uu_app() -> Command {
.short('s')
.long(options::SIZE)
.required_unless_present(options::REFERENCE)
.help(
"set or adjust the size of each file according to SIZE, which is in \
bytes unless --io-blocks is specified",
)
.help(get_message("truncate-help-size"))
.value_name("SIZE"),
)
.arg(
Expand Down Expand Up @@ -181,20 +179,26 @@ fn file_truncate(filename: &str, create: bool, size: u64) -> UResult<()> {
if metadata.file_type().is_fifo() {
return Err(USimpleError::new(
1,
format!(
"cannot open {} for writing: No such device or address",
filename.quote()
get_message_with_args(
"truncate-error-cannot-open-no-device",
HashMap::from([("filename".to_string(), filename.quote().to_string())]),
),
));
}
}

let path = Path::new(filename);
match OpenOptions::new().write(true).create(create).open(path) {
Ok(file) => file.set_len(size),
Err(e) if e.kind() == ErrorKind::NotFound && !create => Ok(()),
Err(e) => Err(e),
}
.map_err_context(|| format!("cannot open {} for writing", filename.quote()))
.map_err_context(|| {
get_message_with_args(
"truncate-error-cannot-open-for-writing",
HashMap::from([("filename".to_string(), filename.quote().to_string())]),
)
})
}

/// Truncate files to a size relative to a given file.
Expand All @@ -221,33 +225,49 @@ fn truncate_reference_and_size(
create: bool,
) -> UResult<()> {
let mode = match parse_mode_and_size(size_string) {
Err(e) => return Err(USimpleError::new(1, format!("Invalid number: {e}"))),
Err(e) => {
return Err(USimpleError::new(
1,
get_message_with_args(
"truncate-error-invalid-number",
HashMap::from([("error".to_string(), e.to_string())]),
),
));
}
Ok(TruncateMode::Absolute(_)) => {
return Err(USimpleError::new(
1,
String::from("you must specify a relative '--size' with '--reference'"),
get_message("truncate-error-must-specify-relative-size"),
));
}
Ok(m) => m,
};

if let TruncateMode::RoundDown(0) | TruncateMode::RoundUp(0) = mode {
return Err(USimpleError::new(1, "division by zero"));
return Err(USimpleError::new(
1,
get_message("truncate-error-division-by-zero"),
));
}

let metadata = metadata(rfilename).map_err(|e| match e.kind() {
ErrorKind::NotFound => USimpleError::new(
1,
format!(
"cannot stat {}: No such file or directory",
rfilename.quote()
get_message_with_args(
"truncate-error-cannot-stat-no-such-file",
HashMap::from([("filename".to_string(), rfilename.quote().to_string())]),
),
),
_ => e.map_err_context(String::new),
})?;

let fsize = metadata.len();
let tsize = mode.to_size(fsize);

for filename in filenames {
file_truncate(filename, create, tsize)?;
}

Ok(())
}

Expand All @@ -272,17 +292,20 @@ fn truncate_reference_file_only(
let metadata = metadata(rfilename).map_err(|e| match e.kind() {
ErrorKind::NotFound => USimpleError::new(
1,
format!(
"cannot stat {}: No such file or directory",
rfilename.quote()
get_message_with_args(
"truncate-error-cannot-stat-no-such-file",
HashMap::from([("filename".to_string(), rfilename.quote().to_string())]),
),
),
_ => e.map_err_context(String::new),
})?;

let tsize = metadata.len();

for filename in filenames {
file_truncate(filename, create, tsize)?;
}

Ok(())
}

Expand All @@ -304,21 +327,33 @@ fn truncate_reference_file_only(
///
/// If at least one file is a named pipe (also known as a fifo).
fn truncate_size_only(size_string: &str, filenames: &[String], create: bool) -> UResult<()> {
let mode = parse_mode_and_size(size_string)
.map_err(|e| USimpleError::new(1, format!("Invalid number: {e}")))?;
let mode = parse_mode_and_size(size_string).map_err(|e| {
USimpleError::new(
1,
get_message_with_args(
"truncate-error-invalid-number",
HashMap::from([("error".to_string(), e.to_string())]),
),
)
})?;

if let TruncateMode::RoundDown(0) | TruncateMode::RoundUp(0) = mode {
return Err(USimpleError::new(1, "division by zero"));
return Err(USimpleError::new(
1,
get_message("truncate-error-division-by-zero"),
));
}

for filename in filenames {
let fsize = match metadata(filename) {
Ok(m) => {
#[cfg(unix)]
if m.file_type().is_fifo() {
return Err(USimpleError::new(
1,
format!(
"cannot open {} for writing: No such device or address",
filename.quote()
get_message_with_args(
"truncate-error-cannot-open-no-device",
HashMap::from([("filename".to_string(), filename.quote().to_string())]),
),
));
}
Expand All @@ -330,6 +365,7 @@ fn truncate_size_only(size_string: &str, filenames: &[String], create: bool) ->
// TODO: Fix duplicate call to stat
file_truncate(filename, create, tsize)?;
}

Ok(())
}

Expand All @@ -341,6 +377,7 @@ fn truncate(
filenames: &[String],
) -> UResult<()> {
let create = !no_create;

// There are four possibilities
// - reference file given and size given,
// - reference file given but no size given,
Expand Down
Loading