]> Savannah Git Hosting - gnulib.git/commitdiff
vasnprintf, u*-vasnprintf: Support string arguments longer than 2 GiB.
authorBruno Haible <bruno@clisp.org>
Wed, 19 Jun 2024 18:46:47 +0000 (20:46 +0200)
committerBruno Haible <bruno@clisp.org>
Wed, 19 Jun 2024 18:47:50 +0000 (20:47 +0200)
* lib/vasnprintf.c: Include <stdint.h>.
(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
lib/vasnprintf.c
modules/unistdio/u16-u16-vasnprintf
modules/unistdio/u16-vasnprintf
modules/unistdio/u32-u32-vasnprintf
modules/unistdio/u32-vasnprintf
modules/unistdio/u8-u8-vasnprintf
modules/unistdio/u8-vasnprintf
modules/unistdio/ulc-vasnprintf

index 0f89dacf4aa758fa2261e794d14d34fd09af0df3..fc98fc99af1d46364b3e837fce39eb51e6e9e334 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2024-06-19  Bruno Haible  <bruno@clisp.org>
+
+       vasnprintf, u*-vasnprintf: Support string arguments longer than 2 GiB.
+       * lib/vasnprintf.c: Include <stdint.h>.
+       (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  <bruno@clisp.org>
 
        vasnwprintf: Optimize handling of %c directive.
index 1838ded22db8a815a4f7524372f02d8e50b6016e..1178822df8ad4fd2bf311629b5e7e7fd6eef3cc4 100644 (file)
@@ -80,6 +80,7 @@
 #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() */
 #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)
index ef5886cb9d6a03ce60866371dbe18a59a8d5a2e1..98adbbb37acb0a5fe3ed88edce2883076526d9f1 100644 (file)
@@ -30,6 +30,7 @@ unistr/u16-strmblen
 unistr/u32-strlen
 unistr/u32-strmblen
 attribute
+stdint
 isnand-nolibm
 isnanl-nolibm
 frexpl-nolibm
index 776961756276c6723c3cb247fe22c88527cb9cde..de541ef1ca60d9d5f6d69094f5e89eef6ae66cea 100644 (file)
@@ -30,6 +30,7 @@ unistr/u16-strmblen
 unistr/u32-strlen
 unistr/u32-strmblen
 attribute
+stdint
 isnand-nolibm
 isnanl-nolibm
 frexpl-nolibm
index 587e4adf990aff51f94a501fb96f9b77b4ae49ec..168cdbd7a99cdc168783af378fb6449b53420ff5 100644 (file)
@@ -30,6 +30,7 @@ unistr/u16-strmblen
 unistr/u32-strlen
 unistr/u32-strmblen
 attribute
+stdint
 isnand-nolibm
 isnanl-nolibm
 frexpl-nolibm
index 84466c8b871e08fcb1060c0c80329d756025f22c..bdea7468b10b5393deb7ea5a32d7b277734a23b7 100644 (file)
@@ -30,6 +30,7 @@ unistr/u16-strmblen
 unistr/u32-strlen
 unistr/u32-strmblen
 attribute
+stdint
 isnand-nolibm
 isnanl-nolibm
 frexpl-nolibm
index d34f513485fc57107073262d26ff17652a3ec42d..5168794bf180bf5795589246a5e118971c5a84c2 100644 (file)
@@ -30,6 +30,7 @@ unistr/u16-strmblen
 unistr/u32-strlen
 unistr/u32-strmblen
 attribute
+stdint
 isnand-nolibm
 isnanl-nolibm
 frexpl-nolibm
index dd71ae96a9547d31632b356314b48e6ac894e56e..cf83b9343cb7c99f1112695786bedd6371cfdb2a 100644 (file)
@@ -30,6 +30,7 @@ unistr/u16-strmblen
 unistr/u32-strlen
 unistr/u32-strmblen
 attribute
+stdint
 isnand-nolibm
 isnanl-nolibm
 frexpl-nolibm
index b2476b1e65e02642412d59bf67209dba1f096ccc..bea310b8747c24ed7fe0c598bab5d98923b289f9 100644 (file)
@@ -27,6 +27,7 @@ unistr/u16-strmblen
 unistr/u32-strlen
 unistr/u32-strmblen
 attribute
+stdint
 mbsnlen
 isnand-nolibm
 isnanl-nolibm