The R Project SVN R

Rev

Rev 41539 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/* vsprintf with automatic memory allocation.
   Copyright (C) 1999, 2002-2006 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_INT
          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;
              while (digitp != dp->precision_end)
                precision = xsum (xtimes (precision, 10), *digitp++ - '0');
            }
            }

          switch (dp->conversion)
            {

            case 'd': case 'i': case 'u':
# ifdef 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 */
                     )
              + 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 */
                     )
              + 1; /* turn floor into ceil */
              else
            tmp_length =
              (unsigned int) (sizeof (unsigned int) * CHAR_BIT
                      * 0.30103 /* binary -> decimal */
                     )
              + 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_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 */
              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 */
              else
            tmp_length =
              (unsigned int) (sizeof (unsigned int) * CHAR_BIT
                      * 0.333334 /* binary -> octal */
                     )
              + 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_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 */
              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 */
              else
            tmp_length =
              (unsigned int) (sizeof (unsigned int) * CHAR_BIT
                      * 0.25 /* binary -> hexadecimal */
                     )
              + 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 (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_INT
          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_INT
              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;
    /* 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