• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* getopt.c - Parse command-line options
2  *
3  * Copyright 2019 The Android Open Source Project
4 
5 USE_GETOPT(NEWTOY(getopt, "^a(alternative)n:(name)o:(options)l*(long)(longoptions)Tu", TOYFLAG_USR|TOYFLAG_BIN))
6 
7 config GETOPT
8   bool "getopt"
9   default n
10   help
11     usage: getopt [OPTIONS] [--] ARG...
12 
13     Parse command-line options for use in shell scripts.
14 
15     -a	Allow long options starting with a single -.
16     -l OPTS	Specify long options.
17     -n NAME	Command name for error messages.
18     -o OPTS	Specify short options.
19     -T	Test whether this is a modern getopt.
20     -u	Output options unquoted.
21 */
22 
23 #define FOR_getopt
24 #include "toys.h"
25 #include <getopt.h> // Everything else uses lib/args.c
26 
27 GLOBALS(
28   struct arg_list *l;
29   char *o, *n;
30 )
31 
out(char * s)32 static void out(char *s)
33 {
34   if (FLAG(u)) printf(" %s", s);
35   else {
36     printf(" '");
37     for (; *s; s++) {
38       if (*s == '\'') printf("'\\''");
39       else putchar(*s);
40     }
41     printf("'");
42   }
43 }
44 
parse_long_opt(void * data,char * str,int len)45 static char *parse_long_opt(void *data, char *str, int len)
46 {
47   struct option **lopt_ptr = data, *lopt = *lopt_ptr;
48 
49   // Trailing : or :: means this option takes a required or optional argument.
50   // no_argument = 0, required_argument = 1, optional_argument = 2.
51   for (lopt->has_arg = 0; len>0 && str[len-1] == ':'; lopt->has_arg++) len--;
52   if (!len || lopt->has_arg>2) return str;
53 
54   lopt->name = xstrndup(str, len);
55 
56   (*lopt_ptr)++;
57   return 0;
58 }
59 
getopt_main(void)60 void getopt_main(void)
61 {
62   int argc = toys.optc+1;
63   char **argv = xzalloc(sizeof(char *)*(argc+1));
64   struct option *lopts = xzalloc(sizeof(struct option)*argc), *lopt = lopts;
65   int i = 0, j = 0, ch;
66 
67   if (FLAG(T)) {
68     toys.exitval = 4;
69     return;
70   }
71 
72   comma_args(TT.l, &lopt, "bad -l", parse_long_opt);
73   argv[j++] = TT.n ? TT.n : "getopt";
74 
75   // Legacy mode: don't quote output and take the first argument as OPTSTR.
76   if (!FLAG(o)) {
77     toys.optflags |= FLAG_u;
78     TT.o = toys.optargs[i++];
79     if (!TT.o) error_exit("no OPTSTR");
80     --argc;
81   }
82 
83   while (i<toys.optc) argv[j++] = toys.optargs[i++];
84 
85   // BSD getopts don't honor argv[0] (for -n), so handle errors ourselves.
86   opterr = 0;
87   optind = 1;
88   while ((ch = (FLAG(a)?getopt_long_only:getopt_long)(argc, argv, TT.o,
89           lopts, &i)) != -1) {
90     if (ch == '?') {
91       fprintf(stderr, "%s: invalid option '%c'\n", argv[0], optopt);
92       toys.exitval = 1;
93     } else if (!ch) {
94       printf(" --%s", lopts[i].name);
95       if (lopts[i].has_arg) out(optarg ? optarg : "");
96     } else {
97       printf(" -%c", ch);
98       if (optarg) out(optarg);
99     }
100   }
101 
102   printf(" --");
103   for (; optind<argc; optind++) out(argv[optind]);
104   printf("\n");
105 }
106