Browse Source

Reorganize this so that getopt_only_only() works correctly.

In order for getopt_only_only() to work we need to check for long
options before short ones.  I have merged getopt_internal and
getopt_long_internal into a single function with the actual long
args parsing broken out into a separate function.
This also simplifies the flow of control.
OPENBSD_3_3
millert 21 years ago
parent
commit
2208de30a3
1 changed files with 208 additions and 221 deletions
  1. +208
    -221
      src/lib/libc/stdlib/getopt_long.c

+ 208
- 221
src/lib/libc/stdlib/getopt_long.c View File

@ -1,4 +1,4 @@
/* $OpenBSD: getopt_long.c,v 1.2 2002/12/03 20:28:12 millert Exp $ */
/* $OpenBSD: getopt_long.c,v 1.3 2002/12/05 21:45:01 millert Exp $ */
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
/*-
@ -38,7 +38,7 @@
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char *rcsid = "$OpenBSD: getopt_long.c,v 1.2 2002/12/03 20:28:12 millert Exp $";
static char *rcsid = "$OpenBSD: getopt_long.c,v 1.3 2002/12/05 21:45:01 millert Exp $";
#endif /* LIBC_SCCS and not lint */
#include <err.h>
@ -57,9 +57,9 @@ char *optarg; /* argument associated with option */
#define PRINT_ERROR ((opterr) && (*options != ':'))
#define FLAG_PERMUTE 0x01
#define FLAG_ALLARGS 0x02
#define FLAG_LONGONLY 0x04
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
/* return values */
#define BADCH (int)'?'
@ -68,9 +68,10 @@ char *optarg; /* argument associated with option */
#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 getopt_internal(int, char * const *, const char *,
const struct option *, int *, int);
static int parse_long_options(int, char * const *, const char *,
const struct option *, int *, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
@ -92,9 +93,7 @@ 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;
gcd(int a, int b)
{
int c;
@ -105,7 +104,7 @@ gcd(a, b)
c = a % b;
}
return b;
return (b);
}
/*
@ -114,11 +113,8 @@ gcd(a, b)
* 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;
permute_args(int panonopt_start, int panonopt_end, int opt_end,
char * const *nargv)
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
@ -148,23 +144,154 @@ permute_args(panonopt_start, panonopt_end, opt_end, nargv)
}
}
/*
* parse_long_options --
* Parse long options in argc/argv argument vector.
* Returns -2 if long_only is set and the current option could be a short
* (single character) option instead.
*/
static int
parse_long_options(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int long_only)
{
char *current_argv, *has_equal;
size_t current_argv_len;
int i, match;
current_argv = place;
match = -1;
optind++;
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) == current_argv_len) {
/* exact match */
match = i;
break;
}
/*
* Don't try a partial match of a short option when in
* long_only mode. Otherwise there is a potential conflict
* between partial matches and short options.
*/
if (long_only && current_argv_len == 1)
continue;
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 (long_only) {
--optind;
return (-2);
}
if (PRINT_ERROR)
warnx(illoptstring, current_argv);
optopt = 0;
return (BADCH);
}
if (idx)
*idx = match;
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
return (0);
} else
return (long_options[match].val);
}
/*
* 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;
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags)
{
char *oli; /* option letter list index */
int optchar;
static int posixly_correct = -1;
optarg = NULL;
/*
* Disable GNU extensions if POSIXLY_CORRECT is set or options
* string begins with a '+'.
*/
if (posixly_correct == -1)
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
if (posixly_correct || *options == '+')
flags &= ~FLAG_PERMUTE;
else if (*options == '-')
flags |= FLAG_ALLARGS;
if (*options == '+' || *options == '-')
options++;
/*
* XXX Some programs (like rsyncd) expect to be able to
* XXX re-initialize optind to 0 and have getopt_long(3)
@ -194,7 +321,7 @@ start:
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return -1;
return (-1);
}
if ((*(place = nargv[optind]) != '-')
|| (place[1] == '\0')) { /* found non-option */
@ -205,14 +332,14 @@ start:
* return non-option as argument to option 1
*/
optarg = nargv[optind++];
return INORDER;
return (INORDER);
}
if (!(flags & FLAG_PERMUTE)) {
/*
* If no permutation wanted, stop parsing
* at first non-option.
*/
return -1;
return (-1);
}
/* do permutation */
if (nonopt_start == -1)
@ -230,42 +357,64 @@ start:
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
if (place[1] && *++place == '-') { /* found "--" */
place++;
return -2;
if (strcmp(place, "--") == 0) {
optind++;
place = EMSG;
/*
* 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);
}
place++;
/* Check long options if we have any */
if (long_options != NULL) {
int long_only = 0;
if (*place == '-' ||
(long_only = (flags & FLAG_LONGONLY))) {
if (!long_only)
place++;
optchar = parse_long_options(nargc, nargv,
options, long_options, idx, long_only);
if (optchar != -2) {
place = EMSG;
return (optchar);
}
}
}
}
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;
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 (long_options != NULL && optchar == 'W' && oli[1] == ';') {
/* -W long-option */
if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return BADARG;
return (BADARG);
} else /* white space */
place = nargv[optind];
/*
* Handle -W arg the same as --arg (which causes getopt to
* stop parsing).
*/
return -2;
optchar = parse_long_options(nargc, nargv, options,
long_options, idx, 0);
place = EMSG;
return (optchar);
}
if (*++oli != ':') { /* doesn't take argument */
if (!*place)
@ -281,7 +430,7 @@ start:
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return BADARG;
return (BADARG);
} else
optarg = nargv[optind];
}
@ -289,7 +438,7 @@ start:
++optind;
}
/* dump back option letter */
return optchar;
return (optchar);
}
#ifdef REPLACE_GETOPT
@ -300,183 +449,20 @@ start:
* [eventually this will replace the BSD getopt]
*/
int
getopt(nargc, nargv, options)
int nargc;
char * const *nargv;
const char *options;
getopt(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 '+'.
* We dont' pass FLAG_PERMUTE to getopt_internal() since
* the BSD getopt(3) (unlike GNU) has never done this.
*
* Furthermore, since many privileged programs call getopt()
* before dropping privileges it makes sense to keep things
* as simple (and bug-free) as possible.
*/
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;
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
}
#endif /* REPLACE_GETOPT */
/*
* getopt_long --
@ -491,7 +477,8 @@ getopt_long(nargc, nargv, options, long_options, idx)
int *idx;
{
return getopt_long_internal(nargc, nargv, options, long_options, idx, 0);
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE));
}
/*
@ -507,6 +494,6 @@ getopt_long_only(nargc, nargv, options, long_options, idx)
int *idx;
{
return getopt_long_internal(nargc, nargv, options, long_options, idx,
FLAG_LONGONLY);
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE|FLAG_LONGONLY));
}

Loading…
Cancel
Save