summary refs log tree commit diff
path: root/argp
diff options
context:
space:
mode:
Diffstat (limited to 'argp')
-rw-r--r--argp/argp-fmtstream.c14
-rw-r--r--argp/argp-help.c329
-rw-r--r--argp/argp-parse.c27
-rw-r--r--argp/argp-test.c2
-rw-r--r--argp/argp.h75
5 files changed, 341 insertions, 106 deletions
diff --git a/argp/argp-fmtstream.c b/argp/argp-fmtstream.c
index c7203deac6..ab2e870af8 100644
--- a/argp/argp-fmtstream.c
+++ b/argp/argp-fmtstream.c
@@ -240,7 +240,14 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
 	      nextline = p;
 	    }
 
-	  if (nextline - (nl + 1) < fs->wmargin)
+	  /* Note: There are a bunch of tests below for
+	     NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall
+	     at the end of the buffer, and NEXTLINE is in fact empty (and so
+	     we need not be careful to maintain its contents).  */
+
+	  if (nextline == buf + len + 1
+	      ? fs->end - nl < fs->wmargin + 1
+	      : nextline - (nl + 1) < fs->wmargin)
 	    /* The margin needs more blanks than we removed.  */
 	    if (fs->end - fs->p > fs->wmargin + 1)
 	      /* Make some space for them.  */
@@ -265,7 +272,8 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
 	       the next word.  */
 	    *nl++ = '\n';
 
-	  if (nextline - nl >= fs->wmargin)
+	  if (nextline - nl >= fs->wmargin
+	      || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin))
 	    /* Add blanks up to the wrap margin column.  */
 	    for (i = 0; i < fs->wmargin; ++i)
 	      *nl++ = ' ';
@@ -275,7 +283,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs)
 
 	  /* Copy the tail of the original buffer into the current buffer
 	     position.  */
-	  if (nl != nextline)
+	  if (nl < nextline)
 	    memmove (nl, nextline, buf + len - nextline);
 	  len -= nextline - buf;
 
diff --git a/argp/argp-help.c b/argp/argp-help.c
index 84f9ca51c9..e954beb5a9 100644
--- a/argp/argp-help.c
+++ b/argp/argp-help.c
@@ -22,6 +22,7 @@
 #include <config.h>
 #endif
 
+#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
@@ -44,7 +45,17 @@
 #include "argp.h"
 #include "argp-fmtstream.h"
 #include "argp-namefrob.h"
+
+/* User-selectable (using an environment variable) formatting parameters.
+
+   These may be specified in an environment variable called `ARGP_HELP_FMT',
+   with a contents like:  VAR1=VAL1,VAR2=VAL2,BOOLVAR2,no-BOOLVAR2
+   Where VALn must be a positive integer.  The list of variables is in the
+   UPARAM_NAMES vector, below.  */
 
+/* Default parameters.  */
+#define DUP_ARGS      0		/* True if option argument can be duplicated. */
+#define DUP_ARGS_NOTE 1		/* True to print a note about duplicate args. */
 #define SHORT_OPT_COL 2		/* column in which short options start */
 #define LONG_OPT_COL  6		/* column in which long options start */
 #define DOC_OPT_COL   2		/* column in which doc options start */
@@ -53,6 +64,146 @@
 #define USAGE_INDENT 12		/* indentation of wrapped usage lines */
 #define RMARGIN      79		/* right margin used for wrapping */
 
+/* User-selectable (using an environment variable) formatting parameters.
+   They must all be of type `int' for the parsing code to work.  */
+struct uparams
+{
+  /* If true, arguments for an option are shown with both short and long
+     options, even when a given option has both, e.g. `-x ARG, --longx=ARG'.
+     If false, then if an option has both, the argument is only shown with
+     the long one, e.g., `-x, --longx=ARG', and a message indicating that
+     this really means both is printed below the options.  */
+  int dup_args;
+
+  /* This is true if when DUP_ARGS is false, and some duplicate arguments have
+     been suppressed, an explanatory message should be printed.  */
+  int dup_args_note;
+
+  /* Various output columns.  */
+  int short_opt_col;
+  int long_opt_col;
+  int doc_opt_col;
+  int opt_doc_col;
+  int header_col;
+  int usage_indent;
+  int rmargin;
+
+  int valid;			/* True when the values in here are valid.  */
+};
+
+/* This is a global variable, as user options are only ever read once.  */
+static struct uparams uparams = {
+  DUP_ARGS, DUP_ARGS_NOTE,
+  SHORT_OPT_COL, LONG_OPT_COL, DOC_OPT_COL, OPT_DOC_COL, HEADER_COL,
+  USAGE_INDENT, RMARGIN,
+  0
+};
+
+/* A particular uparam, and what the user name is.  */
+struct uparam_name
+{
+  const char *name;		/* User name.  */
+  int is_bool;			/* Whether it's `boolean'.  */
+  size_t uparams_offs;		/* Location of the (int) field in UPARAMS.  */
+};
+
+/* The name-field mappings we know about.  */
+static const struct uparam_name uparam_names[] =
+{
+  { "dup-args",       1, offsetof (struct uparams, dup_args) },
+  { "dup-args-note",  1, offsetof (struct uparams, dup_args_note) },
+  { "short-opt-col",  0, offsetof (struct uparams, short_opt_col) },
+  { "long-opt-col",   0, offsetof (struct uparams, long_opt_col) },
+  { "doc-opt-col",    0, offsetof (struct uparams, doc_opt_col) },
+  { "opt-doc-col",    0, offsetof (struct uparams, opt_doc_col) },
+  { "header-col",     0, offsetof (struct uparams, header_col) },
+  { "usage-indent",   0, offsetof (struct uparams, usage_indent) },
+  { "rmargin",        0, offsetof (struct uparams, rmargin) },
+  { 0 }
+};
+
+/* Read user options from the environment, and fill in UPARAMS appropiately.  */
+static void
+fill_in_uparams (const struct argp_state *state)
+{
+  const char *var = getenv ("ARGP_HELP_FMT");
+
+#define SKIPWS(p) do { while (isspace (*p)) p++; } while (0);
+
+  if (var)
+    /* Parse var. */
+    while (*var)
+      {
+	SKIPWS (var);
+
+	if (isalpha (*var))
+	  {
+	    size_t var_len;
+	    const struct uparam_name *un;
+	    int unspec = 0, val = 0;
+	    const char *arg = var;
+
+	    while (isalnum (*arg) || *arg == '-' || *arg == '_')
+	      arg++;
+	    var_len = arg - var;
+
+	    SKIPWS (arg);
+
+	    if (*arg == '\0' || *arg == ',')
+	      unspec = 1;
+	    else if (*arg == '=')
+	      {
+		arg++;
+		SKIPWS (arg);
+	      }
+	    
+	    if (unspec)
+	      if (var[0] == 'n' && var[1] == 'o' && var[2] == '-')
+		{
+		  val = 0;
+		  var += 3;
+		  var_len -= 3;
+		}
+	      else
+		val = 1;
+	    else if (isdigit (*arg))
+	      {
+		val = atoi (arg);
+		while (isdigit (*arg))
+		  arg++;
+		SKIPWS (arg);
+	      }
+
+	    for (un = uparam_names; un->name; un++)
+	      if (strlen (un->name) == var_len
+		  && strncmp (var, un->name, var_len) == 0)
+		{
+		  if (unspec && !un->is_bool)
+		    __argp_failure (state, 0, 0,
+			   _("%.*s: ARGP_HELP_FMT parameter requires a value"),
+				    (int)var_len, var);
+		  else
+		    *(int *)((char *)&uparams + un->uparams_offs) = val;
+		  break;
+		}
+	    if (! un->name)
+	      __argp_failure (state, 0, 0,
+			      _("%.*s: Unknown ARGP_HELP_FMT parameter"),
+			      (int)var_len, var);
+
+	    var = arg;
+	    if (*var == ',')
+	      var++;
+	  }
+	else if (*var)
+	  {
+	    __argp_failure (state, 0, 0,
+			    _("Garbage in ARGP_HELP_FMT: %s"), var);
+	    break;
+	  }
+      }
+}
+
 /* Returns true if OPT hasn't been marked invisible.  Visibility only affects
    whether OPT is displayed or used in sorting, not option shadowing.  */
 #define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN))
@@ -719,13 +870,28 @@ arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt,
 {
   if (real->arg)
     if (real->flags & OPTION_ARG_OPTIONAL)
-      __argp_fmtstream_printf (stream, opt_fmt, _(real->arg));
+      __argp_fmtstream_printf (stream, opt_fmt, gettext (real->arg));
     else
-      __argp_fmtstream_printf (stream, req_fmt, _(real->arg));
+      __argp_fmtstream_printf (stream, req_fmt, gettext (real->arg));
 }
 
 /* Helper functions for hol_entry_help.  */
 
+/* State used during the execution of hol_help.  */
+struct hol_help_state 
+{
+  /* PREV_ENTRY should contain the previous entry printed, or 0.  */
+  struct hol_entry *prev_entry;
+
+  /* If an entry is in a different group from the previous one, and SEP_GROUPS
+     is true, then a blank line will be printed before any output. */
+  int sep_groups;
+
+  /* True if a duplicate option argument was suppressed (only ever set if
+     UPARAMS.dup_args is false).  */
+  int suppressed_dup_arg;
+};
+
 /* Some state used while printing a help entry (used to communicate with
    helper functions).  See the doc for hol_entry_help for more info, as most
    of the fields are copied from its arguments.  */
@@ -733,8 +899,7 @@ struct pentry_state
 {
   const struct hol_entry *entry;
   argp_fmtstream_t stream;
-  struct hol_entry **prev_entry;
-  int *sep_groups;
+  struct hol_help_state *hhstate;
 
   /* True if nothing's been printed so far.  */
   int first;
@@ -746,12 +911,12 @@ struct pentry_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)
+	    const struct argp_state *state)
 {
   if (argp->help_filter)
     /* We must apply a user filter to this output.  */
     {
-      void *input = __argp_input (argp, pest->state);
+      void *input = __argp_input (argp, state);
       return (*argp->help_filter) (key, doc, input);
     }
   else
@@ -769,25 +934,24 @@ print_header (const char *str, const struct argp *argp,
 	      struct pentry_state *pest)
 {
   const char *tstr = gettext (str);
-  const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest);
+  const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state);
 
   if (fstr)
     {
       if (*fstr)
 	{
-	  if (pest->prev_entry && *pest->prev_entry)
+	  if (pest->hhstate->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);
+	  indent_to (pest->stream, uparams.header_col);
+	  __argp_fmtstream_set_lmargin (pest->stream, uparams.header_col);
+	  __argp_fmtstream_set_wmargin (pest->stream, uparams.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. */
+      pest->hhstate->sep_groups = 1; /* Separate subsequent groups. */
     }
 
   if (fstr != tstr)
@@ -803,11 +967,10 @@ comma (unsigned col, struct pentry_state *pest)
 {
   if (pest->first)
     {
-      const struct hol_entry *pe = pest->prev_entry ? *pest->prev_entry : 0;
+      const struct hol_entry *pe = pest->hhstate->prev_entry;
       const struct hol_cluster *cl = pest->entry->cluster;
 
-      if (pest->sep_groups && *pest->sep_groups
-	  && pe && pest->entry->group != pe->group)
+      if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group)
 	__argp_fmtstream_putc (pest->stream, '\n');
 
       if (pe && cl && pe->cluster != cl && cl->header && *cl->header
@@ -830,38 +993,45 @@ comma (unsigned col, struct pentry_state *pest)
   indent_to (pest->stream, col);
 }
 
-/* Print help for ENTRY to STREAM.  *PREV_ENTRY should contain the last entry
-   printed before this, or null if it's the first, and if ENTRY is in a
-   different group, and *SEP_GROUPS is true, then a blank line will be
-   printed before any output.  *SEP_GROUPS is also set to true if a
-   user-specified group header is printed.  */
+/* Print help for ENTRY to STREAM.  */
 static void
 hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
-		argp_fmtstream_t stream,
-		struct hol_entry **prev_entry, int *sep_groups)
+		argp_fmtstream_t stream, struct hol_help_state *hhstate)
 {
   unsigned num;
   const struct argp_option *real = entry->opt, *opt;
   char *so = entry->short_options;
+  int have_long_opt = 0;	/* We have any long options.  */
   /* Saved margins.  */
   int old_lm = __argp_fmtstream_set_lmargin (stream, 0);
   int old_wm = __argp_fmtstream_wmargin (stream);
   /* 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 };
+  struct pentry_state pest = { entry, stream, hhstate, 1, state };
+
+  if (! odoc (real))
+    for (opt = real, num = entry->num; num > 0; opt++, num--)
+      if (opt->name && ovisible (opt))
+	{
+	  have_long_opt = 1;
+	  break;
+	}
 
   /* First emit short options.  */
-  __argp_fmtstream_set_wmargin (stream, SHORT_OPT_COL); /* For truly bizarre cases. */
+  __argp_fmtstream_set_wmargin (stream, uparams.short_opt_col); /* For truly bizarre cases. */
   for (opt = real, num = entry->num; num > 0; opt++, num--)
     if (oshort (opt) && opt->key == *so)
       /* OPT has a valid (non shadowed) short option.  */
       {
 	if (ovisible (opt))
 	  {
-	    comma (SHORT_OPT_COL, &pest);
+	    comma (uparams.short_opt_col, &pest);
 	    __argp_fmtstream_putc (stream, '-');
 	    __argp_fmtstream_putc (stream, *so);
-	    arg (real, " %s", "[%s]", stream);
+	    if (!have_long_opt || uparams.dup_args)
+	      arg (real, " %s", "[%s]", stream);
+	    else if (real->arg)
+	      hhstate->suppressed_dup_arg = 1;
 	  }
 	so++;
       }
@@ -870,27 +1040,32 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
   if (odoc (real))
     /* A `documentation' option.  */
     {
-      __argp_fmtstream_set_wmargin (stream, DOC_OPT_COL);
+      __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col);
       for (opt = real, num = entry->num; num > 0; opt++, num--)
 	if (opt->name && ovisible (opt))
 	  {
-	    comma (DOC_OPT_COL, &pest);
+	    comma (uparams.doc_opt_col, &pest);
 	    /* Calling gettext here isn't quite right, since sorting will
 	       have been done on the original; but documentation options
 	       should be pretty rare anyway...  */
-	    __argp_fmtstream_puts (stream, _(opt->name));
+	    __argp_fmtstream_puts (stream, gettext (opt->name));
 	  }
     }
   else
     /* A real long option.  */
     {
-      __argp_fmtstream_set_wmargin (stream, LONG_OPT_COL);
+      int first_long_opt = 1;
+
+      __argp_fmtstream_set_wmargin (stream, uparams.long_opt_col);
       for (opt = real, num = entry->num; num > 0; opt++, num--)
 	if (opt->name && ovisible (opt))
 	  {
-	    comma (LONG_OPT_COL, &pest);
+	    comma (uparams.long_opt_col, &pest);
 	    __argp_fmtstream_printf (stream, "--%s", opt->name);
-	    arg (real, "=%s", "[=%s]", stream);
+	    if (first_long_opt || uparams.dup_args)
+	      arg (real, "=%s", "[=%s]", stream);
+	    else if (real->arg)
+	      hhstate->suppressed_dup_arg = 1;
 	  }
     }
 
@@ -908,20 +1083,20 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
   else
     {
       const char *tstr = real->doc ? gettext (real->doc) : 0;
-      const char *fstr = filter_doc (tstr, real->key, entry->argp, &pest);
+      const char *fstr = filter_doc (tstr, real->key, entry->argp, state);
       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, uparams.opt_doc_col);
+	  __argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col);
 
-	  if (col > OPT_DOC_COL + 3)
+	  if (col > uparams.opt_doc_col + 3)
 	    __argp_fmtstream_putc (stream, '\n');
-	  else if (col >= OPT_DOC_COL)
+	  else if (col >= uparams.opt_doc_col)
 	    __argp_fmtstream_puts (stream, "   ");
 	  else
-	    indent_to (stream, OPT_DOC_COL);
+	    indent_to (stream, uparams.opt_doc_col);
 
 	  __argp_fmtstream_puts (stream, fstr);
 	}
@@ -933,8 +1108,7 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
       __argp_fmtstream_putc (stream, '\n');
     }
 
-  if (prev_entry)
-    *prev_entry = entry;
+  hhstate->prev_entry = entry;
 
 cleanup:
   __argp_fmtstream_set_lmargin (stream, old_lm);
@@ -948,11 +1122,27 @@ hol_help (struct hol *hol, const struct argp_state *state,
 {
   unsigned num;
   struct hol_entry *entry;
-  struct hol_entry *last_entry = 0;
-  int sep_groups = 0;		/* True if we should separate different
-				   sections with blank lines.   */
+  struct hol_help_state hhstate = { 0, 0, 0 };
+
   for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--)
-    hol_entry_help (entry, state, stream, &last_entry, &sep_groups);
+    hol_entry_help (entry, state, stream, &hhstate);
+
+  if (hhstate.suppressed_dup_arg && uparams.dup_args_note)
+    {
+      const char *tstr = _("\
+Mandatory or optional arguments to long options are also mandatory or \
+optional for any corresponding short options.");
+      const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE,
+				     state ? state->argp : 0, state);
+      if (fstr && *fstr)
+	{
+	  __argp_fmtstream_putc (stream, '\n');
+	  __argp_fmtstream_puts (stream, fstr);
+	  __argp_fmtstream_putc (stream, '\n');
+	}
+      if (fstr && fstr != tstr)
+	free ((char *) fstr);
+    }
 }
 
 /* Helper functions for hol_usage.  */
@@ -965,7 +1155,8 @@ add_argless_short_opt (const struct argp_option *opt,
 		       void *cookie)
 {
   char **snao_end = cookie;
-  if (! (opt->arg || real->arg))
+  if (!(opt->arg || real->arg)
+      && !((opt->flags | real->flags) & OPTION_NO_USAGE))
     *(*snao_end)++ = opt->key;
   return 0;
 }
@@ -979,15 +1170,16 @@ usage_argful_short_opt (const struct argp_option *opt,
 {
   argp_fmtstream_t stream = cookie;
   const char *arg = opt->arg;
+  int flags = opt->flags | real->flags;
 
   if (! arg)
     arg = real->arg;
 
-  if (arg)
+  if (arg && !(flags & OPTION_NO_USAGE))
     {
-      arg = _(arg);
+      arg = gettext (arg);
 
-      if ((opt->flags | real->flags) & OPTION_ARG_OPTIONAL)
+      if (flags & OPTION_ARG_OPTIONAL)
 	__argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg);
       else
 	{
@@ -1010,20 +1202,22 @@ usage_long_opt (const struct argp_option *opt,
 {
   argp_fmtstream_t stream = cookie;
   const char *arg = opt->arg;
+  int flags = opt->flags | real->flags;
 
   if (! arg)
     arg = real->arg;
 
-  if (arg)
-    {
-      arg = gettext (arg);
-      if ((opt->flags | real->flags) & OPTION_ARG_OPTIONAL)
-	__argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg);
-      else
-	__argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg);
-    }
-  else
-    __argp_fmtstream_printf (stream, " [--%s]", opt->name);
+  if (! (flags & OPTION_NO_USAGE))
+    if (arg)
+      {
+	arg = gettext (arg);
+	if (flags & OPTION_ARG_OPTIONAL)
+	  __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg);
+	else
+	  __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg);
+      }
+    else
+      __argp_fmtstream_printf (stream, " [--%s]", opt->name);
 
   return 0;
 }
@@ -1117,7 +1311,7 @@ argp_args_usage (const struct argp *argp, char **levels, int advance,
   char *our_level = *levels;
   int multiple = 0;
   const struct argp_child *child = argp->children;
-  const char *doc = _(argp->args_doc), *nl = 0;
+  const char *doc = gettext (argp->args_doc), *nl = 0;
 
   if (doc)
     {
@@ -1269,7 +1463,10 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream,
   if (! stream)
     return;
 
-  fs = __argp_make_fmtstream (stream, 0, RMARGIN, 0);
+  if (! uparams.valid)
+    fill_in_uparams (state);
+
+  fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0);
   if (! fs)
     return;
 
@@ -1390,7 +1587,7 @@ weak_alias (__argp_help, argp_help)
 /* Output, if appropriate, a usage message for STATE to STREAM.  FLAGS are
    from the set ARGP_HELP_*.  */
 void
-__argp_state_help (struct argp_state *state, FILE *stream, unsigned flags)
+__argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)
 {
   if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream)
     {
@@ -1417,7 +1614,7 @@ weak_alias (__argp_state_help, argp_state_help)
    by the program name and `:', to stderr, and followed by a `Try ... --help'
    message, then exit (1).  */
 void
-__argp_error (struct argp_state *state, const char *fmt, ...)
+__argp_error (const struct argp_state *state, const char *fmt, ...)
 {
   if (!state || !(state->flags & ARGP_NO_ERRS))
     {
@@ -1454,8 +1651,8 @@ weak_alias (__argp_error, argp_error)
    *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 (struct argp_state *state, int status, int errnum,
-	      const char *fmt, ...)
+__argp_failure (const struct argp_state *state, int status, int errnum,
+		const char *fmt, ...)
 {
   if (!state || !(state->flags & ARGP_NO_ERRS))
     {
@@ -1486,7 +1683,7 @@ __argp_failure (struct argp_state *state, int status, int errnum,
 
 	  putc ('\n', stream);
 
-	  if (status)
+	  if (status && (!state || !(state->flags & ARGP_NO_EXIT)))
 	    exit (status);
 	}
     }
diff --git a/argp/argp-parse.c b/argp/argp-parse.c
index af27757273..11c36be705 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_short_name = arg;
+      program_invocation_name = arg;
 
       /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka
 	 __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined
@@ -117,10 +117,13 @@ argp_default_parser (int key, char *arg, struct argp_state *state)
       else
 	program_invocation_short_name = program_invocation_name;
 
+      /* Update what we use for messages.  */
+      state->name = program_invocation_short_name;
+
       if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS))
 	  == ARGP_PARSE_ARGV0)
 	/* Update what getopt uses too.  */
-	state->argv[0] = program_invocation_short_name;
+	state->argv[0] = program_invocation_name;
 
       break;
 
@@ -522,17 +525,18 @@ parser_init (struct parser *parser, const struct argp *argp,
   memset (parser->child_inputs, 0, szs.num_child_inputs * sizeof (void *));
   parser_convert (parser, argp, flags);
 
-  parser->try_getopt = 1;
-
   memset (&parser->state, 0, sizeof (struct argp_state));
   parser->state.argp = parser->argp;
   parser->state.argc = argc;
   parser->state.argv = argv;
+  parser->state.flags = flags;
   parser->state.err_stream = stderr;
   parser->state.out_stream = stdout;
   parser->state.next = 0;	/* Tell getopt to initialize.  */
   parser->state.pstate = parser;
 
+  parser->try_getopt = 1;
+
   /* Call each parser for the first time, giving it a chance to propagate
      values to child parsers.  */
   if (parser->groups < parser->egroup)
@@ -552,12 +556,6 @@ parser_init (struct parser *parser, const struct argp *argp,
   if (err)
     return err;
 
-  if (parser->state.argv == argv && argv[0])
-    /* There's an argv[0]; use it for messages.  */
-    parser->state.name = argv[0];
-  else
-    parser->state.name = program_invocation_name;
-
   /* Getopt is (currently) non-reentrant.  */
   LOCK_GETOPT;
 
@@ -572,6 +570,15 @@ parser_init (struct parser *parser, const struct argp *argp,
   else
     opterr = 1;		/* Print error messages.  */
 
+  if (parser->state.argv == argv && argv[0])
+    /* There's an argv[0]; use it for messages.  */
+    {
+      char *short_name = strrchr (argv[0], '/');
+      parser->state.name = short_name ? short_name + 1 : argv[0];
+    }
+  else
+    parser->state.name = program_invocation_short_name;
+
   return 0;
 }
 
diff --git a/argp/argp-test.c b/argp/argp-test.c
index 1ce48c3e3f..702ae9aa85 100644
--- a/argp/argp-test.c
+++ b/argp/argp-test.c
@@ -187,7 +187,7 @@ help_filter (int key, const char *text, void *input)
     asprintf (&new_text, "%s (ZOT defaults to %x)",
 	      text, params->foonly_default);
   else
-    new_text = (char *) text;
+    new_text = (char *)text;
 
   return new_text;
 }
diff --git a/argp/argp.h b/argp/argp.h
index 2e20ea67f3..cdb32b799a 100644
--- a/argp/argp.h
+++ b/argp/argp.h
@@ -108,6 +108,15 @@ struct argp_option
    is displayed after all options (and OPTION_DOC entries with a leading `-')
    in the same group.  */
 #define OPTION_DOC		0x8
+
+/* This option shouldn't be included in `long' usage messages (but is still
+   included in help messages).  This is mainly intended for options that are
+   completely documented in an argp's ARGS_DOC field, in which case including
+   the option in the generic usage list would be redundant.  For instance,
+   if ARGS_DOC is "FOO BAR\n-x BLAH", and the `-x' option's purpose is to
+   distinguish these two cases, -x should probably be marked
+   OPTION_NO_USAGE.  */
+#define OPTION_NO_USAGE		0x10
 
 struct argp;			/* fwd declare this type */
 struct argp_state;		/* " */
@@ -126,12 +135,21 @@ typedef error_t (*argp_parser_t)(int key, char *arg, struct argp_state *state);
 /* Special values for the KEY argument to an argument parsing function.
    ARGP_ERR_UNKNOWN should be returned if they aren't understood.
 
-   The sequence of keys to parser calls is either (where opt is a user key):
-       ARGP_KEY_INIT (opt | ARGP_KEY_ARG)... ARGP_KEY_END
-   or  ARGP_KEY_INIT opt... ARGP_KEY_NO_ARGS ARGP_KEY_END
+   The sequence of keys to a parsing function is either (where each
+   uppercased word should be prefixed by `ARGP_KEY_' and opt is a user key):
+
+       INIT opt... NO_ARGS END SUCCESS  -- No non-option arguments at all
+   or  INIT (opt | ARG)... END SUCCESS  -- All non-option args parsed
+   or  INIT (opt | ARG)... SUCCESS      -- Some non-option arg unrecognized
 
-   If an error occurs, then the parser is called with ARGP_KEY_ERR, and no
-   other calls are made.  */
+   The third case is where every parser returned ARGP_KEY_UNKNOWN for an
+   argument, in which case parsing stops at that argument (returning the
+   unparsed arguments to the caller of argp_parse if requested, or stopping
+   with an error message if not).
+
+   If an error occurs (either detected by argp, or because the parsing
+   function returned an error value), then the parser is called with
+   ARGP_KEY_ERROR, and no further calls are made.  */
 
 /* This is not an option at all, but rather a command line argument.  If a
    parser receiving this key returns success, the fact is recorded, and the
@@ -160,12 +178,12 @@ typedef error_t (*argp_parser_t)(int key, char *arg, struct argp_state *state);
    never made, so any cleanup must be done here).  */
 #define ARGP_KEY_ERROR		0x1000005
 
-/* An argp structure contains a set of getopt options declarations, a
-   function to deal with getting one, and an optional pointer to another
-   argp structure.  When actually parsing options, getopt is called with
-   the union of all the argp structures chained together through their
-   CHILD pointers, with conflicts being resolved in favor of the first
-   occurance in the chain.  */
+/* An argp structure contains a set of options declarations, a function to
+   deal with parsing one, documentation string, a possible vector of child
+   argp's, and perhaps a function to filter help output.  When actually
+   parsing options, getopt is called with the union of all the argp
+   structures chained together through their CHILD pointers, with conflicts
+   being resolved in favor of the first occurance in the chain.  */
 struct argp
 {
   /* An array of argp_option structures, terminated by an entry with both
@@ -220,6 +238,9 @@ struct argp
 #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.  */
+/* Explanatory note emitted when duplicate option arguments have been
+   suppressed.  */
+#define ARGP_KEY_HELP_DUP_ARGS_NOTE 0x2000005 
 
 /* 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.  */
@@ -373,7 +394,7 @@ extern void (*argp_program_version_hook) __P ((FILE *__stream,
    argp_help if the ARGP_HELP_BUG_ADDR flag is set (as it is by various
    standard help messages), embedded in a sentence that says something like
    `Report bugs to ADDR.'.  */
-extern char *argp_program_bug_address;
+__const extern char *argp_program_bug_address;
 
 /* Flags for argp_help.  */
 #define ARGP_HELP_USAGE		0x01 /* a Usage: message. */
@@ -421,22 +442,22 @@ extern void __argp_help __P ((__const struct argp *__argp, FILE *__stream,
 
 /* Output, if appropriate, a usage message for STATE to STREAM.  FLAGS are
    from the set ARGP_HELP_*.  */
-extern void argp_state_help __P ((struct argp_state *__state, FILE *__stream,
-				  unsigned __flags));
-extern void __argp_state_help __P ((struct argp_state *__state, FILE *__stream,
-				    unsigned __flags));
+extern void argp_state_help __P ((__const struct argp_state *__state,
+				  FILE *__stream, unsigned __flags));
+extern void __argp_state_help __P ((__const struct argp_state *__state,
+				    FILE *__stream, unsigned __flags));
 
 /* Possibly output the standard usage message for ARGP to stderr and exit.  */
-extern void argp_usage __P ((struct argp_state *__state));
-extern void __argp_usage __P ((struct argp_state *__state));
+extern void argp_usage __P ((__const struct argp_state *__state));
+extern void __argp_usage __P ((__const 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).  */
-extern void argp_error __P ((struct argp_state *__state, __const char *__fmt,
-			     ...))
+extern void argp_error __P ((__const struct argp_state *__state,
+			     __const char *__fmt, ...))
      __attribute__ ((__format__ (__printf__, 2, 3)));
-extern void __argp_error __P ((struct argp_state *__state,
+extern void __argp_error __P ((__const struct argp_state *__state,
 			       __const char *__fmt, ...))
      __attribute__ ((__format__ (__printf__, 2, 3)));
 
@@ -448,11 +469,13 @@ extern void __argp_error __P ((struct argp_state *__state,
    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.  */
-extern void argp_failure __P ((struct argp_state *__state, int __status,
-			       int __errnum, __const char *__fmt, ...))
+extern void argp_failure __P ((__const struct argp_state *__state,
+			       int __status, int __errnum,
+			       __const char *__fmt, ...))
      __attribute__ ((__format__ (__printf__, 4, 5)));
-extern void __argp_failure __P ((struct argp_state *__state, int __status,
-				 int __errnum, __const char *__fmt, ...))
+extern void __argp_failure __P ((__const 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.  */
@@ -485,7 +508,7 @@ extern void *__argp_input __P ((__const struct argp *argp,
 #endif
 
 ARGP_EI void
-__argp_usage (struct argp_state *__state)
+__argp_usage (__const struct argp_state *__state)
 {
   __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE);
 }