summaryrefslogtreecommitdiff
path: root/libshouldbeinlibc/argp-parse.c
diff options
context:
space:
mode:
authorMiles Bader <miles@gnu.org>1995-10-10 19:57:09 +0000
committerMiles Bader <miles@gnu.org>1995-10-10 19:57:09 +0000
commit0b41d011be095d7036e3c4eab1fe11c2bf1a17bd (patch)
treed389efaab35a6344fecb146cf63f25092f9521c8 /libshouldbeinlibc/argp-parse.c
parent8043afef8feb939e72a55836ef4262599a6d7dca (diff)
Initial revision
Diffstat (limited to 'libshouldbeinlibc/argp-parse.c')
-rw-r--r--libshouldbeinlibc/argp-parse.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/libshouldbeinlibc/argp-parse.c b/libshouldbeinlibc/argp-parse.c
new file mode 100644
index 00000000..dd42b72d
--- /dev/null
+++ b/libshouldbeinlibc/argp-parse.c
@@ -0,0 +1,356 @@
+/* Hierarchial argument parsing, layered over getopt
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h> /* for CHAR_BIT */
+#include <getopt.h>
+#include <cthreads.h>
+
+#include "argp.h"
+
+#define EOF (-1)
+
+/* 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)
+
+/* ---------------------------------------------------------------- */
+
+#define OPT_HELP -1
+
+static struct argp_option argp_default_options[] =
+{
+ {"help", OPT_HELP, 0, 0, "Give this help list"},
+ {0, 0}
+};
+
+static error_t
+argp_default_parser (int key, char *arg, struct argp_state *state)
+{
+ unsigned usage_flags = ARGP_USAGE_STD_HELP;
+ switch (key)
+ {
+ case OPT_HELP:
+ if (state->flags & ARGP_NO_EXIT)
+ usage_flags &= ~(ARGP_USAGE_EXIT_OK | ARGP_USAGE_EXIT_ERR);
+ argp_usage (state->argp, stdout, usage_flags);
+ return 0;
+ default:
+ return EINVAL;
+ }
+}
+
+static struct argp argp_default_argp =
+ {argp_default_options, &argp_default_parser};
+
+
+/* ---------------------------------------------------------------- */
+
+/* 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;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Used to regulate access to the getopt routines, which are non-reentrant. */
+static struct mutex getopt_lock = MUTEX_INITIALIZER;
+
+/* 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
+ it. If an unknown option is present, EINVAL is returned; if some parser
+ routine returned a non-zero value, it is returned; otherwise 0 is
+ returned. */
+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;
+ /* 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;
+ /* State block supplied to parsing routines. */
+ struct argp_state state = { argp, argc, argv, 0, flags };
+ error_t err = 0;
+
+ if (! (flags & ARGP_NO_HELP))
+ /* Add our own options. */
+ {
+ struct argp **plist = alloca (3 * sizeof (struct argp *));
+ struct argp *top_argp = alloca (sizeof (struct argp));
+
+ /* TOP_ARGP has no options, it just serves to group the user & default
+ argps. */
+ bzero (top_argp, sizeof (*top_argp));
+ top_argp->parents = plist;
+
+ plist[0] = argp;
+ plist[1] = &argp_default_argp;
+ plist[2] = 0;
+
+ argp = top_argp;
+ }
+
+ /* Find the merged set of getopt options, with keys appropiately prefixed. */
+ {
+ char *short_end;
+ int short_len = (flags & ARGP_NO_ARGS) ? 0 : 1;
+ struct option *long_end;
+ int long_len = 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
+ the resulting merged getopt short options string and long-options
+ array, respectively. */
+ void calc_lengths (struct argp *argp)
+ {
+ int num_opts = 0;
+ struct argp **parents = argp->parents;
+ struct argp_option *opt = argp->options;
+
+ num_groups++;
+
+ while (!_option_is_end (opt++))
+ num_opts++;
+
+ short_len += num_opts * 3; /* opt + up to 2 `:'s */
+ long_len += num_opts;
+
+ if (parents)
+ while (*parents)
+ 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)
+ {
+ /* REAL is the most recent non-alias value of OPT. */
+ struct argp_option *opt, *real;
+ struct argp **parents = argp->parents;
+
+ for (opt = argp->options, real = opt; ! _option_is_end (opt); opt++)
+ /* Only add the long option OPT if it hasn't been already. */
+ if (find_long_option (long_opts, opt->name) < 0)
+ {
+ if (! (opt->flags & OPTION_ALIAS))
+ /* OPT isn't an alias, so we can use values from it. */
+ real = opt;
+
+ if (_option_is_short (opt))
+ /* OPT can be used as a short option. */
+ {
+ *short_end++ = opt->key;
+ if (real->arg)
+ {
+ *short_end++ = ':';
+ if (! (real->flags & OPTION_ARG_OPTIONAL))
+ *short_end++ = ':';
+ }
+ *short_end = '\0'; /* keep 0 terminated */
+ }
+
+ if (opt->name)
+ /* OPT can be used as a long option. */
+ {
+ long_end->name = opt->name;
+ long_end->has_arg =
+ (real->arg
+ ? (real->flags & OPTION_ARG_OPTIONAL
+ ? optional_argument
+ : required_argument)
+ : no_argument);
+ 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)... */
+ long_end->val =
+ (opt->key & USER_MASK) + ((group + 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++;
+
+ if (parents)
+ while (*parents)
+ group = convert_options (*parents++, group);
+
+ return group;
+ }
+
+ calc_lengths (argp);
+
+ short_opts = short_end = alloca (short_len + 1);
+ if (flags & ARGP_IN_ORDER)
+ *short_end++ = '-';
+ else if (! (flags & ARGP_NO_ARGS))
+ *short_end++ = '-';
+ *short_end = '\0';
+
+ 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 *));
+
+ convert_options (argp, 0);
+ }
+
+ /* Getopt is (currently) non-reentrant. */
+ mutex_lock (&getopt_lock);
+
+ /* Tell getopt to initialize. */
+ optind = state.index = 0;
+
+ if (flags & ARGP_NO_ERRS)
+ {
+ opterr = 0;
+ if (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. */
+ argv--, argc++;
+ }
+ else
+ 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)
+ {
+ int group = opt >> USER_BITS; /* GROUP here is actually + 1 */
+
+ err = EINVAL; /* until otherwise asserted */
+
+ state.index = optind; /* Store OPTIND in STATE while calling user
+ functions. */
+
+ 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);
+ if (err == EINVAL)
+ /* No parser understood this argument, return immediately. */
+ {
+ if (end_index)
+ /* As long as there's some way for the user to deal with the
+ remaining arguments, don't complain. */
+ err = 0;
+ break;
+ }
+ }
+ else if (group == 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. */
+ char *short_index = index (short_opts, opt);
+ if (short_index)
+ for (group = 0; group < num_groups; group++)
+ if (group_short_ends[group] > short_index)
+ {
+ err = (*group_parsers[group])(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),
+ optarg, &state);
+
+ optind = state.index; /* Put it back in OPTIND for getopt. */
+
+ if (err)
+ break;
+ }
+
+ mutex_unlock (&getopt_lock);
+
+ if (!err && !state.argv[state.index])
+ /* We successfully parsed all arguments! Call all the parsers again,
+ just one last time... */
+ {
+ int group;
+ for (group = 0; group < num_groups && (!err || err == EINVAL); group++)
+ err = (*group_parsers[group])(ARGP_KEY_END, 0, &state);
+ if (err == EINVAL)
+ /* EINVAL here just means that ARGP_KEY_END wasn't understood. */
+ err = 0;
+ }
+
+ if (end_index)
+ *end_index = state.index;
+
+ if (err && !(state.flags & ARGP_NO_HELP))
+ {
+ unsigned usage_flags = ARGP_USAGE_STD;
+ if (state.flags & ARGP_NO_EXIT)
+ usage_flags &= ~(ARGP_USAGE_EXIT_OK | ARGP_USAGE_EXIT_ERR);
+ argp_usage (argp, stderr, usage_flags);
+ }
+
+ return err;
+}