Blame src/estream-printf.c

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