From: Ivailo Monev Date: Mon, 31 Oct 2022 19:48:55 +0000 (+0200) Subject: do not use QTextCodec::codecForLocale() for converting QString to locale-encoded... X-Git-Tag: 4.12.0~261 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=350667cf948bc304dfd6c1671fd99cf2ef3f59e6;p=kde%2FKatie.git do not use QTextCodec::codecForLocale() for converting QString to locale-encoded string if QString::toLocal8Bit() is called from global static destructor it may be too late to cache a QTextCodec, in fact it may cause a crash. QTextCodec::codecForLocale() also has to lock a global mutex Signed-off-by: Ivailo Monev --- diff --git a/src/core/codecs/qtextcodec.cpp b/src/core/codecs/qtextcodec.cpp index 6c5b063a4..49856f03e 100644 --- a/src/core/codecs/qtextcodec.cpp +++ b/src/core/codecs/qtextcodec.cpp @@ -940,6 +940,8 @@ static const qint16 MIBTblSize = sizeof(MIBTbl) / sizeof(MIBTblData); static const UChar nullchar[2] = { 0x5c, 0x30 }; static const UChar questionmarkchar[1] = { 0x3f }; +static QByteArray localecodec; + static inline bool nameMatch(const QByteArray &name, const QByteArray &name2) { if (ucnv_compareNames(name.constData(), name2.constData()) == 0) { @@ -953,6 +955,25 @@ static inline bool nameMatch(const QByteArray &name, const QByteArray &name2) return (ucnv_compareNames(iana, name2.constData()) == 0); } +static QByteArray checkForCodec(const QByteArray &name) +{ + foreach(const QByteArray &codec, QTextCodecPrivate::allCodecs()) { + if (nameMatch(name, codec)) { + return name; + } + } + const int index = name.indexOf('@'); + if (index != -1) { + const QByteArray namepart = name.left(index); + foreach(const QByteArray &codec, QTextCodecPrivate::allCodecs()) { + if (nameMatch(namepart, codec)) { + return namepart; + } + } + } + return QByteArray(); +} + QTextCodecPrivate::QTextCodecPrivate(const QByteArray &aname) : name(aname) { @@ -1016,6 +1037,47 @@ QList QTextCodecPrivate::allMibs() return allmibs; } +QByteArray QTextCodecPrivate::localeCodec() +{ + if (!localecodec.isEmpty()) { + return localecodec; + } + + QByteArray result; + // Get the first non-empty value from $LC_ALL, $LC_CTYPE, and $LANG + // environment variables. + QByteArray lang = qgetenv("LC_ALL"); + if (lang.isEmpty()) { + lang = qgetenv("LC_CTYPE"); + } + if (lang.isEmpty()) { + lang = qgetenv("LANG"); + } + + const int indexOfDot = lang.indexOf('.'); + if (indexOfDot != -1) { + result = checkForCodec(lang.mid(indexOfDot + 1)); + } + + if (result.isEmpty() && !lang.isEmpty()) { + result = checkForCodec(lang); + } + + // Fallback to implementation-defined default locale + if (result.isEmpty()) { + const QByteArray charset = ::nl_langinfo(CODESET); // deep copy + result = checkForCodec(charset); + } + + // nl_langinfo() is documented to return empty string only if its argument + // is not valid + if (Q_UNLIKELY(result.isEmpty())) { + qFatal("Could not detect codec for locale"); + } + + return result; +} + QString QTextCodecPrivate::convertTo(const char *data, int length, const char* const codec) { UErrorCode error = U_ZERO_ERROR; @@ -1163,7 +1225,6 @@ void QTextConverterPrivate::invalidChars(int length) const } -static QByteArray localecodec; #ifndef QT_NO_THREAD Q_GLOBAL_STATIC(QMutex, textCodecsMutex) #endif @@ -1191,64 +1252,6 @@ QTextCodecCleanup::~QTextCodecCleanup() } Q_GLOBAL_STATIC(QTextCodecCleanup, qGlobalQTextCodec) -static QByteArray checkForCodec(const QByteArray &name) -{ - foreach(const QByteArray &codec, QTextCodec::availableCodecs()) { - if (nameMatch(name, codec)) { - return name; - } - } - const int index = name.indexOf('@'); - if (index != -1) { - const QByteArray namepart = name.left(index); - foreach(const QByteArray &codec, QTextCodec::availableCodecs()) { - if (nameMatch(namepart, codec)) { - return namepart; - } - } - } - return QByteArray(); -} - -static QByteArray getLocaleCodec() -{ - Q_ASSERT(localecodec.isEmpty()); - - QByteArray result; - // Get the first non-empty value from $LC_ALL, $LC_CTYPE, and $LANG - // environment variables. - QByteArray lang = qgetenv("LC_ALL"); - if (lang.isEmpty()) { - lang = qgetenv("LC_CTYPE"); - } - if (lang.isEmpty()) { - lang = qgetenv("LANG"); - } - - const int indexOfDot = lang.indexOf('.'); - if (indexOfDot != -1) { - result = checkForCodec(lang.mid(indexOfDot + 1)); - } - - if (result.isEmpty() && !lang.isEmpty()) { - result = checkForCodec(lang); - } - - // Fallback to implementation-defined default locale - if (result.isEmpty()) { - const QByteArray charset = ::nl_langinfo(CODESET); // deep copy - result = checkForCodec(charset); - } - - // nl_langinfo() is documented to return empty string only if its argument - // is not valid - if (Q_UNLIKELY(result.isEmpty())) { - qFatal("Could not detect codec for locale"); - } - - return result; -} - /*! \enum QTextConverter::ConversionFlag @@ -1540,7 +1543,7 @@ void QTextCodec::setCodecForLocale(QTextCodec *c) if (c) { localecodec = c->name(); } else { - localecodec = getLocaleCodec(); + localecodec = QTextCodecPrivate::localeCodec(); } } @@ -1551,7 +1554,7 @@ void QTextCodec::setCodecForLocale(QTextCodec *c) QTextCodec* QTextCodec::codecForLocale() { if (localecodec.isEmpty()) { - localecodec = getLocaleCodec(); + localecodec = QTextCodecPrivate::localeCodec(); } return QTextCodec::codecForName(localecodec); } diff --git a/src/core/codecs/qtextcodec_p.h b/src/core/codecs/qtextcodec_p.h index 39c2f8180..70f5488bf 100644 --- a/src/core/codecs/qtextcodec_p.h +++ b/src/core/codecs/qtextcodec_p.h @@ -51,6 +51,8 @@ public: static QList allCodecs(); static QList allMibs(); + static QByteArray localeCodec(); + static QString convertTo(const char *data, int len, const char* const codec); static QByteArray convertFrom(const QChar *unicode, int len, const char* const codec); diff --git a/src/core/tools/qstring.cpp b/src/core/tools/qstring.cpp index 5c5f06a7a..f28d0abea 100644 --- a/src/core/tools/qstring.cpp +++ b/src/core/tools/qstring.cpp @@ -3181,11 +3181,8 @@ QByteArray QString::toAscii() const static QByteArray toLocal8Bit_helper(const QChar *data, int length) { -#ifndef QT_NO_TEXTCODEC - return QTextCodec::codecForLocale()->fromUnicode(data, length); -#else - return toLatin1_helper(data, length); -#endif // QT_NO_TEXTCODEC + const QByteArray localecodec = QTextCodecPrivate::localeCodec(); + return QTextCodecPrivate::convertFrom(data, length, localecodec.constData()); } /*! @@ -8129,11 +8126,7 @@ QByteArray QStringRef::toAscii() const */ QByteArray QStringRef::toLocal8Bit() const { -#ifndef QT_NO_TEXTCODEC - return QTextCodec::codecForLocale()->fromUnicode(unicode(), length()); -#else - return toLatin1(); -#endif // QT_NO_TEXTCODEC + return toLocal8Bit_helper(unicode(), length()); } /*!