Skip to content

Commit 8526cbd

Browse files
committed
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.
1 parent 3a3ab06 commit 8526cbd

File tree

6 files changed

+233
-97
lines changed

6 files changed

+233
-97
lines changed

src/cli/cli.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414

1515
#include "error.h"
1616
#include "opt.h"
17+
#include "opt_usage.h"
1718

1819
#endif /* CLI_cli_h__ */

src/cli/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ int main(int argc, char **argv)
3434
while (cli_opt_parser_next(&opt, &optparser)) {
3535
if (!opt.spec) {
3636
cli_opt_status_fprint(stderr, PROGRAM_NAME, &opt);
37-
cli_opt_usage_fprint(stderr, PROGRAM_NAME, common_opts);
37+
cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, common_opts);
3838
ret = CLI_EXIT_USAGE;
3939
goto done;
4040
}

src/cli/opt.c

Lines changed: 1 addition & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* This file was produced by using the `rename.pl` script included with
1111
* adopt. The command-line specified was:
1212
*
13-
* ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status
13+
* ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
1414
*/
1515

1616
#include <stdlib.h>
@@ -667,84 +667,3 @@ int cli_opt_status_fprint(
667667
return error;
668668
}
669669

670-
int cli_opt_usage_fprint(
671-
FILE *file,
672-
const char *command,
673-
const cli_opt_spec specs[])
674-
{
675-
const cli_opt_spec *spec;
676-
int choice = 0, next_choice = 0, optional = 0;
677-
int error;
678-
679-
if ((error = fprintf(file, "usage: %s", command)) < 0)
680-
goto done;
681-
682-
for (spec = specs; spec->type; ++spec) {
683-
if (!choice)
684-
optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
685-
686-
next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE);
687-
688-
if (spec->usage & CLI_OPT_USAGE_HIDDEN)
689-
continue;
690-
691-
if (choice)
692-
error = fprintf(file, "|");
693-
else
694-
error = fprintf(file, " ");
695-
696-
if (error < 0)
697-
goto done;
698-
699-
if (optional && !choice && (error = fprintf(file, "[")) < 0)
700-
error = fprintf(file, "[");
701-
if (!optional && !choice && next_choice)
702-
error = fprintf(file, "(");
703-
704-
if (error < 0)
705-
goto done;
706-
707-
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
708-
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) &&
709-
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
710-
error = fprintf(file, "-%c <%s>", spec->alias, spec->value_name);
711-
else if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
712-
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
713-
error = fprintf(file, "-%c [<%s>]", spec->alias, spec->value_name);
714-
else if (spec->type == CLI_OPT_TYPE_VALUE &&
715-
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
716-
error = fprintf(file, "--%s[=<%s>]", spec->name, spec->value_name);
717-
else if (spec->type == CLI_OPT_TYPE_VALUE)
718-
error = fprintf(file, "--%s=<%s>", spec->name, spec->value_name);
719-
else if (spec->type == CLI_OPT_TYPE_ARG)
720-
error = fprintf(file, "<%s>", spec->value_name);
721-
else if (spec->type == CLI_OPT_TYPE_ARGS)
722-
error = fprintf(file, "<%s>...", spec->value_name);
723-
else if (spec->type == CLI_OPT_TYPE_LITERAL)
724-
error = fprintf(file, "--");
725-
else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
726-
error = fprintf(file, "-%c", spec->alias);
727-
else
728-
error = fprintf(file, "--%s", spec->name);
729-
730-
if (error < 0)
731-
goto done;
732-
733-
if (!optional && choice && !next_choice)
734-
error = fprintf(file, ")");
735-
else if (optional && !next_choice)
736-
error = fprintf(file, "]");
737-
738-
if (error < 0)
739-
goto done;
740-
741-
choice = next_choice;
742-
}
743-
744-
error = fprintf(file, "\n");
745-
746-
done:
747-
error = (error < 0) ? -1 : 0;
748-
return error;
749-
}
750-

src/cli/opt.h

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* This file was produced by using the `rename.pl` script included with
1111
* adopt. The command-line specified was:
1212
*
13-
* ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status
13+
* ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
1414
*/
1515

1616
#ifndef CLI_opt_h__
@@ -346,17 +346,4 @@ int cli_opt_status_fprint(
346346
const char *command,
347347
const cli_opt *opt);
348348

349-
/**
350-
* Prints usage information to the given file handle.
351-
*
352-
* @param file The file to print information to
353-
* @param command The name of the command to use when printing
354-
* @param specs The specifications allowed by the command
355-
* @return 0 on success, -1 on failure
356-
*/
357-
int cli_opt_usage_fprint(
358-
FILE *file,
359-
const char *command,
360-
const cli_opt_spec specs[]);
361-
362349
#endif /* CLI_opt_h__ */

src/cli/opt_usage.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
8+
#include "cli.h"
9+
#include "str.h"
10+
11+
static int print_spec_name(git_str *out, const cli_opt_spec *spec)
12+
{
13+
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
14+
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) &&
15+
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
16+
return git_str_printf(out, "-%c <%s>", spec->alias, spec->value_name);
17+
if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
18+
!(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
19+
return git_str_printf(out, "-%c [<%s>]", spec->alias, spec->value_name);
20+
if (spec->type == CLI_OPT_TYPE_VALUE &&
21+
!(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
22+
return git_str_printf(out, "--%s[=<%s>]", spec->name, spec->value_name);
23+
if (spec->type == CLI_OPT_TYPE_VALUE)
24+
return git_str_printf(out, "--%s=<%s>", spec->name, spec->value_name);
25+
if (spec->type == CLI_OPT_TYPE_ARG)
26+
return git_str_printf(out, "<%s>", spec->value_name);
27+
if (spec->type == CLI_OPT_TYPE_ARGS)
28+
return git_str_printf(out, "<%s>...", spec->value_name);
29+
if (spec->type == CLI_OPT_TYPE_LITERAL)
30+
return git_str_printf(out, "--");
31+
if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
32+
return git_str_printf(out, "-%c", spec->alias);
33+
if (spec->name)
34+
return git_str_printf(out, "--%s", spec->name);
35+
36+
GIT_ASSERT(0);
37+
}
38+
39+
/*
40+
* This is similar to adopt's function, but modified to understand
41+
* that we have a command ("git") and a "subcommand" ("checkout").
42+
* It also understands a terminal's line length and wrap appropriately,
43+
* using a `git_str` for storage.
44+
*/
45+
int cli_opt_usage_fprint(
46+
FILE *file,
47+
const char *command,
48+
const char *subcommand,
49+
const cli_opt_spec specs[])
50+
{
51+
git_str usage = GIT_BUF_INIT, opt = GIT_BUF_INIT;
52+
const cli_opt_spec *spec;
53+
size_t i, prefixlen, linelen;
54+
bool choice = false, next_choice = false, optional = false;
55+
int error;
56+
57+
/* TODO: query actual console width. */
58+
int console_width = 80;
59+
60+
if ((error = git_str_printf(&usage, "usage: %s", command)) < 0)
61+
goto done;
62+
63+
if (subcommand &&
64+
(error = git_str_printf(&usage, " %s", subcommand)) < 0)
65+
goto done;
66+
67+
linelen = git_str_len(&usage);
68+
prefixlen = linelen + 1;
69+
70+
for (spec = specs; spec->type; ++spec) {
71+
if (!choice)
72+
optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
73+
74+
next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE);
75+
76+
if (spec->usage & CLI_OPT_USAGE_HIDDEN)
77+
continue;
78+
79+
if (choice)
80+
git_str_putc(&opt, '|');
81+
else
82+
git_str_clear(&opt);
83+
84+
if (optional && !choice)
85+
git_str_putc(&opt, '[');
86+
if (!optional && !choice && next_choice)
87+
git_str_putc(&opt, '(');
88+
89+
if ((error = print_spec_name(&opt, spec)) < 0)
90+
goto done;
91+
92+
if (!optional && choice && !next_choice)
93+
git_str_putc(&opt, ')');
94+
else if (optional && !next_choice)
95+
git_str_putc(&opt, ']');
96+
97+
if ((choice = next_choice))
98+
continue;
99+
100+
if (git_str_oom(&opt)) {
101+
error = -1;
102+
goto done;
103+
}
104+
105+
if (linelen > prefixlen &&
106+
console_width > 0 &&
107+
linelen + git_str_len(&opt) + 1 > (size_t)console_width) {
108+
git_str_putc(&usage, '\n');
109+
110+
for (i = 0; i < prefixlen; i++)
111+
git_str_putc(&usage, ' ');
112+
113+
linelen = prefixlen;
114+
} else {
115+
git_str_putc(&usage, ' ');
116+
linelen += git_str_len(&opt) + 1;
117+
}
118+
119+
git_str_puts(&usage, git_str_cstr(&opt));
120+
121+
if (git_str_oom(&usage)) {
122+
error = -1;
123+
goto done;
124+
}
125+
}
126+
127+
error = fprintf(file, "%s\n", git_str_cstr(&usage));
128+
129+
done:
130+
error = (error < 0) ? -1 : 0;
131+
132+
git_str_dispose(&usage);
133+
git_str_dispose(&opt);
134+
return error;
135+
}
136+
137+
int cli_opt_usage_error(
138+
const char *subcommand,
139+
const cli_opt_spec specs[],
140+
const cli_opt *invalid_opt)
141+
{
142+
cli_opt_status_fprint(stderr, PROGRAM_NAME, invalid_opt);
143+
cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs);
144+
return CLI_EXIT_USAGE;
145+
}
146+
147+
int cli_opt_help_fprint(
148+
FILE *file,
149+
const cli_opt_spec specs[])
150+
{
151+
git_str help = GIT_BUF_INIT;
152+
const cli_opt_spec *spec;
153+
int error;
154+
155+
/* Display required arguments first */
156+
for (spec = specs; spec->type; ++spec) {
157+
if (! (spec->usage & CLI_OPT_USAGE_REQUIRED) ||
158+
(spec->usage & CLI_OPT_USAGE_HIDDEN))
159+
continue;
160+
161+
git_str_printf(&help, " ");
162+
163+
if ((error = print_spec_name(&help, spec)) < 0)
164+
goto done;
165+
166+
git_str_printf(&help, ": %s\n", spec->help);
167+
}
168+
169+
/* Display the remaining arguments */
170+
for (spec = specs; spec->type; ++spec) {
171+
if ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
172+
(spec->usage & CLI_OPT_USAGE_HIDDEN))
173+
continue;
174+
175+
git_str_printf(&help, " ");
176+
177+
if ((error = print_spec_name(&help, spec)) < 0)
178+
goto done;
179+
180+
git_str_printf(&help, ": %s\n", spec->help);
181+
182+
}
183+
184+
if (git_str_oom(&help) ||
185+
p_write(fileno(file), help.ptr, help.size) < 0)
186+
error = -1;
187+
188+
done:
189+
error = (error < 0) ? -1 : 0;
190+
191+
git_str_dispose(&help);
192+
return error;
193+
}
194+

src/cli/opt_usage.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
8+
#ifndef CLI_opt_usage_h__
9+
#define CLI_opt_usage_h__
10+
11+
/**
12+
* Prints usage information to the given file handle.
13+
*
14+
* @param file The file to print information to
15+
* @param command The name of the command to use when printing
16+
* @param subcommand The name of the subcommand (eg "checkout") to use when printing, or NULL to skip
17+
* @param specs The specifications allowed by the command
18+
* @return 0 on success, -1 on failure
19+
*/
20+
int cli_opt_usage_fprint(
21+
FILE *file,
22+
const char *command,
23+
const char *subcommand,
24+
const cli_opt_spec specs[]);
25+
26+
int cli_opt_usage_error(
27+
const char *subcommand,
28+
const cli_opt_spec specs[],
29+
const cli_opt *invalid_opt);
30+
31+
int cli_opt_help_fprint(
32+
FILE *file,
33+
const cli_opt_spec specs[]);
34+
35+
#endif /* CLI_opt_usage_h__ */

0 commit comments

Comments
 (0)