diff options
Diffstat (limited to 'gl/vasnprintf.c')
-rw-r--r-- | gl/vasnprintf.c | 140 |
1 files changed, 90 insertions, 50 deletions
diff --git a/gl/vasnprintf.c b/gl/vasnprintf.c index e618901b..5267b1bb 100644 --- a/gl/vasnprintf.c +++ b/gl/vasnprintf.c @@ -1,5 +1,5 @@ /* vsprintf with automatic memory allocation. - Copyright (C) 1999, 2002-2010 Free Software Foundation, Inc. + Copyright (C) 1999, 2002-2013 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -12,8 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + with this program; if not, see <http://www.gnu.org/licenses/>. */ /* This file can be parametrized with the following macros: VASNPRINTF The name of the function being defined. @@ -88,6 +87,8 @@ /* Checked size_t computations. */ #include "xsize.h" +#include "verify.h" + #if (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL # include <math.h> # include "float+.h" @@ -274,10 +275,10 @@ decimal_point_char (void) { const char *point; /* Determine it in a multithread-safe way. We know nl_langinfo is - multithread-safe on glibc systems and MacOS X systems, but is not required + multithread-safe on glibc systems and Mac OS X 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__ || (defined __APPLE__ && defined __MACH__)) +# if HAVE_NL_LANGINFO && (__GLIBC__ || defined __UCLIBC__ || (defined __APPLE__ && defined __MACH__)) point = nl_langinfo (RADIXCHAR); # elif 1 char pointbuf[5]; @@ -322,11 +323,11 @@ is_infinite_or_zerol (long double x) typedef unsigned int mp_limb_t; # define GMP_LIMB_BITS 32 -typedef int mp_limb_verify[2 * (sizeof (mp_limb_t) * CHAR_BIT == GMP_LIMB_BITS) - 1]; +verify (sizeof (mp_limb_t) * CHAR_BIT == GMP_LIMB_BITS); typedef unsigned long long mp_twolimb_t; # define GMP_TWOLIMB_BITS 64 -typedef int mp_twolimb_verify[2 * (sizeof (mp_twolimb_t) * CHAR_BIT == GMP_TWOLIMB_BITS) - 1]; +verify (sizeof (mp_twolimb_t) * CHAR_BIT == GMP_TWOLIMB_BITS); /* Representation of a bignum >= 0. */ typedef struct @@ -551,32 +552,61 @@ divide (mpn_t a, mpn_t b, mpn_t *q) size_t s; { mp_limb_t msd = b_ptr[b_len - 1]; /* = b[n-1], > 0 */ - s = 31; - if (msd >= 0x10000) - { - msd = msd >> 16; - s -= 16; - } - if (msd >= 0x100) - { - msd = msd >> 8; - s -= 8; - } - if (msd >= 0x10) - { - msd = msd >> 4; - s -= 4; - } - if (msd >= 0x4) + /* Determine s = GMP_LIMB_BITS - integer_length (msd). + Code copied from gnulib's integer_length.c. */ +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + s = __builtin_clz (msd); +# else +# if defined DBL_EXPBIT0_WORD && defined DBL_EXPBIT0_BIT + if (GMP_LIMB_BITS <= DBL_MANT_BIT) { - msd = msd >> 2; - s -= 2; + /* Use 'double' operations. + Assumes an IEEE 754 'double' implementation. */ +# define DBL_EXP_MASK ((DBL_MAX_EXP - DBL_MIN_EXP) | 7) +# define DBL_EXP_BIAS (DBL_EXP_MASK / 2 - 1) +# define NWORDS \ + ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) + union { double value; unsigned int word[NWORDS]; } m; + + /* Use a single integer to floating-point conversion. */ + m.value = msd; + + s = GMP_LIMB_BITS + - (((m.word[DBL_EXPBIT0_WORD] >> DBL_EXPBIT0_BIT) & DBL_EXP_MASK) + - DBL_EXP_BIAS); } - if (msd >= 0x2) + else +# undef NWORDS +# endif { - msd = msd >> 1; - s -= 1; + s = 31; + if (msd >= 0x10000) + { + msd = msd >> 16; + s -= 16; + } + if (msd >= 0x100) + { + msd = msd >> 8; + s -= 8; + } + if (msd >= 0x10) + { + msd = msd >> 4; + s -= 4; + } + if (msd >= 0x4) + { + msd = msd >> 2; + s -= 2; + } + if (msd >= 0x2) + { + msd = msd >> 1; + s -= 1; + } } +# endif } /* 0 <= s < GMP_LIMB_BITS. Copy b, shifting it left by s bits. */ @@ -883,9 +913,9 @@ decode_long_double (long double x, int *ep, mpn_t *mp) y = frexpl (x, &exp); if (!(y >= 0.0L && y < 1.0L)) abort (); - /* x = 2^exp * y = 2^(exp - LDBL_MANT_BIT) * (y * LDBL_MANT_BIT), and the + /* x = 2^exp * y = 2^(exp - LDBL_MANT_BIT) * (y * 2^LDBL_MANT_BIT), and the latter is an integer. */ - /* Convert the mantissa (y * LDBL_MANT_BIT) to a sequence of limbs. + /* Convert the mantissa (y * 2^LDBL_MANT_BIT) to a sequence of limbs. I'm not sure whether it's safe to cast a 'long double' value between 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only 'long double' values between 0 and 2^16 (to 'unsigned int' or 'int', @@ -933,11 +963,11 @@ decode_long_double (long double x, int *ep, mpn_t *mp) abort (); m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo; } -#if 0 /* On FreeBSD 6.1/x86, 'long double' numbers sometimes have excess - precision. */ +# if 0 /* On FreeBSD 6.1/x86, 'long double' numbers sometimes have excess + precision. */ if (!(y == 0.0L)) abort (); -#endif +# endif /* Normalise. */ while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0) m.nlimbs--; @@ -971,9 +1001,9 @@ decode_double (double x, int *ep, mpn_t *mp) y = frexp (x, &exp); if (!(y >= 0.0 && y < 1.0)) abort (); - /* x = 2^exp * y = 2^(exp - DBL_MANT_BIT) * (y * DBL_MANT_BIT), and the + /* x = 2^exp * y = 2^(exp - DBL_MANT_BIT) * (y * 2^DBL_MANT_BIT), and the latter is an integer. */ - /* Convert the mantissa (y * DBL_MANT_BIT) to a sequence of limbs. + /* Convert the mantissa (y * 2^DBL_MANT_BIT) to a sequence of limbs. I'm not sure whether it's safe to cast a 'double' value between 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only 'double' values between 0 and 2^16 (to 'unsigned int' or 'int', @@ -1500,7 +1530,7 @@ is_borderline (const char *digits, size_t precision) /* Returns the number of TCHAR_T units needed as temporary space for the result of sprintf or SNPRINTF of a single conversion directive. */ -static inline size_t +static size_t MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, arg_type type, int flags, size_t width, int has_precision, size_t precision, int pad_ourselves) @@ -1751,8 +1781,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, return NULL; #define CLEANUP() \ - free (d.dir); \ - if (a.arg) \ + if (d.dir != d.direct_alloc_dir) \ + free (d.dir); \ + if (a.arg != a.direct_alloc_arg) \ free (a.arg); if (PRINTF_FETCHARGS (args, &a) < 0) @@ -2621,7 +2652,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, size_t characters; # if !DCHAR_IS_TCHAR /* This code assumes that TCHAR_T is 'char'. */ - typedef int TCHAR_T_verify[2 * (sizeof (TCHAR_T) == 1) - 1]; + verify (sizeof (TCHAR_T) == 1); TCHAR_T *tmpsrc; DCHAR_T *tmpdst; size_t tmpdst_len; @@ -2782,7 +2813,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if (has_width) { # if ENABLE_UNISTDIO - /* Outside POSIX, it's preferrable to compare the width + /* Outside POSIX, it's preferable to compare the width against the number of _characters_ of the converted value. */ w = DCHAR_MBSNLEN (result + length, characters); @@ -4597,6 +4628,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, TCHAR_T *fbp; unsigned int prefix_count; int prefixes[2] IF_LINT (= { 0 }); + int orig_errno; #if !USE_SNPRINTF size_t tmp_length; TCHAR_T tmpbuf[700]; @@ -4751,6 +4783,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, *fbp++ = ' '; if (flags & FLAG_ALT) *fbp++ = '#'; +#if __GLIBC__ >= 2 && !defined __UCLIBC__ + if (flags & FLAG_LOCALIZED) + *fbp++ = 'I'; +#endif if (!pad_ourselves) { if (flags & FLAG_ZERO) @@ -4834,20 +4870,21 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #endif *fbp = dp->conversion; #if USE_SNPRINTF -# if !(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) || ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)) +# if !(((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) && !defined __UCLIBC__) || ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)) fbp[1] = '%'; fbp[2] = 'n'; fbp[3] = '\0'; # else /* On glibc2 systems from glibc >= 2.3 - probably also older - ones - we know that snprintf's returns value conforms to - ISO C 99: the gl_SNPRINTF_DIRECTIVE_N test passes. + ones - we know that snprintf's return value conforms to + ISO C 99: the tests gl_SNPRINTF_RETVAL_C99 and + gl_SNPRINTF_TRUNCATION_C99 pass. Therefore we can avoid using %n in this situation. On glibc2 systems from 2004-10-18 or newer, the use of %n in format strings in writable memory may crash the program (if compiled with _FORTIFY_SOURCE=2), so we should avoid it in this situation. */ - /* On native Win32 systems (such as mingw), we can avoid using + /* On native Windows systems (such as mingw), we can avoid using %n because: - Although the gl_SNPRINTF_TRUNCATION_C99 test fails, snprintf does not write more than the specified number @@ -4856,7 +4893,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, - Although the gl_SNPRINTF_RETVAL_C99 test fails, snprintf allows us to recognize the case of an insufficient buffer size: it returns -1 in this case. - On native Win32 systems (such as mingw) where the OS is + On native Windows systems (such as mingw) where the OS is Windows Vista, the use of %n in format strings by default crashes the program. See <http://gcc.gnu.org/ml/gcc/2007-06/msg00122.html> and @@ -4900,6 +4937,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, *(TCHAR_T *) (result + length) = '\0'; #endif + orig_errno = errno; + for (;;) { int count = -1; @@ -5114,7 +5153,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, size_t tmp_length = MAX_ROOM_NEEDED (&a, dp->arg_index, dp->conversion, type, flags, - width, has_precision, + has_width ? width : 0, + has_precision, precision, pad_ourselves); if (maxlen < tmp_length) @@ -5284,8 +5324,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, DCHAR_T *tmpdst; size_t tmpdst_len; /* This code assumes that TCHAR_T is 'char'. */ - typedef int TCHAR_T_verify - [2 * (sizeof (TCHAR_T) == 1) - 1]; + verify (sizeof (TCHAR_T) == 1); # if USE_SNPRINTF tmpsrc = (TCHAR_T *) (result + length); # else @@ -5378,7 +5417,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { size_t w; # if ENABLE_UNISTDIO - /* Outside POSIX, it's preferrable to compare the width + /* Outside POSIX, it's preferable to compare the width against the number of _characters_ of the converted value. */ w = DCHAR_MBSNLEN (result + length, count); @@ -5498,6 +5537,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, length += count; break; } + errno = orig_errno; #undef pad_ourselves #undef prec_ourselves } |