Search code examples
cargp

Replace `?` key with `h` key in GNU argp


I'm using the argp.h library to parse command line arguments for my program.

I setup it a little and works, but there is just a thing I don't like.

When I run my program with --help it show this:

-?, --help                 Give this help list
    --usage                Give a short usage message
-V, --version              Print program version

Which is correct, but why the short for --help is -?? Is it possible to specify a different key, like -h with a macro maybe?

I took a look at the docs but I didn't find any info about this, except maybe define a custom key, but I don't like this approach or better, I don't know if this is the preferred way.


Solution

  • Some digging around reveals the location in which '?' is defined as the short option for the default help functionality.

    Without getting too lost in the source, it appears that when the default help is enabled, the user's argp and default argp (and if applicable, the version argp) are grouped together as siblings (see also: children), and then the program arguments are parsed. There does not appear to be an interface for changing the default values before this occurs, as the short option is hard coded.

    Turning off the default help / usage options is possible by passing the ARGP_NO_HELP flag to argp_parse.

    argp_state_help can be used to replicate the behaviour of the default options, using various flags. The flags used by the default options are:

    • --help: ARGP_HELP_STD_HELP
    • --usage: ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK

    Where ARGP_HELP_EXIT_OK results in exit(0).

    If not using argp_program_version_hook the behaviour of --version is to simply print argp_program_version.

    A rough example:

    #include <argp.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #define USAGE 0x123
    
    const char *argp_program_version = "example 1.0";
    const char *argp_program_bug_address = "<so@example.com>";
    
    static struct argp_option options[] = {
        { "help", 'h', 0, 0, "show this message", -1 },
        { "version", 'V', 0, 0, "show version", -1 },
        { "usage", USAGE, 0, 0, "show usage", 0 },
        { 0 }
    };
    
    static error_t parse_opt(int key, char *arg, struct argp_state *state)
    {
        switch (key) {
            case 'h':
                argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
                break;
            case USAGE:
                argp_state_help(state, state->out_stream, ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
                break;
            case 'V':
                fprintf(state->out_stream, "%s\n", argp_program_version)
                exit(0);
                break;
            default:
                return ARGP_ERR_UNKNOWN;
        }
    
        return 0;
    }
    
    int main(int argc, char **argv)
    {
        struct argp argp = { options, parse_opt };
        argp_parse(&argp, argc, argv, ARGP_NO_HELP, 0, NULL);
    }