* doc/headers/assert.texi (assert.h): Document assert module use.
[gnulib.git] / lib / vasnprintf.c
index 8af1063..e11f1e9 100644 (file)
 # include "vasnprintf.h"
 #endif
 
+#include <locale.h>    /* localeconv() */
 #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 HAVE_NL_LANGINFO
+# include <langinfo.h>
+#endif
 #if WIDE_CHAR_VERSION
 # include "wprintf-parse.h"
 #else
 #include "xsize.h"
 
 #if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL
+# include <math.h>
 # include "isnan.h"
-# include "isnanl.h"
-# if HAVE_LONG_DOUBLE
-#  include "printf-frexp.h"
-#  include "printf-frexpl.h"
-# endif
+# include "printf-frexp.h"
+# include "isnanl-nolibm.h"
+# include "printf-frexpl.h"
+# include "fpucw.h"
+#endif
+
+/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW.  */
+#ifndef EOVERFLOW
+# define EOVERFLOW E2BIG
 #endif
 
 #if HAVE_WCHAR_T
@@ -109,6 +118,37 @@ local_wcslen (const wchar_t *s)
 # else
    /* Unix.  */
 #  define SNPRINTF snprintf
+   /* Here we need to call the native snprintf, not rpl_snprintf.  */
+#  undef snprintf
+# endif
+#endif
+/* Here we need to call the native sprintf, not rpl_sprintf.  */
+#undef sprintf
+
+#if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL
+/* Determine the decimal-point character according to the current locale.  */
+# ifndef decimal_point_char_defined
+#  define decimal_point_char_defined 1
+static char
+decimal_point_char ()
+{
+  const char *point;
+  /* Determine it in a multithread-safe way.  We know nl_langinfo is
+     multithread-safe on glibc systems, but is not required to be multithread-
+     safe by POSIX.  sprintf(), however, is multithread-safe.  localeconv()
+     is rarely multithread-safe.  */
+#  if HAVE_NL_LANGINFO && __GLIBC__
+  point = nl_langinfo (RADIXCHAR);
+#  elif 1
+  char pointbuf[5];
+  sprintf (pointbuf, "%#.0f", 1.0);
+  point = &pointbuf[1];
+#  else
+  point = localeconv () -> decimal_point;
+#  endif
+  /* The decimal point is always a single byte: either '.' or ','.  */
+  return (point[0] != '\0' ? point[0] : '.');
+}
 # endif
 #endif
 
@@ -344,7 +384,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                  }
 
                /* Allocate a temporary buffer of sufficient size.  */
-# if HAVE_LONG_DOUBLE
                if (type == TYPE_LONGDOUBLE)
                  tmp_length =
                    (unsigned int) ((LDBL_DIG + 1)
@@ -352,7 +391,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                                   )
                    + 1; /* turn floor into ceil */
                else
-# endif
                  tmp_length =
                    (unsigned int) ((DBL_DIG + 1)
                                    * 0.831 /* decimal -> hexadecimal */
@@ -385,7 +423,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
 
                pad_ptr = NULL;
                p = tmp;
-# if HAVE_LONG_DOUBLE
                if (type == TYPE_LONGDOUBLE)
                  {
                    long double arg = a.arg[dp->arg_index].a.a_longdouble;
@@ -404,23 +441,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                    else
                      {
                        int sign = 0;
+                       DECL_LONG_DOUBLE_ROUNDING
+
+                       BEGIN_LONG_DOUBLE_ROUNDING ();
 
-                       if (arg < 0.0L)
+                       if (signbit (arg)) /* arg < 0.0L or negative zero */
                          {
                            sign = -1;
                            arg = -arg;
                          }
-                       else if (arg == 0.0L)
-                         {
-                           /* Distinguish 0.0L and -0.0L.  */
-                           static long double plus_zero = 0.0L;
-                           long double arg_mem = arg;
-                           if (memcmp (&plus_zero, &arg_mem, sizeof (long double)) != 0)
-                             {
-                               sign = -1;
-                               arg = -arg;
-                             }
-                         }
 
                        if (sign < 0)
                          *p++ = '-';
@@ -429,7 +458,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                        else if (flags & FLAG_SPACE)
                          *p++ = ' ';
 
-                       if (x > 0.0L && x + x == x)
+                       if (arg > 0.0L && arg + arg == arg)
                          {
                            if (dp->conversion == 'A')
                              {
@@ -445,7 +474,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                            int exponent;
                            long double mantissa;
 
-                           if (x > 0.0L)
+                           if (arg > 0.0L)
                              mantissa = printf_frexpl (arg, &exponent);
                            else
                              {
@@ -457,7 +486,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                                && precision < (unsigned int) ((LDBL_DIG + 1) * 0.831) + 1)
                              {
                                /* Round the mantissa.  */
-                               long double tail = arg;
+                               long double tail = mantissa;
                                size_t q;
 
                                for (q = precision; ; q--)
@@ -469,15 +498,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                                        if (digit & 1 ? tail >= 0.5L : tail > 0.5L)
                                          tail = 1 - tail;
                                        else
-                                         tail = 0;
+                                         tail = - tail;
                                        break;
                                      }
                                    tail *= 16.0L;
                                  }
-                               if (tail > 0.0L)
+                               if (tail != 0.0L)
                                  for (q = precision; q > 0; q--)
                                    tail *= 0.0625L;
-                               arg += tail;
+                               mantissa += tail;
                              }
 
                            *p++ = '0';
@@ -486,19 +515,20 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                            {
                              int digit;
 
-                             digit = (int) arg;
-                             arg -= digit;
+                             digit = (int) mantissa;
+                             mantissa -= digit;
                              *p++ = '0' + digit;
-                             if ((flags & FLAG_ALT) || arg > 0.0L)
+                             if ((flags & FLAG_ALT)
+                                 || mantissa > 0.0L || precision > 0)
                                {
-                                 *p++ = '.';
+                                 *p++ = decimal_point_char ();
                                  /* This loop terminates because we assume
                                     that FLT_RADIX is a power of 2.  */
-                                 while (arg > 0.0L)
+                                 while (mantissa > 0.0L)
                                    {
-                                     arg *= 16.0L;
-                                     digit = (int) arg;
-                                     arg -= digit;
+                                     mantissa *= 16.0L;
+                                     digit = (int) mantissa;
+                                     mantissa -= digit;
                                      *p++ = digit
                                             + (digit < 10
                                                ? '0'
@@ -514,22 +544,23 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                                }
                              }
                              *p++ = dp->conversion - 'A' + 'P';
-#  if WIDE_CHAR_VERSION
+# if WIDE_CHAR_VERSION
                              {
                                static const wchar_t decimal_format[] =
-                                 { '%', 'd', '\0' };
+                                 { '%', '+', 'd', '\0' };
                                SNPRINTF (p, 6 + 1, decimal_format, exponent);
                              }
-#  else
-                             sprintf (p, "%d", exponent);
-#  endif
+# else
+                             sprintf (p, "%+d", exponent);
+# endif
                              while (*p != '\0')
                                p++;
                          }
+
+                       END_LONG_DOUBLE_ROUNDING ();
                      }
                  }
                else
-# endif
                  {
                    double arg = a.arg[dp->arg_index].a.a_double;
 
@@ -548,22 +579,11 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                      {
                        int sign = 0;
 
-                       if (arg < 0.0)
+                       if (signbit (arg)) /* arg < 0.0 or negative zero */
                          {
                            sign = -1;
                            arg = -arg;
                          }
-                       else if (arg == 0.0)
-                         {
-                           /* Distinguish 0.0 and -0.0.  */
-                           static double plus_zero = 0.0;
-                           double arg_mem = arg;
-                           if (memcmp (&plus_zero, &arg_mem, sizeof (double)) != 0)
-                             {
-                               sign = -1;
-                               arg = -arg;
-                             }
-                         }
 
                        if (sign < 0)
                          *p++ = '-';
@@ -572,7 +592,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                        else if (flags & FLAG_SPACE)
                          *p++ = ' ';
 
-                       if (x > 0.0 && x + x == x)
+                       if (arg > 0.0 && arg + arg == arg)
                          {
                            if (dp->conversion == 'A')
                              {
@@ -588,7 +608,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                            int exponent;
                            double mantissa;
 
-                           if (x > 0.0)
+                           if (arg > 0.0)
                              mantissa = printf_frexp (arg, &exponent);
                            else
                              {
@@ -600,7 +620,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                                && precision < (unsigned int) ((DBL_DIG + 1) * 0.831) + 1)
                              {
                                /* Round the mantissa.  */
-                               double tail = arg;
+                               double tail = mantissa;
                                size_t q;
 
                                for (q = precision; ; q--)
@@ -612,15 +632,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                                        if (digit & 1 ? tail >= 0.5 : tail > 0.5)
                                          tail = 1 - tail;
                                        else
-                                         tail = 0;
+                                         tail = - tail;
                                        break;
                                      }
                                    tail *= 16.0;
                                  }
-                               if (tail > 0.0)
+                               if (tail != 0.0)
                                  for (q = precision; q > 0; q--)
                                    tail *= 0.0625;
-                               arg += tail;
+                               mantissa += tail;
                              }
 
                            *p++ = '0';
@@ -629,19 +649,20 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                            {
                              int digit;
 
-                             digit = (int) arg;
-                             arg -= digit;
+                             digit = (int) mantissa;
+                             mantissa -= digit;
                              *p++ = '0' + digit;
-                             if ((flags & FLAG_ALT) || arg > 0.0)
+                             if ((flags & FLAG_ALT)
+                                 || mantissa > 0.0 || precision > 0)
                                {
-                                 *p++ = '.';
+                                 *p++ = decimal_point_char ();
                                  /* This loop terminates because we assume
                                     that FLT_RADIX is a power of 2.  */
-                                 while (arg > 0.0)
+                                 while (mantissa > 0.0)
                                    {
-                                     arg *= 16.0;
-                                     digit = (int) arg;
-                                     arg -= digit;
+                                     mantissa *= 16.0;
+                                     digit = (int) mantissa;
+                                     mantissa -= digit;
                                      *p++ = digit
                                             + (digit < 10
                                                ? '0'
@@ -660,11 +681,11 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
 # if WIDE_CHAR_VERSION
                              {
                                static const wchar_t decimal_format[] =
-                                 { '%', 'd', '\0' };
+                                 { '%', '+', 'd', '\0' };
                                SNPRINTF (p, 6 + 1, decimal_format, exponent);
                              }
 # else
-                             sprintf (p, "%d", exponent);
+                             sprintf (p, "%+d", exponent);
 # endif
                              while (*p != '\0')
                                p++;
@@ -883,7 +904,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                      break;
 
                    case 'f': case 'F':
-# if HAVE_LONG_DOUBLE
                      if (type == TYPE_LONGDOUBLE)
                        tmp_length =
                          (unsigned int) (LDBL_MAX_EXP
@@ -893,7 +913,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          + 1 /* turn floor into ceil */
                          + 10; /* sign, decimal point etc. */
                      else
-# endif
                        tmp_length =
                          (unsigned int) (DBL_MAX_EXP
                                          * 0.30103 /* binary -> decimal */
@@ -911,7 +930,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                      break;
 
                    case 'a': case 'A':
-# if HAVE_LONG_DOUBLE
                      if (type == TYPE_LONGDOUBLE)
                        tmp_length =
                          (unsigned int) (LDBL_DIG
@@ -919,7 +937,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                                         )
                          + 1; /* turn floor into ceil */
                      else
-# endif
                        tmp_length =
                          (unsigned int) (DBL_DIG
                                          * 0.831 /* decimal -> hexadecimal */
@@ -1038,15 +1055,18 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
 #endif
                    *p++ = 'l';
                    break;
-#if HAVE_LONG_DOUBLE
                  case TYPE_LONGDOUBLE:
                    *p++ = 'L';
                    break;
-#endif
                  default:
                    break;
                  }
-               *p = dp->conversion;
+#if NEED_PRINTF_DIRECTIVE_F
+               if (dp->conversion == 'F')
+                 *p = 'f';
+               else
+#endif
+                 *p = dp->conversion;
 #if USE_SNPRINTF
                p[1] = '%';
                p[2] = 'n';
@@ -1088,6 +1108,9 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                    retcount = 0;
 
 #if USE_SNPRINTF
+                   /* SNPRINTF can fail if maxlen > INT_MAX.  */
+                   if (maxlen > INT_MAX)
+                     goto overflow;
 # define SNPRINTF_BUF(arg) \
                    switch (prefix_count)                                   \
                      {                                                     \
@@ -1196,14 +1219,12 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                          SNPRINTF_BUF (arg);
                        }
                        break;
-#if 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;
@@ -1332,6 +1353,18 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                      free (tmp);
 #endif
 
+#if NEED_PRINTF_DIRECTIVE_F
+                   if (dp->conversion == 'F')
+                     {
+                       /* Convert the %f result to upper case for %F.  */
+                       CHAR_T *rp = result + length;
+                       size_t rc;
+                       for (rc = count; rc > 0; rc--, rp++)
+                         if (*rp >= 'a' && *rp <= 'z')
+                           *rp = *rp - 'a' + 'A';
+                     }
+#endif
+
                    length += count;
                    break;
                  }
@@ -1363,6 +1396,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
        not have this limitation.  */
     return result;
 
+  overflow:
+    if (!(result == resultbuf || result == NULL))
+      free (result);
+    if (buf_malloced != NULL)
+      free (buf_malloced);
+    CLEANUP ();
+    errno = EOVERFLOW;
+    return NULL;
+
   out_of_memory:
     if (!(result == resultbuf || result == NULL))
       free (result);