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
12QDebug 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
19QDebug 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
28KEntryMapIterator KEntryMap::findExactEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags)
29{
30 const KEntryKeyView theKey(group, key, bool(flags & SearchLocalized), bool(flags & SearchDefaults));
31 return find(x: theKey);
32}
33
34KEntryMapIterator KEntryMap::findEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags)
35{
36 KEntryKeyView 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(x: theKey);
43 if (it != end()) {
44 return it;
45 }
46
47 theKey.bLocal = false;
48 }
49 return find(x: theKey);
50}
51
52KEntryMapConstIterator KEntryMap::constFindEntry(const QString &group, QAnyStringView key, SearchFlags flags) const
53{
54 KEntryKeyView 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 = find(x: theKey);
61 if (it != cend()) {
62 return it;
63 }
64
65 theKey.bLocal = false;
66 }
67
68 return find(x: theKey);
69}
70
71bool KEntryMap::setEntry(const QString &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, flags: 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(msg: "Internal KConfig error: cannot mark groups as deleted");
84 }
85 if (it == end()) {
86 insert_or_assign(k: k, obj&: e);
87 return true;
88 } else if (it->second == e) {
89 return false;
90 }
91
92 it->second = e;
93 return true;
94 }
95
96 if (it != end()) {
97 if (it->second.bImmutable) {
98 return false; // we cannot change this entry. Inherits group immutability.
99 }
100 k = it->first;
101 e = it->second;
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 == cend()) {
112 insert_or_assign(k: KEntryKey(group), obj: KEntry());
113 } else if (cit->second.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 // if the key is new and we want to delete it that's a no-op
149 if (e.bDeleted) {
150 return false;
151 }
152
153 // qDebug() << "inserting" << k << "=" << value;
154 insert_or_assign(k: k, obj&: e);
155 if (k.bDefault) {
156 k.bDefault = false;
157 // qDebug() << "also inserting" << k << "=" << value;
158 insert_or_assign(k: k, obj&: e);
159 }
160 // TODO check for presence of unlocalized key
161 return true;
162 }
163
164 // KEntry e2 = it->second;
165 if (options & EntryLocalized) {
166 // fast exit checks for cases where the existing entry is more specific
167 const KEntry &e2 = it->second;
168 if (e2.bLocalizedCountry && !e.bLocalizedCountry) {
169 // lang_COUNTRY > lang
170 return false;
171 }
172 }
173
174 if (it->second != e) {
175 // qDebug() << "changing" << k << "from" << it->second.mValue << "to" << value << e;
176 it->second = e;
177 if (k.bDefault) {
178 KEntryKey nonDefaultKey(k);
179 nonDefaultKey.bDefault = false;
180 insert_or_assign(k: nonDefaultKey, obj&: e);
181 }
182 if (!(options & EntryLocalized)) {
183 KEntryKey theKey(group, key, true, false);
184 // qDebug() << "non-localized entry, remove localized one:" << theKey;
185 erase(x: theKey);
186 if (k.bDefault) {
187 theKey.bDefault = true;
188 erase(x: theKey);
189 }
190 }
191 return true;
192 }
193
194 // qDebug() << k << "was already set to" << e.mValue;
195 if (!(options & EntryLocalized)) {
196 // qDebug() << "unchanged non-localized entry, remove localized one.";
197 KEntryKey theKey(group, key, true, false);
198 bool ret = false;
199 iterator cit = find(x: theKey);
200 if (cit != end()) {
201 erase(position: cit);
202 ret = true;
203 }
204 if (k.bDefault) {
205 theKey.bDefault = true;
206 iterator cit = find(x: theKey);
207 if (cit != end()) {
208 erase(position: cit);
209 return true;
210 }
211 }
212 return ret;
213 }
214
215 // qDebug() << "localized entry, unchanged, return false";
216 // When we are writing a default, we know that the non-
217 // default is the same as the default, so we can simply
218 // use the same branch.
219 return false;
220}
221
222QString KEntryMap::getEntry(const QString &group, QAnyStringView key, const QString &defaultValue, KEntryMap::SearchFlags flags, bool *expand) const
223{
224 const auto it = constFindEntry(group, key, flags);
225 QString theValue = defaultValue;
226
227 if (it != cend() && !it->second.bDeleted) {
228 if (!it->second.mValue.isNull()) {
229 const QByteArray data = it->second.mValue;
230 theValue = QString::fromUtf8(utf8: data.constData(), size: data.length());
231 if (expand) {
232 *expand = it->second.bExpand;
233 }
234 }
235 }
236
237 return theValue;
238}
239
240bool KEntryMap::hasEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags) const
241{
242 const auto it = constFindEntry(group, key, flags);
243 if (it == cend()) {
244 return false;
245 }
246 if (it->second.bDeleted) {
247 return false;
248 }
249 if (key.isNull()) { // looking for group marker
250 return it->second.mValue.isNull();
251 }
252 // if it->bReverted, we'll just return true; the real answer depends on lookup up with SearchDefaults, though.
253 return true;
254}
255
256bool KEntryMap::getEntryOption(const KEntryMapConstIterator &it, KEntryMap::EntryOption option) const
257{
258 if (it == cend()) {
259 return false;
260 }
261
262 switch (option) {
263 case EntryDirty:
264 return it->second.bDirty;
265 case EntryLocalized:
266 return it->first.bLocal;
267 case EntryGlobal:
268 return it->second.bGlobal;
269 case EntryImmutable:
270 return it->second.bImmutable;
271 case EntryDeleted:
272 return it->second.bDeleted;
273 case EntryExpansion:
274 return it->second.bExpand;
275 case EntryNotify:
276 return it->second.bNotify;
277 default:
278 return false;
279 }
280}
281
282void KEntryMap::setEntryOption(KEntryMapIterator it, KEntryMap::EntryOption option, bool bf)
283{
284 if (it == end()) {
285 return;
286 }
287
288 switch (option) {
289 case EntryDirty:
290 it->second.bDirty = bf;
291 return;
292 case EntryGlobal:
293 it->second.bGlobal = bf;
294 return;
295 case EntryImmutable:
296 it->second.bImmutable = bf;
297 return;
298 case EntryDeleted:
299 it->second.bDeleted = bf;
300 return;
301 case EntryExpansion:
302 it->second.bExpand = bf;
303 return;
304 case EntryNotify:
305 it->second.bNotify = bf;
306 return;
307 default:
308 return; // fall through
309 }
310}
311
312bool KEntryMap::revertEntry(const QString &group, QAnyStringView key, KEntryMap::EntryOptions options, KEntryMap::SearchFlags flags)
313{
314 Q_ASSERT((flags & KEntryMap::SearchDefaults) == 0);
315 iterator entry = findEntry(group, key, flags);
316 if (entry == end()) {
317 return false;
318 }
319
320 // qDebug() << "reverting" << entry->first << " = " << entry->mValue;
321 if (entry->second.bReverted) { // already done before
322 return false;
323 }
324
325 KEntryKey defaultKey(entry->first);
326 defaultKey.bDefault = true;
327 // qDebug() << "looking up default entry with key=" << defaultKey;
328 const auto defaultEntry = find(x: defaultKey);
329 if (defaultEntry != cend()) {
330 Q_ASSERT(defaultEntry->first.bDefault);
331 // qDebug() << "found, update entry";
332 entry->second = defaultEntry->second; // copy default value, for subsequent lookups
333 } else {
334 entry->second.mValue = QByteArray();
335 }
336 entry->second.bNotify = entry->second.bNotify || (options & EntryNotify);
337 entry->second.bDirty = true;
338 entry->second.bReverted = true; // skip it when writing out to disk
339
340 // qDebug() << "Here's what we have now:" << *this;
341 return true;
342}
343

source code of kconfig/src/core/kconfigdata.cpp