summaryrefslogtreecommitdiff
path: root/lib/libc/stdlib/getopt_long.c
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2013-06-08 22:47:57 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2013-06-08 22:47:57 +0000
commitb338b534346855a8f1d8fed88487a5671d49d20c (patch)
tree88c917f579df400b05e2e8e8ff7d3cd41249c2d1 /lib/libc/stdlib/getopt_long.c
parentff11d3867468575fa182403699dba5ba440d338c (diff)
Fix parsing of ambiguous options, the whole loop must be processed.
From FreeBSD. OK miod@
Diffstat (limited to 'lib/libc/stdlib/getopt_long.c')
-rw-r--r--lib/libc/stdlib/getopt_long.c37
1 files changed, 22 insertions, 15 deletions
diff --git a/lib/libc/stdlib/getopt_long.c b/lib/libc/stdlib/getopt_long.c
index e149fe0ace2..f46cd8bf4e4 100644
--- a/lib/libc/stdlib/getopt_long.c
+++ b/lib/libc/stdlib/getopt_long.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: getopt_long.c,v 1.25 2011/03/05 22:10:11 guenther Exp $ */
+/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
/*
@@ -77,7 +77,7 @@ char *optarg; /* argument associated with option */
static int getopt_internal(int, char * const *, const char *,
const struct option *, int *, int);
static int parse_long_options(char * const *, const char *,
- const struct option *, int *, int);
+ const struct option *, int *, int, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
@@ -157,14 +157,16 @@ permute_args(int panonopt_start, int panonopt_end, int opt_end,
*/
static int
parse_long_options(char * const *nargv, const char *options,
- const struct option *long_options, int *idx, int short_too)
+ const struct option *long_options, int *idx, int short_too, int flags)
{
char *current_argv, *has_equal;
size_t current_argv_len;
- int i, match;
+ int i, match, exact_match, second_partial_match;
current_argv = place;
match = -1;
+ exact_match = 0;
+ second_partial_match = 0;
optind++;
@@ -184,6 +186,7 @@ parse_long_options(char * const *nargv, const char *options,
if (strlen(long_options[i].name) == current_argv_len) {
/* exact match */
match = i;
+ exact_match = 1;
break;
}
/*
@@ -193,16 +196,20 @@ parse_long_options(char * const *nargv, const char *options,
if (short_too && current_argv_len == 1)
continue;
- if (match == -1) /* partial match */
+ if (match == -1) /* first partial match */
match = i;
- else {
- /* ambiguous abbreviation */
- if (PRINT_ERROR)
- warnx(ambig, (int)current_argv_len,
- current_argv);
- optopt = 0;
- return (BADCH);
- }
+ else if ((flags & FLAG_LONGONLY) ||
+ long_options[i].has_arg != long_options[match].has_arg ||
+ long_options[i].flag != long_options[match].flag ||
+ long_options[i].val != long_options[match].val)
+ second_partial_match = 1;
+ }
+ if (!exact_match && second_partial_match) {
+ /* 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
@@ -398,7 +405,7 @@ start:
short_too = 1; /* could be short option too */
optchar = parse_long_options(nargv, options, long_options,
- idx, short_too);
+ idx, short_too, flags);
if (optchar != -1) {
place = EMSG;
return (optchar);
@@ -435,7 +442,7 @@ start:
} else /* white space */
place = nargv[optind];
optchar = parse_long_options(nargv, options, long_options,
- idx, 0);
+ idx, 0, flags);
place = EMSG;
return (optchar);
}