diff --git a/Cargo.lock b/Cargo.lock index 031c9e329..07c988bd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3498,6 +3498,7 @@ version = "0.1.0" dependencies = [ "clap", "tempfile", + "thiserror 2.0.12", "uu_stdbuf_libstdbuf", "uucore", ] diff --git a/src/uu/stdbuf/Cargo.toml b/src/uu/stdbuf/Cargo.toml index 0da391f49..13559422e 100644 --- a/src/uu/stdbuf/Cargo.toml +++ b/src/uu/stdbuf/Cargo.toml @@ -23,6 +23,7 @@ clap = { workspace = true } libstdbuf = { package = "uu_stdbuf_libstdbuf", path = "src/libstdbuf" } tempfile = { workspace = true } uucore = { workspace = true, features = ["parser"] } +thiserror = { workspace = true } # "feat_external_libstdbuf": use an external libstdbuf.so for stdbuf instead of embedding it into # the stdbuf binary. diff --git a/src/uu/stdbuf/locales/en-US.ftl b/src/uu/stdbuf/locales/en-US.ftl index fb968f985..f2f845a07 100644 --- a/src/uu/stdbuf/locales/en-US.ftl +++ b/src/uu/stdbuf/locales/en-US.ftl @@ -14,3 +14,18 @@ stdbuf-after-help = If MODE is 'L' the corresponding stream will be line buffere NOTE: If COMMAND adjusts the buffering of its standard streams (tee does for e.g.) then that will override corresponding settings changed by stdbuf. Also some filters (like dd and cat etc.) don't use streams for I/O, and are thus unaffected by stdbuf settings. + +stdbuf-help-input = adjust standard input stream buffering +stdbuf-help-output = adjust standard output stream buffering +stdbuf-help-error = adjust standard error stream buffering +stdbuf-value-mode = MODE + +stdbuf-error-line-buffering-stdin-meaningless = line buffering stdin is meaningless +stdbuf-error-invalid-mode = invalid mode {$error} +stdbuf-error-value-too-large = invalid mode '{$value}': Value too large for defined data type +stdbuf-error-command-not-supported = Command not supported for this operating system! +stdbuf-error-external-libstdbuf-not-found = External libstdbuf not found at configured path: {$path} +stdbuf-error-permission-denied = failed to execute process: Permission denied +stdbuf-error-no-such-file = failed to execute process: No such file or directory +stdbuf-error-failed-to-execute = failed to execute process: {$error} +stdbuf-error-killed-by-signal = process killed by signal {$signal} diff --git a/src/uu/stdbuf/locales/fr-FR.ftl b/src/uu/stdbuf/locales/fr-FR.ftl new file mode 100644 index 000000000..5016c6914 --- /dev/null +++ b/src/uu/stdbuf/locales/fr-FR.ftl @@ -0,0 +1,31 @@ +stdbuf-about = Exécute COMMANDE, avec des opérations de mise en mémoire tampon modifiées pour ses flux standards. + + Les arguments obligatoires pour les options longues le sont aussi pour les options courtes. +stdbuf-usage = stdbuf [OPTION]... COMMANDE +stdbuf-after-help = Si MODE est 'L', le flux correspondant sera mis en mémoire tampon par ligne. + Cette option n'est pas valide avec l'entrée standard. + + Si MODE est '0', le flux correspondant ne sera pas mis en mémoire tampon. + + Sinon, MODE est un nombre qui peut être suivi par l'un des suivants : + + KB 1000, K 1024, MB 1000*1000, M 1024*1024, et ainsi de suite pour G, T, P, E, Z, Y. + Dans ce cas, le flux correspondant sera entièrement mis en mémoire tampon avec la taille de tampon définie à MODE octets. + + NOTE : Si COMMANDE ajuste la mise en mémoire tampon de ses flux standards (tee le fait par exemple), cela remplacera les paramètres correspondants modifiés par stdbuf. + De plus, certains filtres (comme dd et cat etc.) n'utilisent pas de flux pour les E/S, et ne sont donc pas affectés par les paramètres stdbuf. + +stdbuf-help-input = ajuster la mise en mémoire tampon du flux d'entrée standard +stdbuf-help-output = ajuster la mise en mémoire tampon du flux de sortie standard +stdbuf-help-error = ajuster la mise en mémoire tampon du flux d'erreur standard +stdbuf-value-mode = MODE + +stdbuf-error-line-buffering-stdin-meaningless = la mise en mémoire tampon par ligne de stdin n'a pas de sens +stdbuf-error-invalid-mode = mode invalide {$error} +stdbuf-error-value-too-large = mode invalide '{$value}' : Valeur trop grande pour le type de données défini +stdbuf-error-command-not-supported = Commande non prise en charge pour ce système d'exploitation ! +stdbuf-error-external-libstdbuf-not-found = libstdbuf externe introuvable au chemin configuré : {$path} +stdbuf-error-permission-denied = échec de l'exécution du processus : Permission refusée +stdbuf-error-no-such-file = échec de l'exécution du processus : Aucun fichier ou répertoire de ce type +stdbuf-error-failed-to-execute = échec de l'exécution du processus : {$error} +stdbuf-error-killed-by-signal = processus tué par le signal {$signal} diff --git a/src/uu/stdbuf/src/stdbuf.rs b/src/uu/stdbuf/src/stdbuf.rs index 8f565e796..2e8f9cfa8 100644 --- a/src/uu/stdbuf/src/stdbuf.rs +++ b/src/uu/stdbuf/src/stdbuf.rs @@ -6,16 +6,18 @@ // spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf use clap::{Arg, ArgAction, ArgMatches, Command}; +use std::collections::HashMap; use std::os::unix::process::ExitStatusExt; use std::path::PathBuf; use std::process; use tempfile::TempDir; use tempfile::tempdir; +use thiserror::Error; use uucore::error::{FromIo, UClapError, UResult, USimpleError, UUsageError}; use uucore::format_usage; use uucore::parser::parse_size::parse_size_u64; -use uucore::locale::get_message; +use uucore::locale::{get_message, get_message_with_args}; mod options { pub const INPUT: &str = "input"; @@ -66,7 +68,15 @@ impl TryFrom<&ArgMatches> for ProgramOptions { } } -struct ProgramOptionsError(String); +#[derive(Debug, Error)] +enum ProgramOptionsError { + #[error("{}", get_message("stdbuf-error-line-buffering-stdin-meaningless"))] + LineBufferingStdinMeaningless, + #[error("{}", get_message_with_args("stdbuf-error-invalid-mode", HashMap::from([("error".to_string(), _0.clone())])))] + InvalidMode(String), + #[error("{}", get_message_with_args("stdbuf-error-value-too-large", HashMap::from([("value".to_string(), _0.clone())])))] + ValueTooLarge(String), +} #[cfg(any( target_os = "linux", @@ -95,7 +105,7 @@ fn preload_strings() -> UResult<(&'static str, &'static str)> { fn preload_strings() -> UResult<(&'static str, &'static str)> { Err(USimpleError::new( 1, - "Command not supported for this operating system!", + get_message("stdbuf-error-command-not-supported"), )) } @@ -104,20 +114,16 @@ fn check_option(matches: &ArgMatches, name: &str) -> Result match value.as_str() { "L" => { if name == options::INPUT { - Err(ProgramOptionsError( - "line buffering stdin is meaningless".to_string(), - )) + Err(ProgramOptionsError::LineBufferingStdinMeaningless) } else { Ok(BufferType::Line) } } x => parse_size_u64(x).map_or_else( - |e| Err(ProgramOptionsError(format!("invalid mode {e}"))), + |e| Err(ProgramOptionsError::InvalidMode(e.to_string())), |m| { Ok(BufferType::Size(m.try_into().map_err(|_| { - ProgramOptionsError(format!( - "invalid mode '{x}': Value too large for defined data type" - )) + ProgramOptionsError::ValueTooLarge(x.to_string()) })?)) }, ), @@ -168,9 +174,9 @@ fn get_preload_env(_tmp_dir: &TempDir) -> UResult<(String, PathBuf)> { Err(USimpleError::new( 1, - format!( - "External libstdbuf not found at configured path: {}", - path_buf.display() + get_message_with_args( + "stdbuf-error-external-libstdbuf-not-found", + HashMap::from([("path".to_string(), path_buf.display().to_string())]), ), )) } @@ -179,7 +185,8 @@ fn get_preload_env(_tmp_dir: &TempDir) -> UResult<(String, PathBuf)> { pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args).with_exit_code(125)?; - let options = ProgramOptions::try_from(&matches).map_err(|e| UUsageError::new(125, e.0))?; + let options = + ProgramOptions::try_from(&matches).map_err(|e| UUsageError::new(125, e.to_string()))?; let mut command_values = matches.get_many::(options::COMMAND).unwrap(); let mut command = process::Command::new(command_values.next().unwrap()); @@ -193,20 +200,25 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { set_command_env(&mut command, "_STDBUF_E", &options.stderr); command.args(command_params); - const EXEC_ERROR: &str = "failed to execute process:"; let mut process = match command.spawn() { Ok(p) => p, Err(e) => { return match e.kind() { std::io::ErrorKind::PermissionDenied => Err(USimpleError::new( 126, - format!("{EXEC_ERROR} Permission denied"), + get_message("stdbuf-error-permission-denied"), )), std::io::ErrorKind::NotFound => Err(USimpleError::new( 127, - format!("{EXEC_ERROR} No such file or directory"), + get_message("stdbuf-error-no-such-file"), + )), + _ => Err(USimpleError::new( + 1, + get_message_with_args( + "stdbuf-error-failed-to-execute", + HashMap::from([("error".to_string(), e.to_string())]), + ), )), - _ => Err(USimpleError::new(1, format!("{EXEC_ERROR} {e}"))), }; } }; @@ -222,7 +234,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } None => Err(USimpleError::new( 1, - format!("process killed by signal {}", status.signal().unwrap()), + get_message_with_args( + "stdbuf-error-killed-by-signal", + HashMap::from([("signal".to_string(), status.signal().unwrap().to_string())]), + ), )), } } @@ -239,7 +254,7 @@ pub fn uu_app() -> Command { Arg::new(options::INPUT) .long(options::INPUT) .short(options::INPUT_SHORT) - .help("adjust standard input stream buffering") + .help(get_message("stdbuf-help-input")) .value_name("MODE") .required_unless_present_any([options::OUTPUT, options::ERROR]), ) @@ -247,7 +262,7 @@ pub fn uu_app() -> Command { Arg::new(options::OUTPUT) .long(options::OUTPUT) .short(options::OUTPUT_SHORT) - .help("adjust standard output stream buffering") + .help(get_message("stdbuf-help-output")) .value_name("MODE") .required_unless_present_any([options::INPUT, options::ERROR]), ) @@ -255,7 +270,7 @@ pub fn uu_app() -> Command { Arg::new(options::ERROR) .long(options::ERROR) .short(options::ERROR_SHORT) - .help("adjust standard error stream buffering") + .help(get_message("stdbuf-help-error")) .value_name("MODE") .required_unless_present_any([options::INPUT, options::OUTPUT]), )