diff options
Diffstat (limited to 'argp')
-rw-r--r-- | argp/argp-help.c | 324 | ||||
-rw-r--r-- | argp/argp-namefrob.h | 2 | ||||
-rw-r--r-- | argp/argp-parse.c | 31 | ||||
-rw-r--r-- | argp/argp-test.c | 98 | ||||
-rw-r--r-- | argp/argp.h | 66 |
5 files changed, 384 insertions, 137 deletions
diff --git a/argp/argp-help.c b/argp/argp-help.c index 5d7df54..84f9ca5 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) { diff --git a/argp/argp-namefrob.h b/argp/argp-namefrob.h index fab7c9d..983ae9f 100644 --- a/argp/argp-namefrob.h +++ b/argp/argp-namefrob.h @@ -30,6 +30,8 @@ #define __option_is_end _option_is_end #undef __option_is_short #define __option_is_short _option_is_short +#undef __argp_input +#define __argp_input _argp_input /* argp-help functions */ #undef __argp_help diff --git a/argp/argp-parse.c b/argp/argp-parse.c index f1f4ce9..af27757 100644 --- a/argp/argp-parse.c +++ b/argp/argp-parse.c @@ -106,7 +106,7 @@ argp_default_parser (int key, char *arg, struct argp_state *state) break; case OPT_PROGNAME: /* Set the program name. */ - program_invocation_name = arg; + program_invocation_short_name = arg; /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined @@ -119,7 +119,8 @@ argp_default_parser (int key, char *arg, struct argp_state *state) if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS)) == ARGP_PARSE_ARGV0) - state->argv[0] = program_invocation_name; /* Update what getopt uses too. */ + /* Update what getopt uses too. */ + state->argv[0] = program_invocation_short_name; break; @@ -231,6 +232,9 @@ struct group /* This group's parsing function. */ argp_parser_t parser; + /* Which argp this group is from. */ + const struct argp *argp; + /* Points to the point in SHORT_OPTS corresponding to the end of the short options for this group. We use it to determine from which group a particular short options is from. */ @@ -380,6 +384,7 @@ convert_options (const struct argp *argp, } group->parser = argp->parser; + group->argp = argp; group->short_end = cvt->short_end; group->args_processed = 0; group->parent = parent; @@ -526,6 +531,7 @@ parser_init (struct parser *parser, const struct argp *argp, parser->state.err_stream = stderr; parser->state.out_stream = stdout; parser->state.next = 0; /* Tell getopt to initialize. */ + parser->state.pstate = parser; /* Call each parser for the first time, giving it a chance to propagate values to child parsers. */ @@ -848,3 +854,24 @@ __argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, #ifdef weak_alias weak_alias (__argp_parse, argp_parse) #endif + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +void * +__argp_input (const struct argp *argp, const struct argp_state *state) +{ + if (state) + { + struct group *group; + struct parser *parser = state->pstate; + + for (group = parser->groups; group < parser->egroup; group++) + if (group->argp == argp) + return group->input; + } + + return 0; +} +#ifdef weak_alias +weak_alias (__argp_input, _argp_input) +#endif diff --git a/argp/argp-test.c b/argp/argp-test.c index ae72b22..1ce48c3 100644 --- a/argp/argp-test.c +++ b/argp/argp-test.c @@ -22,9 +22,12 @@ #include <config.h> #endif +#include <stdlib.h> +#include <time.h> +#include <string.h> #include <argp.h> -char *argp_program_version = "argp-test 1.0"; +const char *argp_program_version = "argp-test 1.0"; struct argp_option sub_options[] = { @@ -64,10 +67,27 @@ sub_parse_opt (int key, char *arg, struct argp_state *state) return 0; } +static char * +sub_help_filter (int key, const char *text, void *input) +{ + if (key == ARGP_KEY_HELP_EXTRA) + return strdup ("This is some extra text from the sub parser (note that it \ +is preceded by a blank line)."); + else + return (char *)text; +} + static struct argp sub_argp = { - sub_options, sub_parse_opt, sub_args_doc, sub_doc + sub_options, sub_parse_opt, sub_args_doc, sub_doc, 0, sub_help_filter }; +/* Structure used to communicate with the parsing functions. */ +struct params +{ + unsigned foonly; /* Value parsed for foonly. */ + unsigned foonly_default; /* Default value for it. */ +}; + #define OPT_PGRP 1 #define OPT_SESS 2 @@ -89,7 +109,7 @@ struct argp_option options[] = " the current process)" }, {0,0,0,0, "Here are some more options:"}, - {"foonly", 'f', "ZOT", 0, "Glork a foonly"}, + {"foonly", 'f', "ZOT", OPTION_ARG_OPTIONAL, "Glork a foonly"}, {"zaza", 'z', 0, 0, "Snit a zar"}, {0} @@ -98,11 +118,28 @@ struct argp_option options[] = static const char args_doc[] = "STRING"; static const char doc[] = "Test program for argp." "\vThis doc string comes after the options." - "\nHey! Some manual formatting!"; + "\nHey! Some manual formatting!" + "\nThe current time is: %s"; + +static void +popt (int key, char *arg) +{ + char buf[10]; + if (isprint (key)) + sprintf (buf, "%c", key); + else + sprintf (buf, "%d", key); + if (arg) + printf ("KEY %s: %s\n", buf, arg); + else + printf ("KEY %s\n", buf); +} static error_t parse_opt (int key, char *arg, struct argp_state *state) { + struct params *params = state->input; + switch (key) { case ARGP_KEY_NO_ARGS: @@ -115,19 +152,17 @@ parse_opt (int key, char *arg, struct argp_state *state) printf ("ARG: %s\n", arg); break; + case 'f': + if (arg) + params->foonly = atoi (arg); + else + params->foonly = params->foonly_default; + popt (key, arg); + break; + case 'p': case 'P': case OPT_PGRP: case 'x': case 'Q': - case 'r': case OPT_SESS: case 'f': case 'z': - { - char buf[10]; - if (isprint (key)) - sprintf (buf, "%c", key); - else - sprintf (buf, "%d", key); - if (arg) - printf ("KEY %s: %s\n", buf, arg); - else - printf ("KEY %s\n", buf); - } + case 'r': case OPT_SESS: case 'z': + popt (key, arg); break; default: @@ -136,12 +171,39 @@ parse_opt (int key, char *arg, struct argp_state *state) return 0; } +static char * +help_filter (int key, const char *text, void *input) +{ + char *new_text; + struct params *params = input; + + if (key == ARGP_KEY_HELP_POST_DOC && text) + { + time_t now = time (0); + asprintf (&new_text, text, ctime (&now)); + } + else if (key == 'f') + /* Show the default for the --foonly option. */ + asprintf (&new_text, "%s (ZOT defaults to %x)", + text, params->foonly_default); + else + new_text = (char *) text; + + return new_text; +} + static struct argp_child argp_children[] = { { &sub_argp }, { 0 } }; -static struct argp argp = { options, parse_opt, args_doc, doc, argp_children }; +static struct argp argp = { + options, parse_opt, args_doc, doc, argp_children, help_filter +}; int main (int argc, char **argv) { - argp_parse (&argp, argc, argv, 0, 0, 0); + struct params params; + params.foonly = 0; + params.foonly_default = random (); + argp_parse (&argp, argc, argv, 0, 0, ¶ms); + printf ("After parsing: foonly = %x\n", params.foonly); return 0; } diff --git a/argp/argp.h b/argp/argp.h index be72756..2e20ea6 100644 --- a/argp/argp.h +++ b/argp/argp.h @@ -22,14 +22,20 @@ #define __ARGP_H__ #include <stdio.h> -#include <errno.h> #include <ctype.h> #include <getopt.h> +#define __need_error_t +#include <errno.h> + #ifndef __const #define __const const #endif +#ifndef __error_t_defined +typedef int error_t; +#endif + #ifndef __P # if (defined (__STDC__) && __STDC__) || defined (__cplusplus) # define __P(args) args @@ -194,7 +200,26 @@ struct argp their own argp structure, which you want to use in conjunction with your own. */ __const struct argp_child *children; + + /* If non-zero, this should be a function to filter the output of help + messages. KEY is either a key from an option, in which case TEXT is + that option's help text, or a special key from the ARGP_KEY_HELP_ + defines, below, describing which other help text TEXT is. The function + should return either TEXT, if it should be used as-is, a replacement + string, which should be malloced, and will be freed by argp, or NULL, + meaning `print nothing'. The value for TEXT is *after* any translation + has been done, so if any of the replacement text also needs translation, + that should be done by the filter function. INPUT is either the input + supplied to argp_parse, or NULL, if argp_help was called directly. */ + char *(*help_filter)(int __key, __const char *__text, void *__input); }; + +/* Possible KEY arguments to a help filter function. */ +#define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceeding options. */ +#define ARGP_KEY_HELP_POST_DOC 0x2000002 /* Help text following options. */ +#define ARGP_KEY_HELP_HEADER 0x2000003 /* Option header string. */ +#define ARGP_KEY_HELP_EXTRA 0x2000004 /* After all other documentation; + TEXT is NULL for this key. */ /* When an argp has a non-zero CHILDREN field, it should point to a vector of argp_child structures, each of which describes a subsidiary argp. */ @@ -265,6 +290,8 @@ struct argp_state /* Streams used when argp prints something. */ FILE *err_stream; /* For errors; initialized to stderr. */ FILE *out_stream; /* For information; initialized to stdout. */ + + void *pstate; /* Private, for use by argp. */ }; /* Flags for argp_parse (note that the defaults are those that are @@ -318,20 +345,20 @@ struct argp_state 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. INPUT is a pointer to a value to be passed in to the parser. */ -error_t argp_parse __P ((__const struct argp *__argp, - int __argc, char **__argv, unsigned __flags, - int *__arg_index, void *__input)); -error_t __argp_parse __P ((__const struct argp *__argp, - int __argc, char **__argv, unsigned __flags, - int *__arg_index, void *__input)); +extern error_t argp_parse __P ((__const struct argp *__argp, + int __argc, char **__argv, unsigned __flags, + int *__arg_index, void *__input)); +extern error_t __argp_parse __P ((__const struct argp *__argp, + int __argc, char **__argv, unsigned __flags, + int *__arg_index, void *__input)); /* Global variables. */ /* If defined or set by the user program to a non-zero value, then a default option --version is added (unless the ARGP_NO_HELP flag is used), which - will print this this string followed by a newline and exit (unless the + will print this string followed by a newline and exit (unless the ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ -extern char *argp_program_version; +extern const char *argp_program_version; /* If defined or set by the user program to a non-zero value, then a default option --version is added (unless the ARGP_NO_HELP flag is used), which @@ -406,9 +433,11 @@ extern void __argp_usage __P ((struct argp_state *__state)); /* If appropriate, print the printf string FMT and following args, preceded by the program name and `:', to stderr, and followed by a `Try ... --help' message, then exit (1). */ -void argp_error __P ((struct argp_state *__state, __const char *__fmt, ...)) +extern void argp_error __P ((struct argp_state *__state, __const char *__fmt, + ...)) __attribute__ ((__format__ (__printf__, 2, 3))); -void __argp_error __P ((struct argp_state *__state, __const char *__fmt, ...)) +extern void __argp_error __P ((struct argp_state *__state, + __const char *__fmt, ...)) __attribute__ ((__format__ (__printf__, 2, 3))); /* Similar to the standard gnu error-reporting function error(), but will @@ -419,11 +448,11 @@ void __argp_error __P ((struct argp_state *__state, __const char *__fmt, ...)) difference between this function and argp_error is that the latter is for *parsing errors*, and the former is for other problems that occur during parsing but don't reflect a (syntactic) problem with the input. */ -void argp_failure __P ((struct argp_state *__state, - int __status, int __errnum, __const char *__fmt, ...)) +extern void argp_failure __P ((struct argp_state *__state, int __status, + int __errnum, __const char *__fmt, ...)) __attribute__ ((__format__ (__printf__, 4, 5))); -void __argp_failure __P ((struct argp_state *__state, - int __status, int __errnum, __const char *__fmt, ...)) +extern void __argp_failure __P ((struct argp_state *__state, int __status, + int __errnum, __const char *__fmt, ...)) __attribute__ ((__format__ (__printf__, 4, 5))); /* Returns true if the option OPT is a valid short option. */ @@ -434,6 +463,13 @@ extern int __option_is_short __P ((__const struct argp_option *__opt)); options array. */ extern int _option_is_end __P ((__const struct argp_option *__opt)); extern int __option_is_end __P ((__const struct argp_option *__opt)); + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +extern void *_argp_input __P ((__const struct argp *argp, + __const struct argp_state *state)); +extern void *__argp_input __P ((__const struct argp *argp, + __const struct argp_state *state)); #ifdef __OPTIMIZE__ |