Use pkgsrc packages from a custom location.
[dragonfly.git] / lib / libc / stdlib / getopt_long.c
blob68e6b1484b4cb8513e762d388ec99b30ed265962
1 /* $NetBSD: getopt_long.c,v 1.16 2003/10/27 00:12:42 lukem Exp $ */
2 /* $DragonFly: src/lib/libc/stdlib/getopt_long.c,v 1.14 2005/11/20 12:37:48 swildner Exp $ */
4 /*-
5 * Copyright (c) 2000 The NetBSD Foundation, Inc.
6 * All rights reserved.
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Dieter Baron and Thomas Klausner.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
40 #include <sys/cdefs.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <getopt.h>
45 #include <stdlib.h>
46 #include <string.h>
48 /* XXX BOOTSTRAPPING */
49 #ifndef __DECONST
50 #define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
51 #endif
53 #ifdef REPLACE_GETOPT
54 int opterr = 1; /* if error message should be printed */
55 int optind = 1; /* index into parent argv vector */
56 int optopt = '?'; /* character checked for validity */
57 int optreset; /* reset getopt */
58 char *optarg; /* argument associated with option */
59 #endif
61 #define IGNORE_FIRST (*options == '-' || *options == '+')
62 #define PRINT_ERROR ((opterr) && ((*options != ':') \
63 || (IGNORE_FIRST && options[1] != ':')))
64 #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
65 #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
66 /* XXX: GNU ignores PC if *options == '-' */
67 #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
69 /* return values */
70 #define BADCH (int)'?'
71 #define BADARG ((IGNORE_FIRST && options[1] == ':') \
72 || (*options == ':') ? (int)':' : (int)'?')
73 #define INORDER (int)1
75 static int getopt_internal(int, char * const *, const char *, int);
76 static int getopt_internal_short(int, char * const *, const char *, int);
77 static int getopt_long_internal(int, char * const *, const char *,
78 const struct option *, int *, int);
79 static int gcd(int, int);
80 static void permute_args(int, int, int, char * const *);
82 static char EMSG[] = {0};
83 static char *place = EMSG; /* option letter processing */
85 /* XXX: set optreset to 1 rather than these two */
86 static int nonopt_start = -1; /* first non option argument (for permute) */
87 static int nonopt_end = -1; /* first option after non options (for permute) */
89 /* Error messages */
90 static const char recargchar[] = "option requires an argument -- %c";
91 static const char recargstring[] = "option requires an argument -- %s";
92 static const char ambig[] = "ambiguous option -- %.*s";
93 static const char noarg[] = "option doesn't take an argument -- %.*s";
94 static const char illoptchar[] = "unknown option -- %c";
95 static const char illoptstring[] = "unknown option -- %s";
99 * Compute the greatest common divisor of a and b.
101 static int
102 gcd(int a, int b)
104 int c;
106 c = a % b;
107 while (c != 0) {
108 a = b;
109 b = c;
110 c = a % b;
113 return b;
117 * Exchange the block from nonopt_start to nonopt_end with the block
118 * from nonopt_end to opt_end (keeping the same order of arguments
119 * in each block).
121 static void
122 permute_args(int panonopt_start, int panonopt_end, int opt_end,
123 char * const *nargv)
125 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
126 char *swap;
129 * compute lengths of blocks and number and size of cycles
131 nnonopts = panonopt_end - panonopt_start;
132 nopts = opt_end - panonopt_end;
133 ncycle = gcd(nnonopts, nopts);
134 cyclelen = (opt_end - panonopt_start) / ncycle;
136 for (i = 0; i < ncycle; i++) {
137 cstart = panonopt_end+i;
138 pos = cstart;
139 for (j = 0; j < cyclelen; j++) {
140 if (pos >= panonopt_end)
141 pos -= nnonopts;
142 else
143 pos += nopts;
144 swap = nargv[pos];
145 /* LINTED const cast */
146 (__DECONST(char **, nargv))[pos] = nargv[cstart];
147 /* LINTED const cast */
148 (__DECONST(char **, nargv))[cstart] = swap;
154 * getopt_internal --
155 * Parse argc/argv argument vector. Called by user level routines.
156 * Returns -2 if -- is found (can be long option or end of options marker).
158 static int
159 getopt_internal(int nargc, char * const *nargv, const char *options,
160 int long_support)
162 optarg = NULL;
165 * XXX Some programs (like rsyncd) expect to be able to
166 * XXX re-initialize optind to 0 and have getopt_long(3)
167 * XXX properly function again. Work around this braindamage.
169 if (optind == 0)
170 optind = 1;
172 if (optreset)
173 nonopt_start = nonopt_end = -1;
174 start:
175 if (optreset || !*place) { /* update scanning pointer */
176 optreset = 0;
177 if (optind >= nargc) { /* end of argument vector */
178 place = EMSG;
179 if (nonopt_end != -1) {
180 /* do permutation, if we have to */
181 permute_args(nonopt_start, nonopt_end,
182 optind, nargv);
183 optind -= nonopt_end - nonopt_start;
185 else if (nonopt_start != -1) {
187 * If we skipped non-options, set optind
188 * to the first of them.
190 optind = nonopt_start;
192 nonopt_start = nonopt_end = -1;
193 return -1;
195 place = nargv[optind];
196 if ((*place == '-') && (place[1] == '\0') && long_support == 0)
197 return -1;
198 if ((*place != '-') ||
199 ((*place == '-') && (place[1] == '\0') && long_support != 0)) {
200 /* found non-option */
201 place = EMSG;
202 if (IN_ORDER) {
204 * GNU extension:
205 * return non-option as argument to option 1
207 optarg = nargv[optind++];
208 return INORDER;
210 if (!PERMUTE) {
212 * if no permutation wanted, stop parsing
213 * at first non-option
215 return -1;
217 /* do permutation */
218 if (nonopt_start == -1)
219 nonopt_start = optind;
220 else if (nonopt_end != -1) {
221 permute_args(nonopt_start, nonopt_end,
222 optind, nargv);
223 nonopt_start = optind -
224 (nonopt_end - nonopt_start);
225 nonopt_end = -1;
227 optind++;
228 /* process next argument */
229 goto start;
231 if (nonopt_start != -1 && nonopt_end == -1)
232 nonopt_end = optind;
233 if (place[1] && *++place == '-') { /* found "--" */
234 if (place[1] == '\0') {
235 ++optind;
237 * We found an option (--), so if we skipped
238 * non-options, we have to permute.
240 if (nonopt_end != -1) {
241 permute_args(nonopt_start, nonopt_end,
242 optind, nargv);
243 optind -= nonopt_end - nonopt_start;
245 nonopt_start = nonopt_end = -1;
246 return -1;
247 } else if (long_support) {
248 place++;
249 return -2;
253 if (long_support == 2 && (place[1] || strchr(options, *place) == NULL))
254 return -3;
255 return getopt_internal_short(nargc, nargv, options, long_support);
258 static int
259 getopt_internal_short(int nargc, char * const *nargv, const char *options,
260 int long_support)
262 const char *oli; /* option letter list index */
263 int optchar;
265 if ((optchar = (int)*place++) == (int)':' ||
266 (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
267 /* option letter unknown or ':' */
268 if (PRINT_ERROR) {
269 if (long_support == 2)
270 warnx(illoptstring, --place);
271 else
272 warnx(illoptchar, optchar);
274 if (long_support == 2)
275 place = EMSG;
276 if (*place == 0)
277 ++optind;
278 optopt = optchar;
279 return BADCH;
281 if (long_support && optchar == 'W' && oli[1] == ';') {
282 /* -W long-option */
283 if (*place)
284 return -2;
286 if (++optind >= nargc) { /* no arg */
287 place = EMSG;
288 if (PRINT_ERROR)
289 warnx(recargchar, optchar);
290 optopt = optchar;
291 return BADARG;
292 } else /* white space */
293 place = nargv[optind];
295 * Handle -W arg the same as --arg (which causes getopt to
296 * stop parsing).
298 return -2;
300 if (*++oli != ':') { /* doesn't take argument */
301 if (!*place)
302 ++optind;
303 } else { /* takes (optional) argument */
304 optarg = NULL;
305 if (*place) /* no white space */
306 optarg = place;
307 /* XXX: disable test for :: if PC? (GNU doesn't) */
308 else if (oli[1] != ':') { /* arg not optional */
309 if (++optind >= nargc) { /* no arg */
310 place = EMSG;
311 if (PRINT_ERROR)
312 warnx(recargchar, optchar);
313 optopt = optchar;
314 return BADARG;
315 } else
316 optarg = nargv[optind];
318 place = EMSG;
319 ++optind;
321 /* dump back option letter */
322 return optchar;
325 static int
326 getopt_long_internal(int nargc, char * const *nargv, const char *options,
327 const struct option *long_options, int *idx, int long_only)
329 int retval;
331 /* idx may be NULL */
333 retval = getopt_internal(nargc, nargv, options, long_only ? 2 : 1);
334 recheck:
335 if (retval == -2 || retval == -3) {
336 char *current_argv, *has_equal;
337 size_t current_argv_len;
338 int i, match;
340 current_argv = place;
341 match = -1;
343 optind++;
344 place = EMSG;
346 if ((has_equal = strchr(current_argv, '=')) != NULL) {
347 /* argument found (--option=arg) */
348 current_argv_len = has_equal - current_argv;
349 has_equal++;
350 } else
351 current_argv_len = strlen(current_argv);
353 for (i = 0; long_options[i].name; i++) {
354 /* find matching long option */
355 if (strncmp(current_argv, long_options[i].name,
356 current_argv_len))
357 continue;
359 if (strlen(long_options[i].name) ==
360 (unsigned)current_argv_len) {
361 /* exact match */
362 match = i;
363 break;
365 if (match == -1) /* partial match */
366 match = i;
367 else {
368 /* ambiguous abbreviation */
369 if (PRINT_ERROR)
370 warnx(ambig, (int)current_argv_len,
371 current_argv);
372 optopt = 0;
373 return BADCH;
376 if (match != -1) { /* option found */
377 if (long_options[match].has_arg == no_argument
378 && has_equal) {
379 if (PRINT_ERROR)
380 warnx(noarg, (int)current_argv_len,
381 current_argv);
383 * XXX: GNU sets optopt to val regardless of
384 * flag
386 if (long_options[match].flag == NULL)
387 optopt = long_options[match].val;
388 else
389 optopt = 0;
390 return BADARG;
392 if (long_options[match].has_arg == required_argument ||
393 long_options[match].has_arg == optional_argument) {
394 if (has_equal)
395 optarg = has_equal;
396 else if (long_options[match].has_arg ==
397 required_argument) {
399 * optional argument doesn't use
400 * next nargv
402 optarg = nargv[optind++];
405 if ((long_options[match].has_arg == required_argument)
406 && (optarg == NULL)) {
408 * Missing argument; leading ':'
409 * indicates no error should be generated
411 if (PRINT_ERROR)
412 warnx(recargstring, current_argv);
414 * XXX: GNU sets optopt to val regardless
415 * of flag
417 if (long_options[match].flag == NULL)
418 optopt = long_options[match].val;
419 else
420 optopt = 0;
421 --optind;
422 return BADARG;
424 } else if (retval == -3) {
425 --optind;
426 place = current_argv;
427 retval = getopt_internal_short(nargc, nargv,
428 options, long_only ? 2 : 1);
429 goto recheck;
430 } else { /* unknown option */
431 if (PRINT_ERROR)
432 warnx(illoptstring, current_argv);
433 optopt = 0;
434 return BADCH;
436 if (long_options[match].flag) {
437 *long_options[match].flag = long_options[match].val;
438 retval = 0;
439 } else
440 retval = long_options[match].val;
441 if (idx)
442 *idx = match;
444 return retval;
447 #ifdef REPLACE_GETOPT
449 * getopt --
450 * Parse argc/argv argument vector.
452 * [eventually this will replace the real getopt]
455 getopt(int nargc, char * const *nargv, const char *options)
457 return getopt_internal(nargc, nargv, options, 0);
459 #endif
462 * getopt_long --
463 * Parse argc/argv argument vector.
467 getopt_long(int nargc, char * const *nargv, const char *options,
468 const struct option *long_options, int *idx)
470 return getopt_long_internal(nargc, nargv, options, long_options,
471 idx, 0);
475 * getopt_long_only --
476 * Parse argc/argv argument vector.
477 * Prefers long options over short options for single dash arguments.
481 getopt_long_only(int nargc, char * const *nargv, const char *options,
482 const struct option *long_options, int *idx)
484 return getopt_long_internal(nargc, nargv, options, long_options,
485 idx, 1);