New upstream version 1.0.0
[fdkaac.git] / missings / getopt.c
CommitLineData
48e2f01c 1/* $OpenBSD: getopt_long.c,v 1.21 2006/09/22 17:22:05 millert Exp $ */
2/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
3
4/*
5 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 * Sponsored in part by the Defense Advanced Research Projects
20 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22 */
23/*-
24 * Copyright (c) 2000 The NetBSD Foundation, Inc.
25 * All rights reserved.
26 *
27 * This code is derived from software contributed to The NetBSD Foundation
28 * by Dieter Baron and Thomas Klausner.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the NetBSD
41 * Foundation, Inc. and its contributors.
42 * 4. Neither the name of The NetBSD Foundation nor the names of its
43 * contributors may be used to endorse or promote products derived
44 * from this software without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
47 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
48 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
49 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
50 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
51 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
52 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
53 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
54 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
56 * POSSIBILITY OF SUCH DAMAGE.
57 */
58
59#if 0
60#if defined(LIBC_SCCS) && !defined(lint)
61static char *rcsid = "$OpenBSD: getopt_long.c,v 1.16 2004/02/04 18:17:25 millert Exp $";
62#endif /* LIBC_SCCS and not lint */
63#endif
64
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <stdarg.h>
69#include <errno.h>
70#include "getopt.h"
71
72#define GNU_COMPATIBLE /* Be more compatible, configure's use us! */
73
74#if 0 /* we prefer to keep our getopt(3) */
75#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
76#endif
77
78int opterr = 1; /* if error message should be printed */
79int optind = 1; /* index into parent argv vector */
80int optopt = '?'; /* character checked for validity */
81int optreset; /* reset getopt */
82char *optarg; /* argument associated with option */
83
84#define PRINT_ERROR ((opterr) && (*options != ':'))
85
86#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
87#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
88#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
89
90/* return values */
91#define BADCH (int)'?'
92#define BADARG ((*options == ':') ? (int)':' : (int)'?')
93#define INORDER (int)1
94
95#define EMSG ""
96
97#ifdef GNU_COMPATIBLE
98#define NO_PREFIX (-1)
99#define D_PREFIX 0
100#define DD_PREFIX 1
101#define W_PREFIX 2
102#endif
103
104static int getopt_internal(int, char * const *, const char *,
105 const struct option *, int *, int);
106static int parse_long_options(char * const *, const char *,
107 const struct option *, int *, int, int);
108static int gcd(int, int);
109static void permute_args(int, int, int, char * const *);
110
111static char *place = EMSG; /* option letter processing */
112
113/* XXX: set optreset to 1 rather than these two */
114static int nonopt_start = -1; /* first non option argument (for permute) */
115static int nonopt_end = -1; /* first option after non options (for permute) */
116
117/* Error messages */
118static const char recargchar[] = "option requires an argument -- %c";
119static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
120#ifdef GNU_COMPATIBLE
121static int dash_prefix = NO_PREFIX;
122static const char gnuoptchar[] = "invalid option -- %c";
123
124static const char recargstring[] = "option `%s%s' requires an argument";
125static const char ambig[] = "option `%s%.*s' is ambiguous";
126static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
127static const char illoptstring[] = "unrecognized option `%s%s'";
128#else
129static const char recargstring[] = "option requires an argument -- %s";
130static const char ambig[] = "ambiguous option -- %.*s";
131static const char noarg[] = "option doesn't take an argument -- %.*s";
132static const char illoptstring[] = "unknown option -- %s";
133#endif
134
135static void
136warnx(const char *fmt, ...)
137{
138 va_list args;
139 va_start(args, fmt);
140 vfprintf(stderr, fmt, args);
141 putc('\n', stderr);
142 va_end(args);
143}
144
145/*
146 * Compute the greatest common divisor of a and b.
147 */
148static int
149gcd(int a, int b)
150{
151 int c;
152
153 c = a % b;
154 while (c != 0) {
155 a = b;
156 b = c;
157 c = a % b;
158 }
159
160 return (b);
161}
162
163/*
164 * Exchange the block from nonopt_start to nonopt_end with the block
165 * from nonopt_end to opt_end (keeping the same order of arguments
166 * in each block).
167 */
168static void
169permute_args(int panonopt_start, int panonopt_end, int opt_end,
170 char * const *nargv)
171{
172 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
173 char *swap;
174
175 /*
176 * compute lengths of blocks and number and size of cycles
177 */
178 nnonopts = panonopt_end - panonopt_start;
179 nopts = opt_end - panonopt_end;
180 ncycle = gcd(nnonopts, nopts);
181 cyclelen = (opt_end - panonopt_start) / ncycle;
182
183 for (i = 0; i < ncycle; i++) {
184 cstart = panonopt_end+i;
185 pos = cstart;
186 for (j = 0; j < cyclelen; j++) {
187 if (pos >= panonopt_end)
188 pos -= nnonopts;
189 else
190 pos += nopts;
191 swap = nargv[pos];
192 /* LINTED const cast */
193 ((char **) nargv)[pos] = nargv[cstart];
194 /* LINTED const cast */
195 ((char **)nargv)[cstart] = swap;
196 }
197 }
198}
199
200/*
201 * parse_long_options --
202 * Parse long options in argc/argv argument vector.
203 * Returns -1 if short_too is set and the option does not match long_options.
204 */
205static int
206parse_long_options(char * const *nargv, const char *options,
207 const struct option *long_options, int *idx, int short_too, int flags)
208{
209 char *current_argv, *has_equal;
210#ifdef GNU_COMPATIBLE
211 char *current_dash;
212#endif
213 size_t current_argv_len;
214 int i, match, exact_match, second_partial_match;
215
216 current_argv = place;
217#ifdef GNU_COMPATIBLE
218 switch (dash_prefix) {
219 case D_PREFIX:
220 current_dash = "-";
221 break;
222 case DD_PREFIX:
223 current_dash = "--";
224 break;
225 case W_PREFIX:
226 current_dash = "-W ";
227 break;
228 default:
229 current_dash = "";
230 break;
231 }
232#endif
233 match = -1;
234 exact_match = 0;
235 second_partial_match = 0;
236
237 optind++;
238
239 if ((has_equal = strchr(current_argv, '=')) != NULL) {
240 /* argument found (--option=arg) */
241 current_argv_len = has_equal - current_argv;
242 has_equal++;
243 } else
244 current_argv_len = strlen(current_argv);
245
246 for (i = 0; long_options[i].name; i++) {
247 /* find matching long option */
248 if (strncmp(current_argv, long_options[i].name,
249 current_argv_len))
250 continue;
251
252 if (strlen(long_options[i].name) == current_argv_len) {
253 /* exact match */
254 match = i;
255 exact_match = 1;
256 break;
257 }
258 /*
259 * If this is a known short option, don't allow
260 * a partial match of a single character.
261 */
262 if (short_too && current_argv_len == 1)
263 continue;
264
265 if (match == -1) /* first partial match */
266 match = i;
267 else if ((flags & FLAG_LONGONLY) ||
268 long_options[i].has_arg !=
269 long_options[match].has_arg ||
270 long_options[i].flag != long_options[match].flag ||
271 long_options[i].val != long_options[match].val)
272 second_partial_match = 1;
273 }
274 if (!exact_match && second_partial_match) {
275 /* ambiguous abbreviation */
276 if (PRINT_ERROR)
277 warnx(ambig,
278#ifdef GNU_COMPATIBLE
279 current_dash,
280#endif
281 (int)current_argv_len,
282 current_argv);
283 optopt = 0;
284 return (BADCH);
285 }
286 if (match != -1) { /* option found */
287 if (long_options[match].has_arg == no_argument
288 && has_equal) {
289 if (PRINT_ERROR)
290 warnx(noarg,
291#ifdef GNU_COMPATIBLE
292 current_dash,
293#endif
294 (int)current_argv_len,
295 current_argv);
296 /*
297 * XXX: GNU sets optopt to val regardless of flag
298 */
299 if (long_options[match].flag == NULL)
300 optopt = long_options[match].val;
301 else
302 optopt = 0;
303#ifdef GNU_COMPATIBLE
304 return (BADCH);
305#else
306 return (BADARG);
307#endif
308 }
309 if (long_options[match].has_arg == required_argument ||
310 long_options[match].has_arg == optional_argument) {
311 if (has_equal)
312 optarg = has_equal;
313 else if (long_options[match].has_arg ==
314 required_argument) {
315 /*
316 * optional argument doesn't use next nargv
317 */
318 optarg = nargv[optind++];
319 }
320 }
321 if ((long_options[match].has_arg == required_argument)
322 && (optarg == NULL)) {
323 /*
324 * Missing argument; leading ':' indicates no error
325 * should be generated.
326 */
327 if (PRINT_ERROR)
328 warnx(recargstring,
329#ifdef GNU_COMPATIBLE
330 current_dash,
331#endif
332 current_argv);
333 /*
334 * XXX: GNU sets optopt to val regardless of flag
335 */
336 if (long_options[match].flag == NULL)
337 optopt = long_options[match].val;
338 else
339 optopt = 0;
340 --optind;
341 return (BADARG);
342 }
343 } else { /* unknown option */
344 if (short_too) {
345 --optind;
346 return (-1);
347 }
348 if (PRINT_ERROR)
349 warnx(illoptstring,
350#ifdef GNU_COMPATIBLE
351 current_dash,
352#endif
353 current_argv);
354 optopt = 0;
355 return (BADCH);
356 }
357 if (idx)
358 *idx = match;
359 if (long_options[match].flag) {
360 *long_options[match].flag = long_options[match].val;
361 return (0);
362 } else
363 return (long_options[match].val);
364}
365
366/*
367 * getopt_internal --
368 * Parse argc/argv argument vector. Called by user level routines.
369 */
370static int
371getopt_internal(int nargc, char * const *nargv, const char *options,
372 const struct option *long_options, int *idx, int flags)
373{
374 char *oli; /* option letter list index */
375 int optchar, short_too;
376 int posixly_correct; /* no static, can be changed on the fly */
377
378 if (options == NULL)
379 return (-1);
380
381 /*
382 * Disable GNU extensions if POSIXLY_CORRECT is set or options
383 * string begins with a '+'.
384 */
385 posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
386#ifdef GNU_COMPATIBLE
387 if (*options == '-')
388 flags |= FLAG_ALLARGS;
389 else if (posixly_correct || *options == '+')
390 flags &= ~FLAG_PERMUTE;
391#else
392 if (posixly_correct || *options == '+')
393 flags &= ~FLAG_PERMUTE;
394 else if (*options == '-')
395 flags |= FLAG_ALLARGS;
396#endif
397 if (*options == '+' || *options == '-')
398 options++;
399
400 /*
401 * XXX Some GNU programs (like cvs) set optind to 0 instead of
402 * XXX using optreset. Work around this braindamage.
403 */
404 if (optind == 0)
405 optind = optreset = 1;
406
407 optarg = NULL;
408 if (optreset)
409 nonopt_start = nonopt_end = -1;
410start:
411 if (optreset || !*place) { /* update scanning pointer */
412 optreset = 0;
413 if (optind >= nargc) { /* end of argument vector */
414 place = EMSG;
415 if (nonopt_end != -1) {
416 /* do permutation, if we have to */
417 permute_args(nonopt_start, nonopt_end,
418 optind, nargv);
419 optind -= nonopt_end - nonopt_start;
420 }
421 else if (nonopt_start != -1) {
422 /*
423 * If we skipped non-options, set optind
424 * to the first of them.
425 */
426 optind = nonopt_start;
427 }
428 nonopt_start = nonopt_end = -1;
429 return (-1);
430 }
431 if (*(place = nargv[optind]) != '-' ||
432#ifdef GNU_COMPATIBLE
433 place[1] == '\0') {
434#else
435 (place[1] == '\0' && strchr(options, '-') == NULL)) {
436#endif
437 place = EMSG; /* found non-option */
438 if (flags & FLAG_ALLARGS) {
439 /*
440 * GNU extension:
441 * return non-option as argument to option 1
442 */
443 optarg = nargv[optind++];
444 return (INORDER);
445 }
446 if (!(flags & FLAG_PERMUTE)) {
447 /*
448 * If no permutation wanted, stop parsing
449 * at first non-option.
450 */
451 return (-1);
452 }
453 /* do permutation */
454 if (nonopt_start == -1)
455 nonopt_start = optind;
456 else if (nonopt_end != -1) {
457 permute_args(nonopt_start, nonopt_end,
458 optind, nargv);
459 nonopt_start = optind -
460 (nonopt_end - nonopt_start);
461 nonopt_end = -1;
462 }
463 optind++;
464 /* process next argument */
465 goto start;
466 }
467 if (nonopt_start != -1 && nonopt_end == -1)
468 nonopt_end = optind;
469
470 /*
471 * If we have "-" do nothing, if "--" we are done.
472 */
473 if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
474 optind++;
475 place = EMSG;
476 /*
477 * We found an option (--), so if we skipped
478 * non-options, we have to permute.
479 */
480 if (nonopt_end != -1) {
481 permute_args(nonopt_start, nonopt_end,
482 optind, nargv);
483 optind -= nonopt_end - nonopt_start;
484 }
485 nonopt_start = nonopt_end = -1;
486 return (-1);
487 }
488 }
489
490 /*
491 * Check long options if:
492 * 1) we were passed some
493 * 2) the arg is not just "-"
494 * 3) either the arg starts with -- we are getopt_long_only()
495 */
496 if (long_options != NULL && place != nargv[optind] &&
497 (*place == '-' || (flags & FLAG_LONGONLY))) {
498 short_too = 0;
499#ifdef GNU_COMPATIBLE
500 dash_prefix = D_PREFIX;
501#endif
502 if (*place == '-') {
503 place++; /* --foo long option */
504#ifdef GNU_COMPATIBLE
505 dash_prefix = DD_PREFIX;
506#endif
507 } else if (*place != ':' && strchr(options, *place) != NULL)
508 short_too = 1; /* could be short option too */
509
510 optchar = parse_long_options(nargv, options, long_options,
511 idx, short_too, flags);
512 if (optchar != -1) {
513 place = EMSG;
514 return (optchar);
515 }
516 }
517
518 if ((optchar = (int)*place++) == (int)':' ||
519 (optchar == (int)'-' && *place != '\0') ||
520 (oli = strchr(options, optchar)) == NULL) {
521 /*
522 * If the user specified "-" and '-' isn't listed in
523 * options, return -1 (non-option) as per POSIX.
524 * Otherwise, it is an unknown option character (or ':').
525 */
526 if (optchar == (int)'-' && *place == '\0')
527 return (-1);
528 if (!*place)
529 ++optind;
530#ifdef GNU_COMPATIBLE
531 if (PRINT_ERROR)
532 warnx(posixly_correct ? illoptchar : gnuoptchar,
533 optchar);
534#else
535 if (PRINT_ERROR)
536 warnx(illoptchar, optchar);
537#endif
538 optopt = optchar;
539 return (BADCH);
540 }
541 if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
542 /* -W long-option */
543 if (*place) /* no space */
544 /* NOTHING */;
545 else if (++optind >= nargc) { /* no arg */
546 place = EMSG;
547 if (PRINT_ERROR)
548 warnx(recargchar, optchar);
549 optopt = optchar;
550 return (BADARG);
551 } else /* white space */
552 place = nargv[optind];
553#ifdef GNU_COMPATIBLE
554 dash_prefix = W_PREFIX;
555#endif
556 optchar = parse_long_options(nargv, options, long_options,
557 idx, 0, flags);
558 place = EMSG;
559 return (optchar);
560 }
561 if (*++oli != ':') { /* doesn't take argument */
562 if (!*place)
563 ++optind;
564 } else { /* takes (optional) argument */
565 optarg = NULL;
566 if (*place) /* no white space */
567 optarg = place;
568 else if (oli[1] != ':') { /* arg not optional */
569 if (++optind >= nargc) { /* no arg */
570 place = EMSG;
571 if (PRINT_ERROR)
572 warnx(recargchar, optchar);
573 optopt = optchar;
574 return (BADARG);
575 } else
576 optarg = nargv[optind];
577 }
578 place = EMSG;
579 ++optind;
580 }
581 /* dump back option letter */
582 return (optchar);
583}
584
585/*
586 * getopt --
587 * Parse argc/argv argument vector.
588 *
589 * [eventually this will replace the BSD getopt]
590 */
591int
592getopt(int nargc, char * const *nargv, const char *options)
593{
594
595 /*
596 * We don't pass FLAG_PERMUTE to getopt_internal() since
597 * the BSD getopt(3) (unlike GNU) has never done this.
598 *
599 * Furthermore, since many privileged programs call getopt()
600 * before dropping privileges it makes sense to keep things
601 * as simple (and bug-free) as possible.
602 */
603 return (getopt_internal(nargc, nargv, options, NULL, NULL, FLAG_PERMUTE));
604}
605
606/*
607 * getopt_long --
608 * Parse argc/argv argument vector.
609 */
610int
611getopt_long(int nargc, char * const *nargv, const char *options,
612 const struct option *long_options, int *idx)
613{
614
615 return (getopt_internal(nargc, nargv, options, long_options, idx,
616 FLAG_PERMUTE));
617}
618
619/*
620 * getopt_long_only --
621 * Parse argc/argv argument vector.
622 */
623int
624getopt_long_only(int nargc, char * const *nargv, const char *options,
625 const struct option *long_options, int *idx)
626{
627
628 return (getopt_internal(nargc, nargv, options, long_options, idx,
629 FLAG_PERMUTE|FLAG_LONGONLY));
630}
This page took 0.042411 seconds and 4 git commands to generate.