getopt_long.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /* $NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $ */
  2. /*-
  3. * Copyright (c) 2000 The NetBSD Foundation, Inc.
  4. * All rights reserved.
  5. *
  6. * This code is derived from software contributed to The NetBSD Foundation
  7. * by Dieter Baron and Thomas Klausner.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  19. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  20. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  21. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  22. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. * POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. #include <assert.h>
  31. #include <err.h>
  32. #include <errno.h>
  33. #include <getopt.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #define REPLACE_GETOPT
  37. #define _DIAGASSERT assert
  38. #ifdef REPLACE_GETOPT
  39. #ifdef __weak_alias
  40. __weak_alias(getopt,_getopt)
  41. #endif
  42. int opterr = 1; /* if error message should be printed */
  43. int optind = 1; /* index into parent argv vector */
  44. int optopt = '?'; /* character checked for validity */
  45. int optreset; /* reset getopt */
  46. char *optarg; /* argument associated with option */
  47. #elif HAVE_NBTOOL_CONFIG_H && !HAVE_DECL_OPTRESET
  48. static int optreset;
  49. #endif
  50. #ifdef __weak_alias
  51. __weak_alias(getopt_long,_getopt_long)
  52. #endif
  53. #define IGNORE_FIRST (*options == '-' || *options == '+')
  54. #define PRINT_ERROR ((opterr) && ((*options != ':') \
  55. || (IGNORE_FIRST && options[1] != ':')))
  56. #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
  57. #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
  58. /* XXX: GNU ignores PC if *options == '-' */
  59. #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
  60. /* return values */
  61. #define BADCH (int)'?'
  62. #define BADARG ((IGNORE_FIRST && options[1] == ':') \
  63. || (*options == ':') ? (int)':' : (int)'?')
  64. #define INORDER (int)1
  65. #define EMSG ""
  66. static int getopt_internal __P((int, char **, const char *));
  67. static int gcd __P((int, int));
  68. static void permute_args __P((int, int, int, char **));
  69. static const char *place = EMSG; /* option letter processing */
  70. /* XXX: set optreset to 1 rather than these two */
  71. static int nonopt_start = -1; /* first non option argument (for permute) */
  72. static int nonopt_end = -1; /* first option after non options (for permute) */
  73. /* Error messages */
  74. static const char recargchar[] = "option requires an argument -- %c";
  75. static const char recargstring[] = "option requires an argument -- %s";
  76. static const char ambig[] = "ambiguous option -- %.*s";
  77. static const char noarg[] = "option doesn't take an argument -- %.*s";
  78. static const char illoptchar[] = "unknown option -- %c";
  79. static const char illoptstring[] = "unknown option -- %s";
  80. /*
  81. * Compute the greatest common divisor of a and b.
  82. */
  83. static int
  84. gcd(a, b)
  85. int a;
  86. int b;
  87. {
  88. int c;
  89. c = a % b;
  90. while (c != 0) {
  91. a = b;
  92. b = c;
  93. c = a % b;
  94. }
  95. return b;
  96. }
  97. /*
  98. * Exchange the block from nonopt_start to nonopt_end with the block
  99. * from nonopt_end to opt_end (keeping the same order of arguments
  100. * in each block).
  101. */
  102. static void
  103. permute_args(panonopt_start, panonopt_end, opt_end, nargv)
  104. int panonopt_start;
  105. int panonopt_end;
  106. int opt_end;
  107. char **nargv;
  108. {
  109. int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
  110. char *swap;
  111. _DIAGASSERT(nargv != NULL);
  112. /*
  113. * compute lengths of blocks and number and size of cycles
  114. */
  115. nnonopts = panonopt_end - panonopt_start;
  116. nopts = opt_end - panonopt_end;
  117. ncycle = gcd(nnonopts, nopts);
  118. cyclelen = (opt_end - panonopt_start) / ncycle;
  119. for (i = 0; i < ncycle; i++) {
  120. cstart = panonopt_end+i;
  121. pos = cstart;
  122. for (j = 0; j < cyclelen; j++) {
  123. if (pos >= panonopt_end)
  124. pos -= nnonopts;
  125. else
  126. pos += nopts;
  127. swap = nargv[pos];
  128. nargv[pos] = nargv[cstart];
  129. nargv[cstart] = swap;
  130. }
  131. }
  132. }
  133. /*
  134. * getopt_internal --
  135. * Parse argc/argv argument vector. Called by user level routines.
  136. * Returns -2 if -- is found (can be long option or end of options marker).
  137. */
  138. static int
  139. getopt_internal(nargc, nargv, options)
  140. int nargc;
  141. char **nargv;
  142. const char *options;
  143. {
  144. char *oli; /* option letter list index */
  145. int optchar;
  146. _DIAGASSERT(nargv != NULL);
  147. _DIAGASSERT(options != NULL);
  148. optarg = NULL;
  149. /*
  150. * XXX Some programs (like rsyncd) expect to be able to
  151. * XXX re-initialize optind to 0 and have getopt_long(3)
  152. * XXX properly function again. Work around this braindamage.
  153. */
  154. if (optind == 0)
  155. optind = 1;
  156. if (optreset)
  157. nonopt_start = nonopt_end = -1;
  158. start:
  159. if (optreset || !*place) { /* update scanning pointer */
  160. optreset = 0;
  161. if (optind >= nargc) { /* end of argument vector */
  162. place = EMSG;
  163. if (nonopt_end != -1) {
  164. /* do permutation, if we have to */
  165. permute_args(nonopt_start, nonopt_end,
  166. optind, nargv);
  167. optind -= nonopt_end - nonopt_start;
  168. }
  169. else if (nonopt_start != -1) {
  170. /*
  171. * If we skipped non-options, set optind
  172. * to the first of them.
  173. */
  174. optind = nonopt_start;
  175. }
  176. nonopt_start = nonopt_end = -1;
  177. return -1;
  178. }
  179. if ((*(place = nargv[optind]) != '-')
  180. || (place[1] == '\0')) { /* found non-option */
  181. place = EMSG;
  182. if (IN_ORDER) {
  183. /*
  184. * GNU extension:
  185. * return non-option as argument to option 1
  186. */
  187. optarg = nargv[optind++];
  188. return INORDER;
  189. }
  190. if (!PERMUTE) {
  191. /*
  192. * if no permutation wanted, stop parsing
  193. * at first non-option
  194. */
  195. return -1;
  196. }
  197. /* do permutation */
  198. if (nonopt_start == -1)
  199. nonopt_start = optind;
  200. else if (nonopt_end != -1) {
  201. permute_args(nonopt_start, nonopt_end,
  202. optind, nargv);
  203. nonopt_start = optind -
  204. (nonopt_end - nonopt_start);
  205. nonopt_end = -1;
  206. }
  207. optind++;
  208. /* process next argument */
  209. goto start;
  210. }
  211. if (nonopt_start != -1 && nonopt_end == -1)
  212. nonopt_end = optind;
  213. if (place[1] && *++place == '-') { /* found "--" */
  214. place++;
  215. return -2;
  216. }
  217. }
  218. if ((optchar = (int)*place++) == (int)':' ||
  219. (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
  220. /* option letter unknown or ':' */
  221. if (!*place)
  222. ++optind;
  223. if (PRINT_ERROR)
  224. warnx(illoptchar, optchar);
  225. optopt = optchar;
  226. return BADCH;
  227. }
  228. if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
  229. /* XXX: what if no long options provided (called by getopt)? */
  230. if (*place)
  231. return -2;
  232. if (++optind >= nargc) { /* no arg */
  233. place = EMSG;
  234. if (PRINT_ERROR)
  235. warnx(recargchar, optchar);
  236. optopt = optchar;
  237. return BADARG;
  238. } else /* white space */
  239. place = nargv[optind];
  240. /*
  241. * Handle -W arg the same as --arg (which causes getopt to
  242. * stop parsing).
  243. */
  244. return -2;
  245. }
  246. if (*++oli != ':') { /* doesn't take argument */
  247. if (!*place)
  248. ++optind;
  249. } else { /* takes (optional) argument */
  250. optarg = NULL;
  251. if (*place) /* no white space */
  252. optarg = (char *)place;
  253. /* XXX: disable test for :: if PC? (GNU doesn't) */
  254. else if (oli[1] != ':') { /* arg not optional */
  255. if (++optind >= nargc) { /* no arg */
  256. place = EMSG;
  257. if (PRINT_ERROR)
  258. warnx(recargchar, optchar);
  259. optopt = optchar;
  260. return BADARG;
  261. } else
  262. optarg = nargv[optind];
  263. }
  264. place = EMSG;
  265. ++optind;
  266. }
  267. /* dump back option letter */
  268. return optchar;
  269. }
  270. #ifdef REPLACE_GETOPT
  271. /*
  272. * getopt --
  273. * Parse argc/argv argument vector.
  274. *
  275. * [eventually this will replace the real getopt]
  276. */
  277. int
  278. getopt(nargc, nargv, options)
  279. int nargc;
  280. char * const *nargv;
  281. const char *options;
  282. {
  283. int retval;
  284. _DIAGASSERT(nargv != NULL);
  285. _DIAGASSERT(options != NULL);
  286. retval = getopt_internal(nargc, (char **)nargv, options);
  287. if (retval == -2) {
  288. ++optind;
  289. /*
  290. * We found an option (--), so if we skipped non-options,
  291. * we have to permute.
  292. */
  293. if (nonopt_end != -1) {
  294. permute_args(nonopt_start, nonopt_end, optind,
  295. (char **)nargv);
  296. optind -= nonopt_end - nonopt_start;
  297. }
  298. nonopt_start = nonopt_end = -1;
  299. retval = -1;
  300. }
  301. return retval;
  302. }
  303. #endif
  304. /*
  305. * getopt_long --
  306. * Parse argc/argv argument vector.
  307. */
  308. int
  309. getopt_long(nargc, nargv, options, long_options, idx)
  310. int nargc;
  311. char * const *nargv;
  312. const char *options;
  313. const struct option *long_options;
  314. int *idx;
  315. {
  316. int retval;
  317. #define IDENTICAL_INTERPRETATION(_x, _y) \
  318. (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
  319. long_options[(_x)].flag == long_options[(_y)].flag && \
  320. long_options[(_x)].val == long_options[(_y)].val)
  321. _DIAGASSERT(nargv != NULL);
  322. _DIAGASSERT(options != NULL);
  323. _DIAGASSERT(long_options != NULL);
  324. /* idx may be NULL */
  325. retval = getopt_internal(nargc, (char **)nargv, options);
  326. if (retval == -2) {
  327. char *current_argv, *has_equal;
  328. size_t current_argv_len;
  329. int i, ambiguous, match;
  330. current_argv = (char *)place;
  331. match = -1;
  332. ambiguous = 0;
  333. optind++;
  334. place = EMSG;
  335. if (*current_argv == '\0') { /* found "--" */
  336. /*
  337. * We found an option (--), so if we skipped
  338. * non-options, we have to permute.
  339. */
  340. if (nonopt_end != -1) {
  341. permute_args(nonopt_start, nonopt_end,
  342. optind, (char **)nargv);
  343. optind -= nonopt_end - nonopt_start;
  344. }
  345. nonopt_start = nonopt_end = -1;
  346. return -1;
  347. }
  348. if ((has_equal = strchr(current_argv, '=')) != NULL) {
  349. /* argument found (--option=arg) */
  350. current_argv_len = has_equal - current_argv;
  351. has_equal++;
  352. } else
  353. current_argv_len = strlen(current_argv);
  354. for (i = 0; long_options[i].name; i++) {
  355. /* find matching long option */
  356. if (strncmp(current_argv, long_options[i].name,
  357. current_argv_len))
  358. continue;
  359. if (strlen(long_options[i].name) ==
  360. (unsigned)current_argv_len) {
  361. /* exact match */
  362. match = i;
  363. ambiguous = 0;
  364. break;
  365. }
  366. if (match == -1) /* partial match */
  367. match = i;
  368. else if (!IDENTICAL_INTERPRETATION(i, match))
  369. ambiguous = 1;
  370. }
  371. if (ambiguous) {
  372. /* ambiguous abbreviation */
  373. if (PRINT_ERROR)
  374. warnx(ambig, (int)current_argv_len,
  375. current_argv);
  376. optopt = 0;
  377. return BADCH;
  378. }
  379. if (match != -1) { /* option found */
  380. if (long_options[match].has_arg == no_argument
  381. && has_equal) {
  382. if (PRINT_ERROR)
  383. warnx(noarg, (int)current_argv_len,
  384. current_argv);
  385. /*
  386. * XXX: GNU sets optopt to val regardless of
  387. * flag
  388. */
  389. if (long_options[match].flag == NULL)
  390. optopt = long_options[match].val;
  391. else
  392. optopt = 0;
  393. return BADARG;
  394. }
  395. if (long_options[match].has_arg == required_argument ||
  396. long_options[match].has_arg == optional_argument) {
  397. if (has_equal)
  398. optarg = has_equal;
  399. else if (long_options[match].has_arg ==
  400. required_argument) {
  401. /*
  402. * optional argument doesn't use
  403. * next nargv
  404. */
  405. optarg = nargv[optind++];
  406. }
  407. }
  408. if ((long_options[match].has_arg == required_argument)
  409. && (optarg == NULL)) {
  410. /*
  411. * Missing argument; leading ':'
  412. * indicates no error should be generated
  413. */
  414. if (PRINT_ERROR)
  415. warnx(recargstring, current_argv);
  416. /*
  417. * XXX: GNU sets optopt to val regardless
  418. * of flag
  419. */
  420. if (long_options[match].flag == NULL)
  421. optopt = long_options[match].val;
  422. else
  423. optopt = 0;
  424. --optind;
  425. return BADARG;
  426. }
  427. } else { /* unknown option */
  428. if (PRINT_ERROR)
  429. warnx(illoptstring, current_argv);
  430. optopt = 0;
  431. return BADCH;
  432. }
  433. if (long_options[match].flag) {
  434. *long_options[match].flag = long_options[match].val;
  435. retval = 0;
  436. } else
  437. retval = long_options[match].val;
  438. if (idx)
  439. *idx = match;
  440. }
  441. return retval;
  442. #undef IDENTICAL_INTERPRETATION
  443. }