]>
Commit | Line | Data |
---|---|---|
2f17c78c | 1 | #include "test-tool.h" |
beb47437 JS |
2 | #include "cache.h" |
3 | #include "parse-options.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"), |
c8ba1639 | 136 | OPT_STRING_LIST(0, "list", &list, "str", "add str to list"), |
010a2dac | 137 | OPT_GROUP("Magic arguments"), |
e0319ff5 RS |
138 | OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", |
139 | number_callback), | |
b9e63ddd | 140 | { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b", |
51a9949e | 141 | PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH }, |
b9e63ddd | 142 | { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL, |
6bbfd1fa | 143 | "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, |
b9e63ddd | 144 | { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL, |
6bbfd1fa | 145 | "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, |
010a2dac SB |
146 | OPT_GROUP("Standard options"), |
147 | OPT__ABBREV(&abbrev), | |
fd03881a | 148 | OPT__VERBOSE(&verbose, "be verbose"), |
e21adb8c | 149 | OPT__DRY_RUN(&dry_run, "dry run"), |
d52ee6e6 | 150 | OPT__QUIET(&quiet, "be quiet"), |
ab6b28b0 JH |
151 | OPT_CALLBACK(0, "expect", &expect, "string", |
152 | "expected output in the variable dump", | |
153 | collect_expect), | |
5c387428 NTND |
154 | OPT_GROUP("Alias"), |
155 | OPT_STRING('A', "alias-source", &string, "string", "get a string"), | |
156 | OPT_ALIAS('Z', "alias-target", "alias-source"), | |
beb47437 JS |
157 | OPT_END(), |
158 | }; | |
159 | int i; | |
ab6b28b0 | 160 | int ret = 0; |
beb47437 | 161 | |
ee4512ed JH |
162 | trace2_cmd_name("_parse_"); |
163 | ||
84d32bf7 | 164 | argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); |
beb47437 | 165 | |
accac419 JH |
166 | if (length_cb.called) { |
167 | const char *arg = length_cb.arg; | |
168 | int unset = length_cb.unset; | |
ab6b28b0 JH |
169 | show(&expect, &ret, "Callback: \"%s\", %d", |
170 | (arg ? arg : "not set"), unset); | |
accac419 | 171 | } |
ab6b28b0 JH |
172 | show(&expect, &ret, "boolean: %d", boolean); |
173 | show(&expect, &ret, "integer: %d", integer); | |
174 | show(&expect, &ret, "magnitude: %lu", magnitude); | |
cb71f8bd | 175 | show(&expect, &ret, "timestamp: %"PRItime, timestamp); |
ab6b28b0 JH |
176 | show(&expect, &ret, "string: %s", string ? string : "(not set)"); |
177 | show(&expect, &ret, "abbrev: %d", abbrev); | |
178 | show(&expect, &ret, "verbose: %d", verbose); | |
179 | show(&expect, &ret, "quiet: %d", quiet); | |
180 | show(&expect, &ret, "dry run: %s", dry_run ? "yes" : "no"); | |
181 | show(&expect, &ret, "file: %s", file ? file : "(not set)"); | |
beb47437 | 182 | |
c8ba1639 | 183 | for (i = 0; i < list.nr; i++) |
ab6b28b0 | 184 | show(&expect, &ret, "list: %s", list.items[i].string); |
c8ba1639 | 185 | |
beb47437 | 186 | for (i = 0; i < argc; i++) |
ab6b28b0 | 187 | show(&expect, &ret, "arg %02d: %s", i, argv[i]); |
beb47437 | 188 | |
c0b80e05 ÆAB |
189 | expect.strdup_strings = 1; |
190 | string_list_clear(&expect, 0); | |
191 | string_list_clear(&list, 0); | |
192 | ||
ab6b28b0 | 193 | return ret; |
beb47437 | 194 | } |
c1b117d3 SG |
195 | |
196 | static void print_args(int argc, const char **argv) | |
197 | { | |
6983f4e3 SG |
198 | int i; |
199 | for (i = 0; i < argc; i++) | |
c1b117d3 SG |
200 | printf("arg %02d: %s\n", i, argv[i]); |
201 | } | |
202 | ||
203 | static int parse_options_flags__cmd(int argc, const char **argv, | |
204 | enum parse_opt_flags test_flags) | |
205 | { | |
206 | const char *usage[] = { | |
207 | "<...> cmd [options]", | |
208 | NULL | |
209 | }; | |
210 | int opt = 0; | |
211 | const struct option options[] = { | |
212 | OPT_INTEGER('o', "opt", &opt, "an integer option"), | |
213 | OPT_END() | |
214 | }; | |
215 | ||
216 | argc = parse_options(argc, argv, NULL, options, usage, test_flags); | |
217 | ||
218 | printf("opt: %d\n", opt); | |
219 | print_args(argc, argv); | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | static enum parse_opt_flags test_flags = 0; | |
225 | static const struct option test_flag_options[] = { | |
226 | OPT_GROUP("flag-options:"), | |
227 | OPT_BIT(0, "keep-dashdash", &test_flags, | |
228 | "pass PARSE_OPT_KEEP_DASHDASH to parse_options()", | |
229 | PARSE_OPT_KEEP_DASHDASH), | |
230 | OPT_BIT(0, "stop-at-non-option", &test_flags, | |
231 | "pass PARSE_OPT_STOP_AT_NON_OPTION to parse_options()", | |
232 | PARSE_OPT_STOP_AT_NON_OPTION), | |
233 | OPT_BIT(0, "keep-argv0", &test_flags, | |
234 | "pass PARSE_OPT_KEEP_ARGV0 to parse_options()", | |
235 | PARSE_OPT_KEEP_ARGV0), | |
99d86d60 SG |
236 | OPT_BIT(0, "keep-unknown-opt", &test_flags, |
237 | "pass PARSE_OPT_KEEP_UNKNOWN_OPT to parse_options()", | |
238 | PARSE_OPT_KEEP_UNKNOWN_OPT), | |
c1b117d3 SG |
239 | OPT_BIT(0, "no-internal-help", &test_flags, |
240 | "pass PARSE_OPT_NO_INTERNAL_HELP to parse_options()", | |
241 | PARSE_OPT_NO_INTERNAL_HELP), | |
fa83cc83 SG |
242 | OPT_BIT(0, "subcommand-optional", &test_flags, |
243 | "pass PARSE_OPT_SUBCOMMAND_OPTIONAL to parse_options()", | |
244 | PARSE_OPT_SUBCOMMAND_OPTIONAL), | |
c1b117d3 SG |
245 | OPT_END() |
246 | }; | |
247 | ||
248 | int cmd__parse_options_flags(int argc, const char **argv) | |
249 | { | |
250 | const char *usage[] = { | |
251 | "test-tool parse-options-flags [flag-options] cmd [options]", | |
252 | NULL | |
253 | }; | |
254 | ||
255 | argc = parse_options(argc, argv, NULL, test_flag_options, usage, | |
256 | PARSE_OPT_STOP_AT_NON_OPTION); | |
257 | ||
45bec2ea | 258 | if (!argc || strcmp(argv[0], "cmd")) { |
c1b117d3 SG |
259 | error("'cmd' is mandatory"); |
260 | usage_with_options(usage, test_flag_options); | |
261 | } | |
262 | ||
263 | return parse_options_flags__cmd(argc, argv, test_flags); | |
264 | } | |
fa83cc83 SG |
265 | |
266 | static int subcmd_one(int argc, const char **argv, const char *prefix) | |
267 | { | |
268 | printf("fn: subcmd_one\n"); | |
269 | print_args(argc, argv); | |
270 | return 0; | |
271 | } | |
272 | ||
273 | static int subcmd_two(int argc, const char **argv, const char *prefix) | |
274 | { | |
275 | printf("fn: subcmd_two\n"); | |
276 | print_args(argc, argv); | |
277 | return 0; | |
278 | } | |
279 | ||
280 | static int parse_subcommand__cmd(int argc, const char **argv, | |
281 | enum parse_opt_flags test_flags) | |
282 | { | |
283 | const char *usage[] = { | |
284 | "<...> cmd subcmd-one", | |
285 | "<...> cmd subcmd-two", | |
286 | NULL | |
287 | }; | |
288 | parse_opt_subcommand_fn *fn = NULL; | |
289 | int opt = 0; | |
290 | struct option options[] = { | |
291 | OPT_SUBCOMMAND("subcmd-one", &fn, subcmd_one), | |
292 | OPT_SUBCOMMAND("subcmd-two", &fn, subcmd_two), | |
293 | OPT_INTEGER('o', "opt", &opt, "an integer option"), | |
294 | OPT_END() | |
295 | }; | |
296 | ||
297 | if (test_flags & PARSE_OPT_SUBCOMMAND_OPTIONAL) | |
298 | fn = subcmd_one; | |
299 | argc = parse_options(argc, argv, NULL, options, usage, test_flags); | |
300 | ||
301 | printf("opt: %d\n", opt); | |
302 | ||
303 | return fn(argc, argv, NULL); | |
304 | } | |
305 | ||
306 | int cmd__parse_subcommand(int argc, const char **argv) | |
307 | { | |
308 | const char *usage[] = { | |
309 | "test-tool parse-subcommand [flag-options] cmd <subcommand>", | |
310 | NULL | |
311 | }; | |
312 | ||
313 | argc = parse_options(argc, argv, NULL, test_flag_options, usage, | |
314 | PARSE_OPT_STOP_AT_NON_OPTION); | |
315 | ||
45bec2ea | 316 | if (!argc || strcmp(argv[0], "cmd")) { |
fa83cc83 SG |
317 | error("'cmd' is mandatory"); |
318 | usage_with_options(usage, test_flag_options); | |
319 | } | |
320 | ||
321 | return parse_subcommand__cmd(argc, argv, test_flags); | |
322 | } |