1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

false,true: Implement custom help, version

This avoids hacking around the short options of these command line
arguments that have been introduced by clap. Additionally, we test and
correctly handle the combination of both version and help. The GNU
binary will ignore both arguments in this case while clap would perform
the first one. A test for this edge case was added.
This commit is contained in:
Andreas Molzer 2022-02-01 14:29:26 +01:00
parent c1e108933f
commit 23a544c485
4 changed files with 69 additions and 38 deletions

View file

@ -4,7 +4,7 @@
// * // *
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
use clap::{App, Arg, ArgSettings, ErrorKind}; use clap::{App, AppSettings, Arg};
use std::io::Write; use std::io::Write;
use uucore::error::{set_exit_code, UResult}; use uucore::error::{set_exit_code, UResult};
@ -18,7 +18,7 @@ static ABOUT: &str = "
#[uucore::main] #[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let app = uu_app(); let mut app = uu_app();
// Mirror GNU options, always return `1`. In particular even the 'successful' cases of no-op, // Mirror GNU options, always return `1`. In particular even the 'successful' cases of no-op,
// and the interrupted display of help and version should return `1`. Also, we return Ok in all // and the interrupted display of help and version should return `1`. Also, we return Ok in all
@ -26,12 +26,19 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// and unwind through the standard library allocation handling machinery. // and unwind through the standard library allocation handling machinery.
set_exit_code(1); set_exit_code(1);
if let Err(err) = app.try_get_matches_from(args) { if let Ok(matches) = app.try_get_matches_from_mut(args) {
if let ErrorKind::DisplayHelp | ErrorKind::DisplayVersion = err.kind { let error = if matches.index_of("help").is_some() {
if let Err(print_fail) = err.print() { app.print_long_help()
// Try to display this error. } else if matches.index_of("version").is_some() {
let _ = writeln!(std::io::stderr(), "{}: {}", uucore::util_name(), print_fail); writeln!(std::io::stdout(), "{}", app.render_version())
} } else {
Ok(())
};
// Try to display this error.
if let Err(print_fail) = error {
// Completely ignore any error here, no more failover and we will fail in any case.
let _ = writeln!(std::io::stderr(), "{}: {}", uucore::util_name(), print_fail);
} }
} }
@ -42,16 +49,17 @@ pub fn uu_app<'a>() -> App<'a> {
App::new(uucore::util_name()) App::new(uucore::util_name())
.version(clap::crate_version!()) .version(clap::crate_version!())
.about(ABOUT) .about(ABOUT)
// Hide the default -V and -h for version and help. // We provide our own help and version options, to ensure maximum compatibility with GNU.
// This requires us to overwrite short, not short_aliases. .setting(AppSettings::DisableHelpFlag | AppSettings::DisableVersionFlag)
.arg( .arg(
Arg::new("dummy-help") Arg::new("help")
.short('h') .long("help")
.setting(ArgSettings::Hidden), .help("Print help information")
.exclusive(true),
) )
.arg( .arg(
Arg::new("dummy-version") Arg::new("version")
.short('V') .long("version")
.setting(ArgSettings::Hidden), .help("Print version information"),
) )
} }

View file

@ -4,7 +4,7 @@
// * // *
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
use clap::{App, Arg, ArgSettings, ErrorKind}; use clap::{App, AppSettings, Arg};
use std::io::Write; use std::io::Write;
use uucore::error::{set_exit_code, UResult}; use uucore::error::{set_exit_code, UResult};
@ -18,18 +18,24 @@ static ABOUT: &str = "
#[uucore::main] #[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let app = uu_app(); let mut app = uu_app();
if let Err(err) = app.try_get_matches_from(args) { if let Ok(matches) = app.try_get_matches_from_mut(args) {
if let ErrorKind::DisplayHelp | ErrorKind::DisplayVersion = err.kind { let error = if matches.index_of("help").is_some() {
if let Err(print_fail) = err.print() { app.print_long_help()
// Try to display this error. } else if matches.index_of("version").is_some() {
let _ = writeln!(std::io::stderr(), "{}: {}", uucore::util_name(), print_fail); writeln!(std::io::stdout(), "{}", app.render_version())
// Mirror GNU options. When failing to print warnings or version flags, then we exit } else {
// with FAIL. This avoids allocation some error information which may result in yet Ok(())
// other types of failure. };
set_exit_code(1);
} if let Err(print_fail) = error {
// Try to display this error.
let _ = writeln!(std::io::stderr(), "{}: {}", uucore::util_name(), print_fail);
// Mirror GNU options. When failing to print warnings or version flags, then we exit
// with FAIL. This avoids allocation some error information which may result in yet
// other types of failure.
set_exit_code(1);
} }
} }
@ -40,16 +46,17 @@ pub fn uu_app<'a>() -> App<'a> {
App::new(uucore::util_name()) App::new(uucore::util_name())
.version(clap::crate_version!()) .version(clap::crate_version!())
.about(ABOUT) .about(ABOUT)
// Hide the default -V and -h for version and help. // We provide our own help and version options, to ensure maximum compatibility with GNU.
// This requires us to overwrite short, not short_aliases. .setting(AppSettings::DisableHelpFlag | AppSettings::DisableVersionFlag)
.arg( .arg(
Arg::new("dummy-help") Arg::new("help")
.short('h') .long("help")
.setting(ArgSettings::Hidden), .help("Print help information")
.exclusive(true),
) )
.arg( .arg(
Arg::new("dummy-version") Arg::new("version")
.short('V') .long("version")
.setting(ArgSettings::Hidden), .help("Print version information"),
) )
} }

View file

@ -25,11 +25,19 @@ fn test_help() {
#[test] #[test]
fn test_short_options() { fn test_short_options() {
for option in ["-h", "-v"] { for option in ["-h", "-V"] {
new_ucmd!().arg(option).fails().stdout_is(""); new_ucmd!().arg(option).fails().stdout_is("");
} }
} }
#[test]
fn test_conflict() {
new_ucmd!()
.args(&["--help", "--version"])
.fails()
.stdout_is("");
}
#[test] #[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))] #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_full() { fn test_full() {

View file

@ -25,11 +25,19 @@ fn test_help() {
#[test] #[test]
fn test_short_options() { fn test_short_options() {
for option in ["-h", "-v"] { for option in ["-h", "-V"] {
new_ucmd!().arg(option).succeeds().stdout_is(""); new_ucmd!().arg(option).succeeds().stdout_is("");
} }
} }
#[test]
fn test_conflict() {
new_ucmd!()
.args(&["--help", "--version"])
.succeeds()
.stdout_is("");
}
#[test] #[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))] #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_full() { fn test_full() {