From 4cada7ff4ddfc45f204f042ff2acfbac851eb25f Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Wed, 19 Jun 2024 20:46:47 +0200 Subject: [PATCH] vasnprintf, u*-vasnprintf: Support string arguments longer than 2 GiB. * lib/vasnprintf.c: Include . (VASNPRINTF): In 64-bit builds, handle the %s directive ourselves. (local_strnlen): Adjust #if condition. * modules/unistdio/u8-vasnprintf (Depends-on): Add stdint. * modules/unistdio/u8-u8-vasnprintf (Depends-on): Likewise. * modules/unistdio/u16-vasnprintf (Depends-on): Likewise. * modules/unistdio/u16-u16-vasnprintf (Depends-on): Likewise. * modules/unistdio/u32-vasnprintf (Depends-on): Likewise. * modules/unistdio/u32-u32-vasnprintf (Depends-on): Likewise. * modules/unistdio/ulc-vasnprintf (Depends-on): Likewise. --- ChangeLog | 14 +++ lib/vasnprintf.c | 186 +++++++++++++++++++++++++++- modules/unistdio/u16-u16-vasnprintf | 1 + modules/unistdio/u16-vasnprintf | 1 + modules/unistdio/u32-u32-vasnprintf | 1 + modules/unistdio/u32-vasnprintf | 1 + modules/unistdio/u8-u8-vasnprintf | 1 + modules/unistdio/u8-vasnprintf | 1 + modules/unistdio/ulc-vasnprintf | 1 + 9 files changed, 206 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 0f89dacf4a..fc98fc99af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2024-06-19 Bruno Haible + + vasnprintf, u*-vasnprintf: Support string arguments longer than 2 GiB. + * lib/vasnprintf.c: Include . + (VASNPRINTF): In 64-bit builds, handle the %s directive ourselves. + (local_strnlen): Adjust #if condition. + * modules/unistdio/u8-vasnprintf (Depends-on): Add stdint. + * modules/unistdio/u8-u8-vasnprintf (Depends-on): Likewise. + * modules/unistdio/u16-vasnprintf (Depends-on): Likewise. + * modules/unistdio/u16-u16-vasnprintf (Depends-on): Likewise. + * modules/unistdio/u32-vasnprintf (Depends-on): Likewise. + * modules/unistdio/u32-u32-vasnprintf (Depends-on): Likewise. + * modules/unistdio/ulc-vasnprintf (Depends-on): Likewise. + 2024-06-19 Bruno Haible vasnwprintf: Optimize handling of %c directive. diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 1838ded22d..1178822df8 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -80,6 +80,7 @@ #endif #include /* localeconv() */ +#include /* PTRDIFF_MAX */ #include /* snprintf(), sprintf() */ #include /* abort(), malloc(), realloc(), free() */ #include /* memcpy(), strlen() */ @@ -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 @@ -2809,6 +2810,189 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } } #endif +#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 && (!DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC) else if ((dp->conversion == 's' && a.arg[dp->arg_index].type == TYPE_WIDE_STRING) diff --git a/modules/unistdio/u16-u16-vasnprintf b/modules/unistdio/u16-u16-vasnprintf index ef5886cb9d..98adbbb37a 100644 --- a/modules/unistdio/u16-u16-vasnprintf +++ b/modules/unistdio/u16-u16-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u16-vasnprintf b/modules/unistdio/u16-vasnprintf index 7769617562..de541ef1ca 100644 --- a/modules/unistdio/u16-vasnprintf +++ b/modules/unistdio/u16-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u32-u32-vasnprintf b/modules/unistdio/u32-u32-vasnprintf index 587e4adf99..168cdbd7a9 100644 --- a/modules/unistdio/u32-u32-vasnprintf +++ b/modules/unistdio/u32-u32-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u32-vasnprintf b/modules/unistdio/u32-vasnprintf index 84466c8b87..bdea7468b1 100644 --- a/modules/unistdio/u32-vasnprintf +++ b/modules/unistdio/u32-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u8-u8-vasnprintf b/modules/unistdio/u8-u8-vasnprintf index d34f513485..5168794bf1 100644 --- a/modules/unistdio/u8-u8-vasnprintf +++ b/modules/unistdio/u8-u8-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u8-vasnprintf b/modules/unistdio/u8-vasnprintf index dd71ae96a9..cf83b9343c 100644 --- a/modules/unistdio/u8-vasnprintf +++ b/modules/unistdio/u8-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/ulc-vasnprintf b/modules/unistdio/ulc-vasnprintf index b2476b1e65..bea310b874 100644 --- a/modules/unistdio/ulc-vasnprintf +++ b/modules/unistdio/ulc-vasnprintf @@ -27,6 +27,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint mbsnlen isnand-nolibm isnanl-nolibm -- 2.39.5