getopt.c 2.5 KB

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