mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 12:30:06 +00:00
getopt: merge from gnulib: alloca avoidance
In one place, glibc's getopt uses alloca to construct a linked list of possibilities for an "ambiguous" long option. In gnulib, malloc should be used instead. Providing for both cases complicates things a fair bit. Instead of merging straight across, therefore, I have chosen to rewrite it using a boolean vector instead of a linked list. There is then only one allocation that might need freeing; in glibc it can honor __libc_use_alloca as usual, and in gnulib we define __libc_use_alloca to always be false, so we don't need ifdefs in the middle of the function. This should also be slightly more efficient in the normal case of long options being fully spelled out -- I think most people aren't even aware they _can_ sometimes abbreviate long options. One interesting consequence is that the list of possibilities is now printed in exactly the order they appear in the list of long options, instead of the first possibility being shuffled to the end. This shouldn't be a big deal but it did break one test that relied on the exact text of this error message. (The reason the previous patch was "in aid of" merging from gnulib is I didn't want to have to make this change in two places.) (The patch looks bigger than it really is because there's a fair bit of reindentation and code rearrangement.) * posix/getopt.c: When used standalone, define __libc_use_alloca as always false and alloca to abort if called. (process_long_option): Rewrite handling of ambiguous long options to use a single boolean vector, not a linked list; use __libc_use_alloca to decide whether to allocate this using alloca. * posix/tst-getopt_long1.c: Adjust text of expected error message.
This commit is contained in:
parent
dfbea09f96
commit
aeacb9f912
@ -1,5 +1,12 @@
|
|||||||
2017-04-07 Zack Weinberg <zackw@panix.com>
|
2017-04-07 Zack Weinberg <zackw@panix.com>
|
||||||
|
|
||||||
|
* posix/getopt.c: When used standalone, define __libc_use_alloca
|
||||||
|
as always false and alloca to abort if called.
|
||||||
|
(process_long_option): Rewrite handling of ambiguous long options
|
||||||
|
to use a single boolean vector, not a linked list; use
|
||||||
|
__libc_use_alloca to decide whether to allocate this using alloca.
|
||||||
|
* posix/tst-getopt_long1.c: Adjust text of expected error message.
|
||||||
|
|
||||||
* posix/getopt.c (process_long_option): New function split out
|
* posix/getopt.c (process_long_option): New function split out
|
||||||
from _getopt_internal_r.
|
from _getopt_internal_r.
|
||||||
(_getopt_internal_r): Replace both copies of the long-option
|
(_getopt_internal_r): Replace both copies of the long-option
|
||||||
|
279
posix/getopt.c
279
posix/getopt.c
@ -51,6 +51,10 @@
|
|||||||
# define flockfile(fp) /* nop */
|
# define flockfile(fp) /* nop */
|
||||||
# define funlockfile(fp) /* nop */
|
# define funlockfile(fp) /* nop */
|
||||||
# endif
|
# endif
|
||||||
|
/* When used standalone, do not attempt to use alloca. */
|
||||||
|
# define __libc_use_alloca(size) 0
|
||||||
|
# undef alloca
|
||||||
|
# define alloca(size) (abort (), (void *)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* This implementation of 'getopt' has three modes for handling
|
/* This implementation of 'getopt' has three modes for handling
|
||||||
@ -197,150 +201,179 @@ process_long_option (int argc, char **argv, const char *optstring,
|
|||||||
size_t namelen;
|
size_t namelen;
|
||||||
const struct option *p;
|
const struct option *p;
|
||||||
const struct option *pfound = NULL;
|
const struct option *pfound = NULL;
|
||||||
struct option_list
|
int n_options;
|
||||||
{
|
|
||||||
const struct option *p;
|
|
||||||
struct option_list *next;
|
|
||||||
} *ambig_list = NULL;
|
|
||||||
int exact = 0;
|
|
||||||
int indfound = -1;
|
|
||||||
int option_index;
|
int option_index;
|
||||||
|
|
||||||
for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
|
for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
|
||||||
/* Do nothing. */ ;
|
/* Do nothing. */ ;
|
||||||
namelen = nameend - d->__nextchar;
|
namelen = nameend - d->__nextchar;
|
||||||
|
|
||||||
/* Test all long options for either exact match
|
/* First look for an exact match, counting the options as a side
|
||||||
or abbreviated matches. */
|
effect. */
|
||||||
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
for (p = longopts, n_options = 0; p->name; p++, n_options++)
|
||||||
if (!strncmp (p->name, d->__nextchar, namelen))
|
if (!strncmp (p->name, d->__nextchar, namelen)
|
||||||
|
&& namelen == strlen (p->name))
|
||||||
{
|
{
|
||||||
if (namelen == strlen (p->name))
|
/* Exact match found. */
|
||||||
{
|
pfound = p;
|
||||||
/* Exact match found. */
|
option_index = n_options;
|
||||||
pfound = p;
|
break;
|
||||||
indfound = option_index;
|
|
||||||
exact = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (pfound == NULL)
|
|
||||||
{
|
|
||||||
/* First nonexact match found. */
|
|
||||||
pfound = p;
|
|
||||||
indfound = option_index;
|
|
||||||
}
|
|
||||||
else if (long_only
|
|
||||||
|| pfound->has_arg != p->has_arg
|
|
||||||
|| pfound->flag != p->flag
|
|
||||||
|| pfound->val != p->val)
|
|
||||||
{
|
|
||||||
/* Second or later nonexact match found. */
|
|
||||||
struct option_list *newp = alloca (sizeof (*newp));
|
|
||||||
newp->p = p;
|
|
||||||
newp->next = ambig_list;
|
|
||||||
ambig_list = newp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ambig_list != NULL && !exact)
|
if (pfound == NULL)
|
||||||
{
|
{
|
||||||
if (print_errors)
|
/* Didn't find an exact match, so look for abbreviations. */
|
||||||
{
|
unsigned char *ambig_set = NULL;
|
||||||
struct option_list first;
|
int ambig_malloced = 0;
|
||||||
first.p = pfound;
|
int ambig_fallback = 0;
|
||||||
first.next = ambig_list;
|
int indfound = -1;
|
||||||
ambig_list = &first;
|
|
||||||
|
|
||||||
flockfile (stderr);
|
for (p = longopts, option_index = 0; p->name; p++, option_index++)
|
||||||
|
if (!strncmp (p->name, d->__nextchar, namelen))
|
||||||
|
{
|
||||||
|
if (pfound == NULL)
|
||||||
|
{
|
||||||
|
/* First nonexact match found. */
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
}
|
||||||
|
else if (long_only
|
||||||
|
|| pfound->has_arg != p->has_arg
|
||||||
|
|| pfound->flag != p->flag
|
||||||
|
|| pfound->val != p->val)
|
||||||
|
{
|
||||||
|
/* Second or later nonexact match found. */
|
||||||
|
if (!ambig_fallback)
|
||||||
|
{
|
||||||
|
if (!print_errors)
|
||||||
|
/* Don't waste effort tracking the ambig set if
|
||||||
|
we're not going to print it anyway. */
|
||||||
|
ambig_fallback = 1;
|
||||||
|
else if (!ambig_set)
|
||||||
|
{
|
||||||
|
if (__libc_use_alloca (n_options))
|
||||||
|
ambig_set = alloca (n_options);
|
||||||
|
else if ((ambig_set = malloc (n_options)) == NULL)
|
||||||
|
/* Fall back to simpler error message. */
|
||||||
|
ambig_fallback = 1;
|
||||||
|
else
|
||||||
|
ambig_malloced = 1;
|
||||||
|
|
||||||
fprintf (stderr, _("%s: option '%s' is ambiguous; possibilities:"),
|
if (ambig_set)
|
||||||
argv[0], argv[d->optind]);
|
{
|
||||||
do
|
memset (ambig_set, 0, n_options);
|
||||||
{
|
ambig_set[indfound] = 1;
|
||||||
fprintf (stderr, " '--%s'", ambig_list->p->name);
|
}
|
||||||
ambig_list = ambig_list->next;
|
}
|
||||||
}
|
if (ambig_set)
|
||||||
while (ambig_list != NULL);
|
ambig_set[option_index] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* This must use 'fprintf' even though it's only printing a
|
if (ambig_set || ambig_fallback)
|
||||||
single character, so that it goes through __fxprintf_nocancel
|
{
|
||||||
when compiled as part of glibc. */
|
if (print_errors)
|
||||||
fprintf (stderr, "\n");
|
{
|
||||||
funlockfile (stderr);
|
if (ambig_fallback)
|
||||||
}
|
fprintf (stderr, _("%s: option '%s%s' is ambiguous\n"),
|
||||||
d->__nextchar += strlen (d->__nextchar);
|
argv[0], prefix, d->__nextchar);
|
||||||
d->optind++;
|
else
|
||||||
d->optopt = 0;
|
{
|
||||||
return '?';
|
flockfile (stderr);
|
||||||
}
|
fprintf (stderr,
|
||||||
|
_("%s: option '%s%s' is ambiguous; possibilities:"),
|
||||||
|
argv[0], prefix, d->__nextchar);
|
||||||
|
|
||||||
|
for (option_index = 0; option_index < n_options; option_index++)
|
||||||
|
if (ambig_set[option_index])
|
||||||
|
fprintf (stderr, " '%s%s'",
|
||||||
|
prefix, longopts[option_index].name);
|
||||||
|
|
||||||
|
/* This must use 'fprintf' even though it's only
|
||||||
|
printing a single character, so that it goes through
|
||||||
|
__fxprintf_nocancel when compiled as part of glibc. */
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
funlockfile (stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ambig_malloced)
|
||||||
|
free (ambig_set);
|
||||||
|
d->__nextchar += strlen (d->__nextchar);
|
||||||
|
d->optind++;
|
||||||
|
d->optopt = 0;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
if (pfound != NULL)
|
|
||||||
{
|
|
||||||
option_index = indfound;
|
option_index = indfound;
|
||||||
d->optind++;
|
|
||||||
if (*nameend)
|
|
||||||
{
|
|
||||||
/* Don't test has_arg with >, because some C compilers don't
|
|
||||||
allow it to be used on enums. */
|
|
||||||
if (pfound->has_arg)
|
|
||||||
d->optarg = nameend + 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (print_errors)
|
|
||||||
fprintf (stderr,
|
|
||||||
_("%s: option '%s%s' doesn't allow an argument\n"),
|
|
||||||
argv[0], prefix, pfound->name);
|
|
||||||
|
|
||||||
d->__nextchar += strlen (d->__nextchar);
|
|
||||||
d->optopt = pfound->val;
|
|
||||||
return '?';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pfound->has_arg == 1)
|
|
||||||
{
|
|
||||||
if (d->optind < argc)
|
|
||||||
d->optarg = argv[d->optind++];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (print_errors)
|
|
||||||
fprintf (stderr,
|
|
||||||
_("%s: option '%s%s' requires an argument\n"),
|
|
||||||
argv[0], prefix, pfound->name);
|
|
||||||
|
|
||||||
d->__nextchar += strlen (d->__nextchar);
|
|
||||||
d->optopt = pfound->val;
|
|
||||||
return optstring[0] == ':' ? ':' : '?';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d->__nextchar += strlen (d->__nextchar);
|
|
||||||
if (longind != NULL)
|
|
||||||
*longind = option_index;
|
|
||||||
if (pfound->flag)
|
|
||||||
{
|
|
||||||
*(pfound->flag) = pfound->val;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return pfound->val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Can't find it as a long option. If this is not getopt_long_only,
|
if (pfound == NULL)
|
||||||
or the option starts with '--' or is not a valid short option,
|
|
||||||
then it's an error. */
|
|
||||||
if (!long_only || argv[d->optind][1] == '-'
|
|
||||||
|| strchr (optstring, *d->__nextchar) == NULL)
|
|
||||||
{
|
{
|
||||||
if (print_errors)
|
/* Can't find it as a long option. If this is not getopt_long_only,
|
||||||
fprintf (stderr, _("%s: unrecognized option '%s%s'\n"),
|
or the option starts with '--' or is not a valid short option,
|
||||||
argv[0], prefix, d->__nextchar);
|
then it's an error. */
|
||||||
|
if (!long_only || argv[d->optind][1] == '-'
|
||||||
|
|| strchr (optstring, *d->__nextchar) == NULL)
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
fprintf (stderr, _("%s: unrecognized option '%s%s'\n"),
|
||||||
|
argv[0], prefix, d->__nextchar);
|
||||||
|
|
||||||
d->__nextchar = NULL;
|
d->__nextchar = NULL;
|
||||||
d->optind++;
|
d->optind++;
|
||||||
d->optopt = 0;
|
d->optopt = 0;
|
||||||
return '?';
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise interpret it as a short option. */
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise interpret it as a short option. */
|
/* We have found a matching long option. Consume it. */
|
||||||
return -1;
|
d->optind++;
|
||||||
|
d->__nextchar = NULL;
|
||||||
|
if (*nameend)
|
||||||
|
{
|
||||||
|
/* Don't test has_arg with >, because some C compilers don't
|
||||||
|
allow it to be used on enums. */
|
||||||
|
if (pfound->has_arg)
|
||||||
|
d->optarg = nameend + 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
fprintf (stderr,
|
||||||
|
_("%s: option '%s%s' doesn't allow an argument\n"),
|
||||||
|
argv[0], prefix, pfound->name);
|
||||||
|
|
||||||
|
d->optopt = pfound->val;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pfound->has_arg == 1)
|
||||||
|
{
|
||||||
|
if (d->optind < argc)
|
||||||
|
d->optarg = argv[d->optind++];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (print_errors)
|
||||||
|
fprintf (stderr,
|
||||||
|
_("%s: option '%s%s' requires an argument\n"),
|
||||||
|
argv[0], prefix, pfound->name);
|
||||||
|
|
||||||
|
d->optopt = pfound->val;
|
||||||
|
return optstring[0] == ':' ? ':' : '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (longind != NULL)
|
||||||
|
*longind = option_index;
|
||||||
|
if (pfound->flag)
|
||||||
|
{
|
||||||
|
*(pfound->flag) = pfound->val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return pfound->val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize internal data upon the first call to getopt. */
|
/* Initialize internal data upon the first call to getopt. */
|
||||||
|
@ -56,7 +56,7 @@ do_test (void)
|
|||||||
printf ("message = \"%s\"\n", line);
|
printf ("message = \"%s\"\n", line);
|
||||||
|
|
||||||
static const char expected[] = "\
|
static const char expected[] = "\
|
||||||
program: option '--on' is ambiguous; possibilities: '--one' '--onto' '--one-one'\n";
|
program: option '--on' is ambiguous; possibilities: '--one' '--one-one' '--onto'\n";
|
||||||
|
|
||||||
return c != '?' || strcmp (line, expected) != 0;
|
return c != '?' || strcmp (line, expected) != 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user