aboutsummaryrefslogtreecommitdiff
path: root/argp
diff options
context:
space:
mode:
Diffstat (limited to 'argp')
-rw-r--r--argp/argp-help.c324
-rw-r--r--argp/argp-namefrob.h2
-rw-r--r--argp/argp-parse.c31
-rw-r--r--argp/argp-test.c98
-rw-r--r--argp/argp.h66
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, &params);
+ 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__