X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fvasnprintf.c;h=a0523ec1fb86488f1152e46c751f289902a0d617;hb=20229ba96d971f97ff03ab74eeb4e7de1acccf98;hp=246c75bdfa0fe63ecee2b8839a7f2cdd7988a3f2;hpb=8ad6a4a39b9e011b35b61d4113dedfbe2a9428c4;p=gnulib.git diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 246c75bdf..a0523ec1f 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -1,5 +1,5 @@ /* vsprintf with automatic memory allocation. - Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. + Copyright (C) 1999, 2002-2007 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,7 +13,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Tell glibc's to provide a prototype for snprintf(). This must come before because may include @@ -22,37 +22,53 @@ # define _GNU_SOURCE 1 #endif -#ifdef HAVE_CONFIG_H -# include +#include +#ifndef IN_LIBINTL +# include #endif -#include /* Specification. */ -#include "vasnprintf.h" +#if WIDE_CHAR_VERSION +# include "vasnwprintf.h" +#else +# include "vasnprintf.h" +#endif +#include /* localeconv() */ #include /* snprintf(), sprintf() */ #include /* abort(), malloc(), realloc(), free() */ #include /* memcpy(), strlen() */ #include /* errno */ #include /* CHAR_BIT */ #include /* DBL_MAX_EXP, LDBL_MAX_EXP */ -#include "printf-parse.h" - -/* For those losing systems which don't have 'alloca' we have to add - some additional code emulating it. */ -#ifdef HAVE_ALLOCA -# define freea(p) /* nothing */ +#if WIDE_CHAR_VERSION +# include "wprintf-parse.h" #else -# define alloca(n) malloc (n) -# define freea(p) free (p) +# include "printf-parse.h" +#endif + +/* Checked size_t computations. */ +#include "xsize.h" + +#if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL +# include "isnan.h" +# include "isnanl.h" +# if HAVE_LONG_DOUBLE +# include "printf-frexp.h" +# include "printf-frexpl.h" +# endif #endif -#ifdef HAVE_WCHAR_T -# ifdef HAVE_WCSLEN +#if HAVE_WCHAR_T +# if HAVE_WCSLEN # define local_wcslen wcslen # else /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid - a dependency towards this library, here is a local substitute. */ + a dependency towards this library, here is a local substitute. + Define this substitute only once, even if this file is included + twice in the same compilation unit. */ +# ifndef local_wcslen_defined +# define local_wcslen_defined 1 static size_t local_wcslen (const wchar_t *s) { @@ -62,16 +78,48 @@ local_wcslen (const wchar_t *s) ; return ptr - s; } +# endif +# endif +#endif + +#if WIDE_CHAR_VERSION +# define VASNPRINTF vasnwprintf +# define CHAR_T wchar_t +# define DIRECTIVE wchar_t_directive +# define DIRECTIVES wchar_t_directives +# define PRINTF_PARSE wprintf_parse +# define USE_SNPRINTF 1 +# if HAVE_DECL__SNWPRINTF + /* On Windows, the function swprintf() has a different signature than + on Unix; we use the _snwprintf() function instead. */ +# define SNPRINTF _snwprintf +# else + /* Unix. */ +# define SNPRINTF swprintf +# endif +#else +# define VASNPRINTF vasnprintf +# define CHAR_T char +# define DIRECTIVE char_directive +# define DIRECTIVES char_directives +# define PRINTF_PARSE printf_parse +# define USE_SNPRINTF (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) +# if HAVE_DECL__SNPRINTF + /* Windows. */ +# define SNPRINTF _snprintf +# else + /* Unix. */ +# define SNPRINTF snprintf # endif #endif -char * -vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) +CHAR_T * +VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list args) { - char_directives d; + DIRECTIVES d; arguments a; - if (printf_parse (format, &d, &a) < 0) + if (PRINTF_PARSE (format, &d, &a) < 0) { errno = EINVAL; return NULL; @@ -90,16 +138,39 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) } { - char *buf = - (char *) alloca (7 + d.max_width_length + d.max_precision_length + 6); - const char *cp; - unsigned int i; - char_directive *dp; + size_t buf_neededlength; + CHAR_T *buf; + CHAR_T *buf_malloced; + const CHAR_T *cp; + size_t i; + DIRECTIVE *dp; /* Output string accumulator. */ - char *result; + CHAR_T *result; size_t allocated; size_t length; + /* Allocate a small buffer that will hold a directive passed to + sprintf or snprintf. */ + buf_neededlength = + xsum4 (7, d.max_width_length, d.max_precision_length, 6); +#if HAVE_ALLOCA + if (buf_neededlength < 4000 / sizeof (CHAR_T)) + { + buf = (CHAR_T *) alloca (buf_neededlength * sizeof (CHAR_T)); + buf_malloced = NULL; + } + else +#endif + { + size_t buf_memsize = xtimes (buf_neededlength, sizeof (CHAR_T)); + if (size_overflow_p (buf_memsize)) + goto out_of_memory_1; + buf = (CHAR_T *) malloc (buf_memsize); + if (buf == NULL) + goto out_of_memory_1; + buf_malloced = buf; + } + if (resultbuf != NULL) { result = resultbuf; @@ -115,31 +186,29 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) result is either == resultbuf or == NULL or malloc-allocated. If length > 0, then result != NULL. */ + /* Ensures that allocated >= needed. Aborts through a jump to + out_of_memory if needed is SIZE_MAX or otherwise too big. */ #define ENSURE_ALLOCATION(needed) \ - if ((needed) > allocated) \ - { \ - char *memory; \ - \ - allocated = (allocated > 0 ? 2 * allocated : 12); \ - if ((needed) > allocated) \ - allocated = (needed); \ - if (result == resultbuf || result == NULL) \ - memory = (char *) malloc (allocated); \ - else \ - memory = (char *) realloc (result, allocated); \ - \ - if (memory == NULL) \ - { \ - if (!(result == resultbuf || result == NULL)) \ - free (result); \ - freea (buf); \ - CLEANUP (); \ - errno = ENOMEM; \ - return NULL; \ - } \ - if (result == resultbuf && length > 0) \ - memcpy (memory, result, length); \ - result = memory; \ + if ((needed) > allocated) \ + { \ + size_t memory_size; \ + CHAR_T *memory; \ + \ + allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ + if ((needed) > allocated) \ + allocated = (needed); \ + memory_size = xtimes (allocated, sizeof (CHAR_T)); \ + if (size_overflow_p (memory_size)) \ + goto out_of_memory; \ + if (result == resultbuf || result == NULL) \ + memory = (CHAR_T *) malloc (memory_size); \ + else \ + memory = (CHAR_T *) realloc (result, memory_size); \ + if (memory == NULL) \ + goto out_of_memory; \ + if (result == resultbuf && length > 0) \ + memcpy (memory, result, length * sizeof (CHAR_T)); \ + result = memory; \ } for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++) @@ -147,10 +216,11 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) if (cp != dp->dir_start) { size_t n = dp->dir_start - cp; + size_t augmented_length = xsum (length, n); - ENSURE_ALLOCATION (length + n); - memcpy (result + length, cp, n); - length += n; + ENSURE_ALLOCATION (augmented_length); + memcpy (result + length, cp, n * sizeof (CHAR_T)); + length = augmented_length; } if (i == d.count) break; @@ -158,15 +228,18 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) /* Execute a single directive. */ if (dp->conversion == '%') { - if (!(dp->arg_index < 0)) + size_t augmented_length; + + if (!(dp->arg_index == ARG_NONE)) abort (); - ENSURE_ALLOCATION (length + 1); + augmented_length = xsum (length, 1); + ENSURE_ALLOCATION (augmented_length); result[length] = '%'; - length += 1; + length = augmented_length; } else { - if (!(dp->arg_index >= 0)) + if (!(dp->arg_index != ARG_NONE)) abort (); if (dp->conversion == 'n') @@ -185,7 +258,7 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) case TYPE_COUNT_LONGINT_POINTER: *a.arg[dp->arg_index].a.a_count_longint_pointer = length; break; -#ifdef HAVE_LONG_LONG +#if HAVE_LONG_LONG_INT case TYPE_COUNT_LONGLONGINT_POINTER: *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; break; @@ -194,41 +267,517 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) abort (); } } +#if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL + else if (dp->conversion == 'a' || dp->conversion == 'A') + { + arg_type type = a.arg[dp->arg_index].type; + int flags = dp->flags; + int has_width; + size_t width; + int has_precision; + size_t precision; + size_t tmp_length; + CHAR_T tmpbuf[700]; + CHAR_T *tmp; + CHAR_T *pad_ptr; + CHAR_T *p; + + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = (unsigned int) (-arg); + } + else + width = arg; + } + else + { + const CHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + has_width = 1; + } + + has_precision = 0; + precision = 0; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const CHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } + + /* Allocate a temporary buffer of sufficient size. */ +# if HAVE_LONG_DOUBLE + if (type == TYPE_LONGDOUBLE) + tmp_length = + (unsigned int) ((LDBL_DIG + 1) + * 0.831 /* decimal -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + else +# endif + tmp_length = + (unsigned int) ((DBL_DIG + 1) + * 0.831 /* decimal -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Account for sign, decimal point etc. */ + tmp_length = xsum (tmp_length, 12); + + if (tmp_length < width) + tmp_length = width; + + tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ + + if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T)) + tmp = tmpbuf; + else + { + size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (CHAR_T *) malloc (tmp_memsize); + if (tmp == NULL) + /* Out of memory. */ + goto out_of_memory; + } + + pad_ptr = NULL; + p = tmp; +# if HAVE_LONG_DOUBLE + if (type == TYPE_LONGDOUBLE) + { + long double arg = a.arg[dp->arg_index].a.a_longdouble; + + if (isnanl (arg)) + { + if (dp->conversion == 'A') + { + *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; + } + else + { + *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; + } + } + else + { + int sign = 0; + + if (arg < 0.0L) + { + sign = -1; + arg = -arg; + } + else if (arg == 0.0L) + { + /* Distinguish 0.0L and -0.0L. */ + static long double plus_zero = 0.0L; + long double arg_mem = arg; + if (memcmp (&plus_zero, &arg_mem, sizeof (long double)) != 0) + { + sign = -1; + arg = -arg; + } + } + + if (sign < 0) + *p++ = '-'; + else if (flags & FLAG_SHOWSIGN) + *p++ = '+'; + else if (flags & FLAG_SPACE) + *p++ = ' '; + + if (arg > 0.0L && arg + arg == arg) + { + if (dp->conversion == 'A') + { + *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; + } + else + { + *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; + } + } + else + { + int exponent; + long double mantissa; + + if (arg > 0.0L) + mantissa = printf_frexpl (arg, &exponent); + else + { + exponent = 0; + mantissa = 0.0L; + } + + if (has_precision + && precision < (unsigned int) ((LDBL_DIG + 1) * 0.831) + 1) + { + /* Round the mantissa. */ + long double tail = mantissa; + size_t q; + + for (q = precision; ; q--) + { + int digit = (int) tail; + tail -= digit; + if (q == 0) + { + if (digit & 1 ? tail >= 0.5L : tail > 0.5L) + tail = 1 - tail; + else + tail = - tail; + break; + } + tail *= 16.0L; + } + if (tail != 0.0L) + for (q = precision; q > 0; q--) + tail *= 0.0625L; + mantissa += tail; + } + + *p++ = '0'; + *p++ = dp->conversion - 'A' + 'X'; + pad_ptr = p; + { + int digit; + + digit = (int) mantissa; + mantissa -= digit; + *p++ = '0' + digit; + if ((flags & FLAG_ALT) + || mantissa > 0.0L || precision > 0) + { + const char *point = + localeconv () -> decimal_point; + /* The decimal point is always a single byte: + either '.' or ','. */ + *p++ = (point[0] != '\0' ? point[0] : '.'); + /* This loop terminates because we assume + that FLT_RADIX is a power of 2. */ + while (mantissa > 0.0L) + { + mantissa *= 16.0L; + digit = (int) mantissa; + mantissa -= digit; + *p++ = digit + + (digit < 10 + ? '0' + : dp->conversion - 10); + if (precision > 0) + precision--; + } + while (precision > 0) + { + *p++ = '0'; + precision--; + } + } + } + *p++ = dp->conversion - 'A' + 'P'; +# if WIDE_CHAR_VERSION + { + static const wchar_t decimal_format[] = + { '%', '+', 'd', '\0' }; + SNPRINTF (p, 6 + 1, decimal_format, exponent); + } +# else + sprintf (p, "%+d", exponent); +# endif + while (*p != '\0') + p++; + } + } + } + else +# endif + { + double arg = a.arg[dp->arg_index].a.a_double; + + if (isnan (arg)) + { + if (dp->conversion == 'A') + { + *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; + } + else + { + *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; + } + } + else + { + int sign = 0; + + if (arg < 0.0) + { + sign = -1; + arg = -arg; + } + else if (arg == 0.0) + { + /* Distinguish 0.0 and -0.0. */ + static double plus_zero = 0.0; + double arg_mem = arg; + if (memcmp (&plus_zero, &arg_mem, sizeof (double)) != 0) + { + sign = -1; + arg = -arg; + } + } + + if (sign < 0) + *p++ = '-'; + else if (flags & FLAG_SHOWSIGN) + *p++ = '+'; + else if (flags & FLAG_SPACE) + *p++ = ' '; + + if (arg > 0.0 && arg + arg == arg) + { + if (dp->conversion == 'A') + { + *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; + } + else + { + *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; + } + } + else + { + int exponent; + double mantissa; + + if (arg > 0.0) + mantissa = printf_frexp (arg, &exponent); + else + { + exponent = 0; + mantissa = 0.0; + } + + if (has_precision + && precision < (unsigned int) ((DBL_DIG + 1) * 0.831) + 1) + { + /* Round the mantissa. */ + double tail = mantissa; + size_t q; + + for (q = precision; ; q--) + { + int digit = (int) tail; + tail -= digit; + if (q == 0) + { + if (digit & 1 ? tail >= 0.5 : tail > 0.5) + tail = 1 - tail; + else + tail = - tail; + break; + } + tail *= 16.0; + } + if (tail != 0.0) + for (q = precision; q > 0; q--) + tail *= 0.0625; + mantissa += tail; + } + + *p++ = '0'; + *p++ = dp->conversion - 'A' + 'X'; + pad_ptr = p; + { + int digit; + + digit = (int) mantissa; + mantissa -= digit; + *p++ = '0' + digit; + if ((flags & FLAG_ALT) + || mantissa > 0.0 || precision > 0) + { + const char *point = + localeconv () -> decimal_point; + /* The decimal point is always a single byte: + either '.' or ','. */ + *p++ = (point[0] != '\0' ? point[0] : '.'); + /* This loop terminates because we assume + that FLT_RADIX is a power of 2. */ + while (mantissa > 0.0) + { + mantissa *= 16.0; + digit = (int) mantissa; + mantissa -= digit; + *p++ = digit + + (digit < 10 + ? '0' + : dp->conversion - 10); + if (precision > 0) + precision--; + } + while (precision > 0) + { + *p++ = '0'; + precision--; + } + } + } + *p++ = dp->conversion - 'A' + 'P'; +# if WIDE_CHAR_VERSION + { + static const wchar_t decimal_format[] = + { '%', '+', 'd', '\0' }; + SNPRINTF (p, 6 + 1, decimal_format, exponent); + } +# else + sprintf (p, "%+d", exponent); +# endif + while (*p != '\0') + p++; + } + } + } + /* The generated string now extends from tmp to p, with the + zero padding insertion point being at pad_ptr. */ + if (has_width && p - tmp < width) + { + size_t pad = width - (p - tmp); + CHAR_T *end = p + pad; + + if (flags & FLAG_LEFT) + { + /* Pad with spaces on the right. */ + for (; pad > 0; pad--) + *p++ = ' '; + } + else if ((flags & FLAG_ZERO) && pad_ptr != NULL) + { + /* Pad with zeroes. */ + CHAR_T *q = end; + + while (p > pad_ptr) + *--q = *--p; + for (; pad > 0; pad--) + *p++ = '0'; + } + else + { + /* Pad with spaces on the left. */ + CHAR_T *q = end; + + while (p > tmp) + *--q = *--p; + for (; pad > 0; pad--) + *p++ = ' '; + } + + p = end; + } + + { + size_t count = p - tmp; + + if (count >= tmp_length) + /* tmp_length was incorrectly calculated - fix the + code above! */ + abort (); + + /* Make room for the result. */ + if (count >= allocated - length) + { + size_t n = xsum (length, count); + + ENSURE_ALLOCATION (n); + } + + /* Append the result. */ + memcpy (result + length, tmp, count * sizeof (CHAR_T)); + if (tmp != tmpbuf) + free (tmp); + length += count; + } + } +#endif else { arg_type type = a.arg[dp->arg_index].type; - char *p; + CHAR_T *p; unsigned int prefix_count; int prefixes[2]; -#if !HAVE_SNPRINTF - unsigned int tmp_length; - char tmpbuf[700]; - char *tmp; +#if !USE_SNPRINTF + size_t tmp_length; + CHAR_T tmpbuf[700]; + CHAR_T *tmp; /* Allocate a temporary buffer of sufficient size for calling sprintf. */ { - unsigned int width; - unsigned int precision; + size_t width; + size_t precision; width = 0; if (dp->width_start != dp->width_end) { - if (dp->width_arg_index >= 0) + if (dp->width_arg_index != ARG_NONE) { int arg; if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); arg = a.arg[dp->width_arg_index].a.a_int; - width = (arg < 0 ? -arg : arg); + width = (arg < 0 ? (unsigned int) (-arg) : arg); } else { - const char *digitp = dp->width_start; + const CHAR_T *digitp = dp->width_start; do - width = width * 10 + (*digitp++ - '0'); + width = xsum (xtimes (width, 10), *digitp++ - '0'); while (digitp != dp->width_end); } } @@ -236,7 +785,7 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) precision = 6; if (dp->precision_start != dp->precision_end) { - if (dp->precision_arg_index >= 0) + if (dp->precision_arg_index != ARG_NONE) { int arg; @@ -247,12 +796,11 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) } else { - const char *digitp = dp->precision_start + 1; + const CHAR_T *digitp = dp->precision_start + 1; precision = 0; - do - precision = precision * 10 + (*digitp++ - '0'); - while (digitp != dp->precision_end); + while (digitp != dp->precision_end) + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); } } @@ -260,44 +808,43 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) { case 'd': case 'i': case 'u': -# ifdef HAVE_LONG_LONG +# if HAVE_LONG_LONG_INT if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.30103 /* binary -> decimal */ - * 2 /* estimate for FLAG_GROUP */ ) - + 1 /* turn floor into ceil */ - + 1; /* account for leading sign */ + + 1; /* turn floor into ceil */ else # endif if (type == TYPE_LONGINT || type == TYPE_ULONGINT) tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.30103 /* binary -> decimal */ - * 2 /* estimate for FLAG_GROUP */ ) - + 1 /* turn floor into ceil */ - + 1; /* account for leading sign */ + + 1; /* turn floor into ceil */ else tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.30103 /* binary -> decimal */ - * 2 /* estimate for FLAG_GROUP */ ) - + 1 /* turn floor into ceil */ - + 1; /* account for leading sign */ + + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Multiply by 2, as an estimate for FLAG_GROUP. */ + tmp_length = xsum (tmp_length, tmp_length); + /* Add 1, to account for a leading sign. */ + tmp_length = xsum (tmp_length, 1); break; case 'o': -# ifdef HAVE_LONG_LONG +# if HAVE_LONG_LONG_INT if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.333334 /* binary -> octal */ ) - + 1 /* turn floor into ceil */ - + 1; /* account for leading sign */ + + 1; /* turn floor into ceil */ else # endif if (type == TYPE_LONGINT || type == TYPE_ULONGINT) @@ -305,26 +852,27 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.333334 /* binary -> octal */ ) - + 1 /* turn floor into ceil */ - + 1; /* account for leading sign */ + + 1; /* turn floor into ceil */ else tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.333334 /* binary -> octal */ ) - + 1 /* turn floor into ceil */ - + 1; /* account for leading sign */ + + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Add 1, to account for a leading sign. */ + tmp_length = xsum (tmp_length, 1); break; case 'x': case 'X': -# ifdef HAVE_LONG_LONG +# if HAVE_LONG_LONG_INT if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ ) - + 1 /* turn floor into ceil */ - + 2; /* account for leading sign or alternate form */ + + 1; /* turn floor into ceil */ else # endif if (type == TYPE_LONGINT || type == TYPE_ULONGINT) @@ -332,19 +880,21 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ ) - + 1 /* turn floor into ceil */ - + 2; /* account for leading sign or alternate form */ + + 1; /* turn floor into ceil */ else tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ ) - + 1 /* turn floor into ceil */ - + 2; /* account for leading sign or alternate form */ + + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Add 2, to account for a leading sign or alternate form. */ + tmp_length = xsum (tmp_length, 2); break; case 'f': case 'F': -# ifdef HAVE_LONG_DOUBLE +# if HAVE_LONG_DOUBLE if (type == TYPE_LONGDOUBLE) tmp_length = (unsigned int) (LDBL_MAX_EXP @@ -352,7 +902,6 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) * 2 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ - + precision + 10; /* sign, decimal point etc. */ else # endif @@ -362,19 +911,39 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) * 2 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ - + precision + 10; /* sign, decimal point etc. */ + tmp_length = xsum (tmp_length, precision); break; case 'e': case 'E': case 'g': case 'G': - case 'a': case 'A': tmp_length = - precision - + 12; /* sign, decimal point, exponent etc. */ + 12; /* sign, decimal point, exponent etc. */ + tmp_length = xsum (tmp_length, precision); + break; + + case 'a': case 'A': +# if HAVE_LONG_DOUBLE + if (type == TYPE_LONGDOUBLE) + tmp_length = + (unsigned int) (LDBL_DIG + * 0.831 /* decimal -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + else +# endif + tmp_length = + (unsigned int) (DBL_DIG + * 0.831 /* decimal -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Account for sign, decimal point etc. */ + tmp_length = xsum (tmp_length, 12); break; case 'c': -# ifdef HAVE_WINT_T +# if HAVE_WINT_T && !WIDE_CHAR_VERSION if (type == TYPE_WIDE_CHAR) tmp_length = MB_CUR_MAX; else @@ -383,11 +952,16 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) break; case 's': -# ifdef HAVE_WCHAR_T +# if HAVE_WCHAR_T if (type == TYPE_WIDE_STRING) - tmp_length = - local_wcslen (a.arg[dp->arg_index].a.a_wide_string) - * MB_CUR_MAX; + { + tmp_length = + local_wcslen (a.arg[dp->arg_index].a.a_wide_string); + +# if !WIDE_CHAR_VERSION + tmp_length = xtimes (tmp_length, MB_CUR_MAX); +# endif + } else # endif tmp_length = strlen (a.arg[dp->arg_index].a.a_string); @@ -409,24 +983,22 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) if (tmp_length < width) tmp_length = width; - tmp_length++; /* account for trailing NUL */ + tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ } - if (tmp_length <= sizeof (tmpbuf)) + if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T)) tmp = tmpbuf; else { - tmp = (char *) malloc (tmp_length); + size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (CHAR_T *) malloc (tmp_memsize); if (tmp == NULL) - { - /* Out of memory. */ - if (!(result == resultbuf || result == NULL)) - free (result); - freea (buf); - CLEANUP (); - errno = ENOMEM; - return NULL; - } + /* Out of memory. */ + goto out_of_memory; } #endif @@ -449,19 +1021,19 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) if (dp->width_start != dp->width_end) { size_t n = dp->width_end - dp->width_start; - memcpy (p, dp->width_start, n); + memcpy (p, dp->width_start, n * sizeof (CHAR_T)); p += n; } if (dp->precision_start != dp->precision_end) { size_t n = dp->precision_end - dp->precision_start; - memcpy (p, dp->precision_start, n); + memcpy (p, dp->precision_start, n * sizeof (CHAR_T)); p += n; } switch (type) { -#ifdef HAVE_LONG_LONG +#if HAVE_LONG_LONG_INT case TYPE_LONGLONGINT: case TYPE_ULONGLONGINT: *p++ = 'l'; @@ -469,15 +1041,15 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) #endif case TYPE_LONGINT: case TYPE_ULONGINT: -#ifdef HAVE_WINT_T +#if HAVE_WINT_T case TYPE_WIDE_CHAR: #endif -#ifdef HAVE_WCHAR_T +#if HAVE_WCHAR_T case TYPE_WIDE_STRING: #endif *p++ = 'l'; break; -#ifdef HAVE_LONG_DOUBLE +#if HAVE_LONG_DOUBLE case TYPE_LONGDOUBLE: *p++ = 'L'; break; @@ -486,7 +1058,7 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) break; } *p = dp->conversion; -#if HAVE_SNPRINTF +#if USE_SNPRINTF p[1] = '%'; p[2] = 'n'; p[3] = '\0'; @@ -496,23 +1068,23 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) /* Construct the arguments for calling snprintf or sprintf. */ prefix_count = 0; - if (dp->width_arg_index >= 0) + if (dp->width_arg_index != ARG_NONE) { if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; } - if (dp->precision_arg_index >= 0) + if (dp->precision_arg_index != ARG_NONE) { if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int; } -#if HAVE_SNPRINTF +#if USE_SNPRINTF /* Prepare checking whether snprintf returns the count via %n. */ - ENSURE_ALLOCATION (length + 1); + ENSURE_ALLOCATION (xsum (length, 1)); result[length] = '\0'; #endif @@ -526,20 +1098,20 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) count = -1; retcount = 0; -#if HAVE_SNPRINTF +#if USE_SNPRINTF # define SNPRINTF_BUF(arg) \ switch (prefix_count) \ { \ case 0: \ - retcount = snprintf (result + length, maxlen, buf, \ + retcount = SNPRINTF (result + length, maxlen, buf, \ arg, &count); \ break; \ case 1: \ - retcount = snprintf (result + length, maxlen, buf, \ + retcount = SNPRINTF (result + length, maxlen, buf, \ prefixes[0], arg, &count); \ break; \ case 2: \ - retcount = snprintf (result + length, maxlen, buf, \ + retcount = SNPRINTF (result + length, maxlen, buf, \ prefixes[0], prefixes[1], arg, \ &count); \ break; \ @@ -615,7 +1187,7 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) SNPRINTF_BUF (arg); } break; -#ifdef HAVE_LONG_LONG +#if HAVE_LONG_LONG_INT case TYPE_LONGLONGINT: { long long int arg = a.arg[dp->arg_index].a.a_longlongint; @@ -635,7 +1207,7 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) SNPRINTF_BUF (arg); } break; -#ifdef HAVE_LONG_DOUBLE +#if HAVE_LONG_DOUBLE case TYPE_LONGDOUBLE: { long double arg = a.arg[dp->arg_index].a.a_longdouble; @@ -649,7 +1221,7 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) SNPRINTF_BUF (arg); } break; -#ifdef HAVE_WINT_T +#if HAVE_WINT_T case TYPE_WIDE_CHAR: { wint_t arg = a.arg[dp->arg_index].a.a_wide_char; @@ -663,7 +1235,7 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) SNPRINTF_BUF (arg); } break; -#ifdef HAVE_WCHAR_T +#if HAVE_WCHAR_T case TYPE_WIDE_STRING: { const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string; @@ -681,7 +1253,7 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) abort (); } -#if HAVE_SNPRINTF +#if USE_SNPRINTF /* Portability: Not all implementations of snprintf() are ISO C 99 compliant. Determine the number of bytes that snprintf() has produced or would have @@ -717,7 +1289,8 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) *and* it returns -1 (rather than the length that would have been required) when the buffer is too small. */ - size_t bigger_need = 2 * allocated + 12; + size_t bigger_need = + xsum (xtimes (allocated, 2), 12); ENSURE_ALLOCATION (bigger_need); continue; } @@ -732,13 +1305,14 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) { if (!(result == resultbuf || result == NULL)) free (result); - freea (buf); + if (buf_malloced != NULL) + free (buf_malloced); CLEANUP (); errno = EINVAL; return NULL; } -#if !HAVE_SNPRINTF +#if !USE_SNPRINTF if (count >= tmp_length) /* tmp_length was incorrectly calculated - fix the code above! */ @@ -751,22 +1325,20 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) /* Need at least count bytes. But allocate proportionally, to avoid looping eternally if snprintf() reports a too small count. */ - size_t n = length + count; - - if (n < 2 * allocated) - n = 2 * allocated; + size_t n = + xmax (xsum (length, count), xtimes (allocated, 2)); ENSURE_ALLOCATION (n); -#if HAVE_SNPRINTF +#if USE_SNPRINTF continue; #endif } -#if HAVE_SNPRINTF +#if USE_SNPRINTF /* The snprintf() result did fit. */ #else /* Append the sprintf() result. */ - memcpy (result + length, tmp, count); + memcpy (result + length, tmp, count * sizeof (CHAR_T)); if (tmp != tmpbuf) free (tmp); #endif @@ -779,22 +1351,45 @@ vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) } /* Add the final NUL. */ - ENSURE_ALLOCATION (length + 1); + ENSURE_ALLOCATION (xsum (length, 1)); result[length] = '\0'; if (result != resultbuf && length + 1 < allocated) { /* Shrink the allocated memory if possible. */ - char *memory; + CHAR_T *memory; - memory = (char *) realloc (result, length + 1); + memory = (CHAR_T *) realloc (result, (length + 1) * sizeof (CHAR_T)); if (memory != NULL) result = memory; } - freea (buf); + if (buf_malloced != NULL) + free (buf_malloced); CLEANUP (); *lengthp = length; + /* Note that we can produce a big string of a length > INT_MAX. POSIX + says that snprintf() fails with errno = EOVERFLOW in this case, but + that's only because snprintf() returns an 'int'. This function does + not have this limitation. */ return result; + + out_of_memory: + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + out_of_memory_1: + CLEANUP (); + errno = ENOMEM; + return NULL; } } + +#undef SNPRINTF +#undef USE_SNPRINTF +#undef PRINTF_PARSE +#undef DIRECTIVES +#undef DIRECTIVE +#undef CHAR_T +#undef VASNPRINTF