(___printf_fp): Minor optimization.
[kopensolaris-gnu/glibc.git] / stdio-common / printf_fp.c
index 9240355..32df06a 100644 (file)
@@ -1,36 +1,33 @@
 /* Floating point output for `printf'.
-   Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+   Copyright (C) 1995-2003, 2006, 2007 Free Software Foundation, Inc.
+
    This file is part of the GNU C Library.
    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
 
    The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
    The GNU C Library 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
-   Library General Public License for more details.
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
 
 /* The gmp headers need some configuration frobs.  */
 #define HAVE_ALLOCA 1
 
-#ifdef USE_IN_LIBIO
-#  include <libioP.h>
-#else
-#  include <stdio.h>
-#endif
+#include <libioP.h>
 #include <alloca.h>
 #include <ctype.h>
 #include <float.h>
 #include <gmp-mparam.h>
-#include <stdlib/gmp.h>
+#include <gmp.h>
 #include <stdlib/gmp-impl.h>
 #include <stdlib/longlong.h>
 #include <stdlib/fpioconst.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <wchar.h>
+
+#ifdef COMPILE_WPRINTF
+# define CHAR_T        wchar_t
+#else
+# define CHAR_T        char
+#endif
 
-#define NDEBUG                 /* Undefine this for debugging assertions.  */
+#include "_i18n_number.h"
+
+#ifndef NDEBUG
+# define NDEBUG                        /* Undefine this for debugging assertions.  */
+#endif
 #include <assert.h>
 
 /* This defines make it possible to use the same code for GNU C library and
    the GNU I/O library.         */
-#ifdef USE_IN_LIBIO
-#  define PUT(f, s, n) _IO_sputn (f, s, n)
-#  define PAD(f, c, n) _IO_padn (f, c, n)
+#define PUT(f, s, n) _IO_sputn (f, s, n)
+#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : INTUSE(_IO_padn) (f, c, n))
 /* We use this file GNU C library and GNU I/O library. So make
    names equal.         */
-#  undef putc
-#  define putc(c, f) _IO_putc_unlocked (c, f)
-#  define size_t     _IO_size_t
-#  define FILE      _IO_FILE
-#else  /* ! USE_IN_LIBIO */
-#  define PUT(f, s, n) fwrite (s, 1, n, f)
-#  define PAD(f, c, n) __printf_pad (f, c, n)
-ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
-#endif /* USE_IN_LIBIO */
+#undef putc
+#define putc(c, f) (wide \
+                   ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
+#define size_t     _IO_size_t
+#define FILE        _IO_FILE
 \f
 /* Macros for doing the actual output.  */
 
@@ -69,25 +72,37 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
     {                                                                        \
       register const int outc = (ch);                                        \
       if (putc (outc, fp) == EOF)                                            \
-       return -1;                                                            \
+       {                                                                     \
+         if (buffer_malloced)                                                \
+           free (wbuffer);                                                   \
+         return -1;                                                          \
+       }                                                                     \
       ++done;                                                                \
     } while (0)
 
-#define PRINT(ptr, len)                                                              \
+#define PRINT(ptr, wptr, len)                                                \
   do                                                                         \
     {                                                                        \
       register size_t outlen = (len);                                        \
       if (len > 20)                                                          \
        {                                                                     \
-         if (PUT (fp, ptr, outlen) != outlen)                                \
-           return -1;                                                        \
+         if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen)   \
+           {                                                                 \
+             if (buffer_malloced)                                            \
+               free (wbuffer);                                               \
+             return -1;                                                      \
+           }                                                                 \
          ptr += outlen;                                                      \
          done += outlen;                                                     \
        }                                                                     \
       else                                                                   \
        {                                                                     \
-         while (outlen-- > 0)                                                \
-           outchar (*ptr++);                                                 \
+         if (wide)                                                           \
+           while (outlen-- > 0)                                              \
+             outchar (*wptr++);                                              \
+         else                                                                \
+           while (outlen-- > 0)                                              \
+             outchar (*ptr++);                                               \
        }                                                                     \
     } while (0)
 
@@ -95,7 +110,11 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
   do                                                                         \
     {                                                                        \
       if (PAD (fp, ch, len) != len)                                          \
-       return -1;                                                            \
+       {                                                                     \
+         if (buffer_malloced)                                                \
+           free (wbuffer);                                                   \
+         return -1;                                                          \
+       }                                                                     \
       done += len;                                                           \
     }                                                                        \
   while (0)
@@ -112,7 +131,8 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
 #define MPN_GE(u,v) \
   (u##size > v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) >= 0))
 
-extern int __isinfl (long double), __isnanl (long double);
+extern int __isinfl_internal (long double) attribute_hidden;
+extern int __isnanl_internal (long double) attribute_hidden;
 
 extern mp_size_t __mpn_extract_double (mp_ptr res_ptr, mp_size_t size,
                                       int *expt, int *is_neg,
@@ -121,17 +141,19 @@ extern mp_size_t __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
                                            int *expt, int *is_neg,
                                            long double value);
 extern unsigned int __guess_grouping (unsigned int intdig_max,
-                                     const char *grouping, wchar_t sepchar);
+                                     const char *grouping);
 
 
-static char *group_number (char *buf, char *bufend, unsigned int intdig_no,
-                          const char *grouping, wchar_t thousands_sep);
+static wchar_t *group_number (wchar_t *buf, wchar_t *bufend,
+                             unsigned int intdig_no, const char *grouping,
+                             wchar_t thousands_sep, int ngroups)
+     internal_function;
 
 
 int
-__printf_fp (FILE *fp,
-            const struct printf_info *info,
-            const void *const *args)
+___printf_fp (FILE *fp,
+             const struct printf_info *info,
+             const void *const *args)
 {
   /* The floating-point value to output.  */
   union
@@ -142,14 +164,17 @@ __printf_fp (FILE *fp,
   fpnum;
 
   /* Locale-dependent representation of decimal point. */
-  wchar_t decimal;
+  const char *decimal;
+  wchar_t decimalwc;
 
   /* Locale-dependent thousands separator and grouping specification.  */
-  wchar_t thousands_sep;
+  const char *thousands_sep = NULL;
+  wchar_t thousands_sepwc = 0;
   const char *grouping;
 
   /* "NaN" or "Inf" for the special cases.  */
   const char *special = NULL;
+  const wchar_t *wspecial = NULL;
 
   /* We need just a few limbs for the input before shifting to the right
      position. */
@@ -173,7 +198,7 @@ __printf_fp (FILE *fp,
   MPN_VAR(tmp);
 
   /* Digit which is result of last hack_digit() call.  */
-  int digit;
+  wchar_t digit;
 
   /* The type of output format that will be used: 'e'/'E' or 'f'.  */
   int type;
@@ -184,7 +209,17 @@ __printf_fp (FILE *fp,
   /* General helper (carry limb).  */
   mp_limb_t cy;
 
-  char hack_digit (void)
+  /* Nonzero if this is output on a wide character stream.  */
+  int wide = info->wide;
+
+  /* Buffer in which we produce the output.  */
+  wchar_t *wbuffer = NULL;
+  /* Flag whether wbuffer is malloc'ed or not.  */
+  int buffer_malloced = 0;
+
+  auto wchar_t hack_digit (void);
+
+  wchar_t hack_digit (void)
     {
       mp_limb_t hi;
 
@@ -193,8 +228,7 @@ __printf_fp (FILE *fp,
       else if (scalesize == 0)
        {
          hi = frac[fracsize - 1];
-         cy = __mpn_mul_1 (frac, frac, fracsize - 1, 10);
-         frac[fracsize - 1] = cy;
+         frac[fracsize - 1] = __mpn_mul_1 (frac, frac, fracsize - 1, 10);
        }
       else
        {
@@ -214,36 +248,39 @@ __printf_fp (FILE *fp,
                  /* We're not prepared for an mpn variable with zero
                     limbs.  */
                  fracsize = 1;
-                 return '0' + hi;
+                 return L'0' + hi;
                }
            }
 
-         cy = __mpn_mul_1 (frac, frac, fracsize, 10);
-         if (cy != 0)
-           frac[fracsize++] = cy;
+         mp_limb_t _cy = __mpn_mul_1 (frac, frac, fracsize, 10);
+         if (_cy != 0)
+           frac[fracsize++] = _cy;
        }
 
-      return '0' + hi;
+      return L'0' + hi;
     }
 
 
   /* Figure out the decimal point character.  */
   if (info->extra == 0)
     {
-      if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT),
-                 strlen (_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT))) <= 0)
-       decimal = (wchar_t) *_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
+      decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
+      decimalwc = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
     }
   else
     {
-      if (mbtowc (&decimal, _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT),
-                 strlen (_NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT))) <= 0)
-       decimal = (wchar_t) *_NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
+      decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
+      if (*decimal == '\0')
+       decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
+      decimalwc = _NL_CURRENT_WORD (LC_MONETARY,
+                                   _NL_MONETARY_DECIMAL_POINT_WC);
+      if (decimalwc == L'\0')
+       decimalwc = _NL_CURRENT_WORD (LC_NUMERIC,
+                                     _NL_NUMERIC_DECIMAL_POINT_WC);
     }
-  /* Give default value.  */
-  if (decimal == L'\0')
-    decimal = L'.';
-
+  /* The decimal point character must not be zero.  */
+  assert (*decimal != '\0');
+  assert (decimalwc != L'\0');
 
   if (info->group)
     {
@@ -257,33 +294,40 @@ __printf_fp (FILE *fp,
       else
        {
          /* Figure out the thousands separator character.  */
-         if (info->extra == 0)
+         if (wide)
            {
-             if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC,
-                                                      THOUSANDS_SEP),
-                         strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP)))
-                 <= 0)
-               thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC,
-                                                       THOUSANDS_SEP);
+             if (info->extra == 0)
+               thousands_sepwc =
+                 _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
+             else
+               thousands_sepwc =
+                 _NL_CURRENT_WORD (LC_MONETARY,
+                                   _NL_MONETARY_THOUSANDS_SEP_WC);
            }
          else
            {
-             if (mbtowc (&thousands_sep, _NL_CURRENT (LC_MONETARY,
-                                                      MON_THOUSANDS_SEP),
-                         strlen (_NL_CURRENT (LC_MONETARY,
-                                              MON_THOUSANDS_SEP))) <= 0)
-               thousands_sep = (wchar_t) *_NL_CURRENT (LC_MONETARY,
-                                                       MON_THOUSANDS_SEP);
+             if (info->extra == 0)
+               thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
+             else
+               thousands_sep = _NL_CURRENT (LC_MONETARY, MON_THOUSANDS_SEP);
            }
 
-         if (thousands_sep == L'\0')
+         if ((wide && thousands_sepwc == L'\0')
+             || (! wide && *thousands_sep == '\0'))
            grouping = NULL;
+         else if (thousands_sepwc == L'\0')
+           /* If we are printing multibyte characters and there is a
+              multibyte representation for the thousands separator,
+              we must ensure the wide character thousands separator
+              is available, even if it is fake.  */
+           thousands_sepwc = 0xfffffffe;
        }
     }
   else
     grouping = NULL;
 
   /* Fetch the argument value. */
+#ifndef __NO_LONG_DOUBLE_MATH
   if (info->is_long_double && sizeof (long double) > sizeof (double))
     {
       fpnum.ldbl = *(const long double *) args[0];
@@ -291,12 +335,30 @@ __printf_fp (FILE *fp,
       /* Check for special values: not a number or infinity.  */
       if (__isnanl (fpnum.ldbl))
        {
-         special = isupper (info->spec) ? "NAN" : "nan";
+         if (isupper (info->spec))
+           {
+             special = "NAN";
+             wspecial = L"NAN";
+           }
+           else
+             {
+               special = "nan";
+               wspecial = L"nan";
+             }
          is_neg = 0;
        }
       else if (__isinfl (fpnum.ldbl))
        {
-         special = isupper (info->spec) ? "INF" : "inf";
+         if (isupper (info->spec))
+           {
+             special = "INF";
+             wspecial = L"INF";
+           }
+         else
+           {
+             special = "inf";
+             wspecial = L"inf";
+           }
          is_neg = fpnum.ldbl < 0;
        }
       else
@@ -310,19 +372,38 @@ __printf_fp (FILE *fp,
        }
     }
   else
+#endif /* no long double */
     {
       fpnum.dbl = *(const double *) args[0];
 
       /* Check for special values: not a number or infinity.  */
       if (__isnan (fpnum.dbl))
        {
-         special = isupper (info->spec) ? "NAN" : "nan";
          is_neg = 0;
+         if (isupper (info->spec))
+           {
+             special = "NAN";
+             wspecial = L"NAN";
+           }
+         else
+           {
+             special = "nan";
+             wspecial = L"nan";
+           }
        }
       else if (__isinf (fpnum.dbl))
        {
-         special = isupper (info->spec) ? "INF" : "inf";
          is_neg = fpnum.dbl < 0;
+         if (isupper (info->spec))
+           {
+             special = "INF";
+             wspecial = L"INF";
+           }
+         else
+           {
+             special = "inf";
+             wspecial = L"inf";
+           }
        }
       else
        {
@@ -352,7 +433,7 @@ __printf_fp (FILE *fp,
       else if (info->space)
        outchar (' ');
 
-      PRINT (special, 3);
+      PRINT (special, wspecial, 3);
 
       if (info->left && width > 0)
        PADN (' ', width);
@@ -367,7 +448,9 @@ __printf_fp (FILE *fp,
      would be really big it could lead to memory problems.  */
   {
     mp_size_t bignum_size = ((ABS (exponent) + BITS_PER_MP_LIMB - 1)
-                            / BITS_PER_MP_LIMB + 4) * sizeof (mp_limb_t);
+                            / BITS_PER_MP_LIMB
+                            + (LDBL_MANT_DIG / BITS_PER_MP_LIMB > 2 ? 8 : 4))
+                           * sizeof (mp_limb_t);
     frac = (mp_limb_t *) alloca (bignum_size);
     tmp = (mp_limb_t *) alloca (bignum_size);
     scale = (mp_limb_t *) alloca (bignum_size);
@@ -383,7 +466,7 @@ __printf_fp (FILE *fp,
       int scaleexpo = 0;
       int explog = LDBL_MAX_10_EXP_LOG;
       int exp10 = 0;
-      const struct mp_power *tens = &_fpioconst_pow10[explog + 1];
+      const struct mp_power *powers = &_fpioconst_pow10[explog + 1];
       int cnt_h, cnt_l, i;
 
       if ((exponent + to_shift) % BITS_PER_MP_LIMB == 0)
@@ -403,23 +486,50 @@ __printf_fp (FILE *fp,
        }
       MPN_ZERO (frac, (exponent + to_shift) / BITS_PER_MP_LIMB);
 
-      assert (tens > &_fpioconst_pow10[0]);
+      assert (powers > &_fpioconst_pow10[0]);
       do
        {
-         --tens;
+         --powers;
 
          /* The number of the product of two binary numbers with n and m
             bits respectively has m+n or m+n-1 bits.   */
-         if (exponent >= scaleexpo + tens->p_expo - 1)
+         if (exponent >= scaleexpo + powers->p_expo - 1)
            {
              if (scalesize == 0)
-               MPN_ASSIGN (tmp, tens->array);
+               {
+#ifndef __NO_LONG_DOUBLE_MATH
+                 if (LDBL_MANT_DIG > _FPIO_CONST_OFFSET * BITS_PER_MP_LIMB
+                     && info->is_long_double)
+                   {
+#define _FPIO_CONST_SHIFT \
+  (((LDBL_MANT_DIG + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB) \
+   - _FPIO_CONST_OFFSET)
+                     /* 64bit const offset is not enough for
+                        IEEE quad long double.  */
+                     tmpsize = powers->arraysize + _FPIO_CONST_SHIFT;
+                     memcpy (tmp + _FPIO_CONST_SHIFT,
+                             &__tens[powers->arrayoff],
+                             tmpsize * sizeof (mp_limb_t));
+                     MPN_ZERO (tmp, _FPIO_CONST_SHIFT);
+                     /* Adjust exponent, as scaleexpo will be this much
+                        bigger too.  */
+                     exponent += _FPIO_CONST_SHIFT * BITS_PER_MP_LIMB;
+                   }
+                 else
+#endif
+                   {
+                     tmpsize = powers->arraysize;
+                     memcpy (tmp, &__tens[powers->arrayoff],
+                             tmpsize * sizeof (mp_limb_t));
+                   }
+               }
              else
                {
                  cy = __mpn_mul (tmp, scale, scalesize,
-                                 &tens->array[_FPIO_CONST_OFFSET],
-                                 tens->arraysize - _FPIO_CONST_OFFSET);
-                 tmpsize = scalesize + tens->arraysize - _FPIO_CONST_OFFSET;
+                                 &__tens[powers->arrayoff
+                                        + _FPIO_CONST_OFFSET],
+                                 powers->arraysize - _FPIO_CONST_OFFSET);
+                 tmpsize = scalesize + powers->arraysize - _FPIO_CONST_OFFSET;
                  if (cy == 0)
                    --tmpsize;
                }
@@ -435,7 +545,7 @@ __printf_fp (FILE *fp,
            }
          --explog;
        }
-      while (tens > &_fpioconst_pow10[0]);
+      while (powers > &_fpioconst_pow10[0]);
       exponent = exp10;
 
       /* Optimize number representations.  We want to represent the numbers
@@ -526,7 +636,7 @@ __printf_fp (FILE *fp,
       /* |FP| < 1.0.  */
       int exp10 = 0;
       int explog = LDBL_MAX_10_EXP_LOG;
-      const struct mp_power *tens = &_fpioconst_pow10[explog + 1];
+      const struct mp_power *powers = &_fpioconst_pow10[explog + 1];
       mp_size_t used_limbs = fracsize - 1;
 
       /* Now shift the input value to its right place. */
@@ -537,27 +647,28 @@ __printf_fp (FILE *fp,
       expsign = 1;
       exponent = -exponent;
 
-      assert (tens != &_fpioconst_pow10[0]);
+      assert (powers != &_fpioconst_pow10[0]);
       do
        {
-         --tens;
+         --powers;
 
-         if (exponent >= tens->m_expo)
+         if (exponent >= powers->m_expo)
            {
              int i, incr, cnt_h, cnt_l;
              mp_limb_t topval[2];
 
              /* The __mpn_mul function expects the first argument to be
                 bigger than the second.  */
-             if (fracsize < tens->arraysize - _FPIO_CONST_OFFSET)
-               cy = __mpn_mul (tmp, &tens->array[_FPIO_CONST_OFFSET],
-                               tens->arraysize - _FPIO_CONST_OFFSET,
+             if (fracsize < powers->arraysize - _FPIO_CONST_OFFSET)
+               cy = __mpn_mul (tmp, &__tens[powers->arrayoff
+                                           + _FPIO_CONST_OFFSET],
+                               powers->arraysize - _FPIO_CONST_OFFSET,
                                frac, fracsize);
              else
                cy = __mpn_mul (tmp, frac, fracsize,
-                               &tens->array[_FPIO_CONST_OFFSET],
-                               tens->arraysize - _FPIO_CONST_OFFSET);
-             tmpsize = fracsize + tens->arraysize - _FPIO_CONST_OFFSET;
+                               &__tens[powers->arrayoff + _FPIO_CONST_OFFSET],
+                               powers->arraysize - _FPIO_CONST_OFFSET);
+             tmpsize = fracsize + powers->arraysize - _FPIO_CONST_OFFSET;
              if (cy == 0)
                --tmpsize;
 
@@ -565,25 +676,27 @@ __printf_fp (FILE *fp,
              incr = (tmpsize - fracsize) * BITS_PER_MP_LIMB
                     + BITS_PER_MP_LIMB - 1 - cnt_h;
 
-             assert (incr <= tens->p_expo);
+             assert (incr <= powers->p_expo);
 
              /* If we increased the exponent by exactly 3 we have to test
                 for overflow.  This is done by comparing with 10 shifted
                 to the right position.  */
              if (incr == exponent + 3)
-               if (cnt_h <= BITS_PER_MP_LIMB - 4)
-                 {
-                   topval[0] = 0;
-                   topval[1]
-                     = ((mp_limb_t) 10) << (BITS_PER_MP_LIMB - 4 - cnt_h);
-                 }
-               else
-                 {
-                   topval[0] = ((mp_limb_t) 10) << (BITS_PER_MP_LIMB - 4);
-                   topval[1] = 0;
-                   (void) __mpn_lshift (topval, topval, 2,
-                                        BITS_PER_MP_LIMB - cnt_h);
-                 }
+               {
+                 if (cnt_h <= BITS_PER_MP_LIMB - 4)
+                   {
+                     topval[0] = 0;
+                     topval[1]
+                       = ((mp_limb_t) 10) << (BITS_PER_MP_LIMB - 4 - cnt_h);
+                   }
+                 else
+                   {
+                     topval[0] = ((mp_limb_t) 10) << (BITS_PER_MP_LIMB - 4);
+                     topval[1] = 0;
+                     (void) __mpn_lshift (topval, topval, 2,
+                                          BITS_PER_MP_LIMB - cnt_h);
+                   }
+               }
 
              /* We have to be careful when multiplying the last factor.
                 If the result is greater than 1.0 be have to test it
@@ -651,7 +764,7 @@ __printf_fp (FILE *fp,
            }
          --explog;
        }
-      while (tens != &_fpioconst_pow10[1] && exponent > 0);
+      while (powers != &_fpioconst_pow10[1] && exponent > 0);
       /* All factors but 10^-1 are tested now. */
       if (exponent > 0)
        {
@@ -694,15 +807,18 @@ __printf_fp (FILE *fp,
 
   {
     int width = info->width;
-    char *buffer, *startp, *cp;
+    wchar_t *wstartp, *wcp;
     int chars_needed;
     int expscale;
     int intdig_max, intdig_no = 0;
-    int fracdig_min, fracdig_max, fracdig_no = 0;
+    int fracdig_min;
+    int fracdig_max;
     int dig_max;
     int significant;
+    int ngroups = 0;
+    char spec = _tolower (info->spec);
 
-    if (tolower (info->spec) == 'e')
+    if (spec == 'e')
       {
        type = info->spec;
        intdig_max = 1;
@@ -712,10 +828,12 @@ __printf_fp (FILE *fp,
        dig_max = INT_MAX;              /* Unlimited.  */
        significant = 1;                /* Does not matter here.  */
       }
-    else if (info->spec == 'f')
+    else if (spec == 'f')
       {
        type = 'f';
        fracdig_min = fracdig_max = info->prec < 0 ? 6 : info->prec;
+       dig_max = INT_MAX;              /* Unlimited.  */
+       significant = 1;                /* Does not matter here.  */
        if (expsign == 0)
          {
            intdig_max = exponent + 1;
@@ -727,8 +845,6 @@ __printf_fp (FILE *fp,
            intdig_max = 1;
            chars_needed = 1 + 1 + fracdig_max;
          }
-       dig_max = INT_MAX;              /* Unlimited.  */
-       significant = 1;                /* Does not matter here.  */
       }
     else
       {
@@ -736,7 +852,10 @@ __printf_fp (FILE *fp,
        if ((expsign == 0 && exponent >= dig_max)
            || (expsign != 0 && exponent > 4))
          {
-           type = isupper (info->spec) ? 'E' : 'e';
+           if ('g' - 'G' == 'e' - 'E')
+             type = 'E' + (info->spec - 'G');
+           else
+             type = isupper (info->spec) ? 'E' : 'e';
            fracdig_max = dig_max - 1;
            intdig_max = 1;
            chars_needed = 1 + 1 + fracdig_max + 1 + 1 + 4;
@@ -746,24 +865,40 @@ __printf_fp (FILE *fp,
            type = 'f';
            intdig_max = expsign == 0 ? exponent + 1 : 0;
            fracdig_max = dig_max - intdig_max;
-           /* We need space for the significant digits and perhaps for
-              leading zeros when < 1.0.  Pessimistic guess: dig_max.  */
-           chars_needed = dig_max + dig_max + 1;
+           /* We need space for the significant digits and perhaps
+              for leading zeros when < 1.0.  The number of leading
+              zeros can be as many as would be required for
+              exponential notation with a negative two-digit
+              exponent, which is 4.  */
+           chars_needed = dig_max + 1 + 4;
          }
        fracdig_min = info->alt ? fracdig_max : 0;
        significant = 0;                /* We count significant digits.  */
       }
 
     if (grouping)
-      /* Guess the number of groups we will make, and thus how
-        many spaces we need for separator characters.  */
-      chars_needed += __guess_grouping (intdig_max, grouping, thousands_sep);
+      {
+       /* Guess the number of groups we will make, and thus how
+          many spaces we need for separator characters.  */
+       ngroups = __guess_grouping (intdig_max, grouping);
+       chars_needed += ngroups;
+      }
 
     /* Allocate buffer for output.  We need two more because while rounding
        it is possible that we need two more characters in front of all the
-       other output.  */
-    buffer = alloca (2 + chars_needed);
-    cp = startp = buffer + 2;  /* Let room for rounding.  */
+       other output.  If the amount of memory we have to allocate is too
+       large use `malloc' instead of `alloca'.  */
+    buffer_malloced = ! __libc_use_alloca (chars_needed * 2 * sizeof (wchar_t));
+    if (__builtin_expect (buffer_malloced, 0))
+      {
+       wbuffer = (wchar_t *) malloc ((2 + chars_needed) * sizeof (wchar_t));
+       if (wbuffer == NULL)
+         /* Signal an error to the caller.  */
+         return -1;
+      }
+    else
+      wbuffer = (wchar_t *) alloca ((2 + chars_needed) * sizeof (wchar_t));
+    wcp = wstartp = wbuffer + 2;       /* Let room for rounding.  */
 
     /* Do the real work: put digits in allocated buffer.  */
     if (expsign == 0 || type != 'f')
@@ -772,30 +907,31 @@ __printf_fp (FILE *fp,
        while (intdig_no < intdig_max)
          {
            ++intdig_no;
-           *cp++ = hack_digit ();
+           *wcp++ = hack_digit ();
          }
        significant = 1;
        if (info->alt
            || fracdig_min > 0
            || (fracdig_max > 0 && (fracsize > 1 || frac[0] != 0)))
-         *cp++ = decimal;
+         *wcp++ = decimalwc;
       }
     else
       {
        /* |fp| < 1.0 and the selected type is 'f', so put "0."
           in the buffer.  */
-       *cp++ = '0';
+       *wcp++ = L'0';
        --exponent;
-       *cp++ = decimal;
+       *wcp++ = decimalwc;
       }
 
     /* Generate the needed number of fractional digits.         */
+    int fracdig_no = 0;
     while (fracdig_no < fracdig_min
           || (fracdig_no < fracdig_max && (fracsize > 1 || frac[0] != 0)))
       {
        ++fracdig_no;
-       *cp = hack_digit ();
-       if (*cp != '0')
+       *wcp = hack_digit ();
+       if (*wcp++ != L'0')
          significant = 1;
        else if (significant == 0)
          {
@@ -803,52 +939,66 @@ __printf_fp (FILE *fp,
            if (fracdig_min > 0)
              ++fracdig_min;
          }
-       ++cp;
       }
 
     /* Do rounding.  */
     digit = hack_digit ();
-    if (digit > '4')
+    if (digit > L'4')
       {
-       char *tp = cp;
-
-       if (digit == '5')
-         /* This is the critical case.  */
-         if (fracsize == 1 && frac[0] == 0)
-           /* Rest of the number is zero -> round to even.
-              (IEEE 754-1985 4.1 says this is the default rounding.)  */
-           if ((*(cp - 1) & 1) == 0)
+       wchar_t *wtp = wcp;
+
+       if (digit == L'5'
+           && ((*(wcp - 1) != decimalwc && (*(wcp - 1) & 1) == 0)
+               || ((*(wcp - 1) == decimalwc && (*(wcp - 2) & 1) == 0))))
+         {
+           /* This is the critical case.        */
+           if (fracsize == 1 && frac[0] == 0)
+             /* Rest of the number is zero -> round to even.
+                (IEEE 754-1985 4.1 says this is the default rounding.)  */
              goto do_expo;
+           else if (scalesize == 0)
+             {
+               /* Here we have to see whether all limbs are zero since no
+                  normalization happened.  */
+               size_t lcnt = fracsize;
+               while (lcnt >= 1 && frac[lcnt - 1] == 0)
+                 --lcnt;
+               if (lcnt == 0)
+                 /* Rest of the number is zero -> round to even.
+                    (IEEE 754-1985 4.1 says this is the default rounding.)  */
+                 goto do_expo;
+             }
+         }
 
        if (fracdig_no > 0)
          {
            /* Process fractional digits.  Terminate if not rounded or
               radix character is reached.  */
-           while (*--tp != decimal && *tp == '9')
-             *tp = '0';
-           if (*tp != decimal)
+           while (*--wtp != decimalwc && *wtp == L'9')
+             *wtp = L'0';
+           if (*wtp != decimalwc)
              /* Round up.  */
-             (*tp)++;
+             (*wtp)++;
          }
 
-       if (fracdig_no == 0 || *tp == decimal)
+       if (fracdig_no == 0 || *wtp == decimalwc)
          {
            /* Round the integer digits.  */
-           if (*(tp - 1) == decimal)
-             --tp;
+           if (*(wtp - 1) == decimalwc)
+             --wtp;
 
-           while (--tp >= startp && *tp == '9')
-             *tp = '0';
+           while (--wtp >= wstartp && *wtp == L'9')
+             *wtp = L'0';
 
-           if (tp >= startp)
+           if (wtp >= wstartp)
              /* Round up.  */
-             (*tp)++;
+             (*wtp)++;
            else
              /* It is more critical.  All digits were 9's.  */
              {
                if (type != 'f')
                  {
-                   *startp = '1';
+                   *wstartp = '1';
                    exponent += expsign == 0 ? 1 : -1;
                  }
                else if (intdig_no == dig_max)
@@ -856,13 +1006,13 @@ __printf_fp (FILE *fp,
                    /* This is the case where for type %g the number fits
                       really in the range for %f output but after rounding
                       the number of digits is too big.  */
-                   *--startp = decimal;
-                   *--startp = '1';
+                   *--wstartp = decimalwc;
+                   *--wstartp = L'1';
 
                    if (info->alt || fracdig_no > 0)
                      {
                        /* Overwrite the old radix character.  */
-                       startp[intdig_no + 2] = '0';
+                       wstartp[intdig_no + 2] = L'0';
                        ++fracdig_no;
                      }
 
@@ -877,7 +1027,7 @@ __printf_fp (FILE *fp,
                  {
                    /* We can simply add another another digit before the
                       radix.  */
-                   *--startp = '1';
+                   *--wstartp = L'1';
                    ++intdig_no;
                  }
 
@@ -886,7 +1036,7 @@ __printf_fp (FILE *fp,
                   fractional digits.  */
                if (intdig_no + fracdig_no > dig_max)
                  {
-                   cp -= intdig_no + fracdig_no - dig_max;
+                   wcp -= intdig_no + fracdig_no - dig_max;
                    fracdig_no -= intdig_no + fracdig_no - dig_max;
                  }
              }
@@ -895,25 +1045,26 @@ __printf_fp (FILE *fp,
 
   do_expo:
     /* Now remove unnecessary '0' at the end of the string.  */
-    while (fracdig_no > fracdig_min && *(cp - 1) == '0')
+    while (fracdig_no > fracdig_min && *(wcp - 1) == L'0')
       {
-       --cp;
+       --wcp;
        --fracdig_no;
       }
     /* If we eliminate all fractional digits we perhaps also can remove
        the radix character.  */
-    if (fracdig_no == 0 && !info->alt && *(cp - 1) == decimal)
-      --cp;
+    if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimalwc)
+      --wcp;
 
     if (grouping)
       /* Add in separator characters, overwriting the same buffer.  */
-      cp = group_number (startp, cp, intdig_no, grouping, thousands_sep);
+      wcp = group_number (wstartp, wcp, intdig_no, grouping, thousands_sepwc,
+                         ngroups);
 
     /* Write the exponent if it is needed.  */
     if (type != 'f')
       {
-       *cp++ = type;
-       *cp++ = expsign ? '-' : '+';
+       *wcp++ = (wchar_t) type;
+       *wcp++ = expsign ? L'-' : L'+';
 
        /* Find the magnitude of the exponent.  */
        expscale = 10;
@@ -922,23 +1073,23 @@ __printf_fp (FILE *fp,
 
        if (exponent < 10)
          /* Exponent always has at least two digits.  */
-         *cp++ = '0';
+         *wcp++ = L'0';
        else
          do
            {
              expscale /= 10;
-             *cp++ = '0' + (exponent / expscale);
+             *wcp++ = L'0' + (exponent / expscale);
              exponent %= expscale;
            }
          while (expscale > 10);
-       *cp++ = '0' + exponent;
+       *wcp++ = L'0' + exponent;
       }
 
     /* Compute number of characters which must be filled with the padding
        character.  */
     if (is_neg || info->showsign || info->space)
       --width;
-    width -= cp - startp;
+    width -= wcp - wstartp;
 
     if (!info->left && info->pad != '0' && width > 0)
       PADN (info->pad, width);
@@ -953,20 +1104,86 @@ __printf_fp (FILE *fp,
     if (!info->left && info->pad == '0' && width > 0)
       PADN ('0', width);
 
-    PRINT (startp, cp - startp);
+    {
+      char *buffer = NULL;
+      char *cp = NULL;
+      char *tmpptr;
+
+      if (! wide)
+       {
+         /* Create the single byte string.  */
+         size_t decimal_len;
+         size_t thousands_sep_len;
+         wchar_t *copywc;
+
+         decimal_len = strlen (decimal);
+
+         if (thousands_sep == NULL)
+           thousands_sep_len = 0;
+         else
+           thousands_sep_len = strlen (thousands_sep);
+
+         if (__builtin_expect (buffer_malloced, 0))
+           {
+             buffer = (char *) malloc (2 + chars_needed + decimal_len
+                                       + ngroups * thousands_sep_len);
+             if (buffer == NULL)
+               {
+                 /* Signal an error to the caller.  */
+                 free (wbuffer);
+                 return -1;
+               }
+           }
+         else
+           buffer = (char *) alloca (2 + chars_needed + decimal_len
+                                     + ngroups * thousands_sep_len);
+
+         /* Now copy the wide character string.  Since the character
+            (except for the decimal point and thousands separator) must
+            be coming from the ASCII range we can esily convert the
+            string without mapping tables.  */
+         for (cp = buffer, copywc = wstartp; copywc < wcp; ++copywc)
+           if (*copywc == decimalwc)
+             cp = (char *) __mempcpy (cp, decimal, decimal_len);
+           else if (*copywc == thousands_sepwc)
+             cp = (char *) __mempcpy (cp, thousands_sep, thousands_sep_len);
+           else
+             *cp++ = (char) *copywc;
+       }
+
+      tmpptr = buffer;
+      if (__builtin_expect (info->i18n, 0))
+        {
+#ifdef COMPILE_WPRINTF
+         wstartp = _i18n_number_rewrite (wstartp, wcp);
+#else
+         tmpptr = _i18n_number_rewrite (tmpptr, cp);
+#endif
+        }
+
+      PRINT (tmpptr, wstartp, wide ? wcp - wstartp : cp - tmpptr);
+
+      /* Free the memory if necessary.  */
+      if (__builtin_expect (buffer_malloced, 0))
+       {
+         free (buffer);
+         free (wbuffer);
+       }
+    }
 
     if (info->left && width > 0)
       PADN (info->pad, width);
   }
   return done;
 }
+ldbl_hidden_def (___printf_fp, __printf_fp)
+ldbl_strong_alias (___printf_fp, __printf_fp)
 \f
 /* Return the number of extra grouping characters that will be inserted
    into a number with INTDIG_MAX integer digits.  */
 
 unsigned int
-__guess_grouping (unsigned int intdig_max, const char *grouping,
-                 wchar_t sepchar)
+__guess_grouping (unsigned int intdig_max, const char *grouping)
 {
   unsigned int groups;
 
@@ -1004,21 +1221,21 @@ __guess_grouping (unsigned int intdig_max, const char *grouping,
    There is guaranteed enough space past BUFEND to extend it.
    Return the new end of buffer.  */
 
-static char *
-group_number (char *buf, char *bufend, unsigned int intdig_no,
-             const char *grouping, wchar_t thousands_sep)
+static wchar_t *
+internal_function
+group_number (wchar_t *buf, wchar_t *bufend, unsigned int intdig_no,
+             const char *grouping, wchar_t thousands_sep, int ngroups)
 {
-  unsigned int groups = __guess_grouping (intdig_no, grouping, thousands_sep);
-  char *p;
+  wchar_t *p;
 
-  if (groups == 0)
+  if (ngroups == 0)
     return bufend;
 
   /* Move the fractional part down.  */
-  memmove (buf + intdig_no + groups, buf + intdig_no,
-          bufend - (buf + intdig_no));
+  __wmemmove (buf + intdig_no + ngroups, buf + intdig_no,
+             bufend - (buf + intdig_no));
 
-  p = buf + intdig_no + groups - 1;
+  p = buf + intdig_no + ngroups - 1;
   do
     {
       unsigned int len = *grouping++;
@@ -1044,5 +1261,5 @@ group_number (char *buf, char *bufend, unsigned int intdig_no,
     *p-- = buf[--intdig_no];
   while (p > buf);
 
-  return bufend + groups;
+  return bufend + ngroups;
 }