getopt.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. #ifndef GETOPT_H_
  2. #define GETOPT_H_
  3. #include <assert.h>
  4. #include <setjmp.h>
  5. #include <stddef.h>
  6. /**
  7. * This getopt implementation parses options of the following forms:
  8. * -a -b -c foo (single-character options)
  9. * -abc foo (packed single-character options)
  10. * -abcfoo (packed single-character options and an argument)
  11. * --foo bar (long option)
  12. * --foo=bar (long option and argument separated by '=')
  13. *
  14. * It does not support abbreviated options (e.g., interpreting --foo as
  15. * --foobar when there are no other --foo* options) since that misfeature
  16. * results in breakage when new options are added. It also does not support
  17. * options appearing after non-options (e.g., "cp foo bar -R") since that is
  18. * a horrible GNU perversion.
  19. *
  20. * Upon encountering '--', it consumes that argument (by incrementing optind)
  21. * and returns NULL to signal the end of option processing. Upon encountering
  22. * a bare '-' argument or any argument not starting with '-' it returns NULL
  23. * to signal the end of option processing (without consuming the argument).
  24. * Note that these behaviours do not apply when such strings are encountered
  25. * as arguments to options; e.g., if "--foo" takes an argument, then the
  26. * command line arguments "--foo -- --bar" is interpreted as having two
  27. * options ("--foo --" and "--bar") and no left-over arguments.
  28. */
  29. /* Work around LLVM bug. */
  30. #ifdef __clang__
  31. #warning Working around bug in LLVM optimizer
  32. #warning For more details see https://bugs.llvm.org/show_bug.cgi?id=27190
  33. #define GETOPT_USE_COMPUTED_GOTO
  34. #endif
  35. /* Work around broken <setjmp.h> header on Solaris. */
  36. #if defined(__GNUC__) && (defined(sun) || defined(__sun))
  37. #warning Working around broken <setjmp.h> header on Solaris
  38. #define GETOPT_USE_COMPUTED_GOTO
  39. #endif
  40. /* Select the method of performing local jumps. */
  41. #ifdef GETOPT_USE_COMPUTED_GOTO
  42. /* Workaround with computed goto. */
  43. #define DO_SETJMP DO_SETJMP_(__LINE__)
  44. #define DO_SETJMP_(x) DO_SETJMP__(x)
  45. #define DO_SETJMP__(x) \
  46. void * getopt_initloop = && getopt_initloop_ ## x; \
  47. getopt_initloop_ ## x:
  48. #define DO_LONGJMP \
  49. goto *getopt_initloop
  50. #else
  51. /* Intended code, for fully C99-compliant systems. */
  52. #define DO_SETJMP \
  53. sigjmp_buf getopt_initloop; \
  54. if (!getopt_initialized) \
  55. sigsetjmp(getopt_initloop, 0)
  56. #define DO_LONGJMP \
  57. siglongjmp(getopt_initloop, 1)
  58. #endif
  59. /* Avoid namespace collisions with libc getopt. */
  60. #define getopt libcperciva_getopt
  61. #define optarg libcperciva_optarg
  62. #define optind libcperciva_optind
  63. #define opterr libcperciva_opterr
  64. #define optreset libcperciva_optreset
  65. /* Standard getopt global variables. */
  66. extern const char * optarg;
  67. extern int optind, opterr, optreset;
  68. /* Dummy option string, equal to "(dummy)". */
  69. #define GETOPT_DUMMY getopt_dummy
  70. /**
  71. * GETOPT(argc, argv):
  72. * When called for the first time (or the first time after optreset is set to
  73. * a nonzero value), return GETOPT_DUMMY, aka. "(dummy)". Thereafter, return
  74. * the next option string and set optarg / optind appropriately; abort if not
  75. * properly initialized when not being called for the first time.
  76. */
  77. #define GETOPT(argc, argv) getopt(argc, argv)
  78. /**
  79. * GETOPT_SWITCH(ch):
  80. * Jump to the appropriate GETOPT_OPT, GETOPT_OPTARG, GETOPT_MISSING_ARG, or
  81. * GETOPT_DEFAULT based on the option string ${ch}. When called for the first
  82. * time, perform magic to index the options.
  83. *
  84. * GETOPT_SWITCH(ch) is equivalent to "switch (ch)" in a standard getopt loop.
  85. */
  86. #define GETOPT_SWITCH(ch) \
  87. volatile size_t getopt_ln_min = __LINE__; \
  88. volatile size_t getopt_ln = getopt_ln_min - 1; \
  89. volatile int getopt_default_missing = 0; \
  90. DO_SETJMP; \
  91. switch (getopt_initialized ? getopt_lookup(ch) + getopt_ln_min : getopt_ln++)
  92. /**
  93. * GETOPT_OPT(os):
  94. * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH.
  95. *
  96. * GETOPT_OPT("-x") is equivalent to "case 'x'" in a standard getopt loop
  97. * which has an optstring containing "x".
  98. */
  99. #define GETOPT_OPT(os) GETOPT_OPT_(os, __LINE__)
  100. #define GETOPT_OPT_(os, ln) GETOPT_OPT__(os, ln)
  101. #define GETOPT_OPT__(os, ln) \
  102. case ln: \
  103. if (getopt_initialized) \
  104. goto getopt_skip_ ## ln; \
  105. getopt_register_opt(os, ln - getopt_ln_min, 0); \
  106. DO_LONGJMP; \
  107. getopt_skip_ ## ln
  108. /**
  109. * GETOPT_OPTARG(os):
  110. * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH,
  111. * unless no argument is available, in which case jump to GETOPT_MISSING_ARG
  112. * (if present) or GETOPT_DEFAULT (if not).
  113. *
  114. * GETOPT_OPTARG("-x") is equivalent to "case 'x'" in a standard getopt loop
  115. * which has an optstring containing "x:".
  116. */
  117. #define GETOPT_OPTARG(os) GETOPT_OPTARG_(os, __LINE__)
  118. #define GETOPT_OPTARG_(os, ln) GETOPT_OPTARG__(os, ln)
  119. #define GETOPT_OPTARG__(os, ln) \
  120. case ln: \
  121. if (getopt_initialized) { \
  122. assert(optarg != NULL); \
  123. goto getopt_skip_ ## ln; \
  124. } \
  125. getopt_register_opt(os, ln - getopt_ln_min, 1); \
  126. DO_LONGJMP; \
  127. getopt_skip_ ## ln
  128. /**
  129. * GETOPT_MISSING_ARG:
  130. * Jump to this point if an option string specified in GETOPT_OPTARG is seen
  131. * but no argument is available.
  132. *
  133. * GETOPT_MISSING_ARG is equivalent to "case ':'" in a standard getopt loop
  134. * which has an optstring starting with ":". As such, it also has the effect
  135. * of disabling warnings about invalid options, as if opterr had been zeroed.
  136. */
  137. #define GETOPT_MISSING_ARG GETOPT_MISSING_ARG_(__LINE__)
  138. #define GETOPT_MISSING_ARG_(ln) GETOPT_MISSING_ARG__(ln)
  139. #define GETOPT_MISSING_ARG__(ln) \
  140. case ln: \
  141. if (getopt_initialized) \
  142. goto getopt_skip_ ## ln; \
  143. getopt_register_missing(ln - getopt_ln_min); \
  144. DO_LONGJMP; \
  145. getopt_skip_ ## ln
  146. /**
  147. * GETOPT_DEFAULT:
  148. * Jump to this point if an unrecognized option is seen or if an option
  149. * specified in GETOPT_OPTARG is seen, no argument is available, and there is
  150. * no GETOPT_MISSING_ARG label.
  151. *
  152. * GETOPT_DEFAULT is equivalent to "case '?'" in a standard getopt loop.
  153. *
  154. * NOTE: This MUST be present in the GETOPT_SWITCH statement, and MUST occur
  155. * after all other GETOPT_* labels.
  156. */
  157. #define GETOPT_DEFAULT GETOPT_DEFAULT_(__LINE__)
  158. #define GETOPT_DEFAULT_(ln) GETOPT_DEFAULT__(ln)
  159. #define GETOPT_DEFAULT__(ln) \
  160. goto getopt_skip_ ## ln; \
  161. case ln: \
  162. getopt_initialized = 1; \
  163. break; \
  164. default: \
  165. if (getopt_initialized) \
  166. goto getopt_skip_ ## ln; \
  167. if (!getopt_default_missing) { \
  168. getopt_setrange(ln - getopt_ln_min); \
  169. getopt_default_missing = 1; \
  170. } \
  171. DO_LONGJMP; \
  172. getopt_skip_ ## ln
  173. /*
  174. * The back-end implementation. These should be considered internal
  175. * interfaces and not used directly.
  176. */
  177. const char * getopt(int, char * const []);
  178. size_t getopt_lookup(const char *);
  179. void getopt_register_opt(const char *, size_t, int);
  180. void getopt_register_missing(size_t);
  181. void getopt_setrange(size_t);
  182. extern const char * getopt_dummy;
  183. extern int getopt_initialized;
  184. #endif /* !GETOPT_H_ */