1#include <linux/compiler.h>
2#include <linux/string.h>
3#include <linux/types.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <stdint.h>
7#include <string.h>
8#include <ctype.h>
9#include "subcmd-util.h"
10#include "parse-options.h"
11#include "subcmd-config.h"
12#include "pager.h"
13
14#define OPT_SHORT 1
15#define OPT_UNSET 2
16
17char *error_buf;
18
19static int opterror(const struct option *opt, const char *reason, int flags)
20{
21 if (flags & OPT_SHORT)
22 fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason);
23 else if (flags & OPT_UNSET)
24 fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason);
25 else
26 fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason);
27
28 return -1;
29}
30
31static const char *skip_prefix(const char *str, const char *prefix)
32{
33 size_t len = strlen(prefix);
34 return strncmp(str, prefix, len) ? NULL : str + len;
35}
36
37static void optwarning(const struct option *opt, const char *reason, int flags)
38{
39 if (flags & OPT_SHORT)
40 fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason);
41 else if (flags & OPT_UNSET)
42 fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason);
43 else
44 fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason);
45}
46
47static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
48 int flags, const char **arg)
49{
50 const char *res;
51
52 if (p->opt) {
53 res = p->opt;
54 p->opt = NULL;
55 } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
56 **(p->argv + 1) == '-')) {
57 res = (const char *)opt->defval;
58 } else if (p->argc > 1) {
59 p->argc--;
60 res = *++p->argv;
61 } else
62 return opterror(opt, "requires a value", flags);
63 if (arg)
64 *arg = res;
65 return 0;
66}
67
68static int get_value(struct parse_opt_ctx_t *p,
69 const struct option *opt, int flags)
70{
71 const char *s, *arg = NULL;
72 const int unset = flags & OPT_UNSET;
73 int err;
74
75 if (unset && p->opt)
76 return opterror(opt, "takes no value", flags);
77 if (unset && (opt->flags & PARSE_OPT_NONEG))
78 return opterror(opt, "isn't available", flags);
79 if (opt->flags & PARSE_OPT_DISABLED)
80 return opterror(opt, "is not usable", flags);
81
82 if (opt->flags & PARSE_OPT_EXCLUSIVE) {
83 if (p->excl_opt && p->excl_opt != opt) {
84 char msg[128];
85
86 if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
87 p->excl_opt->long_name == NULL) {
88 snprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
89 p->excl_opt->short_name);
90 } else {
91 snprintf(msg, sizeof(msg), "cannot be used with %s",
92 p->excl_opt->long_name);
93 }
94 opterror(opt, msg, flags);
95 return -3;
96 }
97 p->excl_opt = opt;
98 }
99 if (!(flags & OPT_SHORT) && p->opt) {
100 switch (opt->type) {
101 case OPTION_CALLBACK:
102 if (!(opt->flags & PARSE_OPT_NOARG))
103 break;
104
105 case OPTION_BOOLEAN:
106 case OPTION_INCR:
107 case OPTION_BIT:
108 case OPTION_SET_UINT:
109 case OPTION_SET_PTR:
110 return opterror(opt, "takes no value", flags);
111 case OPTION_END:
112 case OPTION_ARGUMENT:
113 case OPTION_GROUP:
114 case OPTION_STRING:
115 case OPTION_INTEGER:
116 case OPTION_UINTEGER:
117 case OPTION_LONG:
118 case OPTION_U64:
119 default:
120 break;
121 }
122 }
123
124 if (opt->flags & PARSE_OPT_NOBUILD) {
125 char reason[128];
126 bool noarg = false;
127
128 err = snprintf(reason, sizeof(reason),
129 opt->flags & PARSE_OPT_CANSKIP ?
130 "is being ignored because %s " :
131 "is not available because %s",
132 opt->build_opt);
133 reason[sizeof(reason) - 1] = '\0';
134
135 if (err < 0)
136 strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ?
137 "is being ignored" :
138 "is not available",
139 sizeof(reason));
140
141 if (!(opt->flags & PARSE_OPT_CANSKIP))
142 return opterror(opt, reason, flags);
143
144 err = 0;
145 if (unset)
146 noarg = true;
147 if (opt->flags & PARSE_OPT_NOARG)
148 noarg = true;
149 if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
150 noarg = true;
151
152 switch (opt->type) {
153 case OPTION_BOOLEAN:
154 case OPTION_INCR:
155 case OPTION_BIT:
156 case OPTION_SET_UINT:
157 case OPTION_SET_PTR:
158 case OPTION_END:
159 case OPTION_ARGUMENT:
160 case OPTION_GROUP:
161 noarg = true;
162 break;
163 case OPTION_CALLBACK:
164 case OPTION_STRING:
165 case OPTION_INTEGER:
166 case OPTION_UINTEGER:
167 case OPTION_LONG:
168 case OPTION_U64:
169 default:
170 break;
171 }
172
173 if (!noarg)
174 err = get_arg(p, opt, flags, NULL);
175 if (err)
176 return err;
177
178 optwarning(opt, reason, flags);
179 return 0;
180 }
181
182 switch (opt->type) {
183 case OPTION_BIT:
184 if (unset)
185 *(int *)opt->value &= ~opt->defval;
186 else
187 *(int *)opt->value |= opt->defval;
188 return 0;
189
190 case OPTION_BOOLEAN:
191 *(bool *)opt->value = unset ? false : true;
192 if (opt->set)
193 *(bool *)opt->set = true;
194 return 0;
195
196 case OPTION_INCR:
197 *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
198 return 0;
199
200 case OPTION_SET_UINT:
201 *(unsigned int *)opt->value = unset ? 0 : opt->defval;
202 return 0;
203
204 case OPTION_SET_PTR:
205 *(void **)opt->value = unset ? NULL : (void *)opt->defval;
206 return 0;
207
208 case OPTION_STRING:
209 err = 0;
210 if (unset)
211 *(const char **)opt->value = NULL;
212 else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
213 *(const char **)opt->value = (const char *)opt->defval;
214 else
215 err = get_arg(p, opt, flags, (const char **)opt->value);
216
217 if (opt->set)
218 *(bool *)opt->set = true;
219
220
221 if (opt->flags & PARSE_OPT_NOEMPTY) {
222 const char *val = *(const char **)opt->value;
223
224 if (!val)
225 return err;
226
227
228 if (val[0] == '\0') {
229 *(const char **)opt->value = NULL;
230 return 0;
231 }
232 }
233
234 return err;
235
236 case OPTION_CALLBACK:
237 if (unset)
238 return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
239 if (opt->flags & PARSE_OPT_NOARG)
240 return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
241 if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
242 return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
243 if (get_arg(p, opt, flags, &arg))
244 return -1;
245 return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
246
247 case OPTION_INTEGER:
248 if (unset) {
249 *(int *)opt->value = 0;
250 return 0;
251 }
252 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
253 *(int *)opt->value = opt->defval;
254 return 0;
255 }
256 if (get_arg(p, opt, flags, &arg))
257 return -1;
258 *(int *)opt->value = strtol(arg, (char **)&s, 10);
259 if (*s)
260 return opterror(opt, "expects a numerical value", flags);
261 return 0;
262
263 case OPTION_UINTEGER:
264 if (unset) {
265 *(unsigned int *)opt->value = 0;
266 return 0;
267 }
268 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
269 *(unsigned int *)opt->value = opt->defval;
270 return 0;
271 }
272 if (get_arg(p, opt, flags, &arg))
273 return -1;
274 if (arg[0] == '-')
275 return opterror(opt, "expects an unsigned numerical value", flags);
276 *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
277 if (*s)
278 return opterror(opt, "expects a numerical value", flags);
279 return 0;
280
281 case OPTION_LONG:
282 if (unset) {
283 *(long *)opt->value = 0;
284 return 0;
285 }
286 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
287 *(long *)opt->value = opt->defval;
288 return 0;
289 }
290 if (get_arg(p, opt, flags, &arg))
291 return -1;
292 *(long *)opt->value = strtol(arg, (char **)&s, 10);
293 if (*s)
294 return opterror(opt, "expects a numerical value", flags);
295 return 0;
296
297 case OPTION_U64:
298 if (unset) {
299 *(u64 *)opt->value = 0;
300 return 0;
301 }
302 if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
303 *(u64 *)opt->value = opt->defval;
304 return 0;
305 }
306 if (get_arg(p, opt, flags, &arg))
307 return -1;
308 if (arg[0] == '-')
309 return opterror(opt, "expects an unsigned numerical value", flags);
310 *(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
311 if (*s)
312 return opterror(opt, "expects a numerical value", flags);
313 return 0;
314
315 case OPTION_END:
316 case OPTION_ARGUMENT:
317 case OPTION_GROUP:
318 default:
319 die("should not happen, someone must be hit on the forehead");
320 }
321}
322
323static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
324{
325retry:
326 for (; options->type != OPTION_END; options++) {
327 if (options->short_name == *p->opt) {
328 p->opt = p->opt[1] ? p->opt + 1 : NULL;
329 return get_value(p, options, OPT_SHORT);
330 }
331 }
332
333 if (options->parent) {
334 options = options->parent;
335 goto retry;
336 }
337
338 return -2;
339}
340
341static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
342 const struct option *options)
343{
344 const char *arg_end = strchr(arg, '=');
345 const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
346 int abbrev_flags = 0, ambiguous_flags = 0;
347
348 if (!arg_end)
349 arg_end = arg + strlen(arg);
350
351retry:
352 for (; options->type != OPTION_END; options++) {
353 const char *rest;
354 int flags = 0;
355
356 if (!options->long_name)
357 continue;
358
359 rest = skip_prefix(arg, options->long_name);
360 if (options->type == OPTION_ARGUMENT) {
361 if (!rest)
362 continue;
363 if (*rest == '=')
364 return opterror(options, "takes no value", flags);
365 if (*rest)
366 continue;
367 p->out[p->cpidx++] = arg - 2;
368 return 0;
369 }
370 if (!rest) {
371 if (!prefixcmp(options->long_name, "no-")) {
372
373
374
375
376
377
378 rest = skip_prefix(arg, options->long_name + 3);
379 if (rest) {
380 flags |= OPT_UNSET;
381 goto match;
382 }
383
384 if (!prefixcmp(options->long_name + 3, arg)) {
385 flags |= OPT_UNSET;
386 goto is_abbreviated;
387 }
388 }
389
390 if (!strncmp(options->long_name, arg, arg_end - arg)) {
391is_abbreviated:
392 if (abbrev_option) {
393
394
395
396
397
398
399 ambiguous_option = abbrev_option;
400 ambiguous_flags = abbrev_flags;
401 }
402 if (!(flags & OPT_UNSET) && *arg_end)
403 p->opt = arg_end + 1;
404 abbrev_option = options;
405 abbrev_flags = flags;
406 continue;
407 }
408
409 if (!prefixcmp("no-", arg)) {
410 flags |= OPT_UNSET;
411 goto is_abbreviated;
412 }
413
414 if (strncmp(arg, "no-", 3))
415 continue;
416 flags |= OPT_UNSET;
417 rest = skip_prefix(arg + 3, options->long_name);
418
419 if (!rest && !prefixcmp(options->long_name, arg + 3))
420 goto is_abbreviated;
421 if (!rest)
422 continue;
423 }
424match:
425 if (*rest) {
426 if (*rest != '=')
427 continue;
428 p->opt = rest + 1;
429 }
430 return get_value(p, options, flags);
431 }
432
433 if (ambiguous_option) {
434 fprintf(stderr,
435 " Error: Ambiguous option: %s (could be --%s%s or --%s%s)",
436 arg,
437 (ambiguous_flags & OPT_UNSET) ? "no-" : "",
438 ambiguous_option->long_name,
439 (abbrev_flags & OPT_UNSET) ? "no-" : "",
440 abbrev_option->long_name);
441 return -1;
442 }
443 if (abbrev_option)
444 return get_value(p, abbrev_option, abbrev_flags);
445
446 if (options->parent) {
447 options = options->parent;
448 goto retry;
449 }
450
451 return -2;
452}
453
454static void check_typos(const char *arg, const struct option *options)
455{
456 if (strlen(arg) < 3)
457 return;
458
459 if (!prefixcmp(arg, "no-")) {
460 fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg);
461 exit(129);
462 }
463
464 for (; options->type != OPTION_END; options++) {
465 if (!options->long_name)
466 continue;
467 if (!prefixcmp(options->long_name, arg)) {
468 fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg);
469 exit(129);
470 }
471 }
472}
473
474static void parse_options_start(struct parse_opt_ctx_t *ctx,
475 int argc, const char **argv, int flags)
476{
477 memset(ctx, 0, sizeof(*ctx));
478 ctx->argc = argc - 1;
479 ctx->argv = argv + 1;
480 ctx->out = argv;
481 ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
482 ctx->flags = flags;
483 if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
484 (flags & PARSE_OPT_STOP_AT_NON_OPTION))
485 die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
486}
487
488static int usage_with_options_internal(const char * const *,
489 const struct option *, int,
490 struct parse_opt_ctx_t *);
491
492static int parse_options_step(struct parse_opt_ctx_t *ctx,
493 const struct option *options,
494 const char * const usagestr[])
495{
496 int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
497 int excl_short_opt = 1;
498 const char *arg;
499
500
501 ctx->opt = NULL;
502
503 for (; ctx->argc; ctx->argc--, ctx->argv++) {
504 arg = ctx->argv[0];
505 if (*arg != '-' || !arg[1]) {
506 if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
507 break;
508 ctx->out[ctx->cpidx++] = ctx->argv[0];
509 continue;
510 }
511
512 if (arg[1] != '-') {
513 ctx->opt = ++arg;
514 if (internal_help && *ctx->opt == 'h') {
515 return usage_with_options_internal(usagestr, options, 0, ctx);
516 }
517 switch (parse_short_opt(ctx, options)) {
518 case -1:
519 return parse_options_usage(usagestr, options, arg, 1);
520 case -2:
521 goto unknown;
522 case -3:
523 goto exclusive;
524 default:
525 break;
526 }
527 if (ctx->opt)
528 check_typos(arg, options);
529 while (ctx->opt) {
530 if (internal_help && *ctx->opt == 'h')
531 return usage_with_options_internal(usagestr, options, 0, ctx);
532 arg = ctx->opt;
533 switch (parse_short_opt(ctx, options)) {
534 case -1:
535 return parse_options_usage(usagestr, options, arg, 1);
536 case -2:
537
538
539
540
541
542 ctx->argv[0] = strdup(ctx->opt - 1);
543 *(char *)ctx->argv[0] = '-';
544 goto unknown;
545 case -3:
546 goto exclusive;
547 default:
548 break;
549 }
550 }
551 continue;
552 }
553
554 if (!arg[2]) {
555 if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
556 ctx->argc--;
557 ctx->argv++;
558 }
559 break;
560 }
561
562 arg += 2;
563 if (internal_help && !strcmp(arg, "help-all"))
564 return usage_with_options_internal(usagestr, options, 1, ctx);
565 if (internal_help && !strcmp(arg, "help"))
566 return usage_with_options_internal(usagestr, options, 0, ctx);
567 if (!strcmp(arg, "list-opts"))
568 return PARSE_OPT_LIST_OPTS;
569 if (!strcmp(arg, "list-cmds"))
570 return PARSE_OPT_LIST_SUBCMDS;
571 switch (parse_long_opt(ctx, arg, options)) {
572 case -1:
573 return parse_options_usage(usagestr, options, arg, 0);
574 case -2:
575 goto unknown;
576 case -3:
577 excl_short_opt = 0;
578 goto exclusive;
579 default:
580 break;
581 }
582 continue;
583unknown:
584 if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
585 return PARSE_OPT_UNKNOWN;
586 ctx->out[ctx->cpidx++] = ctx->argv[0];
587 ctx->opt = NULL;
588 }
589 return PARSE_OPT_DONE;
590
591exclusive:
592 parse_options_usage(usagestr, options, arg, excl_short_opt);
593 if ((excl_short_opt && ctx->excl_opt->short_name) ||
594 ctx->excl_opt->long_name == NULL) {
595 char opt = ctx->excl_opt->short_name;
596 parse_options_usage(NULL, options, &opt, 1);
597 } else {
598 parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);
599 }
600 return PARSE_OPT_HELP;
601}
602
603static int parse_options_end(struct parse_opt_ctx_t *ctx)
604{
605 memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
606 ctx->out[ctx->cpidx + ctx->argc] = NULL;
607 return ctx->cpidx + ctx->argc;
608}
609
610int parse_options_subcommand(int argc, const char **argv, const struct option *options,
611 const char *const subcommands[], const char *usagestr[], int flags)
612{
613 struct parse_opt_ctx_t ctx;
614
615
616 if (subcommands && !usagestr[0]) {
617 char *buf = NULL;
618
619 astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]);
620
621 for (int i = 0; subcommands[i]; i++) {
622 if (i)
623 astrcat(&buf, "|");
624 astrcat(&buf, subcommands[i]);
625 }
626 astrcat(&buf, "}");
627
628 usagestr[0] = buf;
629 }
630
631 parse_options_start(&ctx, argc, argv, flags);
632 switch (parse_options_step(&ctx, options, usagestr)) {
633 case PARSE_OPT_HELP:
634 exit(129);
635 case PARSE_OPT_DONE:
636 break;
637 case PARSE_OPT_LIST_OPTS:
638 while (options->type != OPTION_END) {
639 if (options->long_name)
640 printf("--%s ", options->long_name);
641 options++;
642 }
643 putchar('\n');
644 exit(130);
645 case PARSE_OPT_LIST_SUBCMDS:
646 if (subcommands) {
647 for (int i = 0; subcommands[i]; i++)
648 printf("%s ", subcommands[i]);
649 }
650 putchar('\n');
651 exit(130);
652 default:
653 if (ctx.argv[0][1] == '-')
654 astrcatf(&error_buf, "unknown option `%s'",
655 ctx.argv[0] + 2);
656 else
657 astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt);
658 usage_with_options(usagestr, options);
659 }
660
661 return parse_options_end(&ctx);
662}
663
664int parse_options(int argc, const char **argv, const struct option *options,
665 const char * const usagestr[], int flags)
666{
667 return parse_options_subcommand(argc, argv, options, NULL,
668 (const char **) usagestr, flags);
669}
670
671#define USAGE_OPTS_WIDTH 24
672#define USAGE_GAP 2
673
674static void print_option_help(const struct option *opts, int full)
675{
676 size_t pos;
677 int pad;
678
679 if (opts->type == OPTION_GROUP) {
680 fputc('\n', stderr);
681 if (*opts->help)
682 fprintf(stderr, "%s\n", opts->help);
683 return;
684 }
685 if (!full && (opts->flags & PARSE_OPT_HIDDEN))
686 return;
687 if (opts->flags & PARSE_OPT_DISABLED)
688 return;
689
690 pos = fprintf(stderr, " ");
691 if (opts->short_name)
692 pos += fprintf(stderr, "-%c", opts->short_name);
693 else
694 pos += fprintf(stderr, " ");
695
696 if (opts->long_name && opts->short_name)
697 pos += fprintf(stderr, ", ");
698 if (opts->long_name)
699 pos += fprintf(stderr, "--%s", opts->long_name);
700
701 switch (opts->type) {
702 case OPTION_ARGUMENT:
703 break;
704 case OPTION_LONG:
705 case OPTION_U64:
706 case OPTION_INTEGER:
707 case OPTION_UINTEGER:
708 if (opts->flags & PARSE_OPT_OPTARG)
709 if (opts->long_name)
710 pos += fprintf(stderr, "[=<n>]");
711 else
712 pos += fprintf(stderr, "[<n>]");
713 else
714 pos += fprintf(stderr, " <n>");
715 break;
716 case OPTION_CALLBACK:
717 if (opts->flags & PARSE_OPT_NOARG)
718 break;
719
720 case OPTION_STRING:
721 if (opts->argh) {
722 if (opts->flags & PARSE_OPT_OPTARG)
723 if (opts->long_name)
724 pos += fprintf(stderr, "[=<%s>]", opts->argh);
725 else
726 pos += fprintf(stderr, "[<%s>]", opts->argh);
727 else
728 pos += fprintf(stderr, " <%s>", opts->argh);
729 } else {
730 if (opts->flags & PARSE_OPT_OPTARG)
731 if (opts->long_name)
732 pos += fprintf(stderr, "[=...]");
733 else
734 pos += fprintf(stderr, "[...]");
735 else
736 pos += fprintf(stderr, " ...");
737 }
738 break;
739 default:
740 case OPTION_END:
741 case OPTION_GROUP:
742 case OPTION_BIT:
743 case OPTION_BOOLEAN:
744 case OPTION_INCR:
745 case OPTION_SET_UINT:
746 case OPTION_SET_PTR:
747 break;
748 }
749
750 if (pos <= USAGE_OPTS_WIDTH)
751 pad = USAGE_OPTS_WIDTH - pos;
752 else {
753 fputc('\n', stderr);
754 pad = USAGE_OPTS_WIDTH;
755 }
756 fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
757 if (opts->flags & PARSE_OPT_NOBUILD)
758 fprintf(stderr, "%*s(not built-in because %s)\n",
759 USAGE_OPTS_WIDTH + USAGE_GAP, "",
760 opts->build_opt);
761}
762
763static int option__cmp(const void *va, const void *vb)
764{
765 const struct option *a = va, *b = vb;
766 int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;
767
768 if (sa == 0)
769 sa = 'z' + 1;
770 if (sb == 0)
771 sb = 'z' + 1;
772
773 ret = sa - sb;
774
775 if (ret == 0) {
776 const char *la = a->long_name ?: "",
777 *lb = b->long_name ?: "";
778 ret = strcmp(la, lb);
779 }
780
781 return ret;
782}
783
784static struct option *options__order(const struct option *opts)
785{
786 int nr_opts = 0, len;
787 const struct option *o = opts;
788 struct option *ordered;
789
790 for (o = opts; o->type != OPTION_END; o++)
791 ++nr_opts;
792
793 len = sizeof(*o) * (nr_opts + 1);
794 ordered = malloc(len);
795 if (!ordered)
796 goto out;
797 memcpy(ordered, opts, len);
798
799 qsort(ordered, nr_opts, sizeof(*o), option__cmp);
800out:
801 return ordered;
802}
803
804static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)
805{
806 int i;
807
808 for (i = 1; i < ctx->argc; ++i) {
809 const char *arg = ctx->argv[i];
810
811 if (arg[0] != '-') {
812 if (arg[1] == '\0') {
813 if (arg[0] == opt->short_name)
814 return true;
815 continue;
816 }
817
818 if (opt->long_name && strcmp(opt->long_name, arg) == 0)
819 return true;
820
821 if (opt->help && strcasestr(opt->help, arg) != NULL)
822 return true;
823
824 continue;
825 }
826
827 if (arg[1] == opt->short_name ||
828 (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))
829 return true;
830 }
831
832 return false;
833}
834
835static int usage_with_options_internal(const char * const *usagestr,
836 const struct option *opts, int full,
837 struct parse_opt_ctx_t *ctx)
838{
839 struct option *ordered;
840
841 if (!usagestr)
842 return PARSE_OPT_HELP;
843
844 setup_pager();
845
846 if (error_buf) {
847 fprintf(stderr, " Error: %s\n", error_buf);
848 zfree(&error_buf);
849 }
850
851 fprintf(stderr, "\n Usage: %s\n", *usagestr++);
852 while (*usagestr && **usagestr)
853 fprintf(stderr, " or: %s\n", *usagestr++);
854 while (*usagestr) {
855 fprintf(stderr, "%s%s\n",
856 **usagestr ? " " : "",
857 *usagestr);
858 usagestr++;
859 }
860
861 if (opts->type != OPTION_GROUP)
862 fputc('\n', stderr);
863
864 ordered = options__order(opts);
865 if (ordered)
866 opts = ordered;
867
868 for ( ; opts->type != OPTION_END; opts++) {
869 if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))
870 continue;
871 print_option_help(opts, full);
872 }
873
874 fputc('\n', stderr);
875
876 free(ordered);
877
878 return PARSE_OPT_HELP;
879}
880
881void usage_with_options(const char * const *usagestr,
882 const struct option *opts)
883{
884 usage_with_options_internal(usagestr, opts, 0, NULL);
885 exit(129);
886}
887
888void usage_with_options_msg(const char * const *usagestr,
889 const struct option *opts, const char *fmt, ...)
890{
891 va_list ap;
892 char *tmp = error_buf;
893
894 va_start(ap, fmt);
895 if (vasprintf(&error_buf, fmt, ap) == -1)
896 die("vasprintf failed");
897 va_end(ap);
898
899 free(tmp);
900
901 usage_with_options_internal(usagestr, opts, 0, NULL);
902 exit(129);
903}
904
905int parse_options_usage(const char * const *usagestr,
906 const struct option *opts,
907 const char *optstr, bool short_opt)
908{
909 if (!usagestr)
910 goto opt;
911
912 fprintf(stderr, "\n Usage: %s\n", *usagestr++);
913 while (*usagestr && **usagestr)
914 fprintf(stderr, " or: %s\n", *usagestr++);
915 while (*usagestr) {
916 fprintf(stderr, "%s%s\n",
917 **usagestr ? " " : "",
918 *usagestr);
919 usagestr++;
920 }
921 fputc('\n', stderr);
922
923opt:
924 for ( ; opts->type != OPTION_END; opts++) {
925 if (short_opt) {
926 if (opts->short_name == *optstr) {
927 print_option_help(opts, 0);
928 break;
929 }
930 continue;
931 }
932
933 if (opts->long_name == NULL)
934 continue;
935
936 if (!prefixcmp(opts->long_name, optstr))
937 print_option_help(opts, 0);
938 if (!prefixcmp("no-", optstr) &&
939 !prefixcmp(opts->long_name, optstr + 3))
940 print_option_help(opts, 0);
941 }
942
943 return PARSE_OPT_HELP;
944}
945
946
947int parse_opt_verbosity_cb(const struct option *opt,
948 const char *arg __maybe_unused,
949 int unset)
950{
951 int *target = opt->value;
952
953 if (unset)
954
955 *target = 0;
956 else if (opt->short_name == 'v') {
957 if (*target >= 0)
958 (*target)++;
959 else
960 *target = 1;
961 } else {
962 if (*target <= 0)
963 (*target)--;
964 else
965 *target = -1;
966 }
967 return 0;
968}
969
970static struct option *
971find_option(struct option *opts, int shortopt, const char *longopt)
972{
973 for (; opts->type != OPTION_END; opts++) {
974 if ((shortopt && opts->short_name == shortopt) ||
975 (opts->long_name && longopt &&
976 !strcmp(opts->long_name, longopt)))
977 return opts;
978 }
979 return NULL;
980}
981
982void set_option_flag(struct option *opts, int shortopt, const char *longopt,
983 int flag)
984{
985 struct option *opt = find_option(opts, shortopt, longopt);
986
987 if (opt)
988 opt->flags |= flag;
989 return;
990}
991
992void set_option_nobuild(struct option *opts, int shortopt,
993 const char *longopt,
994 const char *build_opt,
995 bool can_skip)
996{
997 struct option *opt = find_option(opts, shortopt, longopt);
998
999 if (!opt)
1000 return;
1001
1002 opt->flags |= PARSE_OPT_NOBUILD;
1003 opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0;
1004 opt->build_opt = build_opt;
1005}
1006