From 92d0835e1488e679cdc15ee6acba69772b6684cd Mon Sep 17 00:00:00 2001 From: Georg Baum Date: Sun, 31 Jan 2016 12:49:17 +0100 Subject: [PATCH] Avoid encoding changes of open streams if possible Changing the codecvt_facet of a file stream after the file has been opened does not work with clang on OS X. Therefore we avoid it if possible (i. e. the new encoding is the same as the old one). --- src/support/docstream.cpp | 41 ++++++++++++++++++++++++++------------- src/support/docstream.h | 2 ++ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/support/docstream.cpp b/src/support/docstream.cpp index b07ac56a32..e33dffc3c0 100644 --- a/src/support/docstream.cpp +++ b/src/support/docstream.cpp @@ -88,6 +88,7 @@ public: } else out_cd_ = (iconv_t)(-1); } + string const & encoding() const { return encoding_; } protected: virtual ~iconv_codecvt_facet() { @@ -375,37 +376,51 @@ odocstream & operator<<(odocstream & os, SetEnc e) if (has_facet(os.rdbuf()->getloc())) { // This stream must be a file stream, since we never imbue // any other stream with a locale having a iconv_codecvt_facet. + iconv_codecvt_facet const & facet = + use_facet(os.rdbuf()->getloc()); + + // FIXME Changing the codecvt facet of an open file is allowed, + // but unsafe for facets that use internal state (see the thread + // "iostreams: Does imbue() need to be called before open()?" + // in comp.std.c++. + // Currently it seems to work with gcc and MSVC, but not with + // clang on OS X. + // Avoid imbueing with the same encoding again if possible. + if (facet.encoding() == e.encoding) + return os; + // Flush the stream so that all pending output is written // with the old encoding. os.flush(); + locale locale(os.rdbuf()->getloc(), new iconv_codecvt_facet(e.encoding, ios_base::out)); - // FIXME Does changing the codecvt facet of an open file - // stream always work? It does with gcc 4.1, but I have read - // somewhere that it does not with MSVC. - // What does the standard say? os.imbue(locale); } return os; } -//CHECKME: I just copied the code above, and have no idea whether it -//is correct... (JMarc) idocstream & operator<<(idocstream & is, SetEnc e) { if (has_facet(is.rdbuf()->getloc())) { // This stream must be a file stream, since we never imbue // any other stream with a locale having a iconv_codecvt_facet. - // Flush the stream so that all pending output is written - // with the old encoding. - //is.flush(); + iconv_codecvt_facet const & facet = + use_facet(is.rdbuf()->getloc()); + + // FIXME Changing the codecvt facet of an open file is allowed, + // but unsafe for facets that use internal state (see the thread + // "iostreams: Does imbue() need to be called before open()?" + // in comp.std.c++. + // Currently it seems to work with gcc and MSVC, but not with + // clang on OS X. + // Avoid imbueing with the same encoding again if possible. + if (facet.encoding() == e.encoding) + return is; + locale locale(is.rdbuf()->getloc(), new iconv_codecvt_facet(e.encoding, ios_base::in)); - // FIXME Does changing the codecvt facet of an open file - // stream always work? It does with gcc 4.1, but I have read - // somewhere that it does not with MSVC. - // What does the standard say? is.imbue(locale); } return is; diff --git a/src/support/docstream.h b/src/support/docstream.h index 108d00e0f6..a6197cccd6 100644 --- a/src/support/docstream.h +++ b/src/support/docstream.h @@ -42,6 +42,8 @@ typedef std::basic_ostream odocstream; /// File stream for reading UTF8-encoded files with automatic conversion to /// UCS4. +/// Buffering must be switched off if the encoding is changed after +/// construction by calling rdbuf()->pubsetbuf(0, 0). class ifdocstream : public std::basic_ifstream { typedef std::basic_ifstream base; public: -- 2.39.2