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 }