aboutsummaryrefslogtreecommitdiff
path: root/src/getopt.c
blob: 321dd9f9b3b40a381ed1d6f4bb02b8628fa56123 (plain)
  1. /* $Id: getopt.c 4022 2008-03-31 06:11:07Z rra $
  2. *
  3. * Replacement implementation of getopt.
  4. *
  5. * This is a replacement implementation for getopt based on the my_getopt
  6. * distribution by Benjamin Sittler. Only the getopt interface is included,
  7. * since remctl doesn't use GNU long options, and the code has been rearranged
  8. * and reworked somewhat to fit with the remctl coding style.
  9. *
  10. * Copyright 1997, 2000, 2001, 2002 Benjamin Sittler
  11. * Copyright 2008 Russ Allbery <rra@stanford.edu>
  12. *
  13. * Permission is hereby granted, free of charge, to any person obtaining a
  14. * copy of this software and associated documentation files (the "Software"),
  15. * to deal in the Software without restriction, including without limitation
  16. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17. * and/or sell copies of the Software, and to permit persons to whom the
  18. * Software is furnished to do so, subject to the following conditions:
  19. *
  20. * The above copyright notice and this permission notice shall be included in
  21. * all copies or substantial portions of the Software.
  22. *
  23. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  26. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  28. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  29. * DEALINGS IN THE SOFTWARE.
  30. */
  31. #include <config.h>
  32. #include <portable/system.h>
  33. #include <portable/getopt.h>
  34. /*
  35. * If we're running the test suite, rename getopt and the global variables to
  36. * avoid conflicts with the system version.
  37. */
  38. #if TESTING
  39. # define getopt test_getopt
  40. int test_getopt(int, char **, const char *);
  41. # define optind test_optind
  42. # define opterr test_opterr
  43. # define optopt test_optopt
  44. # define optarg test_optarg
  45. #endif
  46. /* Initialize global interface variables. */
  47. int optind = 1;
  48. int opterr = 1;
  49. int optopt = 0;
  50. char *optarg = NULL;
  51. /*
  52. * This is the plain old UNIX getopt, with GNU-style extensions. If you're
  53. * porting some piece of UNIX software, this is all you need. It supports
  54. * GNU-style permutation and optional arguments, but does not support the GNU
  55. * -W extension.
  56. *
  57. * This function is not re-entrant or thread-safe, has static variables, and
  58. * generally isn't a great interface, but normally you only call it once.
  59. */
  60. int
  61. getopt(int argc, char *argv[], const char *optstring)
  62. {
  63. const char *p;
  64. size_t offset = 0;
  65. char mode = '\0';
  66. int colon_mode = 0;
  67. int option = -1;
  68. /* Holds the current position in the parameter being parsed. */
  69. static int charind = 0;
  70. /*
  71. * By default, getopt permutes argv as it scans and leaves all non-options
  72. * at the end. This can be changed with the first character of optstring
  73. * or the environment variable POSIXLY_CORRECT. With a first character of
  74. * '+' or when POSIXLY_CORRECT is set, option processing stops at the
  75. * first non-option. If the first character is '-', each non-option argv
  76. * element is handled as if it were the argument of an option with
  77. * character code 1. mode holds this character.
  78. *
  79. * After the optional leading '+' and '-', optstring may contain ':'. If
  80. * present, missing arguments return ':' instead of '?'. colon_mode holds
  81. * this setting.
  82. */
  83. if (getenv("POSIXLY_CORRECT") != NULL) {
  84. mode = '+';
  85. colon_mode = '+';
  86. } else {
  87. if (optstring[offset] == '+' || optstring[offset] == '-') {
  88. mode = optstring[offset];
  89. offset++;
  90. }
  91. if (optstring[offset] == ':') {
  92. colon_mode = 1;
  93. offset++;
  94. }
  95. }
  96. /*
  97. * charind holds where we left off. If it's set, we were in the middle
  98. * of an argv element; if not, we pick up with the next element of
  99. * optind.
  100. */
  101. optarg = NULL;
  102. if (charind == 0) {
  103. if (optind >= argc)
  104. option = -1;
  105. else if (strcmp(argv[optind], "--") == 0) {
  106. optind++;
  107. option = -1;
  108. } else if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
  109. char *tmp;
  110. int i, j, k, end;
  111. if (mode == '+')
  112. option = -1;
  113. else if (mode == '-') {
  114. optarg = argv[optind];
  115. optind++;
  116. option = 1;
  117. } else {
  118. for (i = optind + 1, j = optind; i < argc; i++)
  119. if ((argv[i][0] == '-') && (argv[i][1] != '\0')) {
  120. optind = i;
  121. option = getopt(argc, argv, optstring);
  122. while (i > j) {
  123. --i;
  124. tmp = argv[i];
  125. end = (charind == 0) ? optind - 1 : optind;
  126. for (k = i; k + 1 <= end; k++) {
  127. argv[k] = argv[k + 1];
  128. }
  129. argv[end] = tmp;
  130. --optind;
  131. }
  132. break;
  133. }
  134. if (i == argc)
  135. option = -1;
  136. }
  137. return option;
  138. } else {
  139. charind = 1;
  140. }
  141. }
  142. if (charind != 0) {
  143. optopt = argv[optind][charind];
  144. for (p = optstring + offset; *p != '\0'; p++)
  145. if (optopt == *p) {
  146. p++;
  147. if (*p == ':') {
  148. if (argv[optind][charind + 1] != '\0') {
  149. optarg = &argv[optind][charind + 1];
  150. optind++;
  151. charind = 0;
  152. } else {
  153. p++;
  154. if (*p != ':') {
  155. charind = 0;
  156. optind++;
  157. if (optind >= argc) {
  158. if (opterr)
  159. fprintf(stderr, "%s: option requires"
  160. " an argument -- %c\n", argv[0],
  161. optopt);
  162. option = colon_mode ? ':' : '?';
  163. goto done;
  164. } else {
  165. optarg = argv[optind];
  166. optind++;
  167. }
  168. }
  169. }
  170. }
  171. option = optopt;
  172. }
  173. if (option == -1) {
  174. if (opterr)
  175. fprintf(stderr, "%s: illegal option -- %c\n", argv[0], optopt);
  176. option = '?';
  177. }
  178. }
  179. done:
  180. if (charind != 0) {
  181. charind++;
  182. if (argv[optind][charind] == '\0') {
  183. optind++;
  184. charind = 0;
  185. }
  186. }
  187. if (optind > argc)
  188. optind = argc;
  189. return option;
  190. }