Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/uu/false/src/false.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
pub fn uu_app() -> Command {
Command::new("false")
.version(crate_version!())
.help_template(uucore::localized_help_template("false"))
// GH #10279: GNU `false --help` puts `Usage:` on the first line.
.help_template(uucore::localized_help_template_usage_first("false"))
.about(translate!("false-about"))
// We provide our own help and version options, to ensure maximum compatibility with GNU.
.disable_help_flag(true)
Expand Down
3 changes: 2 additions & 1 deletion src/uu/true/src/true.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
pub fn uu_app() -> Command {
Command::new("true")
.version(crate_version!())
.help_template(uucore::localized_help_template("true"))
// GH #10279: GNU `true --help` puts `Usage:` on the first line.
.help_template(uucore::localized_help_template_usage_first("true"))
.about(translate!("true-about"))
// We provide our own help and version options, to ensure maximum compatibility with GNU.
.disable_help_flag(true)
Expand Down
51 changes: 51 additions & 0 deletions src/uucore/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,57 @@ pub fn localized_help_template_with_colors(
template
}

/// Creates a localized help template that places the `Usage:` line first,
/// matching GNU coreutils' help output ordering.
///
/// The default [`localized_help_template`] puts the `about` text before the
/// usage line. GNU coreutils — including `true(1)` and `false(1)` — puts
/// `Usage:` on the very first line of `--help`. Use this variant for
/// utilities that need that GNU-style layout (see GH #10279).
pub fn localized_help_template_usage_first(util_name: &str) -> clap::builder::StyledStr {
use std::io::IsTerminal;

let colors_enabled = if std::env::var("NO_COLOR").is_ok() {
false
} else if std::env::var("CLICOLOR_FORCE").is_ok() || std::env::var("FORCE_COLOR").is_ok() {
true
} else {
IsTerminal::is_terminal(&std::io::stdout())
&& std::env::var("TERM").unwrap_or_default() != "dumb"
};

localized_help_template_usage_first_with_colors(util_name, colors_enabled)
}

/// Like [`localized_help_template_usage_first`] but with explicit color control.
pub fn localized_help_template_usage_first_with_colors(
util_name: &str,
colors_enabled: bool,
) -> clap::builder::StyledStr {
use std::fmt::Write;

let _ = locale::setup_localization(util_name);
let usage_label = crate::locale::translate!("common-usage");

let mut template = clap::builder::StyledStr::new();

// Usage first.
if colors_enabled {
writeln!(template, "\x1b[1m\x1b[4m{usage_label}:\x1b[0m {{usage}}").unwrap();
} else {
writeln!(template, "{usage_label}: {{usage}}").unwrap();
}

// Then the about text, then options.
write!(
template,
"{{before-help}}{{about-with-newline}}\n{{all-args}}{{after-help}}"
)
.unwrap();

template
}

/// Used to check if the utility is the second argument.
/// Used to check if we were called as a multicall binary (`coreutils <utility>`)
pub fn get_utility_is_second_arg() -> bool {
Expand Down
13 changes: 13 additions & 0 deletions tests/by-util/test_false.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ fn test_help() {
.stdout_contains("false");
}

/// GH #10279: GNU `false --help` opens with the `Usage:` line. The previous
/// uutils help template put the about text first, breaking config tools
/// that grep the first line.
#[test]
fn test_help_starts_with_usage() {
let result = new_ucmd!().args(&["--help"]).fails();
let first_line = result.stdout_str().lines().next().unwrap_or("");
assert!(
first_line.starts_with("Usage:"),
"expected --help first line to start with 'Usage:', got {first_line:?}"
);
}

#[test]
fn test_short_options() {
for option in ["-h", "-V"] {
Expand Down
13 changes: 13 additions & 0 deletions tests/by-util/test_true.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ fn test_help() {
.stdout_contains("true");
}

/// GH #10279: GNU `true --help` opens with the `Usage:` line. The previous
/// uutils help template put the about text first, breaking config tools
/// that grep the first line.
#[test]
fn test_help_starts_with_usage() {
let result = new_ucmd!().args(&["--help"]).succeeds();
let first_line = result.stdout_str().lines().next().unwrap_or("");
assert!(
first_line.starts_with("Usage:"),
"expected --help first line to start with 'Usage:', got {first_line:?}"
);
}

#[test]
fn test_short_options() {
for option in ["-h", "-V"] {
Expand Down
Loading