summaryrefslogtreecommitdiff
path: root/intl/vasnprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'intl/vasnprintf.c')
-rw-r--r--intl/vasnprintf.c887
1 files changed, 887 insertions, 0 deletions
diff --git a/intl/vasnprintf.c b/intl/vasnprintf.c
new file mode 100644
index 0000000..f192f20
--- /dev/null
+++ b/intl/vasnprintf.c
@@ -0,0 +1,887 @@
+/* vsprintf with automatic memory allocation.
+ Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
+
+ This program 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, or (at your option)
+ any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, 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
+ <features.h>, and once <features.h> has been included, it's too late. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#ifndef IN_LIBINTL
+# include <alloca.h>
+#endif
+
+/* Specification. */
+#if WIDE_CHAR_VERSION
+# include "vasnwprintf.h"
+#else
+# include "vasnprintf.h"
+#endif
+
+#include <stdio.h> /* snprintf(), sprintf() */
+#include <stdlib.h> /* abort(), malloc(), realloc(), free() */
+#include <string.h> /* memcpy(), strlen() */
+#include <errno.h> /* errno */
+#include <limits.h> /* CHAR_BIT */
+#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */
+#if WIDE_CHAR_VERSION
+# include "wprintf-parse.h"
+#else
+# include "printf-parse.h"
+#endif
+
+/* Checked size_t computations. */
+#include "xsize.h"
+
+#ifdef HAVE_WCHAR_T
+# ifdef 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.
+ 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)
+{
+ const wchar_t *ptr;
+
+ for (ptr = s; *ptr != (wchar_t) 0; ptr++)
+ ;
+ 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_T *
+VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list args)
+{
+ DIRECTIVES d;
+ arguments a;
+
+ if (PRINTF_PARSE (format, &d, &a) < 0)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+#define CLEANUP() \
+ free (d.dir); \
+ if (a.arg) \
+ free (a.arg);
+
+ if (printf_fetchargs (args, &a) < 0)
+ {
+ CLEANUP ();
+ errno = EINVAL;
+ return NULL;
+ }
+
+ {
+ size_t buf_neededlength;
+ CHAR_T *buf;
+ CHAR_T *buf_malloced;
+ const CHAR_T *cp;
+ 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;
+ allocated = *lengthp;
+ }
+ else
+ {
+ result = NULL;
+ allocated = 0;
+ }
+ length = 0;
+ /* Invariants:
+ 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 ? 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++)
+ {
+ if (cp != dp->dir_start)
+ {
+ size_t n = dp->dir_start - cp;
+ size_t augmented_length = xsum (length, n);
+
+ ENSURE_ALLOCATION (augmented_length);
+ memcpy (result + length, cp, n * sizeof (CHAR_T));
+ length = augmented_length;
+ }
+ if (i == d.count)
+ break;
+
+ /* Execute a single directive. */
+ if (dp->conversion == '%')
+ {
+ size_t augmented_length;
+
+ if (!(dp->arg_index == ARG_NONE))
+ abort ();
+ augmented_length = xsum (length, 1);
+ ENSURE_ALLOCATION (augmented_length);
+ result[length] = '%';
+ length = augmented_length;
+ }
+ else
+ {
+ if (!(dp->arg_index != ARG_NONE))
+ abort ();
+
+ if (dp->conversion == 'n')
+ {
+ switch (a.arg[dp->arg_index].type)
+ {
+ case TYPE_COUNT_SCHAR_POINTER:
+ *a.arg[dp->arg_index].a.a_count_schar_pointer = length;
+ break;
+ case TYPE_COUNT_SHORT_POINTER:
+ *a.arg[dp->arg_index].a.a_count_short_pointer = length;
+ break;
+ case TYPE_COUNT_INT_POINTER:
+ *a.arg[dp->arg_index].a.a_count_int_pointer = length;
+ break;
+ case TYPE_COUNT_LONGINT_POINTER:
+ *a.arg[dp->arg_index].a.a_count_longint_pointer = length;
+ break;
+#ifdef HAVE_LONG_LONG
+ case TYPE_COUNT_LONGLONGINT_POINTER:
+ *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length;
+ break;
+#endif
+ default:
+ abort ();
+ }
+ }
+ else
+ {
+ arg_type type = a.arg[dp->arg_index].type;
+ CHAR_T *p;
+ unsigned int prefix_count;
+ int prefixes[2];
+#if !USE_SNPRINTF
+ size_t tmp_length;
+ CHAR_T tmpbuf[700];
+ CHAR_T *tmp;
+
+ /* Allocate a temporary buffer of sufficient size for calling
+ sprintf. */
+ {
+ size_t width;
+ size_t precision;
+
+ 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;
+ width = (arg < 0 ? (unsigned int) (-arg) : arg);
+ }
+ else
+ {
+ const CHAR_T *digitp = dp->width_start;
+
+ do
+ 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 != 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;
+ precision = (arg < 0 ? 0 : arg);
+ }
+ else
+ {
+ const CHAR_T *digitp = dp->precision_start + 1;
+
+ precision = 0;
+ do
+ precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+ while (digitp != dp->precision_end);
+ }
+ }
+
+ switch (dp->conversion)
+ {
+
+ case 'd': case 'i': case 'u':
+# ifdef HAVE_LONG_LONG
+ 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 */
+ 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 */
+ 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 */
+ break;
+
+ case 'o':
+# ifdef HAVE_LONG_LONG
+ 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 */
+ else
+# endif
+ if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
+ tmp_length =
+ (unsigned int) (sizeof (unsigned long) * CHAR_BIT
+ * 0.333334 /* binary -> octal */
+ )
+ + 1 /* turn floor into ceil */
+ + 1; /* account for leading sign */
+ else
+ tmp_length =
+ (unsigned int) (sizeof (unsigned int) * CHAR_BIT
+ * 0.333334 /* binary -> octal */
+ )
+ + 1 /* turn floor into ceil */
+ + 1; /* account for leading sign */
+ break;
+
+ case 'x': case 'X':
+# ifdef HAVE_LONG_LONG
+ 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 */
+ else
+# endif
+ if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
+ tmp_length =
+ (unsigned int) (sizeof (unsigned long) * CHAR_BIT
+ * 0.25 /* binary -> hexadecimal */
+ )
+ + 1 /* turn floor into ceil */
+ + 2; /* account for leading sign or alternate form */
+ 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 */
+ break;
+
+ case 'f': case 'F':
+# ifdef HAVE_LONG_DOUBLE
+ if (type == TYPE_LONGDOUBLE)
+ tmp_length =
+ (unsigned int) (LDBL_MAX_EXP
+ * 0.30103 /* binary -> decimal */
+ * 2 /* estimate for FLAG_GROUP */
+ )
+ + 1 /* turn floor into ceil */
+ + 10; /* sign, decimal point etc. */
+ else
+# endif
+ tmp_length =
+ (unsigned int) (DBL_MAX_EXP
+ * 0.30103 /* binary -> decimal */
+ * 2 /* estimate for FLAG_GROUP */
+ )
+ + 1 /* turn floor into ceil */
+ + 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 =
+ 12; /* sign, decimal point, exponent etc. */
+ tmp_length = xsum (tmp_length, precision);
+ break;
+
+ case 'c':
+# if defined HAVE_WINT_T && !WIDE_CHAR_VERSION
+ if (type == TYPE_WIDE_CHAR)
+ tmp_length = MB_CUR_MAX;
+ else
+# endif
+ tmp_length = 1;
+ break;
+
+ case 's':
+# ifdef HAVE_WCHAR_T
+ if (type == TYPE_WIDE_STRING)
+ {
+ 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);
+ break;
+
+ case 'p':
+ tmp_length =
+ (unsigned int) (sizeof (void *) * CHAR_BIT
+ * 0.25 /* binary -> hexadecimal */
+ )
+ + 1 /* turn floor into ceil */
+ + 2; /* account for leading 0x */
+ break;
+
+ default:
+ abort ();
+ }
+
+ 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;
+ }
+#endif
+
+ /* Construct the format string for calling snprintf or
+ sprintf. */
+ p = buf;
+ *p++ = '%';
+ if (dp->flags & FLAG_GROUP)
+ *p++ = '\'';
+ if (dp->flags & FLAG_LEFT)
+ *p++ = '-';
+ if (dp->flags & FLAG_SHOWSIGN)
+ *p++ = '+';
+ if (dp->flags & FLAG_SPACE)
+ *p++ = ' ';
+ if (dp->flags & FLAG_ALT)
+ *p++ = '#';
+ if (dp->flags & FLAG_ZERO)
+ *p++ = '0';
+ if (dp->width_start != dp->width_end)
+ {
+ size_t n = dp->width_end - dp->width_start;
+ 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 * sizeof (CHAR_T));
+ p += n;
+ }
+
+ switch (type)
+ {
+#ifdef HAVE_LONG_LONG
+ case TYPE_LONGLONGINT:
+ case TYPE_ULONGLONGINT:
+ *p++ = 'l';
+ /*FALLTHROUGH*/
+#endif
+ case TYPE_LONGINT:
+ case TYPE_ULONGINT:
+#ifdef HAVE_WINT_T
+ case TYPE_WIDE_CHAR:
+#endif
+#ifdef HAVE_WCHAR_T
+ case TYPE_WIDE_STRING:
+#endif
+ *p++ = 'l';
+ break;
+#ifdef HAVE_LONG_DOUBLE
+ case TYPE_LONGDOUBLE:
+ *p++ = 'L';
+ break;
+#endif
+ default:
+ break;
+ }
+ *p = dp->conversion;
+#if USE_SNPRINTF
+ p[1] = '%';
+ p[2] = 'n';
+ p[3] = '\0';
+#else
+ p[1] = '\0';
+#endif
+
+ /* Construct the arguments for calling snprintf or sprintf. */
+ prefix_count = 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 != 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 USE_SNPRINTF
+ /* Prepare checking whether snprintf returns the count
+ via %n. */
+ ENSURE_ALLOCATION (xsum (length, 1));
+ result[length] = '\0';
+#endif
+
+ for (;;)
+ {
+ size_t maxlen;
+ int count;
+ int retcount;
+
+ maxlen = allocated - length;
+ count = -1;
+ retcount = 0;
+
+#if USE_SNPRINTF
+# define SNPRINTF_BUF(arg) \
+ switch (prefix_count) \
+ { \
+ case 0: \
+ retcount = SNPRINTF (result + length, maxlen, buf, \
+ arg, &count); \
+ break; \
+ case 1: \
+ retcount = SNPRINTF (result + length, maxlen, buf, \
+ prefixes[0], arg, &count); \
+ break; \
+ case 2: \
+ retcount = SNPRINTF (result + length, maxlen, buf, \
+ prefixes[0], prefixes[1], arg, \
+ &count); \
+ break; \
+ default: \
+ abort (); \
+ }
+#else
+# define SNPRINTF_BUF(arg) \
+ switch (prefix_count) \
+ { \
+ case 0: \
+ count = sprintf (tmp, buf, arg); \
+ break; \
+ case 1: \
+ count = sprintf (tmp, buf, prefixes[0], arg); \
+ break; \
+ case 2: \
+ count = sprintf (tmp, buf, prefixes[0], prefixes[1],\
+ arg); \
+ break; \
+ default: \
+ abort (); \
+ }
+#endif
+
+ switch (type)
+ {
+ case TYPE_SCHAR:
+ {
+ int arg = a.arg[dp->arg_index].a.a_schar;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_UCHAR:
+ {
+ unsigned int arg = a.arg[dp->arg_index].a.a_uchar;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_SHORT:
+ {
+ int arg = a.arg[dp->arg_index].a.a_short;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_USHORT:
+ {
+ unsigned int arg = a.arg[dp->arg_index].a.a_ushort;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_INT:
+ {
+ int arg = a.arg[dp->arg_index].a.a_int;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_UINT:
+ {
+ unsigned int arg = a.arg[dp->arg_index].a.a_uint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_LONGINT:
+ {
+ long int arg = a.arg[dp->arg_index].a.a_longint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_ULONGINT:
+ {
+ unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#ifdef HAVE_LONG_LONG
+ case TYPE_LONGLONGINT:
+ {
+ long long int arg = a.arg[dp->arg_index].a.a_longlongint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_ULONGLONGINT:
+ {
+ unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#endif
+ case TYPE_DOUBLE:
+ {
+ double arg = a.arg[dp->arg_index].a.a_double;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#ifdef HAVE_LONG_DOUBLE
+ case TYPE_LONGDOUBLE:
+ {
+ long double arg = a.arg[dp->arg_index].a.a_longdouble;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#endif
+ case TYPE_CHAR:
+ {
+ int arg = a.arg[dp->arg_index].a.a_char;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#ifdef HAVE_WINT_T
+ case TYPE_WIDE_CHAR:
+ {
+ wint_t arg = a.arg[dp->arg_index].a.a_wide_char;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#endif
+ case TYPE_STRING:
+ {
+ const char *arg = a.arg[dp->arg_index].a.a_string;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#ifdef HAVE_WCHAR_T
+ case TYPE_WIDE_STRING:
+ {
+ const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#endif
+ case TYPE_POINTER:
+ {
+ void *arg = a.arg[dp->arg_index].a.a_pointer;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ default:
+ abort ();
+ }
+
+#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
+ produced. */
+ if (count >= 0)
+ {
+ /* Verify that snprintf() has NUL-terminated its
+ result. */
+ if (count < maxlen && result[length + count] != '\0')
+ abort ();
+ /* Portability hack. */
+ if (retcount > count)
+ count = retcount;
+ }
+ else
+ {
+ /* snprintf() doesn't understand the '%n'
+ directive. */
+ if (p[1] != '\0')
+ {
+ /* Don't use the '%n' directive; instead, look
+ at the snprintf() return value. */
+ p[1] = '\0';
+ continue;
+ }
+ else
+ {
+ /* Look at the snprintf() return value. */
+ if (retcount < 0)
+ {
+ /* HP-UX 10.20 snprintf() is doubly deficient:
+ It doesn't understand the '%n' directive,
+ *and* it returns -1 (rather than the length
+ that would have been required) when the
+ buffer is too small. */
+ size_t bigger_need =
+ xsum (xtimes (allocated, 2), 12);
+ ENSURE_ALLOCATION (bigger_need);
+ continue;
+ }
+ else
+ count = retcount;
+ }
+ }
+#endif
+
+ /* Attempt to handle failure. */
+ if (count < 0)
+ {
+ if (!(result == resultbuf || result == NULL))
+ free (result);
+ if (buf_malloced != NULL)
+ free (buf_malloced);
+ CLEANUP ();
+ errno = EINVAL;
+ return NULL;
+ }
+
+#if !USE_SNPRINTF
+ if (count >= tmp_length)
+ /* tmp_length was incorrectly calculated - fix the
+ code above! */
+ abort ();
+#endif
+
+ /* Make room for the result. */
+ if (count >= maxlen)
+ {
+ /* Need at least count bytes. But allocate
+ proportionally, to avoid looping eternally if
+ snprintf() reports a too small count. */
+ size_t n =
+ xmax (xsum (length, count), xtimes (allocated, 2));
+
+ ENSURE_ALLOCATION (n);
+#if USE_SNPRINTF
+ continue;
+#endif
+ }
+
+#if USE_SNPRINTF
+ /* The snprintf() result did fit. */
+#else
+ /* Append the sprintf() result. */
+ memcpy (result + length, tmp, count * sizeof (CHAR_T));
+ if (tmp != tmpbuf)
+ free (tmp);
+#endif
+
+ length += count;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Add the final NUL. */
+ ENSURE_ALLOCATION (xsum (length, 1));
+ result[length] = '\0';
+
+ if (result != resultbuf && length + 1 < allocated)
+ {
+ /* Shrink the allocated memory if possible. */
+ CHAR_T *memory;
+
+ memory = (CHAR_T *) realloc (result, (length + 1) * sizeof (CHAR_T));
+ if (memory != NULL)
+ result = memory;
+ }
+
+ if (buf_malloced != NULL)
+ free (buf_malloced);
+ CLEANUP ();
+ *lengthp = length;
+ 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