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