summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2007-05-15 18:10:48 +0200
committerWerner Koch <wk@gnupg.org>2007-05-15 18:10:48 +0200
commit5f3bca96826fbaf9c4469a7eedef9294f4d74bfb (patch)
treedae425970a8c0dd0f77ab62c91b2700dfbaa811e /common
parentPreparing 2.0.4 (diff)
downloadgnupg2-5f3bca96826fbaf9c4469a7eedef9294f4d74bfb.tar.xz
gnupg2-5f3bca96826fbaf9c4469a7eedef9294f4d74bfb.zip
Use estream_asprintf instead of the GNU asprintf.
Diffstat (limited to 'common')
-rw-r--r--common/ChangeLog17
-rw-r--r--common/Makefile.am2
-rw-r--r--common/estream-printf.c2139
-rw-r--r--common/estream-printf.h60
-rw-r--r--common/estream.c143
-rw-r--r--common/estream.h4
-rw-r--r--common/sexp-parse.h32
-rw-r--r--common/sexputil.c12
-rw-r--r--common/util.h20
-rw-r--r--common/vasprintf.c169
-rw-r--r--common/xasprintf.c23
11 files changed, 2301 insertions, 320 deletions
diff --git a/common/ChangeLog b/common/ChangeLog
index 797f11962..5c7697327 100644
--- a/common/ChangeLog
+++ b/common/ChangeLog
@@ -1,3 +1,20 @@
+2007-05-15 Werner Koch <wk@g10code.com>
+
+ * util.h: Do not include gnulib's vasprintf. Redefine asprintf
+ and vasprintf.
+
+ * xasprintf.c (xasprintf, xtryasprintf): Use estream_vasprintf.
+
+ * estream-printf.h, estream-printf.c: New. Taken from current
+ libestream SVN.
+ * Makefile.am (common_sources): Add them.
+
+2007-05-14 Werner Koch <wk@g10code.com>
+
+ * sexp-parse.h (smklen): New.
+ * sexputil.c: Include sexp-parse.h.
+ (make_simple_sexp_from_hexstr): Replace sprintf by smklen.
+
2007-05-07 Werner Koch <wk@g10code.com>
* signal.c (got_fatal_signal): Protect SIG from being clobbered by
diff --git a/common/Makefile.am b/common/Makefile.am
index a0b27a12c..237c8996e 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -54,7 +54,7 @@ common_sources = \
w32reg.c \
signal.c \
dynload.h \
- estream.c estream.h \
+ estream.c estream.h estream-printf.c estream-printf.h \
srv.c srv.h \
dns-cert.c dns-cert.h \
pka.c pka.h \
diff --git a/common/estream-printf.c b/common/estream-printf.c
new file mode 100644
index 000000000..dcbb8073d
--- /dev/null
+++ b/common/estream-printf.c
@@ -0,0 +1,2139 @@
+/* estream-printf.c - Versatile C-99 compliant printf formatting
+ * Copyright (C) 2007 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 General Public License as published
+ * by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Libestream; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * $Id: estream-printf.c 54 2007-05-15 14:12:06Z wk $
+ */
+
+/* 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_THOUSANDS_SEP
+
+ Note that the file estream.m4 provides the autoconf macro
+ ESTREAM_PRINTF_INIT which runs all required checks.
+
+
+ Missing stuff: wchar and wint_t
+ thousands_sep in pr_float.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stddef.h>
+#include <assert.h>
+#if defined(HAVE_INTMAX_T) || defined(HAVE_UINTMAX_T)
+# include <stdint.h>
+#endif
+#ifdef HAVE_LANGINFO_THOUSANDS_SEP
+#include <langinfo.h>
+#endif
+#ifdef TEST
+# include <locale.h>
+#endif
+#ifdef ESTREAM_PRINTF_EXTRA_INCLUDE
+#include ESTREAM_PRINTF_EXTRA_INCLUDE
+#endif
+#include "estream-printf.h"
+
+/* Allow redefinition of asprintf used malloc functions. */
+#ifdef ESTREAM_ASPRINTF_MALLOC
+#define my_asprintf_malloc(a) ESTREAM_ASPRINTF_MALLOC((a))
+#else
+#define my_asprintf_malloc(a) malloc((a))
+#endif
+#ifdef ESTREAM_ASPRINTF_FREE
+#define my_asprintf_free(a) ESTREAM_ASPRINTF_FREE((a))
+#else
+#define my_asprintf_free(a) free((a))
+#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;
+
+
+#ifdef TEST
+static int verbose;
+
+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 /*TEST*/
+
+
+/* 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 addrrss 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:
+ errno = EINVAL;
+ leave:
+ if (argspecs != *argspecs_addr)
+ free (argspecs);
+ *argspecs_addr = NULL;
+ return -1;
+}
+
+/* This function is used for testing to provide default arguments. */
+static int
+read_dummy_value (value_t *value, valtype_t vt)
+{
+ switch (vt)
+ {
+ case VALTYPE_CHAR: value->a_char = 'a'; break;
+ case VALTYPE_CHAR_PTR: value->a_char_ptr = NULL; break;
+ case VALTYPE_SCHAR: value->a_schar = 'a'; break;
+ case VALTYPE_SCHAR_PTR: value->a_schar_ptr = NULL; break;
+ case VALTYPE_UCHAR: value->a_uchar = 'a'; break;
+ case VALTYPE_SHORT: value->a_short = -4711; break;
+ case VALTYPE_USHORT: value->a_ushort = 4711; break;
+ case VALTYPE_SHORT_PTR: value->a_short_ptr = NULL; break;
+ case VALTYPE_INT: value->a_int = 42; break;
+ case VALTYPE_INT_PTR: value->a_int_ptr = NULL; break;
+ case VALTYPE_UINT: value->a_uint = 65535; break;
+ case VALTYPE_LONG: value->a_long = 11071961; break;
+ case VALTYPE_ULONG: value->a_ulong = 19610711; break;
+ case VALTYPE_LONG_PTR: value->a_long_ptr = NULL; break;
+#ifdef HAVE_LONG_LONG_INT
+ case VALTYPE_LONGLONG: value->a_longlong = 11223344; break;
+ case VALTYPE_ULONGLONG: value->a_ulonglong = 2233445566u; break;
+ case VALTYPE_LONGLONG_PTR: value->a_longlong_ptr = NULL; break;
+#endif
+ case VALTYPE_DOUBLE: value->a_double = 3.1415926535; break;
+#ifdef HAVE_LONG_DOUBLE
+ case VALTYPE_LONGDOUBLE: value->a_longdouble = 2.7; break;
+#endif
+ case VALTYPE_STRING: value->a_string = "heart of gold"; break;
+ case VALTYPE_POINTER: value->a_void_ptr = (void*)0xdeadbeef; break;
+#ifdef HAVE_INTMAX_T
+ case VALTYPE_INTMAX: value->a_intmax = 100; break;
+ case VALTYPE_INTMAX_PTR: value->a_intmax_ptr = NULL; break;
+#endif
+#ifdef HAVE_UINTMAX_T
+ case VALTYPE_UINTMAX: value->a_uintmax = 200; break;
+#endif
+ case VALTYPE_SIZE: value->a_size = 65537; break;
+ case VALTYPE_SIZE_PTR: value->a_size_ptr = NULL; break;
+#ifdef HAVE_PTRDIFF_T
+ case VALTYPE_PTRDIFF: value->a_ptrdiff = 2; break;
+ case VALTYPE_PTRDIFF_PTR: value->a_ptrdiff_ptr = NULL; break;
+#endif
+ default: /* Unsupported type. */
+ return -1;
+ }
+ return 0;
+}
+
+
+/* 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;
+
+ if (!vaargs)
+ {
+ if (read_dummy_value (value, vt))
+ return -1;
+ }
+ else
+ {
+ 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_THOUSANDS_SEP
+ nl_langinfo(THOUSANDS_SEP);
+#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[200];
+ 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)
+{
+ int rc;
+ size_t n;
+ const char *string, *s;
+
+ if (arg->vt != VALTYPE_STRING)
+ return -1;
+ string = value.a_string;
+ if (!string)
+ string = "(null)";
+ if (arg->precision >= 0)
+ {
+ for (n=0,s=string; *s && n < arg->precision; 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)
+ return rc;
+ }
+
+ rc = outfnc (outfncarg, string, n);
+ if (rc)
+ return rc;
+ *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)
+ return rc;
+ }
+
+ return 0;
+}
+
+
+/* "p" formatting. */
+static int
+pr_pointer (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;
+
+ 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)
+{
+ 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. VALUETABLE
+ holds the values and may be directly addressed using the poistion
+ 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,
+ 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;
+
+ 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;
+
+ assert (argidx < argspecs_len);
+ argidx++;
+
+ /* Apply indirect field width and precision values. */
+ if (arg->width == STAR_FIELD_VALUE)
+ {
+ 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)
+ {
+ 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
+ {
+ assert (arg->vt == valuetable[arg->arg_pos-1].vt);
+ value = valuetable[arg->arg_pos-1].value;
+ }
+
+ switch (arg->conspec)
+ {
+ case CONSPEC_UNKNOWN: 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:
+ case CONSPEC_STRERROR:
+ rc = pr_string (outfnc, outfncarg, arg, value, nbytes);
+ 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. */
+ s++; /* Need to output a terminating nul; take it from format. */
+ rc = outfnc (outfncarg, format, (n=s - format));
+ 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
+estream_format (estream_printf_out_t outfnc,
+ void *outfncarg,
+ 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 TEST
+ if (verbose > 1)
+ 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;
+ }
+ 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, format,
+ argspecs, argspecs_len, valuetable, myerrno, &nbytes);
+
+ goto leave;
+
+ leave_einval:
+ 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;
+
+ fputs ("OUT->", fp);
+ if ( fwrite (buf, buflen, 1, fp) != 1 )
+ return -1;
+ fputs ("<-\n", fp);
+ return 0;
+}
+
+
+/* A replacement for printf. */
+int
+estream_printf (const char *format, ...)
+{
+ int rc;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format);
+ rc = estream_format (plain_stdio_out, stderr, format, arg_ptr);
+ va_end (arg_ptr);
+
+ return rc;
+}
+
+/* A replacement for fprintf. */
+int
+estream_fprintf (FILE *fp, const char *format, ...)
+{
+ int rc;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format);
+ rc = estream_format (plain_stdio_out, fp, format, arg_ptr);
+ va_end (arg_ptr);
+
+ return rc;
+}
+
+/* A replacement for vfprintf. */
+int
+estream_vfprintf (FILE *fp, const char *format, va_list arg_ptr)
+{
+ return estream_format (plain_stdio_out, fp, 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
+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? bufsize-1:0;
+ parm.count = 0;
+ parm.used = 0;
+ parm.buffer = bufsize?buf:NULL;
+ rc = estream_format (fixed_buffer_out, &parm, format, arg_ptr);
+ if (rc == -1)
+ return -1;
+ if (bufsize && buf && parm.count >= parm.size)
+ buf[parm.size-1] = 0;
+
+ return (int)parm.count; /* Return number of bytes which would have
+ been written. */
+}
+
+/* A replacement for snprintf. */
+int
+estream_snprintf (char *buf, size_t bufsize, const char *format, ...)
+{
+ int rc;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format);
+ rc = 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. */
+ errno = parm->error_flag;
+ return -1;
+ }
+
+ if (parm->used + buflen >= parm->alloced)
+ {
+ char *p;
+
+ parm->alloced += buflen + 512;
+ p = 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 formated. */
+ 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 of vasprintf version -1
+ will be returned on error and NULL stored at BUFP. On success the
+ number of bytes printed will be returned. */
+int
+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_asprintf_malloc (parm.alloced);
+ if (!parm.buffer)
+ {
+ *bufp = NULL;
+ return -1;
+ }
+
+ rc = estream_format (dynamic_buffer_out, &parm, format, arg_ptr);
+
+ if (rc != -1 && parm.error_flag)
+ {
+ rc = -1;
+ errno = parm.error_flag;
+ }
+ if (rc == -1)
+ {
+ memset (parm.buffer, 0, parm.used);
+ my_asprintf_free (parm.buffer);
+ *bufp = NULL;
+ return -1;
+ }
+
+ *bufp = parm.buffer;
+ return parm.used - 1; /* Do not include the 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
+estream_asprintf (char **bufp, const char *format, ...)
+{
+ int rc;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format);
+ rc = estream_vasprintf (bufp, format, arg_ptr);
+ va_end (arg_ptr);
+
+ return rc;
+}
+
+
+#ifdef TEST
+
+static int
+one_test (const char *format, ...)
+{
+ int rc1, rc2;
+ va_list arg_ptr;
+ char *buf1, *buf2;
+
+ if (verbose)
+ printf ("format: ->%s<-\n", format);
+
+ va_start (arg_ptr, format);
+ rc1 = vasprintf (&buf1, format, arg_ptr);
+ va_end (arg_ptr);
+ if (rc1 == -1)
+ {
+ printf (" sys: errno=%d (%s)\n", errno, strerror (errno));
+ buf1 = NULL;
+ }
+ else if (verbose)
+ printf (" sys: ->%s<-\n", buf1);
+
+ va_start (arg_ptr, format);
+ rc2 = estream_vasprintf (&buf2, format, arg_ptr);
+ va_end (arg_ptr);
+ if (rc2 == -1)
+ printf (" our: errno=%d (%s)\n", errno, strerror (errno));
+ else if (verbose)
+ printf (" our: ->%s<-\n", buf2);
+
+ if (rc1 != -1 && rc2 != -1 && strcmp (buf1, buf2))
+ printf ("error: output does not match\n"
+ "format: ->%s<-\n sys: ->%s<-\n our: ->%s<-\n",
+ format, buf1, buf2);
+ else if ( rc1 != rc2 )
+ printf ("error: return codes are different: sys_rc=%d our_rc=%d\n",
+ rc1, rc2);
+
+ free (buf2);
+ free (buf1);
+
+ return 0;
+}
+
+
+static void
+run_tests (void)
+{
+#if 0
+ one_test ("%d %% %'d", 17, 19681977);
+
+ one_test ("%d %% %d", 17, 768114563);
+ one_test ("%d %% %d", 17, -768114563);
+
+ one_test ("%d", 17);
+ one_test ("%4d", 17);
+ one_test ("%40d", 17);
+ one_test ("%-d", 17);
+ one_test ("%-4d", 17);
+ one_test ("%-140d", 17);
+ one_test ("%d", -17);
+ one_test ("%4d", -17);
+ one_test ("%40d", -17);
+ one_test ("%-d", -17);
+ one_test ("%-4d", -17);
+ one_test ("%-40d", -17);
+
+ one_test ("%+4d", 17);
+ one_test ("%+4d", -17);
+ one_test ("%-+4d", 17);
+ one_test ("%-+4d", -17);
+ one_test ("% 4d", 17);
+ one_test ("% 4d", -17);
+ one_test ("%- +4d", 17);
+ one_test ("%- +4d", -17);
+
+ one_test ("%.4d", 17);
+ one_test ("%.0d", 17);
+ one_test ("%.0d", 0);
+ one_test ("%.4d", -17);
+ one_test ("%.0d", -17);
+ one_test ("%6.4d", 17);
+ one_test ("%6.4d", -17);
+ one_test ("%6.0d", 0);
+ one_test ("%4.6d", 17);
+ one_test ("%4.6d", -17);
+
+ one_test ("% 4.6d", 17);
+ one_test ("% 6.0d", 0);
+
+ one_test ("%.4d", 17);
+ one_test ("%04d", 17);
+ one_test ("%.4d", -17);
+ one_test ("%04d", -17);
+ one_test ("%0.d", 0);
+
+ one_test ("%*d", 7, 42);
+ one_test ("%*d", -7, 42);
+ one_test ("%.*d", 7, 42);
+ one_test ("%.*d", -7, 42);
+ one_test ("%*.*d", 10, 7, 42);
+ one_test ("%*.*d", 10, -7, 42);
+ one_test ("%*.*d", -10, 7, 42);
+ one_test ("%*.*d", -10, -7, 42);
+
+ one_test ("%*x", 7, 42);
+ one_test ("%*x", -7, 42);
+ one_test ("%.*x", 7, 42);
+ one_test ("%.*x", -7, 42);
+ one_test ("%*.*x", 10, 7, 42);
+ one_test ("%*.*x", 10, -7, 42);
+ one_test ("%*.*x", -10, 7, 42);
+ one_test ("%*.*x", -10, -7, 42);
+ one_test ("%#*x", 7, 42);
+ one_test ("%#*x", -7, 42);
+ one_test ("%#.*x", 7, 42);
+ one_test ("%#.*x", -7, 42);
+ one_test ("%#*.*x", 10, 7, 42);
+ one_test ("%#*.*x", 10, -7, 42);
+ one_test ("%#*.*x", -10, 7, 42);
+ one_test ("%#*.*x", -10, -7, 42);
+
+ one_test ("%*X", 7, 42);
+ one_test ("%*X", -7, 42);
+ one_test ("%.*X", 7, 42);
+ one_test ("%.*X", -7, 42);
+ one_test ("%*.*X", 10, 7, 42);
+ one_test ("%*.*X", 10, -7, 42);
+ one_test ("%*.*X", -10, 7, 42);
+ one_test ("%*.*X", -10, -7, 42);
+ one_test ("%#*X", 7, 42);
+ one_test ("%#*X", -7, 42);
+ one_test ("%#.*X", 7, 42);
+ one_test ("%#.*X", -7, 42);
+ one_test ("%#*.*X", 10, 7, 42);
+ one_test ("%#*.*X", 10, -7, 42);
+ one_test ("%#*.*X", -10, 7, 42);
+ one_test ("%#*.*X", -10, -7, 42);
+
+ one_test ("%*o", 7, 42);
+ one_test ("%*o", -7, 42);
+ one_test ("%.*o", 7, 42);
+ one_test ("%.*o", -7, 42);
+ one_test ("%*.*o", 10, 7, 42);
+ one_test ("%*.*o", 10, -7, 42);
+ one_test ("%*.*o", -10, 7, 42);
+ one_test ("%*.*o", -10, -7, 42);
+ one_test ("%#*o", 7, 42);
+ one_test ("%#*o", -7, 42);
+ one_test ("%#.*o", 7, 42);
+ one_test ("%#.*o", -7, 42);
+ one_test ("%#*.*o", 10, 7, 42);
+ one_test ("%#*.*o", 10, -7, 42);
+ one_test ("%#*.*o", -10, 7, 42);
+ one_test ("%#*.*o", -10, -7, 42);
+
+ one_test ("%s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%.0s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%.10s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%.48s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%.49s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%.50s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%.51s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%48s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%49s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%50s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%51s", "the quick brown fox jumps over the lazy dogs back");
+ one_test ("%-51s", "the quick brown fox jumps over the lazy dogs back");
+#endif
+
+ one_test ("%f", 3.1415926535);
+ one_test ("%f", -3.1415926535);
+ one_test ("%.10f", 3.1415926535);
+ one_test ("%.2f", 3.1415926535);
+ one_test ("%.1f", 3.1415926535);
+ one_test ("%.0f", 3.1415926535);
+ one_test ("%.20f", 3.1415926535);
+ one_test ("%10.10f", 3.1415926535);
+ one_test ("%10.2f", 3.1415926535);
+ one_test ("%10.1f", 3.1415926535);
+ one_test ("%10.0f", 3.1415926535);
+ one_test ("%30.20f", 3.1415926535);
+ one_test ("%10.10f", -3.1415926535);
+ one_test ("%10.2f", -3.1415926535);
+ one_test ("%10.1f", -3.1415926535);
+ one_test ("%10.0f", -3.1415926535);
+ one_test ("%30.20f", -3.1415926535);
+
+ one_test ("%-10f", 3.1415926535);
+ one_test ("%-10.10f", 3.1415926535);
+ one_test ("%-10.2f", 3.1415926535);
+ one_test ("%-10.1f", 3.1415926535);
+ one_test ("%-10.0f", 3.1415926535);
+ one_test ("%-30.20f", 3.1415926535);
+ one_test ("%-10f", -3.1415926535);
+ one_test ("%-10.10f", -3.1415926535);
+ one_test ("%-10.2f", -3.1415926535);
+ one_test ("%-10.1f", -3.1415926535);
+ one_test ("%-10.0f", -3.1415926535);
+ one_test ("%-30.20f", -3.1415926535);
+
+ one_test ("%#.0f", 3.1415926535);
+ one_test ("%#10.0f", 3.1415926535);
+ one_test ("%#10.0f", -3.1415926535);
+ one_test ("%-#10.0f", 3.1415926535);
+ one_test ("%-#10.0f", -3.1415926535);
+
+ one_test ("%e", 3.1415926535);
+ one_test ("%g", 3.1415926535);
+
+ one_test ("%a", 1);
+ one_test ("%a", -1);
+ one_test ("%a", 3.1415926535);
+
+#ifdef HAVE_LONG_DOUBLE
+ one_test ("%La", 1);
+ one_test ("%La", -1);
+ one_test ("%La", 3.1415926535);
+#endif
+
+#ifdef __GLIBC__
+ /* "%m" is a glibc extension so this _test_ will only work on such a
+ system. */
+ errno = ENOENT;
+ one_test ("%m");
+ errno = ENOENT;
+ one_test ("%d=%m", 17);
+ errno = ENOENT;
+ one_test ("%2$d:%m:%1$d", 42, 17);
+#endif /*__GLIBC__*/
+
+}
+
+static void
+check_snprintf (void)
+{
+ char buffer[20];
+ int rc;
+
+ rc = estream_snprintf (buffer, 0, "%*s", 18, "");
+ if (rc != 19)
+ printf ("rc=%d\n", rc );
+ rc = estream_snprintf (buffer, sizeof buffer, "%*s", 18, "");
+ if (rc != 19)
+ printf ("rc=%d, strlen(buffer)=%d\n", rc, (int)strlen (buffer));
+ rc = estream_snprintf (buffer, sizeof buffer, "%*s", 19, "");
+ if (rc != 20)
+ printf ("rc=%d, strlen(buffer)=%d\n", rc, (int)strlen (buffer));
+ rc = estream_snprintf (buffer, sizeof buffer, "%*s", 20, "");
+ if (rc != 21)
+ printf ("rc=%d, strlen(buffer)=%d\n", rc, (int)strlen (buffer));
+ rc = estream_snprintf (buffer, sizeof buffer, "%*s", 21, "");
+ if (rc != 22)
+ printf ("rc=%d, strlen(buffer)=%d\n", rc, (int)strlen (buffer));
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+ int rc;
+
+ if (argc) {argc--; argv++; }
+
+ setlocale (LC_NUMERIC, "");
+
+ while (argc && !strcmp (*argv, "--verbose"))
+ {
+ verbose++;
+ argc--;
+ argv++;
+ }
+
+ if (!argc)
+ {
+ run_tests ();
+ check_snprintf () ;
+ }
+ else
+ {
+ rc = estream_vfprintf (stdout, argv[0], NULL);
+ fflush (stdout);
+ fprintf (stderr, "[estream_vfprintf returns: %d]\n", rc);
+ }
+
+ return 0;
+}
+#endif /*TEST*/
+/*
+Local Variables:
+compile-command: "cc -Wall -O3 -g -I.. -DHAVE_CONFIG_H -DTEST -o estream-printf estream-printf.c"
+End:
+*/
diff --git a/common/estream-printf.h b/common/estream-printf.h
new file mode 100644
index 000000000..bbf132b26
--- /dev/null
+++ b/common/estream-printf.h
@@ -0,0 +1,60 @@
+/* estream-printf.h - Versatile C-99 compliant printf formatting.
+ * Copyright (C) 2007 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 General Public License as published
+ * by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Libestream; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * $Id: estream-printf.h 54 2007-05-15 14:12:06Z wk $
+ */
+
+#ifndef ESTREAM_PRINTF_H
+#define ESTREAM_PRINTF_H
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
+# define ESTREAM_GCC_A_PRINTF( f, a ) __attribute__ ((format (printf,f,a)))
+#else
+# define ESTREAM_GCC_A_PRINTF( f, a )
+#endif
+
+
+typedef int (*estream_printf_out_t)
+ (void *outfncarg, const char *buf, size_t buflen);
+
+int estream_format (estream_printf_out_t outfnc, void *outfncarg,
+ const char *format, va_list vaargs)
+ ESTREAM_GCC_A_PRINTF(3,0);
+int estream_printf (const char *format, ...)
+ ESTREAM_GCC_A_PRINTF(1,2);
+int estream_fprintf (FILE *fp, const char *format, ... )
+ ESTREAM_GCC_A_PRINTF(2,3);
+int estream_vfprintf (FILE *fp, const char *format, va_list arg_ptr)
+ ESTREAM_GCC_A_PRINTF(2,0);
+int estream_snprintf (char *buf, size_t bufsize, const char *format, ...)
+ ESTREAM_GCC_A_PRINTF(3,4);
+int estream_vsnprintf (char *buf,size_t bufsize,
+ const char *format, va_list arg_ptr)
+ ESTREAM_GCC_A_PRINTF(3,0);
+int estream_asprintf (char **bufp, const char *format, ...)
+ ESTREAM_GCC_A_PRINTF(2,3);
+int estream_vasprintf (char **bufp, const char *format, va_list arg_ptr)
+ ESTREAM_GCC_A_PRINTF(2,0);
+
+
+#endif /*ESTREAM_PRINTF_H*/
diff --git a/common/estream.c b/common/estream.c
index 31e91d5ee..65c566489 100644
--- a/common/estream.c
+++ b/common/estream.c
@@ -1,5 +1,5 @@
/* estream.c - Extended Stream I/O Library
- * Copyright (C) 2004, 2006, 2007 g10 Code GmbH
+ * Copyright (C) 2004, 2005, 2006, 2007 g10 Code GmbH
*
* This file is part of Libestream.
*
@@ -11,7 +11,7 @@
* 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.
+ * General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libestream; if not, write to the Free Software
@@ -49,6 +49,7 @@
# include <pth.h>
#endif
+/* This is for the special hack to use estream.c in GnuPG. */
#ifdef GNUPG_MAJOR_VERSION
#include "../common/util.h"
#endif
@@ -62,7 +63,7 @@ void *memrchr (const void *block, int c, size_t size);
#endif
#include <estream.h>
-
+#include <estream-printf.h>
@@ -747,8 +748,8 @@ static int
es_convert_mode (const char *mode, unsigned int *flags)
{
- /* FIXME: We need to allow all combinations for mode flags and for
- binary we need to do a
+ /* FIXME: We need to allow all mode flags permutations and for
+ binary mode we need to do a
#ifdef HAVE_DOSISH_SYSTEM
setmode (fd, O_BINARY);
@@ -1671,29 +1672,19 @@ doreadline (estream_t ES__RESTRICT stream, size_t max_length,
}
-/* Helper for esprint. */
-#if defined(HAVE_FOPENCOOKIE) || defined(HAVE_FUNOPEN)
-static my_funopen_hook_ret_t
-print_fun_writer (void *cookie_arg, const char *buffer, size_t size)
+/* Output fucntion used for estream_format. */
+static int
+print_writer (void *outfncarg, const char *buf, size_t buflen)
{
- estream_t stream = cookie_arg;
+ estream_t stream = outfncarg;
size_t nwritten;
-
- /* We don't return an error but let es_print check whether an error
- has occured. Internally we skip everything after an error. */
- if (!stream->intern->print_err)
- {
- if (es_writen (stream, buffer, size, &nwritten))
- {
- stream->intern->print_err = 1;
- stream->intern->print_errno = errno;
- }
- else
- stream->intern->print_ntotal += nwritten;
- }
- return 0;
+ int rc;
+
+ nwritten = 0;
+ rc = es_writen (stream, buf, buflen, &nwritten);
+ stream->intern->print_ntotal += nwritten;
+ return rc;
}
-#endif /* HAVE_FOPENCOOKIE || HAVE_FUNOPEN */
/* The core of our printf function. This is called in locked state. */
@@ -1701,98 +1692,13 @@ static int
es_print (estream_t ES__RESTRICT stream,
const char *ES__RESTRICT format, va_list ap)
{
-#if defined(HAVE_FOPENCOOKIE) || defined(HAVE_FUNOPEN)
-
- if (!stream->intern->print_fp)
- {
-#ifdef HAVE_FOPENCOOKIE
- {
- cookie_io_functions_t io = { NULL };
- io.write = print_fun_writer;
-
- stream->intern->print_fp = fopencookie (stream, "w", io);
- }
-#else /*!HAVE_FOPENCOOKIE*/
- stream->intern->print_fp = funopen (stream, NULL,
- print_fun_writer, NULL, NULL);
-#endif /*!HAVE_FOPENCOOKIE*/
- if (!stream->intern->print_fp)
- return -1;
- }
+ int rc;
- stream->intern->print_err = 0;
- stream->intern->print_errno = 0;
stream->intern->print_ntotal = 0;
-
- if ( vfprintf (stream->intern->print_fp, format, ap) < 0
- || fflush (stream->intern->print_fp) )
- {
- stream->intern->print_errno = errno;
- stream->intern->print_err = 1;
- fclose (stream->intern->print_fp);
- stream->intern->print_fp = NULL;
- }
- if (stream->intern->print_err)
- {
- errno = stream->intern->print_errno;
- return -1;
- }
-
+ rc = estream_format (print_writer, stream, format, ap);
+ if (rc)
+ return -1;
return (int)stream->intern->print_ntotal;
-
-#else /* No funopen or fopencookie. */
-
- char data[BUFFER_BLOCK_SIZE];
- size_t bytes_read;
- size_t bytes_written;
- FILE *tmp_stream;
- int err;
-
- bytes_written = 0;
- tmp_stream = NULL;
- err = 0;
-
- tmp_stream = tmpfile ();
- if (! tmp_stream)
- {
- err = errno;
- goto out;
- }
-
- err = vfprintf (tmp_stream, format, ap);
- if (err < 0)
- goto out;
-
- err = fseek (tmp_stream, 0, SEEK_SET);
- if (err)
- goto out;
-
- while (1)
- {
- bytes_read = fread (data, 1, sizeof (data), tmp_stream);
- if (ferror (tmp_stream))
- {
- err = -1;
- break;
- }
-
- err = es_writen (stream, data, bytes_read, NULL);
- if (err)
- break;
- else
- bytes_written += bytes_read;
- if (feof (tmp_stream))
- break;
- }
- if (err)
- goto out;
-
- out:
- if (tmp_stream)
- fclose (tmp_stream);
-
- return err ? -1 : bytes_written;
-#endif /* no funopen or fopencookie */
}
@@ -2718,7 +2624,7 @@ es_vfprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format,
static int
es_fprintf_unlocked (estream_t ES__RESTRICT stream,
- const char *ES__RESTRICT format, ...)
+ const char *ES__RESTRICT format, ...)
{
int ret;
@@ -2868,8 +2774,6 @@ es_opaque_get (estream_t stream)
return opaque;
}
-
-
/* Print a BUFFER to STREAM while replacing all control characters and
the characters in DELIMITERS by standard C escape sequences.
Returns 0 on success or -1 on error. If BYTES_WRITTEN is not NULL
@@ -2992,7 +2896,7 @@ es_write_hexstring (estream_t ES__RESTRICT stream,
encoding. The interface is the same as es_write_sanitized, however
only one delimiter may be supported.
- THIS IS NOT A STANDARD ESTREAM FUNCTION AND ONLY USED BY GNUPG. */
+ THIS IS NOT A STANDARD ESTREAM FUNCTION AND ONLY USED BY GNUPG!. */
int
es_write_sanitized_utf8_buffer (estream_t stream,
const void *buffer, size_t length,
@@ -3025,6 +2929,3 @@ es_write_sanitized_utf8_buffer (estream_t stream,
return es_write_sanitized (stream, p, length, delimiters, bytes_written);
}
#endif /*GNUPG_MAJOR_VERSION*/
-
-
-
diff --git a/common/estream.h b/common/estream.h
index aede40868..0d104723d 100644
--- a/common/estream.h
+++ b/common/estream.h
@@ -1,5 +1,5 @@
/* estream.h - Extended stream I/O/ Library
- * Copyright (C) 2004 g10 Code GmbH
+ * Copyright (C) 2004, 2005, 2006, 2007 g10 Code GmbH
*
* This file is part of Libestream.
*
@@ -211,7 +211,6 @@ void es_opaque_set (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque);
void *es_opaque_get (estream_t stream);
-
#ifdef GNUPG_MAJOR_VERSION
int es_write_sanitized_utf8_buffer (estream_t stream,
const void *buffer, size_t length,
@@ -220,5 +219,6 @@ int es_write_sanitized_utf8_buffer (estream_t stream,
#endif /*GNUPG_MAJOR_VERSION*/
+
#endif /*ESTREAM_H*/
diff --git a/common/sexp-parse.h b/common/sexp-parse.h
index 1064e51c4..bb96e446f 100644
--- a/common/sexp-parse.h
+++ b/common/sexp-parse.h
@@ -1,5 +1,5 @@
/* sexp-parse.h - S-Exp helper functions
- * Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2003, 2007 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -97,4 +97,34 @@ smatch (unsigned char const **buf, size_t buflen, const char *token)
return 1;
}
+/* Format VALUE for use as the length indicatior of an S-expression.
+ The caller needs to provide a buffer HELP_BUFFER wth a length of
+ HELP_BUFLEN. The return value is a pointer into HELP_BUFFER with
+ the formatted length string. The colon and a trailing nul are
+ appended. HELP_BUFLEN must be at least 3 - a more useful value is
+ 15. If LENGTH is not NULL, the LENGTH of the resulting string
+ (excluding the terminating nul) is stored at that address. */
+static inline char *
+smklen (char *help_buffer, size_t help_buflen, size_t value, size_t *length)
+{
+ char *p = help_buffer + help_buflen;
+
+ if (help_buflen >= 3)
+ {
+ *--p = 0;
+ *--p = ':';
+ do
+ {
+ *--p = '0' + (value % 10);
+ value /= 10;
+ }
+ while (value && p > help_buffer);
+ }
+
+ if (length)
+ *length = (help_buffer + help_buflen) - p;
+ return p;
+}
+
+
#endif /*SEXP_PARSE_H*/
diff --git a/common/sexputil.c b/common/sexputil.c
index da6124d91..7bac990b3 100644
--- a/common/sexputil.c
+++ b/common/sexputil.c
@@ -34,7 +34,7 @@
#endif
#include "util.h"
-
+#include "sexp-parse.h"
/* Return the so called "keygrip" which is the SHA-1 hash of the
public key parameters expressed in a way depended on the algorithm.
@@ -115,7 +115,8 @@ make_simple_sexp_from_hexstr (const char *line, size_t *nscanned)
const char *s;
unsigned char *buf;
unsigned char *p;
- char numbuf[50];
+ char numbuf[50], *numbufp;
+ size_t numbuflen;
for (n=0, s=line; hexdigitp (s); s++, n++)
;
@@ -124,11 +125,12 @@ make_simple_sexp_from_hexstr (const char *line, size_t *nscanned)
if (!n)
return NULL;
len = ((n+1) & ~0x01)/2;
- sprintf (numbuf, "(%u:", (unsigned int)len);
- buf = xtrymalloc (strlen (numbuf) + len + 1 + 1);
+ numbufp = smklen (numbuf, sizeof numbuf, len, &numbuflen);
+ buf = xtrymalloc (1 + numbuflen + len + 1 + 1);
if (!buf)
return NULL;
- p = (unsigned char *)stpcpy ((char *)buf, numbuf);
+ buf[0] = '(';
+ p = (unsigned char *)stpcpy ((char *)buf+1, numbufp);
s = line;
if ((n&1))
{
diff --git a/common/util.h b/common/util.h
index 1fc4d2a1e..5aeb7917c 100644
--- a/common/util.h
+++ b/common/util.h
@@ -27,10 +27,6 @@
#include <errno.h> /* We need errno. */
#include <gpg-error.h> /* We need gpg_error_t. */
-/* Common GNUlib includes (-I ../gl/). */
-#include "vasprintf.h"
-
-
/* Hash function used with libksba. */
#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write)
@@ -43,6 +39,14 @@
#include "../jnlib/dotlock.h"
#include "../jnlib/utf8conv.h"
+/* Redefine asprintf by our estream version which uses our own memory
+ allocator.. */
+#include "estream-printf.h"
+#define asprintf estream_asprintf
+#define vasprintf estream_vasprintf
+
+
+/* GCC attributes. */
#if __GNUC__ >= 4
# define GNUPG_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a)))
#else
@@ -175,13 +179,9 @@ void gnupg_rl_initialize (void);
logging subsystem. */
void setup_libgcrypt_logging (void);
-/* Same as asprintf but return an allocated buffer suitable to be
- freed using xfree. This function simply dies on memory failure,
- thus no extra check is required. */
+/* Same as estream_asprintf but die on memory failure. */
char *xasprintf (const char *fmt, ...) JNLIB_GCC_A_PRINTF(1,2);
-/* Same as asprintf but return an allocated buffer suitable to be
- freed using xfree. This function returns NULL on memory failure and
- sets errno. */
+/* This is now an alias to estream_asprintf. */
char *xtryasprintf (const char *fmt, ...) JNLIB_GCC_A_PRINTF(1,2);
const char *print_fname_stdout (const char *s);
diff --git a/common/vasprintf.c b/common/vasprintf.c
deleted file mode 100644
index 4bed8de66..000000000
--- a/common/vasprintf.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/* Like vsprintf but provides a pointer to malloc'd storage, which must
- be freed by the caller.
- Copyright (C) 1994, 2002 Free Software Foundation, Inc.
-
-This file is part of the libiberty library.
-Libiberty is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public
-License as published by the Free Software Foundation; either
-version 2 of the License, or (at your option) any later version.
-
-Libiberty is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with libiberty; see the file COPYING.LIB. If
-not, write to the Free Software Foundation, Inc., 51 Franklin Street,
-Fifth Floor, Boston, MA 02110-1301, USA. */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#ifdef TEST
-int global_total_width;
-#endif
-
-int
-vasprintf (char **result, const char *format, va_list args)
-{
- const char *p = format;
- /* Add one to make sure that it is never zero, which might cause malloc
- to return NULL. */
- int total_width = strlen (format) + 1;
- va_list ap;
-
-#ifdef va_copy
- va_copy (ap, args);
-#else
-#ifdef __va_copy
- __va_copy (ap, args);
-#else
- memcpy (&ap, args, sizeof (va_list));
-#endif /* __va_copy */
-#endif /* va_copy */
-
- while (*p != '\0')
- {
- if (*p++ == '%')
- {
- while (strchr ("-+ #0", *p))
- ++p;
- if (*p == '*')
- {
- ++p;
- total_width += abs (va_arg (ap, int));
- }
- else
- total_width += strtoul (p, (char**)&p, 10);
- if (*p == '.')
- {
- ++p;
- if (*p == '*')
- {
- ++p;
- total_width += abs (va_arg (ap, int));
- }
- else
- total_width += strtoul (p, (char**)&p, 10);
- }
- while (strchr ("hlL", *p))
- ++p;
- /* Should be big enough for any format specifier except %s
- and floats. */
- total_width += 30;
- switch (*p)
- {
- case 'd':
- case 'i':
- case 'o':
- case 'u':
- case 'x':
- case 'X':
- case 'c':
- (void) va_arg (ap, int);
- break;
- case 'f':
- case 'e':
- case 'E':
- case 'g':
- case 'G':
- (void) va_arg (ap, double);
- /* Since an ieee double can have an exponent of 307, we'll
- make the buffer wide enough to cover the gross case. */
- total_width += 307;
- break;
- case 's':
- {
- char *tmp = va_arg (ap, char *);
- if (tmp)
- total_width += strlen (tmp);
- else /* in case the vsprintf does prints a text */
- total_width += 25; /* e.g. "(null pointer reference)" */
- }
- break;
- case 'p':
- case 'n':
- (void) va_arg (ap, char *);
- break;
- }
- }
- }
-#ifdef TEST
- global_total_width = total_width;
-#endif
- *result = malloc (total_width);
- if (*result != NULL)
- return vsprintf (*result, format, args);
- else
- return 0;
-}
-
-
-int
-asprintf (char **buf, const char *fmt, ...)
-{
- int status;
- va_list ap;
-
- va_start (ap, fmt);
- status = vasprintf (buf, fmt, ap);
- va_end (ap);
- return status;
-}
-
-
-#ifdef TEST
-void
-checkit (const char* format, ...)
-{
- va_list args;
- char *result;
-
- va_start (args, format);
- vasprintf (&result, format, args);
- if (strlen (result) < global_total_width)
- printf ("PASS: ");
- else
- printf ("FAIL: ");
- printf ("%d %s\n", global_total_width, result);
-}
-
-int
-main (void)
-{
- checkit ("%d", 0x12345678);
- checkit ("%200d", 5);
- checkit ("%.300d", 6);
- checkit ("%100.150d", 7);
- checkit ("%s", "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
-777777777777777777333333333333366666666666622222222222777777777777733333");
- checkit ("%f%s%d%s", 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx");
-}
-#endif /* TEST */
diff --git a/common/xasprintf.c b/common/xasprintf.c
index 75ae18072..ce013ba63 100644
--- a/common/xasprintf.c
+++ b/common/xasprintf.c
@@ -25,6 +25,11 @@
#include "util.h"
#include "iobuf.h"
+#include "estream-printf.h"
+
+#if !defined(ESTREAM_ASPRINTF_MALLOC) || !defined(ESTREAM_ASPRINTF_FREE)
+#error Need to define ESTREAM_ASPRINTF_MALLOC and _FREE
+#endif
/* Same as asprintf but return an allocated buffer suitable to be
freed using xfree. This function simply dies on memory failure,
@@ -33,15 +38,13 @@ char *
xasprintf (const char *fmt, ...)
{
va_list ap;
- char *buf, *p;
+ char *buf;
va_start (ap, fmt);
- if (vasprintf (&buf, fmt, ap) < 0)
- log_fatal ("asprintf failed: %s\n", strerror (errno));
+ if (estream_vasprintf (&buf, fmt, ap) < 0)
+ log_fatal ("estream_asprintf failed: %s\n", strerror (errno));
va_end (ap);
- p = xstrdup (buf);
- free (buf);
- return p;
+ return buf;
}
/* Same as above but return NULL on memory failure. */
@@ -50,14 +53,12 @@ xtryasprintf (const char *fmt, ...)
{
int rc;
va_list ap;
- char *buf, *p;
+ char *buf;
va_start (ap, fmt);
- rc = vasprintf (&buf, fmt, ap);
+ rc = estream_vasprintf (&buf, fmt, ap);
va_end (ap);
if (rc < 0)
return NULL;
- p = xtrystrdup (buf);
- free (buf);
- return p;
+ return buf;
}