1 /*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
4 SPDX-FileCopyrightText: 1999-2000 Preston Brown <pbrown@kde.org>
5 SPDX-FileCopyrightText: 1996-2000 Matthias Kalle Dalheimer <kalle@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10 #include "kconfigdata_p.h"
11
operator <<(QDebug dbg,const KEntryKey & key)12 QDebug operator<<(QDebug dbg, const KEntryKey &key)
13 {
14 dbg.nospace() << "[" << key.mGroup << ", " << key.mKey << (key.bLocal ? " localized" : "") << (key.bDefault ? " default" : "") << (key.bRaw ? " raw" : "")
15 << "]";
16 return dbg.space();
17 }
18
operator <<(QDebug dbg,const KEntry & entry)19 QDebug operator<<(QDebug dbg, const KEntry &entry)
20 {
21 dbg.nospace() << "[" << entry.mValue << (entry.bDirty ? " dirty" : "") << (entry.bGlobal ? " global" : "")
22 << (entry.bOverridesGlobal ? " overrides global" : "") << (entry.bImmutable ? " immutable" : "") << (entry.bDeleted ? " deleted" : "")
23 << (entry.bReverted ? " reverted" : "") << (entry.bExpand ? " expand" : "") << "]";
24
25 return dbg.space();
26 }
27
findExactEntry(const QByteArray & group,const QByteArray & key,KEntryMap::SearchFlags flags)28 KEntryMapIterator KEntryMap::findExactEntry(const QByteArray &group, const QByteArray &key, KEntryMap::SearchFlags flags)
29 {
30 KEntryKey theKey(group, key, bool(flags & SearchLocalized), bool(flags & SearchDefaults));
31 return find(theKey);
32 }
33
findEntry(const QByteArray & group,const QByteArray & key,KEntryMap::SearchFlags flags)34 KEntryMapIterator KEntryMap::findEntry(const QByteArray &group, const QByteArray &key, KEntryMap::SearchFlags flags)
35 {
36 KEntryKey theKey(group, key, false, bool(flags & SearchDefaults));
37
38 // try the localized key first
39 if (flags & SearchLocalized) {
40 theKey.bLocal = true;
41
42 Iterator it = find(theKey);
43 if (it != end()) {
44 return it;
45 }
46
47 theKey.bLocal = false;
48 }
49 return find(theKey);
50 }
51
constFindEntry(const QByteArray & group,const QByteArray & key,SearchFlags flags) const52 KEntryMapConstIterator KEntryMap::constFindEntry(const QByteArray &group, const QByteArray &key, SearchFlags flags) const
53 {
54 KEntryKey theKey(group, key, false, bool(flags & SearchDefaults));
55
56 // try the localized key first
57 if (flags & SearchLocalized) {
58 theKey.bLocal = true;
59
60 auto it = constFind(theKey);
61 if (it != cend()) {
62 return it;
63 }
64
65 theKey.bLocal = false;
66 }
67
68 return constFind(theKey);
69 }
70
setEntry(const QByteArray & group,const QByteArray & key,const QByteArray & value,KEntryMap::EntryOptions options)71 bool KEntryMap::setEntry(const QByteArray &group, const QByteArray &key, const QByteArray &value, KEntryMap::EntryOptions options)
72 {
73 KEntryKey k;
74 KEntry e;
75 bool newKey = false;
76
77 const Iterator it = findExactEntry(group, key, SearchFlags(options >> 16));
78
79 if (key.isEmpty()) { // inserting a group marker
80 k.mGroup = group;
81 e.bImmutable = (options & EntryImmutable);
82 if (options & EntryDeleted) {
83 qWarning("Internal KConfig error: cannot mark groups as deleted");
84 }
85 if (it == end()) {
86 insert(k, e);
87 return true;
88 } else if (it.value() == e) {
89 return false;
90 }
91
92 it.value() = e;
93 return true;
94 }
95
96 if (it != end()) {
97 if (it->bImmutable) {
98 return false; // we cannot change this entry. Inherits group immutability.
99 }
100 k = it.key();
101 e = *it;
102 // qDebug() << "found existing entry for key" << k;
103 // If overridden entry is global and not default. And it's overridden by a non global
104 if (e.bGlobal && !(options & EntryGlobal) && !k.bDefault) {
105 e.bOverridesGlobal = true;
106 }
107 } else {
108 // make sure the group marker is in the map
109 KEntryMap const *that = this;
110 auto cit = that->constFindEntry(group);
111 if (cit == constEnd()) {
112 insert(KEntryKey(group), KEntry());
113 } else if (cit->bImmutable) {
114 return false; // this group is immutable, so we cannot change this entry.
115 }
116
117 k = KEntryKey(group, key);
118 newKey = true;
119 }
120
121 // set these here, since we may be changing the type of key from the one we found
122 k.bLocal = (options & EntryLocalized);
123 k.bDefault = (options & EntryDefault);
124 k.bRaw = (options & EntryRawKey);
125
126 e.mValue = value;
127 e.bDirty = e.bDirty || (options & EntryDirty);
128 e.bNotify = e.bNotify || (options & EntryNotify);
129
130 e.bGlobal = (options & EntryGlobal); // we can't use || here, because changes to entries in
131 // kdeglobals would be written to kdeglobals instead
132 // of the local config file, regardless of the globals flag
133 e.bImmutable = e.bImmutable || (options & EntryImmutable);
134 if (value.isNull()) {
135 e.bDeleted = e.bDeleted || (options & EntryDeleted);
136 } else {
137 e.bDeleted = false; // setting a value to a previously deleted entry
138 }
139 e.bExpand = (options & EntryExpansion);
140 e.bReverted = false;
141 if (options & EntryLocalized) {
142 e.bLocalizedCountry = (options & EntryLocalizedCountry);
143 } else {
144 e.bLocalizedCountry = false;
145 }
146
147 if (newKey) {
148 // qDebug() << "inserting" << k << "=" << value;
149 insert(k, e);
150 if (k.bDefault) {
151 k.bDefault = false;
152 // qDebug() << "also inserting" << k << "=" << value;
153 insert(k, e);
154 }
155 // TODO check for presence of unlocalized key
156 return true;
157 } else {
158 // KEntry e2 = it.value();
159 if (options & EntryLocalized) {
160 // fast exit checks for cases where the existing entry is more specific
161 const KEntry &e2 = it.value();
162 if (e2.bLocalizedCountry && !e.bLocalizedCountry) {
163 // lang_COUNTRY > lang
164 return false;
165 }
166 }
167 if (it.value() != e) {
168 // qDebug() << "changing" << k << "from" << it.value().mValue << "to" << value << e;
169 it.value() = e;
170 if (k.bDefault) {
171 KEntryKey nonDefaultKey(k);
172 nonDefaultKey.bDefault = false;
173 insert(nonDefaultKey, e);
174 }
175 if (!(options & EntryLocalized)) {
176 KEntryKey theKey(group, key, true, false);
177 // qDebug() << "non-localized entry, remove localized one:" << theKey;
178 remove(theKey);
179 if (k.bDefault) {
180 theKey.bDefault = true;
181 remove(theKey);
182 }
183 }
184 return true;
185 } else {
186 // qDebug() << k << "was already set to" << e.mValue;
187 if (!(options & EntryLocalized)) {
188 // qDebug() << "unchanged non-localized entry, remove localized one.";
189 KEntryKey theKey(group, key, true, false);
190 bool ret = false;
191 Iterator cit = find(theKey);
192 if (cit != end()) {
193 erase(cit);
194 ret = true;
195 }
196 if (k.bDefault) {
197 theKey.bDefault = true;
198 Iterator cit = find(theKey);
199 if (cit != end()) {
200 erase(cit);
201 return true;
202 }
203 }
204 return ret;
205 }
206 // qDebug() << "localized entry, unchanged, return false";
207 // When we are writing a default, we know that the non-
208 // default is the same as the default, so we can simply
209 // use the same branch.
210 return false;
211 }
212 }
213 }
214
getEntry(const QByteArray & group,const QByteArray & key,const QString & defaultValue,KEntryMap::SearchFlags flags,bool * expand) const215 QString KEntryMap::getEntry(const QByteArray &group, const QByteArray &key, const QString &defaultValue, KEntryMap::SearchFlags flags, bool *expand) const
216 {
217 const auto it = constFindEntry(group, key, flags);
218 QString theValue = defaultValue;
219
220 if (it != constEnd() && !it->bDeleted) {
221 if (!it->mValue.isNull()) {
222 const QByteArray data = it->mValue;
223 theValue = QString::fromUtf8(data.constData(), data.length());
224 if (expand) {
225 *expand = it->bExpand;
226 }
227 }
228 }
229
230 return theValue;
231 }
232
hasEntry(const QByteArray & group,const QByteArray & key,KEntryMap::SearchFlags flags) const233 bool KEntryMap::hasEntry(const QByteArray &group, const QByteArray &key, KEntryMap::SearchFlags flags) const
234 {
235 const auto it = constFindEntry(group, key, flags);
236 if (it == constEnd()) {
237 return false;
238 }
239 if (it->bDeleted) {
240 return false;
241 }
242 if (key.isNull()) { // looking for group marker
243 return it->mValue.isNull();
244 }
245 // if it->bReverted, we'll just return true; the real answer depends on lookup up with SearchDefaults, though.
246 return true;
247 }
248
getEntryOption(const KEntryMapConstIterator & it,KEntryMap::EntryOption option) const249 bool KEntryMap::getEntryOption(const KEntryMapConstIterator &it, KEntryMap::EntryOption option) const
250 {
251 if (it != constEnd()) {
252 switch (option) {
253 case EntryDirty:
254 return it->bDirty;
255 case EntryLocalized:
256 return it.key().bLocal;
257 case EntryGlobal:
258 return it->bGlobal;
259 case EntryImmutable:
260 return it->bImmutable;
261 case EntryDeleted:
262 return it->bDeleted;
263 case EntryExpansion:
264 return it->bExpand;
265 case EntryNotify:
266 return it->bNotify;
267 default:
268 break; // fall through
269 }
270 }
271
272 return false;
273 }
274
setEntryOption(KEntryMapIterator it,KEntryMap::EntryOption option,bool bf)275 void KEntryMap::setEntryOption(KEntryMapIterator it, KEntryMap::EntryOption option, bool bf)
276 {
277 if (it != end()) {
278 switch (option) {
279 case EntryDirty:
280 it->bDirty = bf;
281 break;
282 case EntryGlobal:
283 it->bGlobal = bf;
284 break;
285 case EntryImmutable:
286 it->bImmutable = bf;
287 break;
288 case EntryDeleted:
289 it->bDeleted = bf;
290 break;
291 case EntryExpansion:
292 it->bExpand = bf;
293 break;
294 case EntryNotify:
295 it->bNotify = bf;
296 break;
297 default:
298 break; // fall through
299 }
300 }
301 }
302
revertEntry(const QByteArray & group,const QByteArray & key,KEntryMap::EntryOptions options,KEntryMap::SearchFlags flags)303 bool KEntryMap::revertEntry(const QByteArray &group, const QByteArray &key, KEntryMap::EntryOptions options, KEntryMap::SearchFlags flags)
304 {
305 Q_ASSERT((flags & KEntryMap::SearchDefaults) == 0);
306 Iterator entry = findEntry(group, key, flags);
307 if (entry != end()) {
308 // qDebug() << "reverting" << entry.key() << " = " << entry->mValue;
309 if (entry->bReverted) { // already done before
310 return false;
311 }
312
313 KEntryKey defaultKey(entry.key());
314 defaultKey.bDefault = true;
315 // qDebug() << "looking up default entry with key=" << defaultKey;
316 const auto defaultEntry = constFind(defaultKey);
317 if (defaultEntry != constEnd()) {
318 Q_ASSERT(defaultEntry.key().bDefault);
319 // qDebug() << "found, update entry";
320 *entry = *defaultEntry; // copy default value, for subsequent lookups
321 } else {
322 entry->mValue = QByteArray();
323 }
324 entry->bNotify = entry->bNotify || (options & EntryNotify);
325 entry->bDirty = true;
326 entry->bReverted = true; // skip it when writing out to disk
327
328 // qDebug() << "Here's what we have now:" << *this;
329 return true;
330 }
331 return false;
332 }
333