diff options
-rw-r--r-- | libshouldbeinlibc/argp-parse.c | 104 |
1 files changed, 59 insertions, 45 deletions
diff --git a/libshouldbeinlibc/argp-parse.c b/libshouldbeinlibc/argp-parse.c index e80a8f41..72d2ea80 100644 --- a/libshouldbeinlibc/argp-parse.c +++ b/libshouldbeinlibc/argp-parse.c @@ -92,6 +92,24 @@ find_long_option (struct option *long_options, const char *name) /* Used to regulate access to the getopt routines, which are non-reentrant. */ static struct mutex getopt_lock = MUTEX_INITIALIZER; +/* The state of a `group' during parsing. Each group corresponds to a + particular argp structure from the tree of such descending from the top + level argp passed to argp_parse. */ +struct group +{ + /* This group's parsing function. */ + argp_parser_t parser; + + /* 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; + + /* True if this group has successfully processed a non-option argument; + used to determine who to call with ARGP_KEY_NO_ARGS. */ + int processed_arg; +}; + /* Parse the options strings in ARGC & ARGV according to the argp in ARGP. FLAGS is one of the ARGP_ flags above. If OPTIND is non-NULL, the index in ARGV of the first unparsed option is returned in @@ -102,25 +120,19 @@ error_t argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, int *end_index) { - int opt; - /* The number of separate options groups (each from a different argp). */ - unsigned num_groups = 0; + error_t err = 0; /* SHORT_OPTS is the getopt short options string for the union of all the groups of options. */ char *short_opts; - /* GROUP_SHORT_ENDS is an array pointing to the point in SHORT_OPTS - corresponding to the end of the short options for each different group - of options. We use it to determine from which group a particular short - options is from. */ - char **group_short_ends; - /* The parsing function for each group. */ - argp_parser_t *group_parsers; /* LONG_OPTS is the array of getop long option structures for the union of all the groups of options. */ struct option *long_opts; + /* States of the various parsing groups. */ + struct group *groups; /* State block supplied to parsing routines. */ - struct argp_state state = { argp, argc, argv, 0, flags, 0 }; - error_t err = 0; + struct argp_state state = { argp, argc, argv, 0, flags }; + int opt; + struct group *group; if (! (flags & ARGP_NO_HELP)) /* Add our own options. */ @@ -143,9 +155,10 @@ argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, /* Find the merged set of getopt options, with keys appropiately prefixed. */ { char *short_end; - int short_len = (flags & ARGP_NO_ARGS) ? 0 : 1; + unsigned short_len = (flags & ARGP_NO_ARGS) ? 0 : 1; struct option *long_end; - int long_len = 0; + unsigned long_len = 0; + unsigned num_groups = 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 @@ -170,14 +183,11 @@ argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, calc_lengths (*parents++); } - /* Converts all options in ARGP (which has group number GROUP) and - ancestors into getopt options stored in SHORT_OPTS and LONG_OPTS; - SHORT_END and LONG_END are the points at which new options are added. - Each group's entry in GROUP_PARSERS is set to point to it's parser, - and its entry in GROUP_SHORT_ENDS is set to the point in SHORT_OPTS at - which that groups short options start. Returns the next unused group - number. */ - unsigned convert_options (struct argp *argp, unsigned group) + /* 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 + LONG_END are the points at which new options are added. Returns the + next unused group entry. */ + struct group *convert_options (struct argp *argp, struct group *group) { /* REAL is the most recent non-alias value of OPT. */ struct argp_option *opt, *real; @@ -221,15 +231,17 @@ argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, the high 8 bits in all his values (the sign of the lower bits is preserved however)... */ long_end->val = - (opt->key & USER_MASK) + ((group + 1) << USER_BITS); + (opt->key & USER_MASK) + + (((group - groups) + 1) << USER_BITS); /* Keep the LONG_OPTS list terminated. */ (++long_end)->name = NULL; } } - group_parsers[group] = argp->parser; - group_short_ends[group] = short_end; + group->parser = argp->parser; + group->short_end = short_end; + group->processed_arg = 0; group++; @@ -252,10 +264,10 @@ argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, long_opts = long_end = alloca ((long_len + 1) * sizeof (struct option)); long_end->name = NULL; - group_parsers = alloca (num_groups * sizeof (argp_parser_t)); - group_short_ends = alloca (num_groups * sizeof (char *)); + groups = alloca ((num_groups + 1) * sizeof (struct group)); - convert_options (argp, 0); + group = convert_options (argp, 0); + group->parser = 0; /* Mark the end */ } /* Getopt is (currently) non-reentrant. */ @@ -278,7 +290,9 @@ argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, /* Now use getopt on our coalesced options lists. */ while ((opt = getopt_long (state.argc, state.argv, short_opts, long_opts, 0)) != EOF) { - int group = opt >> USER_BITS; /* GROUP here is actually + 1 */ + /* 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; err = EINVAL; /* until otherwise asserted */ @@ -288,8 +302,8 @@ argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, if (opt == 1) /* A non-option argument; try each parser in turn. */ { - for (group = 0; group < num_groups && err == EINVAL; group++) - err = (*group_parsers[group])(ARGP_KEY_ARG, optarg, &state); + for (group = groups; group->parser && err == EINVAL; group++) + err = (*group->parser)(ARGP_KEY_ARG, optarg, &state); if (err == EINVAL) /* No parser understood this argument, return immediately. */ { @@ -303,27 +317,28 @@ argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, /* Remember that we successfully processed a non-option argument -- but only if the user hasn't gotten tricky and set the clock back. */ - state.processed_arg = 1; + (--group)->processed_arg = 1; } - else if (group == 0) + else if (group_key == 0) /* A short option. */ { /* By comparing OPT's position in SHORT_OPTS to the various - starting positions in GROUP_SHORT_ENDS, we can determine which - group OPT came from. */ + 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 = 0; group < num_groups; group++) - if (group_short_ends[group] > short_index) + for (group = groups; group->parser; group++) + if (group->short_end > short_index) { - err = (*group_parsers[group])(opt, optarg, &state); + 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 = (*group_parsers[group - 1])(((opt << GROUP_BITS) >> GROUP_BITS), + err = + (*groups[group_key - 1].parser)(((opt << GROUP_BITS) >> GROUP_BITS), optarg, &state); optind = state.index; /* Put it back in OPTIND for getopt. */ @@ -338,12 +353,11 @@ argp_parse (struct argp *argp, int argc, char **argv, unsigned flags, /* We successfully parsed all arguments! Call all the parsers again, just a few more times... */ { - int group; - if (!state.processed_arg) - for (group = 0; group < num_groups && (!err || err == EINVAL); group++) - err = (*group_parsers[group])(ARGP_KEY_NO_ARGS, 0, &state); - for (group = 0; group < num_groups && (!err || err == EINVAL); group++) - err = (*group_parsers[group])(ARGP_KEY_END, 0, &state); + for (group = groups; group->parser && (!err || err == EINVAL); group++) + if (!group->processed_arg) + err = (*group->parser)(ARGP_KEY_NO_ARGS, 0, &state); + for (group = groups; group->parser && (!err || err == EINVAL); group++) + err = (*group->parser)(ARGP_KEY_END, 0, &state); if (err == EINVAL) /* EINVAL here just means that ARGP_KEY_END wasn't understood. */ err = 0; |