mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
Merge pull request #8243 from sylvestre/l10n-stat
l10n: port stat for translation + use thiserror + add french
This commit is contained in:
commit
fd7661c62e
5 changed files with 333 additions and 70 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3671,6 +3671,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"thiserror 2.0.12",
|
||||||
"uucore",
|
"uucore",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ path = "src/stat.rs"
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
uucore = { workspace = true, features = ["entries", "libc", "fs", "fsext"] }
|
uucore = { workspace = true, features = ["entries", "libc", "fs", "fsext"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
selinux = ["uucore/selinux"]
|
selinux = ["uucore/selinux"]
|
||||||
|
|
|
@ -52,3 +52,59 @@ stat-after-help = Valid format sequences for files (without `--file-system`):
|
||||||
NOTE: your shell may have its own version of stat, which usually supersedes
|
NOTE: your shell may have its own version of stat, which usually supersedes
|
||||||
the version described here. Please refer to your shell's documentation
|
the version described here. Please refer to your shell's documentation
|
||||||
for details about the options it supports.
|
for details about the options it supports.
|
||||||
|
|
||||||
|
## Error messages
|
||||||
|
stat-error-invalid-quoting-style = Invalid quoting style: {$style}
|
||||||
|
stat-error-missing-operand = missing operand
|
||||||
|
Try 'stat --help' for more information.
|
||||||
|
stat-error-invalid-directive = {$directive}: invalid directive
|
||||||
|
stat-error-cannot-read-filesystem = cannot read table of mounted file systems: {$error}
|
||||||
|
stat-error-stdin-filesystem-mode = using '-' to denote standard input does not work in file system mode
|
||||||
|
stat-error-cannot-read-filesystem-info = cannot read file system information for {$file}: {$error}
|
||||||
|
stat-error-cannot-stat = cannot stat {$file}: {$error}
|
||||||
|
|
||||||
|
## Warning messages
|
||||||
|
stat-warning-backslash-end-format = backslash at end of format
|
||||||
|
stat-warning-unrecognized-escape-x = unrecognized escape '\x'
|
||||||
|
stat-warning-incomplete-hex-escape = incomplete hex escape '\x'
|
||||||
|
stat-warning-unrecognized-escape = unrecognized escape '\{$escape}'
|
||||||
|
|
||||||
|
## Help messages
|
||||||
|
stat-help-dereference = follow links
|
||||||
|
stat-help-file-system = display file system status instead of file status
|
||||||
|
stat-help-terse = print the information in terse form
|
||||||
|
stat-help-format = use the specified FORMAT instead of the default;
|
||||||
|
output a newline after each use of FORMAT
|
||||||
|
stat-help-printf = like --format, but interpret backslash escapes,
|
||||||
|
and do not output a mandatory trailing newline;
|
||||||
|
if you want a newline, include \n in FORMAT
|
||||||
|
|
||||||
|
## Word translations
|
||||||
|
stat-word-file = File
|
||||||
|
stat-word-id = ID
|
||||||
|
stat-word-namelen = Namelen
|
||||||
|
stat-word-type = Type
|
||||||
|
stat-word-block = Block
|
||||||
|
stat-word-size = size
|
||||||
|
stat-word-fundamental = Fundamental
|
||||||
|
stat-word-block-size = block size
|
||||||
|
stat-word-blocks = Blocks
|
||||||
|
stat-word-total = Total
|
||||||
|
stat-word-free = Free
|
||||||
|
stat-word-available = Available
|
||||||
|
stat-word-inodes = Inodes
|
||||||
|
stat-word-device = Device
|
||||||
|
stat-word-inode = Inode
|
||||||
|
stat-word-links = Links
|
||||||
|
stat-word-io = IO
|
||||||
|
stat-word-access = Access
|
||||||
|
stat-word-uid = Uid
|
||||||
|
stat-word-gid = Gid
|
||||||
|
stat-word-modify = Modify
|
||||||
|
stat-word-change = Change
|
||||||
|
stat-word-birth = Birth
|
||||||
|
|
||||||
|
## SELinux context messages
|
||||||
|
stat-selinux-failed-get-context = failed to get security context
|
||||||
|
stat-selinux-unsupported-system = unsupported on this system
|
||||||
|
stat-selinux-unsupported-os = unsupported for this operating system
|
||||||
|
|
109
src/uu/stat/locales/fr-FR.ftl
Normal file
109
src/uu/stat/locales/fr-FR.ftl
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
stat-about = afficher le statut du fichier ou du système de fichiers.
|
||||||
|
stat-usage = stat [OPTION]... FICHIER...
|
||||||
|
stat-after-help = Séquences de format valides pour les fichiers (sans `--file-system`) :
|
||||||
|
|
||||||
|
-`%a` : droits d'accès en octal (note : drapeaux printf '#' et '0')
|
||||||
|
-`%A` : droits d'accès en format lisible
|
||||||
|
-`%b` : nombre de blocs alloués (voir %B)
|
||||||
|
-`%B` : la taille en octets de chaque bloc rapporté par %b
|
||||||
|
-`%C` : chaîne de contexte de sécurité SELinux
|
||||||
|
-`%d` : numéro de périphérique en décimal
|
||||||
|
-`%D` : numéro de périphérique en hexadécimal
|
||||||
|
-`%f` : mode brut en hexadécimal
|
||||||
|
-`%F` : type de fichier
|
||||||
|
-`%g` : ID de groupe du propriétaire
|
||||||
|
-`%G` : nom de groupe du propriétaire
|
||||||
|
-`%h` : nombre de liens physiques
|
||||||
|
-`%i` : numéro d'inode
|
||||||
|
-`%m` : point de montage
|
||||||
|
-`%n` : nom de fichier
|
||||||
|
-`%N` : nom de fichier avec guillemets et déréférencement (suivi) si lien symbolique
|
||||||
|
-`%o` : suggestion de taille optimale de transfert E/S
|
||||||
|
-`%s` : taille totale, en octets
|
||||||
|
-`%t` : type de périphérique majeur en hex, pour les fichiers spéciaux caractère/bloc
|
||||||
|
-`%T` : type de périphérique mineur en hex, pour les fichiers spéciaux caractère/bloc
|
||||||
|
-`%u` : ID utilisateur du propriétaire
|
||||||
|
-`%U` : nom d'utilisateur du propriétaire
|
||||||
|
-`%w` : heure de création du fichier, lisible ; - si inconnue
|
||||||
|
-`%W` : heure de création du fichier, secondes depuis l'Époque ; 0 si inconnue
|
||||||
|
-`%x` : heure du dernier accès, lisible
|
||||||
|
-`%X` : heure du dernier accès, secondes depuis l'Époque
|
||||||
|
-`%y` : heure de la dernière modification de données, lisible
|
||||||
|
-`%Y` : heure de la dernière modification de données, secondes depuis l'Époque
|
||||||
|
-`%z` : heure du dernier changement de statut, lisible
|
||||||
|
-`%Z` : heure du dernier changement de statut, secondes depuis l'Époque
|
||||||
|
|
||||||
|
Séquences de format valides pour les systèmes de fichiers :
|
||||||
|
|
||||||
|
-`%a` : blocs libres disponibles pour les non-superutilisateurs
|
||||||
|
-`%b` : blocs de données totaux dans le système de fichiers
|
||||||
|
-`%c` : nœuds de fichiers totaux dans le système de fichiers
|
||||||
|
-`%d` : nœuds de fichiers libres dans le système de fichiers
|
||||||
|
-`%f` : blocs libres dans le système de fichiers
|
||||||
|
-`%i` : ID du système de fichiers en hexadécimal
|
||||||
|
-`%l` : longueur maximale des noms de fichiers
|
||||||
|
-`%n` : nom de fichier
|
||||||
|
-`%s` : taille de bloc (pour des transferts plus rapides)
|
||||||
|
-`%S` : taille de bloc fondamentale (pour les comptes de blocs)
|
||||||
|
-`%t` : type de système de fichiers en hexadécimal
|
||||||
|
-`%T` : type de système de fichiers en format lisible
|
||||||
|
|
||||||
|
NOTE : votre shell peut avoir sa propre version de stat, qui remplace généralement
|
||||||
|
la version décrite ici. Veuillez vous référer à la documentation de votre shell
|
||||||
|
pour les détails sur les options qu'il prend en charge.
|
||||||
|
|
||||||
|
# Messages d'aide
|
||||||
|
stat-help-dereference = suivre les liens
|
||||||
|
stat-help-file-system = afficher le statut du système de fichiers au lieu du statut du fichier
|
||||||
|
stat-help-terse = afficher les informations en forme concise
|
||||||
|
stat-help-format = utiliser le FORMAT spécifié au lieu du défaut ;
|
||||||
|
afficher une nouvelle ligne après chaque utilisation de FORMAT
|
||||||
|
stat-help-printf = comme --format, mais interpréter les séquences d'échappement avec barre oblique inverse,
|
||||||
|
et ne pas afficher une nouvelle ligne finale obligatoire ;
|
||||||
|
si vous voulez une nouvelle ligne, incluez \n dans FORMAT
|
||||||
|
|
||||||
|
## Traductions de mots
|
||||||
|
stat-word-file = Fichier
|
||||||
|
stat-word-id = ID
|
||||||
|
stat-word-namelen = Longnom
|
||||||
|
stat-word-type = Type
|
||||||
|
stat-word-block = Bloc
|
||||||
|
stat-word-size = taille
|
||||||
|
stat-word-fundamental = Fondamentale
|
||||||
|
stat-word-block-size = taille bloc
|
||||||
|
stat-word-blocks = Blocs
|
||||||
|
stat-word-total = Total
|
||||||
|
stat-word-free = Libres
|
||||||
|
stat-word-available = Disponibles
|
||||||
|
stat-word-inodes = Inodes
|
||||||
|
stat-word-device = Périphérique
|
||||||
|
stat-word-inode = Inode
|
||||||
|
stat-word-links = Liens
|
||||||
|
stat-word-io = E/S
|
||||||
|
stat-word-access = Accès
|
||||||
|
stat-word-uid = Uid
|
||||||
|
stat-word-gid = Gid
|
||||||
|
stat-word-modify = Modif
|
||||||
|
stat-word-change = Changt
|
||||||
|
stat-word-birth = Créé
|
||||||
|
|
||||||
|
## Messages d'erreur
|
||||||
|
stat-error-invalid-quoting-style = Style de guillemets invalide : {$style}
|
||||||
|
stat-error-missing-operand = opérande manquant
|
||||||
|
Essayez 'stat --help' pour plus d'informations.
|
||||||
|
stat-error-invalid-directive = {$directive} : directive invalide
|
||||||
|
stat-error-cannot-read-filesystem = impossible de lire la table des systèmes de fichiers montés : {$error}
|
||||||
|
stat-error-stdin-filesystem-mode = utiliser '-' pour désigner l'entrée standard ne fonctionne pas en mode système de fichiers
|
||||||
|
stat-error-cannot-read-filesystem-info = impossible de lire les informations du système de fichiers pour {$file} : {$error}
|
||||||
|
stat-error-cannot-stat = impossible d'obtenir le statut de {$file} : {$error}
|
||||||
|
|
||||||
|
## Messages d'avertissement
|
||||||
|
stat-warning-backslash-end-format = barre oblique inverse à la fin du format
|
||||||
|
stat-warning-unrecognized-escape-x = séquence d'échappement non reconnue '\x'
|
||||||
|
stat-warning-incomplete-hex-escape = séquence d'échappement hexadécimale incomplète '\x'
|
||||||
|
stat-warning-unrecognized-escape = séquence d'échappement non reconnue '\{$escape}'
|
||||||
|
|
||||||
|
## Messages de contexte SELinux
|
||||||
|
stat-selinux-failed-get-context = impossible d'obtenir le contexte de sécurité
|
||||||
|
stat-selinux-unsupported-system = non pris en charge sur ce système
|
||||||
|
stat-selinux-unsupported-os = non pris en charge pour ce système d'exploitation
|
|
@ -4,7 +4,7 @@
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore datetime
|
// spell-checker:ignore datetime
|
||||||
|
|
||||||
use uucore::error::{UResult, USimpleError};
|
use uucore::error::{UError, UResult, USimpleError};
|
||||||
|
|
||||||
use clap::builder::ValueParser;
|
use clap::builder::ValueParser;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
|
@ -26,7 +26,33 @@ use std::os::unix::prelude::OsStrExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
use uucore::locale::get_message;
|
use std::collections::HashMap;
|
||||||
|
use thiserror::Error;
|
||||||
|
use uucore::locale::{get_message, get_message_with_args};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum StatError {
|
||||||
|
#[error("{}", get_message_with_args("stat-error-invalid-quoting-style", HashMap::from([("style".to_string(), style.clone())])))]
|
||||||
|
InvalidQuotingStyle { style: String },
|
||||||
|
#[error("{}", get_message("stat-error-missing-operand"))]
|
||||||
|
MissingOperand,
|
||||||
|
#[error("{}", get_message_with_args("stat-error-invalid-directive", HashMap::from([("directive".to_string(), directive.clone())])))]
|
||||||
|
InvalidDirective { directive: String },
|
||||||
|
#[error("{}", get_message_with_args("stat-error-cannot-read-filesystem", HashMap::from([("error".to_string(), error.clone())])))]
|
||||||
|
CannotReadFilesystem { error: String },
|
||||||
|
#[error("{}", get_message("stat-error-stdin-filesystem-mode"))]
|
||||||
|
StdinFilesystemMode,
|
||||||
|
#[error("{}", get_message_with_args("stat-error-cannot-read-filesystem-info", HashMap::from([("file".to_string(), file.clone()), ("error".to_string(), error.clone())])))]
|
||||||
|
CannotReadFilesystemInfo { file: String, error: String },
|
||||||
|
#[error("{}", get_message_with_args("stat-error-cannot-stat", HashMap::from([("file".to_string(), file.clone()), ("error".to_string(), error.clone())])))]
|
||||||
|
CannotStat { file: String, error: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UError for StatError {
|
||||||
|
fn code(&self) -> i32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod options {
|
mod options {
|
||||||
pub const DEREFERENCE: &str = "dereference";
|
pub const DEREFERENCE: &str = "dereference";
|
||||||
|
@ -54,7 +80,10 @@ fn check_bound(slice: &str, bound: usize, beg: usize, end: usize) -> UResult<()>
|
||||||
if end >= bound {
|
if end >= bound {
|
||||||
return Err(USimpleError::new(
|
return Err(USimpleError::new(
|
||||||
1,
|
1,
|
||||||
format!("{}: invalid directive", slice[beg..end].quote()),
|
StatError::InvalidDirective {
|
||||||
|
directive: slice[beg..end].quote().to_string(),
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -104,7 +133,7 @@ enum QuotingStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for QuotingStyle {
|
impl std::str::FromStr for QuotingStyle {
|
||||||
type Err = String;
|
type Err = StatError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
|
@ -112,7 +141,9 @@ impl std::str::FromStr for QuotingStyle {
|
||||||
"shell" => Ok(QuotingStyle::Shell),
|
"shell" => Ok(QuotingStyle::Shell),
|
||||||
"shell-escape-always" => Ok(QuotingStyle::ShellEscapeAlways),
|
"shell-escape-always" => Ok(QuotingStyle::ShellEscapeAlways),
|
||||||
// The others aren't exposed to the user
|
// The others aren't exposed to the user
|
||||||
_ => Err(format!("Invalid quoting style: {s}")),
|
_ => Err(StatError::InvalidQuotingStyle {
|
||||||
|
style: s.to_string(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,24 +175,22 @@ trait ScanUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScanUtil for str {
|
impl ScanUtil for str {
|
||||||
|
/// Scans for a number at the beginning of the string
|
||||||
|
/// Returns the parsed number and the character count
|
||||||
|
/// Since we only deal with ASCII characters (+, -, 0-9), character count equals byte count
|
||||||
fn scan_num<F>(&self) -> Option<(F, usize)>
|
fn scan_num<F>(&self) -> Option<(F, usize)>
|
||||||
where
|
where
|
||||||
F: std::str::FromStr,
|
F: std::str::FromStr,
|
||||||
{
|
{
|
||||||
let mut chars = self.chars();
|
let mut chars = self.chars();
|
||||||
let mut i = 0;
|
let count = chars
|
||||||
match chars.next() {
|
.next()
|
||||||
Some('-' | '+' | '0'..='9') => i += 1,
|
.filter(|&c| c.is_ascii_digit() || c == '-' || c == '+')
|
||||||
_ => return None,
|
.map(|_| 1 + chars.take_while(char::is_ascii_digit).count())
|
||||||
}
|
.unwrap_or(0);
|
||||||
for c in chars {
|
|
||||||
match c {
|
if count > 0 {
|
||||||
'0'..='9' => i += 1,
|
F::from_str(&self[..count]).ok().map(|x| (x, count))
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i > 0 {
|
|
||||||
F::from_str(&self[..i]).ok().map(|x| (x, i))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -607,6 +636,17 @@ impl Stater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a character index to a byte index in a UTF-8 string
|
||||||
|
/// This is necessary because Rust strings are UTF-8 encoded, so character positions
|
||||||
|
/// don't always align with byte positions for multi-byte characters
|
||||||
|
fn char_index_to_byte_index(format_str: &str, char_index: usize) -> usize {
|
||||||
|
format_str
|
||||||
|
.char_indices()
|
||||||
|
.nth(char_index)
|
||||||
|
.map(|(byte_idx, _)| byte_idx)
|
||||||
|
.unwrap_or(format_str.len())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_percent_case(
|
fn handle_percent_case(
|
||||||
chars: &[char],
|
chars: &[char],
|
||||||
i: &mut usize,
|
i: &mut usize,
|
||||||
|
@ -632,7 +672,8 @@ impl Stater {
|
||||||
let mut precision = Precision::NotSpecified;
|
let mut precision = Precision::NotSpecified;
|
||||||
let mut j = *i;
|
let mut j = *i;
|
||||||
|
|
||||||
if let Some((field_width, offset)) = format_str[j..].scan_num::<usize>() {
|
let j_byte = Self::char_index_to_byte_index(format_str, j);
|
||||||
|
if let Some((field_width, offset)) = format_str[j_byte..].scan_num::<usize>() {
|
||||||
width = field_width;
|
width = field_width;
|
||||||
j += offset;
|
j += offset;
|
||||||
|
|
||||||
|
@ -641,7 +682,10 @@ impl Stater {
|
||||||
let invalid_directive: String = chars[old..=j.min(bound - 1)].iter().collect();
|
let invalid_directive: String = chars[old..=j.min(bound - 1)].iter().collect();
|
||||||
return Err(USimpleError::new(
|
return Err(USimpleError::new(
|
||||||
1,
|
1,
|
||||||
format!("{}: invalid directive", invalid_directive.quote()),
|
StatError::InvalidDirective {
|
||||||
|
directive: invalid_directive.quote().to_string(),
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -651,7 +695,8 @@ impl Stater {
|
||||||
j += 1;
|
j += 1;
|
||||||
check_bound(format_str, bound, old, j)?;
|
check_bound(format_str, bound, old, j)?;
|
||||||
|
|
||||||
match format_str[j..].scan_num::<i32>() {
|
let j_byte = Self::char_index_to_byte_index(format_str, j);
|
||||||
|
match format_str[j_byte..].scan_num::<i32>() {
|
||||||
Some((value, offset)) => {
|
Some((value, offset)) => {
|
||||||
if value >= 0 {
|
if value >= 0 {
|
||||||
precision = Precision::Number(value as usize);
|
precision = Precision::Number(value as usize);
|
||||||
|
@ -698,7 +743,7 @@ impl Stater {
|
||||||
) -> Token {
|
) -> Token {
|
||||||
*i += 1;
|
*i += 1;
|
||||||
if *i >= bound {
|
if *i >= bound {
|
||||||
show_warning!("backslash at end of format");
|
show_warning!("{}", get_message("stat-warning-backslash-end-format"));
|
||||||
return Token::Char('\\');
|
return Token::Char('\\');
|
||||||
}
|
}
|
||||||
match chars[*i] {
|
match chars[*i] {
|
||||||
|
@ -728,22 +773,30 @@ impl Stater {
|
||||||
Token::Byte(value)
|
Token::Byte(value)
|
||||||
}
|
}
|
||||||
'x' => {
|
'x' => {
|
||||||
// Parse hexadecimal escape sequence
|
// Parse hexadecimal escape sequence (\xNN format)
|
||||||
|
// Uses UTF-8 safe byte indexing to handle multi-byte characters properly
|
||||||
if *i + 1 < bound {
|
if *i + 1 < bound {
|
||||||
if let Some((c, offset)) = format_str[*i + 1..].scan_char(16) {
|
let byte_index = Self::char_index_to_byte_index(format_str, *i + 1);
|
||||||
|
if let Some((c, offset)) = format_str[byte_index..].scan_char(16) {
|
||||||
*i += offset;
|
*i += offset;
|
||||||
Token::Byte(c as u8)
|
Token::Byte(c as u8)
|
||||||
} else {
|
} else {
|
||||||
show_warning!("unrecognized escape '\\x'");
|
show_warning!("{}", get_message("stat-warning-unrecognized-escape-x"));
|
||||||
Token::Byte(b'x')
|
Token::Byte(b'x')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
show_warning!("incomplete hex escape '\\x'");
|
show_warning!("{}", get_message("stat-warning-incomplete-hex-escape"));
|
||||||
Token::Byte(b'x')
|
Token::Byte(b'x')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
show_warning!("unrecognized escape '\\{other}'");
|
show_warning!(
|
||||||
|
"{}",
|
||||||
|
get_message_with_args(
|
||||||
|
"stat-warning-unrecognized-escape",
|
||||||
|
HashMap::from([("escape".to_string(), other.to_string())])
|
||||||
|
)
|
||||||
|
);
|
||||||
Token::Byte(other as u8)
|
Token::Byte(other as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -751,8 +804,8 @@ impl Stater {
|
||||||
|
|
||||||
fn generate_tokens(format_str: &str, use_printf: bool) -> UResult<Vec<Token>> {
|
fn generate_tokens(format_str: &str, use_printf: bool) -> UResult<Vec<Token>> {
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
let bound = format_str.len();
|
|
||||||
let chars = format_str.chars().collect::<Vec<char>>();
|
let chars = format_str.chars().collect::<Vec<char>>();
|
||||||
|
let bound = chars.len();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < bound {
|
while i < bound {
|
||||||
match chars.get(i) {
|
match chars.get(i) {
|
||||||
|
@ -785,10 +838,7 @@ impl Stater {
|
||||||
.map(|v| v.map(OsString::from).collect())
|
.map(|v| v.map(OsString::from).collect())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if files.is_empty() {
|
if files.is_empty() {
|
||||||
return Err(Box::new(USimpleError {
|
return Err(Box::new(StatError::MissingOperand) as Box<dyn UError>);
|
||||||
code: 1,
|
|
||||||
message: "missing operand\nTry 'stat --help' for more information.".to_string(),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
let format_str = if matches.contains_id(options::PRINTF) {
|
let format_str = if matches.contains_id(options::PRINTF) {
|
||||||
matches
|
matches
|
||||||
|
@ -819,8 +869,13 @@ impl Stater {
|
||||||
} else {
|
} else {
|
||||||
let mut mount_list = read_fs_list()
|
let mut mount_list = read_fs_list()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
let context = "cannot read table of mounted file systems";
|
USimpleError::new(
|
||||||
USimpleError::new(e.code(), format!("{context}: {e}"))
|
e.code(),
|
||||||
|
StatError::CannotReadFilesystem {
|
||||||
|
error: e.to_string(),
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
})?
|
})?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|mi| mi.mount_dir.clone())
|
.map(|mi| mi.mount_dir.clone())
|
||||||
|
@ -907,17 +962,17 @@ impl Stater {
|
||||||
match uucore::selinux::get_selinux_security_context(Path::new(file))
|
match uucore::selinux::get_selinux_security_context(Path::new(file))
|
||||||
{
|
{
|
||||||
Ok(ctx) => OutputType::Str(ctx),
|
Ok(ctx) => OutputType::Str(ctx),
|
||||||
Err(_) => OutputType::Str(
|
Err(_) => OutputType::Str(get_message(
|
||||||
"failed to get security context".to_string(),
|
"stat-selinux-failed-get-context",
|
||||||
),
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
OutputType::Str("unsupported on this system".to_string())
|
OutputType::Str(get_message("stat-selinux-unsupported-system"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "selinux"))]
|
#[cfg(not(feature = "selinux"))]
|
||||||
{
|
{
|
||||||
OutputType::Str("unsupported for this operating system".to_string())
|
OutputType::Str(get_message("stat-selinux-unsupported-os"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// device number in decimal
|
// device number in decimal
|
||||||
|
@ -1028,7 +1083,7 @@ impl Stater {
|
||||||
let display_name = file.to_string_lossy();
|
let display_name = file.to_string_lossy();
|
||||||
let file = if cfg!(unix) && display_name == "-" {
|
let file = if cfg!(unix) && display_name == "-" {
|
||||||
if self.show_fs {
|
if self.show_fs {
|
||||||
show_error!("using '-' to denote standard input does not work in file system mode");
|
show_error!("{}", StatError::StdinFilesystemMode);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if let Ok(p) = Path::new("/dev/stdin").canonicalize() {
|
if let Ok(p) = Path::new("/dev/stdin").canonicalize() {
|
||||||
|
@ -1055,8 +1110,11 @@ impl Stater {
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
show_error!(
|
show_error!(
|
||||||
"cannot read file system information for {}: {e}",
|
"{}",
|
||||||
display_name.quote(),
|
StatError::CannotReadFilesystemInfo {
|
||||||
|
file: display_name.quote().to_string(),
|
||||||
|
error: e.to_string()
|
||||||
|
}
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1092,7 +1150,13 @@ impl Stater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
show_error!("cannot stat {}: {e}", display_name.quote());
|
show_error!(
|
||||||
|
"{}",
|
||||||
|
StatError::CannotStat {
|
||||||
|
file: display_name.quote().to_string(),
|
||||||
|
error: e.to_string()
|
||||||
|
}
|
||||||
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1107,25 +1171,64 @@ impl Stater {
|
||||||
if terse {
|
if terse {
|
||||||
"%n %i %l %t %s %S %b %f %a %c %d\n".into()
|
"%n %i %l %t %s %S %b %f %a %c %d\n".into()
|
||||||
} else {
|
} else {
|
||||||
" File: \"%n\"\n ID: %-8i Namelen: %-7l Type: %T\nBlock \
|
format!(
|
||||||
size: %-10s Fundamental block size: %S\nBlocks: Total: %-10b \
|
" {}: \"%n\"\n {}: %-8i {}: %-7l {}: %T\n{} \
|
||||||
Free: %-10f Available: %a\nInodes: Total: %-10c Free: %d\n"
|
{}: %-10s {} {}: %S\n{}: {}: %-10b \
|
||||||
.into()
|
{}: %-10f {}: %a\n{}: {}: %-10c {}: %d\n",
|
||||||
|
get_message("stat-word-file"),
|
||||||
|
get_message("stat-word-id"),
|
||||||
|
get_message("stat-word-namelen"),
|
||||||
|
get_message("stat-word-type"),
|
||||||
|
get_message("stat-word-block"),
|
||||||
|
get_message("stat-word-size"),
|
||||||
|
get_message("stat-word-fundamental"),
|
||||||
|
get_message("stat-word-block-size"),
|
||||||
|
get_message("stat-word-blocks"),
|
||||||
|
get_message("stat-word-total"),
|
||||||
|
get_message("stat-word-free"),
|
||||||
|
get_message("stat-word-available"),
|
||||||
|
get_message("stat-word-inodes"),
|
||||||
|
get_message("stat-word-total"),
|
||||||
|
get_message("stat-word-free")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else if terse {
|
} else if terse {
|
||||||
"%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %W %o\n".into()
|
"%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %W %o\n".into()
|
||||||
} else {
|
} else {
|
||||||
[
|
let device_line = if show_dev_type {
|
||||||
" File: %N\n Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n",
|
format!(
|
||||||
if show_dev_type {
|
"{}: %Dh/%dd\t{}: %-10i {}: %-5h {} {}: %t,%T\n",
|
||||||
"Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n"
|
get_message("stat-word-device"),
|
||||||
} else {
|
get_message("stat-word-inode"),
|
||||||
"Device: %Dh/%dd\tInode: %-10i Links: %h\n"
|
get_message("stat-word-links"),
|
||||||
},
|
get_message("stat-word-device"),
|
||||||
"Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n",
|
get_message("stat-word-type")
|
||||||
"Access: %x\nModify: %y\nChange: %z\n Birth: %w\n",
|
)
|
||||||
]
|
} else {
|
||||||
.join("")
|
format!(
|
||||||
|
"{}: %Dh/%dd\t{}: %-10i {}: %h\n",
|
||||||
|
get_message("stat-word-device"),
|
||||||
|
get_message("stat-word-inode"),
|
||||||
|
get_message("stat-word-links")
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
format!(
|
||||||
|
" {}: %N\n {}: %-10s\t{}: %-10b {} {}: %-6o %F\n{}{}: (%04a/%10.10A) {}: (%5u/%8U) {}: (%5g/%8G)\n{}: %x\n{}: %y\n{}: %z\n {}: %w\n",
|
||||||
|
get_message("stat-word-file"),
|
||||||
|
get_message("stat-word-size"),
|
||||||
|
get_message("stat-word-blocks"),
|
||||||
|
get_message("stat-word-io"),
|
||||||
|
get_message("stat-word-block"),
|
||||||
|
device_line,
|
||||||
|
get_message("stat-word-access"),
|
||||||
|
get_message("stat-word-uid"),
|
||||||
|
get_message("stat-word-gid"),
|
||||||
|
get_message("stat-word-access"),
|
||||||
|
get_message("stat-word-modify"),
|
||||||
|
get_message("stat-word-change"),
|
||||||
|
get_message("stat-word-birth")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1155,42 +1258,35 @@ pub fn uu_app() -> Command {
|
||||||
Arg::new(options::DEREFERENCE)
|
Arg::new(options::DEREFERENCE)
|
||||||
.short('L')
|
.short('L')
|
||||||
.long(options::DEREFERENCE)
|
.long(options::DEREFERENCE)
|
||||||
.help("follow links")
|
.help(get_message("stat-help-dereference"))
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::FILE_SYSTEM)
|
Arg::new(options::FILE_SYSTEM)
|
||||||
.short('f')
|
.short('f')
|
||||||
.long(options::FILE_SYSTEM)
|
.long(options::FILE_SYSTEM)
|
||||||
.help("display file system status instead of file status")
|
.help(get_message("stat-help-file-system"))
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::TERSE)
|
Arg::new(options::TERSE)
|
||||||
.short('t')
|
.short('t')
|
||||||
.long(options::TERSE)
|
.long(options::TERSE)
|
||||||
.help("print the information in terse form")
|
.help(get_message("stat-help-terse"))
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::FORMAT)
|
Arg::new(options::FORMAT)
|
||||||
.short('c')
|
.short('c')
|
||||||
.long(options::FORMAT)
|
.long(options::FORMAT)
|
||||||
.help(
|
.help(get_message("stat-help-format"))
|
||||||
"use the specified FORMAT instead of the default;
|
|
||||||
output a newline after each use of FORMAT",
|
|
||||||
)
|
|
||||||
.value_name("FORMAT"),
|
.value_name("FORMAT"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::PRINTF)
|
Arg::new(options::PRINTF)
|
||||||
.long(options::PRINTF)
|
.long(options::PRINTF)
|
||||||
.value_name("FORMAT")
|
.value_name("FORMAT")
|
||||||
.help(
|
.help(get_message("stat-help-printf")),
|
||||||
"like --format, but interpret backslash escapes,
|
|
||||||
and do not output a mandatory trailing newline;
|
|
||||||
if you want a newline, include \n in FORMAT",
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::FILES)
|
Arg::new(options::FILES)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue