summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2020-02-23 11:54:33 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2020-04-06 12:37:03 +0100
commit4f4c7f8cc19cff5b9e36565af6f0c4411f1670ff (patch)
treef1511daea4f3bc286ff5924e48906fae853c3dd8
parentf98c579c7f97cdf33406d40ed23a0f89b2226c9b (diff)
downloadlibgit2-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.txt2
-rw-r--r--cli/cmd.c21
-rw-r--r--cli/cmd.h30
-rw-r--r--cli/cmd_help.c86
-rw-r--r--cli/main.c36
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;