File indexing completed on 2024-05-12 03:54:25

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
0004     SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
0005     SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kconfiggroup.h"
0011 #include "kconfiggroup_p.h"
0012 
0013 #include "kconfig.h"
0014 #include "kconfig_core_log_settings.h"
0015 #include "kconfig_p.h"
0016 #include "kconfigdata_p.h"
0017 #include "ksharedconfig.h"
0018 
0019 #include <QDate>
0020 #include <QDir>
0021 #include <QFile>
0022 #include <QPoint>
0023 #include <QRect>
0024 #include <QSharedData>
0025 #include <QString>
0026 #include <QTextStream>
0027 #include <QUrl>
0028 #include <QUuid>
0029 
0030 #include <algorithm>
0031 #include <array>
0032 #include <math.h>
0033 #include <stdlib.h>
0034 
0035 class KConfigGroupPrivate : public QSharedData
0036 {
0037 public:
0038     KConfigGroupPrivate(KConfig *owner, bool isImmutable, bool isConst, const QString &name)
0039         : mOwner(owner)
0040         , mName(name)
0041         , bImmutable(isImmutable)
0042         , bConst(isConst)
0043     {
0044         if (Q_UNLIKELY(!mOwner->name().isEmpty() && mOwner->accessMode() == KConfigBase::NoAccess)) {
0045             qCWarning(KCONFIG_CORE_LOG) << "Created a KConfigGroup on an inaccessible config location" << mOwner->name() << name;
0046         }
0047     }
0048 
0049     KConfigGroupPrivate(const KSharedConfigPtr &owner, const QString &name)
0050         : sOwner(owner)
0051         , mOwner(sOwner.data())
0052         , mName(name)
0053         , bImmutable(name.isEmpty() ? owner->isImmutable() : owner->isGroupImmutable(name))
0054         , bConst(false)
0055     {
0056         if (Q_UNLIKELY(!mOwner->name().isEmpty() && mOwner->accessMode() == KConfigBase::NoAccess)) {
0057             qCWarning(KCONFIG_CORE_LOG) << "Created a KConfigGroup on an inaccessible config location" << mOwner->name() << name;
0058         }
0059     }
0060 
0061     KConfigGroupPrivate(KConfigGroup *parent, bool isImmutable, bool isConst, const QString &name)
0062         : sOwner(parent->d->sOwner)
0063         , mOwner(parent->d->mOwner)
0064         , mName(name)
0065         , bImmutable(isImmutable)
0066         , bConst(isConst)
0067     {
0068         if (!parent->d->mName.isEmpty()) {
0069             mParent = parent->d;
0070         }
0071     }
0072 
0073     KConfigGroupPrivate(const KConfigGroupPrivate *other, bool isImmutable, const QString &name)
0074         : sOwner(other->sOwner)
0075         , mOwner(other->mOwner)
0076         , mName(name)
0077         , bImmutable(isImmutable)
0078         , bConst(other->bConst)
0079     {
0080         if (!other->mName.isEmpty()) {
0081             mParent = const_cast<KConfigGroupPrivate *>(other);
0082         }
0083     }
0084 
0085     KSharedConfig::Ptr sOwner;
0086     KConfig *mOwner;
0087     QExplicitlySharedDataPointer<KConfigGroupPrivate> mParent;
0088     QString mName;
0089 
0090     /* bitfield */
0091     const bool bImmutable : 1; // is this group immutable?
0092     const bool bConst : 1; // is this group read-only?
0093 
0094     QString fullName() const
0095     {
0096         if (!mParent) {
0097             return name();
0098         }
0099         return mParent->fullName(mName);
0100     }
0101 
0102     QString name() const
0103     {
0104         if (mName.isEmpty()) {
0105             return QStringLiteral("<default>");
0106         }
0107         return mName;
0108     }
0109 
0110     QString fullName(const QString &aGroup) const
0111     {
0112         if (mName.isEmpty()) {
0113             return aGroup;
0114         }
0115         return fullName() + QLatin1Char('\x1d') + aGroup;
0116     }
0117 
0118     static QExplicitlySharedDataPointer<KConfigGroupPrivate> create(KConfigBase *master, const QString &name, bool isImmutable, bool isConst)
0119     {
0120         QExplicitlySharedDataPointer<KConfigGroupPrivate> data;
0121         if (dynamic_cast<KConfigGroup *>(master)) {
0122             data = new KConfigGroupPrivate(static_cast<KConfigGroup *>(master), isImmutable, isConst, name);
0123         } else {
0124             data = new KConfigGroupPrivate(dynamic_cast<KConfig *>(master), isImmutable, isConst, name);
0125         }
0126         return data;
0127     }
0128 
0129     static QByteArray serializeList(const QList<QByteArray> &list);
0130     static QStringList deserializeList(const QString &data);
0131 };
0132 
0133 QByteArray KConfigGroupPrivate::serializeList(const QList<QByteArray> &list)
0134 {
0135     QByteArray value;
0136 
0137     if (!list.isEmpty()) {
0138         auto it = list.cbegin();
0139         const auto end = list.cend();
0140 
0141         value = QByteArray(*it).replace('\\', QByteArrayLiteral("\\\\")).replace(',', QByteArrayLiteral("\\,"));
0142 
0143         while (++it != end) {
0144             // In the loop, so it is not done when there is only one element.
0145             // Doing it repeatedly is a pretty cheap operation.
0146             value.reserve(4096);
0147 
0148             value += ',';
0149             value += QByteArray(*it).replace('\\', QByteArrayLiteral("\\\\")).replace(',', QByteArrayLiteral("\\,"));
0150         }
0151 
0152         // To be able to distinguish an empty list from a list with one empty element.
0153         if (value.isEmpty()) {
0154             value = QByteArrayLiteral("\\0");
0155         }
0156     }
0157 
0158     return value;
0159 }
0160 
0161 QStringList KConfigGroupPrivate::deserializeList(const QString &data)
0162 {
0163     if (data.isEmpty()) {
0164         return QStringList();
0165     }
0166     if (data == QLatin1String("\\0")) {
0167         return QStringList(QString());
0168     }
0169     QStringList value;
0170     QString val;
0171     val.reserve(data.size());
0172     bool quoted = false;
0173     for (int p = 0; p < data.length(); p++) {
0174         if (quoted) {
0175             val += data[p];
0176             quoted = false;
0177         } else if (data[p].unicode() == '\\') {
0178             quoted = true;
0179         } else if (data[p].unicode() == ',') {
0180             val.squeeze(); // release any unused memory
0181             value.append(val);
0182             val.clear();
0183             val.reserve(data.size() - p);
0184         } else {
0185             val += data[p];
0186         }
0187     }
0188     value.append(val);
0189     return value;
0190 }
0191 
0192 static QList<int> asIntList(const QByteArray &string)
0193 {
0194     const auto &splitString = string.split(',');
0195 
0196     QList<int> list;
0197     list.reserve(splitString.count());
0198     for (const QByteArray &s : splitString) {
0199         list << s.toInt();
0200     }
0201     return list;
0202 }
0203 
0204 static QList<qreal> asRealList(const QByteArray &string)
0205 {
0206     const auto &splitString = string.split(',');
0207 
0208     QList<qreal> list;
0209     list.reserve(splitString.count());
0210     for (const QByteArray &s : splitString) {
0211         list << s.toDouble();
0212     }
0213     return list;
0214 }
0215 
0216 static QString errString(const char *pKey, const QByteArray &value, const QVariant &aDefault)
0217 {
0218     return QStringLiteral("\"%1\" - conversion of \"%3\" to %2 failed")
0219         .arg(QString::fromLatin1(pKey), QString::fromLatin1(aDefault.typeName()), QString::fromLatin1(value));
0220 }
0221 
0222 static QString formatError(int expected, int got)
0223 {
0224     return QStringLiteral(" (wrong format: expected %1 items, got %2)").arg(expected).arg(got);
0225 }
0226 
0227 QVariant KConfigGroup::convertToQVariant(const char *pKey, const QByteArray &value, const QVariant &aDefault)
0228 {
0229     // if a type handler is added here you must add a QVConversions definition
0230     // to kconfigconversioncheck_p.h, or KConfigConversionCheck::to_QVariant will not allow
0231     // readEntry<T> to convert to QVariant.
0232     switch (static_cast<QMetaType::Type>(aDefault.userType())) {
0233     case QMetaType::UnknownType:
0234         return QVariant();
0235     case QMetaType::QString:
0236         // this should return the raw string not the dollar expanded string.
0237         // imho if processed string is wanted should call
0238         // readEntry(key, QString) not readEntry(key, QVariant)
0239         return QString::fromUtf8(value);
0240     case QMetaType::QUuid:
0241         return QUuid::fromString(value);
0242     case QMetaType::QVariantList:
0243     case QMetaType::QStringList:
0244         return KConfigGroupPrivate::deserializeList(QString::fromUtf8(value));
0245     case QMetaType::QByteArray:
0246         return value;
0247     case QMetaType::Bool: {
0248         static const std::array<const char *, 4> negatives = {"false", "no", "off", "0"};
0249 
0250         return std::all_of(negatives.begin(), negatives.end(), [value](const char *negativeString) {
0251             return value.compare(negativeString, Qt::CaseInsensitive) != 0;
0252         });
0253     }
0254     case QMetaType::Double:
0255     case QMetaType::Float:
0256     case QMetaType::Int:
0257     case QMetaType::UInt:
0258     case QMetaType::LongLong:
0259     case QMetaType::ULongLong: {
0260         QVariant tmp = value;
0261         if (!tmp.convert(aDefault.metaType())) {
0262             tmp = aDefault;
0263         }
0264         return tmp;
0265     }
0266     case QMetaType::QPoint: {
0267         const auto list = asIntList(value);
0268 
0269         if (list.count() != 2) {
0270             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(2, list.count());
0271             return aDefault;
0272         }
0273         return QPoint(list.at(0), list.at(1));
0274     }
0275     case QMetaType::QPointF: {
0276         const auto list = asRealList(value);
0277 
0278         if (list.count() != 2) {
0279             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(2, list.count());
0280             return aDefault;
0281         }
0282         return QPointF(list.at(0), list.at(1));
0283     }
0284     case QMetaType::QRect: {
0285         const auto list = asIntList(value);
0286 
0287         if (list.count() != 4) {
0288             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(4, list.count());
0289             return aDefault;
0290         }
0291         const QRect rect(list.at(0), list.at(1), list.at(2), list.at(3));
0292         if (!rect.isValid()) {
0293             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
0294             return aDefault;
0295         }
0296         return rect;
0297     }
0298     case QMetaType::QRectF: {
0299         const auto list = asRealList(value);
0300 
0301         if (list.count() != 4) {
0302             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(4, list.count());
0303             return aDefault;
0304         }
0305         const QRectF rect(list.at(0), list.at(1), list.at(2), list.at(3));
0306         if (!rect.isValid()) {
0307             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
0308             return aDefault;
0309         }
0310         return rect;
0311     }
0312     case QMetaType::QSize: {
0313         const auto list = asIntList(value);
0314 
0315         if (list.count() != 2) {
0316             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(2, list.count());
0317             return aDefault;
0318         }
0319         const QSize size(list.at(0), list.at(1));
0320         if (!size.isValid()) {
0321             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
0322             return aDefault;
0323         }
0324         return size;
0325     }
0326     case QMetaType::QSizeF: {
0327         const auto list = asRealList(value);
0328 
0329         if (list.count() != 2) {
0330             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(2, list.count());
0331             return aDefault;
0332         }
0333         const QSizeF size(list.at(0), list.at(1));
0334         if (!size.isValid()) {
0335             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
0336             return aDefault;
0337         }
0338         return size;
0339     }
0340     case QMetaType::QDateTime: {
0341         const auto list = asRealList(value);
0342         if (list.count() < 6) {
0343             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(6, list.count());
0344             return aDefault;
0345         }
0346         const QDate date(list.at(0), list.at(1), list.at(2));
0347         const qreal totalSeconds = list.at(5);
0348         qreal seconds;
0349         const qreal fractional = modf(totalSeconds, &seconds);
0350         const qreal milliseconds = round(fractional * 1000.0);
0351         const QTime time(list.at(3), list.at(4), seconds, milliseconds);
0352         const QDateTime dt(date, time);
0353         if (!dt.isValid()) {
0354             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
0355             return aDefault;
0356         }
0357         return dt;
0358     }
0359     case QMetaType::QDate: {
0360         auto list = asIntList(value);
0361         if (list.count() == 6) {
0362             list = list.mid(0, 3); // don't break config files that stored QDate as QDateTime
0363         }
0364         if (list.count() != 3) {
0365             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(3, list.count());
0366             return aDefault;
0367         }
0368         const QDate date(list.at(0), list.at(1), list.at(2));
0369         if (!date.isValid()) {
0370             qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
0371             return aDefault;
0372         }
0373         return date;
0374     }
0375     case QMetaType::QColor:
0376     case QMetaType::QFont:
0377         qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::readEntry was passed GUI type '" << aDefault.typeName()
0378                                     << "' but KConfigGui isn't linked! If it is linked to your program, "
0379                                        "this is a platform bug. Please inform the KDE developers";
0380         break;
0381     case QMetaType::QUrl:
0382         return QUrl(QString::fromUtf8(value));
0383 
0384     default:
0385         break;
0386     }
0387 
0388     qCWarning(KCONFIG_CORE_LOG) << "unhandled type " << aDefault.typeName();
0389     return QVariant();
0390 }
0391 
0392 static bool cleanHomeDirPath(QString &path, const QString &homeDir)
0393 {
0394 #ifdef Q_OS_WIN // safer
0395     if (!QDir::toNativeSeparators(path).startsWith(QDir::toNativeSeparators(homeDir))) {
0396         return false;
0397     }
0398 #else
0399     if (!path.startsWith(homeDir)) {
0400         return false;
0401     }
0402 #endif
0403 
0404     int len = homeDir.length();
0405     // replace by "$HOME" if possible
0406     if (len && (path.length() == len || path[len] == QLatin1Char('/'))) {
0407         path.replace(0, len, QStringLiteral("$HOME"));
0408         return true;
0409     }
0410 
0411     return false;
0412 }
0413 
0414 static QString translatePath(QString path) // krazy:exclude=passbyvalue
0415 {
0416     if (path.isEmpty()) {
0417         return path;
0418     }
0419 
0420     // only "our" $HOME should be interpreted
0421     path.replace(QLatin1Char('$'), QLatin1String("$$"));
0422 
0423     const bool startsWithFile = path.startsWith(QLatin1String("file:"), Qt::CaseInsensitive);
0424     path = startsWithFile ? QUrl(path).toLocalFile() : path;
0425 
0426     if (QDir::isRelativePath(path)) {
0427         return path;
0428     }
0429 
0430     // Use the same thing as what expandString() will do, to keep data intact
0431 #ifdef Q_OS_WIN
0432     const QString homeDir = QDir::homePath();
0433 #else
0434     const QString homeDir = QFile::decodeName(qgetenv("HOME"));
0435 #endif
0436     (void)cleanHomeDirPath(path, homeDir);
0437 
0438     if (startsWithFile) {
0439         path = QUrl::fromLocalFile(path).toString();
0440     }
0441 
0442     return path;
0443 }
0444 
0445 KConfigGroup::KConfigGroup()
0446     : d()
0447 {
0448 }
0449 
0450 bool KConfigGroup::isValid() const
0451 {
0452     return bool(d);
0453 }
0454 
0455 KConfigGroupGui _kde_internal_KConfigGroupGui;
0456 static inline bool readEntryGui(const QByteArray &data, const char *key, const QVariant &input, QVariant &output)
0457 {
0458     if (_kde_internal_KConfigGroupGui.readEntryGui) {
0459         return _kde_internal_KConfigGroupGui.readEntryGui(data, key, input, output);
0460     }
0461     return false;
0462 }
0463 
0464 static inline bool writeEntryGui(KConfigGroup *cg, const char *key, const QVariant &input, KConfigGroup::WriteConfigFlags flags)
0465 {
0466     if (_kde_internal_KConfigGroupGui.writeEntryGui) {
0467         return _kde_internal_KConfigGroupGui.writeEntryGui(cg, key, input, flags);
0468     }
0469     return false;
0470 }
0471 
0472 KConfigGroup::KConfigGroup(KConfigBase *master, const QString &_group)
0473     : d(KConfigGroupPrivate::create(master, _group, master->isGroupImmutable(_group), false))
0474 {
0475 }
0476 
0477 KConfigGroup::KConfigGroup(const KConfigBase *master, const QString &_group)
0478     : d(KConfigGroupPrivate::create(const_cast<KConfigBase *>(master), _group, master->isGroupImmutable(_group), true))
0479 {
0480 }
0481 
0482 KConfigGroup::KConfigGroup(const KSharedConfigPtr &master, const QString &_group)
0483     : d(new KConfigGroupPrivate(master, _group))
0484 {
0485 }
0486 
0487 KConfigGroup &KConfigGroup::operator=(const KConfigGroup &rhs)
0488 {
0489     d = rhs.d;
0490     return *this;
0491 }
0492 
0493 KConfigGroup::KConfigGroup(const KConfigGroup &rhs)
0494     : d(rhs.d)
0495 {
0496 }
0497 
0498 KConfigGroup::~KConfigGroup()
0499 {
0500     d.reset();
0501 }
0502 
0503 KConfigGroup KConfigGroup::groupImpl(const QString &aGroup)
0504 {
0505     Q_ASSERT_X(isValid(), "KConfigGroup::groupImpl", "accessing an invalid group");
0506     Q_ASSERT_X(!aGroup.isEmpty(), "KConfigGroup::groupImpl", "can not have an unnamed child group");
0507 
0508     KConfigGroup newGroup;
0509 
0510     newGroup.d = new KConfigGroupPrivate(this, isGroupImmutableImpl(aGroup), d->bConst, aGroup);
0511 
0512     return newGroup;
0513 }
0514 
0515 const KConfigGroup KConfigGroup::groupImpl(const QString &aGroup) const
0516 {
0517     Q_ASSERT_X(isValid(), "KConfigGroup::groupImpl", "accessing an invalid group");
0518     Q_ASSERT_X(!aGroup.isEmpty(), "KConfigGroup::groupImpl", "can not have an unnamed child group");
0519 
0520     KConfigGroup newGroup;
0521 
0522     newGroup.d = new KConfigGroupPrivate(const_cast<KConfigGroup *>(this), isGroupImmutableImpl(aGroup), true, aGroup);
0523 
0524     return newGroup;
0525 }
0526 
0527 KConfigGroup KConfigGroup::parent() const
0528 {
0529     Q_ASSERT_X(isValid(), "KConfigGroup::parent", "accessing an invalid group");
0530 
0531     KConfigGroup parentGroup;
0532 
0533     if (d->mParent) {
0534         parentGroup.d = d->mParent;
0535     } else {
0536         parentGroup.d = new KConfigGroupPrivate(d->mOwner, d->mOwner->isImmutable(), d->bConst, QString());
0537         // make sure we keep the refcount up on the KConfig object
0538         parentGroup.d->sOwner = d->sOwner;
0539     }
0540 
0541     return parentGroup;
0542 }
0543 
0544 void KConfigGroup::deleteGroup(WriteConfigFlags flags)
0545 {
0546     Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroup", "accessing an invalid group");
0547     Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroup", "deleting a read-only group");
0548 
0549     config()->deleteGroup(d->fullName(), flags);
0550 }
0551 
0552 QString KConfigGroup::name() const
0553 {
0554     Q_ASSERT_X(isValid(), "KConfigGroup::name", "accessing an invalid group");
0555 
0556     return d->name();
0557 }
0558 
0559 bool KConfigGroup::exists() const
0560 {
0561     Q_ASSERT_X(isValid(), "KConfigGroup::exists", "accessing an invalid group");
0562 
0563     return config()->hasGroup(d->fullName());
0564 }
0565 
0566 bool KConfigGroup::sync()
0567 {
0568     Q_ASSERT_X(isValid(), "KConfigGroup::sync", "accessing an invalid group");
0569 
0570     if (!d->bConst) {
0571         return config()->sync();
0572     }
0573 
0574     return false;
0575 }
0576 
0577 QMap<QString, QString> KConfigGroup::entryMap() const
0578 {
0579     Q_ASSERT_X(isValid(), "KConfigGroup::entryMap", "accessing an invalid group");
0580 
0581     return config()->entryMap(d->fullName());
0582 }
0583 
0584 KConfig *KConfigGroup::config()
0585 {
0586     Q_ASSERT_X(isValid(), "KConfigGroup::config", "accessing an invalid group");
0587 
0588     return d->mOwner;
0589 }
0590 
0591 const KConfig *KConfigGroup::config() const
0592 {
0593     Q_ASSERT_X(isValid(), "KConfigGroup::config", "accessing an invalid group");
0594 
0595     return d->mOwner;
0596 }
0597 
0598 bool KConfigGroup::isEntryImmutable(const char *key) const
0599 {
0600     Q_ASSERT_X(isValid(), "KConfigGroup::isEntryImmutable", "accessing an invalid group");
0601 
0602     return (isImmutable() || !config()->d_func()->canWriteEntry(d->fullName(), key, config()->readDefaults()));
0603 }
0604 
0605 bool KConfigGroup::isEntryImmutable(const QString &key) const
0606 {
0607     return isEntryImmutable(key.toUtf8().constData());
0608 }
0609 
0610 QString KConfigGroup::readEntryUntranslated(const QString &pKey, const QString &aDefault) const
0611 {
0612     return readEntryUntranslated(pKey.toUtf8().constData(), aDefault);
0613 }
0614 
0615 QString KConfigGroup::readEntryUntranslated(const char *key, const QString &aDefault) const
0616 {
0617     Q_ASSERT_X(isValid(), "KConfigGroup::readEntryUntranslated", "accessing an invalid group");
0618 
0619     QString result = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchFlags(), nullptr);
0620     if (result.isNull()) {
0621         return aDefault;
0622     }
0623     return result;
0624 }
0625 
0626 QString KConfigGroup::readEntry(const char *key, const char *aDefault) const
0627 {
0628     return readEntry(key, QString::fromUtf8(aDefault));
0629 }
0630 
0631 QString KConfigGroup::readEntry(const QString &key, const char *aDefault) const
0632 {
0633     return readEntry(key.toUtf8().constData(), aDefault);
0634 }
0635 
0636 QString KConfigGroup::readEntry(const char *key, const QString &aDefault) const
0637 {
0638     Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
0639 
0640     bool expand = false;
0641 
0642     // read value from the entry map
0643     QString aValue = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized, &expand);
0644     if (aValue.isNull()) {
0645         aValue = aDefault;
0646     }
0647 
0648     if (expand) {
0649         return KConfigPrivate::expandString(aValue);
0650     }
0651 
0652     return aValue;
0653 }
0654 
0655 QString KConfigGroup::readEntry(const QString &key, const QString &aDefault) const
0656 {
0657     return readEntry(key.toUtf8().constData(), aDefault);
0658 }
0659 
0660 QStringList KConfigGroup::readEntry(const char *key, const QStringList &aDefault) const
0661 {
0662     Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
0663 
0664     const QString data = readEntry(key, QString());
0665     if (data.isNull()) {
0666         return aDefault;
0667     }
0668 
0669     return KConfigGroupPrivate::deserializeList(data);
0670 }
0671 
0672 QStringList KConfigGroup::readEntry(const QString &key, const QStringList &aDefault) const
0673 {
0674     return readEntry(key.toUtf8().constData(), aDefault);
0675 }
0676 
0677 QVariant KConfigGroup::readEntry(const char *key, const QVariant &aDefault) const
0678 {
0679     Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
0680 
0681     const QByteArray data = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized);
0682     if (data.isNull()) {
0683         return aDefault;
0684     }
0685 
0686     QVariant value;
0687     if (!readEntryGui(data, key, aDefault, value)) {
0688         return convertToQVariant(key, data, aDefault);
0689     }
0690 
0691     return value;
0692 }
0693 
0694 QVariant KConfigGroup::readEntry(const QString &key, const QVariant &aDefault) const
0695 {
0696     return readEntry(key.toUtf8().constData(), aDefault);
0697 }
0698 
0699 QVariantList KConfigGroup::readEntry(const char *key, const QVariantList &aDefault) const
0700 {
0701     Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
0702 
0703     const QString data = readEntry(key, QString());
0704     if (data.isNull()) {
0705         return aDefault;
0706     }
0707 
0708     const auto &list = KConfigGroupPrivate::deserializeList(data);
0709 
0710     QVariantList value;
0711     value.reserve(list.count());
0712     for (const QString &v : list) {
0713         value << v;
0714     }
0715 
0716     return value;
0717 }
0718 
0719 QVariantList KConfigGroup::readEntry(const QString &key, const QVariantList &aDefault) const
0720 {
0721     return readEntry(key.toUtf8().constData(), aDefault);
0722 }
0723 
0724 QStringList KConfigGroup::readXdgListEntry(const QString &key, const QStringList &aDefault) const
0725 {
0726     return readXdgListEntry(key.toUtf8().constData(), aDefault);
0727 }
0728 
0729 QStringList KConfigGroup::readXdgListEntry(const char *key, const QStringList &aDefault) const
0730 {
0731     Q_ASSERT_X(isValid(), "KConfigGroup::readXdgListEntry", "accessing an invalid group");
0732 
0733     const QString data = readEntry(key, QString());
0734     if (data.isNull()) {
0735         return aDefault;
0736     }
0737 
0738     QStringList value;
0739     QString val;
0740     val.reserve(data.size());
0741     // XXX List serialization being a separate layer from low-level parsing is
0742     // probably a bug. No affected entries are defined, though.
0743     bool quoted = false;
0744     for (int p = 0; p < data.length(); p++) {
0745         if (quoted) {
0746             val += data[p];
0747             quoted = false;
0748         } else if (data[p] == QLatin1Char('\\')) {
0749             quoted = true;
0750         } else if (data[p] == QLatin1Char(';')) {
0751             value.append(val);
0752             val.clear();
0753             val.reserve(data.size() - p);
0754         } else {
0755             val += data[p];
0756         }
0757     }
0758     if (!val.isEmpty()) {
0759         value.append(val);
0760     }
0761     return value;
0762 }
0763 
0764 QString KConfigGroup::readPathEntry(const QString &pKey, const QString &aDefault) const
0765 {
0766     return readPathEntry(pKey.toUtf8().constData(), aDefault);
0767 }
0768 
0769 QString KConfigGroup::readPathEntry(const char *key, const QString &aDefault) const
0770 {
0771     Q_ASSERT_X(isValid(), "KConfigGroup::readPathEntry", "accessing an invalid group");
0772 
0773     bool expand = false;
0774 
0775     QString aValue = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized, &expand);
0776     if (aValue.isNull()) {
0777         aValue = aDefault;
0778     }
0779 
0780     return KConfigPrivate::expandString(aValue);
0781 }
0782 
0783 QStringList KConfigGroup::readPathEntry(const QString &pKey, const QStringList &aDefault) const
0784 {
0785     return readPathEntry(pKey.toUtf8().constData(), aDefault);
0786 }
0787 
0788 QStringList KConfigGroup::readPathEntry(const char *key, const QStringList &aDefault) const
0789 {
0790     Q_ASSERT_X(isValid(), "KConfigGroup::readPathEntry", "accessing an invalid group");
0791 
0792     const QString data = readPathEntry(key, QString());
0793     if (data.isNull()) {
0794         return aDefault;
0795     }
0796 
0797     return KConfigGroupPrivate::deserializeList(data);
0798 }
0799 
0800 void KConfigGroup::writeEntry(const char *key, const QString &value, WriteConfigFlags flags)
0801 {
0802     Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
0803     Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
0804 
0805     writeEntry(key, value.toUtf8(), flags);
0806 }
0807 
0808 void KConfigGroup::writeEntry(const QString &key, const QString &value, WriteConfigFlags flags)
0809 {
0810     writeEntry(key.toUtf8().constData(), value, flags);
0811 }
0812 
0813 void KConfigGroup::writeEntry(const QString &key, const char *value, WriteConfigFlags pFlags)
0814 {
0815     Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
0816     Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
0817 
0818     writeEntry(key.toUtf8().constData(), QVariant(QString::fromLatin1(value)), pFlags);
0819 }
0820 
0821 void KConfigGroup::writeEntry(const char *key, const char *value, WriteConfigFlags pFlags)
0822 {
0823     writeEntry(key, QVariant(QString::fromLatin1(value)), pFlags);
0824 }
0825 
0826 void KConfigGroup::writeEntry(const char *key, const QByteArray &value, WriteConfigFlags flags)
0827 {
0828     Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
0829     Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
0830 
0831     config()->d_func()->putData(d->fullName(), key, value.isNull() ? QByteArray("") : value, flags);
0832 }
0833 
0834 void KConfigGroup::writeEntry(const QString &key, const QByteArray &value, WriteConfigFlags pFlags)
0835 {
0836     writeEntry(key.toUtf8().constData(), value, pFlags);
0837 }
0838 
0839 void KConfigGroup::writeEntry(const char *key, const QStringList &list, WriteConfigFlags flags)
0840 {
0841     Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
0842     Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
0843 
0844     QList<QByteArray> balist;
0845     balist.reserve(list.count());
0846 
0847     for (const QString &entry : list) {
0848         balist.append(entry.toUtf8());
0849     }
0850 
0851     writeEntry(key, KConfigGroupPrivate::serializeList(balist), flags);
0852 }
0853 
0854 void KConfigGroup::writeEntry(const QString &key, const QStringList &list, WriteConfigFlags flags)
0855 {
0856     writeEntry(key.toUtf8().constData(), list, flags);
0857 }
0858 
0859 void KConfigGroup::writeEntry(const char *key, const QVariantList &list, WriteConfigFlags flags)
0860 {
0861     Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
0862     Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
0863 
0864     QList<QByteArray> data;
0865     data.reserve(list.count());
0866 
0867     for (const QVariant &v : list) {
0868         if (v.userType() == QMetaType::QByteArray) {
0869             data << v.toByteArray();
0870         } else {
0871             data << v.toString().toUtf8();
0872         }
0873     }
0874 
0875     writeEntry(key, KConfigGroupPrivate::serializeList(data), flags);
0876 }
0877 
0878 void KConfigGroup::writeEntry(const char *key, const QVariant &value, WriteConfigFlags flags)
0879 {
0880     Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
0881     Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
0882 
0883     if (writeEntryGui(this, key, value, flags)) {
0884         return; // GUI type that was handled
0885     }
0886 
0887     QByteArray data;
0888     // if a type handler is added here you must add a QVConversions definition
0889     // to kconfigconversioncheck_p.h, or KConfigConversionCheck::to_QVariant will not allow
0890     // writeEntry<T> to convert to QVariant.
0891     switch (static_cast<QMetaType::Type>(value.userType())) {
0892     case QMetaType::UnknownType:
0893         data = "";
0894         break;
0895     case QMetaType::QByteArray:
0896         data = value.toByteArray();
0897         break;
0898     case QMetaType::QString:
0899     case QMetaType::Int:
0900     case QMetaType::UInt:
0901     case QMetaType::Double:
0902     case QMetaType::Float:
0903     case QMetaType::Bool:
0904     case QMetaType::LongLong:
0905     case QMetaType::ULongLong:
0906         data = value.toString().toUtf8();
0907         break;
0908     case QMetaType::QVariantList:
0909         if (!value.canConvert<QStringList>()) {
0910             qCWarning(KCONFIG_CORE_LOG) << "not all types in \"" << key
0911                                         << "\" can convert to QString,"
0912                                            " information will be lost";
0913         }
0914         Q_FALLTHROUGH();
0915     case QMetaType::QStringList:
0916         writeEntry(key, value.toList(), flags);
0917         return;
0918     case QMetaType::QPoint: {
0919         const QPoint rPoint = value.toPoint();
0920 
0921         const QVariantList list{rPoint.x(), rPoint.y()};
0922 
0923         writeEntry(key, list, flags);
0924         return;
0925     }
0926     case QMetaType::QPointF: {
0927         const QPointF point = value.toPointF();
0928 
0929         const QVariantList list{point.x(), point.y()};
0930 
0931         writeEntry(key, list, flags);
0932         return;
0933     }
0934     case QMetaType::QRect: {
0935         const QRect rRect = value.toRect();
0936 
0937         const QVariantList list{rRect.left(), rRect.top(), rRect.width(), rRect.height()};
0938 
0939         writeEntry(key, list, flags);
0940         return;
0941     }
0942     case QMetaType::QRectF: {
0943         const QRectF rRectF = value.toRectF();
0944 
0945         const QVariantList list{rRectF.left(), rRectF.top(), rRectF.width(), rRectF.height()};
0946 
0947         writeEntry(key, list, flags);
0948         return;
0949     }
0950     case QMetaType::QSize: {
0951         const QSize rSize = value.toSize();
0952 
0953         const QVariantList list{rSize.width(), rSize.height()};
0954 
0955         writeEntry(key, list, flags);
0956         return;
0957     }
0958     case QMetaType::QUuid: {
0959         writeEntry(key, value.toString(), flags);
0960         return;
0961     }
0962     case QMetaType::QSizeF: {
0963         const QSizeF rSizeF = value.toSizeF();
0964 
0965         const QVariantList list{rSizeF.width(), rSizeF.height()};
0966 
0967         writeEntry(key, list, flags);
0968         return;
0969     }
0970     case QMetaType::QDate: {
0971         const QDate date = value.toDate();
0972 
0973         const QVariantList list{date.year(), date.month(), date.day()};
0974 
0975         writeEntry(key, list, flags);
0976         return;
0977     }
0978     case QMetaType::QDateTime: {
0979         const QDateTime rDateTime = value.toDateTime();
0980 
0981         const QTime time = rDateTime.time();
0982         const QDate date = rDateTime.date();
0983 
0984         const QVariantList list{
0985             date.year(),
0986             date.month(),
0987             date.day(),
0988 
0989             time.hour(),
0990             time.minute(),
0991             time.second() + time.msec() / 1000.0,
0992         };
0993 
0994         writeEntry(key, list, flags);
0995         return;
0996     }
0997 
0998     case QMetaType::QColor:
0999     case QMetaType::QFont:
1000         qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::writeEntry was passed GUI type '" << value.typeName()
1001                                     << "' but KConfigGui isn't linked! If it is linked to your program, this is a platform bug. "
1002                                        "Please inform the KDE developers";
1003         break;
1004     case QMetaType::QUrl:
1005         data = QUrl(value.toUrl()).toString().toUtf8();
1006         break;
1007     default:
1008         qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::writeEntry - unhandled type" << value.typeName() << "in group" << name();
1009     }
1010 
1011     writeEntry(key, data, flags);
1012 }
1013 
1014 void KConfigGroup::writeEntry(const QString &key, const QVariant &value, WriteConfigFlags flags)
1015 {
1016     writeEntry(key.toUtf8().constData(), value, flags);
1017 }
1018 
1019 void KConfigGroup::writeEntry(const QString &key, const QVariantList &list, WriteConfigFlags flags)
1020 {
1021     writeEntry(key.toUtf8().constData(), list, flags);
1022 }
1023 
1024 void KConfigGroup::writeXdgListEntry(const QString &key, const QStringList &value, WriteConfigFlags pFlags)
1025 {
1026     writeXdgListEntry(key.toUtf8().constData(), value, pFlags);
1027 }
1028 
1029 void KConfigGroup::writeXdgListEntry(const char *key, const QStringList &list, WriteConfigFlags flags)
1030 {
1031     Q_ASSERT_X(isValid(), "KConfigGroup::writeXdgListEntry", "accessing an invalid group");
1032     Q_ASSERT_X(!d->bConst, "KConfigGroup::writeXdgListEntry", "writing to a read-only group");
1033 
1034     QString value;
1035     value.reserve(4096);
1036 
1037     // XXX List serialization being a separate layer from low-level escaping is
1038     // probably a bug. No affected entries are defined, though.
1039     for (QString val : list) { // clazy:exclude=range-loop
1040         val.replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(QLatin1Char(';'), QLatin1String("\\;"));
1041         value += val + QLatin1Char(';');
1042     }
1043 
1044     writeEntry(key, value, flags);
1045 }
1046 
1047 void KConfigGroup::writePathEntry(const QString &pKey, const QString &path, WriteConfigFlags pFlags)
1048 {
1049     writePathEntry(pKey.toUtf8().constData(), path, pFlags);
1050 }
1051 
1052 void KConfigGroup::writePathEntry(const char *pKey, const QString &path, WriteConfigFlags pFlags)
1053 {
1054     Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group");
1055     Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group");
1056 
1057     config()->d_func()->putData(d->fullName(), pKey, translatePath(path).toUtf8(), pFlags, true);
1058 }
1059 
1060 void KConfigGroup::writePathEntry(const QString &pKey, const QStringList &value, WriteConfigFlags pFlags)
1061 {
1062     writePathEntry(pKey.toUtf8().constData(), value, pFlags);
1063 }
1064 
1065 void KConfigGroup::writePathEntry(const char *pKey, const QStringList &value, WriteConfigFlags pFlags)
1066 {
1067     Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group");
1068     Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group");
1069 
1070     QList<QByteArray> list;
1071     list.reserve(value.length());
1072     for (const QString &path : value) {
1073         list << translatePath(path).toUtf8();
1074     }
1075 
1076     config()->d_func()->putData(d->fullName(), pKey, KConfigGroupPrivate::serializeList(list), pFlags, true);
1077 }
1078 
1079 void KConfigGroup::deleteEntry(const char *key, WriteConfigFlags flags)
1080 {
1081     Q_ASSERT_X(isValid(), "KConfigGroup::deleteEntry", "accessing an invalid group");
1082     Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteEntry", "deleting from a read-only group");
1083 
1084     config()->d_func()->putData(d->fullName(), key, QByteArray(), flags);
1085 }
1086 
1087 void KConfigGroup::deleteEntry(const QString &key, WriteConfigFlags flags)
1088 {
1089     deleteEntry(key.toUtf8().constData(), flags);
1090 }
1091 
1092 void KConfigGroup::revertToDefault(const char *key, WriteConfigFlags flags)
1093 {
1094     Q_ASSERT_X(isValid(), "KConfigGroup::revertToDefault", "accessing an invalid group");
1095     Q_ASSERT_X(!d->bConst, "KConfigGroup::revertToDefault", "writing to a read-only group");
1096 
1097     config()->d_func()->revertEntry(d->fullName(), key, flags);
1098 }
1099 
1100 void KConfigGroup::revertToDefault(const QString &key, WriteConfigFlags flags)
1101 {
1102     revertToDefault(key.toUtf8().constData(), flags);
1103 }
1104 
1105 bool KConfigGroup::hasDefault(const char *key) const
1106 {
1107     Q_ASSERT_X(isValid(), "KConfigGroup::hasDefault", "accessing an invalid group");
1108 
1109     KEntryMap::SearchFlags flags = KEntryMap::SearchDefaults | KEntryMap::SearchLocalized;
1110 
1111     return !config()->d_func()->lookupData(d->fullName(), key, flags).isNull();
1112 }
1113 
1114 bool KConfigGroup::hasDefault(const QString &key) const
1115 {
1116     return hasDefault(key.toUtf8().constData());
1117 }
1118 
1119 bool KConfigGroup::hasKey(const char *key) const
1120 {
1121     Q_ASSERT_X(isValid(), "KConfigGroup::hasKey", "accessing an invalid group");
1122 
1123     KEntryMap::SearchFlags flags = KEntryMap::SearchLocalized;
1124     if (config()->readDefaults()) {
1125         flags |= KEntryMap::SearchDefaults;
1126     }
1127 
1128     return !config()->d_func()->lookupData(d->fullName(), key, flags).isNull();
1129 }
1130 
1131 bool KConfigGroup::hasKey(const QString &key) const
1132 {
1133     return hasKey(key.toUtf8().constData());
1134 }
1135 
1136 bool KConfigGroup::isImmutable() const
1137 {
1138     Q_ASSERT_X(isValid(), "KConfigGroup::isImmutable", "accessing an invalid group");
1139 
1140     return d->bImmutable;
1141 }
1142 
1143 QStringList KConfigGroup::groupList() const
1144 {
1145     Q_ASSERT_X(isValid(), "KConfigGroup::groupList", "accessing an invalid group");
1146 
1147     return config()->d_func()->groupList(d->fullName());
1148 }
1149 
1150 QStringList KConfigGroup::keyList() const
1151 {
1152     Q_ASSERT_X(isValid(), "KConfigGroup::keyList", "accessing an invalid group");
1153 
1154     return config()->d_func()->usedKeyList(d->fullName());
1155 }
1156 
1157 void KConfigGroup::markAsClean()
1158 {
1159     Q_ASSERT_X(isValid(), "KConfigGroup::markAsClean", "accessing an invalid group");
1160 
1161     config()->markAsClean();
1162 }
1163 
1164 KConfigGroup::AccessMode KConfigGroup::accessMode() const
1165 {
1166     Q_ASSERT_X(isValid(), "KConfigGroup::accessMode", "accessing an invalid group");
1167 
1168     return config()->accessMode();
1169 }
1170 
1171 bool KConfigGroup::hasGroupImpl(const QString &b) const
1172 {
1173     Q_ASSERT_X(isValid(), "KConfigGroup::hasGroupImpl", "accessing an invalid group");
1174 
1175     return config()->hasGroup(d->fullName(b));
1176 }
1177 
1178 void KConfigGroup::deleteGroupImpl(const QString &b, WriteConfigFlags flags)
1179 {
1180     Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroupImpl", "accessing an invalid group");
1181     Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroupImpl", "deleting from a read-only group");
1182 
1183     config()->deleteGroup(d->fullName(b), flags);
1184 }
1185 
1186 bool KConfigGroup::isGroupImmutableImpl(const QString &groupName) const
1187 {
1188     Q_ASSERT_X(isValid(), "KConfigGroup::isGroupImmutableImpl", "accessing an invalid group");
1189 
1190     if (!hasGroupImpl(groupName)) { // group doesn't exist yet
1191         return d->bImmutable; // child groups are immutable if the parent is immutable.
1192     }
1193 
1194     return config()->isGroupImmutable(d->fullName(groupName));
1195 }
1196 
1197 void KConfigGroup::copyTo(KConfigBase *other, WriteConfigFlags pFlags) const
1198 {
1199     Q_ASSERT_X(isValid(), "KConfigGroup::copyTo", "accessing an invalid group");
1200     Q_ASSERT(other != nullptr);
1201 
1202     if (KConfigGroup *otherGroup = dynamic_cast<KConfigGroup *>(other)) {
1203         config()->d_func()->copyGroup(d->fullName(), otherGroup->d->fullName(), otherGroup, pFlags);
1204     } else if (KConfig *otherConfig = dynamic_cast<KConfig *>(other)) {
1205         KConfigGroup newGroup = otherConfig->group(d->fullName());
1206         otherConfig->d_func()->copyGroup(d->fullName(), d->fullName(), &newGroup, pFlags);
1207     } else {
1208         Q_ASSERT_X(false, "KConfigGroup::copyTo", "unknown type of KConfigBase");
1209     }
1210 }
1211 
1212 void KConfigGroup::reparent(KConfigBase *parent, WriteConfigFlags pFlags)
1213 {
1214     Q_ASSERT_X(isValid(), "KConfigGroup::reparent", "accessing an invalid group");
1215     Q_ASSERT_X(!d->bConst, "KConfigGroup::reparent", "reparenting a read-only group");
1216     Q_ASSERT_X(!d->bImmutable, "KConfigGroup::reparent", "reparenting an immutable group");
1217     Q_ASSERT(parent != nullptr);
1218 
1219     KConfigGroup oldGroup(*this);
1220 
1221     d = KConfigGroupPrivate::create(parent, d->mName, false, false);
1222     oldGroup.copyTo(this, pFlags);
1223     oldGroup.deleteGroup(); // so that the entries with the old group name are deleted on sync
1224 }
1225 
1226 void KConfigGroup::moveValuesTo(const QList<const char *> &keys, KConfigGroup &other, WriteConfigFlags pFlags)
1227 {
1228     Q_ASSERT(isValid());
1229     Q_ASSERT(other.isValid());
1230 
1231     for (const auto key : keys) {
1232         const QString groupName = name();
1233         const auto entry = config()->d_ptr->lookupInternalEntry(groupName, key, KEntryMap::SearchLocalized);
1234 
1235         // Only write the entry if it is not null, if it is a global enry there is no point in moving it
1236         if (!entry.mValue.isNull() && !entry.bGlobal) {
1237             deleteEntry(key, pFlags);
1238             KEntryMap::EntryOptions options = KEntryMap::EntryOption::EntryDirty;
1239             if (entry.bDeleted) {
1240                 options |= KEntryMap::EntryDeleted;
1241             }
1242 
1243             if (entry.bExpand) {
1244                 options |= KEntryMap::EntryExpansion;
1245             }
1246 
1247             other.config()->d_ptr->setEntryData(other.name(), key, entry.mValue, options);
1248         }
1249     }
1250 }