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