diff --git a/src/uu/pr/locales/en-US.ftl b/src/uu/pr/locales/en-US.ftl index c53c62a87..b4c0e10f2 100644 --- a/src/uu/pr/locales/en-US.ftl +++ b/src/uu/pr/locales/en-US.ftl @@ -13,6 +13,8 @@ pr-help-pages = Begin and stop printing with page FIRST_PAGE[:LAST_PAGE] pr-help-header = Use the string header to replace the file name in the header line. +pr-help-date-format = + Use 'date'-style FORMAT for the header date. pr-help-double-space = Produce output that is double spaced. An extra character is output following every found in the input. diff --git a/src/uu/pr/locales/fr-FR.ftl b/src/uu/pr/locales/fr-FR.ftl index 54b134d00..af5967872 100644 --- a/src/uu/pr/locales/fr-FR.ftl +++ b/src/uu/pr/locales/fr-FR.ftl @@ -13,6 +13,8 @@ pr-help-pages = Commencer et arrêter l'impression à la page PREMIÈRE_PAGE[:DE pr-help-header = Utiliser la chaîne d'en-tête pour remplacer le nom de fichier dans la ligne d'en-tête. +pr-help-date-format = + Utiliser le FORMAT de style 'date' pour la date dans la ligne d'en-tête. pr-help-double-space = Produire une sortie avec double espacement. Un caractère supplémentaire est affiché après chaque trouvé dans l'entrée. diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index 8f0c31489..10e908080 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -19,8 +19,8 @@ use thiserror::Error; use uucore::display::Quotable; use uucore::error::UResult; use uucore::format_usage; -use uucore::translate; use uucore::time::{FormatSystemTimeFallback, format, format_system_time}; +use uucore::translate; const TAB: char = '\t'; const LINES_PER_PAGE: usize = 66; @@ -36,6 +36,7 @@ const FF: u8 = 0x0C_u8; mod options { pub const HEADER: &str = "header"; + pub const DATE_FORMAT: &str = "date-format"; pub const DOUBLE_SPACE: &str = "double-space"; pub const NUMBER_LINES: &str = "number-lines"; pub const FIRST_LINE_NUMBER: &str = "first-line-number"; @@ -176,6 +177,13 @@ pub fn uu_app() -> Command { .help(translate!("pr-help-header")) .value_name("STRING"), ) + .arg( + Arg::new(options::DATE_FORMAT) + .short('D') + .long(options::DATE_FORMAT) + .value_name("FORMAT") + .help(translate!("pr-help-date-format")), + ) .arg( Arg::new(options::DOUBLE_SPACE) .short('d') @@ -401,16 +409,21 @@ fn parse_usize(matches: &ArgMatches, opt: &str) -> Option .map(from_parse_error_to_pr_error) } -fn get_date_format() -> String { - // Replicate behavior from GNU manual. - if std::env::var("POSIXLY_CORRECT").is_ok() - // TODO: This needs to be moved to uucore and handled by icu? - && (std::env::var("LC_TIME").unwrap_or_default() == "POSIX" - || std::env::var("LC_ALL").unwrap_or_default() == "POSIX") - { - "%b %e %H:%M %Y" - } else { - format::LONG_ISO +fn get_date_format(matches: &ArgMatches) -> String { + match matches.get_one::(options::DATE_FORMAT) { + Some(format) => format, + None => { + // Replicate behavior from GNU manual. + if std::env::var("POSIXLY_CORRECT").is_ok() + // TODO: This needs to be moved to uucore and handled by icu? + && (std::env::var("LC_TIME").unwrap_or_default() == "POSIX" + || std::env::var("LC_ALL").unwrap_or_default() == "POSIX") + { + "%b %e %H:%M %Y" + } else { + format::LONG_ISO + } + } } .to_string() } @@ -514,7 +527,7 @@ fn build_options( format_system_time( &mut v, time, - &get_date_format(), + &get_date_format(matches), FormatSystemTimeFallback::Integer, ) .ok() diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index 00a4fa1ad..06405bd07 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -404,6 +404,50 @@ fn test_with_offset_space_option() { #[test] fn test_with_date_format() { + let test_file_path = "test_one_page.log"; + let expected_test_file_path = "test_one_page.log.expected"; + let mut scenario = new_ucmd!(); + let value = file_last_modified_time_format(&scenario, test_file_path, "%Y__%s"); + scenario + .args(&[test_file_path, "-D", "%Y__%s"]) + .succeeds() + .stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]); + + // "Format" doesn't need to contain any replaceable token. + let mut scenario = new_ucmd!(); + scenario + .args(&[test_file_path, "-D", "Hello!"]) + .succeeds() + .stdout_is_templated_fixture( + expected_test_file_path, + &[("{last_modified_time}", "Hello!")], + ); + + // Long option also works + let mut scenario = new_ucmd!(); + scenario + .args(&[test_file_path, "--date-format=Hello!"]) + .succeeds() + .stdout_is_templated_fixture( + expected_test_file_path, + &[("{last_modified_time}", "Hello!")], + ); + + // Option takes precedence over environment variables + let mut scenario = new_ucmd!(); + scenario + .env("POSIXLY_CORRECT", "1") + .env("LC_TIME", "POSIX") + .args(&[test_file_path, "-D", "Hello!"]) + .succeeds() + .stdout_is_templated_fixture( + expected_test_file_path, + &[("{last_modified_time}", "Hello!")], + ); +} + +#[test] +fn test_with_date_format_env() { const POSIXLY_FORMAT: &str = "%b %e %H:%M %Y"; // POSIXLY_CORRECT + LC_ALL/TIME=POSIX uses "%b %e %H:%M %Y" date format