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
67 QT_BEGIN_NAMESPACE
68
69 #ifndef QT_NO_SYSTEMLOCALE
70 struct QLocaleData;
71 class Q_CORE_EXPORT QSystemLocale
72 {
73 public:
74 QSystemLocale();
75 virtual ~QSystemLocale();
76
77 struct CurrencyToStringArgument
78 {
CurrencyToStringArgumentCurrencyToStringArgument79 CurrencyToStringArgument() { }
CurrencyToStringArgumentCurrencyToStringArgument80 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: QStringRef to quote
120 StringToAlternateQuotation, // QString in: QStringRef 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) const;
130 virtual QLocale fallbackUiLocale() const;
131
132 inline const QLocaleData *fallbackUiLocaleData() const;
133 private:
134 QSystemLocale(bool);
135 friend class QSystemLocaleSingleton;
136 };
137 Q_DECLARE_TYPEINFO(QSystemLocale::QueryType, Q_PRIMITIVE_TYPE);
138 Q_DECLARE_TYPEINFO(QSystemLocale::CurrencyToStringArgument, Q_MOVABLE_TYPE);
139 #endif
140
141 #if QT_CONFIG(icu)
142 namespace 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
149 struct QLocaleId
150 {
151 // bypass constructors
fromIdsQLocaleId152 static inline QLocaleId fromIds(ushort language, ushort script, ushort country)
153 {
154 const QLocaleId localeId = { language, script, country };
155 return localeId;
156 }
157
158 inline bool operator==(QLocaleId other) const
159 { return language_id == other.language_id && script_id == other.script_id && country_id == other.country_id; }
160 inline bool operator!=(QLocaleId other) const
161 { return !operator==(other); }
162
163 QLocaleId withLikelySubtagsAdded() const;
164 QLocaleId withLikelySubtagsRemoved() const;
165
166 QByteArray name(char separator = '-') const;
167
168 ushort language_id, script_id, country_id;
169 };
170 Q_DECLARE_TYPEINFO(QLocaleId, Q_PRIMITIVE_TYPE);
171
172 struct QLocaleData
173 {
174 public:
175 // TODO: Remove this?
176 static const QLocaleData *findLocaleData(QLocale::Language language,
177 QLocale::Script script,
178 QLocale::Country country);
179 // Having an offset of current locale, enables us to have multiple sources of data, i.e. user-provided calendar locales
180 static uint findLocaleOffset(QLocale::Language language,
181 QLocale::Script script,
182 QLocale::Country country);
183 static const QLocaleData *c();
184
185 // Maximum number of significant digits needed to represent a double.
186 // We cannot use std::numeric_limits here without constexpr.
187 static const int DoubleMantissaBits = 53;
188 static const int Log10_2_100000 = 30103; // log10(2) * 100000
189 // same as C++11 std::numeric_limits<T>::max_digits10
190 static const int DoubleMaxSignificant = (DoubleMantissaBits * Log10_2_100000) / 100000 + 2;
191
192 // Maximum number of digits before decimal point to represent a double
193 // Same as std::numeric_limits<double>::max_exponent10 + 1
194 static const int DoubleMaxDigitsBeforeDecimal = 309;
195
196 enum DoubleForm {
197 DFExponent = 0,
198 DFDecimal,
199 DFSignificantDigits,
200 _DFMax = DFSignificantDigits
201 };
202
203 enum Flags {
204 NoFlags = 0,
205 AddTrailingZeroes = 0x01,
206 ZeroPadded = 0x02,
207 LeftAdjusted = 0x04,
208 BlankBeforePositive = 0x08,
209 AlwaysShowSign = 0x10,
210 ThousandsGroup = 0x20,
211 CapitalEorX = 0x40,
212
213 ShowBase = 0x80,
214 UppercaseBase = 0x100,
215 ZeroPadExponent = 0x200,
216 ForcePoint = 0x400,
217 IndianNumberGrouping= 0x800
218 };
219
220 enum NumberMode { IntegerMode, DoubleStandardMode, DoubleScientificMode };
221
222 typedef QVarLengthArray<char, 256> CharBuff;
223
224 static QString doubleToString(const QChar zero, const QChar plus,
225 const QChar minus, const QChar exponent,
226 const QChar group, const QChar decimal,
227 double d, int precision,
228 DoubleForm form,
229 int width, unsigned flags);
230 static QString longLongToString(const QChar zero, const QChar group,
231 const QChar plus, const QChar minus,
232 qint64 l, int precision, int base,
233 int width, unsigned flags);
234 static QString unsLongLongToString(const QChar zero, const QChar group,
235 const QChar plus,
236 quint64 l, int precision,
237 int base, int width,
238 unsigned flags);
239
240 QString doubleToString(double d,
241 int precision = -1,
242 DoubleForm form = DFSignificantDigits,
243 int width = -1,
244 unsigned flags = NoFlags) const;
245 QString longLongToString(qint64 l, int precision = -1,
246 int base = 10,
247 int width = -1,
248 unsigned flags = NoFlags) const;
249 QString unsLongLongToString(quint64 l, int precision = -1,
250 int base = 10,
251 int width = -1,
252 unsigned flags = NoFlags) const;
253
254 // this function is meant to be called with the result of stringToDouble or bytearrayToDouble
convertDoubleToFloatQLocaleData255 static float convertDoubleToFloat(double d, bool *ok)
256 {
257 if (qIsInf(d))
258 return float(d);
259 if (std::fabs(d) > std::numeric_limits<float>::max()) {
260 if (ok)
261 *ok = false;
262 const float huge = std::numeric_limits<float>::infinity();
263 return d < 0 ? -huge : huge;
264 }
265 if (d != 0 && float(d) == 0) {
266 // Values that underflow double already failed. Match them:
267 if (ok)
268 *ok = false;
269 return 0;
270 }
271 return float(d);
272 }
273
274 double stringToDouble(QStringView str, bool *ok, QLocale::NumberOptions options) const;
275 qint64 stringToLongLong(QStringView str, int base, bool *ok, QLocale::NumberOptions options) const;
276 quint64 stringToUnsLongLong(QStringView str, int base, bool *ok, QLocale::NumberOptions options) const;
277
278 // this function is used in QIntValidator (QtGui)
279 Q_CORE_EXPORT static qint64 bytearrayToLongLong(const char *num, int base, bool *ok);
280 static quint64 bytearrayToUnsLongLong(const char *num, int base, bool *ok);
281
282 bool numberToCLocale(QStringView s, QLocale::NumberOptions number_options,
283 CharBuff *result) const;
284 inline char digitToCLocale(QChar c) const;
285
286 // this function is used in QIntValidator (QtGui)
287 Q_CORE_EXPORT bool validateChars(QStringView str, NumberMode numMode, QByteArray *buff, int decDigits = -1,
288 QLocale::NumberOptions number_options = QLocale::DefaultNumberOptions) const;
289
290 public:
291 quint16 m_language_id, m_script_id, m_country_id;
292
293 // FIXME QTBUG-69324: not all unicode code-points map to single-token UTF-16 :-(
294 char16_t m_decimal, m_group, m_list, m_percent, m_zero, m_minus, m_plus, m_exponential;
295 char16_t m_quotation_start, m_quotation_end;
296 char16_t m_alternate_quotation_start, m_alternate_quotation_end;
297
298 quint16 m_list_pattern_part_start_idx, m_list_pattern_part_start_size;
299 quint16 m_list_pattern_part_mid_idx, m_list_pattern_part_mid_size;
300 quint16 m_list_pattern_part_end_idx, m_list_pattern_part_end_size;
301 quint16 m_list_pattern_part_two_idx, m_list_pattern_part_two_size;
302 quint16 m_short_date_format_idx, m_short_date_format_size;
303 quint16 m_long_date_format_idx, m_long_date_format_size;
304 quint16 m_short_time_format_idx, m_short_time_format_size;
305 quint16 m_long_time_format_idx, m_long_time_format_size;
306 quint16 m_standalone_short_day_names_idx, m_standalone_short_day_names_size;
307 quint16 m_standalone_long_day_names_idx, m_standalone_long_day_names_size;
308 quint16 m_standalone_narrow_day_names_idx, m_standalone_narrow_day_names_size;
309 quint16 m_short_day_names_idx, m_short_day_names_size;
310 quint16 m_long_day_names_idx, m_long_day_names_size;
311 quint16 m_narrow_day_names_idx, m_narrow_day_names_size;
312 quint16 m_am_idx, m_am_size;
313 quint16 m_pm_idx, m_pm_size;
314 quint16 m_byte_idx, m_byte_size;
315 quint16 m_byte_si_quantified_idx, m_byte_si_quantified_size;
316 quint16 m_byte_iec_quantified_idx, m_byte_iec_quantified_size;
317 char m_currency_iso_code[3];
318 quint16 m_currency_symbol_idx, m_currency_symbol_size;
319 quint16 m_currency_display_name_idx, m_currency_display_name_size;
320 quint8 m_currency_format_idx, m_currency_format_size;
321 quint8 m_currency_negative_format_idx, m_currency_negative_format_size;
322 quint16 m_language_endonym_idx, m_language_endonym_size;
323 quint16 m_country_endonym_idx, m_country_endonym_size;
324 quint16 m_currency_digits : 2;
325 quint16 m_currency_rounding : 3;
326 quint16 m_first_day_of_week : 3;
327 quint16 m_weekend_start : 3;
328 quint16 m_weekend_end : 3;
329 };
330
331 class Q_CORE_EXPORT QLocalePrivate // A POD type
332 {
333 public:
334 static QLocalePrivate *create(
335 const QLocaleData *data, const uint data_offset = 0,
336 QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions)
337 {
338 auto *retval = new QLocalePrivate;
339 retval->m_data = data;
340 retval->ref.storeRelaxed(0);
341 retval->m_data_offset = data_offset;
342 retval->m_numberOptions = numberOptions;
343 return retval;
344 }
345
get(QLocale & l)346 static QLocalePrivate *get(QLocale &l) { return l.d; }
get(const QLocale & l)347 static const QLocalePrivate *get(const QLocale &l) { return l.d; }
348
decimal()349 QChar decimal() const { return QChar(m_data->m_decimal); }
group()350 QChar group() const { return QChar(m_data->m_group); }
list()351 QChar list() const { return QChar(m_data->m_list); }
percent()352 QChar percent() const { return QChar(m_data->m_percent); }
zero()353 QChar zero() const { return QChar(m_data->m_zero); }
plus()354 QChar plus() const { return QChar(m_data->m_plus); }
minus()355 QChar minus() const { return QChar(m_data->m_minus); }
exponential()356 QChar exponential() const { return QChar(m_data->m_exponential); }
357
languageId()358 quint16 languageId() const { return m_data->m_language_id; }
countryId()359 quint16 countryId() const { return m_data->m_country_id; }
360
361 QByteArray bcp47Name(char separator = '-') const;
362 QByteArray rawName(char separator = '-') const;
363
languageCode()364 inline QLatin1String languageCode() const { return languageToCode(QLocale::Language(m_data->m_language_id)); }
scriptCode()365 inline QLatin1String scriptCode() const { return scriptToCode(QLocale::Script(m_data->m_script_id)); }
countryCode()366 inline QLatin1String countryCode() const { return countryToCode(QLocale::Country(m_data->m_country_id)); }
367
368 static QLatin1String languageToCode(QLocale::Language language);
369 static QLatin1String scriptToCode(QLocale::Script script);
370 static QLatin1String countryToCode(QLocale::Country country);
371 static QLocale::Language codeToLanguage(QStringView code) noexcept;
372 static QLocale::Script codeToScript(QStringView code) noexcept;
373 static QLocale::Country codeToCountry(QStringView code) noexcept;
374 static void getLangAndCountry(const QString &name, QLocale::Language &lang,
375 QLocale::Script &script, QLocale::Country &cntry);
376
377 QLocale::MeasurementSystem measurementSystem() const;
378
379 const QLocaleData *m_data;
380 QBasicAtomicInt ref;
381 uint m_data_offset;
382 QLocale::NumberOptions m_numberOptions;
383 };
384
385 #ifndef QT_NO_SYSTEMLOCALE
fallbackUiLocaleData()386 const QLocaleData *QSystemLocale::fallbackUiLocaleData() const { return fallbackUiLocale().d->m_data; }
387 #endif
388
389 template <>
clone()390 inline QLocalePrivate *QSharedDataPointer<QLocalePrivate>::clone()
391 {
392 // cannot use QLocalePrivate's copy constructor
393 // since it is deleted in C++11
394 return QLocalePrivate::create(d->m_data, d->m_data_offset, d->m_numberOptions);
395 }
396
digitToCLocale(QChar in)397 inline char QLocaleData::digitToCLocale(QChar in) const
398 {
399 const ushort tenUnicode = m_zero + 10;
400
401 if (in.unicode() >= m_zero && in.unicode() < tenUnicode)
402 return '0' + in.unicode() - m_zero;
403
404 if (in.unicode() >= '0' && in.unicode() <= '9')
405 return in.toLatin1();
406
407 if (in == m_plus || in == QLatin1Char('+'))
408 return '+';
409
410 if (in == m_minus || in == QLatin1Char('-') || in == QChar(0x2212))
411 return '-';
412
413 if (in == m_decimal)
414 return '.';
415
416 if (in == m_group)
417 return ',';
418
419 if (in == m_exponential || in == QChar(QChar::toUpper(m_exponential)))
420 return 'e';
421
422 // In several languages group() is a non-breaking space (U+00A0) or its thin
423 // version (U+202f), which look like spaces. People (and thus some of our
424 // tests) use a regular space instead and complain if it doesn't work.
425 if ((m_group == 0xA0 || m_group == 0x202f) && in.unicode() == ' ')
426 return ',';
427
428 return 0;
429 }
430
431 QString qt_readEscapedFormatString(QStringView format, int *idx);
432 bool qt_splitLocaleName(const QString &name, QString &lang, QString &script, QString &cntry);
433 int qt_repeatCount(QStringView s);
434
435 enum { AsciiSpaceMask = (1u << (' ' - 1)) |
436 (1u << ('\t' - 1)) | // 9: HT - horizontal tab
437 (1u << ('\n' - 1)) | // 10: LF - line feed
438 (1u << ('\v' - 1)) | // 11: VT - vertical tab
439 (1u << ('\f' - 1)) | // 12: FF - form feed
440 (1u << ('\r' - 1)) }; // 13: CR - carriage return
ascii_isspace(uchar c)441 Q_DECL_CONSTEXPR inline bool ascii_isspace(uchar c)
442 {
443 return c >= 1u && c <= 32u && (AsciiSpaceMask >> uint(c - 1)) & 1u;
444 }
445
446 #if defined(Q_COMPILER_CONSTEXPR)
447 Q_STATIC_ASSERT(ascii_isspace(' '));
448 Q_STATIC_ASSERT(ascii_isspace('\t'));
449 Q_STATIC_ASSERT(ascii_isspace('\n'));
450 Q_STATIC_ASSERT(ascii_isspace('\v'));
451 Q_STATIC_ASSERT(ascii_isspace('\f'));
452 Q_STATIC_ASSERT(ascii_isspace('\r'));
453 Q_STATIC_ASSERT(!ascii_isspace('\0'));
454 Q_STATIC_ASSERT(!ascii_isspace('\a'));
455 Q_STATIC_ASSERT(!ascii_isspace('a'));
456 Q_STATIC_ASSERT(!ascii_isspace('\177'));
457 Q_STATIC_ASSERT(!ascii_isspace(uchar('\200')));
458 Q_STATIC_ASSERT(!ascii_isspace(uchar('\xA0')));
459 Q_STATIC_ASSERT(!ascii_isspace(uchar('\377')));
460 #endif
461
462 QT_END_NAMESPACE
463
464 Q_DECLARE_METATYPE(QStringRef)
465 Q_DECLARE_METATYPE(QList<Qt::DayOfWeek>)
466 #ifndef QT_NO_SYSTEMLOCALE
467 Q_DECLARE_METATYPE(QSystemLocale::CurrencyToStringArgument)
468 #endif
469
470 #endif // QLOCALE_P_H
471