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

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