* lib/fcntl_.h: Prefer #include_next <foo.h> to #include
[gnulib.git] / lib / vasnprintf.c
index b13dbd5..45a2199 100644 (file)
@@ -41,6 +41,9 @@
 #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
@@ -117,6 +125,33 @@ local_wcslen (const wchar_t *s)
 /* 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
+
 CHAR_T *
 VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list args)
 {
@@ -349,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)
@@ -357,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 */
@@ -390,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;
@@ -409,25 +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;
-                           memset (&arg_mem, 0, sizeof (long double));
-                           arg_mem = arg;
-                           if (memcmp (&plus_zero, &arg_mem, sizeof (long double)) != 0)
-                             {
-                               sign = -1;
-                               arg = -arg;
-                             }
-                         }
 
                        if (sign < 0)
                          *p++ = '-';
@@ -499,11 +521,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                              if ((flags & FLAG_ALT)
                                  || mantissa > 0.0L || precision > 0)
                                {
-                                 const char *point =
-                                   localeconv () -> decimal_point;
-                                 /* The decimal point is always a single byte:
-                                    either '.' or ','.  */
-                                 *p++ = (point[0] != '\0' ? point[0] : '.');
+                                 *p++ = decimal_point_char ();
                                  /* This loop terminates because we assume
                                     that FLT_RADIX is a power of 2.  */
                                  while (mantissa > 0.0L)
@@ -526,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' };
                                SNPRINTF (p, 6 + 1, decimal_format, exponent);
                              }
-#  else
+# else
                              sprintf (p, "%+d", exponent);
-#  endif
+# endif
                              while (*p != '\0')
                                p++;
                          }
+
+                       END_LONG_DOUBLE_ROUNDING ();
                      }
                  }
                else
-# endif
                  {
                    double arg = a.arg[dp->arg_index].a.a_double;
 
@@ -560,24 +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;
-                           memset (&arg_mem, 0, sizeof (double));
-                           arg_mem = arg;
-                           if (memcmp (&plus_zero, &arg_mem, sizeof (double)) != 0)
-                             {
-                               sign = -1;
-                               arg = -arg;
-                             }
-                         }
 
                        if (sign < 0)
                          *p++ = '-';
@@ -649,11 +655,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                              if ((flags & FLAG_ALT)
                                  || mantissa > 0.0 || precision > 0)
                                {
-                                 const char *point =
-                                   localeconv () -> decimal_point;
-                                 /* The decimal point is always a single byte:
-                                    either '.' or ','.  */
-                                 *p++ = (point[0] != '\0' ? point[0] : '.');
+                                 *p++ = decimal_point_char ();
                                  /* This loop terminates because we assume
                                     that FLT_RADIX is a power of 2.  */
                                  while (mantissa > 0.0)
@@ -754,42 +756,65 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
            else
              {
                arg_type type = a.arg[dp->arg_index].type;
-               CHAR_T *p;
+               int flags = dp->flags;
+#if !USE_SNPRINTF || NEED_PRINTF_FLAG_ZERO
+               int has_width;
+               size_t width;
+#endif
+#if NEED_PRINTF_FLAG_ZERO
+               int pad_ourselves;
+#else
+#              define pad_ourselves 0
+#endif
+               CHAR_T *fbp;
                unsigned int prefix_count;
                int prefixes[2];
 #if !USE_SNPRINTF
                size_t tmp_length;
                CHAR_T tmpbuf[700];
                CHAR_T *tmp;
+#endif
+
+#if !USE_SNPRINTF || NEED_PRINTF_FLAG_ZERO
+               has_width = 0;
+               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;
+                       if (arg < 0)
+                         {
+                           /* "A negative field width is taken as a '-' flag
+                               followed by a positive field width."  */
+                           flags |= FLAG_LEFT;
+                           width = (unsigned int) (-arg);
+                         }
+                       else
+                         width = arg;
+                     }
+                   else
+                     {
+                       const CHAR_T *digitp = dp->width_start;
+
+                       do
+                         width = xsum (xtimes (width, 10), *digitp++ - '0');
+                       while (digitp != dp->width_end);
+                     }
+                   has_width = 1;
+                 }
+#endif
 
+#if !USE_SNPRINTF
                /* 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)
                    {
@@ -902,7 +927,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
@@ -912,7 +936,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 */
@@ -930,7 +953,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
@@ -938,7 +960,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 */
@@ -1010,33 +1031,56 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                  }
 #endif
 
+               /* Decide whether to perform the padding ourselves.  */
+#if NEED_PRINTF_FLAG_ZERO
+               switch (dp->conversion)
+                 {
+                 case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
+                 case 'a': case 'A':
+                   pad_ourselves = 1;
+                   break;
+                 default:
+                   pad_ourselves = 0;
+                   break;
+                 }
+#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)
+               fbp = buf;
+               *fbp++ = '%';
+#if NEED_PRINTF_FLAG_GROUPING
+               /* The underlying implementation doesn't support the ' flag.
+                  Produce no grouping characters in this case; this is
+                  acceptable because the grouping is locale dependent.  */
+#else
+               if (flags & FLAG_GROUP)
+                 *fbp++ = '\'';
+#endif
+               if (flags & FLAG_LEFT)
+                 *fbp++ = '-';
+               if (flags & FLAG_SHOWSIGN)
+                 *fbp++ = '+';
+               if (flags & FLAG_SPACE)
+                 *fbp++ = ' ';
+               if (flags & FLAG_ALT)
+                 *fbp++ = '#';
+               if (!pad_ourselves)
                  {
-                   size_t n = dp->width_end - dp->width_start;
-                   memcpy (p, dp->width_start, n * sizeof (CHAR_T));
-                   p += n;
+                   if (flags & FLAG_ZERO)
+                     *fbp++ = '0';
+                   if (dp->width_start != dp->width_end)
+                     {
+                       size_t n = dp->width_end - dp->width_start;
+                       memcpy (fbp, dp->width_start, n * sizeof (CHAR_T));
+                       fbp += 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;
+                   memcpy (fbp, dp->precision_start, n * sizeof (CHAR_T));
+                   fbp += n;
                  }
 
                switch (type)
@@ -1044,7 +1088,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
 #if HAVE_LONG_LONG_INT
                  case TYPE_LONGLONGINT:
                  case TYPE_ULONGLONGINT:
-                   *p++ = 'l';
+                   *fbp++ = 'l';
                    /*FALLTHROUGH*/
 #endif
                  case TYPE_LONGINT:
@@ -1055,28 +1099,31 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
 #if HAVE_WCHAR_T
                  case TYPE_WIDE_STRING:
 #endif
-                   *p++ = 'l';
+                   *fbp++ = 'l';
                    break;
-#if HAVE_LONG_DOUBLE
                  case TYPE_LONGDOUBLE:
-                   *p++ = 'L';
+                   *fbp++ = 'L';
                    break;
-#endif
                  default:
                    break;
                  }
-               *p = dp->conversion;
+#if NEED_PRINTF_DIRECTIVE_F
+               if (dp->conversion == 'F')
+                 *fbp = 'f';
+               else
+#endif
+                 *fbp = dp->conversion;
 #if USE_SNPRINTF
-               p[1] = '%';
-               p[2] = 'n';
-               p[3] = '\0';
+               fbp[1] = '%';
+               fbp[2] = 'n';
+               fbp[3] = '\0';
 #else
-               p[1] = '\0';
+               fbp[1] = '\0';
 #endif
 
                /* Construct the arguments for calling snprintf or sprintf.  */
                prefix_count = 0;
-               if (dp->width_arg_index != ARG_NONE)
+               if (!pad_ourselves && dp->width_arg_index != ARG_NONE)
                  {
                    if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
                      abort ();
@@ -1107,6 +1154,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)                                   \
                      {                                                     \
@@ -1215,14 +1265,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;
@@ -1280,11 +1328,11 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                      {
                        /* snprintf() doesn't understand the '%n'
                           directive.  */
-                       if (p[1] != '\0')
+                       if (fbp[1] != '\0')
                          {
                            /* Don't use the '%n' directive; instead, look
                               at the snprintf() return value.  */
-                           p[1] = '\0';
+                           fbp[1] = '\0';
                            continue;
                          }
                        else
@@ -1320,6 +1368,77 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar
                        return NULL;
                      }
 
+                   /* Perform padding.  */
+#if NEED_PRINTF_FLAG_ZERO
+                   if (pad_ourselves && has_width && count < width)
+                     {
+# if USE_SNPRINTF
+                       /* Make room for the result.  */
+                       if (width >= maxlen)
+                         {
+                           /* Need at least width bytes.  But allocate
+                              proportionally, to avoid looping eternally if
+                              snprintf() reports a too small count.  */
+                           size_t n =
+                             xmax (xsum (length, width),
+                                   xtimes (allocated, 2));
+
+                           length += count;
+                           ENSURE_ALLOCATION (n);
+                           length -= count;
+                           maxlen = allocated - length; /* >= width */
+                         }
+# endif
+                       {
+# if USE_SNPRINTF
+                         CHAR_T * const rp = result + length;
+# else
+                         CHAR_T * const rp = tmp;
+# endif
+                         CHAR_T *p = rp + count;
+                         size_t pad = width - count;
+                         CHAR_T *end = p + pad;
+                         CHAR_T *pad_ptr = (*rp == '-' ? rp + 1 : rp);
+                         /* No zero-padding of "inf" and "nan".  */
+                         if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z')
+                             || (*pad_ptr >= 'a' && *pad_ptr <= 'z'))
+                           pad_ptr = NULL;
+                         /* The generated string now extends from rp to p,
+                            with the zero padding insertion point being at
+                            pad_ptr.  */
+
+                         if (flags & FLAG_LEFT)
+                           {
+                             /* Pad with spaces on the right.  */
+                             for (; pad > 0; pad--)
+                               *p++ = ' ';
+                           }
+                         else if ((flags & FLAG_ZERO) && pad_ptr != NULL)
+                           {
+                             /* Pad with zeroes.  */
+                             CHAR_T *q = end;
+
+                             while (p > pad_ptr)
+                               *--q = *--p;
+                             for (; pad > 0; pad--)
+                               *p++ = '0';
+                           }
+                         else
+                           {
+                             /* Pad with spaces on the left.  */
+                             CHAR_T *q = end;
+
+                             while (p > rp)
+                               *--q = *--p;
+                             for (; pad > 0; pad--)
+                               *p++ = ' ';
+                           }
+
+                         count = width; /* = count + pad = end - rp */
+                       }
+                     }
+#endif
+
 #if !USE_SNPRINTF
                    if (count >= tmp_length)
                      /* tmp_length was incorrectly calculated - fix the
@@ -1351,6 +1470,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;
                  }
@@ -1382,6 +1513,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);