X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fvasnprintf.c;h=45a219944499a0b6bfc52394ca5f9fa1c740f846;hb=ab681e53f8100fdb144e0a100d6f2da9ff7c005f;hp=b13dbd5ee1e8af264b05b57b2b0e4db6d7f5ab0b;hpb=c3c8fb8fd5880613c2aa14bae661652b3610eadd;p=gnulib.git diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index b13dbd5ee..45a219944 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -41,6 +41,9 @@ #include /* errno */ #include /* CHAR_BIT */ #include /* DBL_MAX_EXP, LDBL_MAX_EXP */ +#if HAVE_NL_LANGINFO +# include +#endif #if WIDE_CHAR_VERSION # include "wprintf-parse.h" #else @@ -51,12 +54,17 @@ #include "xsize.h" #if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL +# include # 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);