Fix several end cases:

o If a long option looks like an ambiguous abbreviation of two or more long
  options, but all the possible interpretations would return the same
  value, then just return that value without complaining that it's
  ambiguous.

o If a long option could be interpreted either as an exact match for one
  long option, or as an abbreviation for one or more other long options,
  then treat it as the exact match.

These changes align NetBSD's getopt_long(3) with the current behavior of
GNU getopt_long(3), the de facto standard, and FreeBSD's getopt_long(3).
This commit is contained in:
ginsbach 2007-07-05 16:05:40 +00:00
parent 8a70d87249
commit 15b661abe9
2 changed files with 38 additions and 16 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: getopt_long.c,v 1.20 2006/10/04 17:29:42 wiz Exp $ */
/* $NetBSD: getopt_long.c,v 1.21 2007/07/05 16:05:40 ginsbach Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -35,7 +35,7 @@
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: getopt_long.c,v 1.20 2006/10/04 17:29:42 wiz Exp $");
__RCSID("$NetBSD: getopt_long.c,v 1.21 2007/07/05 16:05:40 ginsbach Exp $");
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
@ -362,6 +362,11 @@ getopt_long(nargc, nargv, options, long_options, idx)
{
int retval;
#define IDENTICAL_INTERPRETATION(_x, _y) \
(long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
long_options[(_x)].flag == long_options[(_y)].flag && \
long_options[(_x)].val == long_options[(_y)].val)
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(options != NULL);
_DIAGASSERT(long_options != NULL);
@ -371,10 +376,11 @@ getopt_long(nargc, nargv, options, long_options, idx)
if (retval == -2) {
char *current_argv, *has_equal;
size_t current_argv_len;
int i, match;
int i, ambiguous, match;
current_argv = __UNCONST(place);
match = -1;
ambiguous = 0;
optind++;
place = EMSG;
@ -409,18 +415,21 @@ getopt_long(nargc, nargv, options, long_options, idx)
(unsigned)current_argv_len) {
/* exact match */
match = i;
ambiguous = 0;
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;
}
else if (!IDENTICAL_INTERPRETATION(i, match))
ambiguous = 1;
}
if (ambiguous) {
/* 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
@ -485,5 +494,6 @@ getopt_long(nargc, nargv, options, long_options, idx)
*idx = match;
}
return retval;
#undef IDENTICAL_INTERPRETATION
}
#endif /* !GETOPT_LONG */

View File

@ -1,4 +1,4 @@
# $NetBSD: opttest,v 1.1 2007/01/18 16:29:21 ginsbach Exp $
# $NetBSD: opttest,v 1.2 2007/07/05 16:05:40 ginsbach Exp $
#
# GNU libc tests with these
#
@ -19,9 +19,9 @@ result: -optional=bazbug|0
# This is promblematic
args: foo --col
# GNU libc 2.1.3 this fails with ambiguous
result: !?|0
#result: !?|0
# GNU libc 2.2 >= this works
#result: color|0
result: color|0
#
args: foo --colour
result: -colour|0
@ -30,6 +30,18 @@ result: -colour|0
args: foo -a -b -cfoobar --required foobar --optional=bazbug --none random \
--col --color --colour
# GNU libc 2.1.3 this fails with ambiguous
result: a,b,c=foobar,-required=foobar,-optional=bazbug,-none,!?,-color,-colour|1
#result: a,b,c=foobar,-required=foobar,-optional=bazbug,-none,!?,-color,-colour|1
# GNU libc 2.2 >= this works
#result: a,b,c=foobar,-required=foobar,-optional=bazbug,-none,-color,-color,-colour|1
result: a,b,c=foobar,-required=foobar,-optional=bazbug,-none,-color,-color,-colour|1
#
# The order of long options in the long option array should not matter.
# An exact match should never be treated as ambiguous.
#
optstring: fgl
longopts: 3
longopt: list-foobar, no_argument, lopt, 'f'
longopt: list-goobar, no_argument, lopt, 'g'
longopt: list, no_argument, lopt, 'l'
#
args: foo --list
result: -list|0