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 | |
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 | |
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 | |
28 | KEntryMapIterator 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 | |
34 | KEntryMapIterator 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 | |
52 | KEntryMapConstIterator 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 | |
71 | bool 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 | |
217 | QString 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 | |
235 | bool 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 | |
251 | bool 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 | |
277 | void 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 | |
307 | bool 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 | |