diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2020-02-23 11:54:33 +0000 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2020-04-06 12:37:03 +0100 |
commit | 4f4c7f8cc19cff5b9e36565af6f0c4411f1670ff (patch) | |
tree | f1511daea4f3bc286ff5924e48906fae853c3dd8 | |
parent | f98c579c7f97cdf33406d40ed23a0f89b2226c9b (diff) | |
download | libgit2-4f4c7f8cc19cff5b9e36565af6f0c4411f1670ff.tar.gz |
cli: introduce a help command
Set up a framework for subcommands, and introduce the first, "help".
Help will display the commands available, and information about the
help command itself.
Commands are expected to provide their own usage and help information,
which the help command will proxy to when necessary.
-rw-r--r-- | cli/CMakeLists.txt | 2 | ||||
-rw-r--r-- | cli/cmd.c | 21 | ||||
-rw-r--r-- | cli/cmd.h | 30 | ||||
-rw-r--r-- | cli/cmd_help.c | 86 | ||||
-rw-r--r-- | cli/main.c | 36 |
5 files changed, 169 insertions, 6 deletions
diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index ac40b3298..785ed857e 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -2,7 +2,7 @@ PROJECT(git2_cli C) ADD_DEFINITIONS(-DGIT_DEPRECATE_HARD) -FILE(GLOB CLI_SRC *.c) +FILE(GLOB CLI_SRC *.c cmd/*.c) IF(WIN32 AND NOT CYGWIN) FILE(GLOB CLI_SRC_OS win32/*.c) diff --git a/cli/cmd.c b/cli/cmd.c new file mode 100644 index 000000000..2a7e71cdb --- /dev/null +++ b/cli/cmd.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "cli.h" +#include "cmd.h" + +const cli_cmd_spec *cli_cmd_spec_byname(const char *name) +{ + const cli_cmd_spec *cmd; + + for (cmd = cli_cmds; cmd->name; cmd++) { + if (!strcmp(cmd->name, name)) + return cmd; + } + + return NULL; +} diff --git a/cli/cmd.h b/cli/cmd.h new file mode 100644 index 000000000..816614efc --- /dev/null +++ b/cli/cmd.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef CLI_cmd_h__ +#define CLI_cmd_h__ + +/* Command definitions */ +typedef struct { + const char *name; + int (*fn)(int argc, char **argv); + const char *desc; +} cli_cmd_spec; + +/* Options that are common to all commands (eg --help, --git-dir) */ +extern const cli_opt_spec cli_common_opts[]; + +/* All the commands supported by the CLI */ +extern const cli_cmd_spec cli_cmds[]; + +/* Find a command by name */ +extern const cli_cmd_spec *cli_cmd_spec_byname(const char *name); + +/* Commands */ +extern int cmd_help(int argc, char **argv); + +#endif /* CLI_cmd_h__ */ diff --git a/cli/cmd_help.c b/cli/cmd_help.c new file mode 100644 index 000000000..aceefa59f --- /dev/null +++ b/cli/cmd_help.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include <stdio.h> +#include <git2.h> +#include "cli.h" +#include "cmd.h" + +static char *command; +static int show_help; + +static const cli_opt_spec opts[] = { + { CLI_OPT_SWITCH, "help", 0, &show_help, 1, NULL, "display help about the help command", CLI_OPT_USAGE_HIDDEN }, + { CLI_OPT_ARG, "command", 0, &command, 0, "command", "the command to show help for" }, + { 0 }, +}; + +static void print_help(void) +{ + cli_opt_usage_fprint(stdout, PROGRAM_NAME, "help", opts); + printf("\n"); + + printf("Display help information about %s. If a command is specified, help\n", PROGRAM_NAME); + printf("about that command will be shown. Otherwise, general information about\n"); + printf("%s will be shown, including the commands available.\n", PROGRAM_NAME); +} + +static void print_commands(void) +{ + const cli_cmd_spec *cmd; + + cli_opt_usage_fprint(stdout, PROGRAM_NAME, NULL, cli_common_opts); + printf("\n"); + + printf("These are the %s commands available:\n\n", PROGRAM_NAME); + + for (cmd = cli_cmds; cmd->name; cmd++) + printf(" %-8s %s\n", cmd->name, cmd->desc); + + printf("\nSee '%s help <command>' for more information on a specific command.\n", PROGRAM_NAME); +} + +int cmd_help(int argc, char **argv) +{ + char *fake_args[] = { command, "--help" }; + const cli_cmd_spec *cmd; + cli_opt invalid_opt; + + if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1)) { + cli_opt_status_fprint(stderr, &invalid_opt); + cli_opt_usage_fprint(stdout, PROGRAM_NAME, "help", opts); + return 129; + + } + + /* Show the meta-help */ + if (show_help) { + print_help(); + return 0; + } + + /* We were not asked to show help for a specific command. */ + if (!command) { + print_commands(); + return 0; + } + + /* + * If we were asked for help for a specific command, delegate + * back to that command. This allows us to keep the help logic + * for each command in one place. Emulate the command-line + * arguments that would get us there. + */ + + if ((cmd = cli_cmd_spec_byname(command)) == NULL) { + fprintf(stderr, "%s: '%s' is not a %s command. See '%s --help'.\n", + PROGRAM_NAME, command, PROGRAM_NAME, PROGRAM_NAME); + return 1; + } + + return cmd->fn(2, fake_args); +} diff --git a/cli/main.c b/cli/main.c index 326b055ce..cb1738266 100644 --- a/cli/main.c +++ b/cli/main.c @@ -9,35 +9,42 @@ #include <git2.h> #include <git2client.h> #include "cli.h" +#include "cmd.h" -static int show_version = 0; +static int show_version; static const char *command = NULL; static char **args = NULL; -static const cli_opt_spec common_opts[] = { +const cli_opt_spec cli_common_opts[] = { { CLI_OPT_SWITCH, "version", 0, &show_version, 1, NULL, "display the version" }, { CLI_OPT_ARG, "command", 0, &command, 0, "command", "the command to run", CLI_OPT_USAGE_REQUIRED }, { CLI_OPT_ARGS, "args", 0, &args, 0, "args", "arguments for the command" }, { 0 } }; +const cli_cmd_spec cli_cmds[] = { + { "help", cmd_help, "Display help information" }, + { NULL } +}; + int main(int argc, char **argv) { + const cli_cmd_spec *cmd; cli_opt_parser optparser; cli_opt opt; - int args_len = 0; int error = 0; if (cli_global_init() < 0) cli_die("error: failed to initialize libgit2"); - cli_opt_parser_init(&optparser, common_opts, argv + 1, argc - 1); + cli_opt_parser_init(&optparser, cli_common_opts, argv + 1, argc - 1); /* Parse the top-level (common) options and command information */ while (cli_opt_parser_next(&opt, &optparser)) { if (!opt.spec) { - cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, common_opts); + cli_opt_status_fprint(stderr, &opt); + cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, cli_common_opts); error = 129; goto done; } @@ -58,6 +65,25 @@ int main(int argc, char **argv) goto done; } + /* If there was no command, we want to invoke "help" */ + if (!command) { + static char *help_args[] = { "help", NULL }; + static int help_args_len = 1; + + command = help_args[0]; + args = help_args; + args_len = help_args_len; + } + + if ((cmd = cli_cmd_spec_byname(command)) == NULL) { + fprintf(stderr, "%s: '%s' is not a %s command. See '%s --help'.\n", + PROGRAM_NAME, command, PROGRAM_NAME, PROGRAM_NAME); + error = 1; + goto done; + } + + error = cmd->fn(args_len, args); + done: cli_global_shutdown(); return error; |