aboutsummaryrefslogtreecommitdiff
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
glibc-fw/bug24562.tar.gz
glibc-fw/bug24562.tar.bz2
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;
}