about summary refs log tree commit diff
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
parent11b451c8868d8a2b0edc5dfd44fc58d9ee538be0 (diff)
downloadglibc-fw/bug24562.tar.gz
glibc-fw/bug24562.tar.xz
glibc-fw/bug24562.zip
WIP Support exotic character sets in __fxprintf [BZ #24562] fw/bug24562
This does not quite work because vfprintf has a similar
incorrect assumption.
-rw-r--r--ChangeLog6
-rw-r--r--misc/Makefile8
-rw-r--r--misc/tst-warn-wide-tscii.c63
-rw-r--r--stdio-common/fxprintf.c72
4 files changed, 124 insertions, 25 deletions
diff --git a/ChangeLog b/ChangeLog
index ea7b3d4f48..4cd9f40416 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2019-05-16  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #24562]
+	* stdio-common/fxprintf.c (call_fwprintf): New function.
+	(locked_vfxprintf): Use it.
+
 2019-05-15  Mark Wielaard  <mark@klomp.org>
 
 	[BZ# 24476]
diff --git a/misc/Makefile b/misc/Makefile
index 032f28fc38..5669c859bf 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -86,7 +86,8 @@ tests := tst-dirname tst-tsearch tst-fdset tst-mntent tst-hsearch \
 	 tst-mntent-blank-corrupt tst-mntent-blank-passno bug18240 \
 	 tst-preadvwritev tst-preadvwritev64 tst-makedev tst-empty \
 	 tst-preadvwritev2 tst-preadvwritev64v2 tst-warn-wide \
-	 tst-ldbl-warn tst-ldbl-error tst-dbl-efgcvt tst-ldbl-efgcvt
+	 tst-ldbl-warn tst-ldbl-error tst-dbl-efgcvt tst-ldbl-efgcvt \
+	 tst-warn-wide-tscii
 
 # Tests which need libdl.
 ifeq (yes,$(build-shared))
@@ -138,6 +139,11 @@ CFLAGS-brk.op = $(no-stack-protector)
 
 include ../Rules
 
+LOCALES := en_US.TSCII
+include ../gen-locales.mk
+
+$(objpfx)tst-warn-wide-tscii.out: $(gen-locales)
+
 $(objpfx)libg.a: $(dep-dummy-lib); $(make-dummy-lib)
 
 $(objpfx)tst-tsearch: $(libm)
diff --git a/misc/tst-warn-wide-tscii.c b/misc/tst-warn-wide-tscii.c
new file mode 100644
index 0000000000..d32ddcc97b
--- /dev/null
+++ b/misc/tst-warn-wide-tscii.c
@@ -0,0 +1,63 @@
+/* Test wide output conversion for warn with TSCII.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <err.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+#include <wchar.h>
+
+static int
+do_test (void)
+{
+  if (setlocale (LC_ALL, "en_US.TSCII") == NULL)
+    FAIL_EXIT1 ("setlocale");
+
+  char *path;
+  xclose (create_temp_file ("tst-warn-wide-tscii-", &path));
+  FILE *fp = xfopen (path, "w,ccs=UTF-8");
+  TEST_COMPARE (fwide (fp, 0), 1);
+
+  FILE *old_stderr = stderr;
+  stderr = fp;
+
+  /* Special character that expands to four wide characters.  */
+  warnx ("[[\x8C]]");
+
+  stderr = old_stderr;
+
+  /* Verify that the expected data was written.  */
+  xfclose (fp);
+  fp = xfopen (path, "rb");
+  char line[1024];
+  TEST_VERIFY (fgets (line, sizeof (line), fp) != NULL);
+  TEST_COMPARE_STRING (line, "tst-warn-wide-tscii: [["
+                       "\xe0\xae\x95\xe0\xaf\x8d\xe0\xae\xb7\xe0\xaf\x8d]]\n");
+  TEST_COMPARE (fgetc (fp), EOF);
+  TEST_VERIFY (feof (fp));
+  TEST_VERIFY (!ferror (fp));
+
+  free (path);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
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;
 }