/* vsprintf with automatic memory allocation.
- Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2002-2005 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
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 <stdio.h> to provide a prototype for snprintf().
This must come before <config.h> because <config.h> may include
#include <stdlib.h> /* abort(), malloc(), realloc(), free() */
#include <string.h> /* memcpy(), strlen() */
#include <errno.h> /* errno */
-#include <limits.h> /* CHAR_BIT */
+#include <limits.h> /* CHAR_BIT, INT_MAX */
#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */
#if WIDE_CHAR_VERSION
# include "wprintf-parse.h"
# include "printf-parse.h"
#endif
-/* 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 */
-#else
-# define alloca(n) malloc (n)
-# define freea(p) free (p)
+/* Checked size_t computations. */
+#include "xsize.h"
+
+/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */
+#ifndef EOVERFLOW
+# define EOVERFLOW E2BIG
#endif
#ifdef HAVE_WCHAR_T
}
{
- CHAR_T *buf =
- (CHAR_T *) alloca ((7 + d.max_width_length + d.max_precision_length + 6)
- * sizeof (CHAR_T));
+ size_t buf_neededlength;
+ CHAR_T *buf;
+ CHAR_T *buf_malloced;
const CHAR_T *cp;
- unsigned int i;
+ size_t i;
DIRECTIVE *dp;
/* Output string accumulator. */
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;
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) \
{ \
+ size_t memory_size; \
CHAR_T *memory; \
\
- allocated = (allocated > 0 ? 2 * allocated : 12); \
+ 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 (allocated * sizeof (CHAR_T)); \
+ memory = (CHAR_T *) malloc (memory_size); \
else \
- memory = (CHAR_T *) realloc (result, allocated * sizeof (CHAR_T)); \
- \
+ memory = (CHAR_T *) realloc (result, memory_size); \
if (memory == NULL) \
- { \
- if (!(result == resultbuf || result == NULL)) \
- free (result); \
- freea (buf); \
- CLEANUP (); \
- errno = ENOMEM; \
- return NULL; \
- } \
+ goto out_of_memory; \
if (result == resultbuf && length > 0) \
memcpy (memory, result, length * sizeof (CHAR_T)); \
result = memory; \
if (cp != dp->dir_start)
{
size_t n = dp->dir_start - cp;
+ size_t augmented_length = xsum (length, n);
- ENSURE_ALLOCATION (length + n);
+ ENSURE_ALLOCATION (augmented_length);
memcpy (result + length, cp, n * sizeof (CHAR_T));
- length += n;
+ length = augmented_length;
}
if (i == d.count)
break;
/* 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')
unsigned int prefix_count;
int prefixes[2];
#if !USE_SNPRINTF
- unsigned int tmp_length;
+ 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_T *digitp = dp->width_start;
do
- width = width * 10 + (*digitp++ - '0');
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
}
precision = 6;
if (dp->precision_start != dp->precision_end)
{
- if (dp->precision_arg_index >= 0)
+ if (dp->precision_arg_index != ARG_NONE)
{
int arg;
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');
}
}
* 2 /* estimate for FLAG_GROUP */
)
+ 1 /* turn floor into ceil */
- + precision
+ 10; /* sign, decimal point etc. */
else
# endif
* 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 'c':
case 's':
# ifdef HAVE_WCHAR_T
if (type == TYPE_WIDE_STRING)
-# if WIDE_CHAR_VERSION
- tmp_length =
- local_wcslen (a.arg[dp->arg_index].a.a_wide_string);
-# else
- 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);
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) / sizeof (CHAR_T))
tmp = tmpbuf;
else
{
- tmp = (CHAR_T *) malloc (tmp_length * sizeof (CHAR_T));
+ 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
/* 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 ();
#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
*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;
}
{
if (!(result == resultbuf || result == NULL))
free (result);
- freea (buf);
+ if (buf_malloced != NULL)
+ free (buf_malloced);
CLEANUP ();
errno = EINVAL;
return NULL;
/* 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 USE_SNPRINTF
}
/* Add the final NUL. */
- ENSURE_ALLOCATION (length + 1);
+ ENSURE_ALLOCATION (xsum (length, 1));
result[length] = '\0';
if (result != resultbuf && length + 1 < allocated)
result = memory;
}
- freea (buf);
+ if (buf_malloced != NULL)
+ free (buf_malloced);
CLEANUP ();
*lengthp = length;
+ if (length > INT_MAX)
+ goto length_overflow;
return result;
+
+ length_overflow:
+ /* We could produce such a big string, but its length doesn't fit into
+ an 'int'. POSIX says that snprintf() fails with errno = EOVERFLOW in
+ this case. */
+ if (result != resultbuf)
+ free (result);
+ errno = EOVERFLOW;
+ return NULL;
+
+ 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;
}
}