diff options
author | Miles Bader <miles@gnu.org> | 1995-10-10 19:57:09 +0000 |
---|---|---|
committer | Miles Bader <miles@gnu.org> | 1995-10-10 19:57:09 +0000 |
commit | 0b41d011be095d7036e3c4eab1fe11c2bf1a17bd (patch) | |
tree | d389efaab35a6344fecb146cf63f25092f9521c8 /libshouldbeinlibc | |
parent | 8043afef8feb939e72a55836ef4262599a6d7dca (diff) |
Initial revision
Diffstat (limited to 'libshouldbeinlibc')
-rw-r--r-- | libshouldbeinlibc/argp-help.c | 654 | ||||
-rw-r--r-- | libshouldbeinlibc/argp-parse.c | 356 | ||||
-rw-r--r-- | libshouldbeinlibc/argp.h | 216 |
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__ */ |