Commit 8526cbd5 by Edward Thomson

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.
parent 3a3ab065
......@@ -14,5 +14,6 @@
#include "error.h"
#include "opt.h"
#include "opt_usage.h"
#endif /* CLI_cli_h__ */
......@@ -34,7 +34,7 @@ int main(int argc, char **argv)
while (cli_opt_parser_next(&opt, &optparser)) {
if (!opt.spec) {
cli_opt_status_fprint(stderr, PROGRAM_NAME, &opt);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, common_opts);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, common_opts);
ret = CLI_EXIT_USAGE;
goto done;
}
......
......@@ -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>
......@@ -667,84 +667,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, next_choice = 0, optional = 0;
int error;
if ((error = fprintf(file, "usage: %s", command)) < 0)
goto done;
for (spec = specs; spec->type; ++spec) {
if (!choice)
optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE);
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)
error = fprintf(file, "[");
if (!optional && !choice && next_choice)
error = fprintf(file, "(");
if (error < 0)
goto done;
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) &&
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
error = fprintf(file, "-%c <%s>", spec->alias, spec->value_name);
else if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
error = fprintf(file, "-%c [<%s>]", spec->alias, spec->value_name);
else if (spec->type == CLI_OPT_TYPE_VALUE &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
error = fprintf(file, "--%s[=<%s>]", spec->name, spec->value_name);
else if (spec->type == CLI_OPT_TYPE_VALUE)
error = fprintf(file, "--%s=<%s>", spec->name, spec->value_name);
else if (spec->type == CLI_OPT_TYPE_ARG)
error = fprintf(file, "<%s>", spec->value_name);
else if (spec->type == CLI_OPT_TYPE_ARGS)
error = fprintf(file, "<%s>...", spec->value_name);
else if (spec->type == CLI_OPT_TYPE_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;
if (!optional && choice && !next_choice)
error = fprintf(file, ")");
else if (optional && !next_choice)
error = fprintf(file, "]");
if (error < 0)
goto done;
choice = next_choice;
}
error = fprintf(file, "\n");
done:
error = (error < 0) ? -1 : 0;
return error;
}
......@@ -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__
......@@ -346,17 +346,4 @@ int cli_opt_status_fprint(
const char *command,
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__ */
/*
* 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"
static int print_spec_name(git_str *out, const cli_opt_spec *spec)
{
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) &&
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c <%s>", spec->alias, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c [<%s>]", spec->alias, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE &&
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
return git_str_printf(out, "--%s[=<%s>]", spec->name, spec->value_name);
if (spec->type == CLI_OPT_TYPE_VALUE)
return git_str_printf(out, "--%s=<%s>", spec->name, spec->value_name);
if (spec->type == CLI_OPT_TYPE_ARG)
return git_str_printf(out, "<%s>", spec->value_name);
if (spec->type == CLI_OPT_TYPE_ARGS)
return git_str_printf(out, "<%s>...", spec->value_name);
if (spec->type == CLI_OPT_TYPE_LITERAL)
return git_str_printf(out, "--");
if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
return git_str_printf(out, "-%c", spec->alias);
if (spec->name)
return git_str_printf(out, "--%s", spec->name);
GIT_ASSERT(0);
}
/*
* 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, next_choice = false, optional = 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) {
if (!choice)
optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
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 ((error = print_spec_name(&opt, spec)) < 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;
}
int cli_opt_usage_error(
const char *subcommand,
const cli_opt_spec specs[],
const cli_opt *invalid_opt)
{
cli_opt_status_fprint(stderr, PROGRAM_NAME, invalid_opt);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs);
return CLI_EXIT_USAGE;
}
int cli_opt_help_fprint(
FILE *file,
const cli_opt_spec specs[])
{
git_str help = GIT_BUF_INIT;
const cli_opt_spec *spec;
int error;
/* Display required arguments first */
for (spec = specs; spec->type; ++spec) {
if (! (spec->usage & CLI_OPT_USAGE_REQUIRED) ||
(spec->usage & CLI_OPT_USAGE_HIDDEN))
continue;
git_str_printf(&help, " ");
if ((error = print_spec_name(&help, spec)) < 0)
goto done;
git_str_printf(&help, ": %s\n", spec->help);
}
/* Display the remaining arguments */
for (spec = specs; spec->type; ++spec) {
if ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
(spec->usage & CLI_OPT_USAGE_HIDDEN))
continue;
git_str_printf(&help, " ");
if ((error = print_spec_name(&help, spec)) < 0)
goto done;
git_str_printf(&help, ": %s\n", spec->help);
}
if (git_str_oom(&help) ||
p_write(fileno(file), help.ptr, help.size) < 0)
error = -1;
done:
error = (error < 0) ? -1 : 0;
git_str_dispose(&help);
return error;
}
/*
* 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[]);
int cli_opt_usage_error(
const char *subcommand,
const cli_opt_spec specs[],
const cli_opt *invalid_opt);
int cli_opt_help_fprint(
FILE *file,
const cli_opt_spec specs[]);
#endif /* CLI_opt_usage_h__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment