* [PATCH 1/2] libstdc++: Implement C++23 <print> header [PR107760]
@ 2023-11-17 15:54 Jonathan Wakely
2023-11-17 15:54 ` [PATCH 2/2] libstdc++: Ensure valid UTF-8 in std::vprint_unicode Jonathan Wakely
0 siblings, 1 reply; 2+ messages in thread
From: Jonathan Wakely @ 2023-11-17 15:54 UTC (permalink / raw)
To: libstdc++, gcc-patches
There's a TODO here about checking for invalid UTF-8, which is done by
the next patch.
I don't know if the Windows code actually works. I tried to test it with
mingw and Wine, but I got garbled text. But I'm not sure if that's my
code here, or the conversion to UTF-16, or how I'm testing, or just that
Wine in a Linux terminal doesn't properly emulat the Windows console, or
something else.
This needs tests, so I need to write them before pushing, but I still
plan to get that done for GCC 14.
-- >8 --
libstdc++-v3/ChangeLog:
PR libstdc++/107760
* config/abi/pre/gnu.ver: Export new symbols.
* include/Makefile.am: Add new header.
* include/Makefile.in: Regenerate.
* include/bits/version.def (__cpp_lib_print): Define.
* include/bits/version.h: Regenerate.
* include/std/ostream (vprintf_nonunicode, vprintf_unicode)
(print, println): New functions.
* include/std/print: New file.
* src/c++20/Makefile.am: Add new source file.
* src/c++20/Makefile.in: Regenerate.
* src/c++98/globals_io.cc [_WIN32] (__fd_for_console): New
function.
* src/c++20/print.cc: New file.
---
libstdc++-v3/config/abi/pre/gnu.ver | 4 +
libstdc++-v3/include/Makefile.am | 1 +
libstdc++-v3/include/Makefile.in | 1 +
libstdc++-v3/include/bits/version.def | 9 ++
libstdc++-v3/include/bits/version.h | 29 ++++--
libstdc++-v3/include/std/ostream | 102 ++++++++++++++++++++
libstdc++-v3/include/std/print | 128 ++++++++++++++++++++++++++
libstdc++-v3/src/c++20/Makefile.am | 2 +-
libstdc++-v3/src/c++20/Makefile.in | 4 +-
libstdc++-v3/src/c++20/print.cc | 35 +++++++
libstdc++-v3/src/c++98/globals_io.cc | 23 +++++
11 files changed, 326 insertions(+), 12 deletions(-)
create mode 100644 libstdc++-v3/include/std/print
create mode 100644 libstdc++-v3/src/c++20/print.cc
diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 15b50d51251..c7200929e34 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2514,6 +2514,10 @@ GLIBCXX_3.4.31 {
_ZNKSt12__shared_ptrINSt10filesystem28recursive_directory_iterator10_Dir_stackELN9__gnu_cxx12_Lock_policyE[012]EEcvbEv;
_ZNKSt12__shared_ptrINSt10filesystem7__cxx1128recursive_directory_iterator10_Dir_stackELN9__gnu_cxx12_Lock_policyE[012]EEcvbEv;
+ # These are only defined for *-*-mingw*
+ _ZSt16__fd_for_consolePSt15basic_streambufIcSt11char_traitsIcEE;
+ _ZSt24__write_utf16_to_consoleiNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE;
+
} GLIBCXX_3.4.30;
GLIBCXX_3.4.32 {
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 17d9d9cec31..368b92eafbc 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -85,6 +85,7 @@ std_headers = \
${std_srcdir}/memory_resource \
${std_srcdir}/mutex \
${std_srcdir}/ostream \
+ ${std_srcdir}/print \
${std_srcdir}/queue \
${std_srcdir}/random \
${std_srcdir}/regex \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index f038af709cc..a31588c0100 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -441,6 +441,7 @@ std_freestanding = \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/memory_resource \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/mutex \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/ostream \
+@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/print \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/queue \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/random \
@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/regex \
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 15bd502f52c..8b5cace3775 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1578,6 +1578,15 @@ ftms = {
};
};
+ftms = {
+ name = print;
+ values = {
+ v = 202211;
+ cxxmin = 23;
+ hosted = yes;
+ };
+};
+
ftms = {
name = spanstream;
values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 9563b6cd2f7..f197408e60f 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1923,6 +1923,17 @@
#undef __glibcxx_want_out_ptr
// from version.def line 1582
+#if !defined(__cpp_lib_print)
+# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
+# define __glibcxx_print 202211L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_print)
+# define __cpp_lib_print 202211L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_print) && defined(__glibcxx_want_print) */
+#undef __glibcxx_want_print
+
+// from version.def line 1591
#if !defined(__cpp_lib_spanstream)
# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (__glibcxx_span)
# define __glibcxx_spanstream 202106L
diff --git a/libstdc++-v3/include/std/ostream b/libstdc++-v3/include/std/ostream
index 1de1c1bd359..e81c39a7c80 100644
--- a/libstdc++-v3/include/std/ostream
+++ b/libstdc++-v3/include/std/ostream
@@ -39,6 +39,11 @@
#include <ios>
#include <bits/ostream_insert.h>
+#if __cplusplus > 202002L
+# include <format>
+#endif
+
+# define __glibcxx_want_print
#include <bits/version.h> // __glibcxx_syncbuf
namespace std _GLIBCXX_VISIBILITY(default)
@@ -872,6 +877,103 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#endif // __glibcxx_syncbuf
+#if __cpp_lib_print // C++ >= 23
+
+ inline void
+ vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args)
+ {
+ ostream::sentry __cerb(__os);
+ if (__cerb)
+ {
+ string __out = std::vformat(__fmt, __args);
+ __try
+ {
+ const streamsize __w = __os.width();
+ const streamsize __n = __out.size();
+ if (__w > __n)
+ {
+ const bool __left
+ = (__os.flags() & ios_base::adjustfield) == ios_base::left;
+ if (!__left)
+ std::__ostream_fill(__os, __w - __n);
+ if (__os.good())
+ std::__ostream_write(__os, __out.data(), __n);
+ if (__left && __os.good())
+ std::__ostream_fill(__os, __w - __n);
+ }
+ else
+ std::__ostream_write(__os, __out.data(), __n);
+ }
+ __catch(const __cxxabiv1::__forced_unwind&)
+ {
+ __os._M_setstate(ios_base::badbit);
+ __throw_exception_again;
+ }
+ __catch(...)
+ { __os._M_setstate(ios_base::badbit); }
+ }
+ }
+
+ inline void
+ vprint_unicode(ostream& __os, string_view __fmt, format_args __args)
+ {
+ // TODO: diagnose invalid UTF-8 code units
+#ifdef _WIN32
+ int __fd_for_console(std::streambuf*);
+ void __write_utf16_to_console(int, string);
+
+ // If stream refers to a terminal convert to UTF-16 and use WriteConsoleW.
+ if (int __fd = __fd_for_console(__os.rdbuf()); __fd >= 0)
+ {
+ ostream::sentry __cerb(__os);
+ if (__cerb)
+ {
+ string __out = std::vformat(__fmt, __args);
+ ios_base::iostate __err = ios_base::goodbit;
+ __try
+ {
+ if (__os.rdbuf()->pubsync() == -1)
+ __err = ios::badbit;
+ else if (__write_utf16_to_console(__fd, __out))
+ __err = ios::badbit;
+ }
+ __catch(const __cxxabiv1::__forced_unwind&)
+ {
+ __os._M_setstate(ios_base::badbit);
+ __throw_exception_again;
+ }
+ __catch(...)
+ { __os._M_setstate(ios_base::badbit); }
+
+ if (__err)
+ __os.setstate(__err);
+ }
+ }
+#endif
+ std::vprint_nonunicode(__os, __fmt, __args);
+ }
+
+
+ template<typename... _Args>
+ inline void
+ print(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args)
+ {
+ auto __fmtargs = std::make_format_args(std::forward<_Args>(__args)...);
+ if constexpr (string_view(__GNUC_EXECUTION_CHARSET_NAME) == "UTF-8")
+ std::vprint_unicode(__os, __fmt.get(), __fmtargs);
+ else
+ std::vprint_nonunicode(__os, __fmt.get(), __fmtargs);
+ }
+
+ template<typename... _Args>
+ inline void
+ println(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args)
+ {
+ std::print(__os, "{}\n",
+ std::format(__fmt, std::forward<_Args>(__args)...));
+ }
+#endif // __cpp_lib_print
+
#endif // C++11
_GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/std/print b/libstdc++-v3/include/std/print
new file mode 100644
index 00000000000..75e78841247
--- /dev/null
+++ b/libstdc++-v3/include/std/print
@@ -0,0 +1,128 @@
+// <print> Print functions -*- C++ -*-
+
+// Copyright The GNU Toolchain Authors.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This 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 General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/print
+ * This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_PRINT
+#define _GLIBCXX_PRINT 1
+
+#pragma GCC system_header
+
+#include <bits/requires_hosted.h> // for std::format
+
+#define __glibcxx_want_print
+#include <bits/version.h>
+
+#ifdef __cpp_lib_print // C++ >= 23
+
+#include <format>
+#include <cstdio>
+#include <cerrno>
+#include <bits/functexcept.h>
+
+#ifdef _WIN32
+# include <system_error>
+#endif
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ inline void
+ vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args)
+ {
+ string __out = std::vformat(__fmt, __args);
+ if (std::fwrite(__out.data(), 1, __out.size(), __stream) != __out.size())
+ __throw_system_error(EIO);
+ }
+
+ inline void
+ vprint_unicode(FILE* __stream, string_view __fmt, format_args __args)
+ {
+ // TODO: diagnose invalid UTF-8 code units
+#ifdef _WIN32
+ int __fd_for_console(FILE*);
+ void __write_utf16_to_console(int, string);
+
+ // If stream refers to a terminal convert to UTF-16 and use WriteConsoleW.
+ if (int __fd = __fd_for_console(__stream); __fd >= 0)
+ {
+ string __out = std::vformat(__fmt, __args);
+ error_code __e;
+ if (!std::fflush(__stream))
+ {
+ if (!(__e = __write_utf16_to_console(__fd, __out)))
+ return;
+ }
+ else
+ __e = error_code(errno, generic_category());
+ _GLIBCXX_THROW_OR_ABORT(system_error(__e, "std::vprint_unicode"));
+ }
+#endif
+ std::vprint_nonunicode(__stream, __fmt, __args);
+ }
+
+ template<typename... _Args>
+ inline void
+ print(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args)
+ {
+ auto __fmtargs = std::make_format_args(std::forward<_Args>(__args)...);
+ if constexpr (string_view(__GNUC_EXECUTION_CHARSET_NAME) == "UTF-8")
+ std::vprint_unicode(__stream, __fmt.get(), __fmtargs);
+ else
+ std::vprint_nonunicode(__stream, __fmt.get(), __fmtargs);
+ }
+
+ template<typename... _Args>
+ inline void
+ print(format_string<_Args...> __fmt, _Args&&... __args)
+ { std::print(stdout, __fmt, std::forward<_Args>(__args)...); }
+
+ template<typename... _Args>
+ inline void
+ println(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args)
+ {
+ std::print(__stream, "{}\n",
+ std::format(__fmt, std::forward<_Args>(__args)...));
+ }
+
+ template<typename... _Args>
+ inline void
+ println(format_string<_Args...> __fmt, _Args&&... __args)
+ { std::println(stdout, __fmt, std::forward<_Args>(__args)...); }
+
+ inline void
+ vprint_unicode(string_view __fmt, format_args __args)
+ { std::vprint_unicode(stdout, __fmt, __args); }
+
+ inline void
+ vprint_nonunicode(string_view __fmt, format_args __args)
+ { std::vprint_nonunicode(stdout, __fmt, __args); }
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // __cpp_lib_print
+#endif // _GLIBCXX_PRINT
diff --git a/libstdc++-v3/src/c++20/Makefile.am b/libstdc++-v3/src/c++20/Makefile.am
index e947855e6ae..3cdc6521bb4 100644
--- a/libstdc++-v3/src/c++20/Makefile.am
+++ b/libstdc++-v3/src/c++20/Makefile.am
@@ -36,7 +36,7 @@ else
inst_sources =
endif
-sources = tzdb.cc
+sources = tzdb.cc print.cc
vpath % $(top_srcdir)/src/c++20
diff --git a/libstdc++-v3/src/c++20/Makefile.in b/libstdc++-v3/src/c++20/Makefile.in
index 3ec8c5ce804..b732e6fc005 100644
--- a/libstdc++-v3/src/c++20/Makefile.in
+++ b/libstdc++-v3/src/c++20/Makefile.in
@@ -121,7 +121,7 @@ CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libc__20convenience_la_LIBADD =
-am__objects_1 = tzdb.lo
+am__objects_1 = tzdb.lo print.lo
@ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_2 = sstream-inst.lo
@GLIBCXX_HOSTED_TRUE@am_libc__20convenience_la_OBJECTS = \
@GLIBCXX_HOSTED_TRUE@ $(am__objects_1) $(am__objects_2)
@@ -432,7 +432,7 @@ headers =
@ENABLE_EXTERN_TEMPLATE_TRUE@inst_sources = \
@ENABLE_EXTERN_TEMPLATE_TRUE@ sstream-inst.cc
-sources = tzdb.cc
+sources = tzdb.cc print.cc
@GLIBCXX_HOSTED_FALSE@libc__20convenience_la_SOURCES =
@GLIBCXX_HOSTED_TRUE@libc__20convenience_la_SOURCES = $(sources) $(inst_sources)
diff --git a/libstdc++-v3/src/c++20/print.cc b/libstdc++-v3/src/c++20/print.cc
new file mode 100644
index 00000000000..d97a0c71dfe
--- /dev/null
+++ b/libstdc++-v3/src/c++20/print.cc
@@ -0,0 +1,35 @@
+#ifdef _WIN32
+#include <system_error>
+#include <codecvt>
+#include <bits/locale_conv.h>
+#include <stdio.h> // _fileno
+#include <io.h> // _get_osfhandle, _isatty
+#include <windows.h> // GetLastError, WriteConsoleW
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+ int
+ __fd_for_console(FILE* f)
+ {
+ if (int fd = _fileno(f); fd >= 0 && _isatty(fd))
+ return fd;
+ return -1;
+ }
+
+ error_code
+ __write_utf16_to_console(int fd, string str)
+ {
+ std::wstring wstr;
+ std::codecvt_utf8_utf16<wchar_t> wcvt;
+ const auto p = str.data();
+ unsigned long nchars = 0;
+ if (!std::__str_codecvt_in_all(p, p + str.size(), wstr, wcvt))
+ return std::make_error_code(errc::illegal_byte_sequence);
+ WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
+ wstr.data(), wstr.size(), &nchars, nullptr);
+ if (nchars != wstr.size())
+ return {(int)GetLastError(), system_category()};
+ return {};
+ }
+}
+#endif // _WIN32
diff --git a/libstdc++-v3/src/c++98/globals_io.cc b/libstdc++-v3/src/c++98/globals_io.cc
index 0c4f270977d..538efd01b53 100644
--- a/libstdc++-v3/src/c++98/globals_io.cc
+++ b/libstdc++-v3/src/c++98/globals_io.cc
@@ -107,3 +107,26 @@ namespace __gnu_internal _GLIBCXX_VISIBILITY(hidden)
fake_wfilebuf buf_wcerr;
#endif
} // namespace __gnu_internal
+
+#ifdef _WIN32
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+ int __fd_for_console(FILE*);
+
+ int __fd_for_console(std::streambuf* sb)
+ {
+ using namespace __gnu_internal;
+ using namespace __gnu_cxx;
+
+ FILE* f = NULL;
+ void* p = sb;
+ if (p == buf_cout_sync || p == buf_cin_sync || p == buf_cerr_sync)
+ f = dynamic_cast<stdio_sync_filebuf<char>*>(sb)->file();
+ else if (p == buf_cout || p == buf_cin || p == buf_cerr)
+ f = dynamic_cast<stdio_filebuf<char>*>(sb)->file();
+ else
+ return -1;
+ return __fd_for_console(f);
+ }
+}
+#endif
--
2.41.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH 2/2] libstdc++: Ensure valid UTF-8 in std::vprint_unicode
2023-11-17 15:54 [PATCH 1/2] libstdc++: Implement C++23 <print> header [PR107760] Jonathan Wakely
@ 2023-11-17 15:54 ` Jonathan Wakely
0 siblings, 0 replies; 2+ messages in thread
From: Jonathan Wakely @ 2023-11-17 15:54 UTC (permalink / raw)
To: libstdc++, gcc-patches
This is a naive implementation of the UTF-8 validation algorithm, which
could definitely be optimized. But it's faster than using
std::codecvt_utf8 and checking the result of that, which is the only
existing code we have to do it in the library.
As the TODO suggests, we could do the UTF-8 to UTF-16 conversion at the
same time. But that is only needed for Windows and as I said in the 1/2
email, the output for Windows seems to be broken currently anyway and I
can't test it properly.
-- >8 --
libstdc++-v3/ChangeLog:
* include/bits/locale_conv.h (__to_valid_utf8): New function.
* include/std/ostream (vprint_unicode): Use it.
* include/std/print (vprint_unicode): Use it.
---
libstdc++-v3/include/bits/locale_conv.h | 104 ++++++++++++++++++++++++
libstdc++-v3/include/std/ostream | 74 +++++++++++------
libstdc++-v3/include/std/print | 8 +-
3 files changed, 160 insertions(+), 26 deletions(-)
diff --git a/libstdc++-v3/include/bits/locale_conv.h b/libstdc++-v3/include/bits/locale_conv.h
index 284142a360a..f6ade1d0395 100644
--- a/libstdc++-v3/include/bits/locale_conv.h
+++ b/libstdc++-v3/include/bits/locale_conv.h
@@ -624,6 +624,110 @@ _GLIBCXX_END_NAMESPACE_CXX11
bool _M_always_noconv;
};
+#if __cplusplus >= 202002L
+ template<typename _CharT = char>
+ bool
+ __to_valid_utf8(string& __s)
+ {
+ // TODO if _CharT is wchar_t then transcode at the same time.
+
+ unsigned __seen = 0, __needed = 0;
+ unsigned char __lo_bound = 0x80, __hi_bound = 0xBF;
+ size_t __errors = 0;
+
+ auto __q = __s.data(), __eoq = __q + __s.size();
+ while (__q != __eoq)
+ {
+ unsigned char __byte = *__q;
+ if (__needed == 0)
+ {
+ if (__byte <= 0x7F) // 0x00 to 0x7F
+ {
+ while (++__q != __eoq && (unsigned char)*__q <= 0x7F)
+ { } // Fast forward to the next non-ASCII character.
+ continue;
+ }
+ else if (__byte < 0xC2)
+ {
+ *__q = 0xFF;
+ ++__errors;
+ }
+ else if (__byte <= 0xDF) // 0xC2 to 0xDF
+ {
+ __needed = 1;
+ }
+ else if (__byte <= 0xEF) // 0xE0 to 0xEF
+ {
+ if (__byte == 0xE0)
+ __lo_bound = 0xA0;
+ else if (__byte == 0xED)
+ __hi_bound = 0x9F;
+
+ __needed = 2;
+ }
+ else if (__byte <= 0xF4) // 0xF0 to 0xF4
+ {
+ if (__byte == 0xF0)
+ __lo_bound = 0x90;
+ else if (__byte == 0xF4)
+ __hi_bound = 0x8F;
+
+ __needed = 3;
+ }
+ else
+ {
+ *__q = 0xFF;
+ ++__errors;
+ }
+ }
+ else
+ {
+ if (__byte < __lo_bound || __byte > __hi_bound)
+ {
+ *(__q - __seen - 1) = 0xFF;
+ __builtin_memset(__q - __seen, 0xFE, __seen);
+ ++__errors;
+ __needed = __seen = 0;
+ __lo_bound = 0x80;
+ __hi_bound = 0xBF;
+ continue; // Reprocess the current character.
+ }
+
+ __lo_bound = 0x80;
+ __hi_bound = 0xBF;
+ ++__seen;
+ if (__seen == __needed)
+ __needed = __seen = 0;
+ }
+ __q++;
+ }
+
+ if (__needed)
+ {
+ // The string ends with an incomplete multibyte sequence.
+ if (__seen)
+ __s.resize(__s.size() - __seen);
+ __s.back() = 0xFF;
+ ++__errors;
+ }
+
+ if (__errors == 0)
+ return true;
+
+ string __s2;
+ __s2.reserve(__s.size() + __errors * 2);
+ for (unsigned char __byte : __s)
+ {
+ if (__byte == 0xFF)
+ __s2 += "\uFFFD";
+ else if (__byte != 0xFE)
+ __s2 += (char)__byte;
+ }
+ __s = std::move(__s2);
+ return false;
+ }
+#endif // C++20
+
/// @} group locales
_GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/std/ostream b/libstdc++-v3/include/std/ostream
index e81c39a7c80..760aaa206da 100644
--- a/libstdc++-v3/include/std/ostream
+++ b/libstdc++-v3/include/std/ostream
@@ -917,42 +917,68 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
inline void
vprint_unicode(ostream& __os, string_view __fmt, format_args __args)
{
- // TODO: diagnose invalid UTF-8 code units
-#ifdef _WIN32
- int __fd_for_console(std::streambuf*);
- void __write_utf16_to_console(int, string);
-
- // If stream refers to a terminal convert to UTF-16 and use WriteConsoleW.
- if (int __fd = __fd_for_console(__os.rdbuf()); __fd >= 0)
+ ostream::sentry __cerb(__os);
+ if (__cerb)
{
- ostream::sentry __cerb(__os);
- if (__cerb)
+ string __out = std::vformat(__fmt, __args);
+ std::__to_valid_utf8(__out);
+
+#ifdef _WIN32
+ int __fd_for_console(std::streambuf*);
+ void __write_utf16_to_console(int, string);
+
+ // If stream refers to a terminal output UTF-16 using WriteConsoleW.
+ if (int __fd = __fd_for_console(__os.rdbuf()); __fd >= 0)
{
- string __out = std::vformat(__fmt, __args);
ios_base::iostate __err = ios_base::goodbit;
__try
- {
- if (__os.rdbuf()->pubsync() == -1)
- __err = ios::badbit;
- else if (__write_utf16_to_console(__fd, __out))
- __err = ios::badbit;
- }
+ {
+ if (__os.rdbuf()->pubsync() == -1)
+ __err = ios::badbit;
+ else if (__write_utf16_to_console(__fd, __out))
+ __err = ios::badbit;
+ }
__catch(const __cxxabiv1::__forced_unwind&)
- {
- __os._M_setstate(ios_base::badbit);
- __throw_exception_again;
- }
+ {
+ __os._M_setstate(ios_base::badbit);
+ __throw_exception_again;
+ }
__catch(...)
- { __os._M_setstate(ios_base::badbit); }
+ { __os._M_setstate(ios_base::badbit); }
if (__err)
__os.setstate(__err);
+ return;
}
- }
#endif
- std::vprint_nonunicode(__os, __fmt, __args);
- }
+ __try
+ {
+ const streamsize __w = __os.width();
+ const streamsize __n = __out.size();
+ if (__w > __n)
+ {
+ const bool __left
+ = (__os.flags() & ios_base::adjustfield) == ios_base::left;
+ if (!__left)
+ std::__ostream_fill(__os, __w - __n);
+ if (__os.good())
+ std::__ostream_write(__os, __out.data(), __n);
+ if (__left && __os.good())
+ std::__ostream_fill(__os, __w - __n);
+ }
+ else
+ std::__ostream_write(__os, __out.data(), __n);
+ }
+ __catch(const __cxxabiv1::__forced_unwind&)
+ {
+ __os._M_setstate(ios_base::badbit);
+ __throw_exception_again;
+ }
+ __catch(...)
+ { __os._M_setstate(ios_base::badbit); }
+ }
+ }
template<typename... _Args>
inline void
diff --git a/libstdc++-v3/include/std/print b/libstdc++-v3/include/std/print
index 75e78841247..096b97b1ef7 100644
--- a/libstdc++-v3/include/std/print
+++ b/libstdc++-v3/include/std/print
@@ -62,7 +62,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
inline void
vprint_unicode(FILE* __stream, string_view __fmt, format_args __args)
{
- // TODO: diagnose invalid UTF-8 code units
+ string __out = std::vformat(__fmt, __args);
+ std::__to_valid_utf8(__out);
+
#ifdef _WIN32
int __fd_for_console(FILE*);
void __write_utf16_to_console(int, string);
@@ -82,7 +84,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX_THROW_OR_ABORT(system_error(__e, "std::vprint_unicode"));
}
#endif
- std::vprint_nonunicode(__stream, __fmt, __args);
+
+ if (std::fwrite(__out.data(), 1, __out.size(), __stream) != __out.size())
+ __throw_system_error(EIO);
}
template<typename... _Args>
--
2.41.0
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2023-11-17 16:03 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-17 15:54 [PATCH 1/2] libstdc++: Implement C++23 <print> header [PR107760] Jonathan Wakely
2023-11-17 15:54 ` [PATCH 2/2] libstdc++: Ensure valid UTF-8 in std::vprint_unicode Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).