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

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-2000 Preston Brown <pbrown@kde.org>
0005     SPDX-FileCopyrightText: 1996-2000 Matthias Kalle Dalheimer <kalle@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kconfigdata_p.h"
0011 
0012 QDebug operator<<(QDebug dbg, const KEntryKey &key)
0013 {
0014     dbg.nospace() << "[" << key.mGroup << ", " << key.mKey << (key.bLocal ? " localized" : "") << (key.bDefault ? " default" : "") << (key.bRaw ? " raw" : "")
0015                   << "]";
0016     return dbg.space();
0017 }
0018 
0019 QDebug operator<<(QDebug dbg, const KEntry &entry)
0020 {
0021     dbg.nospace() << "[" << entry.mValue << (entry.bDirty ? " dirty" : "") << (entry.bGlobal ? " global" : "")
0022                   << (entry.bOverridesGlobal ? " overrides global" : "") << (entry.bImmutable ? " immutable" : "") << (entry.bDeleted ? " deleted" : "")
0023                   << (entry.bReverted ? " reverted" : "") << (entry.bExpand ? " expand" : "") << "]";
0024 
0025     return dbg.space();
0026 }
0027 
0028 KEntryMapIterator KEntryMap::findExactEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags)
0029 {
0030     const KEntryKeyView theKey(group, key, bool(flags & SearchLocalized), bool(flags & SearchDefaults));
0031     return find(theKey);
0032 }
0033 
0034 KEntryMapIterator KEntryMap::findEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags)
0035 {
0036     KEntryKeyView theKey(group, key, false, bool(flags & SearchDefaults));
0037 
0038     // try the localized key first
0039     if (flags & SearchLocalized) {
0040         theKey.bLocal = true;
0041 
0042         iterator it = find(theKey);
0043         if (it != end()) {
0044             return it;
0045         }
0046 
0047         theKey.bLocal = false;
0048     }
0049     return find(theKey);
0050 }
0051 
0052 KEntryMapConstIterator KEntryMap::constFindEntry(const QString &group, QAnyStringView key, SearchFlags flags) const
0053 {
0054     KEntryKeyView theKey(group, key, false, bool(flags & SearchDefaults));
0055 
0056     // try the localized key first
0057     if (flags & SearchLocalized) {
0058         theKey.bLocal = true;
0059 
0060         auto it = find(theKey);
0061         if (it != cend()) {
0062             return it;
0063         }
0064 
0065         theKey.bLocal = false;
0066     }
0067 
0068     return find(theKey);
0069 }
0070 
0071 bool KEntryMap::setEntry(const QString &group, const QByteArray &key, const QByteArray &value, KEntryMap::EntryOptions options)
0072 {
0073     KEntryKey k;
0074     KEntry e;
0075     bool newKey = false;
0076 
0077     const iterator it = findExactEntry(group, key, SearchFlags(options >> 16));
0078 
0079     if (key.isEmpty()) { // inserting a group marker
0080         k.mGroup = group;
0081         e.bImmutable = (options & EntryImmutable);
0082         if (options & EntryDeleted) {
0083             qWarning("Internal KConfig error: cannot mark groups as deleted");
0084         }
0085         if (it == end()) {
0086             insert_or_assign(k, e);
0087             return true;
0088         } else if (it->second == e) {
0089             return false;
0090         }
0091 
0092         it->second = e;
0093         return true;
0094     }
0095 
0096     if (it != end()) {
0097         if (it->second.bImmutable) {
0098             return false; // we cannot change this entry. Inherits group immutability.
0099         }
0100         k = it->first;
0101         e = it->second;
0102         // qDebug() << "found existing entry for key" << k;
0103         // If overridden entry is global and not default. And it's overridden by a non global
0104         if (e.bGlobal && !(options & EntryGlobal) && !k.bDefault) {
0105             e.bOverridesGlobal = true;
0106         }
0107     } else {
0108         // make sure the group marker is in the map
0109         KEntryMap const *that = this;
0110         auto cit = that->constFindEntry(group);
0111         if (cit == cend()) {
0112             insert_or_assign(KEntryKey(group), KEntry());
0113         } else if (cit->second.bImmutable) {
0114             return false; // this group is immutable, so we cannot change this entry.
0115         }
0116 
0117         k = KEntryKey(group, key);
0118         newKey = true;
0119     }
0120 
0121     // set these here, since we may be changing the type of key from the one we found
0122     k.bLocal = (options & EntryLocalized);
0123     k.bDefault = (options & EntryDefault);
0124     k.bRaw = (options & EntryRawKey);
0125 
0126     e.mValue = value;
0127     e.bDirty = e.bDirty || (options & EntryDirty);
0128     e.bNotify = e.bNotify || (options & EntryNotify);
0129 
0130     e.bGlobal = (options & EntryGlobal); // we can't use || here, because changes to entries in
0131     // kdeglobals would be written to kdeglobals instead
0132     // of the local config file, regardless of the globals flag
0133     e.bImmutable = e.bImmutable || (options & EntryImmutable);
0134     if (value.isNull()) {
0135         e.bDeleted = e.bDeleted || (options & EntryDeleted);
0136     } else {
0137         e.bDeleted = false; // setting a value to a previously deleted entry
0138     }
0139     e.bExpand = (options & EntryExpansion);
0140     e.bReverted = false;
0141     if (options & EntryLocalized) {
0142         e.bLocalizedCountry = (options & EntryLocalizedCountry);
0143     } else {
0144         e.bLocalizedCountry = false;
0145     }
0146 
0147     if (newKey) {
0148         // qDebug() << "inserting" << k << "=" << value;
0149         insert_or_assign(k, e);
0150         if (k.bDefault) {
0151             k.bDefault = false;
0152             // qDebug() << "also inserting" << k << "=" << value;
0153             insert_or_assign(k, e);
0154         }
0155         // TODO check for presence of unlocalized key
0156         return true;
0157     }
0158 
0159     // KEntry e2 = it->second;
0160     if (options & EntryLocalized) {
0161         // fast exit checks for cases where the existing entry is more specific
0162         const KEntry &e2 = it->second;
0163         if (e2.bLocalizedCountry && !e.bLocalizedCountry) {
0164             // lang_COUNTRY > lang
0165             return false;
0166         }
0167     }
0168 
0169     if (it->second != e) {
0170         // qDebug() << "changing" << k << "from" << it->second.mValue << "to" << value << e;
0171         it->second = e;
0172         if (k.bDefault) {
0173             KEntryKey nonDefaultKey(k);
0174             nonDefaultKey.bDefault = false;
0175             insert_or_assign(nonDefaultKey, e);
0176         }
0177         if (!(options & EntryLocalized)) {
0178             KEntryKey theKey(group, key, true, false);
0179             // qDebug() << "non-localized entry, remove localized one:" << theKey;
0180             erase(theKey);
0181             if (k.bDefault) {
0182                 theKey.bDefault = true;
0183                 erase(theKey);
0184             }
0185         }
0186         return true;
0187     }
0188 
0189     // qDebug() << k << "was already set to" << e.mValue;
0190     if (!(options & EntryLocalized)) {
0191         // qDebug() << "unchanged non-localized entry, remove localized one.";
0192         KEntryKey theKey(group, key, true, false);
0193         bool ret = false;
0194         iterator cit = find(theKey);
0195         if (cit != end()) {
0196             erase(cit);
0197             ret = true;
0198         }
0199         if (k.bDefault) {
0200             theKey.bDefault = true;
0201             iterator cit = find(theKey);
0202             if (cit != end()) {
0203                 erase(cit);
0204                 return true;
0205             }
0206         }
0207         return ret;
0208     }
0209 
0210     // qDebug() << "localized entry, unchanged, return false";
0211     // When we are writing a default, we know that the non-
0212     // default is the same as the default, so we can simply
0213     // use the same branch.
0214     return false;
0215 }
0216 
0217 QString KEntryMap::getEntry(const QString &group, QAnyStringView key, const QString &defaultValue, KEntryMap::SearchFlags flags, bool *expand) const
0218 {
0219     const auto it = constFindEntry(group, key, flags);
0220     QString theValue = defaultValue;
0221 
0222     if (it != cend() && !it->second.bDeleted) {
0223         if (!it->second.mValue.isNull()) {
0224             const QByteArray data = it->second.mValue;
0225             theValue = QString::fromUtf8(data.constData(), data.length());
0226             if (expand) {
0227                 *expand = it->second.bExpand;
0228             }
0229         }
0230     }
0231 
0232     return theValue;
0233 }
0234 
0235 bool KEntryMap::hasEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags) const
0236 {
0237     const auto it = constFindEntry(group, key, flags);
0238     if (it == cend()) {
0239         return false;
0240     }
0241     if (it->second.bDeleted) {
0242         return false;
0243     }
0244     if (key.isNull()) { // looking for group marker
0245         return it->second.mValue.isNull();
0246     }
0247     // if it->bReverted, we'll just return true; the real answer depends on lookup up with SearchDefaults, though.
0248     return true;
0249 }
0250 
0251 bool KEntryMap::getEntryOption(const KEntryMapConstIterator &it, KEntryMap::EntryOption option) const
0252 {
0253     if (it == cend()) {
0254         return false;
0255     }
0256 
0257     switch (option) {
0258     case EntryDirty:
0259         return it->second.bDirty;
0260     case EntryLocalized:
0261         return it->first.bLocal;
0262     case EntryGlobal:
0263         return it->second.bGlobal;
0264     case EntryImmutable:
0265         return it->second.bImmutable;
0266     case EntryDeleted:
0267         return it->second.bDeleted;
0268     case EntryExpansion:
0269         return it->second.bExpand;
0270     case EntryNotify:
0271         return it->second.bNotify;
0272     default:
0273         return false;
0274     }
0275 }
0276 
0277 void KEntryMap::setEntryOption(KEntryMapIterator it, KEntryMap::EntryOption option, bool bf)
0278 {
0279     if (it == end()) {
0280         return;
0281     }
0282 
0283     switch (option) {
0284     case EntryDirty:
0285         it->second.bDirty = bf;
0286         return;
0287     case EntryGlobal:
0288         it->second.bGlobal = bf;
0289         return;
0290     case EntryImmutable:
0291         it->second.bImmutable = bf;
0292         return;
0293     case EntryDeleted:
0294         it->second.bDeleted = bf;
0295         return;
0296     case EntryExpansion:
0297         it->second.bExpand = bf;
0298         return;
0299     case EntryNotify:
0300         it->second.bNotify = bf;
0301         return;
0302     default:
0303         return; // fall through
0304     }
0305 }
0306 
0307 bool KEntryMap::revertEntry(const QString &group, QAnyStringView key, KEntryMap::EntryOptions options, KEntryMap::SearchFlags flags)
0308 {
0309     Q_ASSERT((flags & KEntryMap::SearchDefaults) == 0);
0310     iterator entry = findEntry(group, key, flags);
0311     if (entry == end()) {
0312         return false;
0313     }
0314 
0315     // qDebug() << "reverting" << entry->first << " = " << entry->mValue;
0316     if (entry->second.bReverted) { // already done before
0317         return false;
0318     }
0319 
0320     KEntryKey defaultKey(entry->first);
0321     defaultKey.bDefault = true;
0322     // qDebug() << "looking up default entry with key=" << defaultKey;
0323     const auto defaultEntry = find(defaultKey);
0324     if (defaultEntry != cend()) {
0325         Q_ASSERT(defaultEntry->first.bDefault);
0326         // qDebug() << "found, update entry";
0327         entry->second = defaultEntry->second; // copy default value, for subsequent lookups
0328     } else {
0329         entry->second.mValue = QByteArray();
0330     }
0331     entry->second.bNotify = entry->second.bNotify || (options & EntryNotify);
0332     entry->second.bDirty = true;
0333     entry->second.bReverted = true; // skip it when writing out to disk
0334 
0335     // qDebug() << "Here's what we have now:" << *this;
0336     return true;
0337 }