]>
Commit | Line | Data |
---|---|---|
2f17c78c | 1 | #include "test-tool.h" |
beb47437 | 2 | #include "parse-options.h" |
69a63fe6 | 3 | #include "strbuf.h" |
c8ba1639 | 4 | #include "string-list.h" |
ee4512ed | 5 | #include "trace2.h" |
beb47437 JS |
6 | |
7 | static int boolean = 0; | |
c4aca9cc | 8 | static int integer = 0; |
2a514ed8 | 9 | static unsigned long magnitude = 0; |
dddbad72 | 10 | static timestamp_t timestamp; |
010a2dac | 11 | static int abbrev = 7; |
e0070e8b PB |
12 | static int verbose = -1; /* unspecified */ |
13 | static int dry_run = 0, quiet = 0; | |
beb47437 | 14 | static char *string = NULL; |
df217ed6 | 15 | static char *file = NULL; |
6bbfd1fa | 16 | static int ambiguous; |
beb47437 | 17 | |
accac419 JH |
18 | static struct { |
19 | int called; | |
20 | const char *arg; | |
21 | int unset; | |
22 | } length_cb; | |
23 | ||
2af202be | 24 | static int length_callback(const struct option *opt, const char *arg, int unset) |
010a2dac | 25 | { |
accac419 JH |
26 | length_cb.called = 1; |
27 | length_cb.arg = arg; | |
28 | length_cb.unset = unset; | |
29 | ||
010a2dac SB |
30 | if (unset) |
31 | return 1; /* do not support unset */ | |
32 | ||
8caa3acf | 33 | *(int *)opt->value = strlen(arg); |
010a2dac SB |
34 | return 0; |
35 | } | |
36 | ||
2af202be | 37 | static int number_callback(const struct option *opt, const char *arg, int unset) |
e0319ff5 | 38 | { |
517fe807 | 39 | BUG_ON_OPT_NEG(unset); |
e0319ff5 RS |
40 | *(int *)opt->value = strtol(arg, NULL, 10); |
41 | return 0; | |
42 | } | |
43 | ||
ab6b28b0 JH |
44 | static int collect_expect(const struct option *opt, const char *arg, int unset) |
45 | { | |
46 | struct string_list *expect; | |
47 | struct string_list_item *item; | |
48 | struct strbuf label = STRBUF_INIT; | |
49 | const char *colon; | |
50 | ||
51 | if (!arg || unset) | |
52 | die("malformed --expect option"); | |
53 | ||
54 | expect = (struct string_list *)opt->value; | |
55 | colon = strchr(arg, ':'); | |
56 | if (!colon) | |
57 | die("malformed --expect option, lacking a colon"); | |
58 | strbuf_add(&label, arg, colon - arg); | |
59 | item = string_list_insert(expect, strbuf_detach(&label, NULL)); | |
60 | if (item->util) | |
61 | die("malformed --expect option, duplicate %s", label.buf); | |
62 | item->util = (void *)arg; | |
63 | return 0; | |
64 | } | |
65 | ||
66 | __attribute__((format (printf,3,4))) | |
67 | static void show(struct string_list *expect, int *status, const char *fmt, ...) | |
68 | { | |
69 | struct string_list_item *item; | |
70 | struct strbuf buf = STRBUF_INIT; | |
71 | va_list args; | |
72 | ||
73 | va_start(args, fmt); | |
74 | strbuf_vaddf(&buf, fmt, args); | |
75 | va_end(args); | |
76 | ||
77 | if (!expect->nr) | |
78 | printf("%s\n", buf.buf); | |
79 | else { | |
80 | char *colon = strchr(buf.buf, ':'); | |
81 | if (!colon) | |
82 | die("malformed output format, output lacking colon: %s", fmt); | |
83 | *colon = '\0'; | |
84 | item = string_list_lookup(expect, buf.buf); | |
85 | *colon = ':'; | |
86 | if (!item) | |
87 | ; /* not among entries being checked */ | |
88 | else { | |
89 | if (strcmp((const char *)item->util, buf.buf)) { | |
90 | printf("-%s\n", (char *)item->util); | |
91 | printf("+%s\n", buf.buf); | |
92 | *status = 1; | |
93 | } | |
94 | } | |
95 | } | |
96 | strbuf_release(&buf); | |
97 | } | |
98 | ||
2f17c78c | 99 | int cmd__parse_options(int argc, const char **argv) |
beb47437 | 100 | { |
df217ed6 | 101 | const char *prefix = "prefix/"; |
beb47437 | 102 | const char *usage[] = { |
2f17c78c | 103 | "test-tool parse-options <options>", |
c97ee171 BC |
104 | "", |
105 | "A helper function for the parse-options API.", | |
beb47437 JS |
106 | NULL |
107 | }; | |
ab6b28b0 | 108 | struct string_list expect = STRING_LIST_INIT_NODUP; |
c0b80e05 ÆAB |
109 | struct string_list list = STRING_LIST_INIT_NODUP; |
110 | ||
beb47437 | 111 | struct option options[] = { |
b9e63ddd RS |
112 | OPT_BOOL(0, "yes", &boolean, "get a boolean"), |
113 | OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"), | |
114 | { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL, | |
115 | "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, | |
116 | OPT_COUNTUP('b', "boolean", &boolean, "increment by one"), | |
010a2dac SB |
117 | OPT_BIT('4', "or4", &boolean, |
118 | "bitwise-or boolean with ...0100", 4), | |
2f4b97f9 | 119 | OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4), |
010a2dac | 120 | OPT_GROUP(""), |
beb47437 JS |
121 | OPT_INTEGER('i', "integer", &integer, "get a integer"), |
122 | OPT_INTEGER('j', NULL, &integer, "get a integer, too"), | |
2a514ed8 | 123 | OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"), |
010a2dac | 124 | OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), |
62e7a6f7 PB |
125 | OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1), |
126 | OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2), | |
010a2dac SB |
127 | OPT_CALLBACK('L', "length", &integer, "str", |
128 | "get length of <str>", length_callback), | |
41dbcd45 | 129 | OPT_FILENAME('F', "file", &file, "set file to <file>"), |
010a2dac | 130 | OPT_GROUP("String options"), |
beb47437 JS |
131 | OPT_STRING('s', "string", &string, "string", "get a string"), |
132 | OPT_STRING(0, "string2", &string, "str", "get another string"), | |
243e0614 | 133 | OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), |
3a9f0f41 | 134 | OPT_STRING('o', NULL, &string, "str", "get another string"), |
6acec038 | 135 | OPT_NOOP_NOARG(0, "obsolete"), |
448abbba JH |
136 | OPT_SET_INT_F(0, "longhelp", &integer, "help text of this entry\n" |
137 | "spans multiple lines", 0, PARSE_OPT_NONEG), | |
c8ba1639 | 138 | OPT_STRING_LIST(0, "list", &list, "str", "add str to list"), |
010a2dac | 139 | OPT_GROUP("Magic arguments"), |
e0319ff5 RS |
140 | OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", |
141 | number_callback), | |
b9e63ddd | 142 | { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b", |
51a9949e | 143 | PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH }, |
b9e63ddd | 144 | { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL, |
6bbfd1fa | 145 | "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, |
b9e63ddd | 146 | { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL, |
6bbfd1fa | 147 | "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, |
010a2dac SB |
148 | OPT_GROUP("Standard options"), |
149 | OPT__ABBREV(&abbrev), | |
fd03881a | 150 | OPT__VERBOSE(&verbose, "be verbose"), |
e21adb8c | 151 | OPT__DRY_RUN(&dry_run, "dry run"), |
d52ee6e6 | 152 | OPT__QUIET(&quiet, "be quiet"), |
ab6b28b0 JH |
153 | OPT_CALLBACK(0, "expect", &expect, "string", |
154 | "expected output in the variable dump", | |
155 | collect_expect), | |
5c387428 NTND |
156 | OPT_GROUP("Alias"), |
157 | OPT_STRING('A', "alias-source", &string, "string", "get a string"), | |
158 | OPT_ALIAS('Z', "alias-target", "alias-source"), | |
beb47437 JS |
159 | OPT_END(), |
160 | }; | |
161 | int i; | |
ab6b28b0 | 162 | int ret = 0; |
beb47437 | 163 | |
ee4512ed JH |
164 | trace2_cmd_name("_parse_"); |
165 | ||
84d32bf7 | 166 | argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); |
beb47437 | 167 | |
accac419 JH |
168 | if (length_cb.called) { |
169 | const char *arg = length_cb.arg; | |
170 | int unset = length_cb.unset; | |
ab6b28b0 JH |
171 | show(&expect, &ret, "Callback: \"%s\", %d", |
172 | (arg ? arg : "not set"), unset); | |
accac419 | 173 | } |
ab6b28b0 JH |
174 | show(&expect, &ret, "boolean: %d", boolean); |
175 | show(&expect, &ret, "integer: %d", integer); | |
176 | show(&expect, &ret, "magnitude: %lu", magnitude); | |
cb71f8bd | 177 | show(&expect, &ret, "timestamp: %"PRItime, timestamp); |
ab6b28b0 JH |
178 | show(&expect, &ret, "string: %s", string ? string : "(not set)"); |
179 | show(&expect, &ret, "abbrev: %d", abbrev); | |
180 | show(&expect, &ret, "verbose: %d", verbose); | |
181 | show(&expect, &ret, "quiet: %d", quiet); | |
182 | show(&expect, &ret, "dry run: %s", dry_run ? "yes" : "no"); | |
183 | show(&expect, &ret, "file: %s", file ? file : "(not set)"); | |
beb47437 | 184 | |
c8ba1639 | 185 | for (i = 0; i < list.nr; i++) |
ab6b28b0 | 186 | show(&expect, &ret, "list: %s", list.items[i].string); |
c8ba1639 | 187 | |
beb47437 | 188 | for (i = 0; i < argc; i++) |
ab6b28b0 | 189 | show(&expect, &ret, "arg %02d: %s", i, argv[i]); |
beb47437 | 190 | |
c0b80e05 ÆAB |
191 | expect.strdup_strings = 1; |
192 | string_list_clear(&expect, 0); | |
193 | string_list_clear(&list, 0); | |
194 | ||
ab6b28b0 | 195 | return ret; |
beb47437 | 196 | } |
c1b117d3 SG |
197 | |
198 | static void print_args(int argc, const char **argv) | |
199 | { | |
6983f4e3 SG |
200 | int i; |
201 | for (i = 0; i < argc; i++) | |
c1b117d3 SG |
202 | printf("arg %02d: %s\n", i, argv[i]); |
203 | } | |
204 | ||
205 | static int parse_options_flags__cmd(int argc, const char **argv, | |
206 | enum parse_opt_flags test_flags) | |
207 | { | |
208 | const char *usage[] = { | |
209 | "<...> cmd [options]", | |
210 | NULL | |
211 | }; | |
212 | int opt = 0; | |
213 | const struct option options[] = { | |
214 | OPT_INTEGER('o', "opt", &opt, "an integer option"), | |
215 | OPT_END() | |
216 | }; | |
217 | ||
218 | argc = parse_options(argc, argv, NULL, options, usage, test_flags); | |
219 | ||
220 | printf("opt: %d\n", opt); | |
221 | print_args(argc, argv); | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static enum parse_opt_flags test_flags = 0; | |
227 | static const struct option test_flag_options[] = { | |
228 | OPT_GROUP("flag-options:"), | |
229 | OPT_BIT(0, "keep-dashdash", &test_flags, | |
230 | "pass PARSE_OPT_KEEP_DASHDASH to parse_options()", | |
231 | PARSE_OPT_KEEP_DASHDASH), | |
232 | OPT_BIT(0, "stop-at-non-option", &test_flags, | |
233 | "pass PARSE_OPT_STOP_AT_NON_OPTION to parse_options()", | |
234 | PARSE_OPT_STOP_AT_NON_OPTION), | |
235 | OPT_BIT(0, "keep-argv0", &test_flags, | |
236 | "pass PARSE_OPT_KEEP_ARGV0 to parse_options()", | |
237 | PARSE_OPT_KEEP_ARGV0), | |
99d86d60 SG |
238 | OPT_BIT(0, "keep-unknown-opt", &test_flags, |
239 | "pass PARSE_OPT_KEEP_UNKNOWN_OPT to parse_options()", | |
240 | PARSE_OPT_KEEP_UNKNOWN_OPT), | |
c1b117d3 SG |
241 | OPT_BIT(0, "no-internal-help", &test_flags, |
242 | "pass PARSE_OPT_NO_INTERNAL_HELP to parse_options()", | |
243 | PARSE_OPT_NO_INTERNAL_HELP), | |
fa83cc83 SG |
244 | OPT_BIT(0, "subcommand-optional", &test_flags, |
245 | "pass PARSE_OPT_SUBCOMMAND_OPTIONAL to parse_options()", | |
246 | PARSE_OPT_SUBCOMMAND_OPTIONAL), | |
c1b117d3 SG |
247 | OPT_END() |
248 | }; | |
249 | ||
250 | int cmd__parse_options_flags(int argc, const char **argv) | |
251 | { | |
252 | const char *usage[] = { | |
253 | "test-tool parse-options-flags [flag-options] cmd [options]", | |
254 | NULL | |
255 | }; | |
256 | ||
257 | argc = parse_options(argc, argv, NULL, test_flag_options, usage, | |
258 | PARSE_OPT_STOP_AT_NON_OPTION); | |
259 | ||
45bec2ea | 260 | if (!argc || strcmp(argv[0], "cmd")) { |
c1b117d3 SG |
261 | error("'cmd' is mandatory"); |
262 | usage_with_options(usage, test_flag_options); | |
263 | } | |
264 | ||
265 | return parse_options_flags__cmd(argc, argv, test_flags); | |
266 | } | |
fa83cc83 | 267 | |
126e3b3d | 268 | static int subcmd_one(int argc, const char **argv, const char *prefix UNUSED) |
fa83cc83 SG |
269 | { |
270 | printf("fn: subcmd_one\n"); | |
271 | print_args(argc, argv); | |
272 | return 0; | |
273 | } | |
274 | ||
126e3b3d | 275 | static int subcmd_two(int argc, const char **argv, const char *prefix UNUSED) |
fa83cc83 SG |
276 | { |
277 | printf("fn: subcmd_two\n"); | |
278 | print_args(argc, argv); | |
279 | return 0; | |
280 | } | |
281 | ||
282 | static int parse_subcommand__cmd(int argc, const char **argv, | |
283 | enum parse_opt_flags test_flags) | |
284 | { | |
285 | const char *usage[] = { | |
286 | "<...> cmd subcmd-one", | |
287 | "<...> cmd subcmd-two", | |
288 | NULL | |
289 | }; | |
290 | parse_opt_subcommand_fn *fn = NULL; | |
291 | int opt = 0; | |
292 | struct option options[] = { | |
293 | OPT_SUBCOMMAND("subcmd-one", &fn, subcmd_one), | |
294 | OPT_SUBCOMMAND("subcmd-two", &fn, subcmd_two), | |
295 | OPT_INTEGER('o', "opt", &opt, "an integer option"), | |
296 | OPT_END() | |
297 | }; | |
298 | ||
299 | if (test_flags & PARSE_OPT_SUBCOMMAND_OPTIONAL) | |
300 | fn = subcmd_one; | |
301 | argc = parse_options(argc, argv, NULL, options, usage, test_flags); | |
302 | ||
303 | printf("opt: %d\n", opt); | |
304 | ||
305 | return fn(argc, argv, NULL); | |
306 | } | |
307 | ||
308 | int cmd__parse_subcommand(int argc, const char **argv) | |
309 | { | |
310 | const char *usage[] = { | |
311 | "test-tool parse-subcommand [flag-options] cmd <subcommand>", | |
312 | NULL | |
313 | }; | |
314 | ||
315 | argc = parse_options(argc, argv, NULL, test_flag_options, usage, | |
316 | PARSE_OPT_STOP_AT_NON_OPTION); | |
317 | ||
45bec2ea | 318 | if (!argc || strcmp(argv[0], "cmd")) { |
fa83cc83 SG |
319 | error("'cmd' is mandatory"); |
320 | usage_with_options(usage, test_flag_options); | |
321 | } | |
322 | ||
323 | return parse_subcommand__cmd(argc, argv, test_flags); | |
324 | } |