2005-08-08 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / argp / argp-help.c
index b17c56d..abd59c1 100644 (file)
@@ -1,50 +1,90 @@
 /* Hierarchial argument parsing help output
 /* Hierarchial argument parsing help output
-   Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+   Copyright (C) 1995-2003, 2004, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Written by Miles Bader <miles@gnu.ai.mit.edu>.
 
    The GNU C Library is free software; you can redistribute it and/or
    This file is part of the GNU C Library.
    Written by Miles Bader <miles@gnu.ai.mit.edu>.
 
    The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
    The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
    The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
+   Lesser General Public License for more details.
 
 
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE   1
+#endif
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
+/* AIX requires this to be the first thing in the file.  */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H || defined _LIBC
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+#pragma alloca
+#  else
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif
+#  endif
+# endif
+#endif
+
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #include <stdarg.h>
-#include <malloc.h>
 #include <ctype.h>
 #include <ctype.h>
+#include <limits.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
 
 #ifndef _
 
 #ifndef _
-/* This is for other GNU distributions with internationalized messages.
-   When compiling libc, the _ macro is predefined.  */
-#ifdef HAVE_LIBINTL_H
-# include <libintl.h>
-# define _(msgid)       gettext (msgid)
-#else
-# define _(msgid)       (msgid)
-# define gettext(msgid) (msgid)
+/* This is for other GNU distributions with internationalized messages.  */
+# if defined HAVE_LIBINTL_H || defined _LIBC
+#  include <libintl.h>
+#  ifdef _LIBC
+#   undef dgettext
+#   define dgettext(domain, msgid) \
+  INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES)
+#  endif
+# else
+#  define dgettext(domain, msgid) (msgid)
+# endif
 #endif
 #endif
+
+#ifndef _LIBC
+# if HAVE_STRERROR_R
+#  if !HAVE_DECL_STRERROR_R
+char *strerror_r (int errnum, char *buf, size_t buflen);
+#  endif
+# else
+#  if !HAVE_DECL_STRERROR
+char *strerror (int errnum);
+#  endif
+# endif
 #endif
 
 #include "argp.h"
 #include "argp-fmtstream.h"
 #include "argp-namefrob.h"
 #endif
 
 #include "argp.h"
 #include "argp-fmtstream.h"
 #include "argp-namefrob.h"
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
 \f
 /* User-selectable (using an environment variable) formatting parameters.
 
 \f
 /* User-selectable (using an environment variable) formatting parameters.
 
@@ -158,14 +198,16 @@ fill_in_uparams (const struct argp_state *state)
              }
 
            if (unspec)
              }
 
            if (unspec)
-             if (var[0] == 'n' && var[1] == 'o' && var[2] == '-')
-               {
-                 val = 0;
-                 var += 3;
-                 var_len -= 3;
-               }
-             else
-               val = 1;
+             {
+               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);
            else if (isdigit (*arg))
              {
                val = atoi (arg);
@@ -180,16 +222,18 @@ fill_in_uparams (const struct argp_state *state)
                {
                  if (unspec && !un->is_bool)
                    __argp_failure (state, 0, 0,
                {
                  if (unspec && !un->is_bool)
                    __argp_failure (state, 0, 0,
-                          _("%.*s: ARGP_HELP_FMT parameter requires a value"),
-                                   (int)var_len, var);
+                                   dgettext (state->root_argp->argp_domain, "\
+%.*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,
                  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);
+                             dgettext (state->root_argp->argp_domain, "\
+%.*s: Unknown ARGP_HELP_FMT parameter"),
+                             (int) var_len, var);
 
            var = arg;
            if (*var == ',')
 
            var = arg;
            if (*var == ',')
@@ -198,7 +242,8 @@ fill_in_uparams (const struct argp_state *state)
        else if (*var)
          {
            __argp_failure (state, 0, 0,
        else if (*var)
          {
            __argp_failure (state, 0, 0,
-                           _("Garbage in ARGP_HELP_FMT: %s"), var);
+                           dgettext (state->root_argp->argp_domain,
+                                     "Garbage in ARGP_HELP_FMT: %s"), var);
            break;
          }
       }
            break;
          }
       }
@@ -332,7 +377,7 @@ struct hol_cluster
   const char *header;
 
   /* Used to order clusters within the same group with the same parent,
   const char *header;
 
   /* Used to order clusters within the same group with the same parent,
-     according to the order in which they occured in the parent argp's child
+     according to the order in which they occurred in the parent argp's child
      list.  */
   int index;
 
      list.  */
   int index;
 
@@ -409,6 +454,9 @@ make_hol (const struct argp *argp, struct hol_cluster *cluster)
       hol->short_options = malloc (num_short_options + 1);
 
       assert (hol->entries && hol->short_options);
       hol->short_options = malloc (num_short_options + 1);
 
       assert (hol->entries && hol->short_options);
+#if SIZE_MAX <= UINT_MAX
+      assert (hol->num_entries <= SIZE_MAX / sizeof (struct hol_entry));
+#endif
 
       /* Fill in the entries.  */
       so = hol->short_options;
 
       /* Fill in the entries.  */
       so = hol->short_options;
@@ -458,6 +506,7 @@ hol_add_cluster (struct hol *hol, int group, const char *header, int index,
       cl->index = index;
       cl->parent = parent;
       cl->argp = argp;
       cl->index = index;
       cl->parent = parent;
       cl->argp = argp;
+      cl->depth = parent ? parent->depth + 1 : 0;
 
       cl->next = hol->clusters;
       hol->clusters = cl;
 
       cl->next = hol->clusters;
       hol->clusters = cl;
@@ -487,12 +536,12 @@ hol_free (struct hol *hol)
   free (hol);
 }
 \f
   free (hol);
 }
 \f
-static inline int
+static int
 hol_entry_short_iterate (const struct hol_entry *entry,
                         int (*func)(const struct argp_option *opt,
                                     const struct argp_option *real,
 hol_entry_short_iterate (const struct hol_entry *entry,
                         int (*func)(const struct argp_option *opt,
                                     const struct argp_option *real,
-                                    void *cookie),
-                        void *cookie)
+                                    const char *domain, void *cookie),
+                        const char *domain, void *cookie)
 {
   unsigned nopts;
   int val = 0;
 {
   unsigned nopts;
   int val = 0;
@@ -505,7 +554,7 @@ hol_entry_short_iterate (const struct hol_entry *entry,
        if (!oalias (opt))
          real = opt;
        if (ovisible (opt))
        if (!oalias (opt))
          real = opt;
        if (ovisible (opt))
-         val = (*func)(opt, real, cookie);
+         val = (*func)(opt, real, domain, cookie);
        so++;
       }
 
        so++;
       }
 
@@ -513,11 +562,12 @@ hol_entry_short_iterate (const struct hol_entry *entry,
 }
 
 static inline int
 }
 
 static inline int
+__attribute__ ((always_inline))
 hol_entry_long_iterate (const struct hol_entry *entry,
                        int (*func)(const struct argp_option *opt,
                                    const struct argp_option *real,
 hol_entry_long_iterate (const struct hol_entry *entry,
                        int (*func)(const struct argp_option *opt,
                                    const struct argp_option *real,
-                                   void *cookie),
-                       void *cookie)
+                                   const char *domain, void *cookie),
+                       const char *domain, void *cookie)
 {
   unsigned nopts;
   int val = 0;
 {
   unsigned nopts;
   int val = 0;
@@ -529,7 +579,7 @@ hol_entry_long_iterate (const struct hol_entry *entry,
        if (!oalias (opt))
          real = opt;
        if (ovisible (opt))
        if (!oalias (opt))
          real = opt;
        if (ovisible (opt))
-         val = (*func)(opt, real, cookie);
+         val = (*func)(opt, real, domain, cookie);
       }
 
   return val;
       }
 
   return val;
@@ -538,7 +588,7 @@ hol_entry_long_iterate (const struct hol_entry *entry,
 /* Iterator that returns true for the first short option.  */
 static inline int
 until_short (const struct argp_option *opt, const struct argp_option *real,
 /* Iterator that returns true for the first short option.  */
 static inline int
 until_short (const struct argp_option *opt, const struct argp_option *real,
-            void *cookie)
+            const char *domain, void *cookie)
 {
   return oshort (opt) ? opt->key : 0;
 }
 {
   return oshort (opt) ? opt->key : 0;
 }
@@ -547,7 +597,8 @@ until_short (const struct argp_option *opt, const struct argp_option *real,
 static char
 hol_entry_first_short (const struct hol_entry *entry)
 {
 static char
 hol_entry_first_short (const struct hol_entry *entry)
 {
-  return hol_entry_short_iterate (entry, until_short, 0);
+  return hol_entry_short_iterate (entry, until_short,
+                                 entry->argp->argp_domain, 0);
 }
 
 /* Returns the first valid long option in ENTRY, or 0 if there is none.  */
 }
 
 /* Returns the first valid long option in ENTRY, or 0 if there is none.  */
@@ -671,27 +722,30 @@ canon_doc_option (const char **name)
 /* Order ENTRY1 & ENTRY2 by the order which they should appear in a help
    listing.  */
 static int
 /* Order ENTRY1 & ENTRY2 by the order which they should appear in a help
    listing.  */
 static int
-hol_entry_cmp (const struct hol_entry *entry1, const struct hol_entry *entry2)
+hol_entry_cmp (const struct hol_entry *entry1,
+              const struct hol_entry *entry2)
 {
   /* The group numbers by which the entries should be ordered; if either is
      in a cluster, then this is just the group within the cluster.  */
   int group1 = entry1->group, group2 = entry2->group;
 
   if (entry1->cluster != entry2->cluster)
 {
   /* The group numbers by which the entries should be ordered; if either is
      in a cluster, then this is just the group within the cluster.  */
   int group1 = entry1->group, group2 = entry2->group;
 
   if (entry1->cluster != entry2->cluster)
-    /* The entries are not within the same cluster, so we can't compare them
-       directly, we have to use the appropiate clustering level too.  */
-    if (! entry1->cluster)
-      /* ENTRY1 is at the `base level', not in a cluster, so we have to
-        compare it's group number with that of the base cluster in which
-        ENTRY2 resides.  Note that if they're in the same group, the
-        clustered option always comes laster.  */
-      return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1);
-    else if (! entry2->cluster)
-      /* Likewise, but ENTRY2's not in a cluster.  */
-      return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1);
-    else
-      /* Both entries are in clusters, we can just compare the clusters.  */
-      return hol_cluster_cmp (entry1->cluster, entry2->cluster);
+    {
+      /* The entries are not within the same cluster, so we can't compare them
+        directly, we have to use the appropiate clustering level too.  */
+      if (! entry1->cluster)
+       /* ENTRY1 is at the `base level', not in a cluster, so we have to
+          compare it's group number with that of the base cluster in which
+          ENTRY2 resides.  Note that if they're in the same group, the
+          clustered option always comes laster.  */
+       return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1);
+      else if (! entry2->cluster)
+       /* Likewise, but ENTRY2's not in a cluster.  */
+       return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1);
+      else
+       /* Both entries are in clusters, we can just compare the clusters.  */
+       return hol_cluster_cmp (entry1->cluster, entry2->cluster);
+    }
   else if (group1 == group2)
     /* The entries are both in the same cluster and group, so compare them
        alphabetically.  */
   else if (group1 == group2)
     /* The entries are both in the same cluster and group, so compare them
        alphabetically.  */
@@ -724,7 +778,11 @@ hol_entry_cmp (const struct hol_entry *entry1, const struct hol_entry *entry2)
        {
          char first1 = short1 ? short1 : long1 ? *long1 : 0;
          char first2 = short2 ? short2 : long2 ? *long2 : 0;
        {
          char first1 = short1 ? short1 : long1 ? *long1 : 0;
          char first2 = short2 ? short2 : long2 ? *long2 : 0;
+#ifdef _tolower
+         int lower_cmp = _tolower (first1) - _tolower (first2);
+#else
          int lower_cmp = tolower (first1) - tolower (first2);
          int lower_cmp = tolower (first1) - tolower (first2);
+#endif
          /* Compare ignoring case, except when the options are both the
             same letter, in which case lower-case always comes first.  */
          return lower_cmp ? lower_cmp : first2 - first1;
          /* Compare ignoring case, except when the options are both the
             same letter, in which case lower-case always comes first.  */
          return lower_cmp ? lower_cmp : first2 - first1;
@@ -769,73 +827,81 @@ hol_append (struct hol *hol, struct hol *more)
 
   /* Merge entries.  */
   if (more->num_entries > 0)
 
   /* Merge entries.  */
   if (more->num_entries > 0)
-    if (hol->num_entries == 0)
-      {
-       hol->num_entries = more->num_entries;
-       hol->entries = more->entries;
-       hol->short_options = more->short_options;
-       more->num_entries = 0;  /* Mark MORE's fields as invalid.  */
-      }
-    else
-      /* append the entries in MORE to those in HOL, taking care to only add
-        non-shadowed SHORT_OPTIONS values.  */
-      {
-       unsigned left;
-       char *so, *more_so;
-       struct hol_entry *e;
-       unsigned num_entries = hol->num_entries + more->num_entries;
-       struct hol_entry *entries =
-         malloc (num_entries * sizeof (struct hol_entry));
-       unsigned hol_so_len = strlen (hol->short_options);
-       char *short_options =
-         malloc (hol_so_len + strlen (more->short_options) + 1);
-
-       memcpy (entries, hol->entries,
-               hol->num_entries * sizeof (struct hol_entry));
-       memcpy (entries + hol->num_entries, more->entries,
-               more->num_entries * sizeof (struct hol_entry));
-
-       memcpy (short_options, hol->short_options, hol_so_len);
-
-       /* Fix up the short options pointers from HOL.  */
-       for (e = entries, left = hol->num_entries; left > 0; e++, left--)
-         e->short_options += (short_options - hol->short_options);
-
-       /* Now add the short options from MORE, fixing up its entries too.  */
-       so = short_options + hol_so_len;
-       more_so = more->short_options;
-       for (left = more->num_entries; left > 0; e++, left--)
-         {
-           int opts_left;
-           const struct argp_option *opt;
+    {
+      if (hol->num_entries == 0)
+       {
+         hol->num_entries = more->num_entries;
+         hol->entries = more->entries;
+         hol->short_options = more->short_options;
+         more->num_entries = 0;        /* Mark MORE's fields as invalid.  */
+       }
+      else
+       /* Append the entries in MORE to those in HOL, taking care to only add
+          non-shadowed SHORT_OPTIONS values.  */
+       {
+         unsigned left;
+         char *so, *more_so;
+         struct hol_entry *e;
+         unsigned num_entries = hol->num_entries + more->num_entries;
+         struct hol_entry *entries =
+           malloc (num_entries * sizeof (struct hol_entry));
+         unsigned hol_so_len = strlen (hol->short_options);
+         char *short_options =
+           malloc (hol_so_len + strlen (more->short_options) + 1);
+
+         assert (entries && short_options);
+#if SIZE_MAX <= UINT_MAX
+         assert (num_entries <= SIZE_MAX / sizeof (struct hol_entry));
+#endif
 
 
-           e->short_options = so;
+         __mempcpy (__mempcpy (entries, hol->entries,
+                               hol->num_entries * sizeof (struct hol_entry)),
+                    more->entries,
+                    more->num_entries * sizeof (struct hol_entry));
 
 
-           for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--)
-             {
-               int ch = *more_so;
-               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))
-                     /* The short option CH isn't shadowed by HOL's options,
-                        so add it to the sum.  */
-                     *so++ = ch;
-                   more_so++;
-                 }
-             }
-         }
+         __mempcpy (short_options, hol->short_options, hol_so_len);
+
+         /* Fix up the short options pointers from HOL.  */
+         for (e = entries, left = hol->num_entries; left > 0; e++, left--)
+           e->short_options += (short_options - hol->short_options);
 
 
-       *so = '\0';
+         /* Now add the short options from MORE, fixing up its entries
+            too.  */
+         so = short_options + hol_so_len;
+         more_so = more->short_options;
+         for (left = more->num_entries; left > 0; e++, left--)
+           {
+             int opts_left;
+             const struct argp_option *opt;
 
 
-       free (hol->entries);
-       free (hol->short_options);
+             e->short_options = so;
 
 
-       hol->entries = entries;
-       hol->num_entries = num_entries;
-       hol->short_options = short_options;
-      }
+             for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--)
+               {
+                 int ch = *more_so;
+                 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))
+                       /* The short option CH isn't shadowed by HOL's options,
+                          so add it to the sum.  */
+                       *so++ = ch;
+                     more_so++;
+                   }
+               }
+           }
+
+         *so = '\0';
+
+         free (hol->entries);
+         free (hol->short_options);
+
+         hol->entries = entries;
+         hol->num_entries = num_entries;
+         hol->short_options = short_options;
+       }
+    }
 
   hol_free (more);
 }
 
   hol_free (more);
 }
@@ -866,13 +932,17 @@ space (argp_fmtstream_t stream, size_t ensure)
    optional argument.  */
 static void
 arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt,
    optional argument.  */
 static void
 arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt,
-     argp_fmtstream_t stream)
+     const char *domain, argp_fmtstream_t stream)
 {
   if (real->arg)
 {
   if (real->arg)
-    if (real->flags & OPTION_ARG_OPTIONAL)
-      __argp_fmtstream_printf (stream, opt_fmt, gettext (real->arg));
-    else
-      __argp_fmtstream_printf (stream, req_fmt, gettext (real->arg));
+    {
+      if (real->flags & OPTION_ARG_OPTIONAL)
+       __argp_fmtstream_printf (stream, opt_fmt,
+                                dgettext (domain, real->arg));
+      else
+       __argp_fmtstream_printf (stream, req_fmt,
+                                dgettext (domain, real->arg));
+    }
 }
 \f
 /* Helper functions for hol_entry_help.  */
 }
 \f
 /* Helper functions for hol_entry_help.  */
@@ -921,7 +991,7 @@ filter_doc (const char *doc, int key, const struct argp *argp,
     }
   else
     /* No filter.  */
     }
   else
     /* No filter.  */
-    return (char *)doc;
+    return doc;
 }
 
 /* Prints STR as a header line, with the margin lines set appropiately, and
 }
 
 /* Prints STR as a header line, with the margin lines set appropiately, and
@@ -933,7 +1003,7 @@ static void
 print_header (const char *str, const struct argp *argp,
              struct pentry_state *pest)
 {
 print_header (const char *str, const struct argp *argp,
              struct pentry_state *pest)
 {
-  const char *tstr = gettext (str);
+  const char *tstr = dgettext (argp->argp_domain, str);
   const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state);
 
   if (fstr)
   const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state);
 
   if (fstr)
@@ -973,8 +1043,10 @@ comma (unsigned col, struct pentry_state *pest)
       if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group)
        __argp_fmtstream_putc (pest->stream, '\n');
 
       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
-         && !hol_cluster_is_child (pe->cluster, cl))
+      if (cl && cl->header && *cl->header
+         && (!pe
+             || (pe->cluster != cl
+                 && !hol_cluster_is_child (pe->cluster, cl))))
        /* If we're changing clusters, then this must be the start of the
           ENTRY's cluster unless that is an ancestor of the previous one
           (in which case we had just popped into a sub-cluster for a bit).
        /* If we're changing clusters, then this must be the start of the
           ENTRY's cluster unless that is an ancestor of the previous one
           (in which case we had just popped into a sub-cluster for a bit).
@@ -1029,7 +1101,7 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
            __argp_fmtstream_putc (stream, '-');
            __argp_fmtstream_putc (stream, *so);
            if (!have_long_opt || uparams.dup_args)
            __argp_fmtstream_putc (stream, '-');
            __argp_fmtstream_putc (stream, *so);
            if (!have_long_opt || uparams.dup_args)
-             arg (real, " %s", "[%s]", stream);
+             arg (real, " %s", "[%s]", state->root_argp->argp_domain, stream);
            else if (real->arg)
              hhstate->suppressed_dup_arg = 1;
          }
            else if (real->arg)
              hhstate->suppressed_dup_arg = 1;
          }
@@ -1048,7 +1120,9 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
            /* Calling gettext here isn't quite right, since sorting will
               have been done on the original; but documentation options
               should be pretty rare anyway...  */
            /* 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, gettext (opt->name));
+           __argp_fmtstream_puts (stream,
+                                  dgettext (state->root_argp->argp_domain,
+                                            opt->name));
          }
     }
   else
          }
     }
   else
@@ -1063,7 +1137,8 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
            comma (uparams.long_opt_col, &pest);
            __argp_fmtstream_printf (stream, "--%s", opt->name);
            if (first_long_opt || uparams.dup_args)
            comma (uparams.long_opt_col, &pest);
            __argp_fmtstream_printf (stream, "--%s", opt->name);
            if (first_long_opt || uparams.dup_args)
-             arg (real, "=%s", "[=%s]", stream);
+             arg (real, "=%s", "[=%s]", state->root_argp->argp_domain,
+                  stream);
            else if (real->arg)
              hhstate->suppressed_dup_arg = 1;
          }
            else if (real->arg)
              hhstate->suppressed_dup_arg = 1;
          }
@@ -1073,16 +1148,19 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state,
   __argp_fmtstream_set_lmargin (stream, 0);
 
   if (pest.first)
   __argp_fmtstream_set_lmargin (stream, 0);
 
   if (pest.first)
-    /* Didn't print any switches, what's up?  */
-    if (!oshort (real) && !real->name)
-      /* This is a group header, print it nicely.  */
-      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.  */
+    {
+      /* Didn't print any switches, what's up?  */
+      if (!oshort (real) && !real->name)
+       /* This is a group header, print it nicely.  */
+       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
     {
   else
     {
-      const char *tstr = real->doc ? gettext (real->doc) : 0;
+      const char *tstr = real->doc ? dgettext (state->root_argp->argp_domain,
+                                              real->doc) : 0;
       const char *fstr = filter_doc (tstr, real->key, entry->argp, state);
       if (fstr && *fstr)
        {
       const char *fstr = filter_doc (tstr, real->key, entry->argp, state);
       if (fstr && *fstr)
        {
@@ -1129,11 +1207,11 @@ hol_help (struct hol *hol, const struct argp_state *state,
 
   if (hhstate.suppressed_dup_arg && uparams.dup_args_note)
     {
 
   if (hhstate.suppressed_dup_arg && uparams.dup_args_note)
     {
-      const char *tstr = _("\
+      const char *tstr = dgettext (state->root_argp->argp_domain, "\
 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,
 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);
+                                    state ? state->root_argp : 0, state);
       if (fstr && *fstr)
        {
          __argp_fmtstream_putc (stream, '\n');
       if (fstr && *fstr)
        {
          __argp_fmtstream_putc (stream, '\n');
@@ -1152,7 +1230,7 @@ optional for any corresponding short options.");
 static int
 add_argless_short_opt (const struct argp_option *opt,
                       const struct argp_option *real,
 static int
 add_argless_short_opt (const struct argp_option *opt,
                       const struct argp_option *real,
-                      void *cookie)
+                      const char *domain, void *cookie)
 {
   char **snao_end = cookie;
   if (!(opt->arg || real->arg)
 {
   char **snao_end = cookie;
   if (!(opt->arg || real->arg)
@@ -1166,7 +1244,7 @@ add_argless_short_opt (const struct argp_option *opt,
 static int
 usage_argful_short_opt (const struct argp_option *opt,
                        const struct argp_option *real,
 static int
 usage_argful_short_opt (const struct argp_option *opt,
                        const struct argp_option *real,
-                       void *cookie)
+                       const char *domain, void *cookie)
 {
   argp_fmtstream_t stream = cookie;
   const char *arg = opt->arg;
 {
   argp_fmtstream_t stream = cookie;
   const char *arg = opt->arg;
@@ -1177,7 +1255,7 @@ usage_argful_short_opt (const struct argp_option *opt,
 
   if (arg && !(flags & OPTION_NO_USAGE))
     {
 
   if (arg && !(flags & OPTION_NO_USAGE))
     {
-      arg = gettext (arg);
+      arg = dgettext (domain, arg);
 
       if (flags & OPTION_ARG_OPTIONAL)
        __argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg);
 
       if (flags & OPTION_ARG_OPTIONAL)
        __argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg);
@@ -1198,7 +1276,7 @@ usage_argful_short_opt (const struct argp_option *opt,
 static int
 usage_long_opt (const struct argp_option *opt,
                const struct argp_option *real,
 static int
 usage_long_opt (const struct argp_option *opt,
                const struct argp_option *real,
-               void *cookie)
+               const char *domain, void *cookie)
 {
   argp_fmtstream_t stream = cookie;
   const char *arg = opt->arg;
 {
   argp_fmtstream_t stream = cookie;
   const char *arg = opt->arg;
@@ -1208,16 +1286,18 @@ usage_long_opt (const struct argp_option *opt,
     arg = real->arg;
 
   if (! (flags & OPTION_NO_USAGE))
     arg = real->arg;
 
   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);
+    {
+      if (arg)
+       {
+         arg = dgettext (domain, 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;
 }
 
   return 0;
 }
@@ -1237,7 +1317,8 @@ hol_usage (struct hol *hol, argp_fmtstream_t stream)
       for (entry = hol->entries, nentries = hol->num_entries
           ; nentries > 0
           ; entry++, nentries--)
       for (entry = hol->entries, nentries = hol->num_entries
           ; nentries > 0
           ; entry++, nentries--)
-       hol_entry_short_iterate (entry, add_argless_short_opt, &snao_end);
+       hol_entry_short_iterate (entry, add_argless_short_opt,
+                                entry->argp->argp_domain, &snao_end);
       if (snao_end > short_no_arg_opts)
        {
          *snao_end++ = 0;
       if (snao_end > short_no_arg_opts)
        {
          *snao_end++ = 0;
@@ -1248,13 +1329,15 @@ hol_usage (struct hol *hol, argp_fmtstream_t stream)
       for (entry = hol->entries, nentries = hol->num_entries
           ; nentries > 0
           ; entry++, nentries--)
       for (entry = hol->entries, nentries = hol->num_entries
           ; nentries > 0
           ; entry++, nentries--)
-       hol_entry_short_iterate (entry, usage_argful_short_opt, stream);
+       hol_entry_short_iterate (entry, usage_argful_short_opt,
+                                entry->argp->argp_domain, stream);
 
       /* Finally, a list of long options (whew!).  */
       for (entry = hol->entries, nentries = hol->num_entries
           ; nentries > 0
           ; entry++, nentries--)
 
       /* Finally, a list of long options (whew!).  */
       for (entry = hol->entries, nentries = hol->num_entries
           ; nentries > 0
           ; entry++, nentries--)
-       hol_entry_long_iterate (entry, usage_long_opt, stream);
+       hol_entry_long_iterate (entry, usage_long_opt,
+                               entry->argp->argp_domain, stream);
     }
 }
 \f
     }
 }
 \f
@@ -1305,52 +1388,56 @@ argp_args_levels (const struct argp *argp)
    updated by this routine for the next call if ADVANCE is true.  True is
    returned as long as there are more patterns to output.  */
 static int
    updated by this routine for the next call if ADVANCE is true.  True is
    returned as long as there are more patterns to output.  */
 static int
-argp_args_usage (const struct argp *argp, char **levels, int advance,
-                argp_fmtstream_t stream)
+argp_args_usage (const struct argp *argp, const struct argp_state *state,
+                char **levels, int advance, argp_fmtstream_t stream)
 {
   char *our_level = *levels;
   int multiple = 0;
   const struct argp_child *child = argp->children;
 {
   char *our_level = *levels;
   int multiple = 0;
   const struct argp_child *child = argp->children;
-  const char *doc = gettext (argp->args_doc), *nl = 0;
+  const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = 0;
+  const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state);
 
 
-  if (doc)
+  if (fdoc)
     {
     {
-      nl = strchr (doc, '\n');
-      if (nl)
+      const char *cp = fdoc;
+      nl = __strchrnul (cp, '\n');
+      if (*nl != '\0')
        /* This is a `multi-level' args doc; advance to the correct position
           as determined by our state in LEVELS, and update LEVELS.  */
        {
          int i;
          multiple = 1;
          for (i = 0; i < *our_level; i++)
        /* This is a `multi-level' args doc; advance to the correct position
           as determined by our state in LEVELS, and update LEVELS.  */
        {
          int i;
          multiple = 1;
          for (i = 0; i < *our_level; i++)
-           doc = nl + 1, nl = strchr (doc, '\n');
+           cp = nl + 1, nl = __strchrnul (cp, '\n');
          (*levels)++;
        }
          (*levels)++;
        }
-      if (! nl)
-       nl = doc + strlen (doc);
 
       /* Manually do line wrapping so that it (probably) won't get wrapped at
         any embedded spaces.  */
 
       /* Manually do line wrapping so that it (probably) won't get wrapped at
         any embedded spaces.  */
-      space (stream, 1 + nl - doc);
+      space (stream, 1 + nl - cp);
 
 
-      __argp_fmtstream_write (stream, doc, nl - doc);
+      __argp_fmtstream_write (stream, cp, nl - cp);
     }
     }
+  if (fdoc && fdoc != tdoc)
+    free ((char *)fdoc);       /* Free user's modified doc string.  */
 
   if (child)
     while (child->argp)
 
   if (child)
     while (child->argp)
-      advance = !argp_args_usage ((child++)->argp, levels, advance, stream);
+      advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream);
 
   if (advance && multiple)
 
   if (advance && multiple)
-    /* Need to increment our level.  */
-    if (*nl)
-      /* There's more we can do here.  */
-      {
-       (*our_level)++;
-       advance = 0;            /* Our parent shouldn't advance also. */
-      }
-    else if (*our_level > 0)
-      /* We had multiple levels, but used them up; reset to zero.  */
-      *our_level = 0;
+    {
+      /* Need to increment our level.  */
+      if (*nl)
+       /* There's more we can do here.  */
+       {
+         (*our_level)++;
+         advance = 0;          /* Our parent shouldn't advance also. */
+       }
+      else if (*our_level > 0)
+       /* We had multiple levels, but used them up; reset to zero.  */
+       *our_level = 0;
+    }
 
   return !advance;
 }
 
   return !advance;
 }
@@ -1361,7 +1448,7 @@ argp_args_usage (const struct argp *argp, char **levels, int advance,
    following the `\v' character (nothing for strings without).  Each separate
    bit of documentation is separated a blank line, and if PRE_BLANK is true,
    then the first is as well.  If FIRST_ONLY is true, only the first
    following the `\v' character (nothing for strings without).  Each separate
    bit of documentation is separated a blank line, and if PRE_BLANK is true,
    then the first is as well.  If FIRST_ONLY is true, only the first
-   occurance is output.  Returns true if anything was output.  */
+   occurrence is output.  Returns true if anything was output.  */
 static int
 argp_doc (const struct argp *argp, const struct argp_state *state,
          int post, int pre_blank, int first_only,
 static int
 argp_doc (const struct argp *argp, const struct argp_state *state,
          int post, int pre_blank, int first_only,
@@ -1372,7 +1459,7 @@ argp_doc (const struct argp *argp, const struct argp_state *state,
   void *input = 0;
   int anything = 0;
   size_t inp_text_limit = 0;
   void *input = 0;
   int anything = 0;
   size_t inp_text_limit = 0;
-  const char *doc = gettext (argp->doc);
+  const char *doc = dgettext (argp->argp_domain, argp->doc);
   const struct argp_child *child = argp->children;
 
   if (doc)
   const struct argp_child *child = argp->children;
 
   if (doc)
@@ -1389,7 +1476,7 @@ argp_doc (const struct argp *argp, const struct argp_state *state,
     {
       if (inp_text_limit)
        /* Copy INP_TEXT so that it's nul-terminated.  */
     {
       if (inp_text_limit)
        /* Copy INP_TEXT so that it's nul-terminated.  */
-       inp_text = strndup (inp_text, inp_text_limit);
+       inp_text = __strndup (inp_text, inp_text_limit);
       input = __argp_input (argp, state);
       text =
        (*argp->help_filter) (post
       input = __argp_input (argp, state);
       text =
        (*argp->help_filter) (post
@@ -1463,12 +1550,21 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream,
   if (! stream)
     return;
 
   if (! stream)
     return;
 
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+  __flockfile (stream);
+#endif
+
   if (! uparams.valid)
     fill_in_uparams (state);
 
   fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0);
   if (! fs)
   if (! uparams.valid)
     fill_in_uparams (state);
 
   fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0);
   if (! fs)
-    return;
+    {
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+      __funlockfile (stream);
+#endif
+      return;
+    }
 
   if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG))
     {
 
   if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG))
     {
@@ -1493,22 +1589,28 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream,
       do
        {
          int old_lm;
       do
        {
          int old_lm;
-         int old_wm = __argp_fmtstream_set_wmargin (fs, USAGE_INDENT);
+         int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent);
          char *levels = pattern_levels;
 
          char *levels = pattern_levels;
 
-         __argp_fmtstream_printf (fs, "%s %s",
-                                  _(first_pattern ? "Usage:" : "  or: "),
-                                  name);
+         if (first_pattern)
+           __argp_fmtstream_printf (fs, "%s %s",
+                                    dgettext (argp->argp_domain, "Usage:"),
+                                    name);
+         else
+           __argp_fmtstream_printf (fs, "%s %s",
+                                    dgettext (argp->argp_domain, "  or: "),
+                                    name);
 
          /* We set the lmargin as well as the wmargin, because hol_usage
             manually wraps options with newline to avoid annoying breaks.  */
 
          /* We set the lmargin as well as the wmargin, because hol_usage
             manually wraps options with newline to avoid annoying breaks.  */
-         old_lm = __argp_fmtstream_set_lmargin (fs, USAGE_INDENT);
+         old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent);
 
          if (flags & ARGP_HELP_SHORT_USAGE)
            /* Just show where the options go.  */
            {
              if (hol->num_entries > 0)
 
          if (flags & ARGP_HELP_SHORT_USAGE)
            /* Just show where the options go.  */
            {
              if (hol->num_entries > 0)
-               __argp_fmtstream_puts (fs, _(" [OPTION...]"));
+               __argp_fmtstream_puts (fs, dgettext (argp->argp_domain,
+                                                    " [OPTION...]"));
            }
          else
            /* Actually print the options.  */
            }
          else
            /* Actually print the options.  */
@@ -1517,7 +1619,7 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream,
              flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once.  */
            }
 
              flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once.  */
            }
 
-         more_patterns = argp_args_usage (argp, &levels, 1, fs);
+         more_patterns = argp_args_usage (argp, state, &levels, 1, fs);
 
          __argp_fmtstream_set_wmargin (fs, old_wm);
          __argp_fmtstream_set_lmargin (fs, old_lm);
 
          __argp_fmtstream_set_wmargin (fs, old_wm);
          __argp_fmtstream_set_lmargin (fs, old_lm);
@@ -1535,7 +1637,7 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream,
 
   if (flags & ARGP_HELP_SEE)
     {
 
   if (flags & ARGP_HELP_SEE)
     {
-      __argp_fmtstream_printf (fs, _("\
+      __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\
 Try `%s --help' or `%s --usage' for more information.\n"),
                               name, name);
       anything = 1;
 Try `%s --help' or `%s --usage' for more information.\n"),
                               name, name);
       anything = 1;
@@ -1562,11 +1664,16 @@ Try `%s --help' or `%s --usage' for more information.\n"),
     {
       if (anything)
        __argp_fmtstream_putc (fs, '\n');
     {
       if (anything)
        __argp_fmtstream_putc (fs, '\n');
-      __argp_fmtstream_printf (fs, _("Report bugs to %s.\n"),
+      __argp_fmtstream_printf (fs, dgettext (argp->argp_domain,
+                                            "Report bugs to %s.\n"),
                               argp_program_bug_address);
       anything = 1;
     }
 
                               argp_program_bug_address);
       anything = 1;
     }
 
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+  __funlockfile (stream);
+#endif
+
   if (hol)
     hol_free (hol);
 
   if (hol)
     hol_free (hol);
 
@@ -1584,6 +1691,32 @@ void __argp_help (const struct argp *argp, FILE *stream,
 weak_alias (__argp_help, argp_help)
 #endif
 
 weak_alias (__argp_help, argp_help)
 #endif
 
+#ifndef _LIBC
+char *__argp_basename (char *name)
+{
+  char *short_name = strrchr (name, '/');
+  return short_name ? short_name + 1 : name;
+}
+
+char *
+__argp_short_program_name (void)
+{
+# if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME
+  return program_invocation_short_name;
+# elif HAVE_DECL_PROGRAM_INVOCATION_NAME
+  return __argp_basename (program_invocation_name);
+# else
+  /* FIXME: What now? Miles suggests that it is better to use NULL,
+     but currently the value is passed on directly to fputs_unlocked,
+     so that requires more changes. */
+# if __GNUC__
+#  warning No reasonable value to return
+# endif /* __GNUC__ */
+  return "";
+# endif
+}
+#endif
+
 /* Output, if appropriate, a usage message for STATE to STREAM.  FLAGS are
    from the set ARGP_HELP_*.  */
 void
 /* Output, if appropriate, a usage message for STATE to STREAM.  FLAGS are
    from the set ARGP_HELP_*.  */
 void
@@ -1594,8 +1727,8 @@ __argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)
       if (state && (state->flags & ARGP_LONG_ONLY))
        flags |= ARGP_HELP_LONG_ONLY;
 
       if (state && (state->flags & ARGP_LONG_ONLY))
        flags |= ARGP_HELP_LONG_ONLY;
 
-      _help (state ? state->argp : 0, state, stream, flags,
-            state ? state->name : program_invocation_short_name);
+      _help (state ? state->root_argp : 0, state, stream, flags,
+            state ? state->name : __argp_short_program_name ());
 
       if (!state || ! (state->flags & ARGP_NO_EXIT))
        {
 
       if (!state || ! (state->flags & ARGP_NO_EXIT))
        {
@@ -1624,17 +1757,40 @@ __argp_error (const struct argp_state *state, const char *fmt, ...)
        {
          va_list ap;
 
        {
          va_list ap;
 
-         fputs (state ? state->name : program_invocation_short_name, stream);
-         putc (':', stream);
-         putc (' ', stream);
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+         __flockfile (stream);
+#endif
 
          va_start (ap, fmt);
 
          va_start (ap, fmt);
+
+#ifdef _LIBC
+         char *buf;
+
+         if (vasprintf (&buf, fmt, ap) < 0)
+           buf = NULL;
+
+         __fxprintf (stream, "%s: %s\n",
+                     state ? state->name : __argp_short_program_name (), buf);
+
+         free (buf);
+#else
+         fputs_unlocked (state ? state->name : __argp_short_program_name (),
+                         stream);
+         putc_unlocked (':', stream);
+         putc_unlocked (' ', stream);
+
          vfprintf (stream, fmt, ap);
          vfprintf (stream, fmt, ap);
-         va_end (ap);
 
 
-         putc ('\n', stream);
+         putc_unlocked ('\n', stream);
+#endif
 
          __argp_state_help (state, stream, ARGP_HELP_STD_ERR);
 
          __argp_state_help (state, stream, ARGP_HELP_STD_ERR);
+
+         va_end (ap);
+
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+         __funlockfile (stream);
+#endif
        }
     }
 }
        }
     }
 }
@@ -1660,28 +1816,70 @@ __argp_failure (const struct argp_state *state, int status, int errnum,
 
       if (stream)
        {
 
       if (stream)
        {
-         fputs (state ? state->name : program_invocation_short_name, stream);
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+         __flockfile (stream);
+#endif
+
+#ifdef _LIBC
+         __fxprintf (stream, "%s",
+                     state ? state->name : __argp_short_program_name ());
+#else
+         fputs_unlocked (state ? state->name : __argp_short_program_name (),
+                         stream);
+#endif
 
          if (fmt)
            {
              va_list ap;
 
 
          if (fmt)
            {
              va_list ap;
 
-             putc (':', stream);
-             putc (' ', stream);
-
              va_start (ap, fmt);
              va_start (ap, fmt);
+#ifdef _LIBC
+             char *buf;
+
+             if (vasprintf (&buf, fmt, ap) < 0)
+               buf = NULL;
+
+             __fxprintf (stream, ": %s", buf);
+
+             free (buf);
+#else
+             putc_unlocked (':', stream);
+             putc_unlocked (' ', stream);
+
              vfprintf (stream, fmt, ap);
              vfprintf (stream, fmt, ap);
+#endif
+
              va_end (ap);
            }
 
          if (errnum)
            {
              va_end (ap);
            }
 
          if (errnum)
            {
-             putc (':', stream);
-             putc (' ', stream);
+             char buf[200];
+
+#ifdef _LIBC
+             __fxprintf (stream, ": %s",
+                         __strerror_r (errnum, buf, sizeof (buf)));
+#else
+             putc_unlocked (':', stream);
+             putc_unlocked (' ', stream);
+# ifdef HAVE_STRERROR_R
+             fputs (__strerror_r (errnum, buf, sizeof (buf)), stream);
+# else
              fputs (strerror (errnum), stream);
              fputs (strerror (errnum), stream);
+# endif
+#endif
            }
 
            }
 
-         putc ('\n', stream);
+#ifdef USE_IN_LIBIO
+         if (_IO_fwide (stream, 0) > 0)
+           putwc_unlocked (L'\n', stream);
+         else
+#endif
+           putc_unlocked ('\n', stream);
+
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+         __funlockfile (stream);
+#endif
 
          if (status && (!state || !(state->flags & ARGP_NO_EXIT)))
            exit (status);
 
          if (status && (!state || !(state->flags & ARGP_NO_EXIT)))
            exit (status);