summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libshouldbeinlibc/argp-help.c387
1 files changed, 244 insertions, 143 deletions
diff --git a/libshouldbeinlibc/argp-help.c b/libshouldbeinlibc/argp-help.c
index 4f68bdf3..62608f1c 100644
--- a/libshouldbeinlibc/argp-help.c
+++ b/libshouldbeinlibc/argp-help.c
@@ -1,6 +1,6 @@
/* Hierarchial argument parsing help output
- Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
Written by Miles Bader <miles@gnu.ai.mit.edu>
@@ -32,6 +32,7 @@
#define SHORT_OPT_COL 2 /* column in which short options start */
#define LONG_OPT_COL 6 /* column in which long options start */
+#define DOC_OPT_COL 2 /* column in which doc options start */
#define OPT_DOC_COL 29 /* column in which option text starts */
#define HEADER_COL 1 /* column in which group headers are printed */
#define USAGE_INDENT 12 /* indentation of wrapped usage lines */
@@ -44,11 +45,14 @@
/* Returns true if OPT is an alias for an earlier option. */
#define oalias(opt) ((opt)->flags & OPTION_ALIAS)
+/* Returns true if OPT is an documentation-only entry. */
+#define odoc(opt) ((opt)->flags & OPTION_DOC)
+
/* 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)
+#define oshort(opt) (!odoc(opt) && _option_is_short (opt))
/*
The help format for a particular option is like:
@@ -290,7 +294,9 @@ hol_free (struct hol *hol)
static inline int
hol_entry_short_iterate (const struct hol_entry *entry,
int (*func)(const struct argp_option *opt,
- const struct argp_option *real))
+ const struct argp_option *real,
+ void *cookie),
+ void *cookie)
{
unsigned nopts;
int val = 0;
@@ -303,7 +309,7 @@ hol_entry_short_iterate (const struct hol_entry *entry,
if (!oalias (opt))
real = opt;
if (ovisible (opt))
- val = (*func)(opt, real);
+ val = (*func)(opt, real, cookie);
so++;
}
@@ -313,7 +319,9 @@ hol_entry_short_iterate (const struct hol_entry *entry,
static inline int
hol_entry_long_iterate (const struct hol_entry *entry,
int (*func)(const struct argp_option *opt,
- const struct argp_option *real))
+ const struct argp_option *real,
+ void *cookie),
+ void *cookie)
{
unsigned nopts;
int val = 0;
@@ -325,22 +333,25 @@ hol_entry_long_iterate (const struct hol_entry *entry,
if (!oalias (opt))
real = opt;
if (ovisible (opt))
- val = (*func)(opt, real);
+ val = (*func)(opt, real, cookie);
}
return val;
}
+/* Iterator that returns true for the first short option. */
+static inline int
+until_short (const struct argp_option *opt, const struct argp_option *real,
+ void *cookie)
+{
+ return oshort (opt);
+}
+
/* 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 (const struct argp_option *opt,
- const struct argp_option *real)
- {
- return opt->key;
- }
- return hol_entry_short_iterate (entry, func1);
+ return hol_entry_short_iterate (entry, until_short, 0);
}
/* Returns the first valid long option in ENTRY, or 0 if there is none. */
@@ -442,6 +453,24 @@ hol_cluster_is_child (const struct hol_cluster *cl1,
return cl1 == cl2;
}
+/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail
+ that should be used for comparisons, and returns true iff it should be
+ treated as a non-option. */
+static int
+canon_doc_option (const char **name)
+{
+ int non_opt;
+ /* Skip initial whitespace. */
+ while (isspace (*name))
+ (*name)++;
+ /* Decide whether this looks like an option (leading `-') or not. */
+ non_opt = (**name != '-');
+ /* Skip until part of name used for sorting. */
+ while (**name && !isalnum (*name))
+ (*name)++;
+ return non_opt;
+}
+
/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help
listing. */
static int
@@ -472,10 +501,21 @@ hol_entry_cmp (const struct hol_entry *entry1, const struct hol_entry *entry2)
{
int short1 = hol_entry_first_short (entry1);
int short2 = hol_entry_first_short (entry2);
+ int doc1 = odoc (entry1->opt);
+ int doc2 = odoc (entry2->opt);
const char *long1 = hol_entry_first_long (entry1);
const char *long2 = hol_entry_first_long (entry2);
- if (!short1 && !short2 && long1 && long2)
+ if (doc1)
+ doc1 = canon_doc_option (&long1);
+ if (doc2)
+ doc2 = canon_doc_option (&long2);
+
+ if (doc1 != doc2)
+ /* `documentation' options always follow normal options (or
+ documentation options that *look* like normal options). */
+ return doc1 - doc2;
+ else if (!short1 && !short2 && long1 && long2)
/* Only long options. */
return strcasecmp (long1, long2);
else
@@ -498,18 +538,22 @@ hol_entry_cmp (const struct hol_entry *entry1, const struct hol_entry *entry2)
return group_cmp (group1, group2, 0);
}
+/* Version of hol_entry_cmp with correct signature for qsort. */
+static int
+hol_entry_qcmp (const void *entry1_v, const void *entry2_v)
+{
+ return hol_entry_cmp (entry1_v, entry2_v);
+}
+
/* Sort HOL by group 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 cmp (const void *entry1_v, const void *entry2_v)
- {
- return hol_entry_cmp (entry1_v, entry2_v);
- }
if (hol->num_entries > 0)
- qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), cmp);
+ qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry),
+ hol_entry_qcmp);
}
/* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow
@@ -606,6 +650,92 @@ indent_to (FILE *stream, unsigned col)
while (needed-- > 0)
putc (' ', stream);
}
+
+/* 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. */
+static void
+arg (const struct argp_option *real, char *req_fmt, char *opt_fmt,
+ FILE *stream)
+{
+ if (real->arg)
+ if (real->flags & OPTION_ARG_OPTIONAL)
+ fprintf (stream, opt_fmt, real->arg);
+ else
+ fprintf (stream, req_fmt, real->arg);
+}
+
+/* Helper functions for hol_entry_help. */
+
+/* Some state used while printing a help entry (used to communicate with
+ helper functions). See the doc for hol_entry_help for more info, as most
+ of the fields are copied from its arguments. */
+struct pentry_state
+{
+ const struct hol_entry *entry;
+ FILE *stream;
+ struct hol_entry **prev_entry;
+ int *sep_groups;
+
+ int first; /* True if nothing's been printed so far. */
+};
+
+/* Prints STR as a header line, with the margin lines set appropiately, and
+ notes the fact that groups should be separated with a blank line. Note
+ that the previous wrap margin isn't restored, but the left margin is reset
+ to 0. */
+static void
+print_header (const char *str, struct pentry_state *st)
+{
+ if (*str)
+ {
+ if (st->prev_entry && *st->prev_entry)
+ putc ('\n', st->stream); /* Precede with a blank line. */
+ indent_to (st->stream, HEADER_COL);
+ line_wrap_set_lmargin (st->stream, HEADER_COL);
+ line_wrap_set_wmargin (st->stream, HEADER_COL);
+ fputs (str, st->stream);
+ line_wrap_set_lmargin (st->stream, 0);
+ }
+
+ if (st->sep_groups)
+ *st->sep_groups = 1; /* Separate subsequent groups. */
+}
+
+/* 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. */
+static void
+comma (unsigned col, struct pentry_state *st)
+{
+ if (st->first)
+ {
+ const struct hol_entry *pe = st->prev_entry ? *st->prev_entry : 0;
+ const struct hol_cluster *cl = st->entry->cluster;
+
+ if (st->sep_groups && *st->sep_groups
+ && pe && st->entry->group != pe->group)
+ putc ('\n', st->stream);
+
+ if (pe && cl && pe->cluster != cl && cl->header && *cl->header
+ && !hol_cluster_is_child (pe->cluster, cl))
+ /* If we're changing clusters, then this must be the start of the
+ ENTRY's cluster unless that is an ancestor of the previous one
+ (in which case we had just popped into a sub-cluster for a bit).
+ If so, then print the cluster's header line. */
+ {
+ int old_wm = line_wrap_wmargin (st->stream);
+ print_header (cl->header, st);
+ putc ('\n', st->stream);
+ line_wrap_set_wmargin (st->stream, old_wm);
+ }
+
+ st->first = 0;
+ }
+ else
+ fputs (", ", st->stream);
+
+ indent_to (st->stream, col);
+}
/* Print help for ENTRY to STREAM. *PREV_ENTRY should contain the last entry
printed before this, or null if it's the first, and if ENTRY is in a
@@ -617,72 +747,11 @@ hol_entry_help (struct hol_entry *entry, FILE *stream,
struct hol_entry **prev_entry, int *sep_groups)
{
unsigned num;
- int first = 1; /* True if nothing's been printed so far. */
const struct argp_option *real = entry->opt, *opt;
- const struct hol_entry *pe = prev_entry ? *prev_entry : 0;
- const struct hol_cluster *cl = entry->cluster;
char *so = entry->short_options;
int old_lm = line_wrap_set_lmargin (stream, 0);
int old_wm = line_wrap_wmargin (stream);
-
- /* Prints STR as a header line, with the margin lines set appropiately, and
- notes the fact that groups should be separated with a blank line. Note
- that the previous wrap margin isn't restored, but the left margin is reset
- to 0. */
- void print_header (const char *str)
- {
- if (*str)
- {
- if (pe)
- putc ('\n', stream); /* Precede with a blank line. */
- indent_to (stream, HEADER_COL);
- line_wrap_set_lmargin (stream, HEADER_COL);
- line_wrap_set_wmargin (stream, HEADER_COL);
- fputs (str, stream);
- line_wrap_set_lmargin (stream, 0);
- }
- if (sep_groups)
- *sep_groups = 1; /* Separate subsequent groups. */
- }
-
- /* 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)
- {
- if (sep_groups && *sep_groups && pe && entry->group != pe->group)
- putc ('\n', stream);
- if (pe && cl && pe->cluster != cl && cl->header && *cl->header
- && !hol_cluster_is_child (pe->cluster, cl))
- /* If we're changing clusters, then this must be the start of the
- ENTRY's cluster unless that is an ancestor of the previous one
- (in which case we had just popped into a sub-cluster for a bit).
- If so, then print the cluster's header line. */
- {
- int old_wm = line_wrap_wmargin (stream);
- print_header (cl->header);
- putc ('\n', stream);
- line_wrap_set_wmargin (stream, old_wm);
- }
- first = 0;
- }
- else
- fputs (", ", stream);
- indent_to (stream, 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)
- fprintf (stream, opt_fmt, real->arg);
- else
- fprintf (stream, req_fmt, real->arg);
- }
+ struct pentry_state pest = { entry, stream, prev_entry, sep_groups, 1 };
/* First emit short options. */
line_wrap_set_wmargin (stream, SHORT_OPT_COL); /* For truly bizarre cases. */
@@ -692,30 +761,45 @@ hol_entry_help (struct hol_entry *entry, FILE *stream,
{
if (ovisible (opt))
{
- comma (SHORT_OPT_COL);
+ comma (SHORT_OPT_COL, &pest);
putc ('-', stream);
putc (*so, stream);
- arg (" %s", "[%s]");
+ arg (real, " %s", "[%s]", stream);
}
so++;
}
/* Now, long options. */
- line_wrap_set_wmargin (stream, LONG_OPT_COL);
- for (opt = real, num = entry->num; num > 0; opt++, num--)
- if (opt->name && ovisible (opt))
- {
- comma (LONG_OPT_COL);
- fprintf (stream, "--%s", opt->name);
- arg ("=%s", "[=%s]");
- }
+ if (odoc (real))
+ /* Really a `documentation' option. */
+ {
+ line_wrap_set_wmargin (stream, DOC_OPT_COL);
+ for (opt = real, num = entry->num; num > 0; opt++, num--)
+ if (opt->name && ovisible (opt))
+ {
+ comma (DOC_OPT_COL, &pest);
+ fputs (opt->name, stream);
+ }
+ }
+ else
+ /* A realy long option. */
+ {
+ line_wrap_set_wmargin (stream, LONG_OPT_COL);
+ for (opt = real, num = entry->num; num > 0; opt++, num--)
+ if (opt->name && ovisible (opt))
+ {
+ comma (LONG_OPT_COL, &pest);
+ fprintf (stream, "--%s", opt->name);
+ arg (real, "=%s", "[=%s]", stream);
+ }
+ }
line_wrap_set_lmargin (stream, 0);
- if (first)
+ if (pest.first)
/* Didn't print any switches, what's up? */
if (!oshort (real) && !real->name && real->doc)
/* This is a group header, print it nicely. */
- print_header (real->doc);
+ print_header (real->doc, &pest);
else
/* Just a totally shadowed option or null header; print nothing. */
goto cleanup; /* Just return, after cleaning up. */
@@ -764,6 +848,68 @@ hol_help (struct hol *hol, FILE *stream)
hol_entry_help (entry, stream, &last_entry, &sep_groups);
}
+/* Helper functions for hol_usage. */
+
+/* If OPT is a short option without an arg, append its key to the string
+ pointer pointer to by COOKIE, and advance the pointer. */
+static int
+add_argless_short_opt (const struct argp_option *opt,
+ const struct argp_option *real,
+ void *cookie)
+{
+ char **snao_end = cookie;
+ if (! (opt->arg || real->arg))
+ *(*snao_end)++ = opt->key;
+ return 0;
+}
+
+/* If OPT is a short option with an arg, output a usage entry for it to the
+ stream pointed at by COOKIE. */
+static int
+usage_argful_short_opt (const struct argp_option *opt,
+ const struct argp_option *real,
+ void *cookie)
+{
+ FILE *stream = cookie;
+
+ if (opt->arg || real->arg)
+ if ((opt->flags | real->flags) & OPTION_ARG_OPTIONAL)
+ fprintf (stream, " [-%c[%s]]",
+ opt->key, opt->arg ?: real->arg);
+ else
+ {
+ const char *arg = opt->arg ?: real->arg;
+ /* Manually do line wrapping so that it (probably) won't
+ get wrapped at the embedded space. */
+ if (line_wrap_point (stream) + 6 + strlen (arg)
+ >= line_wrap_rmargin (stream))
+ putc ('\n', stream);
+ else
+ putc (' ', stream);
+ fprintf (stream, "[-%c %s]", opt->key, arg);
+ }
+
+ return 0;
+}
+
+/* Output a usage entry for the long option opt to the stream pointed at by
+ COOKIE. */
+static int
+usage_long_opt (const struct argp_option *opt,
+ const struct argp_option *real,
+ void *cookie)
+{
+ FILE *stream = cookie;
+ if (opt->arg || real->arg)
+ if ((opt->flags | real->flags) & OPTION_ARG_OPTIONAL)
+ fprintf (stream, " [--%s[=%s]]", opt->name, opt->arg ?: real->arg);
+ else
+ fprintf (stream, " [--%s=%s]", opt->name, opt->arg ?: real->arg);
+ else
+ fprintf (stream, " [--%s]", opt->name);
+ return 0;
+}
+
/* Print a short usage description for the arguments in HOL to STREAM. */
static void
hol_usage (struct hol *hol, FILE *stream)
@@ -779,16 +925,7 @@ hol_usage (struct hol *hol, FILE *stream)
for (entry = hol->entries, nentries = hol->num_entries
; nentries > 0
; entry++, nentries--)
- {
- inline int func2 (const struct argp_option *opt,
- const struct argp_option *real)
- {
- if (! (opt->arg || real->arg))
- *snao_end++ = opt->key;
- return 0;
- }
- hol_entry_short_iterate (entry, func2);
- }
+ hol_entry_short_iterate (entry, add_argless_short_opt, &snao_end);
if (snao_end > short_no_arg_opts)
{
*snao_end++ = 0;
@@ -799,52 +936,13 @@ hol_usage (struct hol *hol, FILE *stream)
for (entry = hol->entries, nentries = hol->num_entries
; nentries > 0
; entry++, nentries--)
- {
- inline int func3 (const struct argp_option *opt,
- const struct argp_option *real)
- {
- if (opt->arg || real->arg)
- if ((opt->flags | real->flags) & OPTION_ARG_OPTIONAL)
- fprintf (stream, " [-%c[%s]]",
- opt->key, opt->arg ?: real->arg);
- else
- {
- const char *arg = opt->arg ?: real->arg;
- /* Manually do line wrapping so that it (probably) won't
- get wrapped at the embedded space. */
- if (line_wrap_point (stream) + 6 + strlen (arg)
- >= line_wrap_rmargin (stream))
- putc ('\n', stream);
- else
- putc (' ', stream);
- fprintf (stream, "[-%c %s]", opt->key, arg);
- }
- return 0;
- }
- hol_entry_short_iterate (entry, func3);
- }
+ hol_entry_short_iterate (entry, usage_argful_short_opt, stream);
/* Finally, a list of long options (whew!). */
for (entry = hol->entries, nentries = hol->num_entries
; nentries > 0
; entry++, nentries--)
- {
- int func4 (const struct argp_option *opt,
- const struct argp_option *real)
- {
- if (opt->arg || real->arg)
- if ((opt->flags | real->flags) & OPTION_ARG_OPTIONAL)
- fprintf (stream, " [--%s[=%s]]",
- opt->name, opt->arg ?: real->arg);
- else
- fprintf (stream, " [--%s=%s]",
- opt->name, opt->arg ?: real->arg);
- else
- fprintf (stream, " [--%s]", opt->name);
- return 0;
- }
- hol_entry_long_iterate (entry, func4);
- }
+ hol_entry_long_iterate (entry, usage_long_opt, stream);
}
}
@@ -1042,6 +1140,9 @@ argp_state_help (struct argp_state *state, FILE *stream, unsigned flags)
{
if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream)
{
+ if (state && (state->flags & ARGP_LONG_ONLY))
+ flags |= ARGP_HELP_LONG_ONLY;
+
argp_help (state ? state->argp : 0, stream, flags,
state ? state->name : program_invocation_name);