summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2021-11-26 09:37:29 -0500
committerEdward Thomson <ethomson@edwardthomson.com>2021-11-29 08:16:18 -0500
commite1ae135b27871e8fd8e87b3888fce63cfdc5a062 (patch)
tree0c456df4f204d048b941c7511b9a83474a52c985
parent19d5d6ce16d75f778b93515c503c7bdf37b9fee5 (diff)
downloadlibgit2-e1ae135b27871e8fd8e87b3888fce63cfdc5a062.tar.gz
opt: use a custom function to print usage
Our argument parser (https://github.com/ethomson/adopt) includes a function to print a usage message based on the allowed options. Omit this and use a cutom function that understands that we have subcommands ("checkout", "revert", etc) that each have their own options.
-rw-r--r--src/cli/cli.h1
-rw-r--r--src/cli/main.c2
-rw-r--r--src/cli/opt.c66
-rw-r--r--src/cli/opt.h15
-rw-r--r--src/cli/opt_usage.c125
-rw-r--r--src/cli/opt_usage.h26
6 files changed, 155 insertions, 80 deletions
diff --git a/src/cli/cli.h b/src/cli/cli.h
index 3fddae3b0..cc169511b 100644
--- a/src/cli/cli.h
+++ b/src/cli/cli.h
@@ -11,6 +11,7 @@
#include "git2_util.h"
#include "opt.h"
+#include "opt_usage.h"
#define PROGRAM_NAME "git2"
diff --git a/src/cli/main.c b/src/cli/main.c
index 2c36c713c..b5f464012 100644
--- a/src/cli/main.c
+++ b/src/cli/main.c
@@ -33,7 +33,7 @@ int main(int argc, char **argv)
while (cli_opt_parser_next(&opt, &optparser)) {
if (!opt.spec) {
cli_opt_status_fprint(stderr, &opt);
- cli_opt_usage_fprint(stderr, PROGRAM_NAME, common_opts);
+ cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, common_opts);
error = 129;
goto done;
}
diff --git a/src/cli/opt.c b/src/cli/opt.c
index 40f013c35..ad1592dd1 100644
--- a/src/cli/opt.c
+++ b/src/cli/opt.c
@@ -10,7 +10,7 @@
* This file was produced by using the `rename.pl` script included with
* adopt. The command-line specified was:
*
- * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status
+ * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
*/
#include <stdlib.h>
@@ -283,67 +283,3 @@ int cli_opt_status_fprint(
return error;
}
-int cli_opt_usage_fprint(
- FILE *file,
- const char *command,
- const cli_opt_spec specs[])
-{
- const cli_opt_spec *spec;
- int choice = 0;
- int error;
-
- if ((error = fprintf(file, "usage: %s", command)) < 0)
- goto done;
-
- for (spec = specs; spec->type; ++spec) {
- int optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
-
- if (spec->usage & CLI_OPT_USAGE_HIDDEN)
- continue;
-
- if (choice)
- error = fprintf(file, "|");
- else
- error = fprintf(file, " ");
-
- if (error < 0)
- goto done;
-
- if (optional && !choice && (error = fprintf(file, "[")) < 0)
- goto done;
-
- if (spec->type == CLI_OPT_VALUE && spec->alias)
- error = fprintf(file, "-%c <%s>", spec->alias, spec->value_name);
- else if (spec->type == CLI_OPT_VALUE)
- error = fprintf(file, "--%s=<%s>", spec->name, spec->value_name);
- else if (spec->type == CLI_OPT_VALUE_OPTIONAL && spec->alias)
- error = fprintf(file, "-%c [<%s>]", spec->alias, spec->value_name);
- else if (spec->type == CLI_OPT_VALUE_OPTIONAL)
- error = fprintf(file, "--%s[=<%s>]", spec->name, spec->value_name);
- else if (spec->type == CLI_OPT_ARG)
- error = fprintf(file, "<%s>", spec->value_name);
- else if (spec->type == CLI_OPT_ARGS)
- error = fprintf(file, "<%s...>", spec->value_name);
- else if (spec->type == CLI_OPT_LITERAL)
- error = fprintf(file, "--");
- else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
- error = fprintf(file, "-%c", spec->alias);
- else
- error = fprintf(file, "--%s", spec->name);
-
- if (error < 0)
- goto done;
-
- choice = !!((spec+1)->usage & CLI_OPT_USAGE_CHOICE);
-
- if (optional && !choice && (error = fprintf(file, "]")) < 0)
- goto done;
- }
-
- error = fprintf(file, "\n");
-
-done:
- error = (error < 0) ? -1 : 0;
- return error;
-}
-
diff --git a/src/cli/opt.h b/src/cli/opt.h
index 24e605f39..9d178e97a 100644
--- a/src/cli/opt.h
+++ b/src/cli/opt.h
@@ -10,7 +10,7 @@
* This file was produced by using the `rename.pl` script included with
* adopt. The command-line specified was:
*
- * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status
+ * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
*/
#ifndef CLI_opt_h__
@@ -241,17 +241,4 @@ int cli_opt_status_fprint(
FILE *file,
const cli_opt *opt);
-/**
- * Prints usage information to the given file handle.
- *
- * @param file The file to print information to
- * @param command The name of the command to use when printing
- * @param specs The specifications allowed by the command
- * @return 0 on success, -1 on failure
- */
-int cli_opt_usage_fprint(
- FILE *file,
- const char *command,
- const cli_opt_spec specs[]);
-
#endif /* CLI_opt_h__ */
diff --git a/src/cli/opt_usage.c b/src/cli/opt_usage.c
new file mode 100644
index 000000000..4739d3302
--- /dev/null
+++ b/src/cli/opt_usage.c
@@ -0,0 +1,125 @@
+/*
+ * 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 "str.h"
+
+/*
+ * This is similar to adopt's function, but modified to understand
+ * that we have a command ("git") and a "subcommand" ("checkout").
+ * It also understands a terminal's line length and wrap appropriately,
+ * using a `git_str` for storage.
+ */
+int cli_opt_usage_fprint(
+ FILE *file,
+ const char *command,
+ const char *subcommand,
+ const cli_opt_spec specs[])
+{
+ git_str usage = GIT_BUF_INIT, opt = GIT_BUF_INIT;
+ const cli_opt_spec *spec;
+ size_t i, prefixlen, linelen;
+ bool choice = false;
+ int error;
+
+ /* TODO: query actual console width. */
+ int console_width = 80;
+
+ if ((error = git_str_printf(&usage, "usage: %s", command)) < 0)
+ goto done;
+
+ if (subcommand &&
+ (error = git_str_printf(&usage, " %s", subcommand)) < 0)
+ goto done;
+
+ linelen = git_str_len(&usage);
+ prefixlen = linelen + 1;
+
+ for (spec = specs; spec->type; ++spec) {
+ int optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
+ bool next_choice = !!((spec+1)->usage & CLI_OPT_USAGE_CHOICE);
+
+ if (spec->usage & CLI_OPT_USAGE_HIDDEN)
+ continue;
+
+ if (choice)
+ git_str_putc(&opt, '|');
+ else
+ git_str_clear(&opt);
+
+ if (optional && !choice)
+ git_str_putc(&opt, '[');
+ if (!optional && !choice && next_choice)
+ git_str_putc(&opt, '(');
+
+ if (spec->type == CLI_OPT_VALUE && spec->alias)
+ error = git_str_printf(&opt, "-%c <%s>", spec->alias, spec->value_name);
+ else if (spec->type == CLI_OPT_VALUE)
+ error = git_str_printf(&opt, "--%s=<%s>", spec->name, spec->value_name);
+ else if (spec->type == CLI_OPT_VALUE_OPTIONAL && spec->alias)
+ error = git_str_printf(&opt, "-%c [<%s>]", spec->alias, spec->value_name);
+ else if (spec->type == CLI_OPT_VALUE_OPTIONAL)
+ error = git_str_printf(&opt, "--%s[=<%s>]", spec->name, spec->value_name);
+ else if (spec->type == CLI_OPT_ARG)
+ error = git_str_printf(&opt, "<%s>", spec->value_name);
+ else if (spec->type == CLI_OPT_ARGS)
+ error = git_str_printf(&opt, "<%s...>", spec->value_name);
+ else if (spec->type == CLI_OPT_LITERAL)
+ error = git_str_printf(&opt, "--");
+ else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ error = git_str_printf(&opt, "-%c", spec->alias);
+ else
+ error = git_str_printf(&opt, "--%s", spec->name);
+
+ if (error < 0)
+ goto done;
+
+ if (!optional && choice && !next_choice)
+ git_str_putc(&opt, ')');
+ else if (optional && !next_choice)
+ git_str_putc(&opt, ']');
+
+ if ((choice = next_choice))
+ continue;
+
+ if (git_str_oom(&opt)) {
+ error = -1;
+ goto done;
+ }
+
+ if (linelen > prefixlen &&
+ console_width > 0 &&
+ linelen + git_str_len(&opt) + 1 > (size_t)console_width) {
+ git_str_putc(&usage, '\n');
+
+ for (i = 0; i < prefixlen; i++)
+ git_str_putc(&usage, ' ');
+
+ linelen = prefixlen;
+ } else {
+ git_str_putc(&usage, ' ');
+ linelen += git_str_len(&opt) + 1;
+ }
+
+ git_str_puts(&usage, git_str_cstr(&opt));
+
+ if (git_str_oom(&usage)) {
+ error = -1;
+ goto done;
+ }
+ }
+
+ error = fprintf(file, "%s\n", git_str_cstr(&usage));
+
+done:
+ error = (error < 0) ? -1 : 0;
+
+ git_str_dispose(&usage);
+ git_str_dispose(&opt);
+ return error;
+}
+
diff --git a/src/cli/opt_usage.h b/src/cli/opt_usage.h
new file mode 100644
index 000000000..e1aef0960
--- /dev/null
+++ b/src/cli/opt_usage.h
@@ -0,0 +1,26 @@
+/*
+ * 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_opt_usage_h__
+#define CLI_opt_usage_h__
+
+/**
+ * Prints usage information to the given file handle.
+ *
+ * @param file The file to print information to
+ * @param command The name of the command to use when printing
+ * @param subcommand The name of the subcommand (eg "checkout") to use when printing, or NULL to skip
+ * @param specs The specifications allowed by the command
+ * @return 0 on success, -1 on failure
+ */
+int cli_opt_usage_fprint(
+ FILE *file,
+ const char *command,
+ const char *subcommand,
+ const cli_opt_spec specs[]);
+
+#endif /* CLI_opt_usage_h__ */