summaryrefslogtreecommitdiff
path: root/libshouldbeinlibc/argp-parse.c
diff options
context:
space:
mode:
authorMiles Bader <miles@gnu.org>1996-02-14 19:44:27 +0000
committerMiles Bader <miles@gnu.org>1996-02-14 19:44:27 +0000
commitcd85bf05f3bbbd7555c89cf51c542047e452480b (patch)
tree7540ceb6f9a04c5d3ee3e4395ed138dc5c8fd363 /libshouldbeinlibc/argp-parse.c
parent05b25741421eb109e287e69ce8b66c8d2ba75b67 (diff)
(argp_parse):
Don't parse args in order by default. Deal correctly when the user turns a non-option arg into an option in re-ordering mode. Honor ARGP_NO_ARGS. Use KEY_END, KEY_ARG, and QUOTE. (KEY_END): New macro, in place of EOF. (KEY_ARG, QUOTE): New macros.
Diffstat (limited to 'libshouldbeinlibc/argp-parse.c')
-rw-r--r--libshouldbeinlibc/argp-parse.c155
1 files changed, 100 insertions, 55 deletions
diff --git a/libshouldbeinlibc/argp-parse.c b/libshouldbeinlibc/argp-parse.c
index e1f9b305..b9c04b8f 100644
--- a/libshouldbeinlibc/argp-parse.c
+++ b/libshouldbeinlibc/argp-parse.c
@@ -28,7 +28,14 @@
#include "argp.h"
-#define EOF (-1)
+/* What getopt returns after the end of the options. */
+#define KEY_END (-1)
+/* What getopt returns for a non-option argument. */
+#define KEY_ARG 1
+
+/* 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
@@ -146,8 +153,12 @@ error_t
argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
int *end_index)
{
- int opt;
error_t err = 0;
+ /* 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 = 1;
/* If true, then err == EINVAL is a result of a non-option argument failing
to be parsed (which in some cases isn't actually an error). */
int arg_einval = 0;
@@ -164,7 +175,7 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
/* A pointer for people to use for iteration over GROUPS. */
struct group *group;
/* State block supplied to parsing routines. */
- struct argp_state state = { argp, argc, argv, 0, flags, 0 };
+ struct argp_state state = { argp, argc, argv, 0, flags, 0, 0 };
/* Parse the non-option argument ARG, at the current position. Returns
any error, and sets ARG_EINVAL to true if return EINVAL. */
@@ -180,11 +191,15 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
err = (*group->parser)(ARGP_KEY_ARG, val, &state);
}
- if (!err && state.next >= index)
- /* Remember that we successfully processed a non-option
- argument -- but only if the user hasn't gotten tricky and set
- the clock back. */
- (--group)->args_processed++;
+ if (!err)
+ if (state.next >= index)
+ /* Remember that we successfully processed a non-option
+ argument -- but only if the user hasn't gotten tricky and set
+ the clock back. */
+ (--group)->args_processed++;
+ else
+ /* The user wants to reparse some args, give getopt another try. */
+ try_getopt = 1;
if (err == EINVAL)
*arg_einval = 1;
@@ -192,6 +207,40 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
return err;
}
+ /* Parse the option OPT (with argument ARG), at the current position.
+ Returns any error, and sets ARG_EINVAL to true if it was actually an
+ argument and the parser returned EINVAL. */
+ error_t process_opt (int opt, char *val, int *arg_einval)
+ {
+ /* The group key encoded in the high bits; 0 for short opts or
+ group_number + 1 for long opts. */
+ int group_key = opt >> USER_BITS;
+ error_t err = EINVAL; /* until otherwise asserted */
+
+ if (group_key == 0)
+ /* A short option. */
+ {
+ /* By comparing OPT's position in SHORT_OPTS to the various
+ starting positions in each group's SHORT_END field, we can
+ determine which group OPT came from. */
+ char *short_index = index (short_opts, opt);
+ if (short_index)
+ for (group = groups; group < egroup; group++)
+ if (group->short_end > short_index && group->parser)
+ {
+ err = (*group->parser)(opt, optarg, &state);
+ break;
+ }
+ }
+ else
+ /* A long option. We use shifts instead of masking for extracting
+ the user value in order to preserve the sign. */
+ err =
+ (*groups[group_key - 1].parser)(((opt << GROUP_BITS) >> GROUP_BITS),
+ optarg, &state);
+ return err;
+ }
+
if (! (state.flags & ARGP_NO_HELP))
/* Add our own options. */
{
@@ -325,8 +374,8 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
short_opts = short_end = alloca (short_len + 1);
if (state.flags & ARGP_IN_ORDER)
*short_end++ = '-';
- else if (! (state.flags & ARGP_NO_ARGS))
- *short_end++ = '-';
+ else if (state.flags & ARGP_NO_ARGS)
+ *short_end++ = '+';
*short_end = '\0';
long_opts = long_end = alloca ((long_len + 1) * sizeof (struct option));
@@ -341,7 +390,7 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
mutex_lock (&getopt_lock);
/* Tell getopt to initialize. */
- optind = state.next = 0;
+ state.next = 0;
if (state.flags & ARGP_NO_ERRS)
{
@@ -355,60 +404,56 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
opterr = 1; /* Print error messages. */
/* Now use getopt on our coalesced options lists. */
- while ((opt = getopt_long (state.argc, state.argv, short_opts, long_opts, 0)) != EOF)
+ while (! err)
{
- /* The group key encoded in the high bits; 0 for short opts or
- group_number + 1 for long opts. */
- int group_key = opt >> USER_BITS;
+ int opt;
- err = EINVAL; /* until otherwise asserted */
+ if (state.quoted && state.next < state.quoted)
+ /* The next argument pointer has been moved to before the quoted
+ region, so pretend we never saw the quoting `--', and give getopt
+ another chance. If the user hasn't removed it, getopt will just
+ process it again. */
+ state.quoted = 0;
- state.next = optind; /* Store OPTIND in STATE while calling user
- functions. */
-
- if (opt == 1)
- /* A non-option argument; try each parser in turn. */
- err = process_arg (optarg, &arg_einval);
- else if (group_key == 0)
- /* A short option. */
+ if (try_getopt && !state.quoted)
+ /* Give getopt a chance to parse this. */
{
- /* By comparing OPT's position in SHORT_OPTS to the various
- starting positions in each group's SHORT_END field, we can
- determine which group OPT came from. */
- char *short_index = index (short_opts, opt);
- if (short_index)
- for (group = groups; group < egroup; group++)
- if (group->short_end > short_index && group->parser)
- {
- err = (*group->parser)(opt, optarg, &state);
- break;
- }
+ optind = state.next; /* Put it back in OPTIND for getopt. */
+ opt = getopt_long (state.argc, state.argv, short_opts, long_opts, 0);
+ state.next = optind; /* And see what getopt did. */
+ if (opt == KEY_END)
+ /* Getopt says there are no more options, so stop using getopt. */
+ {
+ try_getopt = 0;
+ if (state.next > 1
+ && strcmp (state.argv[state.next - 1], QUOTE) == 0)
+ /* Not only is this the end of the options, but it's a
+ `quoted' region, which may have args that *look* like
+ options, so we definitely shouldn't try to use getopt past
+ here, whatever happens. */
+ state.quoted = state.next;
+ }
}
else
- /* A long option. We use shifts instead of masking for extracting
- the user value in order to preserve the sign. */
- err =
- (*groups[group_key - 1].parser)(((opt << GROUP_BITS) >> GROUP_BITS),
- optarg, &state);
-
- optind = state.next; /* Put it back in OPTIND for getopt. */
-
- if (err)
- break;
- }
-
- if (opt == EOF)
- {
- state.next = optind; /* Only update NEXT if getopt just failed. */
-
- /* Now process any non-option arguments that getopt didn't handle. */
- while (!err && state.next < state.argc)
- err = process_arg (state.argv[state.next++], &arg_einval);
+ opt = KEY_END;
+
+ if (opt == KEY_END)
+ /* We're past what getopt considers the options. */
+ if (state.next >= state.argc || (state.flags & ARGP_NO_ARGS))
+ break; /* done */
+ else
+ /* A non-option arg. */
+ err = process_arg (state.argv[state.next++], &arg_einval);
+ else if (opt == KEY_ARG)
+ /* A non-option argument; try each parser in turn. */
+ err = process_arg (optarg, &arg_einval);
+ else
+ err = process_opt (opt, optarg, &arg_einval);
}
mutex_unlock (&getopt_lock);
- if (! err)
+ if (state.next == state.argc)
/* We successfully parsed all arguments! Call all the parsers again,
just a few more times... */
{