diff options
Diffstat (limited to 'argp/argp-help.c')
-rw-r--r-- | argp/argp-help.c | 324 |
1 files changed, 222 insertions, 102 deletions
diff --git a/argp/argp-help.c b/argp/argp-help.c index 5d7df5454a..84f9ca51c9 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -169,6 +169,9 @@ struct hol_entry /* The cluster of options this entry belongs to, or 0 if none. */ struct hol_cluster *cluster; + + /* The argp from which this option came. */ + const struct argp *argp; }; /* A cluster of entries to reflect the argp tree structure. */ @@ -190,6 +193,9 @@ struct hol_cluster level. */ struct hol_cluster *parent; + /* The argp from which this cluster is (eventually) derived. */ + const struct argp *argp; + /* The distance this cluster is from the root. */ int depth; @@ -215,13 +221,14 @@ struct hol struct hol_cluster *clusters; }; -/* Create a struct hol from an array of struct argp_option. CLUSTER is the +/* Create a struct hol from the options in ARGP. CLUSTER is the hol_cluster in which these entries occur, or 0, if at the root. */ static struct hol * -make_hol (const struct argp_option *opt, struct hol_cluster *cluster) +make_hol (const struct argp *argp, struct hol_cluster *cluster) { char *so; const struct argp_option *o; + const struct argp_option *opts = argp->options; struct hol_entry *entry; unsigned num_short_options = 0; struct hol *hol = malloc (sizeof (struct hol)); @@ -231,15 +238,15 @@ make_hol (const struct argp_option *opt, struct hol_cluster *cluster) hol->num_entries = 0; hol->clusters = 0; - if (opt) + if (opts) { int cur_group = 0; /* The first option must not be an alias. */ - assert (! oalias (opt)); + assert (! oalias (opts)); /* Calculate the space needed. */ - for (o = opt; ! oend (o); o++) + for (o = opts; ! oend (o); o++) { if (! oalias (o)) hol->num_entries++; @@ -254,7 +261,7 @@ make_hol (const struct argp_option *opt, struct hol_cluster *cluster) /* Fill in the entries. */ so = hol->short_options; - for (o = opt, entry = hol->entries; ! oend (o); entry++) + for (o = opts, entry = hol->entries; ! oend (o); entry++) { entry->opt = o; entry->num = 0; @@ -266,6 +273,7 @@ make_hol (const struct argp_option *opt, struct hol_cluster *cluster) ? cur_group + 1 : cur_group); entry->cluster = cluster; + entry->argp = argp; do { @@ -285,10 +293,10 @@ make_hol (const struct argp_option *opt, struct hol_cluster *cluster) /* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the associated argp child list entry), INDEX, and PARENT, and return a pointer - to it. */ + to it. ARGP is the argp that this cluster results from. */ static struct hol_cluster * hol_add_cluster (struct hol *hol, int group, const char *header, int index, - struct hol_cluster *parent) + struct hol_cluster *parent, const struct argp *argp) { struct hol_cluster *cl = malloc (sizeof (struct hol_cluster)); if (cl) @@ -298,6 +306,7 @@ hol_add_cluster (struct hol *hol, int group, const char *header, int index, cl->index = index; cl->parent = parent; + cl->argp = argp; cl->next = hol->clusters; hol->clusters = cl; @@ -657,8 +666,8 @@ hol_append (struct hol *hol, struct hol *more) if (oshort (opt) && ch == opt->key) /* The next short option in MORE_SO, CH, is from OPT. */ { - if (! find_char (ch, - short_options, short_options + hol_so_len)) + if (! find_char (ch, short_options, + short_options + hol_so_len)) /* The short option CH isn't shadowed by HOL's options, so add it to the sum. */ *so++ = ch; @@ -689,6 +698,18 @@ indent_to (argp_fmtstream_t stream, unsigned col) __argp_fmtstream_putc (stream, ' '); } +/* Output to STREAM either a space, or a newline if there isn't room for at + least ENSURE characters before the right margin. */ +static void +space (argp_fmtstream_t stream, size_t ensure) +{ + if (__argp_fmtstream_point (stream) + ensure + >= __argp_fmtstream_rmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + else + __argp_fmtstream_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. */ @@ -715,44 +736,79 @@ struct pentry_state struct hol_entry **prev_entry; int *sep_groups; - int first; /* True if nothing's been printed so far. */ + /* True if nothing's been printed so far. */ + int first; + + /* If non-zero, the state that was used to print this help. */ + const struct argp_state *state; }; +/* If a user doc filter should be applied to DOC, do so. */ +static const char * +filter_doc (const char *doc, int key, const struct argp *argp, + struct pentry_state *pest) +{ + if (argp->help_filter) + /* We must apply a user filter to this output. */ + { + void *input = __argp_input (argp, pest->state); + return (*argp->help_filter) (key, doc, input); + } + else + /* No filter. */ + return (char *)doc; +} + /* 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 + notes the fact that groups should be separated with a blank line. ARGP is + the argp that should dictate any user doc filtering to take place. 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) +print_header (const char *str, const struct argp *argp, + struct pentry_state *pest) { - if (*str) + const char *tstr = gettext (str); + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest); + + if (fstr) { - if (st->prev_entry && *st->prev_entry) - __argp_fmtstream_putc (st->stream, '\n'); /* Precede with a blank line. */ - indent_to (st->stream, HEADER_COL); - __argp_fmtstream_set_lmargin (st->stream, HEADER_COL); - __argp_fmtstream_set_wmargin (st->stream, HEADER_COL); - __argp_fmtstream_puts (st->stream, str); - __argp_fmtstream_set_lmargin (st->stream, 0); + if (*fstr) + { + if (pest->prev_entry && *pest->prev_entry) + /* Precede with a blank line. */ + __argp_fmtstream_putc (pest->stream, '\n'); + indent_to (pest->stream, HEADER_COL); + __argp_fmtstream_set_lmargin (pest->stream, HEADER_COL); + __argp_fmtstream_set_wmargin (pest->stream, HEADER_COL); + __argp_fmtstream_puts (pest->stream, fstr); + __argp_fmtstream_set_lmargin (pest->stream, 0); + __argp_fmtstream_putc (pest->stream, '\n'); + } + + if (pest->sep_groups) + *pest->sep_groups = 1; /* Separate subsequent groups. */ } - if (st->sep_groups) - *st->sep_groups = 1; /* Separate subsequent groups. */ + if (fstr != tstr) + free ((char *) fstr); } /* 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. */ + sure we're at least to column COL. If this *is* the first item on a line, + prints any pending whitespace/headers that should precede this line. Also + clears FIRST. */ static void -comma (unsigned col, struct pentry_state *st) +comma (unsigned col, struct pentry_state *pest) { - if (st->first) + if (pest->first) { - const struct hol_entry *pe = st->prev_entry ? *st->prev_entry : 0; - const struct hol_cluster *cl = st->entry->cluster; + const struct hol_entry *pe = pest->prev_entry ? *pest->prev_entry : 0; + const struct hol_cluster *cl = pest->entry->cluster; - if (st->sep_groups && *st->sep_groups - && pe && st->entry->group != pe->group) - __argp_fmtstream_putc (st->stream, '\n'); + if (pest->sep_groups && *pest->sep_groups + && pe && pest->entry->group != pe->group) + __argp_fmtstream_putc (pest->stream, '\n'); if (pe && cl && pe->cluster != cl && cl->header && *cl->header && !hol_cluster_is_child (pe->cluster, cl)) @@ -761,18 +817,17 @@ comma (unsigned col, struct pentry_state *st) (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 = __argp_fmtstream_wmargin (st->stream); - print_header (cl->header, st); - __argp_fmtstream_putc (st->stream, '\n'); - __argp_fmtstream_set_wmargin (st->stream, old_wm); + int old_wm = __argp_fmtstream_wmargin (pest->stream); + print_header (cl->header, cl->argp, pest); + __argp_fmtstream_set_wmargin (pest->stream, old_wm); } - st->first = 0; + pest->first = 0; } else - __argp_fmtstream_puts (st->stream, ", "); + __argp_fmtstream_puts (pest->stream, ", "); - indent_to (st->stream, col); + indent_to (pest->stream, col); } /* Print help for ENTRY to STREAM. *PREV_ENTRY should contain the last entry @@ -781,15 +836,19 @@ comma (unsigned col, struct pentry_state *st) printed before any output. *SEP_GROUPS is also set to true if a user-specified group header is printed. */ static void -hol_entry_help (struct hol_entry *entry, argp_fmtstream_t stream, +hol_entry_help (struct hol_entry *entry, const struct argp_state *state, + argp_fmtstream_t stream, struct hol_entry **prev_entry, int *sep_groups) { unsigned num; const struct argp_option *real = entry->opt, *opt; char *so = entry->short_options; + /* Saved margins. */ int old_lm = __argp_fmtstream_set_lmargin (stream, 0); int old_wm = __argp_fmtstream_wmargin (stream); - struct pentry_state pest = { entry, stream, prev_entry, sep_groups, 1 }; + /* PEST is a state block holding some of our variables that we'd like to + share with helper functions. */ + struct pentry_state pest = { entry, stream, prev_entry, sep_groups, 1, state }; /* First emit short options. */ __argp_fmtstream_set_wmargin (stream, SHORT_OPT_COL); /* For truly bizarre cases. */ @@ -809,7 +868,7 @@ hol_entry_help (struct hol_entry *entry, argp_fmtstream_t stream, /* Now, long options. */ if (odoc (real)) - /* Really a `documentation' option. */ + /* A `documentation' option. */ { __argp_fmtstream_set_wmargin (stream, DOC_OPT_COL); for (opt = real, num = entry->num; num > 0; opt++, num--) @@ -823,7 +882,7 @@ hol_entry_help (struct hol_entry *entry, argp_fmtstream_t stream, } } else - /* A realy long option. */ + /* A real long option. */ { __argp_fmtstream_set_wmargin (stream, LONG_OPT_COL); for (opt = real, num = entry->num; num > 0; opt++, num--) @@ -835,39 +894,45 @@ hol_entry_help (struct hol_entry *entry, argp_fmtstream_t stream, } } + /* Next, documentation strings. */ __argp_fmtstream_set_lmargin (stream, 0); + if (pest.first) /* Didn't print any switches, what's up? */ - if (!oshort (real) && !real->name && real->doc) + if (!oshort (real) && !real->name) /* This is a group header, print it nicely. */ - print_header (real->doc, &pest); + print_header (real->doc, entry->argp, &pest); else /* Just a totally shadowed option or null header; print nothing. */ goto cleanup; /* Just return, after cleaning up. */ - else if (real->doc) - /* Now the option documentation. */ + else { - unsigned col = __argp_fmtstream_point (stream); - const char *doc = real->doc; + const char *tstr = real->doc ? gettext (real->doc) : 0; + const char *fstr = filter_doc (tstr, real->key, entry->argp, &pest); + if (fstr && *fstr) + { + unsigned col = __argp_fmtstream_point (stream); - __argp_fmtstream_set_lmargin (stream, OPT_DOC_COL); - __argp_fmtstream_set_wmargin (stream, OPT_DOC_COL); + __argp_fmtstream_set_lmargin (stream, OPT_DOC_COL); + __argp_fmtstream_set_wmargin (stream, OPT_DOC_COL); - if (col > OPT_DOC_COL + 3) - __argp_fmtstream_putc (stream, '\n'); - else if (col >= OPT_DOC_COL) - __argp_fmtstream_puts (stream, " "); - else - indent_to (stream, OPT_DOC_COL); + if (col > OPT_DOC_COL + 3) + __argp_fmtstream_putc (stream, '\n'); + else if (col >= OPT_DOC_COL) + __argp_fmtstream_puts (stream, " "); + else + indent_to (stream, OPT_DOC_COL); - __argp_fmtstream_puts (stream, doc); + __argp_fmtstream_puts (stream, fstr); + } + if (fstr && fstr != tstr) + free ((char *) fstr); /* Reset the left margin. */ __argp_fmtstream_set_lmargin (stream, 0); + __argp_fmtstream_putc (stream, '\n'); } - __argp_fmtstream_putc (stream, '\n'); - if (prev_entry) *prev_entry = entry; @@ -878,7 +943,8 @@ cleanup: /* Output a long help message about the options in HOL to STREAM. */ static void -hol_help (struct hol *hol, argp_fmtstream_t stream) +hol_help (struct hol *hol, const struct argp_state *state, + argp_fmtstream_t stream) { unsigned num; struct hol_entry *entry; @@ -886,7 +952,7 @@ hol_help (struct hol *hol, argp_fmtstream_t stream) int sep_groups = 0; /* True if we should separate different sections with blank lines. */ for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--) - hol_entry_help (entry, stream, &last_entry, &sep_groups); + hol_entry_help (entry, state, stream, &last_entry, &sep_groups); } /* Helper functions for hol_usage. */ @@ -927,11 +993,7 @@ usage_argful_short_opt (const struct argp_option *opt, { /* Manually do line wrapping so that it (probably) won't get wrapped at the embedded space. */ - if (__argp_fmtstream_point (stream) + 6 + strlen (arg) - >= __argp_fmtstream_rmargin (stream)) - __argp_fmtstream_putc (stream, '\n'); - else - __argp_fmtstream_putc (stream, ' '); + space (stream, 6 + strlen (arg)); __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg); } } @@ -1008,7 +1070,7 @@ static struct hol * argp_hol (const struct argp *argp, struct hol_cluster *cluster) { const struct argp_child *child = argp->children; - struct hol *hol = make_hol (argp->options, cluster); + struct hol *hol = make_hol (argp, cluster); if (child) while (child->argp) { @@ -1016,7 +1078,7 @@ argp_hol (const struct argp *argp, struct hol_cluster *cluster) ((child->group || child->header) /* Put CHILD->argp within its own cluster. */ ? hol_add_cluster (hol, child->group, child->header, - child - argp->children, cluster) + child - argp->children, cluster, argp) /* Just merge it into the parent's cluster. */ : cluster); hol_append (hol, argp_hol (child->argp, child_cluster)) ; @@ -1075,11 +1137,7 @@ argp_args_usage (const struct argp *argp, char **levels, int advance, /* Manually do line wrapping so that it (probably) won't get wrapped at any embedded spaces. */ - if (__argp_fmtstream_point (stream) + 1 + nl - doc - >= __argp_fmtstream_rmargin (stream)) - __argp_fmtstream_putc (stream, '\n'); - else - __argp_fmtstream_putc (stream, ' '); + space (stream, 1 + nl - doc); __argp_fmtstream_write (stream, doc, nl - doc); } @@ -1111,46 +1169,98 @@ argp_args_usage (const struct argp *argp, char **levels, int advance, then the first is as well. If FIRST_ONLY is true, only the first occurance is output. Returns true if anything was output. */ static int -argp_doc (const struct argp *argp, int post, int pre_blank, int first_only, +argp_doc (const struct argp *argp, const struct argp_state *state, + int post, int pre_blank, int first_only, argp_fmtstream_t stream) { - const struct argp_child *child = argp->children; - const char *doc = argp->doc; + const char *text; + const char *inp_text; + void *input = 0; int anything = 0; + size_t inp_text_limit = 0; + const char *doc = gettext (argp->doc); + const struct argp_child *child = argp->children; if (doc) { char *vt = strchr (doc, '\v'); + inp_text = post ? (vt ? vt + 1 : 0) : doc; + inp_text_limit = (!post && vt) ? (vt - doc) : 0; + } + else + inp_text = 0; - if (pre_blank && (vt || !post)) + if (argp->help_filter) + /* We have to filter the doc strings. */ + { + if (inp_text_limit) + /* Copy INP_TEXT so that it's nul-terminated. */ + inp_text = strndup (inp_text, inp_text_limit); + input = __argp_input (argp, state); + text = + (*argp->help_filter) (post + ? ARGP_KEY_HELP_POST_DOC + : ARGP_KEY_HELP_PRE_DOC, + inp_text, input); + } + else + text = (const char *) inp_text; + + if (text) + { + if (pre_blank) __argp_fmtstream_putc (stream, '\n'); - if (vt) - if (post) - __argp_fmtstream_puts (stream, vt + 1); - else - __argp_fmtstream_write (stream, doc, vt - doc); + if (text == inp_text && inp_text_limit) + __argp_fmtstream_write (stream, inp_text, inp_text_limit); else - if (! post) - __argp_fmtstream_puts (stream, doc); + __argp_fmtstream_puts (stream, text); + if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) __argp_fmtstream_putc (stream, '\n'); anything = 1; } + + if (text && text != inp_text) + free ((char *) text); /* Free TEXT returned from the help filter. */ + if (inp_text && inp_text_limit && argp->help_filter) + free ((char *) inp_text); /* We copied INP_TEXT, so free it now. */ + + if (post && argp->help_filter) + /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */ + { + text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input); + if (text) + { + if (anything || pre_blank) + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, text); + free ((char *) text); + if (__argp_fmtstream_point (stream) + > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + anything = 1; + } + } + if (child) while (child->argp && !(first_only && anything)) anything |= - argp_doc ((child++)->argp, post, anything || pre_blank, first_only, + argp_doc ((child++)->argp, state, + post, anything || pre_blank, first_only, stream); return anything; } -/* Output a usage message for ARGP to STREAM. FLAGS are from the set - ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ -void __argp_help (const struct argp *argp, FILE *stream, - unsigned flags, char *name) +/* Output a usage message for ARGP to STREAM. If called from + argp_state_help, STATE is the relevent parsing state. FLAGS are from the + set ARGP_HELP_*. NAME is what to use wherever a `program name' is + needed. */ +static void +_help (const struct argp *argp, const struct argp_state *state, FILE *stream, + unsigned flags, char *name) { int anything = 0; /* Whether we've output anything. */ struct hol *hol = 0; @@ -1190,7 +1300,8 @@ void __argp_help (const struct argp *argp, FILE *stream, char *levels = pattern_levels; __argp_fmtstream_printf (fs, "%s %s", - first_pattern ? "Usage:" : " or: ", name); + _(first_pattern ? "Usage:" : " or: "), + name); /* We set the lmargin as well as the wmargin, because hol_usage manually wraps options with newline to avoid annoying breaks. */ @@ -1200,7 +1311,7 @@ void __argp_help (const struct argp *argp, FILE *stream, /* Just show where the options go. */ { if (hol->num_entries > 0) - __argp_fmtstream_puts (fs, " [OPTION...]"); + __argp_fmtstream_puts (fs, _(" [OPTION...]")); } else /* Actually print the options. */ @@ -1223,13 +1334,13 @@ void __argp_help (const struct argp *argp, FILE *stream, } if (flags & ARGP_HELP_PRE_DOC) - anything |= argp_doc (argp, 0, 0, 1, fs); + anything |= argp_doc (argp, state, 0, 0, 1, fs); if (flags & ARGP_HELP_SEE) { - __argp_fmtstream_printf (fs, - "Try `%s --help' or `%s --usage' for more information.\n", - name, name); + __argp_fmtstream_printf (fs, _("\ +Try `%s --help' or `%s --usage' for more information.\n"), + name, name); anything = 1; } @@ -1241,20 +1352,21 @@ void __argp_help (const struct argp *argp, FILE *stream, { if (anything) __argp_fmtstream_putc (fs, '\n'); - hol_help (hol, fs); + hol_help (hol, state, fs); anything = 1; } } if (flags & ARGP_HELP_POST_DOC) /* Print any documentation strings at the end. */ - anything |= argp_doc (argp, 1, anything, 0, fs); + anything |= argp_doc (argp, state, 1, anything, 0, fs); if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address) { if (anything) __argp_fmtstream_putc (fs, '\n'); - __argp_fmtstream_printf (fs, "Report bugs to %s.\n", argp_program_bug_address); + __argp_fmtstream_printf (fs, _("Report bugs to %s.\n"), + argp_program_bug_address); anything = 1; } @@ -1263,6 +1375,14 @@ void __argp_help (const struct argp *argp, FILE *stream, __argp_fmtstream_free (fs); } + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ +void __argp_help (const struct argp *argp, FILE *stream, + unsigned flags, char *name) +{ + _help (argp, 0, stream, flags, name); +} #ifdef weak_alias weak_alias (__argp_help, argp_help) #endif @@ -1277,8 +1397,8 @@ __argp_state_help (struct argp_state *state, FILE *stream, unsigned flags) 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); + _help (state ? state->argp : 0, state, stream, flags, + state ? state->name : program_invocation_short_name); if (!state || ! (state->flags & ARGP_NO_EXIT)) { @@ -1307,7 +1427,7 @@ __argp_error (struct argp_state *state, const char *fmt, ...) { va_list ap; - fputs (state ? state->name : program_invocation_name, stream); + fputs (state ? state->name : program_invocation_short_name, stream); putc (':', stream); putc (' ', stream); @@ -1343,7 +1463,7 @@ __argp_failure (struct argp_state *state, int status, int errnum, if (stream) { - fputs (state ? state->name : program_invocation_name, stream); + fputs (state ? state->name : program_invocation_short_name, stream); if (fmt) { |