[PATCH] libc: Added argp support

Salvatore CRO' salvatore.cro at st.com
Wed Jul 1 22:58:43 UTC 2009


Added argp support as an advanced set of functionalities for
parsing unix-style argument vectors.
In addition to the common getopt interface, it provides
automatic response to `--help' and `--version' options and
use of custom parser in conjunction with argp native option
parser, among others.
Argp support is required by elfutils package.

Signed-off-by: Salvatore Cro <salvatore.cro at st.com>
---
 extra/Configs/Config.in    |   16 +
 libc/Makefile.in           |    1 +
 libc/argp/Makefile         |   13 +
 libc/argp/Makefile.in      |   27 +
 libc/argp/argp-ba.c        |   26 +
 libc/argp/argp-eexst.c     |   32 +
 libc/argp/argp-fmtstream.c |  439 +++++++++++
 libc/argp/argp-fs-xinl.c   |   44 +
 libc/argp/argp-help.c      | 1882 ++++++++++++++++++++++++++++++++++++++++++++
 libc/argp/argp-parse.c     |  949 ++++++++++++++++++++++
 libc/argp/argp-pv.c        |   25 +
 libc/argp/argp-pvh.c       |   32 +
 libc/argp/argp-xinl.c      |   35 +
 13 files changed, 3521 insertions(+), 0 deletions(-)
 create mode 100644 libc/argp/Makefile
 create mode 100644 libc/argp/Makefile.in
 create mode 100644 libc/argp/argp-ba.c
 create mode 100644 libc/argp/argp-eexst.c
 create mode 100644 libc/argp/argp-fmtstream.c
 create mode 100644 libc/argp/argp-fs-xinl.c
 create mode 100644 libc/argp/argp-help.c
 create mode 100644 libc/argp/argp-parse.c
 create mode 100644 libc/argp/argp-pv.c
 create mode 100644 libc/argp/argp-pvh.c
 create mode 100644 libc/argp/argp-xinl.c

diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in
index 6c37039..d7c2bba 100644
--- a/extra/Configs/Config.in
+++ b/extra/Configs/Config.in
@@ -1656,6 +1656,22 @@ config UCLIBC_HAS_GNU_GETSUBOPT
 	  smaller SUSv3 compatible getsubopt().
 
 	  Most people will answer Y.
+
+config UCLIBC_HAS_ARGP
+	bool "Support argp"
+	default n
+	help
+	  Argp is an interface for parsing unix-style argument vectors. Unlike
+	  the common getopt interface, it provides many advanced features in 
+	  addition to parsing options, such as automatic output in response to
+	  `--help' and `--version' options.
+	  A library can export an argp option parser, which programs can easily
+	  use in conjunction with their own option parser.
+
+	  Argp support is needed by elfutils libdw. 
+
+	  Most people can safely answer N.
+
 endmenu
 
 
diff --git a/libc/Makefile.in b/libc/Makefile.in
index 7297e92..691b402 100644
--- a/libc/Makefile.in
+++ b/libc/Makefile.in
@@ -25,6 +25,7 @@ libc_FULL_NAME := libuClibc-$(VERSION).so
 # this comes first, so duplicate removal works correctly
 include $(libc_DIR)/sysdeps/Makefile.in
 
+include $(libc_DIR)/argp/Makefile.in
 include $(libc_DIR)/misc/Makefile.in
 include $(libc_DIR)/pwd_grp/Makefile.in
 include $(libc_DIR)/stdio/Makefile.in
diff --git a/libc/argp/Makefile b/libc/argp/Makefile
new file mode 100644
index 0000000..11f362a
--- /dev/null
+++ b/libc/argp/Makefile
@@ -0,0 +1,13 @@
+# Makefile for uClibc
+#
+# Copyright (C) 2000-2005 Erik Andersen <andersen at uclibc.org>
+#
+# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+#
+
+top_srcdir=../../
+top_builddir=../../
+all: objs
+include $(top_builddir)Rules.mak
+include Makefile.in
+include $(top_srcdir)Makerules
diff --git a/libc/argp/Makefile.in b/libc/argp/Makefile.in
new file mode 100644
index 0000000..08e4c47
--- /dev/null
+++ b/libc/argp/Makefile.in
@@ -0,0 +1,27 @@
+# Makefile for uClibc
+#
+# Copyright (C) 2009  STMicroelectronics Ltd.
+# Author: Salvatore Cro <salvatore.cro at st.com>
+
+# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+#
+
+CSRC := $(addsuffix .c,$(addprefix argp-, ba fmtstream fs-xinl help parse pv pvh xinl eexst))
+
+CFLAGS-argp-xinl.c = -fgnu89-inline
+CFLAGS-argp-fmtstream.c = -fexceptions
+
+CFLAGS-argp-help.c = -fexceptions
+ARGP_DIR := $(top_srcdir)libc/argp
+ARGP_OUT := $(top_builddir)libc/argp
+
+ARGP_SRC := $(patsubst %.c,$(ARGP_DIR)/%.c,$(CSRC))
+ARGP_OBJ := $(patsubst %.c,$(ARGP_OUT)/%.o,$(CSRC))
+
+libc-$(UCLIBC_HAS_ARGP) += $(ARGP_OBJ)
+
+objclean-$(UCLIBC_HAS_ARGP) += argp_clean
+
+argp_clean:
+	$(do_rm) $(addprefix $(ARGP_OUT)/*., o os oS)
+
diff --git a/libc/argp/argp-ba.c b/libc/argp/argp-ba.c
new file mode 100644
index 0000000..f5270c8
--- /dev/null
+++ b/libc/argp/argp-ba.c
@@ -0,0 +1,26 @@
+/* Default definition for ARGP_PROGRAM_BUG_ADDRESS.
+   Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.  */
+
+/* If set by the user program, it should point to string that is the
+   bug-reporting address for the program.  It will be printed by 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.'.  */
+const char *argp_program_bug_address;
diff --git a/libc/argp/argp-eexst.c b/libc/argp/argp-eexst.c
new file mode 100644
index 0000000..9fb0611
--- /dev/null
+++ b/libc/argp/argp-eexst.c
@@ -0,0 +1,32 @@
+/* Default definition for ARGP_ERR_EXIT_STATUS
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sysexits.h>
+
+#include <argp.h>
+
+/* The exit status that argp will use when exiting due to a parsing error.
+   If not defined or set by the user program, this defaults to EX_USAGE from
+   <sysexits.h>.  */
+error_t argp_err_exit_status = EX_USAGE;
diff --git a/libc/argp/argp-fmtstream.c b/libc/argp/argp-fmtstream.c
new file mode 100644
index 0000000..2247cf6
--- /dev/null
+++ b/libc/argp/argp-fmtstream.c
@@ -0,0 +1,439 @@
+/* Word-wrapping and line-truncating streams
+   Copyright (C) 1997-1999,2001,2002,2003,2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.
+
+   Modified for uClibc by: Salvatore Cro <salvatore.cro at st.com>
+*/
+
+/* This package emulates glibc `line_wrap_stream' semantics for systems that
+   don't have that.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "argp-fmtstream.h"
+
+#ifndef ARGP_FMTSTREAM_USE_LINEWRAP
+
+#ifndef isblank
+#define isblank(ch) ((ch)==' ' || (ch)=='\t')
+#endif
+
+#if defined _LIBC && defined USE_IN_LIBIO
+# include <wchar.h>
+# include <libio/libioP.h>
+# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a)
+#else
+# define __vsnprintf(s, l, f, a) vsnprintf (s, l, f, a)
+#endif
+
+#define INIT_BUF_SIZE 200
+#define PRINTF_SIZE_GUESS 150
+
+/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines
+   written on it with LMARGIN spaces and limits them to RMARGIN columns
+   total.  If WMARGIN >= 0, words that extend past RMARGIN are wrapped by
+   replacing the whitespace before them with a newline and WMARGIN spaces.
+   Otherwise, chars beyond RMARGIN are simply dropped until a newline.
+   Returns NULL if there was an error.  */
+argp_fmtstream_t
+__argp_make_fmtstream (FILE *stream,
+		       size_t lmargin, size_t rmargin, ssize_t wmargin)
+{
+  argp_fmtstream_t fs;
+
+  fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream));
+  if (fs != NULL)
+    {
+      fs->stream = stream;
+
+      fs->lmargin = lmargin;
+      fs->rmargin = rmargin;
+      fs->wmargin = wmargin;
+      fs->point_col = 0;
+      fs->point_offs = 0;
+
+      fs->buf = (char *) malloc (INIT_BUF_SIZE);
+      if (! fs->buf)
+	{
+	  free (fs);
+	  fs = 0;
+	}
+      else
+	{
+	  fs->p = fs->buf;
+	  fs->end = fs->buf + INIT_BUF_SIZE;
+	}
+    }
+
+  return fs;
+}
+#if 0
+/* Not exported.  */
+#ifdef weak_alias
+weak_alias (__argp_make_fmtstream, argp_make_fmtstream)
+#endif
+#endif
+
+/* Flush FS to its stream, and free it (but don't close the stream).  */
+void
+__argp_fmtstream_free (argp_fmtstream_t fs)
+{
+  __argp_fmtstream_update (fs);
+  if (fs->p > fs->buf)
+    {
+#ifdef USE_IN_LIBIO
+      __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
+#else
+      fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
+#endif
+    }
+  free (fs->buf);
+  free (fs);
+}
+#if 0
+/* Not exported.  */
+#ifdef weak_alias
+weak_alias (__argp_fmtstream_free, argp_fmtstream_free)
+#endif
+#endif
+
+/* Process FS's buffer so that line wrapping is done from POINT_OFFS to the
+   end of its buffer.  This code is mostly from glibc stdio/linewrap.c.  */
+void
+__argp_fmtstream_update (argp_fmtstream_t fs)
+{
+  char *buf, *nl;
+  size_t len;
+
+  /* Scan the buffer for newlines.  */
+  buf = fs->buf + fs->point_offs;
+  while (buf < fs->p)
+    {
+      size_t r;
+
+      if (fs->point_col == 0 && fs->lmargin != 0)
+	{
+	  /* We are starting a new line.  Print spaces to the left margin.  */
+	  const size_t pad = fs->lmargin;
+	  if (fs->p + pad < fs->end)
+	    {
+	      /* We can fit in them in the buffer by moving the
+		 buffer text up and filling in the beginning.  */
+	      memmove (buf + pad, buf, fs->p - buf);
+	      fs->p += pad; /* Compensate for bigger buffer. */
+	      memset (buf, ' ', pad); /* Fill in the spaces.  */
+	      buf += pad; /* Don't bother searching them.  */
+	    }
+	  else
+	    {
+	      /* No buffer space for spaces.  Must flush.  */
+	      size_t i;
+	      for (i = 0; i < pad; i++)
+		{
+#ifdef USE_IN_LIBIO
+		  if (_IO_fwide (fs->stream, 0) > 0)
+		    putwc_unlocked (L' ', fs->stream);
+		  else
+#endif
+		    putc_unlocked (' ', fs->stream);
+		}
+	    }
+	  fs->point_col = pad;
+	}
+
+      len = fs->p - buf;
+      nl = memchr (buf, '\n', len);
+
+      if (fs->point_col < 0)
+	fs->point_col = 0;
+
+      if (!nl)
+	{
+	  /* The buffer ends in a partial line.  */
+
+	  if (fs->point_col + len < fs->rmargin)
+	    {
+	      /* The remaining buffer text is a partial line and fits
+		 within the maximum line width.  Advance point for the
+		 characters to be written and stop scanning.  */
+	      fs->point_col += len;
+	      break;
+	    }
+	  else
+	    /* Set the end-of-line pointer for the code below to
+	       the end of the buffer.  */
+	    nl = fs->p;
+	}
+      else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin)
+	{
+	  /* The buffer contains a full line that fits within the maximum
+	     line width.  Reset point and scan the next line.  */
+	  fs->point_col = 0;
+	  buf = nl + 1;
+	  continue;
+	}
+
+      /* This line is too long.  */
+      r = fs->rmargin - 1;
+
+      if (fs->wmargin < 0)
+	{
+	  /* Truncate the line by overwriting the excess with the
+	     newline and anything after it in the buffer.  */
+	  if (nl < fs->p)
+	    {
+	      memmove (buf + (r - fs->point_col), nl, fs->p - nl);
+	      fs->p -= buf + (r - fs->point_col) - nl;
+	      /* Reset point for the next line and start scanning it.  */
+	      fs->point_col = 0;
+	      buf += r + 1; /* Skip full line plus \n. */
+	    }
+	  else
+	    {
+	      /* The buffer ends with a partial line that is beyond the
+		 maximum line width.  Advance point for the characters
+		 written, and discard those past the max from the buffer.  */
+	      fs->point_col += len;
+	      fs->p -= fs->point_col - r;
+	      break;
+	    }
+	}
+      else
+	{
+	  /* Do word wrap.  Go to the column just past the maximum line
+	     width and scan back for the beginning of the word there.
+	     Then insert a line break.  */
+
+	  char *p, *nextline;
+	  int i;
+
+	  p = buf + (r + 1 - fs->point_col);
+	  while (p >= buf && !isblank (*p))
+	    --p;
+	  nextline = p + 1;	/* This will begin the next line.  */
+
+	  if (nextline > buf)
+	    {
+	      /* Swallow separating blanks.  */
+	      if (p >= buf)
+		do
+		  --p;
+		while (p >= buf && isblank (*p));
+	      nl = p + 1;	/* The newline will replace the first blank. */
+	    }
+	  else
+	    {
+	      /* A single word that is greater than the maximum line width.
+		 Oh well.  Put it on an overlong line by itself.  */
+	      p = buf + (r + 1 - fs->point_col);
+	      /* Find the end of the long word.  */
+	      do
+		++p;
+	      while (p < nl && !isblank (*p));
+	      if (p == nl)
+		{
+		  /* It already ends a line.  No fussing required.  */
+		  fs->point_col = 0;
+		  buf = nl + 1;
+		  continue;
+		}
+	      /* We will move the newline to replace the first blank.  */
+	      nl = p;
+	      /* Swallow separating blanks.  */
+	      do
+		++p;
+	      while (isblank (*p));
+	      /* The next line will start here.  */
+	      nextline = p;
+	    }
+
+	  /* 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)
+	      && fs->p > nextline)
+	    {
+	      /* The margin needs more blanks than we removed.  */
+	      if (fs->end - fs->p > fs->wmargin + 1)
+		/* Make some space for them.  */
+		{
+		  size_t mv = fs->p - nextline;
+		  memmove (nl + 1 + fs->wmargin, nextline, mv);
+		  nextline = nl + 1 + fs->wmargin;
+		  len = nextline + mv - buf;
+		  *nl++ = '\n';
+		}
+	      else
+		/* Output the first line so we can use the space.  */
+		{
+#if defined _LIBC && defined USE_IN_LIBIO
+		  __fxprintf (fs->stream, "%.*s\n",
+			      (int) (nl - fs->buf), fs->buf);
+#else
+		  if (nl > fs->buf)
+		    fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream);
+		  putc_unlocked ('\n', fs->stream);
+#endif
+
+		  len += buf - fs->buf;
+		  nl = buf = fs->buf;
+		}
+	    }
+	  else
+	    /* We can fit the newline and blanks in before
+	       the next word.  */
+	    *nl++ = '\n';
+
+	  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++ = ' ';
+	  else
+	    for (i = 0; i < fs->wmargin; ++i)
+#ifdef USE_IN_LIBIO
+	      if (_IO_fwide (fs->stream, 0) > 0)
+		putwc_unlocked (L' ', fs->stream);
+	      else
+#endif
+		putc_unlocked (' ', fs->stream);
+
+	  /* Copy the tail of the original buffer into the current buffer
+	     position.  */
+	  if (nl < nextline)
+	    memmove (nl, nextline, buf + len - nextline);
+	  len -= nextline - buf;
+
+	  /* Continue the scan on the remaining lines in the buffer.  */
+	  buf = nl;
+
+	  /* Restore bufp to include all the remaining text.  */
+	  fs->p = nl + len;
+
+	  /* Reset the counter of what has been output this line.  If wmargin
+	     is 0, we want to avoid the lmargin getting added, so we set
+	     point_col to a magic value of -1 in that case.  */
+	  fs->point_col = fs->wmargin ? fs->wmargin : -1;
+	}
+    }
+
+  /* Remember that we've scanned as far as the end of the buffer.  */
+  fs->point_offs = fs->p - fs->buf;
+}
+
+/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by
+   growing the buffer, or by flushing it.  True is returned iff we succeed. */
+int
+__argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount)
+{
+  if ((size_t) (fs->end - fs->p) < amount)
+    {
+      ssize_t wrote;
+
+      /* Flush FS's buffer.  */
+      __argp_fmtstream_update (fs);
+
+#if defined _LIBC && defined USE_IN_LIBIO
+      __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf);
+      wrote = fs->p - fs->buf;
+#else
+      wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream);
+#endif
+      if (wrote == fs->p - fs->buf)
+	{
+	  fs->p = fs->buf;
+	  fs->point_offs = 0;
+	}
+      else
+	{
+	  fs->p -= wrote;
+	  fs->point_offs -= wrote;
+	  memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf);
+	  return 0;
+	}
+
+      if ((size_t) (fs->end - fs->buf) < amount)
+	/* Gotta grow the buffer.  */
+	{
+	  size_t old_size = fs->end - fs->buf;
+	  size_t new_size = old_size + amount;
+	  char *new_buf;
+
+	  if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size)))
+	    {
+	      __set_errno (ENOMEM);
+	      return 0;
+	    }
+
+	  fs->buf = new_buf;
+	  fs->end = new_buf + new_size;
+	  fs->p = fs->buf;
+	}
+    }
+
+  return 1;
+}
+
+ssize_t
+__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...)
+{
+  int out;
+  size_t avail;
+  size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */
+
+  do
+    {
+      va_list args;
+
+      if (! __argp_fmtstream_ensure (fs, size_guess))
+	return -1;
+
+      va_start (args, fmt);
+      avail = fs->end - fs->p;
+      out = __vsnprintf (fs->p, avail, fmt, args);
+      va_end (args);
+      if ((size_t) out >= avail)
+	size_guess = out + 1;
+    }
+  while ((size_t) out >= avail);
+
+  fs->p += out;
+
+  return out;
+}
+#if 0
+/* Not exported.  */
+#ifdef weak_alias
+weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf)
+#endif
+#endif
+
+#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */
diff --git a/libc/argp/argp-fs-xinl.c b/libc/argp/argp-fs-xinl.c
new file mode 100644
index 0000000..442ffa0
--- /dev/null
+++ b/libc/argp/argp-fs-xinl.c
@@ -0,0 +1,44 @@
+/* Real definitions for extern inline functions in argp-fmtstream.h
+   Copyright (C) 1997, 2003, 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define ARGP_FS_EI
+#undef __OPTIMIZE__
+#define __OPTIMIZE__ 1
+#include "argp-fmtstream.h"
+
+#if 0
+/* Not exported.  */
+/* Add weak aliases.  */
+#if _LIBC - 0 && !defined (ARGP_FMTSTREAM_USE_LINEWRAP) && defined (weak_alias)
+
+weak_alias (__argp_fmtstream_putc, argp_fmtstream_putc)
+weak_alias (__argp_fmtstream_puts, argp_fmtstream_puts)
+weak_alias (__argp_fmtstream_write, argp_fmtstream_write)
+weak_alias (__argp_fmtstream_set_lmargin, argp_fmtstream_set_lmargin)
+weak_alias (__argp_fmtstream_set_rmargin, argp_fmtstream_set_rmargin)
+weak_alias (__argp_fmtstream_set_wmargin, argp_fmtstream_set_wmargin)
+weak_alias (__argp_fmtstream_point, argp_fmtstream_point)
+
+#endif
+#endif
diff --git a/libc/argp/argp-help.c b/libc/argp/argp-help.c
new file mode 100644
index 0000000..6f03e22
--- /dev/null
+++ b/libc/argp/argp-help.c
@@ -0,0 +1,1882 @@
+/* Hierarchial argument parsing help output
+   Copyright (C) 1995-2003, 2004, 2005, 2006, 2007
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.
+
+   Modified for uClibc by: Salvatore Cro <salvatore.cro at st.com>
+*/
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE	1
+#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 <ctype.h>
+#include <limits.h>
+#ifdef _LIBC
+# include <wchar.h>
+#endif
+
+#include <features.h>
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.  */
+# if (defined HAVE_LIBINTL_H || defined _LIBC) && defined __UCLIBC_HAS_GETTEXT_AWARENESS__
+#  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 <stdbool.h>
+#include <stdint.h>
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+/* 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 */
+#define OPT_DOC_COL  29		/* column in which option text starts */
+#define HEADER_COL    1		/* column in which group headers are printed */
+#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;
+};
+
+/* 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
+};
+
+/* A particular uparam, and what the user name is.  */
+struct uparam_name
+{
+  const char name[14];		/* User name.  */
+  bool is_bool;			/* Whether it's `boolean'.  */
+  uint8_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",       true, offsetof (struct uparams, dup_args) },
+  { "dup-args-note",  true, offsetof (struct uparams, dup_args_note) },
+  { "short-opt-col",  false, offsetof (struct uparams, short_opt_col) },
+  { "long-opt-col",   false, offsetof (struct uparams, long_opt_col) },
+  { "doc-opt-col",    false, offsetof (struct uparams, doc_opt_col) },
+  { "opt-doc-col",    false, offsetof (struct uparams, opt_doc_col) },
+  { "header-col",     false, offsetof (struct uparams, header_col) },
+  { "usage-indent",   false, offsetof (struct uparams, usage_indent) },
+  { "rmargin",        false, offsetof (struct uparams, rmargin) }
+};
+#define nuparam_names (sizeof (uparam_names) / sizeof (uparam_names[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);
+	      }
+
+	    un = uparam_names;
+	    size_t u;
+	    for (u = 0; u < nuparam_names; ++un, ++u)
+	      if (strlen (un->name) == var_len
+		  && strncmp (var, un->name, var_len) == 0)
+		{
+		  if (unspec && !un->is_bool)
+		    argp_failure (state, 0, 0,
+				    dgettext (state == NULL ? NULL
+					      : 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 (u == nuparam_names)
+	      argp_failure (state, 0, 0,
+			      dgettext (state == NULL ? NULL
+					: state->root_argp->argp_domain, "\
+%.*s: Unknown ARGP_HELP_FMT parameter"),
+			      (int) var_len, var);
+
+	    var = arg;
+	    if (*var == ',')
+	      var++;
+	  }
+	else if (*var)
+	  {
+	    argp_failure (state, 0, 0,
+			    dgettext (state == NULL ? NULL
+				      : state->root_argp->argp_domain,
+				      "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))
+
+/* Returns true if OPT is an alias for an earlier option.  */
+#define oalias(opt) ((opt)->flags & OPTION_ALIAS)
+
+/* Returns true if OPT is an documentation-only entry.  */
+#define odoc(opt) ((opt)->flags & OPTION_DOC)
+
+/* Returns true if OPT is the end-of-list marker for a list of options.  */
+#define oend(opt) __option_is_end (opt)
+
+/* Returns true if OPT has a short option.  */
+#define oshort(opt) __option_is_short (opt)
+
+/*
+   The help format for a particular option is like:
+
+     -xARG, -yARG, --long1=ARG, --long2=ARG        Documentation...
+
+   Where ARG will be omitted if there's no argument, for this option, or
+   will be surrounded by "[" and "]" appropiately if the argument is
+   optional.  The documentation string is word-wrapped appropiately, and if
+   the list of options is long enough, it will be started on a separate line.
+   If there are no short options for a given option, the first long option is
+   indented slighly in a way that's supposed to make most long options appear
+   to be in a separate column.
+
+   For example, the following output (from ps):
+
+     -p PID, --pid=PID          List the process PID
+	 --pgrp=PGRP            List processes in the process group PGRP
+     -P, -x, --no-parent        Include processes without parents
+     -Q, --all-fields           Don't elide unusable fields (normally if there's
+				some reason ps can't print a field for any
+				process, it's removed from the output entirely)
+     -r, --reverse, --gratuitously-long-reverse-option
+				Reverse the order of any sort
+	 --session[=SID]        Add the processes from the session SID (which
+				defaults to the sid of the current process)
+
+    Here are some more options:
+     -f ZOT, --foonly=ZOT       Glork a foonly
+     -z, --zaza                 Snit a zar
+
+     -?, --help                 Give this help list
+	 --usage                Give a short usage message
+     -V, --version              Print program version
+
+   The struct argp_option array for the above could look like:
+
+   {
+     {"pid",       'p',      "PID",  0, "List the process PID"},
+     {"pgrp",      OPT_PGRP, "PGRP", 0, "List processes in the process group PGRP"},
+     {"no-parent", 'P',	      0,     0, "Include processes without parents"},
+     {0,           'x',       0,     OPTION_ALIAS},
+     {"all-fields",'Q',       0,     0, "Don't elide unusable fields (normally"
+                                        " if there's some reason ps can't"
+					" print a field for any process, it's"
+                                        " removed from the output entirely)" },
+     {"reverse",   'r',       0,     0, "Reverse the order of any sort"},
+     {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS},
+     {"session",   OPT_SESS,  "SID", OPTION_ARG_OPTIONAL,
+                                        "Add the processes from the session"
+					" SID (which defaults to the sid of"
+					" the current process)" },
+
+     {0,0,0,0, "Here are some more options:"},
+     {"foonly", 'f', "ZOT", 0, "Glork a foonly"},
+     {"zaza", 'z', 0, 0, "Snit a zar"},
+
+     {0}
+   }
+
+   Note that the last three options are automatically supplied by argp_parse,
+   unless you tell it not to with ARGP_NO_HELP.
+
+*/
+
+/* Returns true if CH occurs between BEG and END.  */
+static int
+find_char (char ch, char *beg, char *end)
+{
+  while (beg < end)
+    if (*beg == ch)
+      return 1;
+    else
+      beg++;
+  return 0;
+}
+
+struct hol_cluster;		/* fwd decl */
+
+struct hol_entry
+{
+  /* First option.  */
+  const struct argp_option *opt;
+  /* Number of options (including aliases).  */
+  unsigned num;
+
+  /* A pointers into the HOL's short_options field, to the first short option
+     letter for this entry.  The order of the characters following this point
+     corresponds to the order of options pointed to by OPT, and there are at
+     most NUM.  A short option recorded in a option following OPT is only
+     valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's
+     probably been shadowed by some other entry).  */
+  char *short_options;
+
+  /* Entries are sorted by their group first, in the order:
+       1, 2, ..., n, 0, -m, ..., -2, -1
+     and then alphabetically within each group.  The default is 0.  */
+  int group;
+
+  /* The cluster of options this entry belongs to, or 0 if none.  */
+  struct hol_cluster *cluster;
+
+  /* The argp from which this option came.  */
+  const struct argp *argp;
+};
+
+/* A cluster of entries to reflect the argp tree structure.  */
+struct hol_cluster
+{
+  /* A descriptive header printed before options in this cluster.  */
+  const char *header;
+
+  /* Used to order clusters within the same group with the same parent,
+     according to the order in which they occurred in the parent argp's child
+     list.  */
+  int index;
+
+  /* How to sort this cluster with respect to options and other clusters at the
+     same depth (clusters always follow options in the same group).  */
+  int group;
+
+  /* The cluster to which this cluster belongs, or 0 if it's at the base
+     level.  */
+  struct hol_cluster *parent;
+
+  /* The argp from which this cluster is (eventually) derived.  */
+  const struct argp *argp;
+
+  /* The distance this cluster is from the root.  */
+  int depth;
+
+  /* Clusters in a given hol are kept in a linked list, to make freeing them
+     possible.  */
+  struct hol_cluster *next;
+};
+
+/* A list of options for help.  */
+struct hol
+{
+  /* An array of hol_entry's.  */
+  struct hol_entry *entries;
+  /* The number of entries in this hol.  If this field is zero, the others
+     are undefined.  */
+  unsigned num_entries;
+
+  /* A string containing all short options in this HOL.  Each entry contains
+     pointers into this string, so the order can't be messed with blindly.  */
+  char *short_options;
+
+  /* Clusters of entries in this hol.  */
+  struct hol_cluster *clusters;
+};
+
+/* Create a struct hol from the options in ARGP.  CLUSTER is the
+   hol_cluster in which these entries occur, or 0, if at the root.  */
+static struct hol *
+make_hol (const struct argp *argp, struct hol_cluster *cluster)
+{
+  char *so;
+  const struct argp_option *o;
+  const struct argp_option *opts = argp->options;
+  struct hol_entry *entry;
+  unsigned num_short_options = 0;
+  struct hol *hol = malloc (sizeof (struct hol));
+
+  assert (hol);
+
+  hol->num_entries = 0;
+  hol->clusters = 0;
+
+  if (opts)
+    {
+      int cur_group = 0;
+
+      /* The first option must not be an alias.  */
+      assert (! oalias (opts));
+
+      /* Calculate the space needed.  */
+      for (o = opts; ! oend (o); o++)
+	{
+	  if (! oalias (o))
+	    hol->num_entries++;
+	  if (oshort (o))
+	    num_short_options++;	/* This is an upper bound.  */
+	}
+
+      hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries);
+      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;
+      for (o = opts, entry = hol->entries; ! oend (o); entry++)
+	{
+	  entry->opt = o;
+	  entry->num = 0;
+	  entry->short_options = so;
+	  entry->group = cur_group =
+	    o->group
+	    ? o->group
+	    : ((!o->name && !o->key)
+	       ? cur_group + 1
+	       : cur_group);
+	  entry->cluster = cluster;
+	  entry->argp = argp;
+
+	  do
+	    {
+	      entry->num++;
+	      if (oshort (o) && ! find_char (o->key, hol->short_options, so))
+		/* O has a valid short option which hasn't already been used.*/
+		*so++ = o->key;
+	      o++;
+	    }
+	  while (! oend (o) && oalias (o));
+	}
+      *so = '\0';		/* null terminated so we can find the length */
+    }
+
+  return hol;
+}
+
+/* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the
+   associated argp child list entry), INDEX, and PARENT, and return a pointer
+   to it.  ARGP is the argp that this cluster results from.  */
+static struct hol_cluster *
+hol_add_cluster (struct hol *hol, int group, const char *header, int index,
+		 struct hol_cluster *parent, const struct argp *argp)
+{
+  struct hol_cluster *cl = malloc (sizeof (struct hol_cluster));
+  if (cl)
+    {
+      cl->group = group;
+      cl->header = header;
+
+      cl->index = index;
+      cl->parent = parent;
+      cl->argp = argp;
+      cl->depth = parent ? parent->depth + 1 : 0;
+
+      cl->next = hol->clusters;
+      hol->clusters = cl;
+    }
+  return cl;
+}
+
+/* Free HOL and any resources it uses.  */
+static void
+hol_free (struct hol *hol)
+{
+  struct hol_cluster *cl = hol->clusters;
+
+  while (cl)
+    {
+      struct hol_cluster *next = cl->next;
+      free (cl);
+      cl = next;
+    }
+
+  if (hol->num_entries > 0)
+    {
+      free (hol->entries);
+      free (hol->short_options);
+    }
+
+  free (hol);
+}
+
+static int
+hol_entry_short_iterate (const struct hol_entry *entry,
+			 int (*func)(const struct argp_option *opt,
+				     const struct argp_option *real,
+				     const char *domain, void *cookie),
+			 const char *domain, void *cookie)
+{
+  unsigned nopts;
+  int val = 0;
+  const struct argp_option *opt, *real = entry->opt;
+  char *so = entry->short_options;
+
+  for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--)
+    if (oshort (opt) && *so == opt->key)
+      {
+	if (!oalias (opt))
+	  real = opt;
+	if (ovisible (opt))
+	  val = (*func)(opt, real, domain, cookie);
+	so++;
+      }
+
+  return val;
+}
+
+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,
+				    const char *domain, void *cookie),
+			const char *domain, void *cookie)
+{
+  unsigned nopts;
+  int val = 0;
+  const struct argp_option *opt, *real = entry->opt;
+
+  for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--)
+    if (opt->name)
+      {
+	if (!oalias (opt))
+	  real = opt;
+	if (ovisible (opt))
+	  val = (*func)(opt, real, domain, cookie);
+      }
+
+  return val;
+}
+
+/* Iterator that returns true for the first short option.  */
+static __inline__ int
+until_short (const struct argp_option *opt, const struct argp_option *real,
+	     const char *domain, void *cookie)
+{
+  return oshort (opt) ? opt->key : 0;
+}
+
+/* Returns the first valid short option in ENTRY, or 0 if there is none.  */
+static char
+hol_entry_first_short (const struct hol_entry *entry)
+{
+  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.  */
+static const char *
+hol_entry_first_long (const struct hol_entry *entry)
+{
+  const struct argp_option *opt;
+  unsigned num;
+  for (opt = entry->opt, num = entry->num; num > 0; opt++, num--)
+    if (opt->name && ovisible (opt))
+      return opt->name;
+  return 0;
+}
+
+/* Returns the entry in HOL with the long option name NAME, or 0 if there is
+   none.  */
+static struct hol_entry *
+hol_find_entry (struct hol *hol, const char *name)
+{
+  struct hol_entry *entry = hol->entries;
+  unsigned num_entries = hol->num_entries;
+
+  while (num_entries-- > 0)
+    {
+      const struct argp_option *opt = entry->opt;
+      unsigned num_opts = entry->num;
+
+      while (num_opts-- > 0)
+	if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0)
+	  return entry;
+	else
+	  opt++;
+
+      entry++;
+    }
+
+  return 0;
+}
+
+/* If an entry with the long option NAME occurs in HOL, set it's special
+   sort position to GROUP.  */
+static void
+hol_set_group (struct hol *hol, const char *name, int group)
+{
+  struct hol_entry *entry = hol_find_entry (hol, name);
+  if (entry)
+    entry->group = group;
+}
+
+/* Order by group:  0, 1, 2, ..., n, -m, ..., -2, -1.
+   EQ is what to return if GROUP1 and GROUP2 are the same.  */
+static int
+group_cmp (int group1, int group2, int eq)
+{
+  if (group1 == group2)
+    return eq;
+  else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0))
+    return group1 - group2;
+  else
+    return group2 - group1;
+}
+
+/* Compare clusters CL1 & CL2 by the order that they should appear in
+   output.  */
+static int
+hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2)
+{
+  /* If one cluster is deeper than the other, use its ancestor at the same
+     level, so that finding the common ancestor is straightforward.  */
+  while (cl1->depth > cl2->depth)
+    cl1 = cl1->parent;
+  while (cl2->depth > cl1->depth)
+    cl2 = cl2->parent;
+
+  /* Now reduce both clusters to their ancestors at the point where both have
+     a common parent; these can be directly compared.  */
+  while (cl1->parent != cl2->parent)
+    cl1 = cl1->parent, cl2 = cl2->parent;
+
+  return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index);
+}
+
+/* Return the ancestor of CL that's just below the root (i.e., has a parent
+   of 0).  */
+static struct hol_cluster *
+hol_cluster_base (struct hol_cluster *cl)
+{
+  while (cl->parent)
+    cl = cl->parent;
+  return cl;
+}
+
+/* Return true if CL1 is a child of CL2.  */
+static int
+hol_cluster_is_child (const struct hol_cluster *cl1,
+		      const struct hol_cluster *cl2)
+{
+  while (cl1 && cl1 != cl2)
+    cl1 = cl1->parent;
+  return cl1 == cl2;
+}
+
+/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail
+   that should be used for comparisons, and returns true iff it should be
+   treated as a non-option.  */
+static int
+canon_doc_option (const char **name)
+{
+  int non_opt;
+  /* Skip initial whitespace.  */
+  while (isspace (**name))
+    (*name)++;
+  /* Decide whether this looks like an option (leading `-') or not.  */
+  non_opt = (**name != '-');
+  /* Skip until part of name used for sorting.  */
+  while (**name && !isalnum (**name))
+    (*name)++;
+  return non_opt;
+}
+
+/* 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)
+{
+  /* 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);
+    }
+  else if (group1 == group2)
+    /* The entries are both in the same cluster and group, so compare them
+       alphabetically.  */
+    {
+      int short1 = hol_entry_first_short (entry1);
+      int short2 = hol_entry_first_short (entry2);
+      int doc1 = odoc (entry1->opt);
+      int doc2 = odoc (entry2->opt);
+      const char *long1 = hol_entry_first_long (entry1);
+      const char *long2 = hol_entry_first_long (entry2);
+
+      if (doc1)
+	doc1 = long1 != NULL && canon_doc_option (&long1);
+      if (doc2)
+	doc2 = long2 != NULL && canon_doc_option (&long2);
+
+      if (doc1 != doc2)
+	/* `documentation' options always follow normal options (or
+	   documentation options that *look* like normal options).  */
+	return doc1 - doc2;
+      else if (!short1 && !short2 && long1 && long2)
+	/* Only long options.  */
+	return strcasecmp (long1, long2);
+      else
+	/* Compare short/short, long/short, short/long, using the first
+	   character of long options.  Entries without *any* valid
+	   options (such as options with OPTION_HIDDEN set) will be put
+	   first, but as they're not displayed, it doesn't matter where
+	   they are.  */
+	{
+	  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);
+#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;
+	}
+    }
+  else
+    /* Within the same cluster, but not the same group, so just compare
+       groups.  */
+    return group_cmp (group1, group2, 0);
+}
+
+/* Version of hol_entry_cmp with correct signature for qsort.  */
+static int
+hol_entry_qcmp (const void *entry1_v, const void *entry2_v)
+{
+  return hol_entry_cmp (entry1_v, entry2_v);
+}
+
+/* Sort HOL by group and alphabetically by option name (with short options
+   taking precedence over long).  Since the sorting is for display purposes
+   only, the shadowing of options isn't effected.  */
+static void
+hol_sort (struct hol *hol)
+{
+  if (hol->num_entries > 0)
+    qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry),
+	   hol_entry_qcmp);
+}
+
+/* Append MORE to HOL, destroying MORE in the process.  Options in HOL shadow
+   any in MORE with the same name.  */
+static void
+hol_append (struct hol *hol, struct hol *more)
+{
+  struct hol_cluster **cl_end = &hol->clusters;
+
+  /* Steal MORE's cluster list, and add it to the end of HOL's.  */
+  while (*cl_end)
+    cl_end = &(*cl_end)->next;
+  *cl_end = more->clusters;
+  more->clusters = 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);
+
+	  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,
+		     more->num_entries * sizeof (struct hol_entry));
+
+	  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);
+
+	  /* 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;
+
+	      e->short_options = so;
+
+	      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);
+}
+
+/* Inserts enough spaces to make sure STREAM is at column COL.  */
+static void
+indent_to (argp_fmtstream_t stream, unsigned col)
+{
+  int needed = col - __argp_fmtstream_point (stream);
+  while (needed-- > 0)
+    __argp_fmtstream_putc (stream, ' ');
+}
+
+/* Output to STREAM either a space, or a newline if there isn't room for at
+   least ENSURE characters before the right margin.  */
+static void
+space (argp_fmtstream_t stream, size_t ensure)
+{
+  if (__argp_fmtstream_point (stream) + ensure
+      >= __argp_fmtstream_rmargin (stream))
+    __argp_fmtstream_putc (stream, '\n');
+  else
+    __argp_fmtstream_putc (stream, ' ');
+}
+
+/* If the option REAL has an argument, we print it in using the printf
+   format REQ_FMT or OPT_FMT depending on whether it's a required or
+   optional argument.  */
+static void
+arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt,
+     const char *domain, argp_fmtstream_t stream)
+{
+  if (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));
+    }
+}
+
+/* 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.  */
+struct pentry_state
+{
+  const struct hol_entry *entry;
+  argp_fmtstream_t stream;
+  struct hol_help_state *hhstate;
+
+  /* True if nothing's been printed so far.  */
+  int first;
+
+  /* If non-zero, the state that was used to print this help.  */
+  const struct argp_state *state;
+};
+
+/* If a user doc filter should be applied to DOC, do so.  */
+static const char *
+filter_doc (const char *doc, int key, const struct argp *argp,
+	    const struct argp_state *state)
+{
+  if (argp && argp->help_filter)
+    /* We must apply a user filter to this output.  */
+    {
+      void *input = __argp_input (argp, state);
+      return (*argp->help_filter) (key, doc, input);
+    }
+  else
+    /* No filter.  */
+    return doc;
+}
+
+/* Prints STR as a header line, with the margin lines set appropiately, and
+   notes the fact that groups should be separated with a blank line.  ARGP is
+   the argp that should dictate any user doc filtering to take place.  Note
+   that the previous wrap margin isn't restored, but the left margin is reset
+   to 0.  */
+static void
+print_header (const char *str, const struct argp *argp,
+	      struct pentry_state *pest)
+{
+  const char *tstr = dgettext (argp->argp_domain, str);
+  const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state);
+
+  if (fstr)
+    {
+      if (*fstr)
+	{
+	  if (pest->hhstate->prev_entry)
+	    /* Precede with a blank line.  */
+	    __argp_fmtstream_putc (pest->stream, '\n');
+	  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');
+	}
+
+      pest->hhstate->sep_groups = 1; /* Separate subsequent groups. */
+    }
+
+  if (fstr != tstr)
+    free ((char *) fstr);
+}
+
+/* Inserts a comma if this isn't the first item on the line, and then makes
+   sure we're at least to column COL.  If this *is* the first item on a line,
+   prints any pending whitespace/headers that should precede this line. Also
+   clears FIRST.  */
+static void
+comma (unsigned col, struct pentry_state *pest)
+{
+  if (pest->first)
+    {
+      const struct hol_entry *pe = pest->hhstate->prev_entry;
+      const struct hol_cluster *cl = pest->entry->cluster;
+
+      if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group)
+	__argp_fmtstream_putc (pest->stream, '\n');
+
+      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 so, then print the cluster's header line.  */
+	{
+	  int old_wm = __argp_fmtstream_wmargin (pest->stream);
+	  print_header (cl->header, cl->argp, pest);
+	  __argp_fmtstream_set_wmargin (pest->stream, old_wm);
+	}
+
+      pest->first = 0;
+    }
+  else
+    __argp_fmtstream_puts (pest->stream, ", ");
+
+  indent_to (pest->stream, col);
+}
+
+/* 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_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, 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, 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 (uparams.short_opt_col, &pest);
+	    __argp_fmtstream_putc (stream, '-');
+	    __argp_fmtstream_putc (stream, *so);
+	    if (!have_long_opt || uparams.dup_args)
+	      arg (real, " %s", "[%s]",
+		   state == NULL ? NULL : state->root_argp->argp_domain,
+		   stream);
+	    else if (real->arg)
+	      hhstate->suppressed_dup_arg = 1;
+	  }
+	so++;
+      }
+
+  /* Now, long options.  */
+  if (odoc (real))
+    /* A `documentation' option.  */
+    {
+      __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 (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,
+				   dgettext (state == NULL ? NULL
+					     : state->root_argp->argp_domain,
+					     opt->name));
+	  }
+    }
+  else
+    /* A real long option.  */
+    {
+      __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 (uparams.long_opt_col, &pest);
+	    __argp_fmtstream_printf (stream, "--%s", opt->name);
+	    arg (real, "=%s", "[=%s]",
+		 state == NULL ? NULL : state->root_argp->argp_domain, stream);
+	  }
+    }
+
+  /* Next, documentation strings.  */
+  __argp_fmtstream_set_lmargin (stream, 0);
+
+  if (pest.first)
+    {
+      /* Didn't print any switches, what's up?  */
+      if (!oshort (real) && !real->name)
+	/* 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
+    {
+      const char *tstr = real->doc ? dgettext (state == NULL ? NULL
+					       : state->root_argp->argp_domain,
+					       real->doc) : 0;
+      const char *fstr = filter_doc (tstr, real->key, entry->argp, state);
+      if (fstr && *fstr)
+	{
+	  unsigned int col = __argp_fmtstream_point (stream);
+
+	  __argp_fmtstream_set_lmargin (stream, uparams.opt_doc_col);
+	  __argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col);
+
+	  if (col > (unsigned int) (uparams.opt_doc_col + 3))
+	    __argp_fmtstream_putc (stream, '\n');
+	  else if (col >= (unsigned int) uparams.opt_doc_col)
+	    __argp_fmtstream_puts (stream, "   ");
+	  else
+	    indent_to (stream, uparams.opt_doc_col);
+
+	  __argp_fmtstream_puts (stream, fstr);
+	}
+      if (fstr && fstr != tstr)
+	free ((char *) fstr);
+
+      /* Reset the left margin.  */
+      __argp_fmtstream_set_lmargin (stream, 0);
+      __argp_fmtstream_putc (stream, '\n');
+    }
+
+  hhstate->prev_entry = entry;
+
+cleanup:
+  __argp_fmtstream_set_lmargin (stream, old_lm);
+  __argp_fmtstream_set_wmargin (stream, old_wm);
+}
+
+/* Output a long help message about the options in HOL to STREAM.  */
+static void
+hol_help (struct hol *hol, const struct argp_state *state,
+	  argp_fmtstream_t stream)
+{
+  unsigned num;
+  struct hol_entry *entry;
+  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, &hhstate);
+
+  if (hhstate.suppressed_dup_arg && uparams.dup_args_note)
+    {
+      const char *tstr = dgettext (state == NULL ? NULL
+				   : 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,
+				     state ? state->root_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.  */
+
+/* If OPT is a short option without an arg, append its key to the string
+   pointer pointer to by COOKIE, and advance the pointer.  */
+static int
+add_argless_short_opt (const struct argp_option *opt,
+		       const struct argp_option *real,
+		       const char *domain, void *cookie)
+{
+  char **snao_end = cookie;
+  if (!(opt->arg || real->arg)
+      && !((opt->flags | real->flags) & OPTION_NO_USAGE))
+    *(*snao_end)++ = opt->key;
+  return 0;
+}
+
+/* If OPT is a short option with an arg, output a usage entry for it to the
+   stream pointed at by COOKIE.  */
+static int
+usage_argful_short_opt (const struct argp_option *opt,
+			const struct argp_option *real,
+			const char *domain, void *cookie)
+{
+  argp_fmtstream_t stream = cookie;
+  const char *arg = opt->arg;
+  int flags = opt->flags | real->flags;
+
+  if (! arg)
+    arg = real->arg;
+
+  if (arg && !(flags & OPTION_NO_USAGE))
+    {
+      arg = dgettext (domain, arg);
+
+      if (flags & OPTION_ARG_OPTIONAL)
+	__argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg);
+      else
+	{
+	  /* Manually do line wrapping so that it (probably) won't
+	     get wrapped at the embedded space.  */
+	  space (stream, 6 + strlen (arg));
+	  __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg);
+	}
+    }
+
+  return 0;
+}
+
+/* Output a usage entry for the long option opt to the stream pointed at by
+   COOKIE.  */
+static int
+usage_long_opt (const struct argp_option *opt,
+		const struct argp_option *real,
+		const char *domain, void *cookie)
+{
+  argp_fmtstream_t stream = cookie;
+  const char *arg = opt->arg;
+  int flags = opt->flags | real->flags;
+
+  if (! arg)
+    arg = real->arg;
+
+  if (! (flags & OPTION_NO_USAGE))
+    {
+      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;
+}
+
+/* Print a short usage description for the arguments in HOL to STREAM.  */
+static void
+hol_usage (struct hol *hol, argp_fmtstream_t stream)
+{
+  if (hol->num_entries > 0)
+    {
+      unsigned nentries;
+      struct hol_entry *entry;
+      char *short_no_arg_opts = alloca (strlen (hol->short_options) + 1);
+      char *snao_end = short_no_arg_opts;
+
+      /* First we put a list of short options without arguments.  */
+      for (entry = hol->entries, nentries = hol->num_entries
+	   ; nentries > 0
+	   ; entry++, nentries--)
+	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;
+	  __argp_fmtstream_printf (stream, " [-%s]", short_no_arg_opts);
+	}
+
+      /* Now a list of short options *with* arguments.  */
+      for (entry = hol->entries, nentries = hol->num_entries
+	   ; nentries > 0
+	   ; entry++, nentries--)
+	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--)
+	hol_entry_long_iterate (entry, usage_long_opt,
+				entry->argp->argp_domain, stream);
+    }
+}
+
+/* Make a HOL containing all levels of options in ARGP.  CLUSTER is the
+   cluster in which ARGP's entries should be clustered, or 0.  */
+static struct hol *
+argp_hol (const struct argp *argp, struct hol_cluster *cluster)
+{
+  const struct argp_child *child = argp->children;
+  struct hol *hol = make_hol (argp, cluster);
+  if (child)
+    while (child->argp)
+      {
+	struct hol_cluster *child_cluster =
+	  ((child->group || child->header)
+	   /* Put CHILD->argp within its own cluster.  */
+	   ? hol_add_cluster (hol, child->group, child->header,
+			      child - argp->children, cluster, argp)
+	   /* Just merge it into the parent's cluster.  */
+	   : cluster);
+	hol_append (hol, argp_hol (child->argp, child_cluster)) ;
+	child++;
+      }
+  return hol;
+}
+
+/* Calculate how many different levels with alternative args strings exist in
+   ARGP.  */
+static size_t
+argp_args_levels (const struct argp *argp)
+{
+  size_t levels = 0;
+  const struct argp_child *child = argp->children;
+
+  if (argp->args_doc && strchr (argp->args_doc, '\n'))
+    levels++;
+
+  if (child)
+    while (child->argp)
+      levels += argp_args_levels ((child++)->argp);
+
+  return levels;
+}
+
+/* Print all the non-option args documented in ARGP to STREAM.  Any output is
+   preceded by a space.  LEVELS is a pointer to a byte vector the length
+   returned by argp_args_levels; it should be initialized to zero, and
+   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, 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;
+  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 (fdoc)
+    {
+      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++)
+	    cp = nl + 1, nl = strchrnul (cp, '\n');
+	  (*levels)++;
+	}
+
+      /* Manually do line wrapping so that it (probably) won't get wrapped at
+	 any embedded spaces.  */
+      space (stream, 1 + nl - cp);
+
+      __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)
+      advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream);
+
+  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;
+    }
+
+  return !advance;
+}
+
+/* Print the documentation for ARGP to STREAM; if POST is false, then
+   everything preceeding a `\v' character in the documentation strings (or
+   the whole string, for those with none) is printed, otherwise, everything
+   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
+   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,
+	  argp_fmtstream_t stream)
+{
+  const char *text;
+  const char *inp_text;
+  void *input = 0;
+  int anything = 0;
+  size_t inp_text_limit = 0;
+  const char *doc = dgettext (argp->argp_domain, argp->doc);
+  const struct argp_child *child = argp->children;
+
+  if (doc)
+    {
+      char *vt = strchr (doc, '\v');
+      inp_text = post ? (vt ? vt + 1 : 0) : doc;
+      inp_text_limit = (!post && vt) ? (vt - doc) : 0;
+    }
+  else
+    inp_text = 0;
+
+  if (argp->help_filter)
+    /* We have to filter the doc strings.  */
+    {
+      if (inp_text_limit)
+	/* Copy INP_TEXT so that it's nul-terminated.  */
+	inp_text = strndup (inp_text, inp_text_limit);
+      input = __argp_input (argp, state);
+      text =
+	(*argp->help_filter) (post
+			      ? ARGP_KEY_HELP_POST_DOC
+			      : ARGP_KEY_HELP_PRE_DOC,
+			      inp_text, input);
+    }
+  else
+    text = (const char *) inp_text;
+
+  if (text)
+    {
+      if (pre_blank)
+	__argp_fmtstream_putc (stream, '\n');
+
+      if (text == inp_text && inp_text_limit)
+	__argp_fmtstream_write (stream, inp_text, inp_text_limit);
+      else
+	__argp_fmtstream_puts (stream, text);
+
+      if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream))
+	__argp_fmtstream_putc (stream, '\n');
+
+      anything = 1;
+    }
+
+  if (text && text != inp_text)
+    free ((char *) text);	/* Free TEXT returned from the help filter.  */
+  if (inp_text && inp_text_limit && argp->help_filter)
+    free ((char *) inp_text);	/* We copied INP_TEXT, so free it now.  */
+
+  if (post && argp->help_filter)
+    /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text.  */
+    {
+      text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input);
+      if (text)
+	{
+	  if (anything || pre_blank)
+	    __argp_fmtstream_putc (stream, '\n');
+	  __argp_fmtstream_puts (stream, text);
+	  free ((char *) text);
+	  if (__argp_fmtstream_point (stream)
+	      > __argp_fmtstream_lmargin (stream))
+	    __argp_fmtstream_putc (stream, '\n');
+	  anything = 1;
+	}
+    }
+
+  if (child)
+    while (child->argp && !(first_only && anything))
+      anything |=
+	argp_doc ((child++)->argp, state,
+		  post, anything || pre_blank, first_only,
+		  stream);
+
+  return anything;
+}
+
+/* Output a usage message for ARGP to STREAM.  If called from
+   argp_state_help, STATE is the relevent parsing state.  FLAGS are from the
+   set ARGP_HELP_*.  NAME is what to use wherever a `program name' is
+   needed. */
+static void
+_help (const struct argp *argp, const struct argp_state *state, FILE *stream,
+       unsigned flags, char *name)
+{
+  int anything = 0;		/* Whether we've output anything.  */
+  struct hol *hol = 0;
+  argp_fmtstream_t fs;
+
+  if (! stream)
+    return;
+
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+  flockfile (stream);
+#endif
+
+  fill_in_uparams (state);
+
+  fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0);
+  if (! fs)
+    {
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+      funlockfile (stream);
+#endif
+      return;
+    }
+
+  if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG))
+    {
+      hol = argp_hol (argp, 0);
+
+      /* If present, these options always come last.  */
+      hol_set_group (hol, "help", -1);
+      hol_set_group (hol, "version", -1);
+
+      hol_sort (hol);
+    }
+
+  if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE))
+    /* Print a short `Usage:' message.  */
+    {
+      int first_pattern = 1, more_patterns;
+      size_t num_pattern_levels = argp_args_levels (argp);
+      char *pattern_levels = alloca (num_pattern_levels);
+
+      memset (pattern_levels, 0, num_pattern_levels);
+
+      do
+	{
+	  int old_lm;
+	  int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent);
+	  char *levels = pattern_levels;
+
+	  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.  */
+	  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)
+		__argp_fmtstream_puts (fs, dgettext (argp->argp_domain,
+						     " [OPTION...]"));
+	    }
+	  else
+	    /* Actually print the options.  */
+	    {
+	      hol_usage (hol, fs);
+	      flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once.  */
+	    }
+
+	  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_putc (fs, '\n');
+	  anything = 1;
+
+	  first_pattern = 0;
+	}
+      while (more_patterns);
+    }
+
+  if (flags & ARGP_HELP_PRE_DOC)
+    anything |= argp_doc (argp, state, 0, 0, 1, fs);
+
+  if (flags & ARGP_HELP_SEE)
+    {
+      __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\
+Try `%s --help' or `%s --usage' for more information.\n"),
+			       name, name);
+      anything = 1;
+    }
+
+  if (flags & ARGP_HELP_LONG)
+    /* Print a long, detailed help message.  */
+    {
+      /* Print info about all the options.  */
+      if (hol->num_entries > 0)
+	{
+	  if (anything)
+	    __argp_fmtstream_putc (fs, '\n');
+	  hol_help (hol, state, fs);
+	  anything = 1;
+	}
+    }
+
+  if (flags & ARGP_HELP_POST_DOC)
+    /* Print any documentation strings at the end.  */
+    anything |= argp_doc (argp, state, 1, anything, 0, fs);
+
+  if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address)
+    {
+      if (anything)
+	__argp_fmtstream_putc (fs, '\n');
+      __argp_fmtstream_printf (fs, dgettext (argp->argp_domain,
+					     "Report bugs to %s.\n"),
+ 			       argp_program_bug_address);
+      anything = 1;
+    }
+
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+  funlockfile (stream);
+#endif
+
+  if (hol)
+    hol_free (hol);
+
+  __argp_fmtstream_free (fs);
+}
+
+/* Output a usage message for ARGP to STREAM.  FLAGS are from the set
+   ARGP_HELP_*.  NAME is what to use wherever a `program name' is needed. */
+void argp_help (const struct argp *argp, FILE *stream,
+		  unsigned flags, char *name)
+{
+  _help (argp, 0, stream, flags, name);
+}
+
+char *
+__argp_short_program_name (void)
+{
+# ifdef __UCLIBC_HAS_PROGRAM_INVOCATION_NAME__
+/*
+ * uClibc provides both program_invocation_name and
+ * program_invocation_short_name
+ */
+ return (char *) program_invocation_short_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
+}
+
+/* Output, if appropriate, a usage message for STATE to STREAM.  FLAGS are
+   from the set ARGP_HELP_*.  */
+void
+argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)
+{
+  if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream)
+    {
+      if (state && (state->flags & ARGP_LONG_ONLY))
+	flags |= ARGP_HELP_LONG_ONLY;
+
+      _help (state ? state->root_argp : 0, state, stream, flags,
+	     state ? state->name : __argp_short_program_name ());
+
+      if (!state || ! (state->flags & ARGP_NO_EXIT))
+	{
+	  if (flags & ARGP_HELP_EXIT_ERR)
+	    exit (argp_err_exit_status);
+	  if (flags & ARGP_HELP_EXIT_OK)
+	    exit (0);
+	}
+  }
+}
+
+/* If appropriate, print the printf string FMT and following args, preceded
+   by the program name and `:', to stderr, and followed by a `Try ... --help'
+   message, then exit (1).  */
+void
+argp_error (const struct argp_state *state, const char *fmt, ...)
+{
+  if (!state || !(state->flags & ARGP_NO_ERRS))
+    {
+      FILE *stream = state ? state->err_stream : stderr;
+
+      if (stream)
+	{
+	  va_list ap;
+
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+	  flockfile (stream);
+#endif
+
+	  va_start (ap, fmt);
+
+#if defined _LIBC && defined USE_IN_LIBIO
+	  char *buf;
+
+	  if (_IO_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);
+
+	  putc_unlocked ('\n', stream);
+#endif
+
+	  argp_state_help (state, stream, ARGP_HELP_STD_ERR);
+
+	  va_end (ap);
+
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+	  funlockfile (stream);
+#endif
+	}
+    }
+}
+
+/* Similar to the standard gnu error-reporting function error(), but will
+   respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print
+   to STATE->err_stream.  This is useful for argument parsing code that is
+   shared between program startup (when exiting is desired) and runtime
+   option parsing (when typically an error code is returned instead).  The
+   difference between this function and argp_error is that the latter is for
+   *parsing errors*, and the former is for other problems that occur during
+   parsing but don't reflect a (syntactic) problem with the input.  */
+void
+argp_failure (const struct argp_state *state, int status, int errnum,
+		const char *fmt, ...)
+{
+  if (!state || !(state->flags & ARGP_NO_ERRS))
+    {
+      FILE *stream = state ? state->err_stream : stderr;
+
+      if (stream)
+	{
+#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE)
+	  flockfile (stream);
+#endif
+
+#if defined _LIBC && defined USE_IN_LIBIO
+	  __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);
+#if defined _LIBC && defined USE_IN_LIBIO
+	      char *buf;
+
+	      if (_IO_vasprintf (&buf, fmt, ap) < 0)
+		buf = NULL;
+
+	      __fxprintf (stream, ": %s", buf);
+
+	      free (buf);
+#else
+	      putc_unlocked (':', stream);
+	      putc_unlocked (' ', stream);
+
+	      vfprintf (stream, fmt, ap);
+#endif
+
+	      va_end (ap);
+	    }
+
+	  if (errnum)
+	    {
+#if (defined _LIBC && defined USE_IN_LIBIO) || defined HAVE_STRERROR_R
+	      char buf[200];
+#endif
+#if defined _LIBC && defined USE_IN_LIBIO
+	      __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
+	    }
+
+#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);
+	}
+    }
+}
diff --git a/libc/argp/argp-parse.c b/libc/argp/argp-parse.c
new file mode 100644
index 0000000..e512f39
--- /dev/null
+++ b/libc/argp/argp-parse.c
@@ -0,0 +1,949 @@
+/* Hierarchial argument parsing, layered over getopt
+   Copyright (C) 1995-2000, 2002, 2003, 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.
+
+   Modified for uClibc by: Salvatore Cro <salvatore.cro at st.com>
+*/
+
+#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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <getopt.h>
+#include <bits/getopt_int.h>
+
+#include <features.h>
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+# if (defined HAVE_LIBINTL_H || defined _LIBC) && defined __UCLIBC_HAS_GETTEXT_AWARENESS__
+#  include <libintl.h>
+#  ifdef _LIBC
+#   undef dgettext
+#   define dgettext(domain, msgid) \
+  INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES)
+#  endif
+# else
+#  define dgettext(domain, msgid) (msgid)
+#  define gettext(msgid) (msgid)
+# endif
+#endif
+#ifndef N_
+# define N_(msgid) (msgid)
+#endif
+
+#include <argp.h>
+
+/* Getopt return values.  */
+#define KEY_END (-1)		/* The end of the options.  */
+#define KEY_ARG 1		/* A non-option argument.  */
+#define KEY_ERR '?'		/* An error parsing the options.  */
+
+/* The meta-argument used to prevent any further arguments being interpreted
+   as options.  */
+#define QUOTE "--"
+
+/* The number of bits we steal in a long-option value for our own use.  */
+#define GROUP_BITS CHAR_BIT
+
+/* The number of bits available for the user value.  */
+#define USER_BITS ((sizeof ((struct option *)0)->val * CHAR_BIT) - GROUP_BITS)
+#define USER_MASK ((1 << USER_BITS) - 1)
+
+/* EZ alias for ARGP_ERR_UNKNOWN.  */
+#define EBADKEY ARGP_ERR_UNKNOWN
+
+/* Default options.  */
+
+/* When argp is given the --HANG switch, _ARGP_HANG is set and argp will sleep
+   for one second intervals, decrementing _ARGP_HANG until it's zero.  Thus
+   you can force the program to continue by attaching a debugger and setting
+   it to 0 yourself.  */
+static volatile int _argp_hang;
+
+#define OPT_PROGNAME	-2
+#define OPT_USAGE	-3
+#define OPT_HANG	-4
+
+static const struct argp_option argp_default_options[] =
+{
+  {"help",	  '?',    	0, 0,  N_("Give this help list"), -1},
+  {"usage",	  OPT_USAGE,	0, 0,  N_("Give a short usage message")},
+  {"program-name",OPT_PROGNAME,"NAME", OPTION_HIDDEN, N_("Set the program name")},
+  {"HANG",	  OPT_HANG,    "SECS", OPTION_ARG_OPTIONAL | OPTION_HIDDEN,
+     N_("Hang for SECS seconds (default 3600)")},
+  {0, 0}
+};
+
+static error_t
+argp_default_parser (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    case '?':
+      argp_state_help (state, state->out_stream, ARGP_HELP_STD_HELP);
+      break;
+    case OPT_USAGE:
+      argp_state_help (state, state->out_stream,
+		       ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
+      break;
+
+    case OPT_PROGNAME:		/* Set the program name.  */
+#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_NAME
+      program_invocation_name = arg;
+#endif
+      /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka
+	 __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined
+	 to be that, so we have to be a bit careful here.]  */
+
+      /* Update what we use for messages.  */
+      state->name = strrchr (arg, '/');
+      if (state->name)
+	state->name++;
+      else
+	state->name = arg;
+
+#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME
+      program_invocation_short_name = state->name;
+#endif
+
+      if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS))
+	  == ARGP_PARSE_ARGV0)
+	/* Update what getopt uses too.  */
+	state->argv[0] = arg;
+
+      break;
+
+    case OPT_HANG:
+      _argp_hang = atoi (arg ? arg : "3600");
+      while (_argp_hang-- > 0)
+	sleep (1);
+      break;
+
+    default:
+      return EBADKEY;
+    }
+  return 0;
+}
+
+static const struct argp argp_default_argp =
+  {argp_default_options, &argp_default_parser, NULL, NULL, NULL, NULL, "libc"};
+
+
+static const struct argp_option argp_version_options[] =
+{
+  {"version",	  'V',    	0, 0,  N_("Print program version"), -1},
+  {0, 0}
+};
+
+static error_t
+argp_version_parser (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    case 'V':
+      if (argp_program_version_hook)
+	(*argp_program_version_hook) (state->out_stream, state);
+      else if (argp_program_version)
+	fprintf (state->out_stream, "%s\n", argp_program_version);
+      else
+	argp_error (state, dgettext (state->root_argp->argp_domain,
+				       "(PROGRAM ERROR) No version known!?"));
+      if (! (state->flags & ARGP_NO_EXIT))
+	exit (0);
+      break;
+    default:
+      return EBADKEY;
+    }
+  return 0;
+}
+
+static const struct argp argp_version_argp =
+  {argp_version_options, &argp_version_parser, NULL, NULL, NULL, NULL, "libc"};
+
+/* Returns the offset into the getopt long options array LONG_OPTIONS of a
+   long option with called NAME, or -1 if none is found.  Passing NULL as
+   NAME will return the number of options.  */
+static int
+find_long_option (struct option *long_options, const char *name)
+{
+  struct option *l = long_options;
+  while (l->name != NULL)
+    if (name != NULL && strcmp (l->name, name) == 0)
+      return l - long_options;
+    else
+      l++;
+  if (name == NULL)
+    return l - long_options;
+  else
+    return -1;
+}
+
+
+/* The state of a `group' during parsing.  Each group corresponds to a
+   particular argp structure from the tree of such descending from the top
+   level argp passed to argp_parse.  */
+struct group
+{
+  /* This group's parsing function.  */
+  argp_parser_t parser;
+
+  /* Which argp this group is from.  */
+  const struct argp *argp;
+
+  /* Points to the point in SHORT_OPTS corresponding to the end of the short
+     options for this group.  We use it to determine from which group a
+     particular short options is from.  */
+  char *short_end;
+
+  /* The number of non-option args sucessfully handled by this parser.  */
+  unsigned args_processed;
+
+  /* This group's parser's parent's group.  */
+  struct group *parent;
+  unsigned parent_index;	/* And the our position in the parent.   */
+
+  /* These fields are swapped into and out of the state structure when
+     calling this group's parser.  */
+  void *input, **child_inputs;
+  void *hook;
+};
+
+/* Call GROUP's parser with KEY and ARG, swapping any group-specific info
+   from STATE before calling, and back into state afterwards.  If GROUP has
+   no parser, EBADKEY is returned.  */
+static error_t
+group_parse (struct group *group, struct argp_state *state, int key, char *arg)
+{
+  if (group->parser)
+    {
+      error_t err;
+      state->hook = group->hook;
+      state->input = group->input;
+      state->child_inputs = group->child_inputs;
+      state->arg_num = group->args_processed;
+      err = (*group->parser)(key, arg, state);
+      group->hook = state->hook;
+      return err;
+    }
+  else
+    return EBADKEY;
+}
+
+struct parser
+{
+  const struct argp *argp;
+
+  /* SHORT_OPTS is the getopt short options string for the union of all the
+     groups of options.  */
+  char *short_opts;
+  /* LONG_OPTS is the array of getop long option structures for the union of
+     all the groups of options.  */
+  struct option *long_opts;
+  /* OPT_DATA is the getopt data used for the re-entrant getopt.  */
+  struct _getopt_data opt_data;
+
+  /* States of the various parsing groups.  */
+  struct group *groups;
+  /* The end of the GROUPS array.  */
+  struct group *egroup;
+  /* An vector containing storage for the CHILD_INPUTS field in all groups.  */
+  void **child_inputs;
+
+  /* True if we think using getopt is still useful; if false, then
+     remaining arguments are just passed verbatim with ARGP_KEY_ARG.  This is
+     cleared whenever getopt returns KEY_END, but may be set again if the user
+     moves the next argument pointer backwards.  */
+  int try_getopt;
+
+  /* State block supplied to parsing routines.  */
+  struct argp_state state;
+
+  /* Memory used by this parser.  */
+  void *storage;
+};
+
+/* The next usable entries in the various parser tables being filled in by
+   convert_options.  */
+struct parser_convert_state
+{
+  struct parser *parser;
+  char *short_end;
+  struct option *long_end;
+  void **child_inputs_end;
+};
+
+/* Converts all options in ARGP (which is put in GROUP) and ancestors
+   into getopt options stored in SHORT_OPTS and LONG_OPTS; SHORT_END and
+   CVT->LONG_END are the points at which new options are added.  Returns the
+   next unused group entry.  CVT holds state used during the conversion.  */
+static struct group *
+convert_options (const struct argp *argp,
+		 struct group *parent, unsigned parent_index,
+		 struct group *group, struct parser_convert_state *cvt)
+{
+  /* REAL is the most recent non-alias value of OPT.  */
+  const struct argp_option *real = argp->options;
+  const struct argp_child *children = argp->children;
+
+  if (real || argp->parser)
+    {
+      const struct argp_option *opt;
+
+      if (real)
+	for (opt = real; !__option_is_end (opt); opt++)
+	  {
+	    if (! (opt->flags & OPTION_ALIAS))
+	      /* OPT isn't an alias, so we can use values from it.  */
+	      real = opt;
+
+	    if (! (real->flags & OPTION_DOC))
+	      /* A real option (not just documentation).  */
+	      {
+		if (__option_is_short (opt))
+		  /* OPT can be used as a short option.  */
+		  {
+		    *cvt->short_end++ = opt->key;
+		    if (real->arg)
+		      {
+			*cvt->short_end++ = ':';
+			if (real->flags & OPTION_ARG_OPTIONAL)
+			  *cvt->short_end++ = ':';
+		      }
+		    *cvt->short_end = '\0'; /* keep 0 terminated */
+		  }
+
+		if (opt->name
+		    && find_long_option (cvt->parser->long_opts, opt->name) < 0)
+		  /* OPT can be used as a long option.  */
+		  {
+		    cvt->long_end->name = opt->name;
+		    cvt->long_end->has_arg =
+		      (real->arg
+		       ? (real->flags & OPTION_ARG_OPTIONAL
+			  ? optional_argument
+			  : required_argument)
+		       : no_argument);
+		    cvt->long_end->flag = 0;
+		    /* we add a disambiguating code to all the user's
+		       values (which is removed before we actually call
+		       the function to parse the value); this means that
+		       the user loses use of the high 8 bits in all his
+		       values (the sign of the lower bits is preserved
+		       however)...  */
+		    cvt->long_end->val =
+		      ((opt->key | real->key) & USER_MASK)
+		      + (((group - cvt->parser->groups) + 1) << USER_BITS);
+
+		    /* Keep the LONG_OPTS list terminated.  */
+		    (++cvt->long_end)->name = NULL;
+		  }
+	      }
+	    }
+
+      group->parser = argp->parser;
+      group->argp = argp;
+      group->short_end = cvt->short_end;
+      group->args_processed = 0;
+      group->parent = parent;
+      group->parent_index = parent_index;
+      group->input = 0;
+      group->hook = 0;
+      group->child_inputs = 0;
+
+      if (children)
+	/* Assign GROUP's CHILD_INPUTS field some space from
+           CVT->child_inputs_end.*/
+	{
+	  unsigned num_children = 0;
+	  while (children[num_children].argp)
+	    num_children++;
+	  group->child_inputs = cvt->child_inputs_end;
+	  cvt->child_inputs_end += num_children;
+	}
+
+      parent = group++;
+    }
+  else
+    parent = 0;
+
+  if (children)
+    {
+      unsigned index = 0;
+      while (children->argp)
+	group =
+	  convert_options (children++->argp, parent, index++, group, cvt);
+    }
+
+  return group;
+}
+
+/* Find the merged set of getopt options, with keys appropiately prefixed. */
+static void
+parser_convert (struct parser *parser, const struct argp *argp, int flags)
+{
+  struct parser_convert_state cvt;
+
+  cvt.parser = parser;
+  cvt.short_end = parser->short_opts;
+  cvt.long_end = parser->long_opts;
+  cvt.child_inputs_end = parser->child_inputs;
+
+  if (flags & ARGP_IN_ORDER)
+    *cvt.short_end++ = '-';
+  else if (flags & ARGP_NO_ARGS)
+    *cvt.short_end++ = '+';
+  *cvt.short_end = '\0';
+
+  cvt.long_end->name = NULL;
+
+  parser->argp = argp;
+
+  if (argp)
+    parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt);
+  else
+    parser->egroup = parser->groups; /* No parsers at all! */
+}
+
+/* Lengths of various parser fields which we will allocated.  */
+struct parser_sizes
+{
+  size_t short_len;		/* Getopt short options string.  */
+  size_t long_len;		/* Getopt long options vector.  */
+  size_t num_groups;		/* Group structures we allocate.  */
+  size_t num_child_inputs;	/* Child input slots.  */
+};
+
+/* For ARGP, increments the NUM_GROUPS field in SZS by the total number of
+ argp structures descended from it, and the SHORT_LEN & LONG_LEN fields by
+ the maximum lengths of the resulting merged getopt short options string and
+ long-options array, respectively.  */
+static void
+calc_sizes (const struct argp *argp,  struct parser_sizes *szs)
+{
+  const struct argp_child *child = argp->children;
+  const struct argp_option *opt = argp->options;
+
+  if (opt || argp->parser)
+    {
+      szs->num_groups++;
+      if (opt)
+	{
+	  int num_opts = 0;
+	  while (!__option_is_end (opt++))
+	    num_opts++;
+	  szs->short_len += num_opts * 3; /* opt + up to 2 `:'s */
+	  szs->long_len += num_opts;
+	}
+    }
+
+  if (child)
+    while (child->argp)
+      {
+	calc_sizes ((child++)->argp, szs);
+	szs->num_child_inputs++;
+      }
+}
+
+
+extern char * __argp_short_program_name (void);
+/* Initializes PARSER to parse ARGP in a manner described by FLAGS.  */
+static error_t
+parser_init (struct parser *parser, const struct argp *argp,
+	     int argc, char **argv, int flags, void *input)
+{
+  error_t err = 0;
+  struct group *group;
+  struct parser_sizes szs;
+  struct _getopt_data opt_data = _GETOPT_DATA_INITIALIZER;
+
+  szs.short_len = (flags & ARGP_NO_ARGS) ? 0 : 1;
+  szs.long_len = 0;
+  szs.num_groups = 0;
+  szs.num_child_inputs = 0;
+
+  if (argp)
+    calc_sizes (argp, &szs);
+
+  /* Lengths of the various bits of storage used by PARSER.  */
+#define GLEN (szs.num_groups + 1) * sizeof (struct group)
+#define CLEN (szs.num_child_inputs * sizeof (void *))
+#define LLEN ((szs.long_len + 1) * sizeof (struct option))
+#define SLEN (szs.short_len + 1)
+
+  parser->storage = malloc (GLEN + CLEN + LLEN + SLEN);
+  if (! parser->storage)
+    return ENOMEM;
+
+  parser->groups = parser->storage;
+  parser->child_inputs = parser->storage + GLEN;
+  parser->long_opts = parser->storage + GLEN + CLEN;
+  parser->short_opts = parser->storage + GLEN + CLEN + LLEN;
+  parser->opt_data = opt_data;
+
+  memset (parser->child_inputs, 0, szs.num_child_inputs * sizeof (void *));
+  parser_convert (parser, argp, flags);
+
+  memset (&parser->state, 0, sizeof (struct argp_state));
+  parser->state.root_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)
+    parser->groups->input = input;
+  for (group = parser->groups;
+       group < parser->egroup && (!err || err == EBADKEY);
+       group++)
+    {
+      if (group->parent)
+	/* If a child parser, get the initial input value from the parent. */
+	group->input = group->parent->child_inputs[group->parent_index];
+
+      if (!group->parser
+	  && group->argp->children && group->argp->children->argp)
+	/* For the special case where no parsing function is supplied for an
+	   argp, propagate its input to its first child, if any (this just
+	   makes very simple wrapper argps more convenient).  */
+	group->child_inputs[0] = group->input;
+
+      err = group_parse (group, &parser->state, ARGP_KEY_INIT, 0);
+    }
+  if (err == EBADKEY)
+    err = 0;			/* Some parser didn't understand.  */
+
+  if (err)
+    return err;
+
+  if (parser->state.flags & ARGP_NO_ERRS)
+    {
+      parser->opt_data.opterr = 0;
+      if (parser->state.flags & ARGP_PARSE_ARGV0)
+	/* getopt always skips ARGV[0], so we have to fake it out.  As long
+	   as OPTERR is 0, then it shouldn't actually try to access it.  */
+	parser->state.argv--, parser->state.argc++;
+    }
+  else
+    parser->opt_data.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 = __argp_short_program_name ();
+
+  return 0;
+}
+
+/* Free any storage consumed by PARSER (but not PARSER itself).  */
+static error_t
+parser_finalize (struct parser *parser,
+		 error_t err, int arg_ebadkey, int *end_index)
+{
+  struct group *group;
+
+  if (err == EBADKEY && arg_ebadkey)
+    /* Suppress errors generated by unparsed arguments.  */
+    err = 0;
+
+  if (! err)
+    {
+      if (parser->state.next == parser->state.argc)
+	/* We successfully parsed all arguments!  Call all the parsers again,
+	   just a few more times... */
+	{
+	  for (group = parser->groups;
+	       group < parser->egroup && (!err || err==EBADKEY);
+	       group++)
+	    if (group->args_processed == 0)
+	      err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, 0);
+	  for (group = parser->egroup - 1;
+	       group >= parser->groups && (!err || err==EBADKEY);
+	       group--)
+	    err = group_parse (group, &parser->state, ARGP_KEY_END, 0);
+
+	  if (err == EBADKEY)
+	    err = 0;		/* Some parser didn't understand.  */
+
+	  /* Tell the user that all arguments are parsed.  */
+	  if (end_index)
+	    *end_index = parser->state.next;
+	}
+      else if (end_index)
+	/* Return any remaining arguments to the user.  */
+	*end_index = parser->state.next;
+      else
+	/* No way to return the remaining arguments, they must be bogus. */
+	{
+	  if (!(parser->state.flags & ARGP_NO_ERRS)
+	      && parser->state.err_stream)
+	    fprintf (parser->state.err_stream,
+		     dgettext (parser->argp->argp_domain,
+			       "%s: Too many arguments\n"),
+		     parser->state.name);
+	  err = EBADKEY;
+	}
+    }
+
+  /* Okay, we're all done, with either an error or success; call the parsers
+     to indicate which one.  */
+
+  if (err)
+    {
+      /* Maybe print an error message.  */
+      if (err == EBADKEY)
+	/* An appropriate message describing what the error was should have
+	   been printed earlier.  */
+	argp_state_help (&parser->state, parser->state.err_stream,
+			   ARGP_HELP_STD_ERR);
+
+      /* Since we didn't exit, give each parser an error indication.  */
+      for (group = parser->groups; group < parser->egroup; group++)
+	group_parse (group, &parser->state, ARGP_KEY_ERROR, 0);
+    }
+  else
+    /* Notify parsers of success, and propagate back values from parsers.  */
+    {
+      /* We pass over the groups in reverse order so that child groups are
+	 given a chance to do there processing before passing back a value to
+	 the parent.  */
+      for (group = parser->egroup - 1
+	   ; group >= parser->groups && (!err || err == EBADKEY)
+	   ; group--)
+	err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, 0);
+      if (err == EBADKEY)
+	err = 0;		/* Some parser didn't understand.  */
+    }
+
+  /* Call parsers once more, to do any final cleanup.  Errors are ignored.  */
+  for (group = parser->egroup - 1; group >= parser->groups; group--)
+    group_parse (group, &parser->state, ARGP_KEY_FINI, 0);
+
+  if (err == EBADKEY)
+    err = EINVAL;
+
+  free (parser->storage);
+
+  return err;
+}
+
+/* Call the user parsers to parse the non-option argument VAL, at the current
+   position, returning any error.  The state NEXT pointer is assumed to have
+   been adjusted (by getopt) to point after this argument; this function will
+   adjust it correctly to reflect however many args actually end up being
+   consumed.  */
+static error_t
+parser_parse_arg (struct parser *parser, char *val)
+{
+  /* Save the starting value of NEXT, first adjusting it so that the arg
+     we're parsing is again the front of the arg vector.  */
+  int index = --parser->state.next;
+  error_t err = EBADKEY;
+  struct group *group;
+  int key = 0;			/* Which of ARGP_KEY_ARG[S] we used.  */
+
+  /* Try to parse the argument in each parser.  */
+  for (group = parser->groups
+       ; group < parser->egroup && err == EBADKEY
+       ; group++)
+    {
+      parser->state.next++;	/* For ARGP_KEY_ARG, consume the arg.  */
+      key = ARGP_KEY_ARG;
+      err = group_parse (group, &parser->state, key, val);
+
+      if (err == EBADKEY)
+	/* This parser doesn't like ARGP_KEY_ARG; try ARGP_KEY_ARGS instead. */
+	{
+	  parser->state.next--;	/* For ARGP_KEY_ARGS, put back the arg.  */
+	  key = ARGP_KEY_ARGS;
+	  err = group_parse (group, &parser->state, key, 0);
+	}
+    }
+
+  if (! err)
+    {
+      if (key == ARGP_KEY_ARGS)
+	/* The default for ARGP_KEY_ARGS is to assume that if NEXT isn't
+	   changed by the user, *all* arguments should be considered
+	   consumed.  */
+	parser->state.next = parser->state.argc;
+
+      if (parser->state.next > index)
+	/* Remember that we successfully processed a non-option
+	   argument -- but only if the user hasn't gotten tricky and set
+	   the clock back.  */
+	(--group)->args_processed += (parser->state.next - index);
+      else
+	/* The user wants to reparse some args, give getopt another try.  */
+	parser->try_getopt = 1;
+    }
+
+  return err;
+}
+
+/* Call the user parsers to parse the option OPT, with argument VAL, at the
+   current position, returning any error.  */
+static error_t
+parser_parse_opt (struct parser *parser, int opt, char *val)
+{
+  /* The group key encoded in the high bits; 0 for short opts or
+     group_number + 1 for long opts.  */
+  int group_key = opt >> USER_BITS;
+  error_t err = EBADKEY;
+
+  if (group_key == 0)
+    /* A short option.  By comparing OPT's position in SHORT_OPTS to the
+       various starting positions in each group's SHORT_END field, we can
+       determine which group OPT came from.  */
+    {
+      struct group *group;
+      char *short_index = strchr (parser->short_opts, opt);
+
+      if (short_index)
+	for (group = parser->groups; group < parser->egroup; group++)
+	  if (group->short_end > short_index)
+	    {
+	      err = group_parse (group, &parser->state, opt,
+				 parser->opt_data.optarg);
+	      break;
+	    }
+    }
+  else
+    /* A long option.  We use shifts instead of masking for extracting
+       the user value in order to preserve the sign.  */
+    err =
+      group_parse (&parser->groups[group_key - 1], &parser->state,
+		   (opt << GROUP_BITS) >> GROUP_BITS,
+		   parser->opt_data.optarg);
+
+  if (err == EBADKEY)
+    /* At least currently, an option not recognized is an error in the
+       parser, because we pre-compute which parser is supposed to deal
+       with each option.  */
+    {
+      static const char bad_key_err[] =
+	N_("(PROGRAM ERROR) Option should have been recognized!?");
+      if (group_key == 0)
+	argp_error (&parser->state, "-%c: %s", opt,
+		      dgettext (parser->argp->argp_domain, bad_key_err));
+      else
+	{
+	  struct option *long_opt = parser->long_opts;
+	  while (long_opt->val != opt && long_opt->name)
+	    long_opt++;
+	  argp_error (&parser->state, "--%s: %s",
+			long_opt->name ? long_opt->name : "???",
+			dgettext (parser->argp->argp_domain, bad_key_err));
+	}
+    }
+
+  return err;
+}
+
+/* Parse the next argument in PARSER (as indicated by PARSER->state.next).
+   Any error from the parsers is returned, and *ARGP_EBADKEY indicates
+   whether a value of EBADKEY is due to an unrecognized argument (which is
+   generally not fatal).  */
+static error_t
+parser_parse_next (struct parser *parser, int *arg_ebadkey)
+{
+  int opt;
+  error_t err = 0;
+
+  if (parser->state.quoted && parser->state.next < parser->state.quoted)
+    /* The next argument pointer has been moved to before the quoted
+       region, so pretend we never saw the quoting `--', and give getopt
+       another chance.  If the user hasn't removed it, getopt will just
+       process it again.  */
+    parser->state.quoted = 0;
+
+  if (parser->try_getopt && !parser->state.quoted)
+    /* Give getopt a chance to parse this.  */
+    {
+      /* Put it back in OPTIND for getopt.  */
+      parser->opt_data.optind = parser->state.next;
+      /* Distinguish KEY_ERR from a real option.  */
+      parser->opt_data.optopt = KEY_END;
+      if (parser->state.flags & ARGP_LONG_ONLY)
+	opt = _getopt_long_only_r (parser->state.argc, parser->state.argv,
+				   parser->short_opts, parser->long_opts, 0,
+				   &parser->opt_data);
+      else
+	opt = _getopt_long_r (parser->state.argc, parser->state.argv,
+			      parser->short_opts, parser->long_opts, 0,
+			      &parser->opt_data);
+      /* And see what getopt did.  */
+      parser->state.next = parser->opt_data.optind;
+
+      if (opt == KEY_END)
+	/* Getopt says there are no more options, so stop using
+	   getopt; we'll continue if necessary on our own.  */
+	{
+	  parser->try_getopt = 0;
+	  if (parser->state.next > 1
+	      && strcmp (parser->state.argv[parser->state.next - 1], QUOTE)
+	           == 0)
+	    /* Not only is this the end of the options, but it's a
+	       `quoted' region, which may have args that *look* like
+	       options, so we definitely shouldn't try to use getopt past
+	       here, whatever happens.  */
+	    parser->state.quoted = parser->state.next;
+	}
+      else if (opt == KEY_ERR && parser->opt_data.optopt != KEY_END)
+	/* KEY_ERR can have the same value as a valid user short
+	   option, but in the case of a real error, getopt sets OPTOPT
+	   to the offending character, which can never be KEY_END.  */
+	{
+	  *arg_ebadkey = 0;
+	  return EBADKEY;
+	}
+    }
+  else
+    opt = KEY_END;
+
+  if (opt == KEY_END)
+    {
+      /* We're past what getopt considers the options.  */
+      if (parser->state.next >= parser->state.argc
+	  || (parser->state.flags & ARGP_NO_ARGS))
+	/* Indicate that we're done.  */
+	{
+	  *arg_ebadkey = 1;
+	  return EBADKEY;
+	}
+      else
+	/* A non-option arg; simulate what getopt might have done.  */
+	{
+	  opt = KEY_ARG;
+	  parser->opt_data.optarg = parser->state.argv[parser->state.next++];
+	}
+    }
+
+  if (opt == KEY_ARG)
+    /* A non-option argument; try each parser in turn.  */
+    err = parser_parse_arg (parser, parser->opt_data.optarg);
+  else
+    err = parser_parse_opt (parser, opt, parser->opt_data.optarg);
+
+  if (err == EBADKEY)
+    *arg_ebadkey = (opt == KEY_END || opt == KEY_ARG);
+
+  return err;
+}
+
+/* Parse the options strings in ARGC & ARGV according to the argp in ARGP.
+   FLAGS is one of the ARGP_ flags above.  If END_INDEX is non-NULL, the
+   index in ARGV of the first unparsed option is returned in it.  If an
+   unknown option is present, EINVAL is returned; if some parser routine
+   returned a non-zero value, it is returned; otherwise 0 is returned.  */
+error_t
+argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags,
+	      int *end_index, void *input)
+{
+  error_t err;
+  struct parser parser;
+
+  /* If true, then err == EBADKEY is a result of a non-option argument failing
+     to be parsed (which in some cases isn't actually an error).  */
+  int arg_ebadkey = 0;
+
+  if (! (flags & ARGP_NO_HELP))
+    /* Add our own options.  */
+    {
+      struct argp_child *child = alloca (4 * sizeof (struct argp_child));
+      struct argp *top_argp = alloca (sizeof (struct argp));
+
+      /* TOP_ARGP has no options, it just serves to group the user & default
+	 argps.  */
+      memset (top_argp, 0, sizeof (*top_argp));
+      top_argp->children = child;
+
+      memset (child, 0, 4 * sizeof (struct argp_child));
+
+      if (argp)
+	(child++)->argp = argp;
+      (child++)->argp = &argp_default_argp;
+      if (argp_program_version || argp_program_version_hook)
+	(child++)->argp = &argp_version_argp;
+      child->argp = 0;
+
+      argp = top_argp;
+    }
+
+  /* Construct a parser for these arguments.  */
+  err = parser_init (&parser, argp, argc, argv, flags, input);
+
+  if (! err)
+    /* Parse! */
+    {
+      while (! err)
+	err = parser_parse_next (&parser, &arg_ebadkey);
+      err = parser_finalize (&parser, err, arg_ebadkey, end_index);
+    }
+
+  return err;
+}
+
+/* Return the input field for ARGP in the parser corresponding to STATE; used
+   by the help routines.  */
+void *
+__argp_input (const struct argp *argp, const struct argp_state *state)
+{
+  if (state)
+    {
+      struct group *group;
+      struct parser *parser = state->pstate;
+
+      for (group = parser->groups; group < parser->egroup; group++)
+	if (group->argp == argp)
+	  return group->input;
+    }
+
+  return 0;
+}
diff --git a/libc/argp/argp-pv.c b/libc/argp/argp-pv.c
new file mode 100644
index 0000000..5987956
--- /dev/null
+++ b/libc/argp/argp-pv.c
@@ -0,0 +1,25 @@
+/* Default definition for ARGP_PROGRAM_VERSION.
+   Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.  */
+
+/* If set by the user program to a non-zero value, then a default option
+   --version is added (unless the ARGP_NO_HELP flag is used), which will
+   print this this string followed by a newline and exit (unless the
+   ARGP_NO_EXIT flag is used).  Overridden by ARGP_PROGRAM_VERSION_HOOK.  */
+const char *argp_program_version;
diff --git a/libc/argp/argp-pvh.c b/libc/argp/argp-pvh.c
new file mode 100644
index 0000000..8a8fd30
--- /dev/null
+++ b/libc/argp/argp-pvh.c
@@ -0,0 +1,32 @@
+/* Default definition for ARGP_PROGRAM_VERSION_HOOK.
+   Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <argp.h>
+
+/* If set by the user program to a non-zero value, then a default option
+   --version is added (unless the ARGP_NO_HELP flag is used), which calls
+   this function with a stream to print the version to and a pointer to the
+   current parsing state, and then exits (unless the ARGP_NO_EXIT flag is
+   used).  This variable takes precedent over ARGP_PROGRAM_VERSION.  */
+void (*argp_program_version_hook) (FILE *stream, struct argp_state *state);
diff --git a/libc/argp/argp-xinl.c b/libc/argp/argp-xinl.c
new file mode 100644
index 0000000..d977a3c
--- /dev/null
+++ b/libc/argp/argp-xinl.c
@@ -0,0 +1,35 @@
+/* Real definitions for extern inline functions in argp.h
+   Copyright (C) 1997, 1998, 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by Miles Bader <miles at 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 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
+   Lesser General Public License for more details.
+
+   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.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined _LIBC || defined HAVE_FEATURES_H
+# include <features.h>
+#endif
+
+#ifndef __USE_EXTERN_INLINES
+# define __USE_EXTERN_INLINES	1
+#endif
+#define ARGP_EI
+#undef __OPTIMIZE__
+#define __OPTIMIZE__ 1
+#include <argp.h>
-- 
1.6.0.6



More information about the uClibc mailing list