summaryrefslogtreecommitdiff
path: root/lib/vasnprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vasnprintf.c')
-rw-r--r--lib/vasnprintf.c607
1 files changed, 468 insertions, 139 deletions
diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c
index de204458..6ae95ca5 100644
--- a/lib/vasnprintf.c
+++ b/lib/vasnprintf.c
@@ -64,7 +64,7 @@
/* As of GCC 11.2.1, gcc -Wanalyzer-too-complex reports that main's
use of CHECK macros expands to code that is too complicated for gcc
-fanalyzer. Suppress the resulting bogus warnings. */
-#if 10 <= __GNUC__
+#if _GL_GNUC_PREREQ (10, 0)
# pragma GCC diagnostic ignored "-Wanalyzer-null-argument"
#endif
@@ -80,13 +80,14 @@
#endif
#include <locale.h> /* localeconv() */
+#include <stdint.h> /* PTRDIFF_MAX */
#include <stdio.h> /* snprintf(), sprintf() */
#include <stdlib.h> /* abort(), malloc(), realloc(), free() */
#include <string.h> /* memcpy(), strlen() */
#include <wchar.h> /* mbstate_t, mbrtowc(), mbrlen(), wcrtomb(), mbszero() */
#include <errno.h> /* errno */
-#include <limits.h> /* CHAR_BIT, INT_WIDTH, LONG_WIDTH */
-#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */
+#include <limits.h> /* CHAR_BIT, INT_MAX, INT_WIDTH, LONG_WIDTH */
+#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP, LDBL_MANT_DIG */
#if HAVE_NL_LANGINFO
# include <langinfo.h>
#endif
@@ -231,7 +232,7 @@
#undef remainder
#define remainder rem
-#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && !WIDE_CHAR_VERSION
+#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (PTRDIFF_MAX > INT_MAX)) && !WIDE_CHAR_VERSION
# if (HAVE_STRNLEN && !defined _AIX)
# define local_strnlen strnlen
# else
@@ -247,7 +248,7 @@ local_strnlen (const char *string, size_t maxlen)
# endif
#endif
-#if (((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_WPRINTF_DIRECTIVE_LC) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR)) && HAVE_WCHAR_T
+#if ((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR)
# if HAVE_WCSLEN
# define local_wcslen wcslen
# else
@@ -270,7 +271,7 @@ local_wcslen (const wchar_t *s)
# endif
#endif
-#if (!USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && HAVE_WCHAR_T && WIDE_CHAR_VERSION
+#if (!USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && WIDE_CHAR_VERSION
# if HAVE_WCSNLEN && HAVE_DECL_WCSNLEN
# define local_wcsnlen wcsnlen
# else
@@ -289,7 +290,7 @@ local_wcsnlen (const wchar_t *s, size_t maxlen)
# endif
#endif
-#if (((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T)) && !WIDE_CHAR_VERSION
+#if ((!USE_SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T)) && !WIDE_CHAR_VERSION
# if ENABLE_WCHAR_FALLBACK
static size_t
wctomb_fallback (char *s, wchar_t wc)
@@ -406,8 +407,45 @@ is_infinite_or_zerol (long double x)
#endif
+#if NEED_PRINTF_LONG_DOUBLE
+
+/* Like frexpl, except that it supports even "unsupported" numbers. */
+# if (LDBL_MANT_DIG == 64 && (defined __ia64 || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_))) && (defined __APPLE__ && defined __MACH__)
+/* Don't assume that frexpl can handle pseudo-denormals; it does not on
+ macOS 12/x86_64. Therefore test for a pseudo-denormal explicitly. */
+
+static
+long double safe_frexpl (long double x, int *exp)
+{
+ union
+ {
+ long double value;
+ struct { unsigned int mant_word[2]; unsigned short sign_exp_word; } r;
+ }
+ u;
+ u.value = x;
+ if (u.r.sign_exp_word == 0 && (u.r.mant_word[1] & 0x80000000u) != 0)
+ {
+ /* Pseudo-Denormal. */
+ *exp = LDBL_MIN_EXP;
+ u.r.sign_exp_word = 1 - LDBL_MIN_EXP;
+ return u.value;
+ }
+ else
+ return frexpl (x, exp);
+}
+
+# else
+# define safe_frexpl frexpl
+# endif
+
+#endif
+
#if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE
+/* An indicator for a failed memory allocation. */
+# define NOMEM_PTR ((void *) (-1))
+
/* Converting 'long double' to decimal without rare rounding bugs requires
real bignums. We use the naming conventions of GNU gmp, but vastly simpler
(and slower) algorithms. */
@@ -428,8 +466,8 @@ typedef struct
} mpn_t;
/* Compute the product of two bignums >= 0.
- Return the allocated memory in case of success, NULL in case of memory
- allocation failure. */
+ Return the allocated memory (possibly NULL) in case of success, NOMEM_PTR
+ in case of memory allocation failure. */
static void *
multiply (mpn_t src1, mpn_t src2, mpn_t *dest)
{
@@ -457,7 +495,7 @@ multiply (mpn_t src1, mpn_t src2, mpn_t *dest)
{
/* src1 or src2 is zero. */
dest->nlimbs = 0;
- dest->limbs = (mp_limb_t *) malloc (1);
+ dest->limbs = NULL;
}
else
{
@@ -469,7 +507,7 @@ multiply (mpn_t src1, mpn_t src2, mpn_t *dest)
dlen = len1 + len2;
dp = (mp_limb_t *) malloc (dlen * sizeof (mp_limb_t));
if (dp == NULL)
- return NULL;
+ return NOMEM_PTR;
for (k = len2; k > 0; )
dp[--k] = 0;
for (i = 0; i < len1; i++)
@@ -500,8 +538,8 @@ multiply (mpn_t src1, mpn_t src2, mpn_t *dest)
the remainder.
Finally, round-to-even is performed: If r > b/2 or if r = b/2 and q is odd,
q is incremented.
- Return the allocated memory in case of success, NULL in case of memory
- allocation failure. */
+ Return the allocated memory (possibly NULL) in case of success, NOMEM_PTR
+ in case of memory allocation failure. */
static void *
divide (mpn_t a, mpn_t b, mpn_t *q)
{
@@ -572,7 +610,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q)
final rounding of q.) */
roomptr = (mp_limb_t *) malloc ((a_len + 2) * sizeof (mp_limb_t));
if (roomptr == NULL)
- return NULL;
+ return NOMEM_PTR;
/* Normalise a. */
while (a_len > 0 && a_ptr[a_len - 1] == 0)
@@ -708,7 +746,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q)
if (tmp_roomptr == NULL)
{
free (roomptr);
- return NULL;
+ return NOMEM_PTR;
}
{
const mp_limb_t *sourceptr = b_ptr;
@@ -930,7 +968,7 @@ divide (mpn_t a, mpn_t b, mpn_t *q)
/* Avoid pointless GCC warning "argument 1 value '18446744073709551615' exceeds
maximum object size 9223372036854775807", triggered by the use of xsum as
argument of malloc. */
-# if __GNUC__ >= 7
+# if _GL_GNUC_PREREQ (7, 0)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Walloc-size-larger-than="
# endif
@@ -991,7 +1029,7 @@ convert_to_decimal (mpn_t a, size_t extra_zeroes)
return c_ptr;
}
-# if __GNUC__ >= 7
+# if _GL_GNUC_PREREQ (7, 0)
# pragma GCC diagnostic pop
# endif
@@ -1015,7 +1053,7 @@ decode_long_double (long double x, int *ep, mpn_t *mp)
if (m.limbs == NULL)
return NULL;
/* Split into exponential part and mantissa. */
- y = frexpl (x, &exp);
+ y = safe_frexpl (x, &exp);
if (!(y >= 0.0L && y < 1.0L))
abort ();
/* x = 2^exp * y = 2^(exp - LDBL_MANT_BIT) * (y * 2^LDBL_MANT_BIT), and the
@@ -1306,7 +1344,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n)
mpn_t denominator;
void *tmp_memory;
tmp_memory = multiply (m, pow5, &numerator);
- if (tmp_memory == NULL)
+ if (tmp_memory == NOMEM_PTR)
{
free (pow5_ptr);
free (memory);
@@ -1379,7 +1417,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n)
/* Here y = round (x * 10^n) = z * 10^extra_zeroes. */
- if (z_memory == NULL)
+ if (z_memory == NOMEM_PTR)
return NULL;
digits = convert_to_decimal (z, extra_zeroes);
free (z_memory);
@@ -1442,7 +1480,7 @@ floorlog10l (long double x)
double l;
/* Split into exponential part and mantissa. */
- y = frexpl (x, &exp);
+ y = safe_frexpl (x, &exp);
if (!(y >= 0.0L && y < 1.0L))
abort ();
if (y == 0.0L)
@@ -2111,10 +2149,9 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion,
break;
case 's':
-# if HAVE_WCHAR_T
if (type == TYPE_WIDE_STRING)
{
-# if WIDE_CHAR_VERSION
+# if WIDE_CHAR_VERSION
/* ISO C says about %ls in fwprintf:
"If the precision is not specified or is greater than the size
of the array, the array shall contain a null wide character."
@@ -2125,7 +2162,7 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion,
tmp_length = local_wcsnlen (arg, precision);
else
tmp_length = local_wcslen (arg);
-# else
+# else
/* ISO C says about %ls in fprintf:
"If a precision is specified, no more than that many bytes are
written (including shift sequences, if any), and the array
@@ -2136,10 +2173,9 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion,
So if there is a precision, we must not use wcslen. */
/* This case has already been handled separately in VASNPRINTF. */
abort ();
-# endif
+# endif
}
else
-# endif
{
# if WIDE_CHAR_VERSION
/* ISO C says about %s in fwprintf:
@@ -2423,6 +2459,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
has_width = 1;
}
@@ -2501,7 +2539,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
/* Use the entire string. */
arg_end = arg + u8_strlen (arg);
- /* The number of characters doesn't matter. */
+ /* The number of characters doesn't matter,
+ because !has_width and therefore width==0. */
characters = 0;
}
@@ -2542,7 +2581,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (converted != result + length)
{
ENSURE_ALLOCATION_ELSE (xsum (length, converted_len),
- { free (converted); goto out_of_memory; });
+ { free (converted); goto out_of_memory; });
DCHAR_CPY (result + length, converted, converted_len);
free (converted);
}
@@ -2603,7 +2642,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
/* Use the entire string. */
arg_end = arg + u16_strlen (arg);
- /* The number of characters doesn't matter. */
+ /* The number of characters doesn't matter,
+ because !has_width and therefore width==0. */
characters = 0;
}
@@ -2644,7 +2684,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (converted != result + length)
{
ENSURE_ALLOCATION_ELSE (xsum (length, converted_len),
- { free (converted); goto out_of_memory; });
+ { free (converted); goto out_of_memory; });
DCHAR_CPY (result + length, converted, converted_len);
free (converted);
}
@@ -2705,7 +2745,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
/* Use the entire string. */
arg_end = arg + u32_strlen (arg);
- /* The number of characters doesn't matter. */
+ /* The number of characters doesn't matter,
+ because !has_width and therefore width==0. */
characters = 0;
}
@@ -2746,7 +2787,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (converted != result + length)
{
ENSURE_ALLOCATION_ELSE (xsum (length, converted_len),
- { free (converted); goto out_of_memory; });
+ { free (converted); goto out_of_memory; });
DCHAR_CPY (result + length, converted, converted_len);
free (converted);
}
@@ -2769,7 +2810,190 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
}
#endif
-#if WIDE_CHAR_VERSION && (!DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC)
+#if !WIDE_CHAR_VERSION && (PTRDIFF_MAX > INT_MAX)
+ else if (dp->conversion == 's'
+ && a.arg[dp->arg_index].type != TYPE_WIDE_STRING)
+ {
+ /* %s in vasnprintf. See the specification of fprintf.
+ We handle it ourselves here, because the string may be longer
+ than INT_MAX characters, whence snprintf or sprintf would
+ fail to process it. */
+ int flags = dp->flags;
+ int has_width;
+ size_t width;
+ int has_precision;
+ size_t precision;
+
+ 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;
+ width = arg;
+ if (arg < 0)
+ {
+ /* "A negative field width is taken as a '-' flag
+ followed by a positive field width." */
+ flags |= FLAG_LEFT;
+ width = -width;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->width_start;
+
+ do
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
+ while (digitp != dp->width_end);
+ }
+ if (width > (size_t) INT_MAX)
+ goto overflow;
+ has_width = 1;
+ }
+
+ has_precision = 0;
+ precision = 6;
+ if (dp->precision_start != dp->precision_end)
+ {
+ if (dp->precision_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->precision_arg_index].a.a_int;
+ /* "A negative precision is taken as if the precision
+ were omitted." */
+ if (arg >= 0)
+ {
+ precision = arg;
+ has_precision = 1;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->precision_start + 1;
+
+ precision = 0;
+ while (digitp != dp->precision_end)
+ precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+ has_precision = 1;
+ }
+ }
+
+ {
+ const char *arg = a.arg[dp->arg_index].a.a_string;
+ size_t bytes;
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
+ size_t characters;
+# endif
+# if !DCHAR_IS_TCHAR
+ /* This code assumes that TCHAR_T is 'char'. */
+ static_assert (sizeof (TCHAR_T) == 1);
+ DCHAR_T *tmpdst;
+ size_t tmpdst_len;
+# endif
+ size_t w;
+
+ if (has_precision)
+ {
+ /* Use only at most PRECISION bytes, from the left. */
+ bytes = local_strnlen (arg, precision);
+ }
+ else
+ {
+ /* Use the entire string, and count the number of
+ bytes. */
+ bytes = strlen (arg);
+ }
+
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
+ if (has_width)
+ characters = mbsnlen (arg, bytes);
+ else
+ {
+ /* The number of characters doesn't matter,
+ because !has_width and therefore width==0. */
+ characters = 0;
+ }
+# endif
+
+# if !DCHAR_IS_TCHAR
+ /* Convert from TCHAR_T[] to DCHAR_T[]. */
+ tmpdst =
+ DCHAR_CONV_FROM_ENCODING (locale_charset (),
+ iconveh_question_mark,
+ arg, bytes,
+ NULL,
+ NULL, &tmpdst_len);
+ if (tmpdst == NULL)
+ goto fail_with_errno;
+# endif
+
+ if (has_width)
+ {
+# if ENABLE_UNISTDIO
+ /* Outside POSIX, it's preferable to compare the width
+ against the number of _characters_ of the converted
+ value. */
+# if DCHAR_IS_TCHAR
+ w = characters;
+# else
+ w = DCHAR_MBSNLEN (tmpdst, tmpdst_len);
+# endif
+# else
+ /* The width is compared against the number of _bytes_
+ of the converted value, says POSIX. */
+ w = bytes;
+# endif
+ }
+ else
+ /* w doesn't matter. */
+ w = 0;
+
+ {
+# if DCHAR_IS_TCHAR
+ size_t total = bytes + (w < width ? width - w : 0);
+ ENSURE_ALLOCATION (xsum (length, total));
+# else
+ size_t total = tmpdst_len + (w < width ? width - w : 0);
+ ENSURE_ALLOCATION_ELSE (xsum (length, total),
+ { free (tmpdst); goto out_of_memory; });
+# endif
+
+ if (w < width && !(flags & FLAG_LEFT))
+ {
+ size_t n = width - w;
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+
+# if DCHAR_IS_TCHAR
+ memcpy (result + length, arg, bytes);
+ length += bytes;
+# else
+ DCHAR_CPY (result + length, tmpdst, tmpdst_len);
+ free (tmpdst);
+ length += tmpdst_len;
+# endif
+
+ if (w < width && (flags & FLAG_LEFT))
+ {
+ size_t n = width - w;
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+ }
+ }
+ }
+#endif
+#if WIDE_CHAR_VERSION && ((PTRDIFF_MAX > INT_MAX) || !DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC)
else if ((dp->conversion == 's'
&& a.arg[dp->arg_index].type == TYPE_WIDE_STRING)
|| (dp->conversion == 'c'
@@ -2810,6 +3034,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
}
{
@@ -2912,7 +3138,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
}
#endif
-#if (!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T
+#if WIDE_CHAR_VERSION || !USE_SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK
else if (dp->conversion == 's'
# if WIDE_CHAR_VERSION
&& a.arg[dp->arg_index].type != TYPE_WIDE_STRING
@@ -2965,6 +3191,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
has_width = 1;
}
@@ -3145,11 +3373,13 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string;
const wchar_t *arg_end;
+ size_t bytes;
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
size_t characters;
+# endif
# if !DCHAR_IS_TCHAR
/* This code assumes that TCHAR_T is 'char'. */
static_assert (sizeof (TCHAR_T) == 1);
- TCHAR_T *tmpsrc;
DCHAR_T *tmpdst;
size_t tmpdst_len;
# endif
@@ -3164,7 +3394,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
mbszero (&state);
# endif
arg_end = arg;
+ bytes = 0;
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
characters = 0;
+# endif
while (precision > 0)
{
char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
@@ -3180,7 +3413,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (precision < (unsigned int) count)
break;
arg_end++;
- characters += count;
+ bytes += count;
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
+ characters += mbsnlen (cbuf, count);
+# endif
precision -= count;
}
}
@@ -3197,7 +3433,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
mbszero (&state);
# endif
arg_end = arg;
+ bytes = 0;
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
characters = 0;
+# endif
for (;;)
{
char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
@@ -3211,7 +3450,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
/* Cannot convert. */
goto fail_with_EILSEQ;
arg_end++;
- characters += count;
+ bytes += count;
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
+ characters += mbsnlen (cbuf, count);
+# endif
}
}
# if DCHAR_IS_TCHAR
@@ -3219,56 +3461,64 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
/* Use the entire string. */
arg_end = arg + local_wcslen (arg);
- /* The number of bytes doesn't matter. */
+ /* The number of bytes and characters doesn't matter,
+ because !has_width and therefore width==0. */
+ bytes = 0;
+# if ENABLE_UNISTDIO
characters = 0;
+# endif
}
# endif
# if !DCHAR_IS_TCHAR
- /* Convert the string into a piece of temporary memory. */
- tmpsrc = (TCHAR_T *) malloc (characters * sizeof (TCHAR_T));
- if (tmpsrc == NULL)
- goto out_of_memory;
{
- TCHAR_T *tmpptr = tmpsrc;
- size_t remaining;
+ TCHAR_T *tmpsrc;
+
+ /* Convert the string into a piece of temporary memory. */
+ tmpsrc = (TCHAR_T *) malloc (bytes * sizeof (TCHAR_T));
+ if (tmpsrc == NULL)
+ goto out_of_memory;
+ {
+ TCHAR_T *tmpptr = tmpsrc;
+ size_t remaining;
# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
- mbstate_t state;
- mbszero (&state);
+ mbstate_t state;
+ mbszero (&state);
# endif
- for (remaining = characters; remaining > 0; )
- {
- char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
- int count;
+ for (remaining = bytes; remaining > 0; )
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
- if (*arg == 0)
- abort ();
- count = local_wcrtomb (cbuf, *arg, &state);
- if (count <= 0)
- /* Inconsistency. */
- abort ();
- memcpy (tmpptr, cbuf, count);
- tmpptr += count;
- arg++;
- remaining -= count;
+ if (*arg == 0)
+ abort ();
+ count = local_wcrtomb (cbuf, *arg, &state);
+ if (count <= 0)
+ /* Inconsistency. */
+ abort ();
+ memcpy (tmpptr, cbuf, count);
+ tmpptr += count;
+ arg++;
+ remaining -= count;
+ }
+ if (!(arg == arg_end))
+ abort ();
+ }
+
+ /* Convert from TCHAR_T[] to DCHAR_T[]. */
+ tmpdst =
+ DCHAR_CONV_FROM_ENCODING (locale_charset (),
+ iconveh_question_mark,
+ tmpsrc, bytes,
+ NULL,
+ NULL, &tmpdst_len);
+ if (tmpdst == NULL)
+ {
+ free (tmpsrc);
+ goto fail_with_errno;
}
- if (!(arg == arg_end))
- abort ();
+ free (tmpsrc);
}
-
- /* Convert from TCHAR_T[] to DCHAR_T[]. */
- tmpdst =
- DCHAR_CONV_FROM_ENCODING (locale_charset (),
- iconveh_question_mark,
- tmpsrc, characters,
- NULL,
- NULL, &tmpdst_len);
- if (tmpdst == NULL)
- {
- free (tmpsrc);
- goto fail_with_errno;
- }
- free (tmpsrc);
# endif
if (has_width)
@@ -3277,11 +3527,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
/* Outside POSIX, it's preferable to compare the width
against the number of _characters_ of the converted
value. */
- w = DCHAR_MBSNLEN (result + length, characters);
+# if DCHAR_IS_TCHAR
+ w = characters;
+# else
+ w = DCHAR_MBSNLEN (tmpdst, tmpdst_len);
+# endif
# else
/* The width is compared against the number of _bytes_
of the converted value, says POSIX. */
- w = characters;
+ w = bytes;
# endif
}
else
@@ -3291,7 +3545,12 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (w < width && !(flags & FLAG_LEFT))
{
size_t n = width - w;
+# if DCHAR_IS_TCHAR
ENSURE_ALLOCATION (xsum (length, n));
+# else
+ ENSURE_ALLOCATION_ELSE (xsum (length, n),
+ { free (tmpdst); goto out_of_memory; });
+# endif
DCHAR_SET (result + length, ' ', n);
length += n;
}
@@ -3305,8 +3564,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
mbstate_t state;
mbszero (&state);
# endif
- ENSURE_ALLOCATION (xsum (length, characters));
- for (remaining = characters; remaining > 0; )
+ ENSURE_ALLOCATION (xsum (length, bytes));
+ for (remaining = bytes; remaining > 0; )
{
char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
int count;
@@ -3350,7 +3609,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
# else
ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len),
- { free (tmpdst); goto out_of_memory; });
+ { free (tmpdst); goto out_of_memory; });
DCHAR_CPY (result + length, tmpdst, tmpdst_len);
free (tmpdst);
length += tmpdst_len;
@@ -3406,17 +3665,21 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
has_width = 1;
}
/* %lc in vasnprintf. See the specification of fprintf. */
{
wchar_t arg = (wchar_t) a.arg[dp->arg_index].a.a_wide_char;
+ size_t bytes;
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
size_t characters;
+# endif
# if !DCHAR_IS_TCHAR
/* This code assumes that TCHAR_T is 'char'. */
static_assert (sizeof (TCHAR_T) == 1);
- TCHAR_T tmpsrc[64]; /* Assume MB_CUR_MAX <= 64. */
DCHAR_T *tmpdst;
size_t tmpdst_len;
# endif
@@ -3427,7 +3690,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
# endif
{
/* Count the number of bytes. */
- characters = 0;
char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
int count;
# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
@@ -3439,43 +3701,54 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (count < 0)
/* Cannot convert. */
goto fail_with_EILSEQ;
- characters = count;
+ bytes = count;
+# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR
+ characters = mbsnlen (cbuf, count);
+# endif
}
# if DCHAR_IS_TCHAR
else
{
- /* The number of bytes doesn't matter. */
+ /* The number of bytes and characters doesn't matter,
+ because !has_width and therefore width==0. */
+ bytes = 0;
+# if ENABLE_UNISTDIO
characters = 0;
+# endif
}
# endif
# if !DCHAR_IS_TCHAR
- /* Convert the string into a piece of temporary memory. */
- if (characters > 0)
- {
- char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
- int count;
+ {
+ TCHAR_T tmpsrc[64]; /* Assume MB_CUR_MAX <= 64. */
+
+ /* Convert the string into a piece of temporary memory. */
+ if (bytes > 0)
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
- mbstate_t state;
- mbszero (&state);
+ mbstate_t state;
+ mbszero (&state);
# endif
- count = local_wcrtomb (cbuf, arg, &state);
- if (count <= 0)
- /* Inconsistency. */
- abort ();
- memcpy (tmpsrc, cbuf, count);
- }
+ count = local_wcrtomb (cbuf, arg, &state);
+ if (count <= 0)
+ /* Inconsistency. */
+ abort ();
+ memcpy (tmpsrc, cbuf, count);
+ }
- /* Convert from TCHAR_T[] to DCHAR_T[]. */
- tmpdst =
- DCHAR_CONV_FROM_ENCODING (locale_charset (),
- iconveh_question_mark,
- tmpsrc, characters,
- NULL,
- NULL, &tmpdst_len);
- if (tmpdst == NULL)
- goto fail_with_errno;
+ /* Convert from TCHAR_T[] to DCHAR_T[]. */
+ tmpdst =
+ DCHAR_CONV_FROM_ENCODING (locale_charset (),
+ iconveh_question_mark,
+ tmpsrc, bytes,
+ NULL,
+ NULL, &tmpdst_len);
+ if (tmpdst == NULL)
+ goto fail_with_errno;
+ }
# endif
if (has_width)
@@ -3484,11 +3757,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
/* Outside POSIX, it's preferable to compare the width
against the number of _characters_ of the converted
value. */
- w = DCHAR_MBSNLEN (result + length, characters);
+# if DCHAR_IS_TCHAR
+ w = characters;
+# else
+ w = DCHAR_MBSNLEN (tmpdst, tmpdst_len);
+# endif
# else
/* The width is compared against the number of _bytes_
of the converted value, says POSIX. */
- w = characters;
+ w = bytes;
# endif
}
else
@@ -3498,7 +3775,12 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (w < width && !(flags & FLAG_LEFT))
{
size_t n = width - w;
+# if DCHAR_IS_TCHAR
ENSURE_ALLOCATION (xsum (length, n));
+# else
+ ENSURE_ALLOCATION_ELSE (xsum (length, n),
+ { free (tmpdst); goto out_of_memory; });
+# endif
DCHAR_SET (result + length, ' ', n);
length += n;
}
@@ -3507,8 +3789,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (has_width)
{
/* We know the number of bytes in advance. */
- ENSURE_ALLOCATION (xsum (length, characters));
- if (characters > 0)
+ ENSURE_ALLOCATION (xsum (length, bytes));
+ if (bytes > 0)
{
int count;
# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
@@ -3542,7 +3824,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
# else
ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len),
- { free (tmpdst); goto out_of_memory; });
+ { free (tmpdst); goto out_of_memory; });
DCHAR_CPY (result + length, tmpdst, tmpdst_len);
free (tmpdst);
length += tmpdst_len;
@@ -3594,6 +3876,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
}
/* %c in vasnwprintf. See the specification of fwprintf. */
@@ -3608,24 +3892,26 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
/* Invalid or incomplete multibyte character. */
goto fail_with_EILSEQ;
- if (1 < width && !(flags & FLAG_LEFT))
- {
- size_t n = width - 1;
- ENSURE_ALLOCATION (xsum (length, n));
- DCHAR_SET (result + length, ' ', n);
- length += n;
- }
+ {
+ size_t total = (1 < width ? width : 1);
+ ENSURE_ALLOCATION (xsum (length, total));
+
+ if (1 < width && !(flags & FLAG_LEFT))
+ {
+ size_t n = width - 1;
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
- ENSURE_ALLOCATION (xsum (length, 1));
- result[length++] = wc;
+ result[length++] = wc;
- if (1 < width && (flags & FLAG_LEFT))
- {
- size_t n = width - 1;
- ENSURE_ALLOCATION (xsum (length, n));
- DCHAR_SET (result + length, ' ', n);
- length += n;
- }
+ if (1 < width && (flags & FLAG_LEFT))
+ {
+ size_t n = width - 1;
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+ }
}
}
#endif
@@ -3682,6 +3968,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
has_width = 1;
}
@@ -3933,7 +4221,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
size_t n = xsum (length, count);
- ENSURE_ALLOCATION (n);
+ ENSURE_ALLOCATION_ELSE (n,
+ { if (tmp != tmpbuf) free (tmp); goto out_of_memory; });
}
/* Append the result. */
@@ -3996,6 +4285,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
}
has_precision = 0;
@@ -4423,7 +4714,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
size_t n = xsum (length, count);
- ENSURE_ALLOCATION (n);
+ ENSURE_ALLOCATION_ELSE (n,
+ { if (tmp != tmpbuf) free (tmp); goto out_of_memory; });
}
/* Append the result. */
@@ -4501,6 +4793,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
}
has_precision = 0;
@@ -5606,7 +5900,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
size_t n = xsum (length, count);
- ENSURE_ALLOCATION (n);
+ ENSURE_ALLOCATION_ELSE (n,
+ { if (tmp != tmpbuf) free (tmp); goto out_of_memory; });
}
/* Append the result. */
@@ -5683,6 +5978,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+ if (width > (size_t) INT_MAX)
+ goto overflow;
+# define WIDTH_IS_CHECKED 1
# if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_FLAG_ALT_PRECISION_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
has_width = 1;
# endif
@@ -5832,6 +6130,43 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (dp->width_start != dp->width_end)
{
size_t n = dp->width_end - dp->width_start;
+#if !WIDTH_IS_CHECKED
+ size_t width;
+ /* Reject an out-of-range width.
+ The underlying SNPRINTF already does this on some
+ platforms (glibc, musl, macOS, FreeBSD, NetBSD,
+ OpenBSD, Cygwin, Solaris, MSVC). However, on others
+ (AIX, mingw), it doesn't; thus this vasnprintf
+ invocation would succeed and produce a wrong result.
+ So, this is redundant on some platforms, but it's a
+ quick check anyway. */
+ 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;
+ if (arg < 0)
+ {
+ /* "A negative field width is taken as a '-' flag
+ followed by a positive field width." */
+ width = -width;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->width_start;
+
+ width = 0;
+ do
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
+ while (digitp != dp->width_end);
+ }
+ if (width > (size_t) INT_MAX)
+ goto overflow;
+#endif
/* The width specification is known to consist only
of standard ASCII characters. */
if (sizeof (FCHAR_T) == sizeof (TCHAR_T))
@@ -5984,9 +6319,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
#if HAVE_WINT_T
case TYPE_WIDE_CHAR:
#endif
- #if HAVE_WCHAR_T
case TYPE_WIDE_STRING:
- #endif
*fbp++ = 'l';
break;
case TYPE_LONGDOUBLE:
@@ -6358,14 +6691,12 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
SNPRINTF_BUF (arg);
}
break;
-#if HAVE_WCHAR_T
case TYPE_WIDE_STRING:
{
const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string;
SNPRINTF_BUF (arg);
}
break;
-#endif
case TYPE_POINTER:
{
void *arg = a.arg[dp->arg_index].a.a_pointer;
@@ -6677,7 +7008,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
goto fail_with_errno;
# endif
ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len),
- { free (tmpdst); goto out_of_memory; });
+ { free (tmpdst); goto out_of_memory; });
DCHAR_CPY (result + length, tmpdst, tmpdst_len);
free (tmpdst);
count = tmpdst_len;
@@ -6929,17 +7260,15 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
not have this limitation. */
return result;
-#if USE_SNPRINTF
overflow:
errno = EOVERFLOW;
goto fail_with_errno;
-#endif
out_of_memory:
errno = ENOMEM;
goto fail_with_errno;
-#if ENABLE_UNISTDIO || ((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION) || (NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION)
+#if ENABLE_UNISTDIO || (WIDE_CHAR_VERSION || !USE_SNPRINTF || (PTRDIFF_MAX > INT_MAX) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION) || (NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION)
fail_with_EILSEQ:
errno = EILSEQ;
goto fail_with_errno;