summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiles Bader <miles@gnu.org>1996-03-19 00:41:48 +0000
committerMiles Bader <miles@gnu.org>1996-03-19 00:41:48 +0000
commit5d061e38326571466c8968abca5b3278d857c4fa (patch)
treeeb75af3e09b2e723812843fc80caba3fde59fa5b
parent22068021234734f63d18751905bd239fff7fdee0 (diff)
(struct group): Add PARENT, PARENT_INDEX, HOOK, and CHILD_HOOKS fields.
(argp_parse): Add HOOK argument. Implement passing hook values to parsers, and propagating them between parents and children. When printing `too many arguments', test ARGP_NO_ERRS, not ARGP_NO_HELP. (argp_default_options): Add --usage option. (argp_default_parser): Use argp_state_help, so we don't need to screw with exit options anymore. Add usage option.
-rw-r--r--libshouldbeinlibc/argp-parse.c161
1 files changed, 125 insertions, 36 deletions
diff --git a/libshouldbeinlibc/argp-parse.c b/libshouldbeinlibc/argp-parse.c
index 386fe3ab..cb42064c 100644
--- a/libshouldbeinlibc/argp-parse.c
+++ b/libshouldbeinlibc/argp-parse.c
@@ -47,10 +47,12 @@
/* ---------------------------------------------------------------- */
#define OPT_PROGNAME -2
+#define OPT_USAGE -3
static const struct argp_option argp_default_options[] =
{
- {"help", '?', 0, 0, "Give this help list", -1},
+ {"help", '?', 0, 0, "Give this help list", -1},
+ {"usage", OPT_USAGE, 0, 0, "Give a short usage message"},
{"program-name",OPT_PROGNAME,"NAME", OPTION_HIDDEN, "Set the program name"},
{0, 0}
};
@@ -58,13 +60,13 @@ static const struct argp_option argp_default_options[] =
static error_t
argp_default_parser (int key, char *arg, struct argp_state *state)
{
- unsigned usage_flags = ARGP_HELP_STD_HELP;
switch (key)
{
case '?':
- if (state->flags & ARGP_NO_EXIT)
- usage_flags &= ~ARGP_HELP_EXIT;
- argp_help (state->argp, stdout, usage_flags);
+ argp_state_help (state, stdout, ARGP_HELP_STD_HELP);
+ break;
+ case OPT_USAGE:
+ argp_state_help (state, stdout, ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
break;
case OPT_PROGNAME: /* Set the program name. */
@@ -76,7 +78,7 @@ argp_default_parser (int key, char *arg, struct argp_state *state)
program_invocation_short_name = program_invocation_name;
if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS))
- == (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS))
+ == ARGP_PARSE_ARGV0)
state->argv[0] = arg; /* Update what getopt uses too. */
break;
@@ -140,6 +142,14 @@ struct group
/* The number of non-option args sucessfully handled by this parser. */
unsigned args_processed;
+
+ /* This group's parser's parent's group. */
+ struct group *parent;
+ unsigned parent_index; /* And the our position in the parent. */
+
+ /* These fields are swapped into and out of the state structure when
+ calling this group's parser. */
+ void *hook, **child_hooks;
};
/* Parse the options strings in ARGC & ARGV according to the argp in
@@ -150,7 +160,7 @@ struct group
returned. */
error_t
argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
- int *end_index)
+ int *end_index, void **hook)
{
error_t err = 0;
/* True if we think using getopt is still useful; if false, then
@@ -173,8 +183,29 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
struct group *egroup;
/* A pointer for people to use for iteration over GROUPS. */
struct group *group;
+ /* An vector containing storage for the CHILD_HOOKS field in all groups. */
+ void **child_hooks;
/* State block supplied to parsing routines. */
- struct argp_state state = { argp, argc, argv, 0, flags, 0, 0 };
+ struct argp_state state = { argp, argc, argv, 0, flags, 0, 0, 0, 0 };
+
+ /* Call GROUP's parser with KEY and ARG, swapping any group-specific info
+ from STATE before calling, and back into state afterwards. If GROUP has
+ no parser, EINVAL is returned. */
+ error_t group_parse (struct group *group, int key, char *arg)
+ {
+ if (group->parser)
+ {
+ error_t err;
+ state.hook = group->hook;
+ state.child_hooks = group->child_hooks;
+ state.arg_num = group->args_processed;
+ err = (*group->parser)(key, arg, &state);
+ group->hook = state.hook;
+ return err;
+ }
+ else
+ return EINVAL;
+ }
/* Parse the non-option argument ARG, at the current position. Returns
any error, and sets ARG_EINVAL to true if return EINVAL. */
@@ -184,11 +215,7 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
error_t err = EINVAL;
for (group = groups; group < egroup && err == EINVAL; group++)
- if (group->parser)
- {
- state.arg_num = group->args_processed;
- err = (*group->parser)(ARGP_KEY_ARG, val, &state);
- }
+ err = group_parse (group, ARGP_KEY_ARG, val);
if (!err)
if (state.next >= index)
@@ -225,9 +252,9 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
char *short_index = index (short_opts, opt);
if (short_index)
for (group = groups; group < egroup; group++)
- if (group->short_end > short_index && group->parser)
+ if (group->short_end > short_index)
{
- err = (*group->parser)(opt, optarg, &state);
+ err = group_parse (group, opt, optarg);
break;
}
}
@@ -249,7 +276,7 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
/* TOP_ARGP has no options, it just serves to group the user & default
argps. */
bzero (top_argp, sizeof (*top_argp));
- top_argp->parents = plist;
+ top_argp->children = plist;
plist[0] = state.argp;
plist[1] = &argp_default_argp;
@@ -265,6 +292,7 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
struct option *long_end;
unsigned long_len = 0;
unsigned num_groups = 0;
+ unsigned num_child_hooks = 0;
/* For ARGP, increments NUM_GROUPS by the total number of argp structures
descended from it, and SHORT_LEN & LONG_LEN by the maximum lengths of
@@ -272,7 +300,7 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
array, respectively. */
void calc_lengths (const struct argp *argp)
{
- const struct argp **parents = argp->parents;
+ const struct argp **children = argp->children;
const struct argp_option *opt = argp->options;
if (opt || argp->parser)
@@ -288,9 +316,12 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
}
}
- if (parents)
- while (*parents)
- calc_lengths (*parents++);
+ if (children)
+ while (*children)
+ {
+ calc_lengths (*children++);
+ num_child_hooks++;
+ }
}
/* Converts all options in ARGP (which is put in GROUP) and ancestors
@@ -298,11 +329,12 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
LONG_END are the points at which new options are added. Returns the
next unused group entry. */
struct group *convert_options (const struct argp *argp,
+ struct group *parent, unsigned parent_index,
struct group *group)
{
/* REAL is the most recent non-alias value of OPT. */
const struct argp_option *real = argp->options;
- const struct argp **parents = argp->parents;
+ const struct argp **children = argp->children;
if (real || argp->parser)
{
@@ -357,13 +389,32 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
group->parser = argp->parser;
group->short_end = short_end;
group->args_processed = 0;
+ group->parent = parent;
+ group->parent_index = parent_index;
+ group->hook = 0;
+ group->child_hooks = 0;
+
+ if (children)
+ /* Assign GROUP's CHILD_HOOKS field a slice from CHILD_HOOKS. */
+ {
+ unsigned num_children = 0;
+ while (children[num_children])
+ num_children++;
+ group->child_hooks = child_hooks;
+ child_hooks += num_children;
+ }
- group++;
+ parent = group++;
}
+ else
+ parent = 0;
- if (parents)
- while (*parents)
- group = convert_options (*parents++, group);
+ if (children)
+ {
+ unsigned index = 0;
+ while (*children)
+ group = convert_options (*children++, parent, index++, group);
+ }
return group;
}
@@ -381,10 +432,24 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
long_end->name = NULL;
groups = alloca ((num_groups + 1) * sizeof (struct group));
+ child_hooks = alloca (num_child_hooks * sizeof (void *));
- egroup = convert_options (state.argp, groups);
+ egroup = convert_options (state.argp, 0, 0, groups);
}
+ /* Call each parser for the first time, giving it a chance to propagate
+ values to child parsers. */
+ groups->hook = hook ? *hook : 0;
+ for (group = groups ; group < egroup && (!err || err == EINVAL); group++)
+ {
+ if (group->parent)
+ /* If is a child parser, get its initial hook value from the parent. */
+ group->hook = group->parent->child_hooks[group->parent_index];
+ err = group_parse (group, ARGP_KEY_INIT, 0);
+ }
+ if (err == EINVAL)
+ err = 0; /* Some parser didn't understand. */
+
/* Getopt is (currently) non-reentrant. */
mutex_lock (&getopt_lock);
@@ -473,11 +538,10 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
just a few more times... */
{
for (group = groups; group < egroup && (!err || err ==EINVAL); group++)
- if (group->args_processed == 0 && group->parser)
- err = (*group->parser)(ARGP_KEY_NO_ARGS, 0, &state);
+ if (group->args_processed == 0)
+ err = group_parse (group, ARGP_KEY_NO_ARGS, 0);
for (group = groups; group < egroup && (!err || err ==EINVAL); group++)
- if (group->parser)
- err = (*group->parser)(ARGP_KEY_END, 0, &state);
+ err = group_parse (group, ARGP_KEY_END, 0);
if (err == EINVAL)
err = 0; /* Some parser didn't understand. */
}
@@ -487,18 +551,43 @@ argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
else
/* No way to return the remaining arguments, they must be bogus. */
{
- if (! (state.flags & ARGP_NO_HELP))
+ if (! (state.flags & ARGP_NO_ERRS))
fprintf (stderr, "%s: Too many arguments\n",
program_invocation_name);
err = EINVAL;
}
- if (err && !(state.flags & ARGP_NO_HELP))
+ /* Okay, we're all done, with either an error or success. We only call the
+ parsers once more, to indicate which one. */
+
+ if (err)
{
- unsigned usage_flags = ARGP_HELP_STD_ERR;
- if (state.flags & ARGP_NO_EXIT)
- usage_flags &= ~ARGP_HELP_EXIT;
- argp_help (state.argp, stderr, usage_flags);
+ /* Maybe print an error message. */
+ argp_state_help (&state, stderr, ARGP_HELP_STD_ERR);
+
+ /* Since we didn't exit, give each parser an error indication. */
+ for (group = groups; group < egroup; group++)
+ group_parse (group, ARGP_KEY_ERROR, 0);
+ }
+ else
+ /* Do final cleanup, including propagating back values from parsers. */
+ {
+ /* We pass over the groups in reverse order so that child groups are
+ given a chance to do there processing before passing back a value to
+ the parent. */
+ for (group = egroup - 1
+ ; group >= groups && (!err || err == EINVAL)
+ ; group--)
+ {
+ err = group_parse (group, ARGP_KEY_SUCCESS, 0);
+ if (group->parent)
+ /* Pass back any value from the child to its parent. */
+ group->parent->child_hooks[group->parent_index] = group->hook;
+ }
+ if (err == EINVAL)
+ err = 0; /* Some parser didn't understand. */
+ if (!err && hook)
+ *hook = groups->hook; /* Return the final value to the user. */
}
return err;