#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
# 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
}
/* Allocate a temporary buffer of sufficient size. */
-# if HAVE_LONG_DOUBLE
if (type == TYPE_LONGDOUBLE)
tmp_length =
(unsigned int) ((LDBL_DIG + 1)
)
+ 1; /* turn floor into ceil */
else
-# endif
tmp_length =
(unsigned int) ((DBL_DIG + 1)
* 0.831 /* decimal -> hexadecimal */
pad_ptr = NULL;
p = tmp;
-# if HAVE_LONG_DOUBLE
if (type == TYPE_LONGDOUBLE)
{
long double arg = a.arg[dp->arg_index].a.a_longdouble;
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++ = '-';
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)
}
}
*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;
{
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++ = '-';
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)
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)
{
break;
case 'f': case 'F':
-# if HAVE_LONG_DOUBLE
if (type == TYPE_LONGDOUBLE)
tmp_length =
(unsigned int) (LDBL_MAX_EXP
+ 1 /* turn floor into ceil */
+ 10; /* sign, decimal point etc. */
else
-# endif
tmp_length =
(unsigned int) (DBL_MAX_EXP
* 0.30103 /* binary -> decimal */
break;
case 'a': case 'A':
-# if HAVE_LONG_DOUBLE
if (type == TYPE_LONGDOUBLE)
tmp_length =
(unsigned int) (LDBL_DIG
)
+ 1; /* turn floor into ceil */
else
-# endif
tmp_length =
(unsigned int) (DBL_DIG
* 0.831 /* decimal -> hexadecimal */
}
#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)
#if HAVE_LONG_LONG_INT
case TYPE_LONGLONGINT:
case TYPE_ULONGLONGINT:
- *p++ = 'l';
+ *fbp++ = 'l';
/*FALLTHROUGH*/
#endif
case TYPE_LONGINT:
#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 ();
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) \
{ \
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;
{
/* 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
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
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;
}
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);