getopt_long_only(). At some point this should replace the BSD getopt(3) but we are not there yet. While I am here add protection from the multiple getopt() definitions due to conflicting standards.OPENBSD_3_3
@ -0,0 +1,85 @@ | |||||
/* $OpenBSD: getopt.h,v 1.1 2002/12/03 20:24:29 millert Exp $ */ | |||||
/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ | |||||
/*- | |||||
* Copyright (c) 2000 The NetBSD Foundation, Inc. | |||||
* All rights reserved. | |||||
* | |||||
* This code is derived from software contributed to The NetBSD Foundation | |||||
* by Dieter Baron and Thomas Klausner. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* 3. All advertising materials mentioning features or use of this software | |||||
* must display the following acknowledgement: | |||||
* This product includes software developed by the NetBSD | |||||
* Foundation, Inc. and its contributors. | |||||
* 4. Neither the name of The NetBSD Foundation nor the names of its | |||||
* contributors may be used to endorse or promote products derived | |||||
* from this software without specific prior written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | |||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | |||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
* POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#ifndef _GETOPT_H_ | |||||
#define _GETOPT_H_ | |||||
#include <sys/cdefs.h> | |||||
/* | |||||
* GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions | |||||
*/ | |||||
#define no_argument 0 | |||||
#define required_argument 1 | |||||
#define optional_argument 2 | |||||
struct option { | |||||
/* name of long option */ | |||||
const char *name; | |||||
/* | |||||
* one of no_argument, required_argument, and optional_argument: | |||||
* whether option takes an argument | |||||
*/ | |||||
int has_arg; | |||||
/* if not NULL, set *flag to val when option found */ | |||||
int *flag; | |||||
/* if flag not NULL, value to set *flag to; else return value */ | |||||
int val; | |||||
}; | |||||
__BEGIN_DECLS | |||||
int getopt_long(int, char * const *, const char *, | |||||
const struct option *, int *); | |||||
int getopt_long_only(int, char * const *, const char *, | |||||
const struct option *, int *); | |||||
#ifndef _GETOPT_DEFINED_ | |||||
#define _GETOPT_DEFINED_ | |||||
int getopt(int, char * const *, const char *); | |||||
int getsubopt(char **, char * const *, char **); | |||||
extern char *optarg; /* getopt(3) external variables */ | |||||
extern int opterr; | |||||
extern int optind; | |||||
extern int optopt; | |||||
extern int optreset; | |||||
extern char *suboptarg; /* getsubopt(3) external variable */ | |||||
#endif | |||||
__END_DECLS | |||||
#endif /* !_GETOPT_H_ */ |
@ -0,0 +1,326 @@ | |||||
.\" $OpenBSD: getopt_long.3,v 1.1 2002/12/03 20:24:30 millert Exp $ | |||||
.\" $NetBSD: getopt_long.3,v 1.11 2002/10/02 10:54:19 wiz Exp $ | |||||
.\" | |||||
.\" Copyright (c) 1988, 1991, 1993 | |||||
.\" The Regents of the University of California. All rights reserved. | |||||
.\" | |||||
.\" Redistribution and use in source and binary forms, with or without | |||||
.\" modification, are permitted provided that the following conditions | |||||
.\" are met: | |||||
.\" 1. Redistributions of source code must retain the above copyright | |||||
.\" notice, this list of conditions and the following disclaimer. | |||||
.\" 2. Redistributions in binary form must reproduce the above copyright | |||||
.\" notice, this list of conditions and the following disclaimer in the | |||||
.\" documentation and/or other materials provided with the distribution. | |||||
.\" 3. All advertising materials mentioning features or use of this software | |||||
.\" must display the following acknowledgement: | |||||
.\" This product includes software developed by the University of | |||||
.\" California, Berkeley and its contributors. | |||||
.\" 4. Neither the name of the University nor the names of its contributors | |||||
.\" may be used to endorse or promote products derived from this software | |||||
.\" without specific prior written permission. | |||||
.\" | |||||
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
.\" SUCH DAMAGE. | |||||
.\" | |||||
.\" @(#)getopt.3 8.5 (Berkeley) 4/27/95 | |||||
.\" | |||||
.Dd April 1, 2000 | |||||
.Dt GETOPT_LONG 3 | |||||
.Os | |||||
.Sh NAME | |||||
.Nm getopt_long , | |||||
.Nm getopt_long_only | |||||
.Nd get long options from command line argument list | |||||
.Sh SYNOPSIS | |||||
.Fd #include <getopt.h> | |||||
.Vt extern char *optarg; | |||||
.Vt extern int optind; | |||||
.Vt extern int optopt; | |||||
.Vt extern int opterr; | |||||
.Vt extern int optreset; | |||||
.Ft int | |||||
.Fn getopt_long "int argc" "char * const *argv" "const char *optstring" "struct option *long options" "int *index" | |||||
.Ft int | |||||
.Fn getopt_long_only "int argc" "char * const *argv" "const char *optstring" "struct option *long options" "int *index" | |||||
.Sh DESCRIPTION | |||||
The | |||||
.Fn getopt_long | |||||
function is similar to | |||||
.Xr getopt 3 | |||||
but it accepts options in two forms: words and characters. | |||||
The | |||||
.Fn getopt_long | |||||
function provides a superset of of the functionality of | |||||
.Xr getopt 3 . | |||||
.Fn getopt_long | |||||
can be used in two ways. | |||||
In the first way, every long option understood by the program has a | |||||
corresponding short option, and the option structure is only used to | |||||
translate from long options to short options. | |||||
When used in this fashion, | |||||
.Fn getopt_long | |||||
behaves identically to | |||||
.Xr getopt 3 . | |||||
This is a good way to add long option processing to an existing program | |||||
with the minimum of rewriting. | |||||
.Pp | |||||
In the second mechanism, a long option sets a flag in the | |||||
.Fa option | |||||
structure passed, or will store a pointer to the command line argument | |||||
in the | |||||
.Fa option | |||||
structure passed to it for options that take arguments. | |||||
Additionally, the long option's argument may be specified as a single | |||||
argument with an equal sign, e.g. | |||||
.Bd -literal | |||||
myprogram --myoption=somevalue | |||||
.Ed | |||||
.Pp | |||||
When a long option is processed the call to | |||||
.Fn getopt_long | |||||
will return 0. | |||||
For this reason, long option processing without | |||||
shortcuts is not backwards compatible with | |||||
.Xr getopt 3 . | |||||
.Pp | |||||
It is possible to combine these methods, providing for long options | |||||
processing with short option equivalents for some options. | |||||
Less frequently used options would be processed as long options only. | |||||
.Pp | |||||
The | |||||
.Fn getopt_long | |||||
call requires a structure to be initialized describing the long | |||||
options. | |||||
The structure is: | |||||
.Bd -literal | |||||
struct option { | |||||
char *name; | |||||
int has_arg; | |||||
int *flag; | |||||
int val; | |||||
}; | |||||
.Ed | |||||
.Pp | |||||
The | |||||
.Fa name | |||||
field should contain the option name without the leading double dash. | |||||
.Pp | |||||
The | |||||
.Fa has_arg | |||||
field should be one of: | |||||
.Bl -tag -width "optional_argument" | |||||
.It Li no_argument | |||||
no argument to the option is expect. | |||||
.It Li required_argument | |||||
an argument to the option is required. | |||||
.It Li optional_argument | |||||
an argument to the option may be presented. | |||||
.El | |||||
.Pp | |||||
If | |||||
.Fa flag | |||||
is not | |||||
.Dv NULL , | |||||
then the integer pointed to by it will be set to the value in the | |||||
.Fa val | |||||
field. | |||||
If the | |||||
.Fa flag | |||||
field is | |||||
.Dv NULL , | |||||
then the | |||||
.Fa val | |||||
field will be returned. | |||||
Setting | |||||
.Fa flag | |||||
to | |||||
.Dv NULL | |||||
and setting | |||||
.Fa val | |||||
to the corresponding short option will make this function act just | |||||
like | |||||
.Xr getopt 3 . | |||||
.Pp | |||||
The | |||||
.Fn getopt_long_only | |||||
function behaves identically to | |||||
.Fn getopt_long | |||||
with the exception that long options may start with | |||||
.Sq - | |||||
in addition to | |||||
.Sq -- . | |||||
If an option starting with | |||||
.Sq - | |||||
does not match a long option but does match a single-character option, | |||||
the single-character option is returned. | |||||
.Sh EXAMPLES | |||||
.Bd -literal -compact | |||||
int bflag, ch, fd; | |||||
int daggerset; | |||||
/* options descriptor */ | |||||
static struct option longopts[] = { | |||||
{ "buffy", no_argument, 0, 'b' }, | |||||
{ "fluoride", required_argument, 0, 'f' }, | |||||
{ "daggerset", no_argument, &daggerset, 1 }, | |||||
{ 0, 0, 0, 0 } | |||||
}; | |||||
bflag = 0; | |||||
while ((ch = getopt_long(argc, argv, "bf:", longopts, NULL)) != -1) | |||||
switch(ch) { | |||||
case 'b': | |||||
bflag = 1; | |||||
break; | |||||
case 'f': | |||||
if ((fd = open(optarg, O_RDONLY, 0)) == -1) | |||||
err(1, "unable to open %s", optarg); | |||||
break; | |||||
case 0: | |||||
if (daggerset) { | |||||
fprintf(stderr,"Buffy will use her dagger to " | |||||
"apply fluoride to dracula's teeth\en"); | |||||
} | |||||
break; | |||||
case '?': | |||||
default: | |||||
usage(); | |||||
} | |||||
argc -= optind; | |||||
argv += optind; | |||||
.Ed | |||||
.Sh IMPLEMENTATION DIFFERENCES | |||||
This section describes differences to the GNU implementation | |||||
found in glibc-2.1.3: | |||||
.Bl -tag -width "xxx" | |||||
.It Li o | |||||
handling of - as first char of option string in presence of | |||||
environment variable POSIXLY_CORRECT: | |||||
.Bl -tag -width "OpenBSD" | |||||
.It Li GNU | |||||
ignores POSIXLY_CORRECT and returns non-options as | |||||
arguments to option '\e1'. | |||||
.It Li OpenBSD | |||||
honors POSIXLY_CORRECT and stops at the first non-option. | |||||
.El | |||||
.It Li o | |||||
handling of :: in options string in presence of POSIXLY_CORRECT: | |||||
.Bl -tag -width "OpenBSD" | |||||
.It Li Both | |||||
GNU and OpenBSD ignore POSIXLY_CORRECT here and take :: to | |||||
mean the preceding option takes an optional argument. | |||||
.El | |||||
.It Li o | |||||
return value in case of missing argument if first character | |||||
(after + or -) in option string is not ':': | |||||
.Bl -tag -width "OpenBSD" | |||||
.It Li GNU | |||||
returns '?' | |||||
.It OpenBSD | |||||
returns ':' (since OpenBSD's getopt does). | |||||
.El | |||||
.It Li o | |||||
handling of --a in getopt: | |||||
.Bl -tag -width "OpenBSD" | |||||
.It Li GNU | |||||
parses this as option '-', option 'a'. | |||||
.It Li OpenBSD | |||||
parses this as '--', and returns -1 (ignoring the a). (Because | |||||
the original getopt does.) | |||||
.El | |||||
.It Li o | |||||
setting of optopt for long options with flag != | |||||
.Dv NULL : | |||||
.Bl -tag -width "OpenBSD" | |||||
.It Li GNU | |||||
sets optopt to val. | |||||
.It Li OpenBSD | |||||
sets optopt to 0 (since val would never be returned). | |||||
.El | |||||
.It Li o | |||||
handling of -W with W; in option string in getopt (not getopt_long): | |||||
.Bl -tag -width "OpenBSD" | |||||
.It Li GNU | |||||
causes a segfault. | |||||
.It Li OpenBSD | |||||
returns \-1, with optind pointing past the argument of -W | |||||
(as if `-W arg' were `--arg', and thus '--' had been found). | |||||
.\" How should we treat W; in the option string when called via | |||||
.\" getopt? Ignore the ';' or treat it as a ':'? Issue a warning? | |||||
.El | |||||
.It Li o | |||||
setting of optarg for long options without an argument that are | |||||
invoked via -W (W; in option string): | |||||
.Bl -tag -width "OpenBSD" | |||||
.It Li GNU | |||||
sets optarg to the option name (the argument of -W). | |||||
.It Li OpenBSD | |||||
sets optarg to | |||||
.Dv NULL | |||||
(the argument of the long option). | |||||
.El | |||||
.It Li o | |||||
handling of -W with an argument that is not (a prefix to) a known | |||||
long option (W; in option string): | |||||
.Bl -tag -width "OpenBSD" | |||||
.It Li GNU | |||||
returns -W with optarg set to the unknown option. | |||||
.It Li OpenBSD | |||||
treats this as an error (unknown option) and returns '?' with | |||||
optopt set to 0 and optarg set to | |||||
.Dv NULL | |||||
(as GNU's man page documents). | |||||
.El | |||||
.It Li o | |||||
The error messages are different. | |||||
.It Li o | |||||
OpenBSD does not permute the argument vector at the same points in | |||||
the calling sequence as GNU does. | |||||
The aspects normally used by the caller | |||||
(ordering after \-1 is returned, value of optind relative | |||||
to current positions) are the same, though. | |||||
(We do fewer variable swaps.) | |||||
.El | |||||
.Sh ENVIRONMENT | |||||
.Bl -tag -width POSIXLY_CORRECT | |||||
.It Ev POSIXLY_CORRECT | |||||
If set, option processing stops when the first non-option is found and | |||||
a leading | |||||
.Sq - | |||||
or | |||||
.Sq + | |||||
in the | |||||
.Ar optstring | |||||
is ignored. | |||||
.El | |||||
.Sh SEE ALSO | |||||
.Xr getopt 3 | |||||
.Sh HISTORY | |||||
The | |||||
.Fn getopt_long | |||||
and | |||||
.Fn getopt_long_only | |||||
functions first appeared in GNU libiberty. | |||||
This implementation first appeared in | |||||
.Ox 3.3 . | |||||
.Sh BUGS | |||||
The | |||||
.Ar argv | |||||
argument is not really | |||||
.Dv const | |||||
as its elements may be permuted (unless | |||||
.Ev POSIXLY_CORRECT | |||||
is set). | |||||
.Pp | |||||
In a future release, this implementation should completely replace | |||||
.Xr getopt 3 . |
@ -0,0 +1,510 @@ | |||||
/* $OpenBSD: getopt_long.c,v 1.1 2002/12/03 20:24:30 millert Exp $ */ | |||||
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ | |||||
/*- | |||||
* Copyright (c) 2000 The NetBSD Foundation, Inc. | |||||
* All rights reserved. | |||||
* | |||||
* This code is derived from software contributed to The NetBSD Foundation | |||||
* by Dieter Baron and Thomas Klausner. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* 3. All advertising materials mentioning features or use of this software | |||||
* must display the following acknowledgement: | |||||
* This product includes software developed by the NetBSD | |||||
* Foundation, Inc. and its contributors. | |||||
* 4. Neither the name of The NetBSD Foundation nor the names of its | |||||
* contributors may be used to endorse or promote products derived | |||||
* from this software without specific prior written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | |||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | |||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
* POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#if defined(LIBC_SCCS) && !defined(lint) | |||||
static char *rcsid = "$OpenBSD: getopt_long.c,v 1.1 2002/12/03 20:24:30 millert Exp $"; | |||||
#endif /* LIBC_SCCS and not lint */ | |||||
#include <err.h> | |||||
#include <errno.h> | |||||
#include <getopt.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
int opterr = 1; /* if error message should be printed */ | |||||
int optind = 1; /* index into parent argv vector */ | |||||
int optopt = '?'; /* character checked for validity */ | |||||
int optreset; /* reset getopt */ | |||||
char *optarg; /* argument associated with option */ | |||||
#define PRINT_ERROR ((opterr) && (*options != ':')) | |||||
#define FLAG_PERMUTE 0x01 | |||||
#define FLAG_ALLARGS 0x02 | |||||
#define FLAG_LONGONLY 0x04 | |||||
/* return values */ | |||||
#define BADCH (int)'?' | |||||
#define BADARG ((*options == ':') ? (int)':' : (int)'?') | |||||
#define INORDER (int)1 | |||||
#define EMSG "" | |||||
static int getopt_internal(int, char * const *, const char *, int); | |||||
static int getopt_long_internal(int, char * const *, const char *, | |||||
const struct option *, int *, int); | |||||
static int gcd(int, int); | |||||
static void permute_args(int, int, int, char * const *); | |||||
static char *place = EMSG; /* option letter processing */ | |||||
/* XXX: set optreset to 1 rather than these two */ | |||||
static int nonopt_start = -1; /* first non option argument (for permute) */ | |||||
static int nonopt_end = -1; /* first option after non options (for permute) */ | |||||
/* Error messages */ | |||||
static const char recargchar[] = "option requires an argument -- %c"; | |||||
static const char recargstring[] = "option requires an argument -- %s"; | |||||
static const char ambig[] = "ambiguous option -- %.*s"; | |||||
static const char noarg[] = "option doesn't take an argument -- %.*s"; | |||||
static const char illoptchar[] = "unknown option -- %c"; | |||||
static const char illoptstring[] = "unknown option -- %s"; | |||||
/* | |||||
* Compute the greatest common divisor of a and b. | |||||
*/ | |||||
static int | |||||
gcd(a, b) | |||||
int a; | |||||
int b; | |||||
{ | |||||
int c; | |||||
c = a % b; | |||||
while (c != 0) { | |||||
a = b; | |||||
b = c; | |||||
c = a % b; | |||||
} | |||||
return b; | |||||
} | |||||
/* | |||||
* Exchange the block from nonopt_start to nonopt_end with the block | |||||
* from nonopt_end to opt_end (keeping the same order of arguments | |||||
* in each block). | |||||
*/ | |||||
static void | |||||
permute_args(panonopt_start, panonopt_end, opt_end, nargv) | |||||
int panonopt_start; | |||||
int panonopt_end; | |||||
int opt_end; | |||||
char * const *nargv; | |||||
{ | |||||
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; | |||||
char *swap; | |||||
/* | |||||
* compute lengths of blocks and number and size of cycles | |||||
*/ | |||||
nnonopts = panonopt_end - panonopt_start; | |||||
nopts = opt_end - panonopt_end; | |||||
ncycle = gcd(nnonopts, nopts); | |||||
cyclelen = (opt_end - panonopt_start) / ncycle; | |||||
for (i = 0; i < ncycle; i++) { | |||||
cstart = panonopt_end+i; | |||||
pos = cstart; | |||||
for (j = 0; j < cyclelen; j++) { | |||||
if (pos >= panonopt_end) | |||||
pos -= nnonopts; | |||||
else | |||||
pos += nopts; | |||||
swap = nargv[pos]; | |||||
/* LINTED const cast */ | |||||
((char **) nargv)[pos] = nargv[cstart]; | |||||
/* LINTED const cast */ | |||||
((char **)nargv)[cstart] = swap; | |||||
} | |||||
} | |||||
} | |||||
/* | |||||
* getopt_internal -- | |||||
* Parse argc/argv argument vector. Called by user level routines. | |||||
* Returns -2 if -- is found (can be long option or end of options marker). | |||||
*/ | |||||
static int | |||||
getopt_internal(nargc, nargv, options, flags) | |||||
int nargc; | |||||
char * const *nargv; | |||||
const char *options; | |||||
int flags; | |||||
{ | |||||
char *oli; /* option letter list index */ | |||||
int optchar; | |||||
optarg = NULL; | |||||
/* | |||||
* XXX Some programs (like rsyncd) expect to be able to | |||||
* XXX re-initialize optind to 0 and have getopt_long(3) | |||||
* XXX properly function again. Work around this braindamage. | |||||
*/ | |||||
if (optind == 0) | |||||
optind = 1; | |||||
if (optreset) | |||||
nonopt_start = nonopt_end = -1; | |||||
start: | |||||
if (optreset || !*place) { /* update scanning pointer */ | |||||
optreset = 0; | |||||
if (optind >= nargc) { /* end of argument vector */ | |||||
place = EMSG; | |||||
if (nonopt_end != -1) { | |||||
/* do permutation, if we have to */ | |||||
permute_args(nonopt_start, nonopt_end, | |||||
optind, nargv); | |||||
optind -= nonopt_end - nonopt_start; | |||||
} | |||||
else if (nonopt_start != -1) { | |||||
/* | |||||
* If we skipped non-options, set optind | |||||
* to the first of them. | |||||
*/ | |||||
optind = nonopt_start; | |||||
} | |||||
nonopt_start = nonopt_end = -1; | |||||
return -1; | |||||
} | |||||
if ((*(place = nargv[optind]) != '-') | |||||
|| (place[1] == '\0')) { /* found non-option */ | |||||
place = EMSG; | |||||
if (flags & FLAG_ALLARGS) { | |||||
/* | |||||
* GNU extension: | |||||
* return non-option as argument to option 1 | |||||
*/ | |||||
optarg = nargv[optind++]; | |||||
return INORDER; | |||||
} | |||||
if (!(flags & FLAG_PERMUTE)) { | |||||
/* | |||||
* If no permutation wanted, stop parsing | |||||
* at first non-option. | |||||
*/ | |||||
return -1; | |||||
} | |||||
/* do permutation */ | |||||
if (nonopt_start == -1) | |||||
nonopt_start = optind; | |||||
else if (nonopt_end != -1) { | |||||
permute_args(nonopt_start, nonopt_end, | |||||
optind, nargv); | |||||
nonopt_start = optind - | |||||
(nonopt_end - nonopt_start); | |||||
nonopt_end = -1; | |||||
} | |||||
optind++; | |||||
/* process next argument */ | |||||
goto start; | |||||
} | |||||
if (nonopt_start != -1 && nonopt_end == -1) | |||||
nonopt_end = optind; | |||||
if (place[1] && *++place == '-') { /* found "--" */ | |||||
place++; | |||||
return -2; | |||||
} | |||||
} | |||||
if ((optchar = (int)*place++) == (int)':' || | |||||
(oli = strchr(options, optchar)) == NULL) { | |||||
/* could it be a long option with a single '-'? */ | |||||
if (flags & FLAG_LONGONLY) | |||||
return -2; | |||||
/* option letter unknown or ':' */ | |||||
if (!*place) | |||||
++optind; | |||||
if (PRINT_ERROR) | |||||
warnx(illoptchar, optchar); | |||||
optopt = optchar; | |||||
return BADCH; | |||||
} | |||||
if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ | |||||
/* XXX: what if no long options provided (called by getopt)? */ | |||||
if (*place) | |||||
return -2; | |||||
if (++optind >= nargc) { /* no arg */ | |||||
place = EMSG; | |||||
if (PRINT_ERROR) | |||||
warnx(recargchar, optchar); | |||||
optopt = optchar; | |||||
return BADARG; | |||||
} else /* white space */ | |||||
place = nargv[optind]; | |||||
/* | |||||
* Handle -W arg the same as --arg (which causes getopt to | |||||
* stop parsing). | |||||
*/ | |||||
return -2; | |||||
} | |||||
if (*++oli != ':') { /* doesn't take argument */ | |||||
if (!*place) | |||||
++optind; | |||||
} else { /* takes (optional) argument */ | |||||
optarg = NULL; | |||||
if (*place) /* no white space */ | |||||
optarg = place; | |||||
/* XXX: disable test for :: if PC? (GNU doesn't) */ | |||||
else if (oli[1] != ':') { /* arg not optional */ | |||||
if (++optind >= nargc) { /* no arg */ | |||||
place = EMSG; | |||||
if (PRINT_ERROR) | |||||
warnx(recargchar, optchar); | |||||
optopt = optchar; | |||||
return BADARG; | |||||
} else | |||||
optarg = nargv[optind]; | |||||
} | |||||
place = EMSG; | |||||
++optind; | |||||
} | |||||
/* dump back option letter */ | |||||
return optchar; | |||||
} | |||||
#ifdef REPLACE_GETOPT | |||||
/* | |||||
* getopt -- | |||||
* Parse argc/argv argument vector. | |||||
* | |||||
* [eventually this will replace the BSD getopt] | |||||
*/ | |||||
int | |||||
getopt(nargc, nargv, options) | |||||
int nargc; | |||||
char * const *nargv; | |||||
const char *options; | |||||
{ | |||||
int retval; | |||||
if ((retval = getopt_internal(nargc, nargv, options, 0)) == -2) { | |||||
++optind; | |||||
/* | |||||
* We found an option (--), so if we skipped non-options, | |||||
* we have to permute. | |||||
*/ | |||||
if (nonopt_end != -1) { | |||||
permute_args(nonopt_start, nonopt_end, optind, | |||||
nargv); | |||||
optind -= nonopt_end - nonopt_start; | |||||
} | |||||
nonopt_start = nonopt_end = -1; | |||||
retval = -1; | |||||
} | |||||
return retval; | |||||
} | |||||
#endif /* REPLACE_GETOPT */ | |||||
/* | |||||
* getopt_long_internal -- | |||||
* Parse argc/argv argument vector. | |||||
*/ | |||||
static int | |||||
getopt_long_internal(nargc, nargv, options, long_options, idx, flags) | |||||
int nargc; | |||||
char * const *nargv; | |||||
const char *options; | |||||
const struct option *long_options; | |||||
int *idx; | |||||
int flags; | |||||
{ | |||||
int retval; | |||||
/* | |||||
* Disable GNU extensions if POSIXLY_CORRECT is set or options | |||||
* string begins with a '+'. | |||||
*/ | |||||
if (getenv("POSIXLY_CORRECT")) { | |||||
if (*options == '+' || *options == '-') | |||||
options++; | |||||
} else { | |||||
if (*options == '+') { | |||||
options++; | |||||
} else { | |||||
flags |= FLAG_PERMUTE; | |||||
if (*options == '-') { | |||||
flags |= FLAG_ALLARGS; | |||||
options++; | |||||
} | |||||
} | |||||
} | |||||
if ((retval = getopt_internal(nargc, nargv, options, flags)) == -2) { | |||||
char *current_argv, *has_equal; | |||||
size_t current_argv_len; | |||||
int i, match; | |||||
current_argv = place; | |||||
match = -1; | |||||
optind++; | |||||
place = EMSG; | |||||
if (*current_argv == '\0') { /* found "--" */ | |||||
/* | |||||
* We found an option (--), so if we skipped | |||||
* non-options, we have to permute. | |||||
*/ | |||||
if (nonopt_end != -1) { | |||||
permute_args(nonopt_start, nonopt_end, | |||||
optind, nargv); | |||||
optind -= nonopt_end - nonopt_start; | |||||
} | |||||
nonopt_start = nonopt_end = -1; | |||||
return -1; | |||||
} | |||||
if ((has_equal = strchr(current_argv, '=')) != NULL) { | |||||
/* argument found (--option=arg) */ | |||||
current_argv_len = has_equal - current_argv; | |||||
has_equal++; | |||||
} else | |||||
current_argv_len = strlen(current_argv); | |||||
for (i = 0; long_options[i].name; i++) { | |||||
/* find matching long option */ | |||||
if (strncmp(current_argv, long_options[i].name, | |||||
current_argv_len)) | |||||
continue; | |||||
if (strlen(long_options[i].name) == | |||||
(unsigned)current_argv_len) { | |||||
/* exact match */ | |||||
match = i; | |||||
break; | |||||
} | |||||
if (match == -1) /* partial match */ | |||||
match = i; | |||||
else { | |||||
/* ambiguous abbreviation */ | |||||
if (PRINT_ERROR) | |||||
warnx(ambig, (int)current_argv_len, | |||||
current_argv); | |||||
optopt = 0; | |||||
return BADCH; | |||||
} | |||||
} | |||||
if (match != -1) { /* option found */ | |||||
if (long_options[match].has_arg == no_argument | |||||
&& has_equal) { | |||||
if (PRINT_ERROR) | |||||
warnx(noarg, (int)current_argv_len, | |||||
current_argv); | |||||
/* | |||||
* XXX: GNU sets optopt to val regardless of | |||||
* flag | |||||
*/ | |||||
if (long_options[match].flag == NULL) | |||||
optopt = long_options[match].val; | |||||
else | |||||
optopt = 0; | |||||
return BADARG; | |||||
} | |||||
if (long_options[match].has_arg == required_argument || | |||||
long_options[match].has_arg == optional_argument) { | |||||
if (has_equal) | |||||
optarg = has_equal; | |||||
else if (long_options[match].has_arg == | |||||
required_argument) { | |||||
/* | |||||
* optional argument doesn't use | |||||
* next nargv | |||||
*/ | |||||
optarg = nargv[optind++]; | |||||
} | |||||
} | |||||
if ((long_options[match].has_arg == required_argument) | |||||
&& (optarg == NULL)) { | |||||
/* | |||||
* Missing argument; leading ':' | |||||
* indicates no error should be generated | |||||
*/ | |||||
if (PRINT_ERROR) | |||||
warnx(recargstring, current_argv); | |||||
/* | |||||
* XXX: GNU sets optopt to val regardless | |||||
* of flag | |||||
*/ | |||||
if (long_options[match].flag == NULL) | |||||
optopt = long_options[match].val; | |||||
else | |||||
optopt = 0; | |||||
--optind; | |||||
return BADARG; | |||||
} | |||||
} else { /* unknown option */ | |||||
if (PRINT_ERROR) | |||||
warnx(illoptstring, current_argv); | |||||
optopt = 0; | |||||
return BADCH; | |||||
} | |||||
if (long_options[match].flag) { | |||||
*long_options[match].flag = long_options[match].val; | |||||
retval = 0; | |||||
} else | |||||
retval = long_options[match].val; | |||||
if (idx) | |||||
*idx = match; | |||||
} | |||||
return retval; | |||||
} | |||||
/* | |||||
* getopt_long -- | |||||
* Parse argc/argv argument vector. | |||||
*/ | |||||
int | |||||
getopt_long(nargc, nargv, options, long_options, idx) | |||||
int nargc; | |||||
char * const *nargv; | |||||
const char *options; | |||||
const struct option *long_options; | |||||
int *idx; | |||||
{ | |||||
return getopt_long_internal(nargc, nargv, options, long_options, idx, 0); | |||||
} | |||||
/* | |||||
* getopt_long_only -- | |||||
* Parse argc/argv argument vector. | |||||
*/ | |||||
int | |||||
getopt_long_only(nargc, nargv, options, long_options, idx) | |||||
int nargc; | |||||
char * const *nargv; | |||||
const char *options; | |||||
const struct option *long_options; | |||||
int *idx; | |||||
{ | |||||
return getopt_long_internal(nargc, nargv, options, long_options, idx, | |||||
FLAG_LONGONLY); | |||||
} |