summary refs log tree commit diff stats
path: root/libotr/libgpg-error-1.42/src/estream-printf.c
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2021-04-09 07:19:03 -0300
committerSoniEx2 <endermoneymod@gmail.com>2021-04-09 07:19:03 -0300
commit0e752a6e215aee21dc73da097c3225495d54a5b6 (patch)
treeb81be02cbf2f06aebf322ac4a5d014b44176bba5 /libotr/libgpg-error-1.42/src/estream-printf.c
parent7754076c715285173311a1b6811ce377950e18a6 (diff)
Add libotr/etc sources
Diffstat (limited to 'libotr/libgpg-error-1.42/src/estream-printf.c')
-rw-r--r--libotr/libgpg-error-1.42/src/estream-printf.c1904
1 files changed, 1904 insertions, 0 deletions
diff --git a/libotr/libgpg-error-1.42/src/estream-printf.c b/libotr/libgpg-error-1.42/src/estream-printf.c
new file mode 100644
index 0000000..fe25657
--- /dev/null
+++ b/libotr/libgpg-error-1.42/src/estream-printf.c
@@ -0,0 +1,1904 @@
+/* estream-printf.c - Versatile mostly C-99 compliant printf formatting
+ * Copyright (C) 2007, 2008, 2009, 2010, 2012, 2014 g10 Code GmbH
+ *
+ * This file is part of Libestream.
+ *
+ * Libestream 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.
+ *
+ * Libestream 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 Libestream; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * ALTERNATIVELY, Libestream may be distributed under the terms of the
+ * following license, in which case the provisions of this license are
+ * required INSTEAD OF the GNU General Public License. If you wish to
+ * allow use of your version of this file only under the terms of the
+ * GNU General Public License, and not to allow others to use your
+ * version of this file under the terms of the following license,
+ * indicate your decision by deleting this paragraph and the license
+ * below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*  Required autoconf tests:
+
+    AC_TYPE_LONG_LONG_INT            defines HAVE_LONG_LONG_INT
+    AC_TYPE_LONG_DOUBLE              defines HAVE_LONG_DOUBLE
+    AC_TYPE_INTMAX_T                 defines HAVE_INTMAX_T
+    AC_TYPE_UINTMAX_T                defines HAVE_UINTMAX_T
+    AC_CHECK_TYPES([ptrdiff_t])      defines HAVE_PTRDIFF_T
+    AC_CHECK_SIZEOF([unsigned long]) defines SIZEOF_UNSIGNED_LONG
+    AC_CHECK_SIZEOF([void *])        defines SIZEOF_VOID_P
+                                             HAVE_LANGINFO_THOUSEP
+
+    Note that the file estream.m4 provides the autoconf macro
+    ESTREAM_PRINTF_INIT which runs all required checks.
+    See estream-printf.h for ways to tune this code.
+
+  Missing stuff:  wchar and wint_t
+                  thousep in pr_float.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(_WIN32) && !defined(HAVE_W32_SYSTEM)
+# define HAVE_W32_SYSTEM 1
+# if defined(__MINGW32CE__) && !defined (HAVE_W32CE_SYSTEM)
+#  define HAVE_W32CE_SYSTEM
+# endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stddef.h>
+#if defined(HAVE_INTMAX_T) || defined(HAVE_UINTMAX_T)
+# ifdef HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#ifdef HAVE_LANGINFO_THOUSEP
+#include <langinfo.h>
+#endif
+#ifdef HAVE_W32CE_SYSTEM
+#include <gpg-error.h>  /* ERRNO replacement.  */
+#endif
+#ifdef _ESTREAM_PRINTF_EXTRA_INCLUDE
+# include _ESTREAM_PRINTF_EXTRA_INCLUDE
+#endif
+#include "estream-printf.h"
+
+/* #define DEBUG 1 */
+
+
+/* Allow redefinition of asprintf used realloc function.  */
+#if defined(_ESTREAM_PRINTF_REALLOC)
+#define my_printf_realloc(a,b) _ESTREAM_PRINTF_REALLOC((a),(b))
+#else
+#define my_printf_realloc(a,b) fixed_realloc((a),(b))
+#endif
+
+/* A wrapper to set ERRNO.  */
+#ifdef HAVE_W32CE_SYSTEM
+# define _set_errno(a)  gpg_err_set_errno ((a))
+#else
+# define _set_errno(a)  do { errno = (a); } while (0)
+#endif
+
+
+/* Calculate array dimension.  */
+#ifndef DIM
+#define DIM(array) (sizeof (array) / sizeof (*array))
+#endif
+
+
+/* We allow for that many args without requiring malloced memory. */
+#define DEFAULT_MAX_ARGSPECS  5
+
+/* We allow for that many values without requiring malloced memory. */
+#define DEFAULT_MAX_VALUES  8
+
+/* We allocate this many new array argspec elements each time.  */
+#define ARGSPECS_BUMP_VALUE   10
+
+/* Special values for the field width and the precision.  */
+#define NO_FIELD_VALUE   (-1)
+#define STAR_FIELD_VALUE (-2)
+
+/* Bit valuues used for the conversion flags. */
+#define FLAG_GROUPING   1
+#define FLAG_LEFT_JUST  2
+#define FLAG_PLUS_SIGN  4
+#define FLAG_SPACE_PLUS 8
+#define FLAG_ALT_CONV   16
+#define FLAG_ZERO_PAD   32
+
+/* Constants used the length modifiers.  */
+typedef enum
+  {
+    LENMOD_NONE = 0,
+    LENMOD_CHAR,     /* "hh" */
+    LENMOD_SHORT,    /* "h"  */
+    LENMOD_LONG,     /* "l"  */
+    LENMOD_LONGLONG, /* "ll" */
+    LENMOD_INTMAX,   /* "j"  */
+    LENMOD_SIZET,    /* "z"  */
+    LENMOD_PTRDIFF,  /* "t"  */
+    LENMOD_LONGDBL   /* "L"  */
+  } lenmod_t;
+
+/* All the conversion specifiers.  */
+typedef enum
+  {
+    CONSPEC_UNKNOWN = 0,
+    CONSPEC_DECIMAL,
+    CONSPEC_OCTAL,
+    CONSPEC_UNSIGNED,
+    CONSPEC_HEX,
+    CONSPEC_HEX_UP,
+    CONSPEC_FLOAT,
+    CONSPEC_FLOAT_UP,
+    CONSPEC_EXP,
+    CONSPEC_EXP_UP,
+    CONSPEC_F_OR_G,
+    CONSPEC_F_OR_G_UP,
+    CONSPEC_HEX_EXP,
+    CONSPEC_HEX_EXP_UP,
+    CONSPEC_CHAR,
+    CONSPEC_STRING,
+    CONSPEC_POINTER,
+    CONSPEC_STRERROR,
+    CONSPEC_BYTES_SO_FAR
+  } conspec_t;
+
+
+/* Constants describing all the suppoorted types.  Note that we list
+   all the types we know about even if certain types are not available
+   on this system. */
+typedef enum
+  {
+    VALTYPE_UNSUPPORTED = 0,  /* Artificial type for error detection.  */
+    VALTYPE_CHAR,
+    VALTYPE_SCHAR,
+    VALTYPE_UCHAR,
+    VALTYPE_SHORT,
+    VALTYPE_USHORT,
+    VALTYPE_INT,
+    VALTYPE_UINT,
+    VALTYPE_LONG,
+    VALTYPE_ULONG,
+    VALTYPE_LONGLONG,
+    VALTYPE_ULONGLONG,
+    VALTYPE_DOUBLE,
+    VALTYPE_LONGDOUBLE,
+    VALTYPE_STRING,
+    VALTYPE_INTMAX,
+    VALTYPE_UINTMAX,
+    VALTYPE_SIZE,
+    VALTYPE_PTRDIFF,
+    VALTYPE_POINTER,
+    VALTYPE_CHAR_PTR,
+    VALTYPE_SCHAR_PTR,
+    VALTYPE_SHORT_PTR,
+    VALTYPE_INT_PTR,
+    VALTYPE_LONG_PTR,
+    VALTYPE_LONGLONG_PTR,
+    VALTYPE_INTMAX_PTR,
+    VALTYPE_SIZE_PTR,
+    VALTYPE_PTRDIFF_PTR
+  } valtype_t;
+
+
+/* A union used to store the actual values. */
+typedef union
+{
+  char a_char;
+  signed char a_schar;
+  unsigned char a_uchar;
+  short a_short;
+  unsigned short a_ushort;
+  int a_int;
+  unsigned int a_uint;
+  long int a_long;
+  unsigned long int a_ulong;
+#ifdef HAVE_LONG_LONG_INT
+  long long int a_longlong;
+  unsigned long long int a_ulonglong;
+#endif
+  double a_double;
+#ifdef HAVE_LONG_DOUBLE
+  long double a_longdouble;
+#endif
+  const char *a_string;
+#ifdef HAVE_INTMAX_T
+  intmax_t a_intmax;
+#endif
+#ifdef HAVE_UINTMAX_T
+  intmax_t a_uintmax;
+#endif
+  size_t a_size;
+#ifdef HAVE_PTRDIFF_T
+  ptrdiff_t a_ptrdiff;
+#endif
+  void *a_void_ptr;
+  char *a_char_ptr;
+  signed char *a_schar_ptr;
+  short *a_short_ptr;
+  int  *a_int_ptr;
+  long *a_long_ptr;
+#ifdef HAVE_LONG_LONG_INT
+  long long int *a_longlong_ptr;
+#endif
+#ifdef HAVE_INTMAX_T
+  intmax_t *a_intmax_ptr;
+#endif
+  size_t *a_size_ptr;
+#ifdef HAVE_PTRDIFF_T
+  ptrdiff_t *a_ptrdiff_ptr;
+#endif
+} value_t;
+
+/* An object used to keep track of a format option and arguments. */
+struct argspec_s
+{
+  size_t length;       /* The length of these args including the percent.  */
+  unsigned int flags;  /* The conversion flags (bits defined by FLAG_foo).  */
+  int width;           /* The field width.  */
+  int precision;       /* The precision.  */
+  lenmod_t lenmod;     /* The length modifier.  */
+  conspec_t conspec;   /* The conversion specifier.  */
+  int arg_pos;         /* The position of the argument.  This one may
+                          be -1 to indicate that no value is expected
+                          (e.g. for "%m").  */
+  int width_pos;       /* The position of the argument for a field
+                          width star's value. 0 for not used.  */
+  int precision_pos;   /* The position of the argument for the a
+                          precision star's value.  0 for not used. */
+  valtype_t vt;        /* The type of the corresponding argument.  */
+};
+typedef struct argspec_s *argspec_t;
+
+/* An object to build up a table of values and their types.  */
+struct valueitem_s
+{
+  valtype_t vt;  /* The type of the value.  */
+  value_t value; /* The value.  */
+};
+typedef struct valueitem_s *valueitem_t;
+
+
+/* Not all systems have a C-90 compliant realloc.  To cope with this
+   we use this simple wrapper. */
+#ifndef _ESTREAM_PRINTF_REALLOC
+static void *
+fixed_realloc (void *a, size_t n)
+{
+  if (!a)
+    return malloc (n);
+
+  if (!n)
+    {
+      free (a);
+      return NULL;
+    }
+
+  return realloc (a, n);
+}
+#endif /*!_ESTREAM_PRINTF_REALLOC*/
+
+
+#ifdef DEBUG
+static void
+dump_argspecs (argspec_t arg, size_t argcount)
+{
+  int idx;
+
+  for (idx=0; argcount; argcount--, arg++, idx++)
+    fprintf (stderr,
+             "%2d: len=%u flags=%u width=%d prec=%d mod=%d "
+             "con=%d vt=%d pos=%d-%d-%d\n",
+             idx,
+             (unsigned int)arg->length,
+             arg->flags,
+             arg->width,
+             arg->precision,
+             arg->lenmod,
+             arg->conspec,
+             arg->vt,
+             arg->arg_pos,
+             arg->width_pos,
+             arg->precision_pos);
+}
+#endif /*DEBUG*/
+
+
+/* Set the vt field for ARG.  */
+static void
+compute_type (argspec_t arg)
+{
+  switch (arg->conspec)
+    {
+    case CONSPEC_UNKNOWN:
+      arg->vt = VALTYPE_UNSUPPORTED;
+      break;
+
+    case CONSPEC_DECIMAL:
+      switch (arg->lenmod)
+        {
+        case LENMOD_CHAR: arg->vt = VALTYPE_SCHAR; break;
+        case LENMOD_SHORT: arg->vt = VALTYPE_SHORT; break;
+        case LENMOD_LONG: arg->vt = VALTYPE_LONG; break;
+        case LENMOD_LONGLONG: arg->vt = VALTYPE_LONGLONG; break;
+        case LENMOD_INTMAX: arg->vt = VALTYPE_INTMAX; break;
+        case LENMOD_SIZET: arg->vt = VALTYPE_SIZE; break;
+        case LENMOD_PTRDIFF: arg->vt = VALTYPE_PTRDIFF; break;
+        default: arg->vt = VALTYPE_INT; break;
+        }
+      break;
+
+    case CONSPEC_OCTAL:
+    case CONSPEC_UNSIGNED:
+    case CONSPEC_HEX:
+    case CONSPEC_HEX_UP:
+      switch (arg->lenmod)
+        {
+        case LENMOD_CHAR: arg->vt = VALTYPE_UCHAR; break;
+        case LENMOD_SHORT: arg->vt = VALTYPE_USHORT; break;
+        case LENMOD_LONG: arg->vt = VALTYPE_ULONG; break;
+        case LENMOD_LONGLONG: arg->vt = VALTYPE_ULONGLONG; break;
+        case LENMOD_INTMAX: arg->vt = VALTYPE_UINTMAX; break;
+        case LENMOD_SIZET: arg->vt = VALTYPE_SIZE; break;
+        case LENMOD_PTRDIFF: arg->vt = VALTYPE_PTRDIFF; break;
+        default: arg->vt = VALTYPE_UINT; break;
+        }
+      break;
+
+    case CONSPEC_FLOAT:
+    case CONSPEC_FLOAT_UP:
+    case CONSPEC_EXP:
+    case CONSPEC_EXP_UP:
+    case CONSPEC_F_OR_G:
+    case CONSPEC_F_OR_G_UP:
+    case CONSPEC_HEX_EXP:
+    case CONSPEC_HEX_EXP_UP:
+      switch (arg->lenmod)
+        {
+        case LENMOD_LONGDBL: arg->vt = VALTYPE_LONGDOUBLE; break;
+        case LENMOD_LONG: arg->vt = VALTYPE_DOUBLE; break;
+        default: arg->vt = VALTYPE_DOUBLE; break;
+        }
+      break;
+
+    case CONSPEC_CHAR:
+      arg->vt = VALTYPE_INT;
+      break;
+
+    case CONSPEC_STRING:
+      arg->vt = VALTYPE_STRING;
+      break;
+
+    case CONSPEC_POINTER:
+      arg->vt = VALTYPE_POINTER;
+      break;
+
+    case CONSPEC_STRERROR:
+      arg->vt = VALTYPE_STRING;
+      break;
+
+    case CONSPEC_BYTES_SO_FAR:
+      switch (arg->lenmod)
+        {
+        case LENMOD_CHAR: arg->vt = VALTYPE_SCHAR_PTR; break;
+        case LENMOD_SHORT: arg->vt = VALTYPE_SHORT_PTR; break;
+        case LENMOD_LONG: arg->vt = VALTYPE_LONG_PTR; break;
+        case LENMOD_LONGLONG: arg->vt = VALTYPE_LONGLONG_PTR; break;
+        case LENMOD_INTMAX: arg->vt = VALTYPE_INTMAX_PTR; break;
+        case LENMOD_SIZET: arg->vt = VALTYPE_SIZE_PTR; break;
+        case LENMOD_PTRDIFF: arg->vt = VALTYPE_PTRDIFF_PTR; break;
+        default: arg->vt = VALTYPE_INT_PTR; break;
+        }
+      break;
+
+    }
+}
+
+
+
+/* Parse the FORMAT string and populate the specification array stored
+   at the address ARGSPECS_ADDR.  The caller has provided enough space
+   to store up to MAX_ARGSPECS in that buffer.  The function may
+   however ignore the provided buffer and malloc a larger one.  On
+   success the address of that larger buffer will be stored at
+   ARGSPECS_ADDR.  The actual number of specifications will be
+   returned at R_ARGSPECS_COUNT. */
+static int
+parse_format (const char *format,
+              argspec_t *argspecs_addr, size_t max_argspecs,
+              size_t *r_argspecs_count)
+{
+  const char *s;
+  argspec_t argspecs = *argspecs_addr;
+  argspec_t arg;
+  size_t argcount = 0;
+
+  if (!format)
+    goto leave_einval;
+
+  for (; *format; format++)
+    {
+      unsigned int flags;
+      int width, precision;
+      lenmod_t lenmod;
+      conspec_t conspec;
+      int arg_pos, width_pos, precision_pos;
+
+      if (*format != '%')
+        continue;
+      s = ++format;
+      if (!*s)
+        goto leave_einval;
+      if (*s == '%')
+        continue; /* Just a quoted percent.  */
+
+      /* First check whether there is a positional argument.  */
+      arg_pos = 0; /* No positional argument given.  */
+      if (*s >= '1' && *s <= '9')
+        {
+          const char *save_s = s;
+
+          arg_pos = (*s++ - '0');
+          for (; *s >= '0' && *s <= '9'; s++)
+            arg_pos = 10*arg_pos + (*s - '0');
+          if (arg_pos < 0)
+            goto leave_einval; /* Overflow during conversion.  */
+          if (*s == '$')
+            s++;
+          else
+            {
+              arg_pos = 0;
+              s = save_s;
+            }
+        }
+
+      /* Parse the flags.  */
+      flags = 0;
+      for ( ; *s; s++)
+        {
+          switch (*s)
+            {
+            case '\'': flags |= FLAG_GROUPING; break;
+            case '-': flags |= FLAG_LEFT_JUST; break;
+            case '+': flags |= FLAG_PLUS_SIGN; break;
+            case ' ': flags |= FLAG_SPACE_PLUS; break;
+            case '#': flags |= FLAG_ALT_CONV; break;
+            case '0': flags |= FLAG_ZERO_PAD; break;
+            default:
+              goto flags_parsed;
+            }
+        }
+    flags_parsed:
+
+      /* Parse the field width.  */
+      width_pos = 0;
+      if (*s == '*')
+        {
+          width = STAR_FIELD_VALUE;
+          s++;
+          /* If we have a positional argument, another one might also
+             be used to give the position of the star's value. */
+          if (arg_pos && *s >= '1' && *s <= '9')
+            {
+              width_pos = (*s++ - '0');
+              for (; *s >= '0' && *s <= '9'; s++)
+                width_pos = 10*width_pos + (*s - '0');
+              if (width_pos < 1)
+                goto leave_einval; /* Overflow during conversion.  */
+              if (*s != '$')
+                goto leave_einval; /* Not followed by $.  */
+              s++;
+            }
+        }
+      else if ( *s >= '0' && *s <= '9')
+        {
+          width = (*s++ - '0');
+          for (; *s >= '0' && *s <= '9'; s++)
+            {
+              if (!width && *s == '0')
+                goto leave_einval; /* Leading zeroes are not allowed.
+                                      Fixme: check what other
+                                      implementations do. */
+              width = 10*width + (*s - '0');
+            }
+          if (width < 0)
+            goto leave_einval; /* Overflow during conversion.  */
+        }
+      else
+        width = NO_FIELD_VALUE;
+
+      /* Parse the precision.  */
+      precision_pos = 0;
+      precision = NO_FIELD_VALUE;
+      if (*s == '.')
+        {
+          int ignore_value = (s[1] == '-');
+
+          s++;
+          if (*s == '*')
+            {
+              precision = STAR_FIELD_VALUE;
+              s++;
+              /* If we have a positional argument, another one might also
+                 be used to give the position of the star's value. */
+              if (arg_pos && *s >= '1' && *s <= '9')
+                {
+                  precision_pos = (*s++ - '0');
+                  for (; *s >= '0' && *s <= '9'; s++)
+                    precision_pos = 10*precision_pos + (*s - '0');
+                  if (precision_pos < 1)
+                    goto leave_einval; /* Overflow during conversion.  */
+                  if (*s != '$')
+                    goto leave_einval; /* Not followed by $.  */
+                  s++;
+                }
+            }
+          else if ( *s >= '0' && *s <= '9')
+            {
+              precision = (*s++ - '0');
+              for (; *s >= '0' && *s <= '9'; s++)
+                {
+                  if (!precision && *s == '0')
+                    goto leave_einval; /* Leading zeroes are not allowed.
+                                          Fixme: check what other
+                                          implementations do. */
+                  precision = 10*precision + (*s - '0');
+                }
+              if (precision < 0)
+                goto leave_einval; /* Overflow during conversion.  */
+            }
+          else
+            precision = 0;
+          if (ignore_value)
+            precision = NO_FIELD_VALUE;
+        }
+
+      /* Parse the length modifiers.  */
+      switch (*s)
+        {
+        case 'h':
+          if (s[1] == 'h')
+            {
+              lenmod = LENMOD_CHAR;
+              s++;
+            }
+          else
+            lenmod = LENMOD_SHORT;
+          s++;
+          break;
+        case 'l':
+          if (s[1] == 'l')
+            {
+              lenmod = LENMOD_LONGLONG;
+              s++;
+            }
+          else
+            lenmod = LENMOD_LONG;
+          s++;
+          break;
+        case 'j': lenmod = LENMOD_INTMAX; s++; break;
+        case 'z': lenmod = LENMOD_SIZET; s++; break;
+        case 't': lenmod = LENMOD_PTRDIFF; s++; break;
+        case 'L': lenmod = LENMOD_LONGDBL; s++; break;
+        default:  lenmod = LENMOD_NONE; break;
+        }
+
+      /* Parse the conversion specifier.  */
+      switch (*s)
+        {
+        case 'd':
+        case 'i': conspec = CONSPEC_DECIMAL; break;
+        case 'o': conspec = CONSPEC_OCTAL; break;
+        case 'u': conspec = CONSPEC_UNSIGNED; break;
+        case 'x': conspec = CONSPEC_HEX; break;
+        case 'X': conspec = CONSPEC_HEX_UP; break;
+        case 'f': conspec = CONSPEC_FLOAT; break;
+        case 'F': conspec = CONSPEC_FLOAT_UP; break;
+        case 'e': conspec = CONSPEC_EXP; break;
+        case 'E': conspec = CONSPEC_EXP_UP; break;
+        case 'g': conspec = CONSPEC_F_OR_G; break;
+        case 'G': conspec = CONSPEC_F_OR_G_UP; break;
+        case 'a': conspec = CONSPEC_HEX_EXP; break;
+        case 'A': conspec = CONSPEC_HEX_EXP_UP; break;
+        case 'c': conspec = CONSPEC_CHAR; break;
+        case 's': conspec = CONSPEC_STRING; break;
+        case 'p': conspec = CONSPEC_POINTER; break;
+        case 'n': conspec = CONSPEC_BYTES_SO_FAR; break;
+        case 'C': conspec = CONSPEC_CHAR; lenmod = LENMOD_LONG; break;
+        case 'S': conspec = CONSPEC_STRING; lenmod = LENMOD_LONG; break;
+        case 'm': conspec = CONSPEC_STRERROR; arg_pos = -1; break;
+        default: conspec = CONSPEC_UNKNOWN;
+        }
+
+      /* Save the args. */
+      if (argcount >= max_argspecs)
+        {
+          /* We either need to allocate a new array instead of the
+             caller provided one or realloc the array.  Instead of
+             using realloc we allocate a new one and release the
+             original one then. */
+          size_t n, newmax;
+          argspec_t newarg;
+
+          newmax = max_argspecs + ARGSPECS_BUMP_VALUE;
+          if (newmax <= max_argspecs)
+            goto leave_einval;  /* Too many arguments. */
+          newarg = calloc (newmax, sizeof *newarg);
+          if (!newarg)
+            goto leave;
+          for (n=0; n < argcount; n++)
+            newarg[n] = argspecs[n];
+          if (argspecs != *argspecs_addr)
+            free (argspecs);
+          argspecs = newarg;
+          max_argspecs = newmax;
+        }
+
+      arg = argspecs + argcount;
+      arg->length = s - format + 2;
+      arg->flags = flags;
+      arg->width = width;
+      arg->precision = precision;
+      arg->lenmod = lenmod;
+      arg->conspec = conspec;
+      arg->arg_pos = arg_pos;
+      arg->width_pos = width_pos;
+      arg->precision_pos = precision_pos;
+      compute_type (arg);
+      argcount++;
+      format = s;
+    }
+
+  *argspecs_addr = argspecs;
+  *r_argspecs_count = argcount;
+  return 0; /* Success.  */
+
+ leave_einval:
+  _set_errno (EINVAL);
+ leave:
+  if (argspecs != *argspecs_addr)
+    free (argspecs);
+  *argspecs_addr = NULL;
+  return -1;
+}
+
+
+/* This function reads all the values as specified by VALUETABLE into
+   VALUETABLE.  The values are expected in VAARGS.  The function
+   returns -1 if a specified type is not supported. */
+static int
+read_values (valueitem_t valuetable, size_t valuetable_len, va_list vaargs)
+{
+  int validx;
+
+  for (validx=0; validx < valuetable_len; validx++)
+    {
+      value_t *value = &valuetable[validx].value;
+      valtype_t vt = valuetable[validx].vt;
+
+      switch (vt)
+        {
+        case VALTYPE_CHAR: value->a_char = va_arg (vaargs, int); break;
+        case VALTYPE_CHAR_PTR:
+          value->a_char_ptr = va_arg (vaargs, char *);
+          break;
+        case VALTYPE_SCHAR: value->a_schar = va_arg (vaargs, int); break;
+        case VALTYPE_SCHAR_PTR:
+          value->a_schar_ptr = va_arg (vaargs, signed char *);
+          break;
+        case VALTYPE_UCHAR: value->a_uchar = va_arg (vaargs, int); break;
+        case VALTYPE_SHORT: value->a_short = va_arg (vaargs, int); break;
+        case VALTYPE_USHORT: value->a_ushort = va_arg (vaargs, int); break;
+        case VALTYPE_SHORT_PTR:
+          value->a_short_ptr = va_arg (vaargs, short *);
+          break;
+        case VALTYPE_INT:
+          value->a_int = va_arg (vaargs, int);
+          break;
+        case VALTYPE_INT_PTR:
+          value->a_int_ptr = va_arg (vaargs, int *);
+          break;
+        case VALTYPE_UINT:
+          value->a_uint = va_arg (vaargs, unsigned int);
+          break;
+        case VALTYPE_LONG:
+          value->a_long = va_arg (vaargs, long);
+          break;
+        case VALTYPE_ULONG:
+          value->a_ulong = va_arg (vaargs, unsigned long);
+          break;
+        case VALTYPE_LONG_PTR:
+          value->a_long_ptr = va_arg (vaargs, long *);
+          break;
+#ifdef HAVE_LONG_LONG_INT
+        case VALTYPE_LONGLONG:
+          value->a_longlong = va_arg (vaargs, long long int);
+          break;
+        case VALTYPE_ULONGLONG:
+          value->a_ulonglong = va_arg (vaargs, unsigned long long int);
+          break;
+        case VALTYPE_LONGLONG_PTR:
+          value->a_longlong_ptr = va_arg (vaargs, long long *);
+          break;
+#endif
+        case VALTYPE_DOUBLE:
+          value->a_double = va_arg (vaargs, double);
+          break;
+#ifdef HAVE_LONG_DOUBLE
+        case VALTYPE_LONGDOUBLE:
+          value->a_longdouble = va_arg (vaargs, long double);
+          break;
+#endif
+        case VALTYPE_STRING:
+          value->a_string = va_arg (vaargs, const char *);
+          break;
+        case VALTYPE_POINTER:
+          value->a_void_ptr = va_arg (vaargs, void *);
+          break;
+#ifdef HAVE_INTMAX_T
+        case VALTYPE_INTMAX:
+          value->a_intmax = va_arg (vaargs, intmax_t);
+          break;
+        case VALTYPE_INTMAX_PTR:
+          value->a_intmax_ptr = va_arg (vaargs, intmax_t *);
+          break;
+#endif
+#ifdef HAVE_UINTMAX_T
+        case VALTYPE_UINTMAX:
+          value->a_uintmax = va_arg (vaargs, uintmax_t);
+          break;
+#endif
+        case VALTYPE_SIZE:
+          value->a_size = va_arg (vaargs, size_t);
+          break;
+        case VALTYPE_SIZE_PTR:
+          value->a_size_ptr = va_arg (vaargs, size_t *);
+          break;
+#ifdef HAVE_PTRDIFF_T
+        case VALTYPE_PTRDIFF:
+          value->a_ptrdiff = va_arg (vaargs, ptrdiff_t);
+          break;
+        case VALTYPE_PTRDIFF_PTR:
+          value->a_ptrdiff_ptr = va_arg (vaargs, ptrdiff_t *);
+          break;
+#endif
+        default: /* Unsupported type.  */
+          return -1;
+        }
+    }
+  return 0;
+}
+
+
+
+/* Output COUNT padding characters PADCHAR and update NBYTES by the
+   number of bytes actually written.  */
+static int
+pad_out (estream_printf_out_t outfnc, void *outfncarg,
+         int padchar, int count, size_t *nbytes)
+{
+  char buf[32];
+  size_t n;
+  int rc;
+
+  while (count > 0)
+    {
+      n = (count <= sizeof buf)? count : sizeof buf;
+      memset (buf, padchar, n);
+      rc = outfnc (outfncarg, buf, n);
+      if (rc)
+        return rc;
+      *nbytes += n;
+      count -= n;
+    }
+
+  return 0;
+}
+
+
+/* "d,i,o,u,x,X" formatting.  OUTFNC and OUTFNCARG describes the
+   output routine, ARG gives the argument description and VALUE the
+   actual value (its type is available through arg->vt).  */
+static int
+pr_integer (estream_printf_out_t outfnc, void *outfncarg,
+            argspec_t arg, value_t value, size_t *nbytes)
+{
+  int rc;
+#ifdef HAVE_LONG_LONG_INT
+  unsigned long long aulong;
+#else
+  unsigned long aulong;
+#endif
+  char numbuf[100];
+  char *p, *pend;
+  size_t n;
+  char signchar = 0;
+  int n_prec;  /* Number of extra precision digits required.  */
+  int n_extra; /* Extra number of prefix or sign characters.  */
+
+  if (arg->conspec == CONSPEC_DECIMAL)
+    {
+#ifdef HAVE_LONG_LONG_INT
+      long long along;
+#else
+      long along;
+#endif
+
+      switch (arg->vt)
+        {
+        case VALTYPE_SHORT: along = value.a_short; break;
+        case VALTYPE_INT: along = value.a_int; break;
+        case VALTYPE_LONG: along = value.a_long; break;
+#ifdef HAVE_LONG_LONG_INT
+        case VALTYPE_LONGLONG: along = value.a_longlong; break;
+        case VALTYPE_SIZE: along = value.a_size; break;
+# ifdef HAVE_INTMAX_T
+        case VALTYPE_INTMAX: along = value.a_intmax; break;
+# endif
+# ifdef HAVE_PTRDIFF_T
+        case VALTYPE_PTRDIFF: along = value.a_ptrdiff; break;
+# endif
+#endif /*HAVE_LONG_LONG_INT*/
+        default:
+          return -1;
+        }
+      if (along < 0)
+        {
+          aulong = -along;
+          signchar = '-';
+        }
+      else
+        aulong = along;
+    }
+  else
+    {
+      switch (arg->vt)
+        {
+        case VALTYPE_USHORT: aulong = value.a_ushort; break;
+        case VALTYPE_UINT: aulong = value.a_uint; break;
+        case VALTYPE_ULONG: aulong = value.a_ulong; break;
+#ifdef HAVE_LONG_LONG_INT
+        case VALTYPE_ULONGLONG: aulong = value.a_ulonglong; break;
+        case VALTYPE_SIZE: aulong = value.a_size; break;
+# ifdef HAVE_UINTMAX_T
+        case VALTYPE_UINTMAX: aulong = value.a_uintmax; break;
+# endif
+# ifdef HAVE_PTRDIFF_T
+        case VALTYPE_PTRDIFF: aulong = value.a_ptrdiff; break;
+# endif
+#endif /*HAVE_LONG_LONG_INT*/
+        default:
+          return -1;
+        }
+    }
+
+  if (signchar == '-')
+    ;
+  else if ((arg->flags & FLAG_PLUS_SIGN))
+    signchar = '+';
+  else if ((arg->flags & FLAG_SPACE_PLUS))
+    signchar = ' ';
+
+  n_extra = !!signchar;
+
+  /* We build the string up backwards.  */
+  p = pend = numbuf + DIM(numbuf);
+  if ((!aulong && !arg->precision))
+    ;
+  else if (arg->conspec == CONSPEC_DECIMAL
+           || arg->conspec == CONSPEC_UNSIGNED)
+    {
+      int grouping = -1;
+      const char * grouping_string =
+#ifdef HAVE_LANGINFO_THOUSEP
+        nl_langinfo(THOUSEP);
+#else
+        "'";
+#endif
+
+      do
+        {
+          if ((arg->flags & FLAG_GROUPING)
+              && (++grouping == 3) && *grouping_string)
+            {
+              *--p = *grouping_string;
+              grouping = 0;
+            }
+          *--p = '0' + (aulong % 10);
+          aulong /= 10;
+        }
+      while (aulong);
+    }
+  else if (arg->conspec == CONSPEC_OCTAL)
+    {
+      do
+        {
+          *--p = '0' + (aulong % 8);
+          aulong /= 8;
+        }
+      while (aulong);
+      if ((arg->flags & FLAG_ALT_CONV) && *p != '0')
+        *--p = '0';
+    }
+  else /* HEX or HEXUP */
+    {
+      const char *digits = ((arg->conspec == CONSPEC_HEX)
+                            ? "0123456789abcdef" : "0123456789ABCDEF");
+      do
+        {
+          *--p = digits[(aulong % 16)];
+          aulong /= 16;
+        }
+      while (aulong);
+      if ((arg->flags & FLAG_ALT_CONV))
+        n_extra += 2;
+    }
+
+  n = pend - p;
+
+  if ((arg->flags & FLAG_ZERO_PAD)
+      && arg->precision == NO_FIELD_VALUE && !(arg->flags & FLAG_LEFT_JUST)
+      && n && arg->width - n_extra > n )
+    n_prec = arg->width - n_extra - n;
+  else if (arg->precision > 0 && arg->precision > n)
+    n_prec = arg->precision - n;
+  else
+    n_prec = 0;
+
+  if (!(arg->flags & FLAG_LEFT_JUST)
+      && arg->width >= 0 && arg->width - n_extra > n
+      && arg->width - n_extra - n >= n_prec )
+    {
+      rc = pad_out (outfnc, outfncarg, ' ',
+                    arg->width - n_extra - n - n_prec, nbytes);
+      if (rc)
+        return rc;
+    }
+
+  if (signchar)
+    {
+      rc = outfnc (outfncarg, &signchar, 1);
+      if (rc)
+        return rc;
+      *nbytes += 1;
+    }
+
+  if ((arg->flags & FLAG_ALT_CONV)
+      && (arg->conspec == CONSPEC_HEX || arg->conspec == CONSPEC_HEX_UP))
+    {
+      rc = outfnc (outfncarg, arg->conspec == CONSPEC_HEX? "0x": "0X", 2);
+      if (rc)
+        return rc;
+      *nbytes += 2;
+    }
+
+  if (n_prec)
+    {
+      rc = pad_out (outfnc, outfncarg, '0', n_prec, nbytes);
+      if (rc)
+        return rc;
+    }
+
+  rc = outfnc (outfncarg, p, pend - p);
+  if (rc)
+    return rc;
+  *nbytes += pend - p;
+
+  if ((arg->flags & FLAG_LEFT_JUST)
+      && arg->width >= 0 && arg->width - n_extra - n_prec > n)
+    {
+      rc = pad_out (outfnc, outfncarg, ' ',
+                    arg->width - n_extra - n_prec - n, nbytes);
+      if (rc)
+        return rc;
+    }
+
+  return 0;
+}
+
+
+/* "e,E,f,F,g,G,a,A" formatting.  OUTFNC and OUTFNCARG describes the
+   output routine, ARG gives the argument description and VALUE the
+   actual value (its type is available through arg->vt).  For
+   portability reasons sprintf is used for the actual formatting.
+   This is useful because sprint is the only standard function to
+   convert a floating number into its ascii representation.  To avoid
+   using malloc we just pass the precision to sprintf and do the final
+   formatting with our own code.  */
+static int
+pr_float (estream_printf_out_t outfnc, void *outfncarg,
+          argspec_t arg, value_t value, size_t *nbytes)
+{
+  int rc;
+#ifdef HAVE_LONG_DOUBLE
+  long double adblfloat = 0; /* Just to please gcc.  */
+  int use_dbl = 0;
+#endif
+  double afloat;
+  char numbuf[350];
+  char formatstr[20];
+  char *p, *pend;
+  size_t n;
+  char signchar = 0;
+  int n_extra;  /* Extra number of prefix or sign characters.  */
+
+  switch (arg->vt)
+    {
+    case VALTYPE_DOUBLE: afloat = value.a_double; break;
+#ifdef HAVE_LONG_DOUBLE
+    case VALTYPE_LONGDOUBLE:
+      afloat = 0;  /* Just to please gcc.  */
+      adblfloat = value.a_longdouble;
+      use_dbl=1; break;
+#endif
+    default:
+      return -1;
+    }
+
+  /* We build the string using sprint.  */
+  p = formatstr + sizeof formatstr;
+  *--p = 0;
+  switch (arg->conspec)
+    {
+    case CONSPEC_FLOAT:      *--p = 'f'; break;
+    case CONSPEC_FLOAT_UP:   *--p = 'F'; break;
+    case CONSPEC_EXP:        *--p = 'e'; break;
+    case CONSPEC_EXP_UP:     *--p = 'E'; break;
+    case CONSPEC_F_OR_G:     *--p = 'g'; break;
+    case CONSPEC_F_OR_G_UP:  *--p = 'G'; break;
+    case CONSPEC_HEX_EXP:    *--p = 'a'; break;
+    case CONSPEC_HEX_EXP_UP: *--p = 'A'; break;
+    default:
+      return -1; /* Actually a bug.  */
+    }
+#ifdef HAVE_LONG_DOUBLE
+  if (use_dbl)
+    *--p = 'L';
+#endif
+  if (arg->precision != NO_FIELD_VALUE)
+    {
+      /* Limit it to a meaningful value so that even a stupid sprintf
+         won't overflow our buffer.  */
+      n = arg->precision <= 100? arg->precision : 100;
+      do
+        {
+          *--p = '0' + (n % 10);
+          n /= 10;
+        }
+      while (n);
+      *--p = '.';
+    }
+  if ((arg->flags & FLAG_ALT_CONV))
+    *--p = '#';
+  *--p = '%';
+#ifdef HAVE_LONG_DOUBLE
+  if (use_dbl)
+    sprintf (numbuf, p, adblfloat);
+  else
+#endif /*HAVE_LONG_DOUBLE*/
+    sprintf (numbuf, p, afloat);
+  p = numbuf;
+  n = strlen (numbuf);
+  pend = p + n;
+
+  if (*p =='-')
+    {
+      signchar = '-';
+      p++;
+      n--;
+    }
+  else if ((arg->flags & FLAG_PLUS_SIGN))
+    signchar = '+';
+  else if ((arg->flags & FLAG_SPACE_PLUS))
+    signchar = ' ';
+
+  n_extra = !!signchar;
+
+  if (!(arg->flags & FLAG_LEFT_JUST)
+      && arg->width >= 0 && arg->width - n_extra > n)
+    {
+      rc = pad_out (outfnc, outfncarg, ' ', arg->width - n_extra - n, nbytes);
+      if (rc)
+        return rc;
+    }
+
+  if (signchar)
+    {
+      rc = outfnc (outfncarg, &signchar, 1);
+      if (rc)
+        return rc;
+      *nbytes += 1;
+    }
+
+  rc = outfnc (outfncarg, p, pend - p);
+  if (rc)
+    return rc;
+  *nbytes += pend - p;
+
+  if ((arg->flags & FLAG_LEFT_JUST)
+      && arg->width >= 0 && arg->width - n_extra > n)
+    {
+      rc = pad_out (outfnc, outfncarg, ' ', arg->width - n_extra - n, nbytes);
+      if (rc)
+        return rc;
+    }
+
+  return 0;
+}
+
+
+/* "c" formatting.  */
+static int
+pr_char (estream_printf_out_t outfnc, void *outfncarg,
+            argspec_t arg, value_t value, size_t *nbytes)
+{
+  int rc;
+  char buf[1];
+
+  if (arg->vt != VALTYPE_INT)
+    return -1;
+  buf[0] = (unsigned int)value.a_int;
+  rc = outfnc (outfncarg, buf, 1);
+  if(rc)
+    return rc;
+  *nbytes += 1;
+
+  return 0;
+}
+
+
+/* "s" formatting.  */
+static int
+pr_string (estream_printf_out_t outfnc, void *outfncarg,
+           argspec_t arg, value_t value, size_t *nbytes,
+           gpgrt_string_filter_t sf, void *sfvalue, int string_no)
+{
+  int rc;
+  size_t n;
+  const char *string, *s;
+
+  if (arg->vt != VALTYPE_STRING)
+    return -1;
+  if (sf)
+    string = sf (value.a_string, string_no, sfvalue);
+  else
+    string = value.a_string;
+
+  if (!string)
+    string = "(null)";
+  if (arg->precision >= 0)
+    {
+      /* Test for nul after N so that we can pass a non-nul terminated
+         string.  */
+      for (n=0,s=string; n < arg->precision && *s; s++)
+        n++;
+    }
+  else
+    n = strlen (string);
+
+  if (!(arg->flags & FLAG_LEFT_JUST)
+      && arg->width >= 0 && arg->width > n )
+    {
+      rc = pad_out (outfnc, outfncarg, ' ', arg->width - n, nbytes);
+      if (rc)
+        goto leave;
+    }
+
+  rc = outfnc (outfncarg, string, n);
+  if (rc)
+    goto leave;
+  *nbytes += n;
+
+  if ((arg->flags & FLAG_LEFT_JUST)
+      && arg->width >= 0 && arg->width > n)
+    {
+      rc = pad_out (outfnc, outfncarg, ' ', arg->width - n, nbytes);
+      if (rc)
+        goto leave;
+    }
+
+  rc = 0;
+
+ leave:
+  if (sf) /* Tell the filter to release resources.  */
+    sf (value.a_string, -1, sfvalue);
+
+  return rc;
+}
+
+
+/* "p" formatting.  */
+static int
+pr_pointer (estream_printf_out_t outfnc, void *outfncarg,
+            argspec_t arg, value_t value, size_t *nbytes)
+{
+  int rc;
+#if defined(HAVE_LONG_LONG_INT) && (SIZEOF_UNSIGNED_LONG < SIZEOF_VOID_P)
+  unsigned long long aulong;
+#else
+  unsigned long aulong;
+#endif
+  char numbuf[100];
+  char *p, *pend;
+
+  if (arg->vt != VALTYPE_POINTER)
+    return -1;
+  /* We assume that a pointer can be converted to an unsigned long.
+     That is not correct for a 64 bit Windows, but then we assume that
+     long long is supported and usable for storing a pointer.  */
+#if defined(HAVE_LONG_LONG_INT) && (SIZEOF_UNSIGNED_LONG < SIZEOF_VOID_P)
+  aulong = (unsigned long long)value.a_void_ptr;
+#else
+  aulong = (unsigned long)value.a_void_ptr;
+#endif
+
+  p = pend = numbuf + DIM(numbuf);
+  do
+    {
+      *--p = "0123456789abcdefx"[(aulong % 16)];
+      aulong /= 16;
+    }
+  while (aulong);
+  while ((pend-p) < 2*sizeof (aulong))
+    *--p = '0';
+  *--p = 'x';
+  *--p = '0';
+
+  rc = outfnc (outfncarg, p, pend - p);
+  if (rc)
+    return rc;
+  *nbytes += pend - p;
+
+  return 0;
+}
+
+/* "n" pesudo format operation.  */
+static int
+pr_bytes_so_far (estream_printf_out_t outfnc, void *outfncarg,
+                 argspec_t arg, value_t value, size_t *nbytes)
+{
+  (void)outfnc;
+  (void)outfncarg;
+
+  switch (arg->vt)
+    {
+    case VALTYPE_SCHAR_PTR:
+      *value.a_schar_ptr = (signed char)(unsigned int)(*nbytes);
+      break;
+    case VALTYPE_SHORT_PTR:
+      *value.a_short_ptr = (short)(unsigned int)(*nbytes);
+      break;
+    case VALTYPE_LONG_PTR:
+      *value.a_long_ptr = (long)(*nbytes);
+      break;
+#ifdef HAVE_LONG_LONG_INT
+    case VALTYPE_LONGLONG_PTR:
+      *value.a_longlong_ptr = (long long)(*nbytes);
+      break;
+#endif
+#ifdef HAVE_INTMAX_T
+    case VALTYPE_INTMAX_PTR:
+      *value.a_intmax_ptr = (intmax_t)(*nbytes);
+      break;
+#endif
+    case VALTYPE_SIZE_PTR:
+      *value.a_size_ptr = (*nbytes);
+      break;
+#ifdef HAVE_PTRDIFF_T
+    case VALTYPE_PTRDIFF_PTR:
+      *value.a_ptrdiff_ptr = (ptrdiff_t)(*nbytes);
+      break;
+#endif
+    case VALTYPE_INT_PTR:
+      *value.a_int_ptr = (int)(*nbytes);
+      break;
+    default:
+      return -1; /* An unsupported type has been used.  */
+    }
+
+  return 0;
+}
+
+
+
+/* Run the actual formatting.  OUTFNC and OUTFNCARG are the output
+ * functions.  FORMAT is format string ARGSPECS is the parsed format
+ * string, ARGSPECS_LEN the number of items in ARGSPECS.
+ * STRING_FILTER is an optional function to filter string (%s) args;
+ * it is called with the original string and the count of already
+ * processed %s arguments.  Its return value will be used instead of
+ * the original string.  VALUETABLE holds the values and may be
+ * directly addressed using the position arguments given by ARGSPECS.
+ * MYERRNO is used for the "%m" conversion. NBYTES well be updated to
+ * reflect the number of bytes send to the output function. */
+static int
+do_format (estream_printf_out_t outfnc, void *outfncarg,
+           gpgrt_string_filter_t sf, void *sfvalue,
+           const char *format, argspec_t argspecs, size_t argspecs_len,
+           valueitem_t valuetable, int myerrno, size_t *nbytes)
+{
+  int rc = 0;
+  const char *s;
+  argspec_t arg = argspecs;
+  int argidx = 0; /* Only used for assertion.  */
+  size_t n;
+  value_t value;
+  int string_no = 0;  /* Number of processed "%s" args.  */
+
+  s = format;
+  while ( *s )
+    {
+      if (*s != '%')
+        {
+          s++;
+          continue;
+        }
+      if (s != format)
+        {
+          rc = outfnc (outfncarg, format, (n=s-format));
+          if (rc)
+            return rc;
+          *nbytes += n;
+        }
+      if (s[1] == '%')
+        {
+          /* Note that this code ignores one trailing percent escape -
+             this is however okay as the args parser must have
+             detected this already.  */
+          rc = outfnc (outfncarg, s, 1);
+          if (rc)
+            return rc;
+          *nbytes += 1;
+          s += 2;
+          format = s;
+          continue;
+        }
+
+      /* Save the next start.  */
+      s += arg->length;
+      format = s;
+
+      gpgrt_assert (argidx < argspecs_len);
+      argidx++;
+
+      /* Apply indirect field width and precision values.  */
+      if (arg->width == STAR_FIELD_VALUE)
+        {
+          gpgrt_assert (valuetable[arg->width_pos-1].vt == VALTYPE_INT);
+          arg->width = valuetable[arg->width_pos-1].value.a_int;
+          if (arg->width < 0)
+            {
+              arg->width = -arg->width;
+              arg->flags |= FLAG_LEFT_JUST;
+            }
+        }
+      if (arg->precision == STAR_FIELD_VALUE)
+        {
+          gpgrt_assert (valuetable[arg->precision_pos-1].vt == VALTYPE_INT);
+          arg->precision = valuetable[arg->precision_pos-1].value.a_int;
+          if (arg->precision < 0)
+            arg->precision = NO_FIELD_VALUE;
+        }
+
+      if (arg->arg_pos == -1 && arg->conspec == CONSPEC_STRERROR)
+        value.a_string = strerror (myerrno);
+      else
+        {
+          gpgrt_assert (arg->vt == valuetable[arg->arg_pos-1].vt);
+          value = valuetable[arg->arg_pos-1].value;
+        }
+
+      switch (arg->conspec)
+        {
+        case CONSPEC_UNKNOWN: gpgrt_assert (!"bug"); break;
+
+        case CONSPEC_DECIMAL:
+        case CONSPEC_UNSIGNED:
+        case CONSPEC_OCTAL:
+        case CONSPEC_HEX:
+        case CONSPEC_HEX_UP:
+          rc = pr_integer (outfnc, outfncarg, arg, value, nbytes);
+          break;
+        case CONSPEC_FLOAT:
+        case CONSPEC_FLOAT_UP:
+        case CONSPEC_EXP:
+        case CONSPEC_EXP_UP:
+        case CONSPEC_F_OR_G:
+        case CONSPEC_F_OR_G_UP:
+        case CONSPEC_HEX_EXP:
+        case CONSPEC_HEX_EXP_UP:
+          rc = pr_float (outfnc, outfncarg, arg, value, nbytes);
+          break;
+        case CONSPEC_CHAR:
+          rc = pr_char (outfnc, outfncarg, arg, value, nbytes);
+          break;
+        case CONSPEC_STRING:
+          rc = pr_string (outfnc, outfncarg, arg, value, nbytes,
+                          sf, sfvalue, string_no++);
+          break;
+        case CONSPEC_STRERROR:
+          rc = pr_string (outfnc, outfncarg, arg, value, nbytes,
+                          NULL, NULL, 0);
+          break;
+        case CONSPEC_POINTER:
+          rc = pr_pointer (outfnc, outfncarg, arg, value, nbytes);
+          break;
+        case CONSPEC_BYTES_SO_FAR:
+          rc = pr_bytes_so_far (outfnc, outfncarg, arg, value, nbytes);
+          break;
+        }
+      if (rc)
+        return rc;
+      arg++;
+    }
+
+  /* Print out any trailing stuff. */
+  n = s - format;
+  rc = n? outfnc (outfncarg, format, n) : 0;
+  if (!rc)
+    *nbytes += n;
+
+  return rc;
+}
+
+
+
+
+/* The versatile printf formatting routine.  It expects a callback
+   function OUTFNC and an opaque argument OUTFNCARG used for actual
+   output of the formatted stuff.  FORMAT is the format specification
+   and VAARGS a variable argumemt list matching the arguments of
+   FORMAT.  */
+int
+_gpgrt_estream_format (estream_printf_out_t outfnc,
+                       void *outfncarg,
+                       gpgrt_string_filter_t sf, void *sfvalue,
+                       const char *format, va_list vaargs)
+{
+  /* Buffer to hold the argspecs and a pointer to it.*/
+  struct argspec_s argspecs_buffer[DEFAULT_MAX_ARGSPECS];
+  argspec_t argspecs = argspecs_buffer;
+  size_t argspecs_len;  /* Number of specifications in ARGSPECS.  */
+
+  /* Buffer to hold the description for the values.  */
+  struct valueitem_s valuetable_buffer[DEFAULT_MAX_VALUES];
+  valueitem_t valuetable = valuetable_buffer;
+
+  int rc;        /* Return code. */
+  size_t argidx; /* Used to index the argspecs array.  */
+  size_t validx; /* Used to index the valuetable.  */
+  int max_pos;   /* Highest argument position.  */
+
+  size_t nbytes = 0; /* Keep track of the number of bytes passed to
+                        the output function.  */
+
+  int myerrno = errno; /* Save the errno for use with "%m". */
+
+
+  /* Parse the arguments to come up with descriptive list.  We can't
+     do this on the fly because we need to support positional
+     arguments. */
+  rc = parse_format (format, &argspecs, DIM(argspecs_buffer), &argspecs_len);
+  if (rc)
+    goto leave;
+
+  /* Check that all ARG_POS fields are set.  */
+  for (argidx=0,max_pos=0; argidx < argspecs_len; argidx++)
+    {
+      if (argspecs[argidx].arg_pos != -1
+          && argspecs[argidx].arg_pos > max_pos)
+        max_pos = argspecs[argidx].arg_pos;
+      if (argspecs[argidx].width_pos > max_pos)
+        max_pos = argspecs[argidx].width_pos;
+      if (argspecs[argidx].precision_pos > max_pos)
+        max_pos = argspecs[argidx].precision_pos;
+    }
+  if (!max_pos)
+    {
+      /* Fill in all the positions.  */
+      for (argidx=0; argidx < argspecs_len; argidx++)
+        {
+          if (argspecs[argidx].width == STAR_FIELD_VALUE)
+            argspecs[argidx].width_pos = ++max_pos;
+          if (argspecs[argidx].precision == STAR_FIELD_VALUE)
+            argspecs[argidx].precision_pos = ++max_pos;
+          if (argspecs[argidx].arg_pos != -1 )
+            argspecs[argidx].arg_pos = ++max_pos;
+        }
+    }
+  else
+    {
+      /* Check that they are all filled.   More test are done later.  */
+      for (argidx=0; argidx < argspecs_len; argidx++)
+        {
+          if (!argspecs[argidx].arg_pos
+              || (argspecs[argidx].width == STAR_FIELD_VALUE
+                  && !argspecs[argidx].width_pos)
+              || (argspecs[argidx].precision == STAR_FIELD_VALUE
+                  && !argspecs[argidx].precision_pos))
+            goto leave_einval;
+        }
+    }
+  /* Check that there is no overflow in max_pos and that it has a
+     reasonable length.  There may never be more elements than the
+     number of characters in FORMAT.  */
+  if (max_pos < 0 || max_pos >= strlen (format))
+    goto leave_einval;
+
+#ifdef DEBUG
+    dump_argspecs (argspecs, argspecs_len);
+#endif
+
+  /* Allocate a table to hold the values.  If it is small enough we
+     use a stack allocated buffer.  */
+  if (max_pos > DIM(valuetable_buffer))
+    {
+      valuetable = calloc (max_pos, sizeof *valuetable);
+      if (!valuetable)
+        goto leave_error;
+    }
+  else
+    {
+      for (validx=0; validx < DIM(valuetable_buffer); validx++)
+        {
+          valuetable[validx].vt = VALTYPE_UNSUPPORTED;
+          memset (&valuetable[validx].value, 0,
+                  sizeof valuetable[validx].value);
+        }
+    }
+  for (argidx=0; argidx < argspecs_len; argidx++)
+    {
+      if (argspecs[argidx].arg_pos != - 1)
+        {
+          validx = argspecs[argidx].arg_pos - 1;
+          if (valuetable[validx].vt)
+            goto leave_einval; /* Already defined. */
+          valuetable[validx].vt = argspecs[argidx].vt;
+        }
+      if (argspecs[argidx].width == STAR_FIELD_VALUE)
+        {
+          validx = argspecs[argidx].width_pos - 1;
+          if (valuetable[validx].vt)
+            goto leave_einval; /* Already defined.  */
+          valuetable[validx].vt = VALTYPE_INT;
+        }
+      if (argspecs[argidx].precision == STAR_FIELD_VALUE)
+        {
+          validx = argspecs[argidx].precision_pos - 1;
+          if (valuetable[validx].vt)
+            goto leave_einval; /* Already defined.  */
+          valuetable[validx].vt = VALTYPE_INT;
+        }
+    }
+
+  /* Read all the arguments.  This will error out for unsupported
+     types and for not given positional arguments. */
+  rc = read_values (valuetable, max_pos, vaargs);
+  if (rc)
+    goto leave_einval;
+
+/*   for (validx=0; validx < max_pos; validx++) */
+/*     fprintf (stderr, "%2d: vt=%d\n", validx, valuetable[validx].vt); */
+
+  /* Everything has been collected, go ahead with the formatting.  */
+  rc = do_format (outfnc, outfncarg, sf, sfvalue, format,
+                  argspecs, argspecs_len, valuetable, myerrno, &nbytes);
+
+  goto leave;
+
+ leave_einval:
+  _set_errno (EINVAL);
+ leave_error:
+  rc = -1;
+ leave:
+  if (valuetable != valuetable_buffer)
+    free (valuetable);
+  if (argspecs != argspecs_buffer)
+    free (argspecs);
+  return rc;
+}
+
+
+
+
+/* A simple output handler utilizing stdio.  */
+static int
+plain_stdio_out (void *outfncarg, const char *buf, size_t buflen)
+{
+  FILE *fp = (FILE*)outfncarg;
+
+  if ( fwrite (buf, buflen, 1, fp) != 1 )
+    return -1;
+  return 0;
+}
+
+
+/* A replacement for printf.  */
+int
+_gpgrt_estream_printf (const char *format, ...)
+{
+  int rc;
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  rc = _gpgrt_estream_format (plain_stdio_out, stderr, NULL, NULL,
+                              format, arg_ptr);
+  va_end (arg_ptr);
+
+  return rc;
+}
+
+/* A replacement for fprintf.  */
+int
+_gpgrt_estream_fprintf (FILE *fp, const char *format, ...)
+{
+  int rc;
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  rc = _gpgrt_estream_format (plain_stdio_out, fp, NULL, NULL,
+                              format, arg_ptr);
+  va_end (arg_ptr);
+
+  return rc;
+}
+
+/* A replacement for vfprintf.  */
+int
+_gpgrt_estream_vfprintf (FILE *fp, const char *format, va_list arg_ptr)
+{
+  return _gpgrt_estream_format (plain_stdio_out, fp, NULL, NULL,
+                                format, arg_ptr);
+}
+
+
+
+/* Communication object used between estream_snprintf and
+   fixed_buffer_out.  */
+struct fixed_buffer_parm_s
+{
+  size_t size;    /* Size of the buffer.  */
+  size_t count;   /* Number of bytes requested for output.  */
+  size_t used;    /* Used size of the buffer.  */
+  char *buffer;   /* Provided buffer.  */
+};
+
+/* A simple malloced buffer output handler.  */
+static int
+fixed_buffer_out (void *outfncarg, const char *buf, size_t buflen)
+{
+  struct fixed_buffer_parm_s *parm = outfncarg;
+
+  parm->count += buflen;
+
+  if (!parm->buffer)
+    ;
+  else if (parm->used + buflen < parm->size)
+    {
+      /* Handle the common case that everything fits into the buffer
+         separately.  */
+      memcpy (parm->buffer + parm->used, buf, buflen);
+      parm->used += buflen;
+    }
+  else
+    {
+      /* The slow version of above.  */
+      for ( ;buflen && parm->used < parm->size; buflen--)
+        parm->buffer[parm->used++] = *buf++;
+    }
+
+  return 0;
+}
+
+
+/* A replacement for vsnprintf. */
+int
+_gpgrt_estream_vsnprintf (char *buf, size_t bufsize,
+                   const char *format, va_list arg_ptr)
+{
+  struct fixed_buffer_parm_s parm;
+  int rc;
+
+  parm.size = bufsize;
+  parm.count = 0;
+  parm.used = 0;
+  parm.buffer = bufsize?buf:NULL;
+  rc = _gpgrt_estream_format (fixed_buffer_out, &parm, NULL, NULL,
+                              format, arg_ptr);
+  if (!rc)
+    rc = fixed_buffer_out (&parm, "", 1); /* Print terminating Nul.  */
+  if (rc == -1)
+    return -1;
+  if (bufsize && buf && parm.size && parm.count >= parm.size)
+    buf[parm.size-1] = 0;
+
+  parm.count--; /* Do not count the trailing nul.  */
+  return (int)parm.count; /* Return number of bytes which would have
+                             been written.  */
+}
+
+/* A replacement for snprintf.  */
+int
+_gpgrt_estream_snprintf (char *buf, size_t bufsize, const char *format, ...)
+{
+  int rc;
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  rc = _gpgrt_estream_vsnprintf (buf, bufsize, format, arg_ptr);
+  va_end (arg_ptr);
+
+  return rc;
+}
+
+
+
+/* Communication object used between estream_asprintf and
+   dynamic_buffer_out.  */
+struct dynamic_buffer_parm_s
+{
+  int error_flag; /* Internal helper.  */
+  size_t alloced; /* Allocated size of the buffer.  */
+  size_t used;    /* Used size of the buffer.  */
+  char *buffer;   /* Malloced buffer.  */
+};
+
+/* A simple malloced buffer output handler.  */
+static int
+dynamic_buffer_out (void *outfncarg, const char *buf, size_t buflen)
+{
+  struct dynamic_buffer_parm_s *parm = outfncarg;
+
+  if (parm->error_flag)
+    {
+      /* Just in case some formatting routine did not checked for an
+         error. */
+      _set_errno (parm->error_flag);
+      return -1;
+    }
+
+  if (parm->used + buflen >= parm->alloced)
+    {
+      char *p;
+
+      parm->alloced += buflen + 512;
+      p = my_printf_realloc (parm->buffer, parm->alloced);
+      if (!p)
+        {
+          parm->error_flag = errno ? errno : ENOMEM;
+          /* Wipe out what we already accumulated.  This is useful in
+             case sensitive data is formatted.  */
+          memset (parm->buffer, 0, parm->used);
+          return -1;
+        }
+      parm->buffer = p;
+    }
+  memcpy (parm->buffer + parm->used, buf, buflen);
+  parm->used += buflen;
+
+  return 0;
+}
+
+
+/* A replacement for vasprintf.  As with the BSD version of vasprintf
+   -1 will be returned on error and NULL stored at BUFP.  On success
+   the number of bytes printed will be returned. */
+int
+_gpgrt_estream_vasprintf (char **bufp, const char *format, va_list arg_ptr)
+{
+  struct dynamic_buffer_parm_s parm;
+  int rc;
+
+  parm.error_flag = 0;
+  parm.alloced = 512;
+  parm.used = 0;
+  parm.buffer = my_printf_realloc (NULL, parm.alloced);
+  if (!parm.buffer)
+    {
+      *bufp = NULL;
+      return -1;
+    }
+
+  rc = _gpgrt_estream_format (dynamic_buffer_out, &parm, NULL, NULL,
+                              format, arg_ptr);
+  if (!rc)
+    rc = dynamic_buffer_out (&parm, "", 1); /* Print terminating Nul.  */
+  /* Fixme: Should we shrink the resulting buffer?  */
+  if (rc != -1 && parm.error_flag)
+    {
+      rc = -1;
+      _set_errno (parm.error_flag);
+    }
+  if (rc == -1)
+    {
+      memset (parm.buffer, 0, parm.used);
+      if (parm.buffer)
+        my_printf_realloc (parm.buffer, 0);
+      *bufp = NULL;
+      return -1;
+    }
+  gpgrt_assert (parm.used);   /* We have at least the terminating Nul.  */
+  *bufp = parm.buffer;
+  return parm.used - 1; /* Do not include that Nul. */
+}
+
+/* A replacement for asprintf.  As with the BSD of asprintf version -1
+   will be returned on error and NULL stored at BUFP.  On success the
+   number of bytes printed will be returned. */
+int
+_gpgrt_estream_asprintf (char **bufp, const char *format, ...)
+{
+  int rc;
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  rc = _gpgrt_estream_vasprintf (bufp, format, arg_ptr);
+  va_end (arg_ptr);
+
+  return rc;
+}
+
+/* A variant of asprintf.  The function returns the allocated buffer
+   or NULL on error; ERRNO is set in the error case.  The caller
+   should use es_free to release the buffer.  This function actually
+   belongs into estream-printf but we put it here as a convenience
+   and because es_free is required anyway.  */
+char *
+_gpgrt_estream_bsprintf (const char *format, ...)
+{
+  int rc;
+  va_list ap;
+  char *buf;
+
+  va_start (ap, format);
+  rc = _gpgrt_estream_vasprintf (&buf, format, ap);
+  va_end (ap);
+  if (rc < 0)
+    return NULL;
+  return buf;
+}