aboutsummaryrefslogtreecommitdiff
path: root/src/getopt.c
blob: c0aa182fdc15c9822869e8080e167537d0ddec0b (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 <stdlib.h>
  33. #include <stdio.h>
  34. #include <string.h>
  35. /*
  36. * If we're running the test suite, rename getopt and the global variables to
  37. * avoid conflicts with the system version.
  38. */
  39. #if TESTING
  40. # define getopt test_getopt
  41. int test_getopt(int, char **, const char *);
  42. # define optind test_optind
  43. # define opterr test_opterr
  44. # define optopt test_optopt
  45. # define optarg test_optarg
  46. #endif
  47. /* Initialize global interface variables. */
  48. int optind = 1;
  49. int opterr = 1;
  50. int optopt = 0;
  51. char *optarg = NULL;
  52. /*
  53. * This is the plain old UNIX getopt, with GNU-style extensions. If you're
  54. * porting some piece of UNIX software, this is all you need. It supports
  55. * GNU-style permutation and optional arguments, but does not support the GNU
  56. * -W extension.
  57. *
  58. * This function is not re-entrant or thread-safe, has static variables, and
  59. * generally isn't a great interface, but normally you only call it once.
  60. */
  61. int
  62. getopt(int argc, char *argv[], const char *optstring)
  63. {
  64. const char *p;
  65. size_t offset = 0;
  66. char mode = '\0';
  67. int colon_mode = 0;
  68. int option = -1;
  69. /* Holds the current position in the parameter being parsed. */
  70. static int charind = 0;
  71. /*
  72. * By default, getopt permutes argv as it scans and leaves all non-options
  73. * at the end. This can be changed with the first character of optstring
  74. * or the environment variable POSIXLY_CORRECT. With a first character of
  75. * '+' or when POSIXLY_CORRECT is set, option processing stops at the
  76. * first non-option. If the first character is '-', each non-option argv
  77. * element is handled as if it were the argument of an option with
  78. * character code 1. mode holds this character.
  79. *
  80. * After the optional leading '+' and '-', optstring may contain ':'. If
  81. * present, missing arguments return ':' instead of '?'. colon_mode holds
  82. * this setting.
  83. */
  84. if (getenv("POSIXLY_CORRECT") != NULL) {
  85. mode = '+';
  86. colon_mode = '+';
  87. } else {
  88. if (optstring[offset] == '+' || optstring[offset] == '-') {
  89. mode = optstring[offset];
  90. offset++;
  91. }
  92. if (optstring[offset] == ':') {
  93. colon_mode = 1;
  94. offset++;
  95. }
  96. }
  97. /*
  98. * charind holds where we left off. If it's set, we were in the middle
  99. * of an argv element; if not, we pick up with the next element of
  100. * optind.
  101. */
  102. optarg = NULL;
  103. if (charind == 0) {
  104. if (optind >= argc)
  105. option = -1;
  106. else if (strcmp(argv[optind], "--") == 0) {
  107. optind++;
  108. option = -1;
  109. } else if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
  110. char *tmp;
  111. int i, j, k, end;
  112. if (mode == '+')
  113. option = -1;
  114. else if (mode == '-') {
  115. optarg = argv[optind];
  116. optind++;
  117. option = 1;
  118. } else {
  119. for (i = optind + 1, j = optind; i < argc; i++)
  120. if ((argv[i][0] == '-') && (argv[i][1] != '\0')) {
  121. optind = i;
  122. option = getopt(argc, argv, optstring);
  123. while (i > j) {
  124. --i;
  125. tmp = argv[i];
  126. end = (charind == 0) ? optind - 1 : optind;
  127. for (k = i; k + 1 <= end; k++) {
  128. argv[k] = argv[k + 1];
  129. }
  130. argv[end] = tmp;
  131. --optind;
  132. }
  133. break;
  134. }
  135. if (i == argc)
  136. option = -1;
  137. }
  138. return option;
  139. } else {
  140. charind = 1;
  141. }
  142. }
  143. if (charind != 0) {
  144. optopt = argv[optind][charind];
  145. for (p = optstring + offset; *p != '\0'; p++)
  146. if (optopt == *p) {
  147. p++;
  148. if (*p == ':') {
  149. if (argv[optind][charind + 1] != '\0') {
  150. optarg = &argv[optind][charind + 1];
  151. optind++;
  152. charind = 0;
  153. } else {
  154. p++;
  155. if (*p != ':') {
  156. charind = 0;
  157. optind++;
  158. if (optind >= argc) {
  159. if (opterr)
  160. fprintf(stderr, "%s: option requires"
  161. " an argument -- %c\n", argv[0],
  162. optopt);
  163. option = colon_mode ? ':' : '?';
  164. goto done;
  165. } else {
  166. optarg = argv[optind];
  167. optind++;
  168. }
  169. }
  170. }
  171. }
  172. option = optopt;
  173. }
  174. if (option == -1) {
  175. if (opterr)
  176. fprintf(stderr, "%s: illegal option -- %c\n", argv[0], optopt);
  177. option = '?';
  178. }
  179. }
  180. done:
  181. if (charind != 0) {
  182. charind++;
  183. if (argv[optind][charind] == '\0') {
  184. optind++;
  185. charind = 0;
  186. }
  187. }
  188. if (optind > argc)
  189. optind = argc;
  190. return option;
  191. }