xref: /aosp_15_r20/external/toybox/toys/other/getopt.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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