1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#ifndef QLOCALE_P_H
42#define QLOCALE_P_H
43
44//
45// W A R N I N G
46// -------------
47//
48// This file is not part of the Qt API. It exists for the convenience
49// of internal files. This header file may change from version to version
50// without notice, or even be removed.
51//
52// We mean it.
53//
54
55#include <QtCore/private/qglobal_p.h>
56#include "QtCore/qstring.h"
57#include "QtCore/qvarlengtharray.h"
58#include "QtCore/qvariant.h"
59#include "QtCore/qnumeric.h"
60#include <QtCore/qcalendar.h>
61
62#include "qlocale.h"
63
64#include <limits>
65#include <cmath>
66
67QT_BEGIN_NAMESPACE
68
69#ifndef QT_NO_SYSTEMLOCALE
70struct QLocaleData;
71class Q_CORE_EXPORT QSystemLocale
72{
73public:
74 QSystemLocale();
75 virtual ~QSystemLocale();
76
77 struct CurrencyToStringArgument
78 {
79 CurrencyToStringArgument() { }
80 CurrencyToStringArgument(const QVariant &v, const QString &s)
81 : value(v), symbol(s) { }
82 QVariant value;
83 QString symbol;
84 };
85
86 enum QueryType {
87 LanguageId, // uint
88 CountryId, // uint
89 DecimalPoint, // QString
90 GroupSeparator, // QString (empty QString means: don't group digits)
91 ZeroDigit, // QString
92 NegativeSign, // QString
93 DateFormatLong, // QString
94 DateFormatShort, // QString
95 TimeFormatLong, // QString
96 TimeFormatShort, // QString
97 DayNameLong, // QString, in: int
98 DayNameShort, // QString, in: int
99 MonthNameLong, // QString, in: int
100 MonthNameShort, // QString, in: int
101 DateToStringLong, // QString, in: QDate
102 DateToStringShort, // QString in: QDate
103 TimeToStringLong, // QString in: QTime
104 TimeToStringShort, // QString in: QTime
105 DateTimeFormatLong, // QString
106 DateTimeFormatShort, // QString
107 DateTimeToStringLong, // QString in: QDateTime
108 DateTimeToStringShort, // QString in: QDateTime
109 MeasurementSystem, // uint
110 PositiveSign, // QString
111 AMText, // QString
112 PMText, // QString
113 FirstDayOfWeek, // Qt::DayOfWeek
114 Weekdays, // QList<Qt::DayOfWeek>
115 CurrencySymbol, // QString in: CurrencyToStringArgument
116 CurrencyToString, // QString in: qlonglong, qulonglong or double
117 Collation, // QString
118 UILanguages, // QStringList
119 StringToStandardQuotation, // QString in: QStringView to quote
120 StringToAlternateQuotation, // QString in: QStringView to quote
121 ScriptId, // uint
122 ListToSeparatedString, // QString
123 LocaleChanged, // system locale changed
124 NativeLanguageName, // QString
125 NativeCountryName, // QString
126 StandaloneMonthNameLong, // QString, in: int
127 StandaloneMonthNameShort // QString, in: int
128 };
129 virtual QVariant query(QueryType type, QVariant in = QVariant()) const;
130 virtual QLocale fallbackUiLocale() const;
131
132 inline const QLocaleData *fallbackUiLocaleData() const;
133private:
134 QSystemLocale(bool);
135 friend class QSystemLocaleSingleton;
136};
137Q_DECLARE_TYPEINFO(QSystemLocale::QueryType, Q_PRIMITIVE_TYPE);
138Q_DECLARE_TYPEINFO(QSystemLocale::CurrencyToStringArgument, Q_MOVABLE_TYPE);
139#endif
140
141#if QT_CONFIG(icu)
142namespace QIcu {
143 QString toUpper(const QByteArray &localeId, const QString &str, bool *ok);
144 QString toLower(const QByteArray &localeId, const QString &str, bool *ok);
145}
146#endif
147
148
149struct QLocaleId
150{
151 inline bool operator==(QLocaleId other) const
152 { return language_id == other.language_id && script_id == other.script_id && country_id == other.country_id; }
153 inline bool operator!=(QLocaleId other) const
154 { return !operator==(other); }
155
156 QLocaleId withLikelySubtagsAdded() const;
157 QLocaleId withLikelySubtagsRemoved() const;
158
159 QByteArray name(char separator = '-') const;
160
161 ushort language_id = 0, script_id = 0, country_id = 0;
162};
163Q_DECLARE_TYPEINFO(QLocaleId, Q_PRIMITIVE_TYPE);
164
165struct QLocaleData
166{
167public:
168 // TODO: Remove this?
169 static const QLocaleData *findLocaleData(QLocale::Language language,
170 QLocale::Script script,
171 QLocale::Country country);
172 // Having an offset of current locale, enables us to have multiple sources of data, i.e. user-provided calendar locales
173 static uint findLocaleOffset(QLocale::Language language,
174 QLocale::Script script,
175 QLocale::Country country);
176 static const QLocaleData *c();
177
178 enum DoubleForm {
179 DFExponent = 0,
180 DFDecimal,
181 DFSignificantDigits,
182 _DFMax = DFSignificantDigits
183 };
184
185 enum Flags {
186 NoFlags = 0,
187 AddTrailingZeroes = 0x01,
188 ZeroPadded = 0x02,
189 LeftAdjusted = 0x04,
190 BlankBeforePositive = 0x08,
191 AlwaysShowSign = 0x10,
192 GroupDigits = 0x20,
193 CapitalEorX = 0x40,
194
195 ShowBase = 0x80,
196 UppercaseBase = 0x100,
197 ZeroPadExponent = 0x200,
198 ForcePoint = 0x400
199 };
200
201 enum NumberMode { IntegerMode, DoubleStandardMode, DoubleScientificMode };
202
203 typedef QVarLengthArray<char, 256> CharBuff;
204
205private:
206 enum PrecisionMode {
207 PMDecimalDigits = 0x01,
208 PMSignificantDigits = 0x02,
209 PMChopTrailingZeros = 0x03
210 };
211
212 QString decimalForm(QString &&digits, int decpt, int precision,
213 PrecisionMode pm, bool mustMarkDecimal,
214 bool groupDigits) const;
215 QString exponentForm(QString &&digits, int decpt, int precision,
216 PrecisionMode pm, bool mustMarkDecimal,
217 int minExponentDigits) const;
218 QString signPrefix(bool negative, unsigned flags) const;
219 QString applyIntegerFormatting(QString &&numStr, bool negative, int precision,
220 int base, int width, unsigned flags) const;
221
222public:
223 QString doubleToString(double d,
224 int precision = -1,
225 DoubleForm form = DFSignificantDigits,
226 int width = -1,
227 unsigned flags = NoFlags) const;
228 QString longLongToString(qint64 l, int precision = -1,
229 int base = 10,
230 int width = -1,
231 unsigned flags = NoFlags) const;
232 QString unsLongLongToString(quint64 l, int precision = -1,
233 int base = 10,
234 int width = -1,
235 unsigned flags = NoFlags) const;
236
237 // this function is meant to be called with the result of stringToDouble or bytearrayToDouble
238 static float convertDoubleToFloat(double d, bool *ok)
239 {
240 if (qIsInf(d))
241 return float(d);
242 if (std::fabs(d) > std::numeric_limits<float>::max()) {
243 if (ok)
244 *ok = false;
245 const float huge = std::numeric_limits<float>::infinity();
246 return d < 0 ? -huge : huge;
247 }
248 if (d != 0 && float(d) == 0) {
249 // Values that underflow double already failed. Match them:
250 if (ok)
251 *ok = false;
252 return 0;
253 }
254 return float(d);
255 }
256
257 double stringToDouble(QStringView str, bool *ok, QLocale::NumberOptions options) const;
258 qint64 stringToLongLong(QStringView str, int base, bool *ok, QLocale::NumberOptions options) const;
259 quint64 stringToUnsLongLong(QStringView str, int base, bool *ok, QLocale::NumberOptions options) const;
260
261 // this function is used in QIntValidator (QtGui)
262 Q_CORE_EXPORT static qint64 bytearrayToLongLong(const char *num, int base, bool *ok);
263 static quint64 bytearrayToUnsLongLong(const char *num, int base, bool *ok);
264
265 bool numberToCLocale(QStringView s, QLocale::NumberOptions number_options,
266 CharBuff *result) const;
267 inline char numericToCLocale(QStringView in) const;
268
269 // this function is used in QIntValidator (QtGui)
270 Q_CORE_EXPORT bool validateChars(QStringView str, NumberMode numMode, QByteArray *buff, int decDigits = -1,
271 QLocale::NumberOptions number_options = QLocale::DefaultNumberOptions) const;
272
273 QString decimalPoint() const;
274 QString groupSeparator() const;
275 QString listSeparator() const;
276 QString percentSign() const;
277 QString zeroDigit() const;
278 char32_t zeroUcs() const;
279 QString positiveSign() const;
280 QString negativeSign() const;
281 QString exponentSeparator() const;
282
283 struct DataRange
284 {
285 quint16 offset;
286 quint16 size;
287 QString getData(const char16_t *table) const
288 {
289 return size > 0
290 ? QString::fromRawData(reinterpret_cast<const QChar *>(table + offset), size)
291 : QString();
292 }
293 QStringView viewData(const char16_t *table) const
294 {
295 return { reinterpret_cast<const QChar *>(table + offset), size };
296 }
297 QString getListEntry(const char16_t *table, int index) const
298 {
299 return listEntry(table, index).getData(table);
300 }
301 QStringView viewListEntry(const char16_t *table, int index) const
302 {
303 return listEntry(table, index).viewData(table);
304 }
305 char32_t ucsFirst(const char16_t *table) const
306 {
307 if (size && !QChar::isSurrogate(table[offset]))
308 return table[offset];
309 if (size > 1 && QChar::isHighSurrogate(table[offset]))
310 return QChar::surrogateToUcs4(table[offset], table[offset + 1]);
311 return 0;
312 }
313 private:
314 DataRange listEntry(const char16_t *table, int index) const
315 {
316 const char16_t separator = ';';
317 quint16 i = 0;
318 while (index > 0 && i < size) {
319 if (table[offset + i] == separator)
320 index--;
321 i++;
322 }
323 quint16 end = i;
324 while (end < size && table[offset + end] != separator)
325 end++;
326 return { quint16(offset + i), quint16(end - i) };
327 }
328 };
329
330#define ForEachQLocaleRange(X) \
331 X(startListPattern) X(midListPattern) X(endListPattern) X(pairListPattern) X(listDelimit) \
332 X(decimalSeparator) X(groupDelim) X(percent) X(zero) X(minus) X(plus) X(exponential) \
333 X(quoteStart) X(quoteEnd) X(quoteStartAlternate) X(quoteEndAlternate) \
334 X(longDateFormat) X(shortDateFormat) X(longTimeFormat) X(shortTimeFormat) \
335 X(longDayNamesStandalone) X(longDayNames) \
336 X(shortDayNamesStandalone) X(shortDayNames) \
337 X(narrowDayNamesStandalone) X(narrowDayNames) \
338 X(anteMeridiem) X(postMeridiem) \
339 X(byteCount) X(byteAmountSI) X(byteAmountIEC) \
340 X(currencySymbol) X(currencyDisplayName) \
341 X(currencyFormat) X(currencyFormatNegative) \
342 X(endonymLanguage) X(endonymCountry)
343
344#define rangeGetter(name) \
345 DataRange name() const { return { m_ ## name ## _idx, m_ ## name ## _size }; }
346 ForEachQLocaleRange(rangeGetter)
347#undef rangeGetter
348
349public:
350 quint16 m_language_id, m_script_id, m_country_id;
351
352 // Offsets, then sizes, for each range:
353#define rangeIndex(name) quint16 m_ ## name ## _idx;
354 ForEachQLocaleRange(rangeIndex)
355#undef rangeIndex
356#define Size(name) quint8 m_ ## name ## _size;
357 ForEachQLocaleRange(Size)
358#undef Size
359
360#undef ForEachQLocaleRange
361
362 // Strays:
363 char m_currency_iso_code[3];
364 quint8 m_currency_digits : 2;
365 quint8 m_currency_rounding : 3; // (not yet used !)
366 quint8 m_first_day_of_week : 3;
367 quint8 m_weekend_start : 3;
368 quint8 m_weekend_end : 3;
369 quint8 m_grouping_top : 2; // Must have this many before the first grouping separator
370 quint8 m_grouping_higher : 3; // Number of digits between grouping separators
371 quint8 m_grouping_least : 3; // Number of digits after last grouping separator (before decimal).
372};
373
374class Q_CORE_EXPORT QLocalePrivate // A POD type
375{
376public:
377 static QLocalePrivate *create(
378 const QLocaleData *data, const uint data_offset = 0,
379 QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions)
380 {
381 auto *retval = new QLocalePrivate;
382 retval->m_data = data;
383 retval->ref.storeRelaxed(0);
384 retval->m_data_offset = data_offset;
385 retval->m_numberOptions = numberOptions;
386 return retval;
387 }
388
389 static QLocalePrivate *get(QLocale &l) { return l.d; }
390 static const QLocalePrivate *get(const QLocale &l) { return l.d; }
391
392 quint16 languageId() const { return m_data->m_language_id; }
393 quint16 countryId() const { return m_data->m_country_id; }
394
395 QByteArray bcp47Name(char separator = '-') const;
396 QByteArray rawName(char separator = '-') const;
397
398 inline QLatin1String languageCode() const { return languageToCode(QLocale::Language(m_data->m_language_id)); }
399 inline QLatin1String scriptCode() const { return scriptToCode(QLocale::Script(m_data->m_script_id)); }
400 inline QLatin1String countryCode() const { return countryToCode(QLocale::Country(m_data->m_country_id)); }
401
402 static QLatin1String languageToCode(QLocale::Language language);
403 static QLatin1String scriptToCode(QLocale::Script script);
404 static QLatin1String countryToCode(QLocale::Country country);
405 static QLocale::Language codeToLanguage(QStringView code) noexcept;
406 static QLocale::Script codeToScript(QStringView code) noexcept;
407 static QLocale::Country codeToCountry(QStringView code) noexcept;
408 static void getLangAndCountry(const QString &name, QLocale::Language &lang,
409 QLocale::Script &script, QLocale::Country &cntry);
410
411 QLocale::MeasurementSystem measurementSystem() const;
412
413 const QLocaleData *m_data;
414 QBasicAtomicInt ref;
415 uint m_data_offset;
416 QLocale::NumberOptions m_numberOptions;
417};
418
419#ifndef QT_NO_SYSTEMLOCALE
420const QLocaleData *QSystemLocale::fallbackUiLocaleData() const { return fallbackUiLocale().d->m_data; }
421#endif
422
423template <>
424inline QLocalePrivate *QSharedDataPointer<QLocalePrivate>::clone()
425{
426 // cannot use QLocalePrivate's copy constructor
427 // since it is deleted in C++11
428 return QLocalePrivate::create(d->m_data, d->m_data_offset, d->m_numberOptions);
429}
430
431inline char QLocaleData::numericToCLocale(QStringView in) const
432{
433 Q_ASSERT(in.size() == 1 || (in.size() == 2 && in.at(0).isHighSurrogate()));
434
435 if (in == positiveSign() || in == u"+")
436 return '+';
437
438 if (in == negativeSign() || in == u"-" || in == u"\x2212")
439 return '-';
440
441 if (in == decimalPoint())
442 return '.';
443
444 if (in.compare(exponentSeparator(), Qt::CaseInsensitive) == 0)
445 return 'e';
446
447 const QString group = groupSeparator();
448 if (in == group)
449 return ',';
450
451 // In several languages group() is a non-breaking space (U+00A0) or its thin
452 // version (U+202f), which look like spaces. People (and thus some of our
453 // tests) use a regular space instead and complain if it doesn't work.
454 // Should this be extended generally to any case where group is a space ?
455 if ((group == u"\xa0" || group == u"\x202f") && in == u" ")
456 return ',';
457
458 const char32_t inUcs4 = in.size() == 2
459 ? QChar::surrogateToUcs4(in.at(0), in.at(1)) : in.at(0).unicode();
460 const char32_t zeroUcs4 = zeroUcs();
461 // Must match qlocale_tools.h's unicodeForDigit()
462 if (zeroUcs4 == u'\u3007') {
463 // QTBUG-85409: Suzhou's digits aren't contiguous !
464 if (inUcs4 == zeroUcs4)
465 return '0';
466 if (inUcs4 > u'\u3020' && inUcs4 <= u'\u3029')
467 return inUcs4 - u'\u3020';
468 } else if (zeroUcs4 <= inUcs4 && inUcs4 < zeroUcs4 + 10) {
469 return '0' + inUcs4 - zeroUcs4;
470 }
471 if ('0' <= inUcs4 && inUcs4 <= '9')
472 return inUcs4;
473
474 return 0;
475}
476
477QString qt_readEscapedFormatString(QStringView format, int *idx);
478bool qt_splitLocaleName(const QString &name, QString &lang, QString &script, QString &cntry);
479int qt_repeatCount(QStringView s);
480
481enum { AsciiSpaceMask = (1u << (' ' - 1)) |
482 (1u << ('\t' - 1)) | // 9: HT - horizontal tab
483 (1u << ('\n' - 1)) | // 10: LF - line feed
484 (1u << ('\v' - 1)) | // 11: VT - vertical tab
485 (1u << ('\f' - 1)) | // 12: FF - form feed
486 (1u << ('\r' - 1)) }; // 13: CR - carriage return
487constexpr inline bool ascii_isspace(uchar c)
488{
489 return c >= 1u && c <= 32u && (AsciiSpaceMask >> uint(c - 1)) & 1u;
490}
491
492#if defined(Q_COMPILER_CONSTEXPR)
493static_assert(ascii_isspace(' '));
494static_assert(ascii_isspace('\t'));
495static_assert(ascii_isspace('\n'));
496static_assert(ascii_isspace('\v'));
497static_assert(ascii_isspace('\f'));
498static_assert(ascii_isspace('\r'));
499static_assert(!ascii_isspace('\0'));
500static_assert(!ascii_isspace('\a'));
501static_assert(!ascii_isspace('a'));
502static_assert(!ascii_isspace('\177'));
503static_assert(!ascii_isspace(uchar('\200')));
504static_assert(!ascii_isspace(uchar('\xA0')));
505static_assert(!ascii_isspace(uchar('\377')));
506#endif
507
508QT_END_NAMESPACE
509
510Q_DECLARE_METATYPE(QStringView)
511Q_DECLARE_METATYPE(QList<Qt::DayOfWeek>)
512#ifndef QT_NO_SYSTEMLOCALE
513Q_DECLARE_METATYPE(QSystemLocale::CurrencyToStringArgument)
514#endif
515
516#endif // QLOCALE_P_H
517