Skip to content

Commit d7a38c5

Browse files
MadCodergitster
authored andcommitted
parse-options: be able to generate usages automatically
Signed-off-by: Pierre Habouzit <[email protected]> Signed-off-by: Shawn O. Pearce <[email protected]>
1 parent 4a59fd1 commit d7a38c5

File tree

2 files changed

+79
-9
lines changed

2 files changed

+79
-9
lines changed

parse-options.c

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
123123
}
124124

125125
int parse_options(int argc, const char **argv, const struct option *options,
126-
const char *usagestr, int flags)
126+
const char * const usagestr[], int flags)
127127
{
128128
struct optparse_t args = { argv + 1, argc - 1, NULL };
129129
int j = 0;
@@ -140,9 +140,9 @@ int parse_options(int argc, const char **argv, const struct option *options,
140140
args.opt = arg + 1;
141141
do {
142142
if (*args.opt == 'h')
143-
usage(usagestr);
143+
usage_with_options(usagestr, options);
144144
if (parse_short_opt(&args, options) < 0)
145-
usage(usagestr);
145+
usage_with_options(usagestr, options);
146146
} while (args.opt);
147147
continue;
148148
}
@@ -156,12 +156,75 @@ int parse_options(int argc, const char **argv, const struct option *options,
156156
}
157157

158158
if (!strcmp(arg + 2, "help"))
159-
usage(usagestr);
159+
usage_with_options(usagestr, options);
160160
if (parse_long_opt(&args, arg + 2, options))
161-
usage(usagestr);
161+
usage_with_options(usagestr, options);
162162
}
163163

164164
memmove(argv + j, args.argv, args.argc * sizeof(*argv));
165165
argv[j + args.argc] = NULL;
166166
return j + args.argc;
167167
}
168+
169+
#define USAGE_OPTS_WIDTH 24
170+
#define USAGE_GAP 2
171+
172+
void usage_with_options(const char * const *usagestr,
173+
const struct option *opts)
174+
{
175+
struct strbuf sb;
176+
177+
strbuf_init(&sb, 4096);
178+
strbuf_addstr(&sb, *usagestr);
179+
strbuf_addch(&sb, '\n');
180+
while (*++usagestr)
181+
strbuf_addf(&sb, " %s\n", *usagestr);
182+
183+
if (opts->type != OPTION_GROUP)
184+
strbuf_addch(&sb, '\n');
185+
186+
for (; opts->type != OPTION_END; opts++) {
187+
size_t pos;
188+
int pad;
189+
190+
if (opts->type == OPTION_GROUP) {
191+
strbuf_addch(&sb, '\n');
192+
if (*opts->help)
193+
strbuf_addf(&sb, "%s\n", opts->help);
194+
continue;
195+
}
196+
197+
pos = sb.len;
198+
strbuf_addstr(&sb, " ");
199+
if (opts->short_name)
200+
strbuf_addf(&sb, "-%c", opts->short_name);
201+
if (opts->long_name && opts->short_name)
202+
strbuf_addstr(&sb, ", ");
203+
if (opts->long_name)
204+
strbuf_addf(&sb, "--%s", opts->long_name);
205+
206+
switch (opts->type) {
207+
case OPTION_INTEGER:
208+
strbuf_addstr(&sb, " <n>");
209+
break;
210+
case OPTION_STRING:
211+
if (opts->argh)
212+
strbuf_addf(&sb, " <%s>", opts->argh);
213+
else
214+
strbuf_addstr(&sb, " ...");
215+
break;
216+
default:
217+
break;
218+
}
219+
220+
pad = sb.len - pos;
221+
if (pad <= USAGE_OPTS_WIDTH)
222+
pad = USAGE_OPTS_WIDTH - pad;
223+
else {
224+
strbuf_addch(&sb, '\n');
225+
pad = USAGE_OPTS_WIDTH;
226+
}
227+
strbuf_addf(&sb, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
228+
}
229+
usage(sb.buf);
230+
}

parse-options.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
enum parse_opt_type {
55
OPTION_END,
6+
OPTION_GROUP,
67
OPTION_BOOLEAN,
78
OPTION_STRING,
89
OPTION_INTEGER,
@@ -17,19 +18,25 @@ struct option {
1718
int short_name;
1819
const char *long_name;
1920
void *value;
21+
const char *argh;
22+
const char *help;
2023
};
2124

2225
#define OPT_END() { OPTION_END }
23-
#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v) }
24-
#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v) }
25-
#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v) }
26+
#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
27+
#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
28+
#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) }
29+
#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) }
2630

2731
/* parse_options() will filter out the processed options and leave the
2832
* non-option argments in argv[].
2933
* Returns the number of arguments left in argv[].
3034
*/
3135
extern int parse_options(int argc, const char **argv,
3236
const struct option *options,
33-
const char *usagestr, int flags);
37+
const char * const usagestr[], int flags);
38+
39+
extern NORETURN void usage_with_options(const char * const *usagestr,
40+
const struct option *options);
3441

3542
#endif

0 commit comments

Comments
 (0)