New module 'mbsrchr'.
[gnulib.git] / lib / vasnprintf.c
index c0754ea..67406ae 100644 (file)
@@ -1,5 +1,5 @@
 /* vsprintf with automatic memory allocation.
-   Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2002-2007 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
@@ -22,9 +22,7 @@
 # define _GNU_SOURCE    1
 #endif
 
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
 #ifndef IN_LIBINTL
 # include <alloca.h>
 #endif
@@ -51,8 +49,8 @@
 /* Checked size_t computations.  */
 #include "xsize.h"
 
-#ifdef HAVE_WCHAR_T
-# ifdef HAVE_WCSLEN
+#if HAVE_WCHAR_T
+# if HAVE_WCSLEN
 #  define local_wcslen wcslen
 # else
    /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid
@@ -250,7 +248,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                  case TYPE_COUNT_LONGINT_POINTER:
                    *a.arg[dp->arg_index].a.a_count_longint_pointer = length;
                    break;
-#ifdef HAVE_LONG_LONG
+#if HAVE_LONG_LONG_INT
                  case TYPE_COUNT_LONGLONGINT_POINTER:
                    *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length;
                    break;
@@ -315,9 +313,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);
                        }
                    }
 
@@ -325,44 +322,43 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                    {
 
                    case 'd': case 'i': case 'u':
-# ifdef HAVE_LONG_LONG
+# if 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 */
-                                         * 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':
-# ifdef HAVE_LONG_LONG
+# if 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 */
-                         + 1; /* account for leading sign */
+                         + 1; /* turn floor into ceil */
                      else
 # endif
                      if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
@@ -370,26 +366,27 @@ 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':
-# ifdef HAVE_LONG_LONG
+# if 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 */
-                         + 2; /* account for leading sign or alternate form */
+                         + 1; /* turn floor into ceil */
                      else
 # endif
                      if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
@@ -397,19 +394,21 @@ 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':
-# ifdef HAVE_LONG_DOUBLE
+# if HAVE_LONG_DOUBLE
                      if (type == TYPE_LONGDOUBLE)
                        tmp_length =
                          (unsigned int) (LDBL_MAX_EXP
@@ -438,7 +437,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                      break;
 
                    case 'c':
-# if defined HAVE_WINT_T && !WIDE_CHAR_VERSION
+# if HAVE_WINT_T && !WIDE_CHAR_VERSION
                      if (type == TYPE_WIDE_CHAR)
                        tmp_length = MB_CUR_MAX;
                      else
@@ -447,7 +446,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                      break;
 
                    case 's':
-# ifdef HAVE_WCHAR_T
+# if HAVE_WCHAR_T
                      if (type == TYPE_WIDE_STRING)
                        {
                          tmp_length =
@@ -528,7 +527,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
 
                switch (type)
                  {
-#ifdef HAVE_LONG_LONG
+#if HAVE_LONG_LONG_INT
                  case TYPE_LONGLONGINT:
                  case TYPE_ULONGLONGINT:
                    *p++ = 'l';
@@ -536,15 +535,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
 #endif
                  case TYPE_LONGINT:
                  case TYPE_ULONGINT:
-#ifdef HAVE_WINT_T
+#if HAVE_WINT_T
                  case TYPE_WIDE_CHAR:
 #endif
-#ifdef HAVE_WCHAR_T
+#if HAVE_WCHAR_T
                  case TYPE_WIDE_STRING:
 #endif
                    *p++ = 'l';
                    break;
-#ifdef HAVE_LONG_DOUBLE
+#if HAVE_LONG_DOUBLE
                  case TYPE_LONGDOUBLE:
                    *p++ = 'L';
                    break;
@@ -682,7 +681,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          SNPRINTF_BUF (arg);
                        }
                        break;
-#ifdef HAVE_LONG_LONG
+#if HAVE_LONG_LONG_INT
                      case TYPE_LONGLONGINT:
                        {
                          long long int arg = a.arg[dp->arg_index].a.a_longlongint;
@@ -702,7 +701,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          SNPRINTF_BUF (arg);
                        }
                        break;
-#ifdef HAVE_LONG_DOUBLE
+#if HAVE_LONG_DOUBLE
                      case TYPE_LONGDOUBLE:
                        {
                          long double arg = a.arg[dp->arg_index].a.a_longdouble;
@@ -716,7 +715,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          SNPRINTF_BUF (arg);
                        }
                        break;
-#ifdef HAVE_WINT_T
+#if HAVE_WINT_T
                      case TYPE_WIDE_CHAR:
                        {
                          wint_t arg = a.arg[dp->arg_index].a.a_wide_char;
@@ -730,7 +729,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          SNPRINTF_BUF (arg);
                        }
                        break;
-#ifdef HAVE_WCHAR_T
+#if HAVE_WCHAR_T
                      case TYPE_WIDE_STRING:
                        {
                          const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string;
@@ -863,6 +862,10 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
       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: