1 /* getopt.c - Parse command-line options
2 *
3 * Copyright 2019 The Android Open Source Project
4 *
5 * See https://man7.org/linux/man-pages/man1/getopt.1.html
6
7 USE_GETOPT(NEWTOY(getopt, "^a(alternative)n:(name)o:(options)l*(long)(longoptions)Tu", TOYFLAG_USR|TOYFLAG_BIN))
8
9 config GETOPT
10 bool "getopt"
11 default y
12 help
13 usage: getopt [-aTu] [-lo OPTIONS] [-n NAME] [OPTIONS] ARG...
14
15 Outputs command line with recognized OPTIONS character arguments moved to
16 front, then "--", then non-option arguments. Returns 1 if unknown options.
17 OPTIONS followed by : take an argument, or :: for optional arguments (which
18 must be attached, ala -xblah or --long=blah).
19
20 -a Allow long options starting with a single -
21 -l Long OPTIONS (repeated or comma separated)
22 -n Command NAME for error messages
23 -o Short OPTIONS (instead of using first argument)
24 -T Test whether this is a modern getopt
25 -u Unquoted output (default if no other options set)
26
27 Example:
28 $ getopt -l long:,arg:: abc command --long -b there --arg
29 --long '-b' --arg '' -- 'command' 'there'
30 */
31
32 #define FOR_getopt
33 #include "toys.h"
34 #include <getopt.h> // Everything else uses lib/args.c
35
36 GLOBALS(
37 struct arg_list *l;
38 char *o, *n;
39 )
40
out(char * s)41 static void out(char *s)
42 {
43 if (FLAG(u)) xprintf(" %s", s);
44 else {
45 xputsn(" '");
46 for (; *s; s++) {
47 if (*s == '\'') xputsn("'\\''");
48 else putchar(*s);
49 }
50 putchar('\'');
51 }
52 }
53
parse_long_opt(void * data,char * str,int len)54 static char *parse_long_opt(void *data, char *str, int len)
55 {
56 struct option **lopt_ptr = data, *lopt = *lopt_ptr;
57
58 // Trailing : or :: means this option takes a required or optional argument.
59 // no_argument = 0, required_argument = 1, optional_argument = 2.
60 for (lopt->has_arg = 0; len>0 && str[len-1] == ':'; lopt->has_arg++) len--;
61 if (!len || lopt->has_arg>2) return str;
62
63 lopt->name = xstrndup(str, len);
64 (*lopt_ptr)++;
65
66 return 0;
67 }
68
getopt_main(void)69 void getopt_main(void)
70 {
71 int argc = toys.optc+1, i = 0, j = 0, ch;
72 char **argv = xzalloc(sizeof(char *)*(argc+1));
73 struct option *lopts = xzalloc(sizeof(struct option)*argc), *lopt = lopts;
74
75 if (FLAG(T)) {
76 toys.exitval = 4;
77 return;
78 }
79
80 comma_args(TT.l, &lopt, "bad -l", parse_long_opt);
81 argv[j++] = TT.n ? : "getopt";
82
83 if (!FLAG(o)) {
84 TT.o = toys.optargs[i++];
85 argc--;
86 }
87 if (!TT.o) error_exit("no OPTSTR");
88 if (!toys.optflags) toys.optflags = FLAG_u;
89
90 while (i<toys.optc) argv[j++] = toys.optargs[i++];
91
92 // BSD getopt doesn't honor argv[0] (for -n), so handle errors ourselves.
93 opterr = 0;
94 optind = 1;
95 while ((ch = (FLAG(a) ? getopt_long_only : getopt_long)(argc, argv, TT.o,
96 lopts, &i)) != -1) {
97 if (ch == '?') {
98 fprintf(stderr, "%s: invalid option '%c'\n", *argv, optopt);
99 toys.exitval = 1;
100 } else if (!ch) {
101 xprintf(" --%s", lopts[i].name);
102 if (lopts[i].has_arg) out(optarg ? : "");
103 } else {
104 xprintf(" -%c", ch);
105 if (optarg) out(optarg);
106 }
107 }
108
109 xputsn(" --");
110 for (; optind<argc; optind++) out(argv[optind]);
111 putchar('\n');
112 }
113