Logo Search packages:      
Sourcecode: coreutils version File versions

vasnprintf.c

/* 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 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 General Public License for more details.

   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.  */

/* 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
#include <alloca.h>

/* Specification.  */
#include "vasnprintf.h"

#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 */
#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 */
#else
# define alloca(n) malloc (n)
# define freea(p) free (p)
#endif

#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.  */
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

char *
vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args)
{
  char_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;
    }

  {
    char *buf =
      (char *) alloca (7 + d.max_width_length + d.max_precision_length + 6);
    const char *cp;
    unsigned int i;
    char_directive *dp;
    /* Output string accumulator.  */
    char *result;
    size_t allocated;
    size_t length;

    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.  */

#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;                                \
      }

    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;

          ENSURE_ALLOCATION (length + n);
          memcpy (result + length, cp, n);
          length += n;
        }
      if (i == d.count)
        break;

      /* Execute a single directive.  */
      if (dp->conversion == '%')
        {
          if (!(dp->arg_index < 0))
            abort ();
          ENSURE_ALLOCATION (length + 1);
          result[length] = '%';
          length += 1;
        }
      else
        {
          if (!(dp->arg_index >= 0))
            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 *p;
            unsigned int prefix_count;
            int prefixes[2];
#if !HAVE_SNPRINTF
            unsigned int tmp_length;
            char tmpbuf[700];
            char *tmp;

            /* Allocate a temporary buffer of sufficient size for calling
               sprintf.  */
            {
              unsigned int width;
              unsigned int precision;

              width = 0;
              if (dp->width_start != dp->width_end)
                {
                  if (dp->width_arg_index >= 0)
                  {
                    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);
                  }
                  else
                  {
                    const char *digitp = dp->width_start;

                    do
                      width = width * 10 + (*digitp++ - '0');
                    while (digitp != dp->width_end);
                  }
                }

              precision = 6;
              if (dp->precision_start != dp->precision_end)
                {
                  if (dp->precision_arg_index >= 0)
                  {
                    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 *digitp = dp->precision_start + 1;

                    precision = 0;
                    do
                      precision = 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 */
                    + precision
                    + 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 */
                    + precision
                    + 10; /* sign, decimal point etc. */
                  break;

                case 'e': case 'E': case 'g': case 'G':
                case 'a': case 'A':
                  tmp_length =
                  precision
                  + 12; /* sign, decimal point, exponent etc. */
                  break;

                case 'c':
# ifdef HAVE_WINT_T
                  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)
                    * MB_CUR_MAX;
                  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++; /* account for trailing NUL */
            }

            if (tmp_length <= sizeof (tmpbuf))
              tmp = tmpbuf;
            else
              {
                tmp = (char *) malloc (tmp_length);
                if (tmp == NULL)
                  {
                  /* Out of memory.  */
                  if (!(result == resultbuf || result == NULL))
                    free (result);
                  freea (buf);
                  CLEANUP ();
                  errno = ENOMEM;
                  return NULL;
                  }
              }
#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);
                p += n;
              }
            if (dp->precision_start != dp->precision_end)
              {
                size_t n = dp->precision_end - dp->precision_start;
                memcpy (p, dp->precision_start, n);
                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 HAVE_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 >= 0)
              {
                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 (!(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
            /* Prepare checking whether snprintf returns the count
               via %n.  */
            ENSURE_ALLOCATION (length + 1);
            result[length] = '\0';
#endif

            for (;;)
              {
                size_t maxlen;
                int count;
                int retcount;

                maxlen = allocated - length;
                count = -1;
                retcount = 0;

#if HAVE_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 HAVE_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 if (retcount < 0)
                    {
                      /* The system's snprintf is sorely deficient:
                         it doesn't recognize the `%n' directive, and it
                         returns -1 (rather than the length that would
                         have been required) when the buffer is too small.
                         This is the case at with least HPUX 10.20.
                         Double the memory allocation.  */
                      size_t n = allocated;
                      if (n < 2 * allocated)
                        {
                        n = 2 * allocated;
                        ENSURE_ALLOCATION (n);
                        continue;
                        }
                    }
                  count = retcount;
                  }
#endif

                /* Attempt to handle failure.  */
                if (count < 0)
                  {
                  if (!(result == resultbuf || result == NULL))
                    free (result);
                  freea (buf);
                  CLEANUP ();
                  errno = EINVAL;
                  return NULL;
                  }

#if !HAVE_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 = length + count;

                  if (n < 2 * allocated)
                    n = 2 * allocated;

                  ENSURE_ALLOCATION (n);
#if HAVE_SNPRINTF
                  continue;
#endif
                  }

#if HAVE_SNPRINTF
                /* The snprintf() result did fit.  */
#else
                /* Append the sprintf() result.  */
                memcpy (result + length, tmp, count);
                if (tmp != tmpbuf)
                  free (tmp);
#endif

                length += count;
                break;
              }
            }
        }
      }

    /* Add the final NUL.  */
    ENSURE_ALLOCATION (length + 1);
    result[length] = '\0';

    if (result != resultbuf && length + 1 < allocated)
      {
      /* Shrink the allocated memory if possible.  */
      char *memory;

      memory = (char *) realloc (result, length + 1);
      if (memory != NULL)
        result = memory;
      }

    freea (buf);
    CLEANUP ();
    *lengthp = length;
    return result;
  }
}

Generated by  Doxygen 1.6.0   Back to index