]> git.proxmox.com Git - libgit2.git/blob - src/cli/opt_usage.c
New upstream version 1.5.0+ds
[libgit2.git] / src / cli / opt_usage.c
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 = 0;
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