summaryrefslogtreecommitdiff
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
parent8043afef8feb939e72a55836ef4262599a6d7dca (diff)
Initial revision
-rw-r--r--libshouldbeinlibc/argp-help.c654
-rw-r--r--libshouldbeinlibc/argp-parse.c356
-rw-r--r--libshouldbeinlibc/argp.h216
3 files changed, 1226 insertions, 0 deletions
diff --git a/libshouldbeinlibc/argp-help.c b/libshouldbeinlibc/argp-help.c
new file mode 100644
index 00000000..024c03a9
--- /dev/null
+++ b/libshouldbeinlibc/argp-help.c
@@ -0,0 +1,654 @@
+/* Hierarchial argument parsing help output
+
+ 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 <assert.h>
+#include <stdarg.h>
+#include <malloc.h>
+#include <ctype.h>
+
+#include <line.h>
+
+#include "argp.h"
+
+#define SHORT_OPT_COL 2 /* column in which short options start */
+#define LONG_OPT_COL 6 /* column in which long options start */
+#define OPT_DOC_COL 29 /* column in which option text starts */
+#define USAGE_INDENT 12 /* indentation of wrapped usage lines */
+#define RMARGIN 78 /* right margin used for wrapping */
+
+/* Returns true if OPT hasn't been marked invisible. Visibility only affects
+ whether OPT is displayed or used in sorting, not option shadowing. */
+#define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN))
+
+/* Returns true if OPT is an alias for an earlier option. */
+#define oalias(opt) ((opt)->flags & OPTION_ALIAS)
+
+/* Returns true if OPT is the end-of-list marker for a list of options. */
+#define oend(opt) _option_is_end (opt)
+
+/* Returns true if OPT has a short option. */
+#define oshort(opt) _option_is_short (opt)
+
+/*
+ The help format for a particular option is like:
+
+ -xARG, -yARG, --long1=ARG, --long2=ARG Documentation...
+
+ Where ARG will be omitted if there's no argument, for this option, or
+ will be surrounded by "[" and "]" appropiately if the argument is
+ optional. The documentation string is word-wrapped appropiately, and if
+ the list of options is long enough, it will be started on a separate line.
+ If there are no short options for a given option, the first long option is
+ indented slighly in a way that's supposed to make most long options appear
+ to be in a separate column.
+
+ For example (from ps):
+
+ -p PID, --pid=PID List the process PID
+ --pgrp=PGRP List processes in the process group PGRP
+ -P, -x, --no-parent Include processes without parents
+ -Q, --all-fields Don't elide unusable fields (normally if there's
+ some reason ps can't print a field for any
+ process, it's removed from the output entirely)
+ -r, --reverse, --gratuitously-long-reverse-option
+ Reverse the order of any sort
+ --session[=SID] Add the processes from the session SID (which
+ defaults to the sid of the current process)
+
+ The struct argp_option array for the above could look like:
+
+ {
+ {"pid", 'p', "PID", 0,
+ "List the process PID"},
+ {"pgrp", OPT_PGRP, "PGRP", 0,
+ "List processes in the process group PGRP"},
+ {"no-parent", 'P', 0, 0,
+ "Include processes without parents"},
+ {0, 'x', 0, OPTION_ALIAS},
+ {"all-fields",'Q', 0, 0,
+ "Don't elide unusable fields (normally if there's some reason ps \
+can't print a field for any process, it's removed from the output entirely)"},
+ {"reverse", 'r', 0, 0,
+ "Reverse the order of any sort"},
+ {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS},
+ {"session", OPT_SESS, "SID", OPTION_ARG_OPTIONAL,
+ "Add the processes from the session SID (which defaults to the sid of \
+the current process)"},
+ }
+
+*/
+
+/* Returns true if CH occurs between BEG and END. */
+static int
+find_char (char ch, char *beg, char *end)
+{
+ while (beg < end)
+ if (*beg == ch)
+ return 1;
+ else
+ beg++;
+ return 0;
+}
+
+struct hol_entry
+{
+ /* First option. */
+ struct argp_option *opt;
+ /* Number of options (including aliases). */
+ unsigned num;
+
+ /* A pointers into the HOL's short_options field, to the first short option
+ letter for this entry. The order of the characters following this point
+ corresponds to the order of options pointed to by OPT, and there are at
+ most NUM. A short option recorded in a option following OPT is only
+ valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's
+ probably been shadowed by some other entry). */
+ char *short_options;
+
+ /* Entries are sorted by their sort_class first, in the order:
+ 1, 2, ..., n, 0, -m, ..., -2, -1
+ and then alphabetically within each class. The default is 0. */
+ int sort_class;
+};
+
+/* A list of options for help. */
+struct hol
+{
+ /* A string containing all short options in this HOL. Each entry contains
+ pointers into this string, so the order can't be messed with blindly. */
+ char *short_options;
+ /* An array of hol_entry's. */
+ struct hol_entry *entries;
+ unsigned num_entries;
+};
+
+/* Create a struct hol from an array of struct argp_option. */
+struct hol *make_hol (struct argp_option *opt)
+{
+ char *so;
+ struct argp_option *o;
+ struct hol_entry *entry;
+ unsigned num_short_options = 0;
+ struct hol *hol = malloc (sizeof (struct hol));
+
+ assert (hol);
+
+ /* The first option must not be an alias. */
+ assert (! oalias (opt));
+
+ hol->num_entries = 0;
+
+ /* Calculate the space needed. */
+ for (o = opt; ! oend (o); o++)
+ {
+ if (! oalias (o))
+ hol->num_entries++;
+ if (oshort (o))
+ num_short_options++; /* This is an upper bound. */
+ }
+
+ hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries);
+ hol->short_options = malloc (num_short_options);
+
+ assert (hol->entries && hol->short_options);
+
+ /* Fill in the entries. */
+ so = hol->short_options;
+ for (o = opt, entry = hol->entries; ! oend (o); entry++)
+ {
+ entry->opt = o;
+ entry->num = 0;
+ entry->short_options = so;
+ entry->sort_class = 0;
+
+ do
+ {
+ entry->num++;
+ if (oshort (o) && ! find_char (o->key, hol->short_options, so))
+ /* O has a valid short option which hasn't already been used. */
+ *so++ = o->key;
+ o++;
+ }
+ while (! oend (o) && oalias (o));
+ }
+
+ return hol;
+}
+
+/* Free HOL and any resources it uses. */
+static void
+hol_free (struct hol *hol)
+{
+ free (hol->entries);
+ free (hol->short_options);
+ free (hol);
+}
+
+static inline int
+hol_entry_short_iterate (const struct hol_entry *entry,
+ int (*func)(struct argp_option *opt,
+ struct argp_option *real))
+{
+ unsigned nopts;
+ int val = 0;
+ struct argp_option *opt, *real = entry->opt;
+ char *so = entry->short_options;
+
+ for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--)
+ if (oshort (opt) && *so == opt->key)
+ {
+ if (!oalias (opt))
+ real = opt;
+ if (ovisible (opt))
+ val = (*func)(opt, real);
+ so++;
+ }
+
+ return val;
+}
+
+static inline int
+hol_entry_long_iterate (const struct hol_entry *entry,
+ int (*func)(struct argp_option *opt,
+ struct argp_option *real))
+{
+ unsigned nopts;
+ int val = 0;
+ struct argp_option *opt, *real = entry->opt;
+
+ for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--)
+ if (opt->name)
+ {
+ if (!oalias (opt))
+ real = opt;
+ if (ovisible (opt))
+ val = (*func)(opt, real);
+ }
+
+ return val;
+}
+
+/* Returns the first valid short option in ENTRY, or 0 if there is none. */
+static char
+hol_entry_first_short (const struct hol_entry *entry)
+{
+ inline int func1 (struct argp_option *opt, struct argp_option *real)
+ {
+ return opt->key;
+ }
+ return hol_entry_short_iterate (entry, func1);
+}
+
+/* Returns the first valid long option in ENTRY, or 0 if there is none. */
+static char *
+hol_entry_first_long (const struct hol_entry *entry)
+{
+ struct argp_option *opt;
+ unsigned num;
+ for (opt = entry->opt, num = entry->num; num > 0; opt++, num--)
+ if (opt->name && ovisible (opt))
+ return opt->name;
+ return 0;
+}
+
+/* Returns the entry in HOL with the long option name NAME, or 0 if there is
+ none. */
+static struct hol_entry *hol_find_entry (struct hol *hol, char *name)
+{
+ struct hol_entry *entry = hol->entries;
+ unsigned num_entries = hol->num_entries;
+
+ while (num_entries-- > 0)
+ {
+ struct argp_option *opt = entry++->opt;
+ unsigned num_opts = entry->num;
+
+ while (num_opts-- > 0)
+ if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0)
+ return entry;
+ else
+ opt++;
+ }
+
+ return 0;
+}
+
+/* If an entry with the long option NAME occurs in HOL, set it's special
+ sort position to CLASS. */
+static void
+hol_set_sort_class (struct hol *hol, char *name, int class)
+{
+ struct hol_entry *entry = hol_find_entry (hol, name);
+ if (entry)
+ entry->sort_class = class;
+}
+
+/* Sort HOL by sort class and alphabetically by option name (with short
+ options taking precedence over long). Since the sorting is for display
+ purposes only, the shadowing of options isn't effected. */
+static void
+hol_sort (struct hol *hol)
+{
+ int entry_cmp (const void *entry1_v, const void *entry2_v)
+ {
+ const struct hol_entry *entry1 = entry1_v, *entry2 = entry2_v;
+ int class1 = entry1->sort_class, class2 = entry2->sort_class;
+
+ if (class1 == class2)
+ /* Normal comparison. */
+ {
+ int short1 = hol_entry_first_short (entry1);
+ int short2 = hol_entry_first_short (entry2);
+ char *long1 = hol_entry_first_long (entry1);
+ char *long2 = hol_entry_first_long (entry2);
+
+ if (!short1 && !short2 && long1 && long2)
+ /* Only long options. */
+ return strcasecmp (long1, long2);
+ else
+ /* Compare short/short, long/short, short/long, using the first
+ character of long options. Entries without *any* valid
+ options (such as options with OPTION_HIDDEN set) will be put
+ first, but as they're not displayed, it doesn't matter where
+ they are. */
+ {
+ char first1 = short1 ?: long1 ? *long1 : 0;
+ char first2 = short2 ?: long2 ? *long2 : 0;
+ /* Compare ignoring case, except when the options are both the
+ same letter, in which case lower-case always comes first. */
+ return (tolower (first2) - tolower (first1)) ?: first2 - first1;
+ }
+ }
+ else
+ /* Order by class: 1, 2, ..., n, 0, -m, ..., -2, -1 */
+ if ((class1 < 0 && class2 < 0) || (class1 > 0 && class2 > 0))
+ return class2 - class1;
+ else
+ return class1 - class2;
+ }
+
+ qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), entry_cmp);
+}
+
+/* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow
+ any in MORE with the same name. */
+static void
+hol_append (struct hol *hol, struct hol *more)
+{
+}
+
+static void
+hol_entry_help (struct hol_entry *entry, struct line *line)
+{
+ unsigned num;
+ int first = 1; /* True if nothing's been printed so far. */
+ struct argp_option *real = entry->opt, *opt;
+ char *so = entry->short_options;
+
+ /* Inserts a comma if this isn't the first item on the line, and then makes
+ sure we're at least to column COL. Also clears FIRST. */
+ void comma (unsigned col)
+ {
+ if (first)
+ first = 0;
+ else
+ {
+ line_putc (line, ',');
+ line_putc (line, ' ');
+ }
+ line_indent_to (line, col);
+ }
+
+ /* If the option REAL has an argument, we print it in using the printf
+ format REQ_FMT or OPT_FMT depending on whether it's a required or
+ optional argument. */
+ void arg (char *req_fmt, char *opt_fmt)
+ {
+ if (real->arg)
+ if (real->flags & OPTION_ARG_OPTIONAL)
+ line_printf (line, opt_fmt, real->arg);
+ else
+ line_printf (line, req_fmt, real->arg);
+ }
+
+ /* First emit short options. */
+ for (opt = real, num = entry->num; num > 0; opt++, num--)
+ if (oshort (opt) && opt->key == *so)
+ /* OPT has a valid (non shadowed) short option. */
+ {
+ if (ovisible (opt))
+ {
+ comma (SHORT_OPT_COL);
+ line_putc (line, '-');
+ line_putc (line, *so);
+ arg (" %s", "[%s]");
+ }
+ so++;
+ }
+
+ /* Now, long options. */
+ for (opt = real, num = entry->num; num > 0; opt++, num--)
+ if (opt->name && ovisible (opt))
+ {
+ comma (LONG_OPT_COL);
+ line_printf (line, "--%s", opt->name);
+ arg ("=%s", "[=%s]");
+ }
+
+ if (first)
+ /* There are no valid options, so it'd be silly to print anything else. */
+ return;
+
+ /* Now the option documentation. */
+ if (real->doc)
+ {
+ unsigned col = line_column (line);
+ char *doc = opt->doc;
+
+ if (col > OPT_DOC_COL + 3)
+ line_newline (line, OPT_DOC_COL);
+ else if (col >= OPT_DOC_COL)
+ line_printf (line, " ");
+ else
+ line_indent_to (line, OPT_DOC_COL);
+
+ line_fill (line, doc, OPT_DOC_COL);
+ }
+
+ line_newline (line, 0);
+}
+
+/* Output a long help message about the options in HOL to LINE. */
+static void
+hol_help (struct hol *hol, struct line *line)
+{
+ unsigned num;
+ struct hol_entry *entry;
+ for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--)
+ hol_entry_help (entry, line);
+}
+
+/* Add the formatted output from FMT &c to LINE, preceded by a space if it
+ fits on the same line, otherwise starting on a new line and indented by
+ USAGE_INDENT spaces. */
+static void
+add_usage_item (struct line *line, char *fmt, ...)
+{
+ va_list ap;
+ unsigned len;
+ static char item[RMARGIN + 1];
+
+ va_start (ap, fmt);
+ vsnprintf (item, sizeof (item), fmt, ap);
+ va_end (ap);
+
+ len = strlen (item);
+ if (line_left (line, len + 1) >= 0)
+ line_putc (line, ' ');
+ else
+ line_newline (line, USAGE_INDENT);
+ line_puts (line, item);
+}
+
+/* Print a short usage description for the arguments in HOL to LINE. */
+static void
+hol_usage (struct hol *hol, struct line *line)
+{
+ unsigned nentries;
+ struct hol_entry *entry;
+ char *short_no_arg_opts = alloca (strlen (hol->short_options));
+ char *snao_end = short_no_arg_opts;
+
+ /* First we put a list of short options without arguments. */
+ for (entry = hol->entries, nentries = hol->num_entries
+ ; nentries > 0
+ ; entry++, nentries--)
+ {
+ inline int func2 (struct argp_option *opt, struct argp_option *real)
+ {
+ if (! (opt->arg || real->arg))
+ *snao_end++ = opt->key;
+ return 0;
+ }
+ hol_entry_short_iterate (entry, func2);
+ }
+ if (snao_end > short_no_arg_opts)
+ {
+ *snao_end++ = 0;
+ add_usage_item (line, "[-%s]", short_no_arg_opts);
+ }
+
+ /* Now a list of short options *with* arguments. */
+ for (entry = hol->entries, nentries = hol->num_entries
+ ; nentries > 0
+ ; entry++, nentries--)
+ {
+ inline int func3 (struct argp_option *opt, struct argp_option *real)
+ {
+ if (opt->arg || real->arg)
+ if ((opt->flags | real->flags) & OPTION_ARG_OPTIONAL)
+ add_usage_item (line, "[-%c[%s]]",
+ opt->key, opt->arg ?: real->arg);
+ else
+ add_usage_item (line, "[-%c %s]",
+ opt->key, opt->arg ?: real->arg);
+ return 0;
+ }
+ hol_entry_short_iterate (entry, func3);
+ }
+
+ /* Finally, a list of long options (whew!). */
+ for (entry = hol->entries, nentries = hol->num_entries
+ ; nentries > 0
+ ; entry++, nentries--)
+ {
+ int func4 (struct argp_option *opt, struct argp_option *real)
+ {
+ if (opt->arg || real->arg)
+ if ((opt->flags | real->flags) & OPTION_ARG_OPTIONAL)
+ add_usage_item (line, "[--%s[=%s]]",
+ opt->name, opt->arg ?: real->arg);
+ else
+ add_usage_item (line, "[--%s=%s]",
+ opt->name, opt->arg ?: real->arg);
+ else
+ add_usage_item (line, "[--%s]", opt->name);
+ return 0;
+ }
+ hol_entry_long_iterate (entry, func4);
+ }
+}
+
+/* Make a HOL containing all levels of options in ARGP. */
+static struct hol *
+argp_hol (struct argp *argp)
+{
+ struct argp **parents = argp->parents;
+ struct hol *hol = make_hol (argp->options);
+ if (parents)
+ while (*parents)
+ hol_append (hol, argp_hol (*parents++));
+ return hol;
+}
+
+/* Print all the non-option args documented in ARGP to LINE. Any output is
+ preceded by a space. */
+static void
+argp_args_usage (struct argp *argp, struct line *line)
+{
+ struct argp **parents = argp->parents;
+ char *doc = argp->args_doc;
+ if (doc)
+ add_usage_item (line, "%s", doc);
+ if (parents)
+ while (*parents)
+ argp_args_usage (*parents++, line);
+}
+
+/* Print the documentation for ARGP to LINE. Each separate bit of
+ documentation is preceded by a blank line. */
+static void
+argp_doc (struct argp *argp, struct line *line)
+{
+ struct argp **parents = argp->parents;
+ char *doc = argp->doc;
+ if (doc)
+ {
+ line_newline (line, 0);
+ line_fill (line, doc, 0);
+ line_freshline (line, 0);
+ }
+ if (parents)
+ while (*parents)
+ argp_doc (*parents++, line);
+}
+
+/* Output a usage message for ARGP to STREAM. FLAGS are from the set
+ ARGP_USAGE_*. */
+void argp_usage (struct argp *argp, FILE *stream, unsigned flags)
+{
+ int first = 1;
+ struct hol *hol = 0;
+ struct line *line = make_line (stream, RMARGIN);
+
+ /* `paragraph break' -- print a blank line if there's any output so far. */
+ void pbreak ()
+ {
+ if (! first)
+ line_newline (line, 0);
+ }
+
+ if (flags & (ARGP_USAGE_USAGE | ARGP_USAGE_HELP))
+ {
+ hol = argp_hol (argp);
+
+ /* If present, these options always come last. */
+ hol_set_sort_class (hol, "help", -2);
+ hol_set_sort_class (hol, "version", -1);
+
+ hol_sort (hol);
+ }
+
+ if (flags & ARGP_USAGE_USAGE)
+ /* Print a short help message. */
+ {
+ line_printf (line, "Usage: %s", program_invocation_name);
+ hol_usage (hol, line);
+ argp_args_usage (argp, line);
+ line_newline (line, 0);
+ first = 0;
+ }
+
+ if (flags & ARGP_USAGE_SEE)
+ {
+ line_printf (line, "Try `%s --help' for more information.\n",
+ program_invocation_name);
+ first = 0;
+ }
+
+ if (flags & ARGP_USAGE_HELP)
+ /* Print a long, detailed help message. */
+ {
+ /* Print info about all the options. */
+ if (hol->num_entries > 0)
+ {
+ pbreak ();
+ hol_help (hol, line);
+ first = 0;
+ }
+
+ /* Finally, print any documentation strings at the end. */
+ argp_doc (argp, line);
+ }
+
+ if (hol)
+ hol_free (hol);
+
+ line_free (line);
+
+ if (flags & ARGP_USAGE_EXIT_ERR)
+ exit (1);
+ if (flags & ARGP_USAGE_EXIT_OK)
+ exit (0);
+}
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;
+}
diff --git a/libshouldbeinlibc/argp.h b/libshouldbeinlibc/argp.h
new file mode 100644
index 00000000..c834c2a1
--- /dev/null
+++ b/libshouldbeinlibc/argp.h
@@ -0,0 +1,216 @@
+/* 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. */
+
+#ifndef __ARGP_H__
+#define __ARGP_H__
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+
+struct argp_option
+{
+ /* The long option name. For more than one name for the same option, you
+ can use following options with the OPTION_ALIAS flag set. */
+ char *name;
+
+ /* What key is returned for this option. If ascii and > 0, then it's also
+ accepted as a short option. */
+ int key;
+
+ /* If non-NULL, this is the name of the argument associated with this
+ option, which is required unless the OPTION_ARG_OPTIONAL flags is set. */
+ char *arg;
+
+ /* ARGP_OPTION_ flags. */
+ int flags;
+
+ /* The doc string for this option. */
+ char *doc;
+};
+
+/* The argument associated with this option is optional. */
+#define OPTION_ARG_OPTIONAL 0x1
+/* This option isn't displayed in any help messages. */
+#define OPTION_HIDDEN 0x2
+/* This option is an alias for the closest previous non-alias option. This
+ means that it will be displayed in the same help entry, and will inherit
+ fields other than NAME and KEY from the aliased option. */
+#define OPTION_ALIAS 0x4
+
+struct argp; /* fwd declare this type */
+struct argp_state; /* " */
+
+/* The type of a pointer to an argp parsing function. */
+typedef error_t (*argp_parser_t)(int key, char *arg, struct argp_state *state);
+
+/* Special values for the KEY argument to an argument parsing function.
+ EINVAL should be returned if they aren't understood. */
+/* This is not an option at all, but rather a command line argument. */
+#define ARGP_KEY_ARG 0
+/* There are no more command line arguments at all. */
+#define ARGP_KEY_END 1
+
+/* An argp structure contains a set of getopt options declarations, a
+ function to deal with getting one, and an optional pointer to another
+ argp structure. When actually parsing options, getopt is called with
+ the union of all the argp structures chained together through their
+ PARENT pointers, with conflicts being resolved in favor of the first
+ occurance in the chain. */
+struct argp
+{
+ /* An array of argp_option structures, terminated by an entry with both
+ NAME and KEY having a value of 0. */
+ struct argp_option *options;
+
+ /* What to do with an option from this structure. KEY is the key
+ associated with the option, and ARG is any associated argument (NULL if
+ none was supplied). If KEY isn't understood, EINVAL should be
+ returned. If a non-zero, non-EINVAL value is returned, then parsing is
+ stopped immediately, and that value is returned from argp_parse().
+ For special (non-user-supplied) values of KEY, see the ARGP_KEY_
+ definitions below. */
+ argp_parser_t parser;
+
+ /* A string describing what other arguments are wanted by this program. It
+ is only used by argp_usage to print the `Usage:' message. */
+ char *args_doc;
+
+ /* A string containing extra text to be printed after the options in a long
+ help message, if it is non-NULL. */
+ char *doc;
+
+ /* A NULL terminated list of other argp structures that should be parsed
+ with this one. Any conflicts are resolved in favor of this argp, or
+ early argps in the PARENTS list. This field is useful if you use
+ libraries that supply their own argp structure, which you want to use in
+ conjunction with your own. */
+ struct argp **parents;
+};
+
+/* Parsing state. This is provided to parsing functions called by argp,
+ which may examine and, as noted, modify fields. */
+struct argp_state
+{
+ /* The top level ARGP being parsed. */
+ struct argp *argp;
+
+ /* The argument vector being parsed. May be modified. */
+ int argc;
+ char **argv;
+
+ /* The current index into ARGV. May be modified. */
+ int index;
+
+ /* The flags supplied to argp_parse. May be modified. */
+ unsigned flags;
+};
+
+/* Flags for argp_parse (note that the defaults are those that are
+ convenient for program command line parsing): */
+
+/* Don't ignore the first element of ARGV. Normally (and always unless
+ ARGP_NO_ERRS is set) the first element of the argument vector is
+ skipped for option parsing purposes, as it corresponds to the program name
+ in a command line. */
+#define ARGP_PARSE_ARGV0 0x1
+
+/* Don't print error messages for unknown options to stderr; unless this flag
+ is set, ARGP_PARSE_ARGV0 is ignored, as ARGV[0] is used as the program
+ name in the error messages. */
+#define ARGP_NO_ERRS 0x2
+
+/* Don't parse any non-option args. Normally non-option args are parsed by
+ calling the parse functions with a key of ARGP_KEY_ARG, and the actual
+ arg as the value. Since it's impossible to know which parse function
+ wants to handle it, each one is called in turn, until one returns 0 or an
+ error other than EINVAL; if an argument is handled by no one, the
+ argp_parse returns prematurely (but with a return value of 0). If
+ all args have been parsed without error, all parsing functions are called
+ one last time with a key of ARGP_KEY_END. This flag needn't normally
+ be set, as the normal behavior is to stop parsing as soon as some argument
+ can't be handled. */
+#define ARGP_NO_ARGS 0x4
+
+/* Parse options and arguments in the same order they occur on the command
+ line -- normally they're rearranged so that all options come first. */
+#define ARGP_IN_ORDER 0x8
+
+/* Don't provide the following standard help behavior:
+ o A long option --help is automatically added, which causes usage and
+ option help information to be output to stdout, and exit (0) called.
+ o Any option parsing errors will result in a short `Try --help' message
+ to be output to stderr and exit (1) called. */
+#define ARGP_NO_HELP 0x16
+
+/* Disables the exiting behavior of the above default argp help messages. */
+#define ARGP_NO_EXIT 0x32
+
+/* Turns off any message-printing/exiting options. */
+#define ARGP_SILENT (ARGP_NO_ERRS | ARGP_NO_HELP)
+
+/* Parse the options strings in ARGC & ARGV according to the options in
+ ARGP. FLAGS is one of the ARGP_ flags above. If ARG_INDEX 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. This function may also call exit unless the ARGP_NO_HELP
+ flag is set. */
+error_t argp_parse (struct argp *argp, int argc, char **argv, unsigned flags,
+ int *arg_index);
+
+/* Flags for argp_usage: */
+#define ARGP_USAGE_USAGE 0x01 /* Print a Usage: message. */
+#define ARGP_USAGE_SEE 0x02 /* Print a `for more help...' message. */
+#define ARGP_USAGE_HELP 0x04 /* Print a long help message. */
+#define ARGP_USAGE_EXIT_ERR 0x08 /* Call exit(1) instead of returning. */
+#define ARGP_USAGE_EXIT_OK 0x10 /* Call exit(0) instead of returning. */
+
+/* The standard thing to do after a program command line parsing error. */
+#define ARGP_USAGE_STD \
+ (ARGP_USAGE_USAGE | ARGP_USAGE_SEE | ARGP_USAGE_EXIT_ERR)
+/* The standard thing to do in response to a --help option. */
+#define ARGP_USAGE_STD_HELP \
+ (ARGP_USAGE_USAGE | ARGP_USAGE_HELP | ARGP_USAGE_EXIT_OK)
+
+/* Output a usage message for ARGP to STREAM. FLAGS are from the set
+ ARGP_USAGE_*. */
+void argp_usage (struct argp *argp, FILE *stream, unsigned flags);
+
+/* Returns true if the option OPT is a valid short option. */
+extern inline int
+_option_is_short (struct argp_option *opt)
+{
+ int key = opt->key;
+ return key > 0 && isprint (key);
+}
+
+/* Returns true if the option OPT is in fact the last (unused) entry in an
+ options array. */
+extern inline int
+_option_is_end (struct argp_option *opt)
+{
+ return !opt->key && !opt->name;
+}
+
+#endif /* __ARGP_H__ */