about summary refs log tree commit diff
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 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)
 	    {
diff --git a/argp/argp-namefrob.h b/argp/argp-namefrob.h
index fab7c9d94a..983ae9fc0a 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 f1f4ce9dfc..af27757273 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 ae72b2230c..1ce48c3e3f 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 be727561a1..2e20ea67f3 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__