Commit b04e81f6 authored by Niels Möller's avatar Niels Möller

* src/argp/argp-parse.c: Deleted getopt-related code.

(struct parser): New field nextchar. Deleted fields try_getotp and long_opts.
(find_short_option): New function.
(match_option): New function.
(find_long_option): New function.
(struct parser_convert_state): Deleted field long_end.
(convert_options): Don't build getopt_long-style option array.
(parser_convert): Deleted FLAGS arument.
(struct parser_sizes): Deleted field long_len.
(parser_init): Set short_opts to NULL, unless ARGP_LONG_ONLY is
used.
(classify_arg): New function.
(parser_parse_next): Don't use getopt_long().

Rev: src/argp/argp-parse.c:1.8
parent 5de3f8af
......@@ -18,6 +18,10 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
......@@ -26,7 +30,7 @@
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <getopt.h>
#include <assert.h>
#ifndef UNUSED
# if __GNUC__ >= 2
......@@ -62,36 +66,17 @@
#endif
#endif /* _LIBC */
/* FIXME: Where are these defined??? /nisse */
#if HAVE_PROGRAM_INVOCATION_NAME
extern const char *program_invocation_name;
#endif
#if HAVE_PROGRAM_INVOCATION_SHORT_NAME
extern const char *program_short_name;
#endif
#include "argp.h"
#include "argp-namefrob.h"
/* Getopt return values. */
#define KEY_END (-1) /* The end of the options. */
#define KEY_ARG 1 /* A non-option argument. */
#define KEY_ERR '?' /* An error parsing the options. */
/* The meta-argument used to prevent any further arguments being interpreted
as options. */
#define QUOTE "--"
/* The number of bits we steal in a long-option value for our own use. */
#define GROUP_BITS CHAR_BIT
/* The number of bits available for the user value. */
#define USER_BITS ((sizeof ((struct option *)0)->val * CHAR_BIT) - GROUP_BITS)
#define USER_MASK ((1 << USER_BITS) - 1)
/* EZ alias for ARGP_ERR_UNKNOWN. */
#define EBADKEY ARGP_ERR_UNKNOWN
/* Default options. */
......@@ -201,59 +186,7 @@ argp_version_parser (int key, char *arg UNUSED, struct argp_state *state)
static const struct argp argp_version_argp =
{argp_version_options, &argp_version_parser, NULL, NULL, NULL, NULL, "libc"};
/* Returns the offset into the getopt long options array LONG_OPTIONS of a
long option with called NAME, or -1 if none is found. Passing NULL as
NAME will return the number of options. */
static int
find_long_option (struct option *long_options, const char *name)
{
struct option *l = long_options;
while (l->name != NULL)
if (name != NULL && strcmp (l->name, name) == 0)
return l - long_options;
else
l++;
if (name == NULL)
return l - long_options;
else
return -1;
}
/* If we can, we regulate access to getopt, which is non-reentrant, with a
mutex. Since the case we're trying to guard against is two different
threads interfering, and it's possible that someone might want to call
argp_parse recursively (they're careful), we use a recursive lock if
possible. */
#if _LIBC - 0
__libc_lock_define_initialized_recursive (static, getopt_lock)
#define LOCK_GETOPT __libc_lock_lock_recursive (getopt_lock)
#define UNLOCK_GETOPT __libc_lock_unlock_recursive (getopt_lock)
#else /* !_LIBC */
#ifdef HAVE_CTHREADS_H
static struct mutex getopt_lock = MUTEX_INITIALIZER;
#define LOCK_GETOPT mutex_lock (&getopt_lock)
#define UNLOCK_GETOPT mutex_unlock (&getopt_lock)
#else /* !HAVE_CTHREADS_H */
#define LOCK_GETOPT (void)0
#define UNLOCK_GETOPT (void)0
#endif /* HAVE_CTHREADS_H */
#endif /* _LIBC */
/* This hack to allow programs that know what's going on to call argp
recursively. If someday argp is changed not to use the non-reentrant
getopt interface, we can get rid of this shit. XXX */
void
_argp_unlock_xxx (void)
{
UNLOCK_GETOPT;
}
/* The state of a `group' during parsing. Each group corresponds to a
particular argp structure from the tree of such descending from the top
......@@ -266,11 +199,12 @@ struct group
/* Which argp this group is from. */
const struct argp *argp;
#if 0
/* Points to the point in SHORT_OPTS corresponding to the end of the short
options for this group. We use it to determine from which group a
particular short options is from. */
char *short_end;
#endif
/* The number of non-option args sucessfully handled by this parser. */
unsigned args_processed;
......@@ -309,32 +243,137 @@ struct parser
{
const struct argp *argp;
/* SHORT_OPTS is the getopt short options string for the union of all the
groups of options. */
/* String of all recognized short options. Needed for ARGP_LONG_ONLY. */
char *short_opts;
/* LONG_OPTS is the array of getop long option structures for the union of
all the groups of options. */
struct option *long_opts;
/* For parsing combined short options. */
char *nextchar;
/* States of the various parsing groups. */
struct group *groups;
/* The end of the GROUPS array. */
struct group *egroup;
/* An vector containing storage for the CHILD_INPUTS field in all groups. */
void **child_inputs;
/* True if we think using getopt is still useful; if false, then
remaining arguments are just passed verbatim with ARGP_KEY_ARG. This is
cleared whenever getopt returns KEY_END, but may be set again if the user
moves the next argument pointer backwards. */
int try_getopt;
/* State block supplied to parsing routines. */
struct argp_state state;
/* Memory used by this parser. */
void *storage;
};
/* Search for a group defining a short option. */
static const struct argp_option *
find_short_option(struct parser *parser, int key, struct group **p)
{
struct group *group;
assert(key >= 0);
assert(isascii(key));
for (group = parser->groups; group < parser->egroup; group++)
{
const struct argp_option *opts;
for (opts = group->argp->options; !__option_is_end(opts); opts++)
if (opts->key == key)
{
*p = group;
return opts;
}
}
return NULL;
}
enum match_result { MATCH_EXACT, MATCH_PARTIAL, MATCH_NO };
/* Matches an encountern long-option argument ARG against an option NAME.
* ARG is terminated by NUL or '='.
*
* For partial matches, *QUALITY is set to the length of the common
* prefix. */
static enum match_result
match_option(const char *arg, const char *name,
unsigned *quality)
{
unsigned i;
for (i = 0;; i++)
{
if ( !arg[i] || (arg[i] == '='))
{
if (!name[i])
return MATCH_EXACT;
}
else if (!name[i])
/* The argument is longer than the option name */
return MATCH_NO;
else if (!arg[i] != name[i])
{
*quality = i;
return MATCH_PARTIAL;
}
}
}
static const struct argp_option *
find_long_option(struct parser *parser,
const char *arg,
struct group **p)
{
struct group *group;
/* Best match found so far. */
unsigned best_match = 0; /* Match length */
struct group *best_group = NULL;
const struct argp_option *best_option = NULL;
/* True if we get several "best" matches. (A best_match of zero is
always ambigous). */
int ambiguous = 1;
for (group = parser->groups; group < parser->egroup; group++)
{
const struct argp_option *opts;
for (opts = group->argp->options; !__option_is_end(opts); opts++)
{
unsigned prefix;
if (!opts->name)
continue;
switch (match_option(arg, opts->name, &prefix))
{
case MATCH_NO:
break;
case MATCH_PARTIAL:
if (prefix > best_match)
{
ambiguous = 0;
best_match = prefix;
best_group = group;
best_option = opts;
}
else if (prefix == best_match)
ambiguous = 1;
break;
case MATCH_EXACT:
/* Exact match. */
*p = group;
return opts;
}
}
}
if (!ambiguous)
{
*p = best_group;
return best_option;
}
return NULL;
}
/* The next usable entries in the various parser tables being filled in by
convert_options. */
......@@ -342,91 +381,44 @@ struct parser_convert_state
{
struct parser *parser;
char *short_end;
struct option *long_end;
void **child_inputs_end;
};
/* Converts all options in ARGP (which is put in GROUP) and ancestors
into getopt options stored in SHORT_OPTS and LONG_OPTS; SHORT_END and
CVT->LONG_END are the points at which new options are added. Returns the
next unused group entry. CVT holds state used during the conversion. */
/* Initialize GROUP from ARGP. If CVT->SHORT_END is non-NULL, short
options are recorded in the short options string. Returns the next
unused group entry. CVT holds state used during the conversion. */
static struct group *
convert_options (const struct argp *argp,
struct group *parent, unsigned parent_index,
struct group *group, struct parser_convert_state *cvt)
{
/* REAL is the most recent non-alias value of OPT. */
const struct argp_option *real = argp->options;
const struct argp_option *opt = argp->options;
const struct argp_child *children = argp->children;
if (real || argp->parser)
if (opt || argp->parser)
{
const struct argp_option *opt;
if (real)
for (opt = real; !__option_is_end (opt); opt++)
{
if (! (opt->flags & OPTION_ALIAS))
/* OPT isn't an alias, so we can use values from it. */
real = opt;
if (! (real->flags & OPTION_DOC))
/* A real option (not just documentation). */
{
if (__option_is_short (opt))
/* OPT can be used as a short option. */
{
*cvt->short_end++ = opt->key;
if (real->arg)
{
*cvt->short_end++ = ':';
if (real->flags & OPTION_ARG_OPTIONAL)
*cvt->short_end++ = ':';
}
*cvt->short_end = '\0'; /* keep 0 terminated */
}
if (opt->name
&& find_long_option (cvt->parser->long_opts, opt->name) < 0)
/* OPT can be used as a long option. */
{
cvt->long_end->name = opt->name;
cvt->long_end->has_arg =
(real->arg
? (real->flags & OPTION_ARG_OPTIONAL
? optional_argument
: required_argument)
: no_argument);
cvt->long_end->flag = 0;
/* we add a disambiguating code to all the user's
values (which is removed before we actually call
the function to parse the value); this means that
the user loses use of the high 8 bits in all his
values (the sign of the lower bits is preserved
however)... */
cvt->long_end->val =
((opt->key | real->key) & USER_MASK)
+ (((group - cvt->parser->groups) + 1) << USER_BITS);
/* Keep the LONG_OPTS list terminated. */
(++cvt->long_end)->name = NULL;
}
}
}
/* This parser needs a group. FIXME: Do we really need a group
if argp->parser if NULL? */
if (cvt->short_end)
{
/* Record any short options. */
for ( ; !__option_is_end (opt); opt++)
if (__option_is_short(opt))
*cvt->short_end++ = opt->key;
}
group->parser = argp->parser;
group->argp = argp;
group->short_end = cvt->short_end;
group->args_processed = 0;
group->parent = parent;
group->parent_index = parent_index;
group->input = 0;
group->hook = 0;
group->child_inputs = 0;
if (children)
/* Assign GROUP's CHILD_INPUTS field some space from
CVT->child_inputs_end.*/
CVT->child_inputs_end.*/
{
unsigned num_children = 0;
while (children[num_children].argp)
......@@ -434,7 +426,6 @@ convert_options (const struct argp *argp,
group->child_inputs = cvt->child_inputs_end;
cvt->child_inputs_end += num_children;
}
parent = group++;
}
else
......@@ -450,39 +441,35 @@ convert_options (const struct argp *argp,
return group;
}
/* Find the merged set of getopt options, with keys appropiately prefixed. */
/* Allocate and initialize the group structures, so that they are
ordered as if by traversing the corresponding argp parser tree in
pre-order. Also build the list of short options, if that is needed. */
static void
parser_convert (struct parser *parser, const struct argp *argp, int flags)
parser_convert (struct parser *parser, const struct argp *argp)
{
struct parser_convert_state cvt;
cvt.parser = parser;
cvt.short_end = parser->short_opts;
cvt.long_end = parser->long_opts;
cvt.child_inputs_end = parser->child_inputs;
if (flags & ARGP_IN_ORDER)
*cvt.short_end++ = '-';
else if (flags & ARGP_NO_ARGS)
*cvt.short_end++ = '+';
*cvt.short_end = '\0';
cvt.long_end->name = NULL;
parser->argp = argp;
if (argp)
parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt);
else
parser->egroup = parser->groups; /* No parsers at all! */
if (parser->short_opts)
*cvt.short_end ='\0';
}
/* Lengths of various parser fields which we will allocated. */
struct parser_sizes
{
size_t short_len; /* Getopt short options string. */
size_t long_len; /* Getopt long options vector. */
/* Needed only ARGP_LONG_ONLY */
size_t short_len; /* Number of short options. */
size_t num_groups; /* Group structures we allocate. */
size_t num_child_inputs; /* Child input slots. */
};
......@@ -499,14 +486,12 @@ calc_sizes (const struct argp *argp, struct parser_sizes *szs)
if (opt || argp->parser)
{
/* FIXME: Do we really need a group if argp->parser if NULL? */
szs->num_groups++;
if (opt)
{
int num_opts = 0;
while (!__option_is_end (opt++))
num_opts++;
szs->short_len += num_opts * 3; /* opt + up to 2 `:'s */
szs->long_len += num_opts;
while (__option_is_short (opt++))
szs->short_len++;
}
}
......@@ -527,34 +512,41 @@ parser_init (struct parser *parser, const struct argp *argp,
struct group *group;
struct parser_sizes szs;
szs.short_len = (flags & ARGP_NO_ARGS) ? 0 : 1;
szs.long_len = 0;
szs.short_len = 0;
szs.num_groups = 0;
szs.num_child_inputs = 0;
if (argp)
calc_sizes (argp, &szs);
if (!(flags & ARGP_LONG_ONLY))
/* We have no use for the short option array. */
szs.short_len = 0;
/* Lengths of the various bits of storage used by PARSER. */
#define GLEN (szs.num_groups + 1) * sizeof (struct group)
#define CLEN (szs.num_child_inputs * sizeof (void *))
#define LLEN ((szs.long_len + 1) * sizeof (struct option))
#define SLEN (szs.short_len + 1)
#define STORAGE(offset) ((void *) (((char *) parser->storage) + (offset)))
parser->storage = malloc (GLEN + CLEN + LLEN + SLEN);
parser->storage = malloc (GLEN + CLEN + SLEN);
if (! parser->storage)
return ENOMEM;
parser->groups = parser->storage;
parser->child_inputs = STORAGE(GLEN);
parser->long_opts = STORAGE(GLEN + CLEN);
parser->short_opts = STORAGE(GLEN + CLEN + LLEN);
parser->child_inputs = STORAGE(GLEN);
memset (parser->child_inputs, 0, szs.num_child_inputs * sizeof (void *));
parser_convert (parser, argp, flags);
if (flags & ARGP_LONG_ONLY)
parser->short_opts = STORAGE(GLEN + CLEN);
else
parser->short_opts = NULL;
parser_convert (parser, argp);
memset (&parser->state, 0, sizeof (struct argp_state));
parser->state.root_argp = parser->argp;
parser->state.argc = argc;
parser->state.argv = argv;
......@@ -564,7 +556,10 @@ parser_init (struct parser *parser, const struct argp *argp,
parser->state.next = 0; /* Tell getopt to initialize. */
parser->state.pstate = parser;
parser->nextchar = NULL;
#if 0
parser->try_getopt = 1;
#endif
/* Call each parser for the first time, giving it a chance to propagate
values to child parsers. */
......@@ -593,25 +588,14 @@ parser_init (struct parser *parser, const struct argp *argp,
if (err)
return err;
/* Getopt is (currently) non-reentrant. */
LOCK_GETOPT;
if (parser->state.flags & ARGP_NO_ERRS)
{
opterr = 0;
if (parser->state.flags & ARGP_PARSE_ARGV0)
/* getopt always skips ARGV[0], so we have to fake it out. As long
as OPTERR is 0, then it shouldn't actually try to access it. */
parser->state.argv--, parser->state.argc++;
}
else
opterr = 1; /* Print error messages. */
if (parser->state.argv == argv && argv[0])
if (argv[0] && !(parser->state.flags & ARGP_PARSE_ARGV0))
/* There's an argv[0]; use it for messages. */
{
char *short_name = strrchr (argv[0], '/');
parser->state.name = short_name ? short_name + 1 : argv[0];
/* Don't parse it as an argument. */
parser->state.next = 1;
}
else
{
......@@ -635,8 +619,6 @@ parser_finalize (struct parser *parser,
{
struct group *group;
UNLOCK_GETOPT;
if (err == EBADKEY && arg_ebadkey)
/* Suppress errors generated by unparsed arguments. */
err = 0;
......@@ -768,14 +750,17 @@ parser_parse_arg (struct parser *parser, char *val)
argument -- but only if the user hasn't gotten tricky and set
the clock back. */
(--group)->args_processed += (parser->state.next - index);
#if 0
else
/* The user wants to reparse some args, give getopt another try. */
parser->try_getopt = 1;
#endif
}
return err;
}
#if 0
/* Call the user parsers to parse the option OPT, with argument VAL, at the
current position, returning any error. */
static error_t
......@@ -832,7 +817,63 @@ parser_parse_opt (struct parser *parser, int opt, char *val UNUSED)
return err;
}
#endif
enum arg_type { ARG_ARG, ARG_SHORT_OPTION, ARG_LONG_OPTION, ARG_QUOTE };
static enum arg_type
classify_arg(struct parser *parser, char *arg, char **opt)
{
if (arg[0] == '-')
/* Looks like an option... */
switch (arg[1])
{
case '\0':
/* "-" is not an option. */
return ARG_ARG;
case '-':
/* Long option, or quote. */
if (!arg[2])
return ARG_QUOTE;
/* A long option. */
*opt = arg + 2;
return ARG_LONG_OPTION;
default:
/* Short option. But if ARGP_LONG_ONLY, it can also be a long option. */
*opt = arg + 1;
if (parser->state.flags & ARGP_LONG_ONLY)
{
/* Rules from getopt.c:
If long_only and the ARGV-element has the form "-f",
where f is a valid short option, don't consider it an
abbreviated form of a long option that starts with f.
Otherwise there would be no way to give the -f short
option.
On the other hand, if there's a long option "fubar" and
the ARGV-element is "-fu", do consider that an
abbreviation of the long option, just like "--fu", and
not "-f" with arg "u".
This distinction seems to be the most useful approach. */
assert(parser->short_opts);
if (arg[2] || !strchr(parser->short_opts, arg[1]))
return ARG_LONG_OPTION;
}
return ARG_SHORT_OPTION;
}
else
return ARG_ARG;
}
/* Parse the next argument in PARSER (as indicated by PARSER->state.next).
Any error from the parsers is returned, and *ARGP_EBADKEY indicates
whether a value of EBADKEY is due to an unrecognized argument (which is
......@@ -840,9 +881,6 @@ parser_parse_opt (struct parser *parser, int opt, char *val UNUSED)
static error_t