about summary refs log tree commit diff
path: root/stdio-common/fxprintf.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2019-05-16 12:08:37 +0200
committerFlorian Weimer <fweimer@redhat.com>2019-05-16 12:08:38 +0200
commitd9d04be3f0c0e86ce2cf1738a6b6544df3aecb75 (patch)
tree6a73e7e54ca84cdea9cccb2569b19fa2ea3449f0 /stdio-common/fxprintf.c
parent11b451c8868d8a2b0edc5dfd44fc58d9ee538be0 (diff)
downloadglibc-d9d04be3f0c0e86ce2cf1738a6b6544df3aecb75.tar.gz
glibc-d9d04be3f0c0e86ce2cf1738a6b6544df3aecb75.tar.xz
glibc-d9d04be3f0c0e86ce2cf1738a6b6544df3aecb75.zip
WIP Support exotic character sets in __fxprintf [BZ #24562] fw/bug24562
This does not quite work because vfprintf has a similar
incorrect assumption.
Diffstat (limited to 'stdio-common/fxprintf.c')
-rw-r--r--stdio-common/fxprintf.c72
1 files changed, 48 insertions, 24 deletions
diff --git a/stdio-common/fxprintf.c b/stdio-common/fxprintf.c
index 9866c8b3be..8d1769fb24 100644
--- a/stdio-common/fxprintf.c
+++ b/stdio-common/fxprintf.c
@@ -23,6 +23,18 @@
 #include <wchar.h>
 #include <libioP.h>
 
+/* See libio/fwprintf.c.  */
+static int
+call_fwprintf (FILE *stream, unsigned int mode_flags,
+	       const wchar_t *format, ...)
+{
+  va_list arg;
+  va_start (arg, format);
+  int done = __vfwprintf_internal (stream, format, arg, mode_flags);
+  va_end (arg);
+  return done;
+}
+
 static int
 locked_vfxprintf (FILE *fp, const char *fmt, va_list ap,
 		  unsigned int mode_flags)
@@ -30,34 +42,46 @@ locked_vfxprintf (FILE *fp, const char *fmt, va_list ap,
   if (_IO_fwide (fp, 0) <= 0)
     return __vfprintf_internal (fp, fmt, ap, mode_flags);
 
-  /* We must convert the narrow format string to a wide one.
-     Each byte can produce at most one wide character.  */
-  wchar_t *wfmt;
-  mbstate_t mbstate;
-  int res;
-  int used_malloc = 0;
-  size_t len = strlen (fmt) + 1;
-
-  if (__glibc_unlikely (len > SIZE_MAX / sizeof (wchar_t)))
+  int saved_errno = errno;
+
+  /* Format the narrow string as a multibyte string.  Try to use an
+     on-stack buffer first, to avoid the heap allocation.  */
+  char buffer[512];
+  va_list ap1;
+  va_copy (ap1, ap);
+  int res = __vsnprintf_internal (buffer, sizeof (buffer),
+				  fmt, ap1, mode_flags);
+  va_end (ap1);
+  if (res < 0)
+    return res;
+  char *ptr;
+  if (res < sizeof (buffer))
+    ptr = buffer;
+  else
     {
-      __set_errno (EOVERFLOW);
-      return -1;
+      /* Use a heap allocation for a large buffer.  */
+      if (res == INT_MAX)
+	{
+	  __set_errno (EOVERFLOW);
+	  return -1;
+	}
+      size_t len = res + 1;
+      ptr = malloc (len);
+      if (ptr == NULL)
+	return -1;
+      __set_errno (saved_errno);
+      res = __vsnprintf_internal (ptr, len, fmt, ap, mode_flags);
+      if (res < 0)
+	return -1;
     }
-  if (__libc_use_alloca (len * sizeof (wchar_t)))
-    wfmt = alloca (len * sizeof (wchar_t));
-  else if ((wfmt = malloc (len * sizeof (wchar_t))) == NULL)
-    return -1;
-  else
-    used_malloc = 1;
-
-  memset (&mbstate, 0, sizeof mbstate);
-  res = __mbsrtowcs (wfmt, &fmt, len, &mbstate);
 
-  if (res != -1)
-    res = __vfwprintf_internal (fp, wfmt, ap, mode_flags);
+  /* Write the formatted multibyte string to the wide stream.  */
+  if (res >= 0)
+    res = call_fwprintf (fp, mode_flags, L"%s", ptr);
 
-  if (used_malloc)
-    free (wfmt);
+  if (ptr != buffer)
+    free (ptr);
+  __set_errno (saved_errno);
 
   return res;
 }