Normalize inclusion of `config.h'.
[gnulib.git] / lib / vasnprintf.c
index 380170d..3b496d2 100644 (file)
@@ -1,5 +1,5 @@
 /* vsprintf with automatic memory allocation.
-   Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
+   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 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 <stdio.h> to provide a prototype for snprintf().
    This must come before <config.h> because <config.h> may include
@@ -40,7 +40,7 @@
 #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"
 /* Checked size_t computations.  */
 #include "xsize.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)
+/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW.  */
+#ifndef EOVERFLOW
+# define EOVERFLOW E2BIG
 #endif
 
 #ifdef HAVE_WCHAR_T
@@ -139,10 +135,9 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
     }
 
   {
-    size_t buf_neededlength =
-      xsum4 (7, d.max_width_length, d.max_precision_length, 6);
-    CHAR_T *buf =
-      (CHAR_T *) alloca (xtimes (buf_neededlength, sizeof (CHAR_T)));
+    size_t buf_neededlength;
+    CHAR_T *buf;
+    CHAR_T *buf_malloced;
     const CHAR_T *cp;
     size_t i;
     DIRECTIVE *dp;
@@ -151,6 +146,28 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
     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;
@@ -210,7 +227,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
          {
            size_t augmented_length;
 
-           if (!(dp->arg_index < 0))
+           if (!(dp->arg_index == ARG_NONE))
              abort ();
            augmented_length = xsum (length, 1);
            ENSURE_ALLOCATION (augmented_length);
@@ -219,7 +236,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
          }
        else
          {
-           if (!(dp->arg_index >= 0))
+           if (!(dp->arg_index != ARG_NONE))
              abort ();
 
            if (dp->conversion == 'n')
@@ -267,7 +284,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                  width = 0;
                  if (dp->width_start != dp->width_end)
                    {
-                     if (dp->width_arg_index >= 0)
+                     if (dp->width_arg_index != ARG_NONE)
                        {
                          int arg;
 
@@ -289,7 +306,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                  precision = 6;
                  if (dp->precision_start != dp->precision_end)
                    {
-                     if (dp->precision_arg_index >= 0)
+                     if (dp->precision_arg_index != ARG_NONE)
                        {
                          int arg;
 
@@ -303,9 +320,8 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          const CHAR_T *digitp = dp->precision_start + 1;
 
                          precision = 0;
-                         do
+                         while (digitp != dp->precision_end)
                            precision = xsum (xtimes (precision, 10), *digitp++ - '0');
-                         while (digitp != dp->precision_end);
                        }
                    }
 
@@ -318,28 +334,28 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                        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':
@@ -349,8 +365,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          (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)
@@ -358,15 +373,17 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          (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':
@@ -376,8 +393,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          (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)
@@ -385,15 +401,17 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          (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':
@@ -551,13 +569,13 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
 
                /* 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 ();
@@ -788,7 +806,8 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                      {
                        if (!(result == resultbuf || result == NULL))
                          free (result);
-                       freea (buf);
+                       if (buf_malloced != NULL)
+                         free (buf_malloced);
                        CLEANUP ();
                        errno = EINVAL;
                        return NULL;
@@ -846,15 +865,29 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
          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);
-    freea (buf);
+    if (buf_malloced != NULL)
+      free (buf_malloced);
+  out_of_memory_1:
     CLEANUP ();
     errno = ENOMEM;
     return NULL;