xref: /third_party/toybox/toys/pending/getopt.c (revision 0f66f451)
1/* getopt.c - Parse command-line options
2 *
3 * Copyright 2019 The Android Open Source Project
4
5USE_GETOPT(NEWTOY(getopt, "^a(alternative)n:(name)o:(options)l*(long)(longoptions)Tu", TOYFLAG_USR|TOYFLAG_BIN))
6
7config 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
26GLOBALS(
27  struct arg_list *l;
28  char *o, *n;
29)
30
31static void out(char *s)
32{
33  if (FLAG(u)) printf(" %s", s);
34  else {
35    printf(" '");
36    for (; *s; s++) {
37      if (*s == '\'') printf("'\\''");
38      else putchar(*s);
39    }
40    printf("'");
41  }
42}
43
44static char *parse_long_opt(void *data, char *str, int len)
45{
46  struct option **lopt_ptr = data, *lopt = *lopt_ptr;
47
48  // Trailing : or :: means this option takes a required or optional argument.
49  // no_argument = 0, required_argument = 1, optional_argument = 2.
50  for (lopt->has_arg = 0; len>0 && str[len-1] == ':'; lopt->has_arg++) len--;
51  if (!len || lopt->has_arg>2) return str;
52
53  lopt->name = xstrndup(str, len);
54
55  (*lopt_ptr)++;
56  return 0;
57}
58
59void getopt_main(void)
60{
61  int argc = toys.optc+1;
62  char **argv = xzalloc(sizeof(char *)*(argc+1));
63  struct option *lopts = xzalloc(sizeof(struct option)*argc), *lopt = lopts;
64  int i = 0, j = 0, ch;
65
66  if (FLAG(T)) {
67    toys.exitval = 4;
68    return;
69  }
70
71  comma_args(TT.l, &lopt, "bad -l", parse_long_opt);
72  argv[j++] = TT.n ? TT.n : "getopt";
73
74  // Legacy mode: don't quote output and take the first argument as OPTSTR.
75  if (!FLAG(o)) {
76    toys.optflags |= FLAG_u;
77    TT.o = toys.optargs[i++];
78    if (!TT.o) error_exit("no OPTSTR");
79    --argc;
80  }
81
82  while (i<toys.optc) argv[j++] = toys.optargs[i++];
83
84  // BSD getopts don't honor argv[0] (for -n), so handle errors ourselves.
85  opterr = 0;
86  optind = 1;
87  while ((ch = (FLAG(a)?getopt_long_only:getopt_long)(argc, argv, TT.o,
88          lopts, &i)) != -1) {
89    if (ch == '?') {
90      fprintf(stderr, "%s: invalid option '%c'\n", argv[0], optopt);
91      toys.exitval = 1;
92    } else if (!ch) {
93      printf(" --%s", lopts[i].name);
94      if (lopts[i].has_arg) out(optarg ? optarg : "");
95    } else {
96      printf(" -%c", ch);
97      if (optarg) out(optarg);
98    }
99  }
100
101  printf(" --");
102  for (; optind<argc; optind++) out(argv[optind]);
103  printf("\n");
104}
105