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