2005-08-08 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / argp / argp-help.c
index 5ab6aa7..abd59c1 100644 (file)
@@ -1,22 +1,22 @@
 /* Hierarchial argument parsing help output
-   Copyright (C) 1995, 1996, 1997, 1998, 1999 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
-   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
-   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
 #include <config.h>
 #endif
 
-#ifndef alloca
-# ifdef __GNUC__
-#  define alloca __builtin_alloca
-#  define HAVE_ALLOCA 1
+/* AIX requires this to be the first thing in the file.  */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H || defined _LIBC
+#  include <alloca.h>
 # else
-#  if defined HAVE_ALLOCA_H || defined _LIBC
-#   include <alloca.h>
+#  ifdef _AIX
+#pragma alloca
 #  else
-#   ifdef _AIX
- #pragma alloca
-#   else
-#    ifndef alloca
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
 char *alloca ();
-#    endif
 #   endif
 #  endif
 # endif
@@ -50,21 +46,45 @@ char *alloca ();
 #include <string.h>
 #include <assert.h>
 #include <stdarg.h>
-#include <malloc.h>
 #include <ctype.h>
+#include <limits.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
 
 #ifndef _
 /* This is for other GNU distributions with internationalized messages.  */
-# ifdef HAVE_LIBINTL_H
+# 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
 
+#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"
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
 \f
 /* User-selectable (using an environment variable) formatting parameters.
 
@@ -357,7 +377,7 @@ struct hol_cluster
   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;
 
@@ -434,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);
+#if SIZE_MAX <= UINT_MAX
+      assert (hol->num_entries <= SIZE_MAX / sizeof (struct hol_entry));
+#endif
 
       /* Fill in the entries.  */
       so = hol->short_options;
@@ -513,7 +536,7 @@ hol_free (struct hol *hol)
   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,
@@ -539,6 +562,7 @@ hol_entry_short_iterate (const struct hol_entry *entry,
 }
 
 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,
@@ -825,6 +849,11 @@ hol_append (struct hol *hol, struct hol *more)
          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
+
          __mempcpy (__mempcpy (entries, hol->entries,
                                hol->num_entries * sizeof (struct hol_entry)),
                     more->entries,
@@ -1419,7 +1448,7 @@ argp_args_usage (const struct argp *argp, const struct argp_state *state,
    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,
@@ -1521,7 +1550,9 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream,
   if (! stream)
     return;
 
-  flockfile (stream);
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+  __flockfile (stream);
+#endif
 
   if (! uparams.valid)
     fill_in_uparams (state);
@@ -1529,7 +1560,9 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream,
   fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0);
   if (! fs)
     {
-      funlockfile (stream);
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+      __funlockfile (stream);
+#endif
       return;
     }
 
@@ -1637,7 +1670,9 @@ Try `%s --help' or `%s --usage' for more information.\n"),
       anything = 1;
     }
 
-  funlockfile (stream);
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+  __funlockfile (stream);
+#endif
 
   if (hol)
     hol_free (hol);
@@ -1656,6 +1691,32 @@ void __argp_help (const struct argp *argp, FILE *stream,
 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
@@ -1667,7 +1728,7 @@ __argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)
        flags |= ARGP_HELP_LONG_ONLY;
 
       _help (state ? state->root_argp : 0, state, stream, flags,
-            state ? state->name : program_invocation_short_name);
+            state ? state->name : __argp_short_program_name ());
 
       if (!state || ! (state->flags & ARGP_NO_EXIT))
        {
@@ -1696,22 +1757,40 @@ __argp_error (const struct argp_state *state, const char *fmt, ...)
        {
          va_list ap;
 
-         flockfile (stream);
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+         __flockfile (stream);
+#endif
+
+         va_start (ap, fmt);
+
+#ifdef _LIBC
+         char *buf;
+
+         if (vasprintf (&buf, fmt, ap) < 0)
+           buf = NULL;
 
-         fputs_unlocked (state ? state->name : program_invocation_short_name,
+         __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);
 
-         va_start (ap, fmt);
          vfprintf (stream, fmt, ap);
-         va_end (ap);
 
          putc_unlocked ('\n', stream);
+#endif
 
          __argp_state_help (state, stream, ARGP_HELP_STD_ERR);
 
-         funlockfile (stream);
+         va_end (ap);
+
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+         __funlockfile (stream);
+#endif
        }
     }
 }
@@ -1737,33 +1816,70 @@ __argp_failure (const struct argp_state *state, int status, int errnum,
 
       if (stream)
        {
-         flockfile (stream);
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+         __flockfile (stream);
+#endif
 
-         fputs_unlocked (state ? state->name : program_invocation_short_name,
+#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;
 
+             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);
 
-             va_start (ap, fmt);
              vfprintf (stream, fmt, ap);
+#endif
+
              va_end (ap);
            }
 
          if (errnum)
            {
+             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);
+# endif
+#endif
            }
 
-         putc_unlocked ('\n', stream);
+#ifdef USE_IN_LIBIO
+         if (_IO_fwide (stream, 0) > 0)
+           putwc_unlocked (L'\n', stream);
+         else
+#endif
+           putc_unlocked ('\n', stream);
 
-         funlockfile (stream);
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+         __funlockfile (stream);
+#endif
 
          if (status && (!state || !(state->flags & ARGP_NO_EXIT)))
            exit (status);