From c0ea88a6776b6281d5774d03572b1aecc2ef3c25 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 6 Jan 2020 17:47:41 +0100 Subject: Fix MS-Win system locale code to return QString for numeric tokens QSystemLocale::query() is specified to return a QString (wrapped in a QVariant) for the various tokens used in formatting numbers (zero digit, signs, separators) but the MS-Win back-end was returning QChar (wrapped as QVariant) instead, using the first UCS-2 code-point of the string (even if this was the first of a surrogate pair). The same error shall be perpetrated by its caller, but we can at least DTRT in the back-end, ready for the coming fix (in Qt 6) to its caller. In the process, eliminate some local variables that shadowed a member variable and adapt number-conversion to cope with surrogate-pair digits. Optimised the latter for the case where zero is "0". Change-Id: Idfb416c301add4c961dde613b3dc28b2e31fd0af Reviewed-by: Thiago Macieira --- src/corelib/text/qlocale_win.cpp | 97 +++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 35 deletions(-) (limited to 'src/corelib') diff --git a/src/corelib/text/qlocale_win.cpp b/src/corelib/text/qlocale_win.cpp index 79ea67f966..4b4152c519 100644 --- a/src/corelib/text/qlocale_win.cpp +++ b/src/corelib/text/qlocale_win.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -106,11 +106,11 @@ struct QSystemLocalePrivate { QSystemLocalePrivate(); - QChar zeroDigit(); - QChar decimalPoint(); - QChar groupSeparator(); - QChar negativeSign(); - QChar positiveSign(); + QString zeroDigit(); + QString decimalPoint(); + QString groupSeparator(); + QString negativeSign(); + QString positiveSign(); QVariant dateFormat(QLocale::FormatType); QVariant timeFormat(QLocale::FormatType); QVariant dateTimeFormat(QLocale::FormatType); @@ -147,12 +147,11 @@ private: WCHAR lcName[LOCALE_NAME_MAX_LENGTH]; #endif SubstitutionType substitutionType; - QChar zero; + QString zero; // cached value for zeroDigit() int getLocaleInfo(LCTYPE type, LPWSTR data, int size); QString getLocaleInfo(LCTYPE type, int maxlen = 0); int getLocaleInfo_int(LCTYPE type, int maxlen = 0); - QChar getLocaleInfo_qchar(LCTYPE type); int getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size); int getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size); @@ -236,12 +235,6 @@ int QSystemLocalePrivate::getLocaleInfo_int(LCTYPE type, int maxlen) return ok ? v : 0; } -QChar QSystemLocalePrivate::getLocaleInfo_qchar(LCTYPE type) -{ - QString str = getLocaleInfo(type); - return str.isEmpty() ? QChar() : str.at(0); -} - QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution() { if (substitutionType == SUnknown) { @@ -257,13 +250,12 @@ QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution() else if (buf[0] == '2') substitutionType = QSystemLocalePrivate::SAlways; else { - wchar_t digits[11]; + wchar_t digits[11]; // See zeroDigit() for why 11. if (!getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) { substitutionType = QSystemLocalePrivate::SNever; return substitutionType; } - const wchar_t zero = digits[0]; - if (buf[0] == zero + 2) + if (buf[0] == digits[0] + 2) substitutionType = QSystemLocalePrivate::SAlways; else substitutionType = QSystemLocalePrivate::SNever; @@ -274,40 +266,75 @@ QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution() QString &QSystemLocalePrivate::substituteDigits(QString &string) { - ushort zero = zeroDigit().unicode(); - ushort *qch = reinterpret_cast(string.data()); - for (ushort *end = qch + string.size(); qch != end; ++qch) { - if (*qch >= '0' && *qch <= '9') - *qch = zero + (*qch - '0'); + zeroDigit(); // Ensure zero is set. + switch (zero.size()) { + case 1: { + const ushort offset = zero.at(0).unicode() - '0'; + if (!offset) // Nothing to do + break; + Q_ASSERT(offset > 9); + ushort *const qch = reinterpret_cast(string.data()); + for (int i = 0, stop = string.size(); i < stop; ++i) { + ushort &ch = qch[i]; + if (ch >= '0' && ch <= '9') + ch += offset; + } + break; + } + case 2: { + // Surrogate pair (high, low): + uint digit = QChar::surrogateToUcs4(zero.at(0), zero.at(1)); + for (int i = 0; i < 10; i++) { + const QChar s[2] = { QChar::highSurrogate(digit + i), QChar::lowSurrogate(digit + i) }; + string.replace(QString(QLatin1Char('0' + i)), QString(s, 2)); + } + break; + } + default: + Q_ASSERT(!"Expected zero digit to be a single UCS2 code-point or a surrogate pair"); + case 0: // Apparently this locale info was not available. + break; } return string; } -QChar QSystemLocalePrivate::zeroDigit() +QString QSystemLocalePrivate::zeroDigit() { - if (zero.isNull()) - zero = getLocaleInfo_qchar(LOCALE_SNATIVEDIGITS); + if (zero.isEmpty()) { + /* Ten digits plus a terminator. + + https://docs.microsoft.com/en-us/windows/win32/intl/locale-snative-constants + "Native equivalents of ASCII 0 through 9. The maximum number of + characters allowed for this string is eleven, including a terminating + null character." + */ + wchar_t digits[11]; + if (getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) { + // assert all(digits[i] == i + digits[0] for i in range(1, 10)), assumed above + zero = QString::fromWCharArray(digits, 1); + } + } return zero; } -QChar QSystemLocalePrivate::decimalPoint() +QString QSystemLocalePrivate::decimalPoint() { - return getLocaleInfo_qchar(LOCALE_SDECIMAL); + return getLocaleInfo(LOCALE_SDECIMAL); } -QChar QSystemLocalePrivate::groupSeparator() +QString QSystemLocalePrivate::groupSeparator() { - return getLocaleInfo_qchar(LOCALE_STHOUSAND); + return getLocaleInfo(LOCALE_STHOUSAND); } -QChar QSystemLocalePrivate::negativeSign() +QString QSystemLocalePrivate::negativeSign() { - return getLocaleInfo_qchar(LOCALE_SNEGATIVESIGN); + return getLocaleInfo(LOCALE_SNEGATIVESIGN); } -QChar QSystemLocalePrivate::positiveSign() +QString QSystemLocalePrivate::positiveSign() { - return getLocaleInfo_qchar(LOCALE_SPOSITIVESIGN); + return getLocaleInfo(LOCALE_SPOSITIVESIGN); } QVariant QSystemLocalePrivate::dateFormat(QLocale::FormatType type) @@ -677,7 +704,7 @@ void QSystemLocalePrivate::update() GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH); #endif substitutionType = SUnknown; - zero = QChar(); + zero.resize(0); } QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt) @@ -749,7 +776,7 @@ QLocale QSystemLocale::fallbackUiLocale() const return QLocale(QString::fromLatin1(getWinLocaleName())); } -QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const +QVariant QSystemLocale::query(QueryType type, QVariant in) const { QSystemLocalePrivate *d = systemLocalePrivate(); switch(type) { -- cgit v1.2.3