File indexing completed on 2024-04-21 14:55:37

0001 /* This file is part of the KDE libraries
0002    Copyright (c) 1997,2001 Stephan Kulow <coolo@kde.org>
0003    Copyright (c) 1999 Preston Brown <pbrown@kde.org>
0004    Copyright (c) 1999-2002 Hans Petter Bieker <bieker@kde.org>
0005    Copyright (c) 2002 Lukas Tinkl <lukas@kde.org>
0006    Copyright (C) 2007 Bernhard Loos <nhuh.put@web.de>
0007    Copyright (C) 2009, 2010 John Layt <john@layt.net>
0008 
0009    This library is free software; you can redistribute it and/or
0010    modify it under the terms of the GNU Library General Public
0011    License as published by the Free Software Foundation; either
0012    version 2 of the License, or (at your option) any later version.
0013 
0014    This library is distributed in the hope that it will be useful,
0015    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017    Library General Public License for more details.
0018 
0019    You should have received a copy of the GNU Library General Public License
0020    along with this library; see the file COPYING.LIB.  If not, write to
0021    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022    Boston, MA 02110-1301, USA.
0023 */
0024 
0025 #include "klocale_p.h"
0026 
0027 #include "config-localization.h"
0028 
0029 #include <math.h>
0030 #include <locale.h>
0031 
0032 #if HAVE_SYS_TIME_H
0033 #include <sys/time.h>
0034 #endif
0035 #if HAVE_TIME_H
0036 #include <time.h>
0037 #endif
0038 #if HAVE_LANGINFO_H
0039 #include <langinfo.h>
0040 #endif
0041 
0042 #include <QTextCodec>
0043 #include <QFile>
0044 #include <QDebug>
0045 #include <QPrinter>
0046 #include <QRegExp>
0047 #include <QLocale>
0048 #include <QHash>
0049 #include <QMutexLocker>
0050 #include <QStringList>
0051 #include <QCoreApplication>
0052 #include <QDir>
0053 #include <qstandardpaths.h>
0054 
0055 #include "kconfig.h"
0056 #include "kdatetime.h"
0057 #include "kcalendarsystem.h"
0058 #include "kcurrencycode.h"
0059 #include "klocalizedstring.h"
0060 #include "kconfiggroup.h"
0061 #include "kdayperiod_p.h"
0062 
0063 class KLocaleStaticData
0064 {
0065 public:
0066 
0067     KLocaleStaticData();
0068 
0069     // FIXME: Temporary until full language-sensitivity implemented.
0070     QHash<KLocale::DigitSet, QStringList> languagesUsingDigitSet;
0071 };
0072 
0073 KLocaleStaticData::KLocaleStaticData()
0074 {
0075     // Languages using non-Western Arabic digit sets.
0076     // FIXME: Temporary until full language-sensitivity implemented.
0077     languagesUsingDigitSet.insert(KLocale::ArabicIndicDigits, QStringList() << QString::fromLatin1("ar") << QString::fromLatin1("ps"));
0078     languagesUsingDigitSet.insert(KLocale::BengaliDigits, QStringList() << QString::fromLatin1("bn") << QString::fromLatin1("as"));
0079     languagesUsingDigitSet.insert(KLocale::DevenagariDigits, QStringList() << QString::fromLatin1("hi") << QString::fromLatin1("ne"));
0080     languagesUsingDigitSet.insert(KLocale::EasternArabicIndicDigits, QStringList() << QString::fromLatin1("fa") << QString::fromLatin1("ur"));
0081     languagesUsingDigitSet.insert(KLocale::GujaratiDigits, QStringList() << QString::fromLatin1("gu"));
0082     languagesUsingDigitSet.insert(KLocale::GurmukhiDigits, QStringList() << QString::fromLatin1("pa"));
0083     languagesUsingDigitSet.insert(KLocale::KannadaDigits, QStringList() << QString::fromLatin1("kn"));
0084     languagesUsingDigitSet.insert(KLocale::KhmerDigits, QStringList() << QString::fromLatin1("km"));
0085     languagesUsingDigitSet.insert(KLocale::MalayalamDigits, QStringList() << QString::fromLatin1("ml"));
0086     languagesUsingDigitSet.insert(KLocale::OriyaDigits, QStringList() << QString::fromLatin1("or"));
0087     languagesUsingDigitSet.insert(KLocale::TamilDigits, QStringList() << QString::fromLatin1("ta"));
0088     languagesUsingDigitSet.insert(KLocale::TeluguDigits, QStringList() << QString::fromLatin1("te"));
0089     languagesUsingDigitSet.insert(KLocale::ThaiDigits, QStringList() << QString::fromLatin1("th"));
0090 }
0091 
0092 Q_GLOBAL_STATIC(KLocaleStaticData, staticData)
0093 
0094 Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_kLocaleMutex, (QMutex::Recursive))
0095 
0096 KLocalePrivate::KLocalePrivate(KLocale *q_ptr)
0097     : q(q_ptr),
0098       m_config(KSharedConfig::Ptr()),
0099       m_country(QString()),
0100       m_language(QString()),
0101       m_languages(nullptr),
0102       m_calendar(nullptr),
0103       m_currency(nullptr),
0104       m_codecForEncoding(nullptr)
0105 {
0106 }
0107 
0108 KLocalePrivate::KLocalePrivate(const KLocalePrivate &rhs)
0109 {
0110     copy(rhs);
0111 }
0112 
0113 KLocalePrivate &KLocalePrivate::operator=(const KLocalePrivate &rhs)
0114 {
0115     copy(rhs);
0116     return *this;
0117 }
0118 
0119 KSharedConfig::Ptr KLocalePrivate::config()
0120 {
0121     if (m_config) {
0122         return m_config;
0123     } else {
0124         return KSharedConfig::openConfig();
0125     }
0126 }
0127 
0128 void KLocalePrivate::copy(const KLocalePrivate &rhs)
0129 {
0130     // Parent KLocale
0131     q = nullptr;
0132 
0133     // Config
0134     m_config = rhs.m_config;
0135 
0136     // Country settings
0137     m_country = rhs.m_country;
0138     m_countryDivisionCode = rhs.m_countryDivisionCode;
0139 
0140     // Language settings
0141     m_language = rhs.m_language;
0142     m_languages = nullptr;
0143     m_languageList = rhs.m_languageList;
0144     m_languageSensitiveDigits = rhs.m_languageSensitiveDigits;
0145     m_nounDeclension = rhs.m_nounDeclension;
0146 
0147     // Calendar settings
0148     m_calendarSystem = rhs.m_calendarSystem;
0149     m_calendar = nullptr;
0150     m_weekStartDay = rhs.m_weekStartDay;
0151     m_workingWeekStartDay = rhs.m_workingWeekStartDay;
0152     m_workingWeekEndDay = rhs.m_workingWeekEndDay;
0153     m_weekDayOfPray = rhs.m_weekDayOfPray;
0154 
0155     // Date/Time settings
0156     m_dateFormat = rhs.m_dateFormat;
0157     m_dateFormatShort = rhs.m_dateFormatShort;
0158     m_timeFormat = rhs.m_timeFormat;
0159     m_dateTimeDigitSet = rhs.m_dateTimeDigitSet;
0160     m_dateMonthNamePossessive = rhs.m_dateMonthNamePossessive;
0161     m_dayPeriods = rhs.m_dayPeriods;
0162     m_weekNumberSystem = rhs.m_weekNumberSystem;
0163 
0164     // Number settings
0165     m_decimalPlaces = rhs.m_decimalPlaces;
0166     m_decimalSymbol = rhs.m_decimalSymbol;
0167     m_thousandsSeparator = rhs.m_thousandsSeparator;
0168     m_numericDigitGrouping = rhs.m_numericDigitGrouping;
0169     m_positiveSign = rhs.m_positiveSign;
0170     m_negativeSign = rhs.m_negativeSign;
0171     m_digitSet = rhs.m_digitSet;
0172 
0173     // Currency settings
0174     m_currencyCode = rhs.m_currencyCode;
0175     m_currency = nullptr;
0176     m_currencyCodeList = rhs.m_currencyCodeList;
0177 
0178     // Money settings
0179     m_currencySymbol = rhs.m_currencySymbol;
0180     m_monetaryDecimalSymbol = rhs.m_monetaryDecimalSymbol;
0181     m_monetaryThousandsSeparator = rhs.m_monetaryThousandsSeparator;
0182     m_monetaryDigitGrouping = rhs.m_monetaryDigitGrouping;
0183     m_monetaryDecimalPlaces = rhs.m_monetaryDecimalPlaces;
0184     m_positiveMonetarySignPosition = rhs.m_positiveMonetarySignPosition;
0185     m_negativeMonetarySignPosition = rhs.m_negativeMonetarySignPosition;
0186     m_positivePrefixCurrencySymbol = rhs.m_positivePrefixCurrencySymbol;
0187     m_negativePrefixCurrencySymbol = rhs.m_negativePrefixCurrencySymbol;
0188     m_monetaryDigitSet = rhs.m_monetaryDigitSet;
0189 
0190     // Units settings
0191     m_binaryUnitDialect = rhs.m_binaryUnitDialect;
0192     m_byteSizeFmt = rhs.m_byteSizeFmt;
0193     m_pageSize = rhs.m_pageSize;
0194     m_measureSystem = rhs.m_measureSystem;
0195 
0196     // Encoding settings
0197     m_encoding = rhs.m_encoding;
0198     m_codecForEncoding = rhs.m_codecForEncoding;
0199     m_utf8FileEncoding = rhs.m_utf8FileEncoding;
0200 }
0201 
0202 KLocalePrivate::~KLocalePrivate()
0203 {
0204     delete m_currency;
0205     delete m_calendar;
0206     delete m_languages;
0207 }
0208 
0209 // init only called from platform specific constructor, so set everything up
0210 // Will be given a persistantConfig or a tempConfig or neither, but never both
0211 void KLocalePrivate::init(const QString &language, const QString &country,
0212                           KSharedConfig::Ptr persistantConfig, KConfig *tempConfig)
0213 {
0214     // Only keep the persistant config if it is not the global
0215     if (persistantConfig != KSharedConfig::Ptr() && persistantConfig != KSharedConfig::openConfig()) {
0216         m_config = persistantConfig;
0217     }
0218 
0219     KConfigGroup cg;
0220     bool useEnvironmentVariables;
0221 
0222     // We can't read the formats from the config until we know what locale to read in, but we need
0223     // to read the config to find out the locale.  The Country and Language settings should never
0224     // be localized in the config, so we can read a temp copy of them to get us started.
0225 
0226     // If no config given, use the global config and include envvars, otherwise use only the config.
0227     if (m_config != KSharedConfig::Ptr()) {
0228         cg = m_config->group(QLatin1String("Locale"));
0229         useEnvironmentVariables = false;
0230     } else if (tempConfig == nullptr || tempConfig == KSharedConfig::openConfig().data()) {
0231         cg = KSharedConfig::openConfig()->group(QLatin1String("Locale"));
0232         useEnvironmentVariables = true;
0233     } else {
0234         cg = tempConfig->group(QLatin1String("Locale"));
0235         useEnvironmentVariables = false;
0236     }
0237 
0238     initEncoding();
0239     initCountry(country, cg.readEntry(QLatin1String("Country")));
0240     initLanguageList(language, cg.readEntry(QLatin1String("Language")), useEnvironmentVariables);
0241     // Now that we have a language, we can set up the config which uses it to setLocale()
0242     initConfig(tempConfig);
0243     initFormat();
0244 }
0245 
0246 // Init the config, this is called during construction and by later setCountry/setLanguage calls.
0247 // You _must_ have the m_language set to a valid language or en_US before calling this so a
0248 // setLocale can be applied to the config
0249 void KLocalePrivate::initConfig(KConfig *config)
0250 {
0251     // * If we were constructed with a KSharedConfig it means the user gave it to us
0252     //   to use for the life of the KLocale, so just keep using it after a setLocale
0253     // * If passed in KConfig is null or the global config then use the global, but
0254     //   do the setLocale first.
0255     // * If we have a KConfig we need to use that, but due to keeping old behaviour
0256     //   of not requiring access to it for life we can't keep a reference so instead
0257     //   take a copy and use that, but do setLocale first.
0258 
0259     if (m_config != KSharedConfig::Ptr()) {
0260         m_config->setLocale(m_language);
0261     } else {
0262         // If no config given then use the global
0263         if (config == nullptr || config == KSharedConfig::openConfig().data()) {
0264             KSharedConfig::openConfig()->setLocale(m_language);
0265         } else {
0266             config->setLocale(m_language);
0267             m_config = KSharedConfig::openConfig();
0268             config->copyTo(QString(), m_config.data());
0269             m_config->markAsClean();
0270         }
0271     }
0272 }
0273 
0274 void KLocalePrivate::getLanguagesFromVariable(QStringList &list, const char *variable, bool isLanguageList)
0275 {
0276     QByteArray var(qgetenv(variable));
0277     if (!var.isEmpty()) {
0278         QString value = QFile::decodeName(var);
0279         if (isLanguageList) {
0280             list += value.split(QLatin1Char(':'));
0281         } else {
0282             // Process the value to create possible combinations.
0283             QString lang, ctry, modf, cset;
0284             KLocale::splitLocale(value, lang, ctry, modf, cset);
0285 
0286             if (!ctry.isEmpty() && !modf.isEmpty()) {
0287                 list += lang + QLatin1Char('_') + ctry + QLatin1Char('@') + modf;
0288             }
0289             // NOTE: The priority is tricky in case both ctry and modf are present.
0290             // Should really lang@modf be of higher priority than lang_ctry?
0291             // For at least one case (Serbian language), it is better this way.
0292             if (!modf.isEmpty()) {
0293                 list += lang + QLatin1Char('@') + modf;
0294             }
0295             if (!ctry.isEmpty()) {
0296                 list += lang + QLatin1Char('_') + ctry;
0297             }
0298             list += lang;
0299         }
0300     }
0301 }
0302 
0303 // init the country at construction only, will ensure we always have a country set
0304 void KLocalePrivate::initCountry(const QString &country, const QString &configCountry)
0305 {
0306     // Cache the valid countries list and add the default C as it is valid to use
0307     QStringList validCountries = allCountriesList();
0308     validCountries.append(defaultCountry());
0309 
0310     // First check if the constructor passed in a value and if so if it is valid
0311     QString putativeCountry = country;
0312 
0313     if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
0314 
0315         // If the requested country is not valid, try the country as set in the config:
0316         putativeCountry = configCountry;
0317 
0318         if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
0319 
0320             // If the config country is not valid try the current host system country
0321             putativeCountry = systemCountry();
0322 
0323             if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
0324                 // Only if no other option, resort to the default C
0325                 putativeCountry = defaultCountry();
0326             }
0327         }
0328     }
0329 
0330     // Always save as lowercase, unless it's C when we want it uppercase
0331     if (putativeCountry.toLower() == defaultCountry().toLower()) {
0332         m_country = defaultCountry();
0333     } else {
0334         m_country = putativeCountry.toLower();
0335     }
0336 }
0337 
0338 QString KLocalePrivate::systemCountry() const
0339 {
0340     // Use QLocale for now as it supposedly provides a sensible default most times,
0341     // e.g. if locale is only "de" it is assumed to mean country of "DE"
0342     QString systemCountry, s1, s2, s3;
0343     splitLocale(QLocale::system().name(), s1, systemCountry, s2, s3);
0344     return systemCountry.toLower();
0345 }
0346 
0347 void KLocalePrivate::initLanguageList(const QString &language, const QString &configLanguages,
0348                                       bool useEnvironmentVariables)
0349 {
0350     m_language = language;
0351 
0352     // Collect possible languages by decreasing priority.
0353     // The priority is as follows:
0354     // - the internally set language, if any
0355     // - KDE_LANG environment variable (can be a list)
0356     // - KDE configuration (can be a list)
0357     // - environment variables considered by gettext(3)
0358     // The environment variables are not considered if useEnvironmentVariables is false.
0359     QStringList list;
0360     if (!m_language.isEmpty()) {
0361         list += m_language;
0362     }
0363 
0364     // If the Locale object was created with a specific config file, then do not use the
0365     // environmental variables.  If the locale object was created with the global config, then
0366     // do use the environmental variables.
0367     if (useEnvironmentVariables) {
0368         // KDE_LANG contains list of language codes, not locale string.
0369         getLanguagesFromVariable(list, "KDE_LANG", true);
0370     }
0371 
0372     if (!configLanguages.isEmpty()) {
0373         list += configLanguages.split(QLatin1Char(':'));
0374     }
0375 
0376     if (useEnvironmentVariables) {
0377         // Collect languages by same order of priority as for gettext(3).
0378         // LANGUAGE contains list of language codes, not locale string.
0379         getLanguagesFromVariable(list, "LANGUAGE", true);
0380         getLanguagesFromVariable(list, "LC_ALL");
0381         getLanguagesFromVariable(list, "LC_MESSAGES");
0382         getLanguagesFromVariable(list, "LANG");
0383     }
0384 
0385     // fall back to the system language
0386     list += systemLanguageList();
0387 
0388     // Send the list to filter for really present languages on the system.
0389     setLanguage(list);
0390 }
0391 
0392 QStringList KLocalePrivate::systemLanguageList() const
0393 {
0394     return QStringList();
0395 }
0396 
0397 void KLocalePrivate::initFormat()
0398 {
0399     KConfigGroup cg(config(), "Locale");
0400 
0401     KConfig entryFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") + QString::fromLatin1("%1/country.desktop").arg(m_country)));
0402     entryFile.setLocale(m_language);
0403     KConfigGroup entry(&entryFile, "KCM Locale");
0404 
0405     //One-time conversion in 4.4 from FracDigits to DecimalPlaces and MonetaryDecimalPlaces
0406     //If user has personal setting for FracDigits then use it for both Decimal Places
0407     //TODO: Possible to do with kconf_update
0408     if (cg.hasKey("FracDigits")) {
0409         QString fracDigits = cg.readEntry("FracDigits", "");
0410         if (!fracDigits.isEmpty()) {
0411             cg.writeEntry("DecimalPlaces", fracDigits);
0412             cg.writeEntry("MonetaryDecimalPlaces", fracDigits);
0413         }
0414         cg.deleteEntry("FracDigits");
0415         cg.config()->sync();
0416     }
0417 
0418     // Numeric
0419 #define readConfigEntry(key, default, save) \
0420     save = entry.readEntry(key, default); \
0421     save = cg.readEntry(key, save);
0422 
0423 #define readConfigNumEntry(key, default, save, type) \
0424     save = (type)entry.readEntry(key, int(default)); \
0425     save = (type)cg.readEntry(key, int(save));
0426 
0427     // Country settings
0428     readConfigEntry("CountryDivisionCode", QString(), m_countryDivisionCode);
0429 
0430     // Numeric formats
0431     readConfigNumEntry("DecimalPlaces", 2, m_decimalPlaces, int);
0432 
0433     readConfigEntry("DecimalSymbol", ".", m_decimalSymbol);
0434     readConfigEntry("ThousandsSeparator", ",", m_thousandsSeparator);
0435     m_thousandsSeparator.remove(QString::fromLatin1("$0"));
0436     QString digitGroupFormat;
0437     readConfigEntry("DigitGroupFormat", "3", digitGroupFormat);
0438     m_numericDigitGrouping = digitGroupFormatToList(digitGroupFormat);
0439 
0440     readConfigEntry("PositiveSign", "", m_positiveSign);
0441     readConfigEntry("NegativeSign", "-", m_negativeSign);
0442 
0443     readConfigNumEntry("DigitSet", KLocale::ArabicDigits, m_digitSet, KLocale::DigitSet);
0444     // FIXME: Temporary until full language-sensitivity implemented.
0445     readConfigEntry("LanguageSensitiveDigits", true, m_languageSensitiveDigits);
0446 
0447     // Currency
0448     readConfigEntry("CurrencyCode", "USD", m_currencyCode);
0449     initCurrency();
0450     readConfigEntry("CurrencySymbol", m_currency->defaultSymbol(), m_currencySymbol);
0451     readConfigEntry("CurrencyCodesInUse", QStringList(m_currencyCode), m_currencyCodeList);
0452 
0453     // Monetary formats
0454     readConfigNumEntry("MonetaryDecimalPlaces", m_currency->decimalPlaces(), m_monetaryDecimalPlaces, int);
0455 
0456     readConfigEntry("MonetaryDecimalSymbol", ".", m_monetaryDecimalSymbol);
0457     readConfigEntry("MonetaryThousandsSeparator", ",", m_monetaryThousandsSeparator);
0458     m_monetaryThousandsSeparator.remove(QString::fromLatin1("$0"));
0459     readConfigEntry("MonetaryDigitGroupFormat", "3", digitGroupFormat);
0460     m_monetaryDigitGrouping = digitGroupFormatToList(digitGroupFormat);
0461 
0462     readConfigEntry("PositivePrefixCurrencySymbol", true, m_positivePrefixCurrencySymbol);
0463     readConfigEntry("NegativePrefixCurrencySymbol", true, m_negativePrefixCurrencySymbol);
0464     readConfigNumEntry("PositiveMonetarySignPosition", KLocale::BeforeQuantityMoney,
0465                        m_positiveMonetarySignPosition, KLocale::SignPosition);
0466     readConfigNumEntry("NegativeMonetarySignPosition", KLocale::ParensAround,
0467                        m_negativeMonetarySignPosition, KLocale::SignPosition);
0468 
0469     readConfigNumEntry("MonetaryDigitSet", KLocale::ArabicDigits,
0470                        m_monetaryDigitSet, KLocale::DigitSet);
0471     readConfigNumEntry("BinaryUnitDialect", KLocale::IECBinaryDialect,
0472                        m_binaryUnitDialect, KLocale::BinaryUnitDialect);
0473 
0474     // Date and time
0475     readConfigEntry("TimeFormat", "%H:%M:%S", m_timeFormat);
0476     readConfigEntry("DateFormat", "%A %d %B %Y", m_dateFormat);
0477     readConfigEntry("DateFormatShort", "%Y-%m-%d", m_dateFormatShort);
0478     readConfigNumEntry("WeekStartDay", 1, m_weekStartDay, int);                //default to Monday
0479     readConfigNumEntry("WorkingWeekStartDay", 1, m_workingWeekStartDay, int);  //default to Monday
0480     readConfigNumEntry("WorkingWeekEndDay", 5, m_workingWeekEndDay, int);      //default to Friday
0481     readConfigNumEntry("WeekDayOfPray", 7, m_weekDayOfPray, int);              //default to Sunday
0482     readConfigNumEntry("DateTimeDigitSet", KLocale::ArabicDigits,
0483                        m_dateTimeDigitSet, KLocale::DigitSet);
0484     readConfigNumEntry("WeekNumberSystem", KLocale::IsoWeekNumber,
0485                        m_weekNumberSystem, KLocale::WeekNumberSystem);
0486 
0487     // other
0488 #ifndef QT_NO_PRINTER
0489     readConfigNumEntry("PageSize", QPrinter::A4, m_pageSize, QPrinter::PageSize);
0490 #endif
0491     readConfigNumEntry("MeasureSystem", KLocale::Metric, m_measureSystem, KLocale::MeasureSystem);
0492     QString calendarType;
0493     readConfigEntry("CalendarSystem", "gregorian", calendarType);
0494     setCalendar(calendarType);
0495 
0496     //Grammatical
0497     //Precedence here is l10n / i18n / config file
0498     KConfig langCfg(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("locale/") + QString::fromLatin1("%1/kf5_entry.desktop").arg(m_language)));
0499     KConfigGroup lang(&langCfg, "KCM Locale");
0500 #define read3ConfigBoolEntry(key, default, save) \
0501     save = entry.readEntry(key, default); \
0502     save = lang.readEntry(key, save); \
0503     save = cg.readEntry(key, save);
0504 
0505     read3ConfigBoolEntry("NounDeclension", false, m_nounDeclension);
0506     read3ConfigBoolEntry("DateMonthNamePossessive", false, m_dateMonthNamePossessive);
0507 
0508     initDayPeriods(cg);
0509 }
0510 
0511 void KLocalePrivate::initDayPeriods(const KConfigGroup &cg)
0512 {
0513     // Prefer any l10n file value for country/language,
0514     // otherwise default to language only value which will be filled in later when i18n available
0515 
0516     //Day Period are stored in config as one QStringList entry per Day Period
0517     //PeriodCode,LongName,ShortName,NarrowName,StartTime,EndTime,Offset,OffsetIfZero
0518     //where start and end time are in the format HH:MM:SS.MMM
0519 
0520     m_dayPeriods.clear();
0521     QString periodKey = QString::fromLatin1("DayPeriod1");
0522     int i = 1;
0523     while (cg.hasKey(periodKey)) {
0524         QStringList period = cg.readEntry(periodKey, QStringList());
0525         if (period.count() == 8) {
0526             m_dayPeriods.append(KDayPeriod(period[0], period[1], period[2], period[3],
0527                                            QTime::fromString(period[4], QString::fromLatin1("HH:mm:ss.zzz")),
0528                                            QTime::fromString(period[5], QString::fromLatin1("HH:mm:ss.zzz")),
0529                                            period[6].toInt(), period[7].toInt()));
0530         }
0531         i = i + 1;
0532         periodKey = QString::fromLatin1("DayPeriod%1").arg(i);
0533     }
0534 }
0535 
0536 bool KLocalePrivate::setCountry(const QString &country, KConfig *newConfig)
0537 {
0538     // Cache the valid countries list and add the default C as it is valid to use
0539     QStringList validCountries = allCountriesList();
0540     validCountries.append(defaultCountry());
0541 
0542     QString putativeCountry = country;
0543 
0544     if (putativeCountry.isEmpty()) {
0545         // An empty string means to use the system country
0546         putativeCountry = systemCountry();
0547         if (putativeCountry.isEmpty() || !validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
0548             // If the system country is not valid, use the default
0549             putativeCountry = defaultCountry();
0550         }
0551     } else if (!validCountries.contains(putativeCountry, Qt::CaseInsensitive)) {
0552         return false;
0553     }
0554 
0555     // Always save as lowercase, unless it's C when we want it uppercase
0556     if (putativeCountry.toLower() == defaultCountry().toLower()) {
0557         m_country = defaultCountry();
0558     } else {
0559         m_country = putativeCountry.toLower();
0560     }
0561 
0562     // Get rid of the old config, start again with the new
0563     m_config = KSharedConfig::Ptr();
0564     initConfig(newConfig);
0565 
0566     // Init all the settings
0567     initFormat();
0568 
0569     return true;
0570 }
0571 
0572 bool KLocalePrivate::setCountryDivisionCode(const QString &countryDivisionCode)
0573 {
0574     m_countryDivisionCode = countryDivisionCode;
0575     return true;
0576 }
0577 
0578 bool KLocalePrivate::setLanguage(const QString &language, KConfig *config)
0579 {
0580     QMutexLocker lock(s_kLocaleMutex());
0581     m_languageList.removeAll(language);
0582     m_languageList.prepend(language);   // let us consider this language to be the most important one
0583 
0584     m_language = language; // remember main language for shortcut evaluation
0585 
0586     // Get rid of the old config, start again with the new
0587     m_config = KSharedConfig::Ptr();
0588     initConfig(config);
0589 
0590     // Init the new format settings
0591     initFormat();
0592 
0593     // Maybe the mo-files for this language are empty, but in principle we can speak all languages
0594     return true;
0595 }
0596 
0597 // KDE5 Unlike the other setLanguage call this does not reparse the config so the localized config
0598 // settings for the new primary language will _not_ be loaded.  In KDE5 always keep the original
0599 // config so this can be reparsed when required.
0600 bool KLocalePrivate::setLanguage(const QStringList &languages)
0601 {
0602     QMutexLocker lock(s_kLocaleMutex());
0603     // This list might contain
0604     // 1) some empty strings that we have to eliminate
0605     // 2) duplicate entries like in de:fr:de, where we have to keep the first occurrence of a
0606     //    language in order to preserve the order of precenence of the user
0607     // 3) languages into which the application is not translated. For those languages we should not
0608     //    even load kdelibs.mo or kio.po. these languages have to be dropped. Otherwise we get
0609     //    strange side effects, e.g. with Hebrew: the right/left switch for languages that write
0610     //    from right to left (like Hebrew or Arabic) is set in kdelibs.mo. If you only have
0611     //    kdelibs.mo but nothing from appname.mo, you get a mostly English app with layout from
0612     //    right to left. That was considered to be a bug by the Hebrew translators.
0613     QStringList list;
0614     foreach (const QString &language, languages) {
0615         if (!language.isEmpty() && !list.contains(language) && KLocalizedString::isApplicationTranslatedInto(language)) {
0616             list.append(language);
0617         }
0618     }
0619 
0620     if (!list.contains(KLocale::defaultLanguage())) {
0621         // English should always be added as final possibility; this is important
0622         // for proper initialization of message text post-processors which are
0623         // needed for English too, like semantic to visual formatting, etc.
0624         list.append(KLocale::defaultLanguage());
0625     }
0626 
0627     m_language = list.first(); // keep this for shortcut evaluations
0628 
0629     m_languageList = list; // keep this new list of languages to use
0630 
0631     return true; // we found something. Maybe it's only English, but we found something
0632 }
0633 
0634 void KLocalePrivate::initCurrency()
0635 {
0636     if (m_currencyCode.isEmpty() || !KCurrencyCode::isValid(m_currencyCode)) {
0637         m_currencyCode = KLocale::defaultCurrencyCode();
0638     }
0639 
0640     if (!m_currency || m_currencyCode != m_currency->isoCurrencyCode() || !m_currency->isValid()) {
0641         delete m_currency;
0642         m_currency = new KCurrencyCode(m_currencyCode, m_language);
0643     }
0644 }
0645 
0646 void KLocalePrivate::setCurrencyCode(const QString &newCurrencyCode)
0647 {
0648     if (!newCurrencyCode.isEmpty() && newCurrencyCode != m_currency->isoCurrencyCode() &&
0649             KCurrencyCode::isValid(newCurrencyCode)) {
0650         m_currencyCode = newCurrencyCode;
0651         initCurrency();
0652     }
0653 }
0654 
0655 void KLocalePrivate::splitLocale(const QString &aLocale, QString &language, QString &country,
0656                                  QString &modifier, QString &charset)
0657 {
0658     QString locale = aLocale;
0659 
0660     language.clear();
0661     country.clear();
0662     modifier.clear();
0663     charset.clear();
0664 
0665     // In case there are several concatenated locale specifications,
0666     // truncate all but first.
0667     int f = locale.indexOf(QLatin1Char(':'));
0668     if (f >= 0) {
0669         locale.truncate(f);
0670     }
0671 
0672     f = locale.indexOf(QLatin1Char('.'));
0673     if (f >= 0) {
0674         charset = locale.mid(f + 1);
0675         locale.truncate(f);
0676     }
0677 
0678     f = locale.indexOf(QLatin1Char('@'));
0679     if (f >= 0) {
0680         modifier = locale.mid(f + 1);
0681         locale.truncate(f);
0682     }
0683 
0684     f = locale.indexOf(QLatin1Char('_'));
0685     if (f >= 0) {
0686         country = locale.mid(f + 1);
0687         locale.truncate(f);
0688     }
0689 
0690     language = locale;
0691 }
0692 
0693 QString KLocalePrivate::language() const
0694 {
0695     return m_language;
0696 }
0697 
0698 QString KLocalePrivate::country() const
0699 {
0700     return m_country;
0701 }
0702 
0703 QString KLocalePrivate::countryDivisionCode() const
0704 {
0705     if (m_countryDivisionCode.isEmpty()) {
0706         return country().toUpper();
0707     } else {
0708         return m_countryDivisionCode;
0709     }
0710 }
0711 
0712 KCurrencyCode *KLocalePrivate::currency()
0713 {
0714     if (!m_currency) {
0715         initCurrency();
0716     }
0717     return m_currency;
0718 }
0719 
0720 QString KLocalePrivate::currencyCode() const
0721 {
0722     return m_currencyCode;
0723 }
0724 
0725 QList<KLocale::DigitSet> KLocalePrivate::allDigitSetsList() const
0726 {
0727     QList<KLocale::DigitSet> digitSets;
0728     digitSets.append(KLocale::ArabicDigits);
0729     digitSets.append(KLocale::ArabicIndicDigits);
0730     digitSets.append(KLocale::BengaliDigits);
0731     digitSets.append(KLocale::DevenagariDigits);
0732     digitSets.append(KLocale::EasternArabicIndicDigits);
0733     digitSets.append(KLocale::GujaratiDigits);
0734     digitSets.append(KLocale::GurmukhiDigits);
0735     digitSets.append(KLocale::KannadaDigits);
0736     digitSets.append(KLocale::KhmerDigits);
0737     digitSets.append(KLocale::MalayalamDigits);
0738     digitSets.append(KLocale::OriyaDigits);
0739     digitSets.append(KLocale::TamilDigits);
0740     digitSets.append(KLocale::TeluguDigits);
0741     digitSets.append(KLocale::ThaiDigits);
0742     std::sort(digitSets.begin(), digitSets.end());
0743     return digitSets;
0744 }
0745 
0746 QString KLocalePrivate::digitSetString(KLocale::DigitSet digitSet)
0747 {
0748     switch (digitSet) {
0749     case KLocale::ArabicIndicDigits:
0750         return QString::fromUtf8("٠١٢٣٤٥٦٧٨٩");
0751     case KLocale::BengaliDigits:
0752         return QString::fromUtf8("০১২৩৪৫৬৭৮৯");
0753     case KLocale::DevenagariDigits:
0754         return QString::fromUtf8("०१२३४५६७८९");
0755     case KLocale::EasternArabicIndicDigits:
0756         return QString::fromUtf8("۰۱۲۳۴۵۶۷۸۹");
0757     case KLocale::GujaratiDigits:
0758         return QString::fromUtf8("૦૧૨૩૪૫૬૭૮૯");
0759     case KLocale::GurmukhiDigits:
0760         return QString::fromUtf8("੦੧੨੩੪੫੬੭੮੯");
0761     case KLocale::KannadaDigits:
0762         return QString::fromUtf8("೦೧೨೩೪೫೬೭೮೯");
0763     case KLocale::KhmerDigits:
0764         return QString::fromUtf8("០១២៣៤៥៦៧៨៩");
0765     case KLocale::MalayalamDigits:
0766         return QString::fromUtf8("൦൧൨൩൪൫൬൭൮൯");
0767     case KLocale::OriyaDigits:
0768         return QString::fromUtf8("୦୧୨୩୪୫୬୭୮୯");
0769     case KLocale::TamilDigits:
0770         return QString::fromUtf8("௦௧௨௩௪௫௬௭௮");
0771     case KLocale::TeluguDigits:
0772         return QString::fromUtf8("౦౧౨౩౪౫౬౭౯");
0773     case KLocale::ThaiDigits:
0774         return QString::fromUtf8("๐๑๒๓๔๕๖๗๘๙");
0775     default:
0776         return QString::fromUtf8("0123456789");
0777     }
0778 }
0779 
0780 QString KLocalePrivate::digitSetToName(KLocale::DigitSet digitSet, bool withDigits) const
0781 {
0782     QString name;
0783     switch (digitSet) {
0784     case KLocale::ArabicIndicDigits:
0785         name = i18nc("digit set", "Arabic-Indic");
0786         break;
0787     case KLocale::BengaliDigits:
0788         name = i18nc("digit set", "Bengali");
0789         break;
0790     case KLocale::DevenagariDigits:
0791         name = i18nc("digit set", "Devanagari");
0792         break;
0793     case KLocale::EasternArabicIndicDigits:
0794         name = i18nc("digit set", "Eastern Arabic-Indic");
0795         break;
0796     case KLocale::GujaratiDigits:
0797         name = i18nc("digit set", "Gujarati");
0798         break;
0799     case KLocale::GurmukhiDigits:
0800         name = i18nc("digit set", "Gurmukhi");
0801         break;
0802     case KLocale::KannadaDigits:
0803         name = i18nc("digit set", "Kannada");
0804         break;
0805     case KLocale::KhmerDigits:
0806         name = i18nc("digit set", "Khmer");
0807         break;
0808     case KLocale::MalayalamDigits:
0809         name = i18nc("digit set", "Malayalam");
0810         break;
0811     case KLocale::OriyaDigits:
0812         name = i18nc("digit set", "Oriya");
0813         break;
0814     case KLocale::TamilDigits:
0815         name = i18nc("digit set", "Tamil");
0816         break;
0817     case KLocale::TeluguDigits:
0818         name = i18nc("digit set", "Telugu");
0819         break;
0820     case KLocale::ThaiDigits:
0821         name = i18nc("digit set", "Thai");
0822         break;
0823     default:
0824         name = i18nc("digit set", "Arabic");
0825     }
0826     if (withDigits) {
0827         QString digits = digitSetString(digitSet);
0828         QString nameWithDigits = i18nc("name of digit set with digit string, "
0829                                        "e.g. 'Arabic (0123456789)'", "%1 (%2)", name, digits);
0830         return nameWithDigits;
0831     } else {
0832         return name;
0833     }
0834 }
0835 
0836 QString KLocalePrivate::convertDigits(const QString &str, KLocale::DigitSet digitSet, bool ignoreContext) const
0837 {
0838     if (!ignoreContext) {
0839         // Fall back to Western Arabic digits if requested digit set
0840         // is not appropriate for current application language.
0841         // FIXME: Temporary until full language-sensitivity implemented.
0842         KLocaleStaticData *s = staticData();
0843         if (m_languageSensitiveDigits && !s->languagesUsingDigitSet[digitSet].contains(m_language)) {
0844             digitSet = KLocale::ArabicDigits;
0845         }
0846     }
0847 
0848     QString nstr;
0849     QString digitDraw = digitSetString(digitSet);
0850     foreach (const QChar &c, str) {
0851         if (c.isDigit()) {
0852             nstr += digitDraw[c.digitValue()];
0853         } else {
0854             nstr += c;
0855         }
0856     }
0857     return nstr;
0858 }
0859 
0860 QString KLocalePrivate::toArabicDigits(const QString &str)
0861 {
0862     QString nstr;
0863     foreach (const QChar &c, str) {
0864         if (c.isDigit()) {
0865             nstr += QChar('0' + c.digitValue());
0866         } else {
0867             nstr += c;
0868         }
0869     }
0870     return nstr;
0871 }
0872 
0873 bool KLocalePrivate::nounDeclension() const
0874 {
0875     return m_nounDeclension;
0876 }
0877 
0878 bool KLocalePrivate::dateMonthNamePossessive() const
0879 {
0880     return m_dateMonthNamePossessive;
0881 }
0882 
0883 int KLocalePrivate::weekStartDay() const
0884 {
0885     return m_weekStartDay;
0886 }
0887 
0888 int KLocalePrivate::workingWeekStartDay() const
0889 {
0890     return m_workingWeekStartDay;
0891 }
0892 
0893 int KLocalePrivate::workingWeekEndDay() const
0894 {
0895     return m_workingWeekEndDay;
0896 }
0897 
0898 int KLocalePrivate::weekDayOfPray() const
0899 {
0900     return m_weekDayOfPray;
0901 }
0902 
0903 int KLocalePrivate::decimalPlaces() const
0904 {
0905     return m_decimalPlaces;
0906 }
0907 
0908 QString KLocalePrivate::decimalSymbol() const
0909 {
0910     return m_decimalSymbol;
0911 }
0912 
0913 QString KLocalePrivate::thousandsSeparator() const
0914 {
0915     return m_thousandsSeparator;
0916 }
0917 
0918 QList<int> KLocalePrivate::numericDigitGrouping() const
0919 {
0920     return m_numericDigitGrouping;
0921 }
0922 
0923 QString KLocalePrivate::currencySymbol() const
0924 {
0925     return m_currencySymbol;
0926 }
0927 
0928 QString KLocalePrivate::monetaryDecimalSymbol() const
0929 {
0930     return m_monetaryDecimalSymbol;
0931 }
0932 
0933 QString KLocalePrivate::monetaryThousandsSeparator() const
0934 {
0935     return m_monetaryThousandsSeparator;
0936 }
0937 
0938 QList<int> KLocalePrivate::monetaryDigitGrouping() const
0939 {
0940     return m_monetaryDigitGrouping;
0941 }
0942 
0943 QString KLocalePrivate::positiveSign() const
0944 {
0945     return m_positiveSign;
0946 }
0947 
0948 QString KLocalePrivate::negativeSign() const
0949 {
0950     return m_negativeSign;
0951 }
0952 
0953 /* Just copy to keep the diff looking clean, delete later
0954 int KLocale::fracDigits() const
0955 {
0956     return monetaryDecimalPlaces();
0957 }
0958 */
0959 
0960 int KLocalePrivate::monetaryDecimalPlaces() const
0961 {
0962     return m_monetaryDecimalPlaces;
0963 }
0964 
0965 bool KLocalePrivate::positivePrefixCurrencySymbol() const
0966 {
0967     return m_positivePrefixCurrencySymbol;
0968 }
0969 
0970 bool KLocalePrivate::negativePrefixCurrencySymbol() const
0971 {
0972     return m_negativePrefixCurrencySymbol;
0973 }
0974 
0975 KLocale::SignPosition KLocalePrivate::positiveMonetarySignPosition() const
0976 {
0977     return m_positiveMonetarySignPosition;
0978 }
0979 
0980 KLocale::SignPosition KLocalePrivate::negativeMonetarySignPosition() const
0981 {
0982     return m_negativeMonetarySignPosition;
0983 }
0984 
0985 static inline void put_it_in(QChar *buffer, int &index, const QString &s)
0986 {
0987     for (int l = 0; l < s.length(); l++) {
0988         buffer[index++] = s.at(l);
0989     }
0990 }
0991 
0992 static inline void put_it_in(QChar *buffer, int &index, int number)
0993 {
0994     buffer[index++] = number / 10 + '0';
0995     buffer[index++] = number % 10 + '0';
0996 }
0997 
0998 // Convert POSIX Digit Group Format string into a Qlist<int>, e.g. "3;2" converts to (3,2)
0999 QList<int> KLocalePrivate::digitGroupFormatToList(const QString &digitGroupFormat) const
1000 {
1001     QList<int> groupList;
1002     QStringList stringList = digitGroupFormat.split(QLatin1Char(';'));
1003     foreach (const QString &size, stringList) {
1004         groupList.append(size.toInt());
1005     }
1006     return groupList;
1007 }
1008 
1009 // Inserts all required occurrences of the group separator into a number string.
1010 QString KLocalePrivate::formatDigitGroup(const QString &number, const QString &groupSeparator, const QString &decimalSeperator, QList<int> groupList) const
1011 {
1012     if (groupList.isEmpty() || groupSeparator.isEmpty()) {
1013         return number;
1014     }
1015 
1016     QString num = number;
1017     int groupCount = groupList.count();
1018     int groupAt = 0;
1019     int groupSize = groupList.at(groupAt);
1020     int pos = num.indexOf(decimalSeperator);
1021     if (pos == -1) {
1022         pos = num.length();
1023     }
1024     pos = pos - groupSize;
1025 
1026     while (pos > 0 && groupSize > 0) {
1027         num.insert(pos, groupSeparator);
1028         if (groupAt + 1 < groupCount) {
1029             ++groupAt;
1030             groupSize = groupList.at(groupAt);
1031         }
1032         pos = pos - groupSize;
1033     }
1034 
1035     return num;
1036 }
1037 
1038 // Strips all occurrences of the group separator from a number, returns ok if the separators were all in the valid positions
1039 QString KLocalePrivate::parseDigitGroup(const QString &number, const QString &groupSeparator, const QString &decimalSeparator, QList<int> groupList, bool *ok) const
1040 {
1041     QString num = number;
1042     bool valid = true;
1043 
1044     if (!groupSeparator.isEmpty()) {
1045         if (!groupList.isEmpty()) {
1046             int separatorSize = groupSeparator.length();
1047             int groupCount = groupList.count();
1048             int groupAt = 0;
1049             int groupSize = groupList.at(groupAt);
1050             int pos = number.indexOf(decimalSeparator);
1051             if (pos == -1) {
1052                 pos = number.length();
1053             }
1054             pos = pos - groupSize - separatorSize;
1055 
1056             while (pos > 0 && valid && groupSize > 0) {
1057                 if (num.mid(pos, separatorSize) == groupSeparator) {
1058                     num.remove(pos, separatorSize);
1059                     if (groupAt + 1 < groupCount) {
1060                         ++groupAt;
1061                         groupSize = groupList.at(groupAt);
1062                     }
1063                     pos = pos - groupSize - separatorSize;
1064                 } else {
1065                     valid = false;
1066                 }
1067             }
1068         }
1069 
1070         if (num.contains(groupSeparator)) {
1071             valid = false;
1072             num = num.remove(groupSeparator);
1073         }
1074     }
1075 
1076     if (ok) {
1077         *ok = valid;
1078     }
1079 
1080     return num;
1081 }
1082 
1083 QString KLocalePrivate::formatMoney(double num, const QString &symbol, int precision) const
1084 {
1085     // some defaults
1086     QString currencyString = symbol;
1087     if (symbol.isNull()) {
1088         currencyString = currencySymbol();
1089     }
1090     if (precision < 0) {
1091         precision = monetaryDecimalPlaces();
1092     }
1093 
1094     // the number itself
1095     bool neg = num < 0;
1096     QString res = QString::number(neg ? -num : num, 'f', precision);
1097 
1098     // Replace dot with locale decimal separator
1099     res.replace(QLatin1Char('.'), monetaryDecimalSymbol());
1100 
1101     // Insert the thousand separators
1102     res = formatDigitGroup(res, monetaryThousandsSeparator(), monetaryDecimalSymbol(), monetaryDigitGrouping());
1103 
1104     // set some variables we need later
1105     int signpos = neg
1106                   ? negativeMonetarySignPosition()
1107                   : positiveMonetarySignPosition();
1108     QString sign = neg
1109                    ? negativeSign()
1110                    : positiveSign();
1111 
1112     switch (signpos) {
1113     case KLocale::ParensAround:
1114         res.prepend(QLatin1Char('('));
1115         res.append(QLatin1Char(')'));
1116         break;
1117     case KLocale::BeforeQuantityMoney:
1118         res.prepend(sign);
1119         break;
1120     case KLocale::AfterQuantityMoney:
1121         res.append(sign);
1122         break;
1123     case KLocale::BeforeMoney:
1124         currencyString.prepend(sign);
1125         break;
1126     case KLocale::AfterMoney:
1127         currencyString.append(sign);
1128         break;
1129     }
1130 
1131     if (neg ? negativePrefixCurrencySymbol() :
1132             positivePrefixCurrencySymbol()) {
1133         res.prepend(QLatin1Char(' '));
1134         res.prepend(currencyString);
1135     } else {
1136         res.append(QLatin1Char(' '));
1137         res.append(currencyString);
1138     }
1139 
1140     // Convert to target digit set.
1141     res = convertDigits(res, m_monetaryDigitSet);
1142 
1143     return res;
1144 }
1145 
1146 QString KLocalePrivate::formatNumber(double num, int precision) const
1147 {
1148     if (precision < 0) {
1149         precision = decimalPlaces();
1150     }
1151     // no need to round since QString::number does this for us
1152     return formatNumber(QString::number(num, 'f', precision), false, 0);
1153 }
1154 
1155 QString KLocalePrivate::formatLong(long num) const
1156 {
1157     return formatNumber((double)num, 0);
1158 }
1159 
1160 // increase the digit at 'position' by one
1161 static void _inc_by_one(QString &str, int position)
1162 {
1163     for (int i = position; i >= 0; i--) {
1164         char last_char = str[i].toLatin1();
1165         switch (last_char) {
1166         case '0':
1167             str[i] = '1';
1168             break;
1169         case '1':
1170             str[i] = '2';
1171             break;
1172         case '2':
1173             str[i] = '3';
1174             break;
1175         case '3':
1176             str[i] = '4';
1177             break;
1178         case '4':
1179             str[i] = '5';
1180             break;
1181         case '5':
1182             str[i] = '6';
1183             break;
1184         case '6':
1185             str[i] = '7';
1186             break;
1187         case '7':
1188             str[i] = '8';
1189             break;
1190         case '8':
1191             str[i] = '9';
1192             break;
1193         case '9':
1194             str[i] = '0';
1195             if (i == 0) {
1196                 str.prepend(QLatin1Char('1'));
1197             }
1198             continue;
1199         case '.':
1200             continue;
1201         }
1202         break;
1203     }
1204 }
1205 
1206 // Cut off if more digits in fractional part than 'precision'
1207 static void _round(QString &str, int precision)
1208 {
1209     int decimalSymbolPos = str.indexOf(QLatin1Char('.'));
1210 
1211     if (decimalSymbolPos == -1) {
1212         if (precision == 0) {
1213             return;
1214         } else if (precision > 0) { // add dot if missing (and needed)
1215             str.append(QLatin1Char('.'));
1216             decimalSymbolPos = str.length() - 1;
1217         }
1218     }
1219     // fill up with more than enough zeroes (in case fractional part too short)
1220     str.reserve(str.length() + precision);
1221     for (int i = 0; i < precision; ++i) {
1222         str.append(QLatin1Char('0'));
1223     }
1224 
1225     // Now decide whether to round up or down
1226     char last_char = str[decimalSymbolPos + precision + 1].toLatin1();
1227     switch (last_char) {
1228     case '0':
1229     case '1':
1230     case '2':
1231     case '3':
1232     case '4':
1233         // nothing to do, rounding down
1234         break;
1235     case '5':
1236     case '6':
1237     case '7':
1238     case '8':
1239     case '9':
1240         _inc_by_one(str, decimalSymbolPos + precision);
1241         break;
1242     default:
1243         break;
1244     }
1245 
1246     decimalSymbolPos = str.indexOf(QLatin1Char('.'));
1247     str.truncate(decimalSymbolPos + precision + 1);
1248 
1249     // if precision == 0 delete also '.'
1250     if (precision == 0) {
1251         str = str.left(decimalSymbolPos);
1252     }
1253 
1254     str.squeeze();
1255 }
1256 
1257 QString KLocalePrivate::formatNumber(const QString &numStr, bool round, int precision) const
1258 {
1259     QString tmpString = numStr;
1260 
1261     if (precision < 0) {
1262         precision = decimalPlaces();
1263     }
1264 
1265     // Skip the sign (for now)
1266     const bool neg = (tmpString[0] == QLatin1Char('-'));
1267     if (neg || tmpString[0] == QLatin1Char('+')) {
1268         tmpString.remove(0, 1);
1269     }
1270 
1271     //qDebug()<<"tmpString:"<<tmpString;
1272 
1273     // Split off exponential part (including 'e'-symbol)
1274     const int expPos = tmpString.indexOf(QLatin1Char('e')); // -1 if not found
1275     QString mantString = tmpString.left(expPos); // entire string if no 'e' found
1276     QString expString;
1277     if (expPos > -1) {
1278         expString = tmpString.mid(expPos); // includes the 'e', or empty if no 'e'
1279         if (expString.length() == 1) {
1280             expString.clear();
1281         }
1282     }
1283 
1284     //qDebug()<<"mantString:"<<mantString;
1285     //qDebug()<<"expString:"<<expString;
1286     if (mantString.isEmpty() || !mantString[0].isDigit()) {// invalid number
1287         mantString = QLatin1Char('0');
1288     }
1289 
1290     if (round) {
1291         _round(mantString, precision);
1292     }
1293 
1294     // Replace dot with locale decimal separator
1295     mantString.replace(QLatin1Char('.'), decimalSymbol());
1296 
1297     // Insert the thousand separators
1298     mantString = formatDigitGroup(mantString, thousandsSeparator(), decimalSymbol(), numericDigitGrouping());
1299 
1300     // How can we know where we should put the sign?
1301     mantString.prepend(neg ? negativeSign() : positiveSign());
1302 
1303     // Convert to target digit set.
1304     if (digitSet() != KLocale::ArabicDigits) {
1305         mantString = convertDigits(mantString, digitSet());
1306         expString = convertDigits(expString, digitSet());
1307     }
1308 
1309     return mantString + expString;
1310 }
1311 
1312 // Returns a list of already translated units to use later in formatByteSize
1313 // and friends.  Account for every unit in KLocale::BinarySizeUnits
1314 QList<QString> KLocalePrivate::dialectUnitsList(KLocale::BinaryUnitDialect dialect)
1315 {
1316     QList<QString> binaryUnits;
1317 
1318     // Adds a given translation to the binaryUnits list.
1319 #define CACHE_BYTE_FMT(ctxt_text) \
1320     binaryUnits.append(i18nc(ctxt_text, QLatin1String("%1")));
1321 
1322     // Do not remove i18n: comments below, they are used by the
1323     // translators.
1324 
1325     // This prefix is shared by all current dialects.
1326     // i18n: Dumb message, avoid any markup or scripting.
1327     CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in bytes", "%1 B"));
1328 
1329     switch (dialect) {
1330     case KLocale::MetricBinaryDialect:
1331         // i18n: Dumb message, avoid any markup or scripting.
1332         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 1000 bytes", "%1 kB"));
1333         // i18n: Dumb message, avoid any markup or scripting.
1334         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^6 bytes", "%1 MB"));
1335         // i18n: Dumb message, avoid any markup or scripting.
1336         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^9 bytes", "%1 GB"));
1337         // i18n: Dumb message, avoid any markup or scripting.
1338         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^12 bytes", "%1 TB"));
1339         // i18n: Dumb message, avoid any markup or scripting.
1340         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^15 bytes", "%1 PB"));
1341         // i18n: Dumb message, avoid any markup or scripting.
1342         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^18 bytes", "%1 EB"));
1343         // i18n: Dumb message, avoid any markup or scripting.
1344         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^21 bytes", "%1 ZB"));
1345         // i18n: Dumb message, avoid any markup or scripting.
1346         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 10^24 bytes", "%1 YB"));
1347         break;
1348 
1349     case KLocale::JEDECBinaryDialect:
1350         // i18n: Dumb message, avoid any markup or scripting.
1351         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 1024 bytes", "%1 KB"));
1352         // i18n: Dumb message, avoid any markup or scripting.
1353         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^20 bytes", "%1 MB"));
1354         // i18n: Dumb message, avoid any markup or scripting.
1355         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^30 bytes", "%1 GB"));
1356         // i18n: Dumb message, avoid any markup or scripting.
1357         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^40 bytes", "%1 TB"));
1358         // i18n: Dumb message, avoid any markup or scripting.
1359         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^50 bytes", "%1 PB"));
1360         // i18n: Dumb message, avoid any markup or scripting.
1361         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^60 bytes", "%1 EB"));
1362         // i18n: Dumb message, avoid any markup or scripting.
1363         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^70 bytes", "%1 ZB"));
1364         // i18n: Dumb message, avoid any markup or scripting.
1365         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("memory size in 2^80 bytes", "%1 YB"));
1366         break;
1367 
1368     case KLocale::IECBinaryDialect:
1369     default:
1370         // i18n: Dumb message, avoid any markup or scripting.
1371         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 1024 bytes", "%1 KiB"));
1372         // i18n: Dumb message, avoid any markup or scripting.
1373         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^20 bytes", "%1 MiB"));
1374         // i18n: Dumb message, avoid any markup or scripting.
1375         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^30 bytes", "%1 GiB"));
1376         // i18n: Dumb message, avoid any markup or scripting.
1377         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^40 bytes", "%1 TiB"));
1378         // i18n: Dumb message, avoid any markup or scripting.
1379         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^50 bytes", "%1 PiB"));
1380         // i18n: Dumb message, avoid any markup or scripting.
1381         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^60 bytes", "%1 EiB"));
1382         // i18n: Dumb message, avoid any markup or scripting.
1383         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^70 bytes", "%1 ZiB"));
1384         // i18n: Dumb message, avoid any markup or scripting.
1385         CACHE_BYTE_FMT(I18N_NOOP2_NOSTRIP("size in 2^80 bytes", "%1 YiB"));
1386         break;
1387     }
1388 
1389     return binaryUnits;
1390 }
1391 
1392 QString KLocalePrivate::formatByteSize(double size, int precision, KLocale::BinaryUnitDialect dialect,
1393                                        KLocale::BinarySizeUnits specificUnit)
1394 {
1395     // Error checking
1396     if (dialect <= KLocale::DefaultBinaryDialect || dialect > KLocale::LastBinaryDialect) {
1397         dialect = m_binaryUnitDialect;
1398     }
1399 
1400     if (specificUnit < KLocale::DefaultBinaryUnits || specificUnit > KLocale::UnitLastUnit) {
1401         specificUnit = KLocale::DefaultBinaryUnits;
1402     }
1403 
1404     // Choose appropriate units.
1405     QList<QString> dialectUnits;
1406     if (dialect == m_binaryUnitDialect) {
1407         // Cache default units for speed
1408         if (m_byteSizeFmt.size() == 0) {
1409             QMutexLocker lock(s_kLocaleMutex());
1410 
1411             // We only cache the user's default dialect.
1412             m_byteSizeFmt = dialectUnitsList(m_binaryUnitDialect);
1413         }
1414 
1415         dialectUnits = m_byteSizeFmt;
1416     } else {
1417         dialectUnits = dialectUnitsList(dialect);
1418     }
1419 
1420     int unit = 0; // Selects what unit to use from cached list
1421     double multiplier = 1024.0;
1422 
1423     if (dialect == KLocale::MetricBinaryDialect) {
1424         multiplier = 1000.0;
1425     }
1426 
1427     // If a specific unit conversion is given, use it directly.  Otherwise
1428     // search until the result is in [0, multiplier) (or out of our range).
1429     if (specificUnit == KLocale::DefaultBinaryUnits) {
1430         while (qAbs(size) >= multiplier && unit < (int) KLocale::UnitYottaByte) {
1431             size /= multiplier;
1432             unit++;
1433         }
1434     } else {
1435         // A specific unit is in use
1436         unit = static_cast<int>(specificUnit);
1437         if (unit > 0) {
1438             size /= pow(multiplier, unit);
1439         }
1440     }
1441 
1442     if (unit == 0) {
1443         // Bytes, no rounding
1444         return dialectUnits[unit].arg(formatNumber(size, 0));
1445     }
1446 
1447     return dialectUnits[unit].arg(formatNumber(size, precision));
1448 }
1449 
1450 QString KLocalePrivate::formatByteSize(double size)
1451 {
1452     return formatByteSize(size, 1);
1453 }
1454 
1455 KLocale::BinaryUnitDialect KLocalePrivate::binaryUnitDialect() const
1456 {
1457     return m_binaryUnitDialect;
1458 }
1459 
1460 void KLocalePrivate::setBinaryUnitDialect(KLocale::BinaryUnitDialect newDialect)
1461 {
1462     if (newDialect > KLocale::DefaultBinaryDialect && newDialect <= KLocale::LastBinaryDialect) {
1463         QMutexLocker lock(s_kLocaleMutex());
1464         m_binaryUnitDialect = newDialect;
1465         m_byteSizeFmt.clear(); // Reset cached translations.
1466     }
1467 }
1468 
1469 QString KLocalePrivate::formatDuration(unsigned long mSec) const
1470 {
1471     if (mSec >= 24 * 3600000) {
1472         return i18nc("@item:intext %1 is a real number, e.g. 1.23 days", "%1 days",
1473                      formatNumber(mSec / (24 * 3600000.0), 2));
1474     } else if (mSec >= 3600000) {
1475         return i18nc("@item:intext %1 is a real number, e.g. 1.23 hours", "%1 hours",
1476                      formatNumber(mSec / 3600000.0, 2));
1477     } else if (mSec >= 60000) {
1478         return i18nc("@item:intext %1 is a real number, e.g. 1.23 minutes", "%1 minutes",
1479                      formatNumber(mSec / 60000.0, 2));
1480     } else if (mSec >= 1000) {
1481         return i18nc("@item:intext %1 is a real number, e.g. 1.23 seconds", "%1 seconds",
1482                      formatNumber(mSec / 1000.0, 2));
1483     }
1484     return i18ncp("@item:intext", "%1 millisecond", "%1 milliseconds", mSec);
1485 }
1486 
1487 QString KLocalePrivate::formatSingleDuration(KLocalePrivate::DurationType durationType, int n)
1488 {
1489     switch (durationType) {
1490     case KLocalePrivate::DaysDurationType:
1491         return i18ncp("@item:intext", "1 day", "%1 days", n);
1492     case KLocalePrivate::HoursDurationType:
1493         return i18ncp("@item:intext", "1 hour", "%1 hours", n);
1494     case KLocalePrivate::MinutesDurationType:
1495         return i18ncp("@item:intext", "1 minute", "%1 minutes", n);
1496     case KLocalePrivate::SecondsDurationType:
1497         return i18ncp("@item:intext", "1 second", "%1 seconds", n);
1498     }
1499     return QString();
1500 }
1501 
1502 QString KLocalePrivate::prettyFormatDuration(unsigned long mSec) const
1503 {
1504     unsigned long ms = mSec;
1505     int days = ms / (24 * 3600000);
1506     ms = ms % (24 * 3600000);
1507     int hours = ms / 3600000;
1508     ms = ms % 3600000;
1509     int minutes = ms / 60000;
1510     ms = ms % 60000;
1511     int seconds = qRound(ms / 1000.0);
1512 
1513     // Handle correctly problematic case #1 (look at KLocaleTest::prettyFormatDuration()
1514     // at klocaletest.cpp)
1515     if (seconds == 60) {
1516         return prettyFormatDuration(mSec - ms + 60000);
1517     }
1518 
1519     if (days && hours) {
1520         return i18nc("@item:intext days and hours. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem",
1521                      "%1 and %2", formatSingleDuration(KLocalePrivate::DaysDurationType, days),
1522                      formatSingleDuration(KLocalePrivate::HoursDurationType, hours));
1523     } else if (days) {
1524         return formatSingleDuration(KLocalePrivate::DaysDurationType, days);
1525     } else if (hours && minutes) {
1526         return i18nc("@item:intext hours and minutes. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem",
1527                      "%1 and %2",
1528                      formatSingleDuration(KLocalePrivate::HoursDurationType, hours),
1529                      formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes));
1530     } else if (hours) {
1531         return formatSingleDuration(KLocalePrivate::HoursDurationType, hours);
1532     } else if (minutes && seconds) {
1533         return i18nc("@item:intext minutes and seconds. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem",
1534                      "%1 and %2",
1535                      formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes),
1536                      formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds));
1537     } else if (minutes) {
1538         return formatSingleDuration(KLocalePrivate::MinutesDurationType, minutes);
1539     } else {
1540         return formatSingleDuration(KLocalePrivate::SecondsDurationType, seconds);
1541     }
1542 }
1543 
1544 QString KLocalePrivate::formatDate(const QDate &date, KLocale::DateFormat format)
1545 {
1546     return calendar()->formatDate(date, format);
1547 }
1548 
1549 double KLocalePrivate::readNumber(const QString &_str, bool *ok) const
1550 {
1551     QString str = _str.trimmed();
1552     bool neg = false;
1553 
1554     // Check negative or positive signs
1555     // Assumes blank sign is positive even if pos sign set, unless already taken by negative
1556     if (!negativeSign().isEmpty() && str.indexOf(negativeSign()) == 0) {
1557         neg = true;
1558         str.remove(0, negativeSign().length());
1559         str = str.trimmed();
1560     } else if (!positiveSign().isEmpty() && str.indexOf(positiveSign()) == 0) {
1561         neg = false;
1562         str.remove(0, positiveSign().length());
1563         str = str.trimmed();
1564     } else if (negativeSign().isEmpty() && str[0].isDigit()) {
1565         neg = true;
1566     }
1567 
1568     /* will hold the scientific notation portion of the number.
1569        Example, with 2.34E+23, exponentialPart == "E+23"
1570     */
1571     QString exponentialPart;
1572     int EPos;
1573 
1574     EPos = str.indexOf(QLatin1Char('E'), 0, Qt::CaseInsensitive);
1575 
1576     if (EPos != -1) {
1577         exponentialPart = str.mid(EPos);
1578         str = str.left(EPos);
1579         str = str.trimmed();
1580     }
1581 
1582     // Remove group separators
1583     bool groupOk = true;
1584     if (str.contains(thousandsSeparator())) {
1585         str = parseDigitGroup(str, thousandsSeparator(), decimalSymbol(),
1586                               numericDigitGrouping(), &groupOk);
1587     }
1588 
1589     if (!groupOk) {
1590         if (ok) {
1591             *ok = false;
1592         }
1593         return 0.0;
1594     }
1595 
1596     int pos = str.indexOf(decimalSymbol());
1597     QString major;
1598     QString minor;
1599     if (pos == -1) {
1600         major = str;
1601     } else {
1602         major = str.left(pos);
1603         minor = str.mid(pos + decimalSymbol().length());
1604     }
1605 
1606     // Check the major and minor parts are only digits
1607     bool digitTest = true;
1608     foreach (const QChar &ch, major) {
1609         if (!ch.isDigit()) {
1610             digitTest = false;
1611             break;
1612         }
1613     }
1614     foreach (const QChar &ch, minor) {
1615         if (!ch.isDigit()) {
1616             digitTest = false;
1617             break;
1618         }
1619     }
1620     if (!digitTest) {
1621         if (ok) {
1622             *ok = false;
1623         }
1624         return 0.0;
1625     }
1626 
1627     QString tot;
1628     if (neg) {
1629         tot = QLatin1Char('-');
1630     }
1631     tot += major + QLatin1Char('.') + minor + exponentialPart;
1632     tot = toArabicDigits(tot);
1633     return tot.toDouble(ok);
1634 }
1635 
1636 double KLocalePrivate::readMoney(const QString &_str, bool *ok) const
1637 {
1638     QString str = _str.trimmed();
1639     bool neg = false;
1640     bool currencyFound = false;
1641     QString symbol = currencySymbol();
1642 
1643     // First try removing currency symbol from either end
1644     int pos = str.indexOf(symbol);
1645     if (pos == 0 || pos == (int) str.length() - symbol.length()) {
1646         str.remove(pos, symbol.length());
1647         str = str.trimmed();
1648         currencyFound = true;
1649     }
1650     if (str.isEmpty()) {
1651         if (ok) {
1652             *ok = false;
1653         }
1654         return 0;
1655     }
1656 
1657     // Then try removing sign from either end (with a special case for parenthesis)
1658     if (str[0] == QLatin1Char('(') && str[str.length() - 1] == QLatin1Char(')')) {
1659         if (positiveMonetarySignPosition() != KLocale::ParensAround) {
1660             neg = true;
1661         }
1662         str.remove(str.length() - 1, 1);
1663         str.remove(0, 1);
1664         str = str.trimmed();
1665     } else {
1666         int len = 0;
1667         QString sign;
1668         int negLen = negativeSign().length();
1669         QString negSign = negativeSign();
1670         if (!negSign.isEmpty() && (str.left(negLen) == negSign || str.right(negSign.length()) == negSign)) {
1671             neg = true;
1672             len = negLen;
1673             sign = negSign;
1674         } else {
1675             int posLen = positiveSign().length();
1676             QString posSign = positiveSign();
1677             if (!posSign.isEmpty() && (str.left(posLen) == posSign || str.right(posSign.length()) == posSign)) {
1678                 len = posLen;
1679                 sign = posSign;
1680             } else if (negSign.isEmpty() && str[0].isDigit() && str[str.length() - 1].isDigit()) {
1681                 neg = true;
1682             }
1683         }
1684         if (!sign.isEmpty()) {
1685             if (str.left(len) == sign) {
1686                 str.remove(0, len);
1687             } else {
1688                 str.remove(str.length() - len, len);
1689             }
1690             str = str.trimmed();
1691         }
1692     }
1693 
1694     // Finally try again for the currency symbol, if we didn't find
1695     // it already (because of the negative sign being in the way).
1696     if (!currencyFound) {
1697         pos = str.indexOf(symbol);
1698         if (pos == 0 || pos == (int) str.length() - symbol.length()) {
1699             str.remove(pos, symbol.length());
1700             str = str.trimmed();
1701         }
1702     }
1703 
1704     // Remove group separators
1705     bool groupOk = true;
1706     if (str.contains(monetaryThousandsSeparator())) {
1707         str = parseDigitGroup(str, monetaryThousandsSeparator(), monetaryDecimalSymbol(),
1708                               monetaryDigitGrouping(), &groupOk);
1709     }
1710 
1711     if (!groupOk) {
1712         if (ok) {
1713             *ok = false;
1714         }
1715         return 0.0;
1716     }
1717 
1718     // And parse the rest as a number
1719     pos = str.indexOf(monetaryDecimalSymbol());
1720     QString major;
1721     QString minor;
1722     if (pos == -1) {
1723         major = str;
1724     } else {
1725         major = str.left(pos);
1726         minor = str.mid(pos + monetaryDecimalSymbol().length());
1727     }
1728 
1729     // Check the major and minor parts are only digits
1730     bool digitTest = true;
1731     foreach (const QChar &ch, major) {
1732         if (!ch.isDigit()) {
1733             digitTest = false;
1734             break;
1735         }
1736     }
1737     foreach (const QChar &ch, minor) {
1738         if (!ch.isDigit()) {
1739             digitTest = false;
1740             break;
1741         }
1742     }
1743     if (!digitTest) {
1744         if (ok) {
1745             *ok = false;
1746         }
1747         return 0.0;
1748     }
1749 
1750     QString tot;
1751     if (neg) {
1752         tot = QLatin1Char('-');
1753     }
1754     tot += major + QLatin1Char('.') + minor;
1755     tot = toArabicDigits(tot);
1756     return tot.toDouble(ok);
1757 }
1758 
1759 /**
1760  * helper function to read integers
1761  * @param str
1762  * @param pos the position to start at. It will be updated when we parse it.
1763  * @return the integer read in the string, or -1 if no string
1764  */
1765 static int readInt(const QString &str, int &pos)
1766 {
1767     if (!str.at(pos).isDigit()) {
1768         return -1;
1769     }
1770     int result = 0;
1771     for (; str.length() > pos && str.at(pos).isDigit(); ++pos) {
1772         result *= 10;
1773         result += str.at(pos).digitValue();
1774     }
1775 
1776     return result;
1777 }
1778 
1779 QDate KLocalePrivate::readDate(const QString &intstr, bool *ok)
1780 {
1781     return calendar()->readDate(intstr, ok);
1782 }
1783 
1784 QDate KLocalePrivate::readDate(const QString &intstr, KLocale::ReadDateFlags flags, bool *ok)
1785 {
1786     return calendar()->readDate(intstr, flags, ok);
1787 }
1788 
1789 QDate KLocalePrivate::readDate(const QString &intstr, const QString &fmt, bool *ok)
1790 {
1791     return calendar()->readDate(intstr, fmt, ok);
1792 }
1793 
1794 QTime KLocalePrivate::readTime(const QString &intstr, bool *ok) const
1795 {
1796     QTime time = readLocaleTime(intstr, ok, KLocale::TimeDefault, KLocale::ProcessStrict);
1797     if (time.isValid()) {
1798         return time;
1799     }
1800     return readLocaleTime(intstr, ok, KLocale::TimeWithoutSeconds, KLocale::ProcessStrict);
1801 }
1802 
1803 QTime KLocalePrivate::readTime(const QString &intstr, KLocale::ReadTimeFlags flags, bool *ok) const
1804 {
1805     return readLocaleTime(intstr, ok, (flags == KLocale::WithSeconds) ? KLocale::TimeDefault : KLocale::TimeWithoutSeconds,
1806                           KLocale::ProcessStrict);
1807 }
1808 
1809 // remove the first occurrence of the 2-character string
1810 // strip2char from inout and if found, also remove one preceding
1811 // punctuation character and arbitrary number of spaces.
1812 static void stripStringAndPreceedingSeparator(QString &inout, const QLatin1String &strip2char)
1813 {
1814     int remPos = inout.indexOf(strip2char);
1815     if (remPos == -1) {
1816         return;
1817     }
1818     int endPos = remPos + 2;
1819     int curPos = remPos - 1;
1820     while (curPos >= 0 && inout.at(curPos).isSpace()) {
1821         curPos--;
1822     }
1823     // remove the separator sign before the seconds
1824     // and assume that works everywhere
1825     if (curPos >= 0 && inout.at(curPos).isPunct() && inout.at(curPos) != QLatin1Char('%')) {
1826         curPos--;
1827     }
1828     while (curPos >= 0 && inout.at(curPos).isSpace()) {
1829         curPos--;
1830     }
1831 
1832     remPos = qMax(curPos + 1, 0);
1833     inout.remove(remPos, endPos - remPos);
1834 }
1835 
1836 // remove the first occurrence of the 2-character string
1837 // strip2char from inout and if found, also remove one
1838 // succeeding punctuation character and arbitrary number of spaces.
1839 static void stripStringAndSucceedingSeparator(QString &inout, const QLatin1String &strip2char)
1840 {
1841     int remPos = inout.indexOf(strip2char);
1842     if (remPos == -1) {
1843         return;
1844     }
1845     int curPos = remPos + 2;
1846     while (curPos < inout.size() &&
1847             (inout.at(curPos).isSpace() ||
1848              (inout.at(curPos).isPunct() && inout.at(curPos) != QLatin1Char('%')))) {
1849         curPos++;
1850     }
1851     inout.remove(remPos, curPos - remPos);
1852 }
1853 
1854 // remove the first occurrence of "%p" from the inout.
1855 static void stripAmPmFormat(QString &inout)
1856 {
1857     // NOTE: this function assumes that %p - if it's present -
1858     //       is either the first or the last element of the format
1859     //       string. Either a succeeding or a preceding
1860     //       punctuation symbol is stripped.
1861     int length = inout.size();
1862     int ppos = inout.indexOf(QLatin1String("%p"));
1863     if (ppos == -1) {
1864         return;
1865     } else if (ppos == 0) {
1866         // first element, eat succeeding punctuation and spaces
1867         ppos = 2;
1868         while (ppos < length && (inout.at(ppos).isSpace() || inout.at(ppos).isPunct()) &&
1869                 inout.at(ppos) != QLatin1Char('%')) {
1870             ppos++;
1871         }
1872         inout = inout.mid(ppos);
1873     } else {
1874         stripStringAndPreceedingSeparator(inout, QLatin1String("%p"));
1875     }
1876 }
1877 
1878 QTime KLocalePrivate::readLocaleTime(const QString &intstr, bool *ok, KLocale::TimeFormatOptions options,
1879                                      KLocale::TimeProcessingOptions processing) const
1880 {
1881     QString str(intstr.simplified().toLower());
1882     QString format(timeFormat().simplified());
1883 
1884     int hour = -1;
1885     int minute = -1;
1886     int second = -1;
1887     bool useDayPeriod = false;
1888     KDayPeriod dayPeriod = dayPeriodForTime(QTime(0, 0, 0));
1889     int strpos = 0;
1890     int formatpos = 0;
1891     bool error = false;
1892 
1893     bool excludeSecs = ((options & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds);
1894     bool isDuration = ((options & KLocale::TimeDuration) == KLocale::TimeDuration);
1895     bool noAmPm = ((options & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm);
1896     bool foldHours = ((options & KLocale::TimeFoldHours) == KLocale::TimeFoldHours);
1897     bool strict = ((processing & KLocale::ProcessStrict) == KLocale::ProcessStrict);
1898 
1899     // if seconds aren't needed, strip them from the timeFormat
1900     if (excludeSecs) {
1901         stripStringAndPreceedingSeparator(format, QLatin1String("%S"));
1902         second = 0; // seconds are always 0
1903     }
1904 
1905     // if hours are folded, strip them from the timeFormat
1906     if (foldHours) {
1907         stripStringAndSucceedingSeparator(format, QLatin1String("%H"));
1908         stripStringAndSucceedingSeparator(format, QLatin1String("%k"));
1909         stripStringAndSucceedingSeparator(format, QLatin1String("%I"));
1910         stripStringAndSucceedingSeparator(format, QLatin1String("%l"));
1911     }
1912 
1913     // if am/pm isn't needed, strip it from the timeFormat
1914     if (noAmPm) {
1915         stripAmPmFormat(format);
1916     }
1917 
1918     while (!error && (format.length() > formatpos || str.length() > strpos)) {
1919         if (!(format.length() > formatpos && str.length() > strpos)) {
1920             error = true;
1921             break;
1922         }
1923 
1924         QChar c = format.at(formatpos++);
1925         if (c.isSpace()) {
1926             if (strict) { // strict processing: space is needed
1927                 if (!str.at(strpos).isSpace()) {
1928                     error = true;
1929                     break;
1930                 }
1931                 strpos++;
1932             } else { // lax processing: space in str not needed
1933                 // 1 space maximum as str is simplified
1934                 if (str.at(strpos).isSpace()) {
1935                     strpos++;
1936                 }
1937             }
1938             continue;
1939         }
1940 
1941         if (c != QLatin1Char('%')) {
1942             if (c != str.at(strpos++)) {
1943                 error = true;
1944                 break;
1945             }
1946             continue;
1947         }
1948 
1949         c = format.at(formatpos++);
1950         switch (c.unicode()) {
1951 
1952         case 'p': // Day Period, normally AM/PM
1953         case 'P': { // Lowercase Day Period, normally am/pm
1954             error = true;
1955             foreach (const KDayPeriod &testDayPeriod, dayPeriods()) {
1956                 QString dayPeriodText = testDayPeriod.periodName(KLocale::ShortName);
1957                 int len = dayPeriodText.length();
1958                 if (str.mid(strpos, len) == dayPeriodText.toLower()) {
1959                     dayPeriod = testDayPeriod;
1960                     strpos += len;
1961                     error = false;
1962                     useDayPeriod = true;
1963                     break;
1964                 }
1965             }
1966             break;
1967         }
1968 
1969         case 'k':  // 24h Hours Short Number
1970         case 'H':  // 24h Hours Long Number
1971             useDayPeriod = false;
1972             hour = readInt(str, strpos);
1973             break;
1974 
1975         case 'l': // 12h Hours Short Number
1976         case 'I': // 12h Hours Long Number
1977             useDayPeriod = !isDuration;
1978             hour = readInt(str, strpos);
1979             break;
1980 
1981         case 'M':
1982             minute = readInt(str, strpos);
1983             // minutes can be bigger than 59 if hours are folded
1984             if (foldHours) {
1985                 // if hours are folded, make sure minutes doesn't get bigger than 59.
1986                 hour = minute / 60;
1987                 minute = minute % 60;
1988             }
1989             break;
1990 
1991         case 'S':
1992             second = readInt(str, strpos);
1993             break;
1994         }
1995 
1996         // NOTE: if anything is performed inside this loop, be sure to
1997         //       check for error!
1998     }
1999 
2000     QTime returnTime;
2001     if (!error) {
2002         if (useDayPeriod) {
2003             returnTime = dayPeriod.time(hour, minute, second);
2004         } else {
2005             returnTime = QTime(hour, minute, second);
2006         }
2007     }
2008     if (ok) {
2009         *ok = returnTime.isValid();
2010     }
2011     return returnTime;
2012 }
2013 
2014 QString KLocalePrivate::formatTime(const QTime &time, bool includeSecs, bool isDuration) const
2015 {
2016     KLocale::TimeFormatOptions options = KLocale::TimeDefault;
2017     if (!includeSecs) {
2018         options |= KLocale::TimeWithoutSeconds;
2019     }
2020     if (isDuration) {
2021         options |= KLocale::TimeDuration;
2022     }
2023     return formatLocaleTime(time, options);
2024 }
2025 
2026 QString KLocalePrivate::formatLocaleTime(const QTime &time, KLocale::TimeFormatOptions options) const
2027 {
2028     QString rst(timeFormat());
2029 
2030     bool excludeSecs = ((options & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds);
2031     bool isDuration = ((options & KLocale::TimeDuration) == KLocale::TimeDuration);
2032     bool noAmPm = ((options & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm);
2033     bool foldHours = ((options & KLocale::TimeFoldHours) == KLocale::TimeFoldHours);
2034 
2035     // if seconds aren't needed, strip them from the timeFormat
2036     if (excludeSecs) {
2037         stripStringAndPreceedingSeparator(rst, QLatin1String("%S"));
2038     }
2039 
2040     // if hours should be folded, strip all hour symbols from the timeFormat
2041     if (foldHours) {
2042         stripStringAndSucceedingSeparator(rst, QLatin1String("%H"));
2043         stripStringAndSucceedingSeparator(rst, QLatin1String("%k"));
2044         stripStringAndSucceedingSeparator(rst, QLatin1String("%I"));
2045         stripStringAndSucceedingSeparator(rst, QLatin1String("%l"));
2046     }
2047 
2048     // if am/pm isn't needed, strip it from the timeFormat
2049     if (noAmPm) {
2050         stripAmPmFormat(rst);
2051     }
2052 
2053     // only "pm/am" and %M here can grow, the rest shrinks, but
2054     // I'm rather safe than sorry
2055     QChar *buffer = new QChar[rst.length() * 3 / 2 + 32];
2056 
2057     int index = 0;
2058     bool escape = false;
2059     int number = 0;
2060 
2061     for (int format_index = 0; format_index < rst.length(); format_index++) {
2062         if (!escape) {
2063             if (rst.at(format_index).unicode() == '%') {
2064                 escape = true;
2065             } else {
2066                 buffer[index++] = rst.at(format_index);
2067             }
2068         } else {
2069             switch (rst.at(format_index).unicode()) {
2070             case '%':
2071                 buffer[index++] = QLatin1Char('%');
2072                 break;
2073             case 'H':
2074                 put_it_in(buffer, index, time.hour());
2075                 break;
2076             case 'I':
2077                 if (isDuration) {
2078                     put_it_in(buffer, index, time.hour());
2079                 } else {
2080                     put_it_in(buffer, index, dayPeriodForTime(time).hourInPeriod(time));
2081                 }
2082                 break;
2083             case 'M':
2084                 if (foldHours) {
2085                     put_it_in(buffer, index, QString::number(time.hour() * 60 + time.minute()));
2086                 } else {
2087                     put_it_in(buffer, index, time.minute());
2088                 }
2089                 break;
2090             case 'S':
2091                 put_it_in(buffer, index, time.second());
2092                 break;
2093             case 'k':
2094             case 'l':
2095                 // to share the code
2096                 if (!isDuration && rst.at(format_index).unicode() == 'l') {
2097                     number = dayPeriodForTime(time).hourInPeriod(time);
2098                 } else {
2099                     number = time.hour();
2100                 }
2101                 if (number / 10) {
2102                     buffer[index++] = number / 10 + '0';
2103                 }
2104                 buffer[index++] = number % 10 + '0';
2105                 break;
2106             case 'p': {
2107                 put_it_in(buffer, index, dayPeriodForTime(time).periodName(KLocale::ShortName));
2108                 break;
2109             }
2110             default:
2111                 buffer[index++] = rst.at(format_index);
2112                 break;
2113             }
2114             escape = false;
2115         }
2116     }
2117     QString ret(buffer, index);
2118     delete [] buffer;
2119     ret = convertDigits(ret, dateTimeDigitSet());
2120     return ret.trimmed();
2121 }
2122 
2123 bool KLocalePrivate::use12Clock() const
2124 {
2125     if ((timeFormat().contains(QString::fromLatin1("%I")) > 0) ||
2126             (timeFormat().contains(QString::fromLatin1("%l")) > 0)) {
2127         return true;
2128     } else {
2129         return false;
2130     }
2131 }
2132 
2133 void KLocalePrivate::setDayPeriods(const QList<KDayPeriod> &dayPeriods)
2134 {
2135     if (dayPeriods.count() > 0) {
2136         foreach (const KDayPeriod &dayPeriod, dayPeriods) {
2137             if (!dayPeriod.isValid()) {
2138                 return;
2139             }
2140         }
2141         m_dayPeriods = dayPeriods;
2142     }
2143 }
2144 
2145 QList<KDayPeriod> KLocalePrivate::dayPeriods() const
2146 {
2147     // If no Day Periods currently loaded then it means there were no country specific ones defined
2148     // in the country l10n file, so default to standard AM/PM translations for the users language.
2149     // Note we couldn't do this in initDayPeriods() as i18n isn't available until we have a
2150     // valid loacle constructed.
2151     if (m_dayPeriods.isEmpty()) {
2152         m_dayPeriods.append(KDayPeriod(QString::fromLatin1("am"),
2153                                        i18nc("Before Noon KLocale::LongName", "Ante Meridiem"),
2154                                        i18nc("Before Noon KLocale::ShortName", "AM"),
2155                                        i18nc("Before Noon KLocale::NarrowName", "A"),
2156                                        QTime(0, 0, 0), QTime(11, 59, 59, 999), 0, 12));
2157         m_dayPeriods.append(KDayPeriod(QString::fromLatin1("pm"),
2158                                        i18nc("After Noon KLocale::LongName", "Post Meridiem"),
2159                                        i18nc("After Noon KLocale::ShortName", "PM"),
2160                                        i18nc("After Noon KLocale::NarrowName", "P"),
2161                                        QTime(12, 0, 0), QTime(23, 59, 59, 999), 0, 12));
2162     }
2163     return m_dayPeriods;
2164 }
2165 
2166 KDayPeriod KLocalePrivate::dayPeriodForTime(const QTime &time) const
2167 {
2168     if (time.isValid()) {
2169         foreach (const KDayPeriod &dayPeriod, dayPeriods()) {
2170             if (dayPeriod.isValid(time)) {
2171                 return dayPeriod;
2172             }
2173         }
2174     }
2175     return KDayPeriod();
2176 }
2177 
2178 QStringList KLocalePrivate::languageList() const
2179 {
2180     return m_languageList;
2181 }
2182 
2183 QStringList KLocalePrivate::currencyCodeList() const
2184 {
2185     return m_currencyCodeList;
2186 }
2187 
2188 QString KLocalePrivate::formatDateTime(const KLocale *locale, const QDateTime &dateTime, KLocale::DateFormat format,
2189                                        bool includeSeconds, int daysTo, int secsTo)
2190 {
2191     // Have to do Fancy Date formatting here rather than using normal KCalendarSystem::formatDate()
2192     // as daysTo is relative to the time spec which formatDate doesn't know about.  Needs to be
2193     // kept in sync with Fancy Date code in KCalendarSystem::formatDate().  Fix in KDE5.
2194 
2195     // Only do Fancy if less than an hour into the future or less than a week in the past
2196     if ((daysTo == 0 && secsTo > 3600) ||  daysTo < 0 || daysTo > 6) {
2197         if (format == KLocale::FancyShortDate) {
2198             format = KLocale::ShortDate;
2199         } else if (format == KLocale::FancyLongDate) {
2200             format = KLocale::LongDate;
2201         }
2202     }
2203 
2204     QString dateStr;
2205     if (format == KLocale::FancyShortDate || format == KLocale::FancyLongDate) {
2206         switch (daysTo) {
2207         case 0:
2208             dateStr = i18n("Today");
2209             break;
2210         case 1:
2211             dateStr = i18n("Yesterday");
2212             break;
2213         default:
2214             dateStr = locale->calendar()->weekDayName(dateTime.date());
2215         }
2216     } else {
2217         dateStr = locale->formatDate(dateTime.date(), format);
2218     }
2219 
2220     KLocale::TimeFormatOption timeFormat;
2221     if (includeSeconds) {
2222         timeFormat = KLocale::TimeDefault;
2223     } else {
2224         timeFormat = KLocale::TimeWithoutSeconds;
2225     }
2226 
2227     return i18nc("concatenation of dates and time", "%1 %2", dateStr,
2228                  locale->formatLocaleTime(dateTime.time(), timeFormat));
2229 }
2230 
2231 QString KLocalePrivate::formatDateTime(const QDateTime &dateTime, KLocale::DateFormat format, bool includeSeconds) const
2232 {
2233     QDateTime now = QDateTime::currentDateTime();
2234     int daysTo = dateTime.date().daysTo(now.date());
2235     int secsTo = now.secsTo(dateTime);
2236     return KLocalePrivate::formatDateTime(q, dateTime, format, includeSeconds, daysTo, secsTo);
2237 }
2238 
2239 QString KLocalePrivate::formatDateTime(const KDateTime &dateTime, KLocale::DateFormat format,
2240                                        KLocale::DateTimeFormatOptions options)
2241 {
2242     QString dt;
2243 
2244     if (dateTime.isDateOnly()) {
2245         dt = formatDate(dateTime.date(), format);
2246     } else {
2247         KDateTime now = KDateTime::currentDateTime(dateTime.timeSpec());
2248         int daysTo = dateTime.date().daysTo(now.date());
2249         int secsTo = now.secsTo(dateTime);
2250         dt = KLocalePrivate::formatDateTime(q, dateTime.dateTime(), format, (options & KLocale::Seconds), daysTo, secsTo);
2251     }
2252 
2253     if (options & KLocale::TimeZone) {
2254         QString tz;
2255         switch (dateTime.timeType()) {
2256         case KDateTime::OffsetFromUTC:
2257             tz = i18n(dateTime.toString(QString::fromLatin1("%z")).toUtf8());
2258             break;
2259         case KDateTime::UTC:
2260         case KDateTime::TimeZone:
2261             tz = i18n(dateTime.toString(QString::fromLatin1((format == KLocale::ShortDate) ? "%Z" : "%:Z")).toUtf8());
2262             break;
2263         case KDateTime::ClockTime:
2264         default:
2265             break;
2266         }
2267         return i18nc("concatenation of date/time and time zone", "%1 %2", dt, tz);
2268     }
2269 
2270     return dt;
2271 }
2272 
2273 bool KLocalePrivate::useDefaultLanguage() const
2274 {
2275     return language() == KLocale::defaultLanguage();
2276 }
2277 
2278 void KLocalePrivate::initEncoding()
2279 {
2280     m_codecForEncoding = nullptr;
2281 
2282     // This all made more sense when we still had the EncodingEnum config key.
2283 
2284     QByteArray codeset = systemCodeset();
2285 
2286     if (!codeset.isEmpty()) {
2287         QTextCodec *codec = QTextCodec::codecForName(codeset);
2288         if (codec) {
2289             setEncoding(codec->mibEnum());
2290         }
2291     } else {
2292         setEncoding(QTextCodec::codecForLocale()->mibEnum());
2293     }
2294 
2295     if (!m_codecForEncoding) {
2296         qWarning() << "Cannot resolve system encoding, defaulting to ISO 8859-1.";
2297         const int mibDefault = 4; // ISO 8859-1
2298         setEncoding(mibDefault);
2299     }
2300 
2301     Q_ASSERT(m_codecForEncoding);
2302 }
2303 
2304 QByteArray KLocalePrivate::systemCodeset() const
2305 {
2306     QByteArray codeset;
2307 #if HAVE_LANGINFO_H
2308     // Qt since 4.2 always returns 'System' as codecForLocale and KDE (for example
2309     // KEncodingFileDialog) expects real encoding name. So on systems that have langinfo.h use
2310     // nl_langinfo instead, just like Qt compiled without iconv does. Windows already has its own
2311     // workaround
2312 
2313     codeset = nl_langinfo(CODESET);
2314 
2315     if ((codeset == "ANSI_X3.4-1968") || (codeset == "US-ASCII")) {
2316         // means ascii, "C"; QTextCodec doesn't know, so avoid warning
2317         codeset = "ISO-8859-1";
2318     }
2319 #endif
2320     return codeset;
2321 }
2322 
2323 static inline bool isUnicodeNonCharacter(uint ucs4)
2324 {
2325     return (ucs4 & 0xfffe) == 0xfffe || (ucs4 - 0xfdd0U) < 16;
2326 }
2327 
2328 QByteArray KLocalePrivate::encodeFileNameUTF8(const QString &fileName)
2329 {
2330     if (fileName.isNull()) {
2331         return QByteArray();
2332     }
2333     int len = fileName.length();
2334     const QChar *uc = fileName.constData();
2335 
2336     uchar replacement = '?';
2337     int rlen = 3 * len;
2338     int surrogate_high = -1;
2339 
2340     QByteArray rstr;
2341     rstr.resize(rlen);
2342     uchar *cursor = (uchar *)rstr.data();
2343     const QChar *ch = uc;
2344     int invalid = 0;
2345 
2346     const QChar *end = ch + len;
2347     while (ch < end) {
2348         uint u = ch->unicode();
2349         if (surrogate_high >= 0) {
2350             if (ch->isLowSurrogate()) {
2351                 u = QChar::surrogateToUcs4(surrogate_high, u);
2352                 surrogate_high = -1;
2353             } else {
2354                 // high surrogate without low
2355                 *cursor = replacement;
2356                 ++ch;
2357                 ++invalid;
2358                 surrogate_high = -1;
2359                 continue;
2360             }
2361         } else if (ch->isLowSurrogate()) {
2362             // low surrogate without high
2363             *cursor = replacement;
2364             ++ch;
2365             ++invalid;
2366             continue;
2367         } else if (ch->isHighSurrogate()) {
2368             surrogate_high = u;
2369             ++ch;
2370             continue;
2371         }
2372 
2373         if (u >= 0x10FE00 && u <= 0x10FE7F) {
2374             *cursor++ = (uchar)(u - 0x10FE00 + 128);
2375         } else if (u < 0x80) {
2376             *cursor++ = (uchar)u;
2377         } else {
2378             if (u < 0x0800) {
2379                 *cursor++ = 0xc0 | ((uchar)(u >> 6));
2380             } else {
2381                 // is it one of the Unicode non-characters?
2382                 if (isUnicodeNonCharacter(u)) {
2383                     *cursor++ = replacement;
2384                     ++ch;
2385                     ++invalid;
2386                     continue;
2387                 }
2388 
2389                 if (u > 0xffff) {
2390                     *cursor++ = 0xf0 | ((uchar)(u >> 18));
2391                     *cursor++ = 0x80 | (((uchar)(u >> 12)) & 0x3f);
2392                 } else {
2393                     *cursor++ = 0xe0 | (((uchar)(u >> 12)) & 0x3f);
2394                 }
2395                 *cursor++ = 0x80 | (((uchar)(u >> 6)) & 0x3f);
2396             }
2397             *cursor++ = 0x80 | ((uchar)(u & 0x3f));
2398         }
2399         ++ch;
2400     }
2401 
2402     rstr.resize(cursor - (const uchar *)rstr.constData());
2403     return rstr;
2404 }
2405 
2406 QString KLocalePrivate::decodeFileNameUTF8(const QByteArray &localFileName)
2407 {
2408     const char *chars = localFileName;
2409     int len = qstrlen(chars);
2410     int need = 0;
2411     uint uc = 0;
2412     uint min_uc = 0;
2413 
2414     QString result(2 * (len + 1), Qt::Uninitialized); // worst case
2415     ushort *qch = (ushort *)result.unicode();
2416     uchar ch;
2417 
2418     for (int i = 0; i < len; ++i) {
2419         ch = chars[i];
2420         if (need) {
2421             if ((ch & 0xc0) == 0x80) {
2422                 uc = (uc << 6) | (ch & 0x3f);
2423                 --need;
2424                 if (!need) {
2425                     bool nonCharacter;
2426                     if (!(nonCharacter = isUnicodeNonCharacter(uc)) && uc > 0xffff && uc < 0x110000) {
2427                         // surrogate pair
2428                         Q_ASSERT((qch - (ushort *)result.unicode()) + 2 < result.length());
2429                         *qch++ = QChar::highSurrogate(uc);
2430                         *qch++ = QChar::lowSurrogate(uc);
2431                     } else if ((uc < min_uc) || (uc >= 0xd800 && uc <= 0xdfff) || nonCharacter || uc >= 0x110000) {
2432                         // error: overlong sequence, UTF16 surrogate or non-character
2433                         goto error;
2434                     } else {
2435                         *qch++ = uc;
2436                     }
2437                 }
2438             } else {
2439                 goto error;
2440             }
2441         } else {
2442             if (ch < 128) {
2443                 *qch++ = ushort(ch);
2444             } else if ((ch & 0xe0) == 0xc0) {
2445                 uc = ch & 0x1f;
2446                 need = 1;
2447                 min_uc = 0x80;
2448             } else if ((ch & 0xf0) == 0xe0) {
2449                 uc = ch & 0x0f;
2450                 need = 2;
2451                 min_uc = 0x800;
2452             } else if ((ch & 0xf8) == 0xf0) {
2453                 uc = ch & 0x07;
2454                 need = 3;
2455                 min_uc = 0x10000;
2456             } else {
2457                 goto error;
2458             }
2459         }
2460     }
2461     if (need > 0) {
2462         // unterminated UTF sequence
2463         goto error;
2464     }
2465     result.truncate(qch - (ushort *)result.unicode());
2466     return result;
2467 
2468 error:
2469 
2470     qch = (ushort *)result.unicode();
2471     for (int i = 0; i < len; ++i) {
2472         ch = chars[i];
2473         if (ch < 128) {
2474             *qch++ = ushort(ch);
2475         } else {
2476             uint uc = ch - 128 + 0x10FE00; //U+10FE00-U+10FE7F
2477             *qch++ = QChar::highSurrogate(uc);
2478             *qch++ = QChar::lowSurrogate(uc);
2479         }
2480     }
2481     result.truncate(qch - (ushort *)result.unicode());
2482     return result;
2483 }
2484 
2485 void KLocalePrivate::setDateFormat(const QString &format)
2486 {
2487     m_dateFormat = format.trimmed();
2488 }
2489 
2490 void KLocalePrivate::setDateFormatShort(const QString &format)
2491 {
2492     m_dateFormatShort = format.trimmed();
2493 }
2494 
2495 void KLocalePrivate::setDateMonthNamePossessive(bool possessive)
2496 {
2497     m_dateMonthNamePossessive = possessive;
2498 }
2499 
2500 void KLocalePrivate::setTimeFormat(const QString &format)
2501 {
2502     m_timeFormat = format.trimmed();
2503 }
2504 
2505 void KLocalePrivate::setWeekStartDay(int day)
2506 {
2507     if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
2508         m_weekStartDay = day;
2509     }
2510 }
2511 
2512 void KLocalePrivate::setWorkingWeekStartDay(int day)
2513 {
2514     if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
2515         m_workingWeekStartDay = day;
2516     }
2517 }
2518 
2519 void KLocalePrivate::setWorkingWeekEndDay(int day)
2520 {
2521     if (day >= 1 && day <= calendar()->daysInWeek(QDate())) {
2522         m_workingWeekEndDay = day;
2523     }
2524 }
2525 
2526 void KLocalePrivate::setWeekDayOfPray(int day)
2527 {
2528     if (day >= 0 && day <= calendar()->daysInWeek(QDate())) { // 0 = None
2529         m_weekDayOfPray = day;
2530     }
2531 }
2532 
2533 QString KLocalePrivate::dateFormat() const
2534 {
2535     return m_dateFormat;
2536 }
2537 
2538 QString KLocalePrivate::dateFormatShort() const
2539 {
2540     return m_dateFormatShort;
2541 }
2542 
2543 QString KLocalePrivate::timeFormat() const
2544 {
2545     return m_timeFormat;
2546 }
2547 
2548 void KLocalePrivate::setDecimalPlaces(int digits)
2549 {
2550     m_decimalPlaces = digits;
2551 }
2552 
2553 void KLocalePrivate::setDecimalSymbol(const QString &symbol)
2554 {
2555     m_decimalSymbol = symbol.trimmed();
2556 }
2557 
2558 void KLocalePrivate::setThousandsSeparator(const QString &separator)
2559 {
2560     // allow spaces here
2561     m_thousandsSeparator = separator;
2562 }
2563 
2564 void KLocalePrivate::setNumericDigitGrouping(QList<int> groupList)
2565 {
2566     m_numericDigitGrouping = groupList;
2567 }
2568 
2569 void KLocalePrivate::setPositiveSign(const QString &sign)
2570 {
2571     m_positiveSign = sign.trimmed();
2572 }
2573 
2574 void KLocalePrivate::setNegativeSign(const QString &sign)
2575 {
2576     m_negativeSign = sign.trimmed();
2577 }
2578 
2579 void KLocalePrivate::setPositiveMonetarySignPosition(KLocale::SignPosition signpos)
2580 {
2581     m_positiveMonetarySignPosition = signpos;
2582 }
2583 
2584 void KLocalePrivate::setNegativeMonetarySignPosition(KLocale::SignPosition signpos)
2585 {
2586     m_negativeMonetarySignPosition = signpos;
2587 }
2588 
2589 void KLocalePrivate::setPositivePrefixCurrencySymbol(bool prefix)
2590 {
2591     m_positivePrefixCurrencySymbol = prefix;
2592 }
2593 
2594 void KLocalePrivate::setNegativePrefixCurrencySymbol(bool prefix)
2595 {
2596     m_negativePrefixCurrencySymbol = prefix;
2597 }
2598 
2599 void KLocalePrivate::setMonetaryDecimalPlaces(int digits)
2600 {
2601     m_monetaryDecimalPlaces = digits;
2602 }
2603 
2604 void KLocalePrivate::setMonetaryThousandsSeparator(const QString &separator)
2605 {
2606     // allow spaces here
2607     m_monetaryThousandsSeparator = separator;
2608 }
2609 
2610 void KLocalePrivate::setMonetaryDigitGrouping(QList<int> groupList)
2611 {
2612     m_monetaryDigitGrouping = groupList;
2613 }
2614 
2615 void KLocalePrivate::setMonetaryDecimalSymbol(const QString &symbol)
2616 {
2617     m_monetaryDecimalSymbol = symbol.trimmed();
2618 }
2619 
2620 void KLocalePrivate::setCurrencySymbol(const QString &symbol)
2621 {
2622     m_currencySymbol = symbol.trimmed();
2623 }
2624 
2625 int KLocalePrivate::pageSize() const
2626 {
2627     return m_pageSize;
2628 }
2629 
2630 void KLocalePrivate::setPageSize(int size)
2631 {
2632     // #### check if it's in range??
2633     m_pageSize = size;
2634 }
2635 
2636 KLocale::MeasureSystem KLocalePrivate::measureSystem() const
2637 {
2638     return m_measureSystem;
2639 }
2640 
2641 void KLocalePrivate::setMeasureSystem(KLocale::MeasureSystem value)
2642 {
2643     m_measureSystem = value;
2644 }
2645 
2646 QString KLocalePrivate::defaultLanguage()
2647 {
2648     static const QString en_US = QString::fromLatin1("en_US");
2649     return en_US;
2650 }
2651 
2652 QString KLocalePrivate::defaultCountry()
2653 {
2654     return QString::fromLatin1("C");
2655 }
2656 
2657 QString KLocalePrivate::defaultCurrencyCode()
2658 {
2659     return QString::fromLatin1("USD");
2660 }
2661 
2662 const QByteArray KLocalePrivate::encoding()
2663 {
2664     return codecForEncoding()->name();
2665 }
2666 
2667 int KLocalePrivate::encodingMib() const
2668 {
2669     return codecForEncoding()->mibEnum();
2670 }
2671 
2672 int KLocalePrivate::fileEncodingMib() const
2673 {
2674     if (m_utf8FileEncoding) {
2675         return 106;
2676     }
2677     return codecForEncoding()->mibEnum();
2678 }
2679 
2680 QTextCodec *KLocalePrivate::codecForEncoding() const
2681 {
2682     return m_codecForEncoding;
2683 }
2684 
2685 bool KLocalePrivate::setEncoding(int mibEnum)
2686 {
2687     QTextCodec *codec = QTextCodec::codecForMib(mibEnum);
2688     if (codec) {
2689         m_codecForEncoding = codec;
2690     }
2691 
2692     return codec != nullptr;
2693 }
2694 
2695 QStringList KLocalePrivate::allLanguagesList()
2696 {
2697     if (!m_languages) {
2698         m_languages = new KConfig(QLatin1String("locale/kf5_all_languages"), KConfig::NoGlobals, QStandardPaths::GenericDataLocation);
2699     }
2700     return m_languages->groupList();
2701 }
2702 
2703 QStringList KLocalePrivate::installedLanguages()
2704 {
2705     QStringList languages;
2706     const QStringList localeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("locale"), QStandardPaths::LocateDirectory);
2707     Q_FOREACH (const QString &localeDir, localeDirs) {
2708         const QStringList entries = QDir(localeDir).entryList(QDir::Dirs);
2709         Q_FOREACH (const QString &d, entries) {
2710             if (QFile::exists(localeDir + QLatin1Char('/') + d + QLatin1String("/kf5_entry.desktop"))) {
2711                 languages.append(d);
2712             }
2713         }
2714     }
2715 
2716     languages.sort();
2717     return languages;
2718 }
2719 
2720 QString KLocalePrivate::languageCodeToName(const QString &language)
2721 {
2722     if (!m_languages) {
2723         m_languages = new KConfig(QLatin1String("locale/kf5_all_languages"), KConfig::NoGlobals, QStandardPaths::GenericDataLocation);
2724     }
2725 
2726     KConfigGroup cg(m_languages, language);
2727     return cg.readEntry("Name");
2728 }
2729 
2730 QStringList KLocalePrivate::allCountriesList() const
2731 {
2732     QStringList countries;
2733     const QStringList localeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/"), QStandardPaths::LocateDirectory);
2734     Q_FOREACH (const QString &localeDir, localeDirs) {
2735         const QStringList entries = QDir(localeDir).entryList(QDir::Dirs);
2736         Q_FOREACH (const QString &d, entries) {
2737             if (QFile::exists(localeDir + QLatin1Char('/') + d + QLatin1String("/country.desktop"))) {
2738                 countries.append(d);
2739             }
2740         }
2741     }
2742     return countries;
2743 }
2744 
2745 QString KLocalePrivate::countryCodeToName(const QString &country) const
2746 {
2747     QString countryName;
2748     QString entryFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") + country.toLower() + QLatin1String("/country.desktop"));
2749     if (!entryFile.isEmpty()) {
2750         KConfig cfg(entryFile);
2751         KConfigGroup cg(&cfg, "KCM Locale");
2752         countryName = cg.readEntry("Name");
2753     }
2754     return countryName;
2755 }
2756 
2757 KLocale::CalendarSystem KLocalePrivate::calendarTypeToCalendarSystem(const QString &calendarType) const
2758 {
2759     if (calendarType == QLatin1String("coptic")) {
2760         return KLocale::CopticCalendar;
2761     } else if (calendarType == QLatin1String("ethiopian")) {
2762         return KLocale::EthiopianCalendar;
2763     } else if (calendarType == QLatin1String("gregorian")) {
2764         return KLocale::QDateCalendar;
2765     } else if (calendarType == QLatin1String("gregorian-proleptic")) {
2766         return KLocale::GregorianCalendar;
2767     } else if (calendarType == QLatin1String("hebrew")) {
2768         return KLocale::HebrewCalendar;
2769     } else if (calendarType == QLatin1String("hijri")) {
2770         return KLocale::IslamicCivilCalendar;
2771     } else if (calendarType == QLatin1String("indian-national")) {
2772         return KLocale::IndianNationalCalendar;
2773     } else if (calendarType == QLatin1String("jalali")) {
2774         return KLocale::JalaliCalendar;
2775     } else if (calendarType == QLatin1String("japanese")) {
2776         return KLocale::JapaneseCalendar;
2777     } else if (calendarType == QLatin1String("julian")) {
2778         return KLocale::JulianCalendar;
2779     } else if (calendarType == QLatin1String("minguo")) {
2780         return KLocale::MinguoCalendar;
2781     } else if (calendarType == QLatin1String("thai")) {
2782         return KLocale::ThaiCalendar;
2783     } else {
2784         return KLocale::QDateCalendar;
2785     }
2786 }
2787 
2788 QString KLocalePrivate::calendarSystemToCalendarType(KLocale::CalendarSystem calendarSystem) const
2789 {
2790     switch (calendarSystem) {
2791     case KLocale::GregorianCalendar:
2792         return QLatin1String("gregorian");
2793     case KLocale::CopticCalendar:
2794         return QLatin1String("coptic");
2795     case KLocale::EthiopianCalendar:
2796         return QLatin1String("ethiopian");
2797     case KLocale::HebrewCalendar:
2798         return QLatin1String("hebrew");
2799     case KLocale::IslamicCivilCalendar:
2800         return QLatin1String("hijri");
2801     case KLocale::IndianNationalCalendar:
2802         return QLatin1String("indian-national");
2803     case KLocale::JalaliCalendar:
2804         return QLatin1String("jalali");
2805     case KLocale::JapaneseCalendar:
2806         return QLatin1String("japanese");
2807     case KLocale::JulianCalendar:
2808         return QLatin1String("julian");
2809     case KLocale::MinguoCalendar:
2810         return QLatin1String("minguo");
2811     case KLocale::ThaiCalendar:
2812         return QLatin1String("thai");
2813     default:
2814         return QLatin1String("gregorian");
2815     }
2816 }
2817 
2818 void KLocalePrivate::setCalendar(const QString &calendarType)
2819 {
2820     setCalendarSystem(calendarTypeToCalendarSystem(calendarType));
2821 }
2822 
2823 void KLocalePrivate::setCalendarSystem(KLocale::CalendarSystem calendarSystem)
2824 {
2825     m_calendarSystem = calendarSystem;
2826     delete m_calendar;
2827     m_calendar = nullptr;
2828 }
2829 
2830 QString KLocalePrivate::calendarType() const
2831 {
2832     return calendarSystemToCalendarType(m_calendarSystem);
2833 }
2834 
2835 KLocale::CalendarSystem KLocalePrivate::calendarSystem() const
2836 {
2837     return m_calendarSystem;
2838 }
2839 
2840 const KCalendarSystem *KLocalePrivate::calendar()
2841 {
2842     if (!m_calendar) {
2843         m_calendar = KCalendarSystem::create(m_calendarSystem, m_config, q);
2844     }
2845 
2846     return m_calendar;
2847 }
2848 
2849 void KLocalePrivate::setWeekNumberSystem(KLocale::WeekNumberSystem weekNumberSystem)
2850 {
2851     m_weekNumberSystem = weekNumberSystem;
2852 }
2853 
2854 KLocale::WeekNumberSystem KLocalePrivate::weekNumberSystem()
2855 {
2856     return m_weekNumberSystem;
2857 }
2858 
2859 void KLocalePrivate::setDigitSet(KLocale::DigitSet digitSet)
2860 {
2861     m_digitSet = digitSet;
2862 }
2863 
2864 KLocale::DigitSet KLocalePrivate::digitSet() const
2865 {
2866     return m_digitSet;
2867 }
2868 
2869 void KLocalePrivate::setMonetaryDigitSet(KLocale::DigitSet digitSet)
2870 {
2871     m_monetaryDigitSet = digitSet;
2872 }
2873 
2874 KLocale::DigitSet KLocalePrivate::monetaryDigitSet() const
2875 {
2876     return m_monetaryDigitSet;
2877 }
2878 
2879 void KLocalePrivate::setDateTimeDigitSet(KLocale::DigitSet digitSet)
2880 {
2881     m_dateTimeDigitSet = digitSet;
2882 }
2883 
2884 KLocale::DigitSet KLocalePrivate::dateTimeDigitSet() const
2885 {
2886     return m_dateTimeDigitSet;
2887 }