diff options
author | Werner Koch <wk@gnupg.org> | 2007-05-15 18:10:48 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2007-05-15 18:10:48 +0200 |
commit | 5f3bca96826fbaf9c4469a7eedef9294f4d74bfb (patch) | |
tree | dae425970a8c0dd0f77ab62c91b2700dfbaa811e /common | |
parent | Preparing 2.0.4 (diff) | |
download | gnupg2-5f3bca96826fbaf9c4469a7eedef9294f4d74bfb.tar.xz gnupg2-5f3bca96826fbaf9c4469a7eedef9294f4d74bfb.zip |
Use estream_asprintf instead of the GNU asprintf.
Diffstat (limited to 'common')
-rw-r--r-- | common/ChangeLog | 17 | ||||
-rw-r--r-- | common/Makefile.am | 2 | ||||
-rw-r--r-- | common/estream-printf.c | 2139 | ||||
-rw-r--r-- | common/estream-printf.h | 60 | ||||
-rw-r--r-- | common/estream.c | 143 | ||||
-rw-r--r-- | common/estream.h | 4 | ||||
-rw-r--r-- | common/sexp-parse.h | 32 | ||||
-rw-r--r-- | common/sexputil.c | 12 | ||||
-rw-r--r-- | common/util.h | 20 | ||||
-rw-r--r-- | common/vasprintf.c | 169 | ||||
-rw-r--r-- | common/xasprintf.c | 23 |
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; } |