summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2020-02-23 11:54:33 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2020-06-05 07:14:25 +0100
commit3826bd94e4de1824c827562929798691884659d3 (patch)
tree6bb12eb53c528536e3cd4ca2fd458666077b74b3
parentb836199acc526dc888b61d05920c57d1b7f6cb77 (diff)
downloadlibgit2-3826bd94e4de1824c827562929798691884659d3.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--src/cli/cmd.c21
-rw-r--r--src/cli/cmd.h30
-rw-r--r--src/cli/cmd_help.c86
-rw-r--r--src/cli/main.c50
4 files changed, 183 insertions, 4 deletions
diff --git a/src/cli/cmd.c b/src/cli/cmd.c
new file mode 100644
index 000000000..2a7e71cdb
--- /dev/null
+++ b/src/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/src/cli/cmd.h b/src/cli/cmd.h
new file mode 100644
index 000000000..816614efc
--- /dev/null
+++ b/src/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/src/cli/cmd_help.c b/src/cli/cmd_help.c
new file mode 100644
index 000000000..aceefa59f
--- /dev/null
+++ b/src/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/src/cli/main.c b/src/cli/main.c
index d69c1aa3c..932b6a588 100644
--- a/src/cli/main.c
+++ b/src/cli/main.c
@@ -9,35 +9,46 @@
#include <git2.h>
#include <git2client.h>
#include "cli.h"
+#include "cmd.h"
+static int show_help = 0;
static int show_version = 0;
static char *command = NULL;
static char **args = NULL;
-static const cli_opt_spec common_opts[] = {
+const cli_opt_spec cli_common_opts[] = {
+ { CLI_OPT_SWITCH, "help", 0, &show_help, 1, NULL, "display help information" },
{ 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;
-
+ char *help_args[3];
+ int help_args_len;
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 +69,37 @@ int main(int argc, char **argv)
goto done;
}
+ /* If "--help command" is specified, invoke "command --help" */
+ if (show_help && command) {
+ help_args[0] = (char *)command;
+ help_args[1] = "--help";
+ help_args[2] = NULL;
+ help_args_len = 2;
+
+ args = help_args;
+ args_len = help_args_len;
+ }
+
+ /* If there was no command, we want to invoke "help" */
+ else if (show_help || !command) {
+ help_args[0] = "help";
+ help_args[1] = NULL;
+ 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;