getopt_long.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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 "file.h"
  31. #ifndef lint
  32. FILE_RCSID("@(#)$File: getopt_long.c,v 1.7 2018/09/09 20:33:28 christos Exp $")
  33. #endif /* lint */
  34. #include <assert.h>
  35. #ifdef HAVE_ERR_H
  36. #include <err.h>
  37. #else
  38. #define warnx printf
  39. #endif
  40. #include <errno.h>
  41. #if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
  42. #include <getopt.h>
  43. #else
  44. #include "mygetopt.h"
  45. #endif
  46. #include <stdlib.h>
  47. #include <string.h>
  48. #define REPLACE_GETOPT
  49. #ifndef _DIAGASSERT
  50. #define _DIAGASSERT assert
  51. #endif
  52. #ifdef REPLACE_GETOPT
  53. #ifdef __weak_alias
  54. __weak_alias(getopt,_getopt)
  55. #endif
  56. int opterr = 1; /* if error message should be printed */
  57. int optind = 1; /* index into parent argv vector */
  58. int optopt = '?'; /* character checked for validity */
  59. int optreset; /* reset getopt */
  60. char *optarg; /* argument associated with option */
  61. #elif HAVE_NBTOOL_CONFIG_H && !HAVE_DECL_OPTRESET
  62. static int optreset;
  63. #endif
  64. #ifdef __weak_alias
  65. __weak_alias(getopt_long,_getopt_long)
  66. #endif
  67. #define IGNORE_FIRST (*options == '-' || *options == '+')
  68. #define PRINT_ERROR ((opterr) && ((*options != ':') \
  69. || (IGNORE_FIRST && options[1] != ':')))
  70. #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
  71. #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
  72. /* XXX: GNU ignores PC if *options == '-' */
  73. #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
  74. /* return values */
  75. #define BADCH (int)'?'
  76. #define BADARG ((IGNORE_FIRST && options[1] == ':') \
  77. || (*options == ':') ? (int)':' : (int)'?')
  78. #define INORDER (int)1
  79. #define EMSG ""
  80. static int getopt_internal(int, char **, const char *);
  81. static int gcd(int, int);
  82. static void permute_args(int, int, int, char **);
  83. static const char *place = EMSG; /* option letter processing */
  84. /* XXX: set optreset to 1 rather than these two */
  85. static int nonopt_start = -1; /* first non option argument (for permute) */
  86. static int nonopt_end = -1; /* first option after non options (for permute) */
  87. /* Error messages */
  88. static const char recargchar[] = "option requires an argument -- %c";
  89. static const char recargstring[] = "option requires an argument -- %s";
  90. static const char ambig[] = "ambiguous option -- %.*s";
  91. static const char noarg[] = "option doesn't take an argument -- %.*s";
  92. static const char illoptchar[] = "unknown option -- %c";
  93. static const char illoptstring[] = "unknown option -- %s";
  94. /*
  95. * Compute the greatest common divisor of a and b.
  96. */
  97. static int
  98. gcd(a, b)
  99. int a;
  100. int b;
  101. {
  102. int c;
  103. c = a % b;
  104. while (c != 0) {
  105. a = b;
  106. b = c;
  107. c = a % b;
  108. }
  109. return b;
  110. }
  111. /*
  112. * Exchange the block from nonopt_start to nonopt_end with the block
  113. * from nonopt_end to opt_end (keeping the same order of arguments
  114. * in each block).
  115. */
  116. static void
  117. permute_args(panonopt_start, panonopt_end, opt_end, nargv)
  118. int panonopt_start;
  119. int panonopt_end;
  120. int opt_end;
  121. char **nargv;
  122. {
  123. int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
  124. char *swap;
  125. _DIAGASSERT(nargv != NULL);
  126. /*
  127. * compute lengths of blocks and number and size of cycles
  128. */
  129. nnonopts = panonopt_end - panonopt_start;
  130. nopts = opt_end - panonopt_end;
  131. ncycle = gcd(nnonopts, nopts);
  132. cyclelen = (opt_end - panonopt_start) / ncycle;
  133. for (i = 0; i < ncycle; i++) {
  134. cstart = panonopt_end+i;
  135. pos = cstart;
  136. for (j = 0; j < cyclelen; j++) {
  137. if (pos >= panonopt_end)
  138. pos -= nnonopts;
  139. else
  140. pos += nopts;
  141. swap = nargv[pos];
  142. nargv[pos] = nargv[cstart];
  143. nargv[cstart] = swap;
  144. }
  145. }
  146. }
  147. /*
  148. * getopt_internal --
  149. * Parse argc/argv argument vector. Called by user level routines.
  150. * Returns -2 if -- is found (can be long option or end of options marker).
  151. */
  152. static int
  153. getopt_internal(nargc, nargv, options)
  154. int nargc;
  155. char **nargv;
  156. const char *options;
  157. {
  158. char *oli; /* option letter list index */
  159. int optchar;
  160. _DIAGASSERT(nargv != NULL);
  161. _DIAGASSERT(options != NULL);
  162. optarg = NULL;
  163. /*
  164. * XXX Some programs (like rsyncd) expect to be able to
  165. * XXX re-initialize optind to 0 and have getopt_long(3)
  166. * XXX properly function again. Work around this braindamage.
  167. */
  168. if (optind == 0)
  169. optind = 1;
  170. if (optreset)
  171. nonopt_start = nonopt_end = -1;
  172. start:
  173. if (optreset || !*place) { /* update scanning pointer */
  174. optreset = 0;
  175. if (optind >= nargc) { /* end of argument vector */
  176. place = EMSG;
  177. if (nonopt_end != -1) {
  178. /* do permutation, if we have to */
  179. permute_args(nonopt_start, nonopt_end,
  180. optind, nargv);
  181. optind -= nonopt_end - nonopt_start;
  182. }
  183. else if (nonopt_start != -1) {
  184. /*
  185. * If we skipped non-options, set optind
  186. * to the first of them.
  187. */
  188. optind = nonopt_start;
  189. }
  190. nonopt_start = nonopt_end = -1;
  191. return -1;
  192. }
  193. if ((*(place = nargv[optind]) != '-')
  194. || (place[1] == '\0')) { /* found non-option */
  195. place = EMSG;
  196. if (IN_ORDER) {
  197. /*
  198. * GNU extension:
  199. * return non-option as argument to option 1
  200. */
  201. optarg = nargv[optind++];
  202. return INORDER;
  203. }
  204. if (!PERMUTE) {
  205. /*
  206. * if no permutation wanted, stop parsing
  207. * at first non-option
  208. */
  209. return -1;
  210. }
  211. /* do permutation */
  212. if (nonopt_start == -1)
  213. nonopt_start = optind;
  214. else if (nonopt_end != -1) {
  215. permute_args(nonopt_start, nonopt_end,
  216. optind, nargv);
  217. nonopt_start = optind -
  218. (nonopt_end - nonopt_start);
  219. nonopt_end = -1;
  220. }
  221. optind++;
  222. /* process next argument */
  223. goto start;
  224. }
  225. if (nonopt_start != -1 && nonopt_end == -1)
  226. nonopt_end = optind;
  227. if (place[1] && *++place == '-') { /* found "--" */
  228. place++;
  229. return -2;
  230. }
  231. }
  232. if ((optchar = (int)*place++) == (int)':' ||
  233. (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
  234. /* option letter unknown or ':' */
  235. if (!*place)
  236. ++optind;
  237. if (PRINT_ERROR)
  238. warnx(illoptchar, optchar);
  239. optopt = optchar;
  240. return BADCH;
  241. }
  242. if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
  243. /* XXX: what if no long options provided (called by getopt)? */
  244. if (*place)
  245. return -2;
  246. if (++optind >= nargc) { /* no arg */
  247. place = EMSG;
  248. if (PRINT_ERROR)
  249. warnx(recargchar, optchar);
  250. optopt = optchar;
  251. return BADARG;
  252. } else /* white space */
  253. place = nargv[optind];
  254. /*
  255. * Handle -W arg the same as --arg (which causes getopt to
  256. * stop parsing).
  257. */
  258. return -2;
  259. }
  260. if (*++oli != ':') { /* doesn't take argument */
  261. if (!*place)
  262. ++optind;
  263. } else { /* takes (optional) argument */
  264. optarg = NULL;
  265. if (*place) /* no white space */
  266. optarg = (char *)place;
  267. /* XXX: disable test for :: if PC? (GNU doesn't) */
  268. else if (oli[1] != ':') { /* arg not optional */
  269. if (++optind >= nargc) { /* no arg */
  270. place = EMSG;
  271. if (PRINT_ERROR)
  272. warnx(recargchar, optchar);
  273. optopt = optchar;
  274. return BADARG;
  275. } else
  276. optarg = nargv[optind];
  277. }
  278. place = EMSG;
  279. ++optind;
  280. }
  281. /* dump back option letter */
  282. return optchar;
  283. }
  284. #ifdef REPLACE_GETOPT
  285. /*
  286. * getopt --
  287. * Parse argc/argv argument vector.
  288. *
  289. * [eventually this will replace the real getopt]
  290. */
  291. int
  292. getopt(nargc, nargv, options)
  293. int nargc;
  294. char * const *nargv;
  295. const char *options;
  296. {
  297. int retval;
  298. _DIAGASSERT(nargv != NULL);
  299. _DIAGASSERT(options != NULL);
  300. retval = getopt_internal(nargc, (char **)nargv, options);
  301. if (retval == -2) {
  302. ++optind;
  303. /*
  304. * We found an option (--), so if we skipped non-options,
  305. * we have to permute.
  306. */
  307. if (nonopt_end != -1) {
  308. permute_args(nonopt_start, nonopt_end, optind,
  309. (char **)nargv);
  310. optind -= nonopt_end - nonopt_start;
  311. }
  312. nonopt_start = nonopt_end = -1;
  313. retval = -1;
  314. }
  315. return retval;
  316. }
  317. #endif
  318. /*
  319. * getopt_long --
  320. * Parse argc/argv argument vector.
  321. */
  322. int
  323. getopt_long(nargc, nargv, options, long_options, idx)
  324. int nargc;
  325. char * const *nargv;
  326. const char *options;
  327. const struct option *long_options;
  328. int *idx;
  329. {
  330. int retval;
  331. #define IDENTICAL_INTERPRETATION(_x, _y) \
  332. (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
  333. long_options[(_x)].flag == long_options[(_y)].flag && \
  334. long_options[(_x)].val == long_options[(_y)].val)
  335. _DIAGASSERT(nargv != NULL);
  336. _DIAGASSERT(options != NULL);
  337. _DIAGASSERT(long_options != NULL);
  338. /* idx may be NULL */
  339. retval = getopt_internal(nargc, (char **)nargv, options);
  340. if (retval == -2) {
  341. char *current_argv, *has_equal;
  342. size_t current_argv_len;
  343. int i, ambiguous, match;
  344. current_argv = (char *)place;
  345. match = -1;
  346. ambiguous = 0;
  347. optind++;
  348. place = EMSG;
  349. if (*current_argv == '\0') { /* found "--" */
  350. /*
  351. * We found an option (--), so if we skipped
  352. * non-options, we have to permute.
  353. */
  354. if (nonopt_end != -1) {
  355. permute_args(nonopt_start, nonopt_end,
  356. optind, (char **)nargv);
  357. optind -= nonopt_end - nonopt_start;
  358. }
  359. nonopt_start = nonopt_end = -1;
  360. return -1;
  361. }
  362. if ((has_equal = strchr(current_argv, '=')) != NULL) {
  363. /* argument found (--option=arg) */
  364. current_argv_len = has_equal - current_argv;
  365. has_equal++;
  366. } else
  367. current_argv_len = strlen(current_argv);
  368. for (i = 0; long_options[i].name; i++) {
  369. /* find matching long option */
  370. if (strncmp(current_argv, long_options[i].name,
  371. current_argv_len))
  372. continue;
  373. if (strlen(long_options[i].name) ==
  374. (unsigned)current_argv_len) {
  375. /* exact match */
  376. match = i;
  377. ambiguous = 0;
  378. break;
  379. }
  380. if (match == -1) /* partial match */
  381. match = i;
  382. else if (!IDENTICAL_INTERPRETATION(i, match))
  383. ambiguous = 1;
  384. }
  385. if (ambiguous) {
  386. /* ambiguous abbreviation */
  387. if (PRINT_ERROR)
  388. warnx(ambig, (int)current_argv_len,
  389. current_argv);
  390. optopt = 0;
  391. return BADCH;
  392. }
  393. if (match != -1) { /* option found */
  394. if (long_options[match].has_arg == no_argument
  395. && has_equal) {
  396. if (PRINT_ERROR)
  397. warnx(noarg, (int)current_argv_len,
  398. current_argv);
  399. /*
  400. * XXX: GNU sets optopt to val regardless of
  401. * flag
  402. */
  403. if (long_options[match].flag == NULL)
  404. optopt = long_options[match].val;
  405. else
  406. optopt = 0;
  407. return BADARG;
  408. }
  409. if (long_options[match].has_arg == required_argument ||
  410. long_options[match].has_arg == optional_argument) {
  411. if (has_equal)
  412. optarg = has_equal;
  413. else if (long_options[match].has_arg ==
  414. required_argument) {
  415. /*
  416. * optional argument doesn't use
  417. * next nargv
  418. */
  419. optarg = nargv[optind++];
  420. }
  421. }
  422. if ((long_options[match].has_arg == required_argument)
  423. && (optarg == NULL)) {
  424. /*
  425. * Missing argument; leading ':'
  426. * indicates no error should be generated
  427. */
  428. if (PRINT_ERROR)
  429. warnx(recargstring, current_argv);
  430. /*
  431. * XXX: GNU sets optopt to val regardless
  432. * of flag
  433. */
  434. if (long_options[match].flag == NULL)
  435. optopt = long_options[match].val;
  436. else
  437. optopt = 0;
  438. --optind;
  439. return BADARG;
  440. }
  441. } else { /* unknown option */
  442. if (PRINT_ERROR)
  443. warnx(illoptstring, current_argv);
  444. optopt = 0;
  445. return BADCH;
  446. }
  447. if (long_options[match].flag) {
  448. *long_options[match].flag = long_options[match].val;
  449. retval = 0;
  450. } else
  451. retval = long_options[match].val;
  452. if (idx)
  453. *idx = match;
  454. }
  455. return retval;
  456. #undef IDENTICAL_INTERPRETATION
  457. }