/src/kconfig/src/core/kconfig.cpp
Line | Count | Source |
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 Preston Brown <pbrown@kde.org> |
5 | | SPDX-FileCopyrightText: 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org> |
6 | | |
7 | | SPDX-License-Identifier: LGPL-2.0-or-later |
8 | | */ |
9 | | |
10 | | #include "kconfig.h" |
11 | | #include "kconfig_p.h" |
12 | | |
13 | | #include "config-kconfig.h" |
14 | | #include "dbussanitizer_p.h" |
15 | | #include "kconfig_core_log_settings.h" |
16 | | |
17 | | #include <fcntl.h> |
18 | | |
19 | | #include "kconfiggroup.h" |
20 | | |
21 | | #include <QBasicMutex> |
22 | | #include <QByteArray> |
23 | | #include <QCache> |
24 | | #include <QCoreApplication> |
25 | | #include <QDir> |
26 | | #include <QFile> |
27 | | #include <QLocale> |
28 | | #include <QMutexLocker> |
29 | | #include <QProcess> |
30 | | #include <QSet> |
31 | | #include <QThreadStorage> |
32 | | #include <QTimeZone> |
33 | | |
34 | | #include <algorithm> |
35 | | #include <iterator> |
36 | | #include <set> |
37 | | |
38 | | #if KCONFIG_USE_DBUS |
39 | | #include <QDBusConnection> |
40 | | #include <QDBusMessage> |
41 | | #include <QDBusMetaType> |
42 | | #endif |
43 | | |
44 | | #ifdef Q_OS_WIN |
45 | | #include "registry_win_p.h" |
46 | | #endif |
47 | | |
48 | | bool KConfigPrivate::mappingsRegistered = false; |
49 | | |
50 | | // For caching purposes |
51 | | static bool s_wasTestModeEnabled = false; |
52 | | |
53 | | Q_GLOBAL_STATIC(QStringList, s_globalFiles) // For caching purposes. |
54 | | static QBasicMutex s_globalFilesMutex; |
55 | | Q_GLOBAL_STATIC_WITH_ARGS(QString, sGlobalFileName, (QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/kdeglobals"))) |
56 | | |
57 | | using ParseCacheKey = std::pair<QStringList, QString>; |
58 | | using ParseCacheTimestamp = QList<qint64>; |
59 | | struct ParseCacheValue { |
60 | | KEntryMap entries; |
61 | | ParseCacheTimestamp timestamp; |
62 | | }; |
63 | | QThreadStorage<QCache<ParseCacheKey, ParseCacheValue>> sGlobalParse; |
64 | | |
65 | | #ifndef Q_OS_WIN |
66 | | static const Qt::CaseSensitivity sPathCaseSensitivity = Qt::CaseSensitive; |
67 | | #else |
68 | | static const Qt::CaseSensitivity sPathCaseSensitivity = Qt::CaseInsensitive; |
69 | | #endif |
70 | | |
71 | | static QString getDefaultLocaleName() |
72 | 121k | { |
73 | | #if defined(Q_OS_WIN) || defined(Q_OS_MAC) |
74 | | if (QLocale() == QLocale::system()) { |
75 | | // If the default locale hasn't been changed then |
76 | | // On Windows and Apple OSs, we cannot use QLocale::system() if an application-specific |
77 | | // language was set by kxmlgui because Qt ignores LANGUAGE on Windows and Apple OSs. |
78 | | if (const auto firstLanguage = qEnvironmentVariable("LANGUAGE").section(u':', 0, 0, QString::SectionSkipEmpty); !firstLanguage.isEmpty()) { |
79 | | return firstLanguage; |
80 | | } |
81 | | // Also prefer the configured display language over the system language |
82 | | if (const auto languages = QLocale::system().uiLanguages(); !languages.isEmpty()) { |
83 | | // uiLanguages() uses dashes as separator, but KConfig assumes underscores |
84 | | return languages.value(0).replace(u'-', u'_'); |
85 | | } |
86 | | } |
87 | | #endif |
88 | 121k | return QLocale().name(); |
89 | 121k | } |
90 | | |
91 | | KConfigPrivate::KConfigPrivate(KConfig::OpenFlags flags, |
92 | | QStandardPaths::StandardLocation resourceType, |
93 | | std::unique_ptr<KConfigIniBackendAbstractDevice> backend) |
94 | 121k | : openFlags(flags) |
95 | 121k | , resourceType(resourceType) |
96 | 121k | , mBackend(std::move(backend)) |
97 | 121k | , bDirty(false) |
98 | 121k | , bReadDefaults(false) |
99 | 121k | , bFileImmutable(false) |
100 | 121k | , bForceGlobal(false) |
101 | 121k | , bSuppressGlobal(false) |
102 | 121k | , configState(KConfigBase::NoAccess) |
103 | 121k | { |
104 | 121k | const bool isTestMode = QStandardPaths::isTestModeEnabled(); |
105 | | // If sGlobalFileName was initialised and testMode has been toggled, |
106 | | // sGlobalFileName may need to be updated to point to the correct kdeglobals file |
107 | 121k | if (sGlobalFileName.exists() && s_wasTestModeEnabled != isTestMode) { |
108 | 0 | s_wasTestModeEnabled = isTestMode; |
109 | 0 | *sGlobalFileName = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/kdeglobals"); |
110 | 0 | } |
111 | | |
112 | 121k | static QBasicAtomicInt use_etc_kderc = Q_BASIC_ATOMIC_INITIALIZER(-1); |
113 | 121k | if (use_etc_kderc.loadRelaxed() < 0) { |
114 | 15 | use_etc_kderc.storeRelaxed(!qEnvironmentVariableIsSet("KDE_SKIP_KDERC")); // for unit tests |
115 | 15 | } |
116 | 121k | if (use_etc_kderc.loadRelaxed()) { |
117 | 15 | etc_kderc = |
118 | | #ifdef Q_OS_WIN |
119 | | QFile::decodeName(qgetenv("WINDIR") + "/kde5rc"); |
120 | | #else |
121 | 15 | QStringLiteral("/etc/kde5rc"); |
122 | 15 | #endif |
123 | 15 | if (!QFileInfo(etc_kderc).isReadable()) { |
124 | 15 | use_etc_kderc.storeRelaxed(false); |
125 | 15 | etc_kderc.clear(); |
126 | 15 | } |
127 | 15 | } |
128 | | |
129 | 121k | setLocale(getDefaultLocaleName()); |
130 | 121k | } |
131 | | |
132 | | bool KConfigPrivate::lockLocal() |
133 | 1 | { |
134 | 1 | return mBackend.lock(); |
135 | 1 | } |
136 | | |
137 | | static bool isGroupOrSubGroupMatch(KEntryMapConstIterator entryMapIt, const QString &group) |
138 | 0 | { |
139 | 0 | const QString &entryGroup = entryMapIt->first.mGroup; |
140 | 0 | Q_ASSERT_X(entryGroup.startsWith(group), Q_FUNC_INFO, "Precondition"); |
141 | 0 | return entryGroup.size() == group.size() || entryGroup[group.size()] == QLatin1Char('\x1d'); |
142 | 0 | } |
143 | | |
144 | | void KConfigPrivate::copyGroup(const QString &source, const QString &destination, KConfigGroup *otherGroup, KConfigBase::WriteConfigFlags flags) const |
145 | 0 | { |
146 | 0 | KEntryMap &otherMap = otherGroup->config()->d_ptr->entryMap; |
147 | 0 | const bool sameName = (destination == source); |
148 | | |
149 | | // we keep this bool outside the for loop so that if |
150 | | // the group is empty, we don't end up marking the other config |
151 | | // as dirty erroneously |
152 | 0 | bool dirtied = false; |
153 | |
|
154 | 0 | entryMap.forEachEntryWhoseGroupStartsWith(source, [&source, &destination, flags, &otherMap, sameName, &dirtied](KEntryMapConstIterator entryMapIt) { |
155 | | // don't copy groups that start with the same prefix, but are not sub-groups |
156 | 0 | if (!isGroupOrSubGroupMatch(entryMapIt, source)) { |
157 | 0 | return; |
158 | 0 | } |
159 | | |
160 | 0 | KEntryKey newKey = entryMapIt->first; |
161 | |
|
162 | 0 | if (flags & KConfigBase::Localized) { |
163 | 0 | newKey.bLocal = true; |
164 | 0 | } |
165 | |
|
166 | 0 | if (!sameName) { |
167 | 0 | newKey.mGroup.replace(0, source.size(), destination); |
168 | 0 | } |
169 | |
|
170 | 0 | KEntry entry = entryMapIt->second; |
171 | 0 | dirtied = entry.bDirty = flags & KConfigBase::Persistent; |
172 | |
|
173 | 0 | if (flags & KConfigBase::Global) { |
174 | 0 | entry.bGlobal = true; |
175 | 0 | } |
176 | |
|
177 | 0 | if (flags & KConfigBase::Notify) { |
178 | 0 | entry.bNotify = true; |
179 | 0 | } |
180 | |
|
181 | 0 | otherMap[newKey] = entry; |
182 | 0 | }); |
183 | |
|
184 | 0 | if (dirtied) { |
185 | 0 | otherGroup->config()->d_ptr->bDirty = true; |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | | QString KConfigPrivate::expandString(const QString &value) |
190 | 0 | { |
191 | 0 | QString aValue = value; |
192 | | |
193 | | // check for environment variables and make necessary translations |
194 | 0 | int nDollarPos = aValue.indexOf(QLatin1Char('$')); |
195 | 0 | while (nDollarPos != -1 && nDollarPos + 1 < aValue.length()) { |
196 | | // there is at least one $ |
197 | 0 | if (aValue.at(nDollarPos + 1) != QLatin1Char('$')) { |
198 | 0 | int nEndPos = nDollarPos + 1; |
199 | | // the next character is not $ |
200 | 0 | QStringView aVarName; |
201 | 0 | if (aValue.at(nEndPos) == QLatin1Char('{')) { |
202 | 0 | while ((nEndPos < aValue.length()) && (aValue[nEndPos] != QLatin1Char('}'))) { |
203 | 0 | ++nEndPos; |
204 | 0 | } |
205 | 0 | ++nEndPos; |
206 | 0 | aVarName = QStringView(aValue).mid(nDollarPos + 2, nEndPos - nDollarPos - 3); |
207 | 0 | } else { |
208 | 0 | while (nEndPos < aValue.length() && (aValue[nEndPos].isNumber() || aValue[nEndPos].isLetter() || aValue[nEndPos] == QLatin1Char('_'))) { |
209 | 0 | ++nEndPos; |
210 | 0 | } |
211 | 0 | aVarName = QStringView(aValue).mid(nDollarPos + 1, nEndPos - nDollarPos - 1); |
212 | 0 | } |
213 | 0 | QString env; |
214 | 0 | if (!aVarName.isEmpty()) { |
215 | | #ifdef Q_OS_WIN |
216 | | if (aVarName == QLatin1String("HOME")) { |
217 | | env = QDir::homePath(); |
218 | | } else |
219 | | #endif |
220 | 0 | { |
221 | 0 | QByteArray pEnv = qgetenv(aVarName.toLatin1().constData()); |
222 | 0 | if (!pEnv.isEmpty()) { |
223 | 0 | env = QString::fromLocal8Bit(pEnv.constData()); |
224 | 0 | } else { |
225 | 0 | if (aVarName == QLatin1String("QT_DATA_HOME")) { |
226 | 0 | env = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); |
227 | 0 | } else if (aVarName == QLatin1String("QT_CONFIG_HOME")) { |
228 | 0 | env = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); |
229 | 0 | } else if (aVarName == QLatin1String("QT_CACHE_HOME")) { |
230 | 0 | env = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); |
231 | 0 | } |
232 | 0 | } |
233 | 0 | } |
234 | 0 | aValue.replace(nDollarPos, nEndPos - nDollarPos, env); |
235 | 0 | nDollarPos += env.length(); |
236 | 0 | } else { |
237 | 0 | aValue.remove(nDollarPos, nEndPos - nDollarPos); |
238 | 0 | } |
239 | 0 | } else { |
240 | | // remove one of the dollar signs |
241 | 0 | aValue.remove(nDollarPos, 1); |
242 | 0 | ++nDollarPos; |
243 | 0 | } |
244 | 0 | nDollarPos = aValue.indexOf(QLatin1Char('$'), nDollarPos); |
245 | 0 | } |
246 | |
|
247 | 0 | return aValue; |
248 | 0 | } |
249 | | |
250 | | KConfig::KConfig(const QString &file, OpenFlags mode, QStandardPaths::StandardLocation resourceType) |
251 | 121k | : d_ptr(new KConfigPrivate(mode, resourceType)) |
252 | 121k | { |
253 | 121k | d_ptr->changeFileName(file); // set the local file name |
254 | | |
255 | | // read initial information off disk |
256 | 121k | reparseConfiguration(); |
257 | 121k | } |
258 | | |
259 | | KConfig::KConfig(const std::shared_ptr<QIODevice> &device, OpenFlags mode) |
260 | 0 | : d_ptr(new KConfigPrivate(mode, |
261 | 0 | QStandardPaths::StandardLocation::GenericConfigLocation // a default location type |
262 | 0 | , |
263 | 0 | std::make_unique<KConfigIniBackendQIODevice>(device))) |
264 | 0 | { |
265 | 0 | d_ptr->configState = d_ptr->mBackend.accessMode(); |
266 | | |
267 | | // read initial information off device |
268 | 0 | reparseConfiguration(); |
269 | 0 | } |
270 | | |
271 | | #if KCONFIGCORE_BUILD_DEPRECATED_SINCE(6, 3) |
272 | | KConfig::KConfig(const QString &file, const QString &backend, QStandardPaths::StandardLocation resourceType) |
273 | 0 | : d_ptr(new KConfigPrivate(SimpleConfig, resourceType)) |
274 | 0 | { |
275 | 0 | Q_UNUSED(backend); |
276 | 0 | d_ptr->changeFileName(file); // set the local file name |
277 | | |
278 | | // read initial information off disk |
279 | 0 | reparseConfiguration(); |
280 | 0 | } |
281 | | #endif |
282 | | |
283 | | KConfig::KConfig(KConfigPrivate &d) |
284 | 0 | : d_ptr(&d) |
285 | 0 | { |
286 | 0 | } |
287 | | |
288 | | KConfig::~KConfig() |
289 | 121k | { |
290 | 121k | Q_D(KConfig); |
291 | 121k | if (d->bDirty) { |
292 | 0 | sync(); |
293 | 0 | } |
294 | 121k | delete d; |
295 | 121k | } |
296 | | |
297 | | static bool isNonDeletedKey(KEntryMapConstIterator entryMapIt) |
298 | 0 | { |
299 | 0 | return !entryMapIt->first.mKey.isNull() && !entryMapIt->second.bDeleted; |
300 | 0 | } |
301 | | // is a key without default values and not deleted |
302 | | static bool isSetKey(KEntryMapConstIterator entryMapIt) |
303 | 0 | { |
304 | 0 | return !entryMapIt->first.bDefault && !entryMapIt->second.bDeleted; |
305 | 0 | } |
306 | | |
307 | | static int findFirstGroupEndPos(const QString &groupFullName, int from = 0) |
308 | 0 | { |
309 | 0 | const auto index = groupFullName.indexOf(QLatin1Char('\x1d'), from); |
310 | 0 | return index == -1 ? groupFullName.size() : index; |
311 | 0 | } |
312 | | |
313 | | static QStringList stringListFromStringViewCollection(const QSet<QStringView> &source) |
314 | 0 | { |
315 | 0 | QStringList list; |
316 | 0 | list.reserve(source.size()); |
317 | 0 | std::transform(source.cbegin(), source.cend(), std::back_inserter(list), [](QStringView view) { |
318 | 0 | return view.toString(); |
319 | 0 | }); |
320 | 0 | return list; |
321 | 0 | } |
322 | | |
323 | | QStringList KConfig::groupList() const |
324 | 0 | { |
325 | 0 | Q_D(const KConfig); |
326 | 0 | QSet<QStringView> groups; |
327 | |
|
328 | 0 | for (auto entryMapIt = d->entryMap.cbegin(); entryMapIt != d->entryMap.cend(); ++entryMapIt) { |
329 | 0 | const QString &group = entryMapIt->first.mGroup; |
330 | 0 | if (isNonDeletedKey(entryMapIt) && !group.isEmpty() && group != QStringLiteral("<default>") && group != QStringLiteral("$Version")) { |
331 | 0 | groups.insert(QStringView(group).left(findFirstGroupEndPos(group))); |
332 | 0 | } |
333 | 0 | } |
334 | |
|
335 | 0 | return stringListFromStringViewCollection(groups); |
336 | 0 | } |
337 | | |
338 | | QStringList KConfigPrivate::groupList(const QString &groupName) const |
339 | 0 | { |
340 | 0 | const QString theGroup = groupName + QLatin1Char('\x1d'); |
341 | 0 | QSet<QStringView> groups; |
342 | |
|
343 | 0 | entryMap.forEachEntryWhoseGroupStartsWith(theGroup, [&theGroup, &groups](KEntryMapConstIterator entryMapIt) { |
344 | 0 | if (isNonDeletedKey(entryMapIt)) { |
345 | 0 | const QString &entryGroup = entryMapIt->first.mGroup; |
346 | 0 | const auto subgroupStartPos = theGroup.size(); |
347 | 0 | const auto subgroupEndPos = findFirstGroupEndPos(entryGroup, subgroupStartPos); |
348 | 0 | groups.insert(QStringView(entryGroup).mid(subgroupStartPos, subgroupEndPos - subgroupStartPos)); |
349 | 0 | } |
350 | 0 | }); |
351 | |
|
352 | 0 | return stringListFromStringViewCollection(groups); |
353 | 0 | } |
354 | | |
355 | | /// Returns @p parentGroup itself, all its subgroups, subsubgroups, and so on, including deleted groups. |
356 | | QSet<QString> KConfigPrivate::allSubGroups(const QString &parentGroup) const |
357 | 0 | { |
358 | 0 | QSet<QString> groups; |
359 | |
|
360 | 0 | entryMap.forEachEntryWhoseGroupStartsWith(parentGroup, [&parentGroup, &groups](KEntryMapConstIterator entryMapIt) { |
361 | 0 | const KEntryKey &key = entryMapIt->first; |
362 | 0 | if (key.mKey.isNull() && isGroupOrSubGroupMatch(entryMapIt, parentGroup)) { |
363 | 0 | groups << key.mGroup; |
364 | 0 | } |
365 | 0 | }); |
366 | |
|
367 | 0 | return groups; |
368 | 0 | } |
369 | | |
370 | | bool KConfigPrivate::hasNonDeletedEntries(const QString &group) const |
371 | 0 | { |
372 | 0 | return entryMap.anyEntryWhoseGroupStartsWith(group, [&group](KEntryMapConstIterator entryMapIt) { |
373 | 0 | return isGroupOrSubGroupMatch(entryMapIt, group) && isNonDeletedKey(entryMapIt); |
374 | 0 | }); |
375 | 0 | } |
376 | | |
377 | | QList<QByteArray> KConfigPrivate::keyListImpl(const QString &theGroup) const |
378 | 0 | { |
379 | 0 | std::set<QByteArray> tmp; // unique set, sorted for unittests |
380 | |
|
381 | 0 | entryMap.forEachEntryOfGroup(theGroup, [&tmp](KEntryMapConstIterator it) { |
382 | 0 | if (isNonDeletedKey(it)) { |
383 | 0 | tmp.insert(it->first.mKey); |
384 | 0 | } |
385 | 0 | }); |
386 | |
|
387 | 0 | return QList<QByteArray>(tmp.begin(), tmp.end()); |
388 | 0 | } |
389 | | |
390 | | QStringList KConfigPrivate::usedKeyList(const QString &theGroup) const |
391 | 0 | { |
392 | 0 | std::set<QString> tmp; // unique set, sorting as side-effect |
393 | |
|
394 | 0 | entryMap.forEachEntryOfGroup(theGroup, [&tmp](KEntryMapConstIterator it) { |
395 | | // leave the default values and deleted entries out, same as KConfig::entryMap() |
396 | 0 | if (isSetKey(it)) { |
397 | 0 | const QString key = QString::fromUtf8(it->first.mKey); |
398 | 0 | tmp.insert(key); |
399 | 0 | } |
400 | 0 | }); |
401 | |
|
402 | 0 | return QStringList(tmp.begin(), tmp.end()); |
403 | 0 | } |
404 | | |
405 | | QMap<QString, QString> KConfig::entryMap(const QString &aGroup) const |
406 | 0 | { |
407 | 0 | Q_D(const KConfig); |
408 | 0 | QMap<QString, QString> theMap; |
409 | 0 | const QString theGroup = aGroup.isEmpty() ? QStringLiteral("<default>") : aGroup; |
410 | |
|
411 | 0 | d->entryMap.forEachEntryOfGroup(theGroup, [&theMap](KEntryMapConstIterator it) { |
412 | | // leave the default values and deleted entries out |
413 | 0 | if (isSetKey(it)) { |
414 | 0 | const QString key = QString::fromUtf8(it->first.mKey.constData()); |
415 | | // the localized entry should come first, so don't overwrite it |
416 | | // with the non-localized entry |
417 | 0 | if (!theMap.contains(key)) { |
418 | 0 | if (it->second.bExpand) { |
419 | 0 | theMap.insert(key, KConfigPrivate::expandString(QString::fromUtf8(it->second.mValue.constData()))); |
420 | 0 | } else { |
421 | 0 | theMap.insert(key, QString::fromUtf8(it->second.mValue.constData())); |
422 | 0 | } |
423 | 0 | } |
424 | 0 | } |
425 | 0 | }); |
426 | |
|
427 | 0 | return theMap; |
428 | 0 | } |
429 | | |
430 | | bool KConfig::sync() |
431 | 121k | { |
432 | 121k | Q_D(KConfig); |
433 | | |
434 | 121k | if (isImmutable() || !d->mBackend.isWritable()) { |
435 | | // can't write to an immutable or anonymous file. |
436 | 0 | return false; |
437 | 0 | } |
438 | | |
439 | 121k | QHash<QString, QByteArrayList> notifyGroupsLocal; |
440 | 121k | QHash<QString, QByteArrayList> notifyGroupsGlobal; |
441 | | |
442 | 121k | if (d->bDirty) { |
443 | 1 | const QByteArray utf8Locale(locale().toUtf8()); |
444 | | |
445 | | // Create the containing dir, maybe it wasn't there |
446 | 1 | d->mBackend.createEnclosing(); |
447 | | |
448 | | // lock the local file |
449 | 1 | if (d->configState == ReadWrite && !d->lockLocal()) { |
450 | 0 | qCWarning(KCONFIG_CORE_LOG) << "Couldn't lock local file:" << d->mBackend.backingDevicePath(); |
451 | 0 | return false; |
452 | 0 | } |
453 | | |
454 | | // Rewrite global/local config only if there is a dirty entry in it. |
455 | 1 | bool writeGlobals = false; |
456 | 1 | bool writeLocals = false; |
457 | | |
458 | 2 | for (const auto &[key, e] : d->entryMap) { |
459 | 2 | if (e.bDirty) { |
460 | 1 | if (e.bGlobal) { |
461 | 0 | writeGlobals = true; |
462 | 0 | if (e.bNotify) { |
463 | 0 | notifyGroupsGlobal[key.mGroup] << key.mKey; |
464 | 0 | } |
465 | 1 | } else { |
466 | 1 | writeLocals = true; |
467 | 1 | if (e.bNotify) { |
468 | 0 | notifyGroupsLocal[key.mGroup] << key.mKey; |
469 | 0 | } |
470 | 1 | } |
471 | 1 | } |
472 | 2 | } |
473 | | |
474 | 1 | d->bDirty = false; // will revert to true if a config write fails |
475 | | |
476 | 1 | if (d->wantGlobals() && writeGlobals) { |
477 | 0 | KConfigIniBackend tmp(std::make_unique<KConfigIniBackendPathDevice>(*sGlobalFileName)); |
478 | 0 | if (d->configState == ReadWrite && !tmp.lock()) { |
479 | 0 | qCWarning(KCONFIG_CORE_LOG) << "Couldn't lock global file:" << d->mBackend.backingDevicePath(); |
480 | | |
481 | | // unlock the local config if we're returning early |
482 | 0 | if (d->mBackend.isLocked()) { |
483 | 0 | d->mBackend.unlock(); |
484 | 0 | } |
485 | |
|
486 | 0 | d->bDirty = true; |
487 | 0 | return false; |
488 | 0 | } |
489 | 0 | if (!tmp.writeConfig(utf8Locale, d->entryMap, KConfigIniBackend::WriteGlobal)) { |
490 | 0 | d->bDirty = true; |
491 | 0 | } |
492 | 0 | if (tmp.isLocked()) { |
493 | 0 | tmp.unlock(); |
494 | 0 | } |
495 | 0 | } |
496 | | |
497 | 1 | if (writeLocals) { |
498 | 1 | if (!d->mBackend.writeConfig(utf8Locale, d->entryMap, KConfigIniBackend::WriteOptions())) { |
499 | 0 | qCWarning(KCONFIG_CORE_LOG) << "Couldn't write to config:" << d->mBackend.backingDevicePath(); |
500 | 0 | d->bDirty = true; |
501 | 0 | } |
502 | 1 | } |
503 | 1 | if (d->mBackend.isLocked()) { |
504 | 1 | d->mBackend.unlock(); |
505 | 1 | } |
506 | 1 | } |
507 | | |
508 | | // Notifying absolute paths is not supported and also makes no sense. |
509 | 121k | const bool isAbsolutePath = !name().isEmpty() && name().at(0) == QLatin1Char('/'); |
510 | 121k | if (!notifyGroupsLocal.isEmpty() && !isAbsolutePath) { |
511 | 0 | d->notifyClients(notifyGroupsLocal, kconfigDBusSanitizePath(QLatin1Char('/') + name())); |
512 | 0 | } |
513 | 121k | if (!notifyGroupsGlobal.isEmpty()) { |
514 | 0 | d->notifyClients(notifyGroupsGlobal, QStringLiteral("/kdeglobals")); |
515 | 0 | } |
516 | | |
517 | 121k | return !d->bDirty; |
518 | 121k | } |
519 | | |
520 | | void KConfigPrivate::notifyClients(const QHash<QString, QByteArrayList> &changes, const QString &path) |
521 | 0 | { |
522 | | #if KCONFIG_USE_DBUS |
523 | | qDBusRegisterMetaType<QByteArrayList>(); |
524 | | |
525 | | qDBusRegisterMetaType<QHash<QString, QByteArrayList>>(); |
526 | | |
527 | | QDBusMessage message = QDBusMessage::createSignal(path, QStringLiteral("org.kde.kconfig.notify"), QStringLiteral("ConfigChanged")); |
528 | | message.setArguments({QVariant::fromValue(changes)}); |
529 | | QDBusConnection::sessionBus().send(message); |
530 | | #else |
531 | 0 | Q_UNUSED(changes) |
532 | 0 | Q_UNUSED(path) |
533 | 0 | #endif |
534 | 0 | } |
535 | | |
536 | | void KConfig::markAsClean() |
537 | 0 | { |
538 | 0 | Q_D(KConfig); |
539 | 0 | d->bDirty = false; |
540 | | |
541 | | // clear any dirty flags that entries might have set |
542 | 0 | for (auto &[_, entry] : d->entryMap) { |
543 | 0 | entry.bDirty = false; |
544 | 0 | entry.bNotify = false; |
545 | 0 | } |
546 | 0 | } |
547 | | |
548 | | bool KConfig::isDirty() const |
549 | 0 | { |
550 | 0 | Q_D(const KConfig); |
551 | 0 | return d->bDirty; |
552 | 0 | } |
553 | | |
554 | | void KConfig::checkUpdate(const QString &id, const QString &updateFile) |
555 | 0 | { |
556 | 0 | const KConfigGroup cg(this, QStringLiteral("$Version")); |
557 | 0 | const QString cfg_id = updateFile + QLatin1Char(':') + id; |
558 | 0 | const QStringList ids = cg.readEntry("update_info", QStringList()); |
559 | 0 | if (!ids.contains(cfg_id)) { |
560 | 0 | QProcess::execute(QStringLiteral(KCONF_UPDATE_INSTALL_LOCATION), QStringList{QStringLiteral("--check"), updateFile}); |
561 | 0 | reparseConfiguration(); |
562 | 0 | } |
563 | 0 | } |
564 | | |
565 | | KConfig *KConfig::copyTo(const QString &file, KConfig *config) const |
566 | 0 | { |
567 | 0 | Q_D(const KConfig); |
568 | 0 | if (!config) { |
569 | 0 | config = new KConfig(QString(), SimpleConfig, d->resourceType); |
570 | 0 | } |
571 | 0 | config->d_func()->changeFileName(file); |
572 | 0 | config->d_func()->entryMap = d->entryMap; |
573 | 0 | config->d_func()->bFileImmutable = false; |
574 | |
|
575 | 0 | for (auto &[_, entry] : config->d_func()->entryMap) { |
576 | 0 | entry.bDirty = true; |
577 | 0 | } |
578 | 0 | config->d_ptr->bDirty = true; |
579 | |
|
580 | 0 | return config; |
581 | 0 | } |
582 | | |
583 | | void KConfig::copyFrom(const KConfig &config) const |
584 | 0 | { |
585 | 0 | Q_D(const KConfig); |
586 | 0 | d_ptr->entryMap = config.d_func()->entryMap; |
587 | 0 | d_ptr->bFileImmutable = false; |
588 | |
|
589 | 0 | for (auto &[_, entry] : d_ptr->entryMap) { |
590 | 0 | entry.bDirty = true; |
591 | 0 | } |
592 | 0 | d_ptr->bDirty = true; |
593 | 0 | } |
594 | | |
595 | | // TODO KF7 remove, expose QIODevice instead |
596 | | // have a separate static funtion for relative filename config files |
597 | | QString KConfig::name() const |
598 | 432k | { |
599 | 432k | Q_D(const KConfig); |
600 | 432k | return d->fileName; |
601 | 432k | } |
602 | | |
603 | | KConfig::OpenFlags KConfig::openFlags() const |
604 | 0 | { |
605 | 0 | Q_D(const KConfig); |
606 | 0 | return d->openFlags; |
607 | 0 | } |
608 | | |
609 | | struct KConfigStaticData { |
610 | | QString globalMainConfigName; |
611 | | // Keep a copy so we can use it in global dtors, after qApp is gone |
612 | | QStringList appArgs; |
613 | | }; |
614 | | Q_GLOBAL_STATIC(KConfigStaticData, globalData) |
615 | | static QBasicMutex s_globalDataMutex; |
616 | | |
617 | | void KConfig::setMainConfigName(const QString &str) |
618 | 0 | { |
619 | 0 | QMutexLocker locker(&s_globalDataMutex); |
620 | 0 | globalData()->globalMainConfigName = str; |
621 | 0 | } |
622 | | |
623 | | QString KConfig::mainConfigName() |
624 | 135k | { |
625 | 135k | QMutexLocker locker(&s_globalDataMutex); |
626 | 135k | KConfigStaticData *data = globalData(); |
627 | 135k | if (data->appArgs.isEmpty()) { |
628 | 135k | data->appArgs = QCoreApplication::arguments(); |
629 | 135k | } |
630 | | |
631 | | // --config on the command line overrides everything else |
632 | 135k | const QStringList args = data->appArgs; |
633 | 135k | for (int i = 1; i < args.count(); ++i) { |
634 | 0 | if (args.at(i) == QLatin1String("--config") && i < args.count() - 1) { |
635 | 0 | return args.at(i + 1); |
636 | 0 | } |
637 | 0 | } |
638 | 135k | const QString globalName = data->globalMainConfigName; |
639 | 135k | if (!globalName.isEmpty()) { |
640 | 0 | return globalName; |
641 | 0 | } |
642 | | |
643 | 135k | QString appName = QCoreApplication::applicationName(); |
644 | 135k | return appName + QLatin1String("rc"); |
645 | 135k | } |
646 | | |
647 | | void KConfigPrivate::changeFileName(const QString &name) |
648 | 121k | { |
649 | 121k | fileName = name; |
650 | | |
651 | 121k | QString file; |
652 | 121k | if (name.isEmpty()) { |
653 | 0 | if (wantDefaults()) { // accessing default app-specific config "appnamerc" |
654 | 0 | fileName = KConfig::mainConfigName(); |
655 | 0 | file = QStandardPaths::writableLocation(resourceType) + QLatin1Char('/') + fileName; |
656 | 0 | } else if (wantGlobals()) { // accessing "kdeglobals" by specifying no filename and NoCascade - XXX used anywhere? |
657 | 0 | resourceType = QStandardPaths::GenericConfigLocation; |
658 | 0 | fileName = QStringLiteral("kdeglobals"); |
659 | 0 | file = *sGlobalFileName; |
660 | 0 | } else { |
661 | | // anonymous config |
662 | 0 | openFlags = KConfig::SimpleConfig; |
663 | 0 | return; |
664 | 0 | } |
665 | 121k | } else if (QDir::isAbsolutePath(fileName)) { |
666 | 0 | fileName = QFileInfo(fileName).canonicalFilePath(); |
667 | 0 | if (fileName.isEmpty()) { // file doesn't exist (yet) |
668 | 0 | fileName = name; |
669 | 0 | } |
670 | 0 | file = fileName; |
671 | 121k | } else { |
672 | 121k | file = QStandardPaths::writableLocation(resourceType) + QLatin1Char('/') + fileName; |
673 | 121k | } |
674 | | |
675 | 121k | Q_ASSERT(!file.isEmpty()); |
676 | | |
677 | 121k | bSuppressGlobal = (file.compare(*sGlobalFileName, sPathCaseSensitivity) == 0); |
678 | | |
679 | 121k | mBackend.setDeviceInterface(std::make_unique<KConfigIniBackendPathDevice>(file)); |
680 | | |
681 | 121k | configState = mBackend.accessMode(); |
682 | 121k | } |
683 | | |
684 | | void KConfig::reparseConfiguration() |
685 | 160k | { |
686 | 160k | Q_D(KConfig); |
687 | 160k | if (!d->mBackend.hasOpenableDeviceInterface()) { |
688 | 0 | return; |
689 | 0 | } |
690 | | |
691 | | // Don't lose pending changes |
692 | 160k | if (!d->isReadOnly() && d->bDirty) { |
693 | 0 | sync(); |
694 | 0 | } |
695 | | |
696 | 160k | d->entryMap.clear(); |
697 | | |
698 | 160k | d->bFileImmutable = false; |
699 | | |
700 | 160k | { |
701 | 160k | QMutexLocker locker(&s_globalFilesMutex); |
702 | 160k | s_globalFiles()->clear(); |
703 | 160k | } |
704 | | |
705 | | // Parse all desired files from the least to the most specific. |
706 | 160k | if (d->wantGlobals()) { |
707 | 160k | d->parseGlobalFiles(); |
708 | 160k | } |
709 | | |
710 | | #ifdef Q_OS_WIN |
711 | | // Parse the windows registry defaults if desired |
712 | | if (d->openFlags & ~KConfig::SimpleConfig) { |
713 | | d->parseWindowsDefaults(); |
714 | | } |
715 | | #endif |
716 | | |
717 | 160k | d->parseConfigFiles(); |
718 | 160k | } |
719 | | |
720 | | QStringList KConfigPrivate::getGlobalFiles() const |
721 | 160k | { |
722 | 160k | QMutexLocker locker(&s_globalFilesMutex); |
723 | 160k | if (s_globalFiles()->isEmpty()) { |
724 | 160k | const QStringList paths1 = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("kdeglobals")); |
725 | 160k | const QStringList paths2 = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("system.kdeglobals")); |
726 | | |
727 | 160k | const bool useEtcKderc = !etc_kderc.isEmpty(); |
728 | 160k | s_globalFiles()->reserve(paths1.size() + paths2.size() + (useEtcKderc ? 1 : 0)); |
729 | | |
730 | 160k | for (const QString &dir1 : paths1) { |
731 | 0 | s_globalFiles()->push_front(dir1); |
732 | 0 | } |
733 | 160k | for (const QString &dir2 : paths2) { |
734 | 0 | s_globalFiles()->push_front(dir2); |
735 | 0 | } |
736 | | |
737 | 160k | if (useEtcKderc) { |
738 | 0 | s_globalFiles()->push_front(etc_kderc); |
739 | 0 | } |
740 | 160k | } |
741 | | |
742 | 160k | return *s_globalFiles(); |
743 | 160k | } |
744 | | |
745 | | void KConfigPrivate::parseGlobalFiles() |
746 | 160k | { |
747 | 160k | const QStringList globalFiles = getGlobalFiles(); |
748 | | // qDebug() << "parsing global files" << globalFiles; |
749 | | |
750 | 160k | Q_ASSERT(entryMap.empty()); |
751 | | |
752 | 160k | ParseCacheTimestamp timestamp; |
753 | 160k | timestamp.reserve(globalFiles.count()); |
754 | 160k | for (const auto &file : globalFiles) { |
755 | 0 | timestamp << QFileInfo(file).lastModified(QTimeZone::UTC).toMSecsSinceEpoch(); |
756 | 0 | } |
757 | | |
758 | 160k | const ParseCacheKey key = {globalFiles, locale}; |
759 | 160k | auto data = sGlobalParse.localData().object(key); |
760 | 160k | if (data) { |
761 | 38.8k | if (data->timestamp != timestamp) { |
762 | 0 | data = nullptr; |
763 | 38.8k | } else { |
764 | 38.8k | entryMap = data->entries; |
765 | 38.8k | return; |
766 | 38.8k | } |
767 | 38.8k | } |
768 | | |
769 | 121k | const QByteArray utf8Locale = locale.toUtf8(); |
770 | 121k | for (const QString &file : globalFiles) { |
771 | 0 | KConfigIniBackend::ParseOptions parseOpts = KConfigIniBackend::ParseGlobal | KConfigIniBackend::ParseExpansions; |
772 | |
|
773 | 0 | if (file.compare(*sGlobalFileName, sPathCaseSensitivity) != 0) { |
774 | 0 | parseOpts |= KConfigIniBackend::ParseDefaults; |
775 | 0 | } |
776 | |
|
777 | 0 | KConfigIniBackend backend(std::make_unique<KConfigIniBackendPathDevice>(file)); |
778 | 0 | if (backend.parseConfig(utf8Locale, entryMap, parseOpts) == KConfigIniBackend::ParseImmutable) { |
779 | 0 | break; |
780 | 0 | } |
781 | 0 | } |
782 | 121k | sGlobalParse.localData().insert(key, new ParseCacheValue({entryMap, timestamp})); |
783 | 121k | } |
784 | | |
785 | | #ifdef Q_OS_WIN |
786 | | void KConfigPrivate::parseWindowsDefaults() |
787 | | { |
788 | | if (QCoreApplication::organizationName().isEmpty() || fileName.isEmpty() || QFileInfo(fileName).isAbsolute()) { |
789 | | return; |
790 | | } |
791 | | const QString registryKey = QCoreApplication::organizationName() + u'/' + (fileName.endsWith(QStringLiteral("rc")) ? fileName.chopped(2) : fileName); |
792 | | WindowsRegistry::parse(registryKey, entryMap); |
793 | | } |
794 | | #endif |
795 | | |
796 | | void KConfigPrivate::parseConfigFiles() |
797 | 160k | { |
798 | | // can only read the file if there is a backend and a file name |
799 | 160k | if (!mBackend.hasOpenableDeviceInterface()) { |
800 | 0 | return; |
801 | 0 | } |
802 | | |
803 | 160k | const auto backingDevicePath = mBackend.backingDevicePath(); |
804 | 160k | bFileImmutable = false; |
805 | | |
806 | 160k | QList<QString> files; |
807 | 160k | if (wantDefaults()) { |
808 | 160k | if (bSuppressGlobal) { |
809 | 0 | files = getGlobalFiles(); |
810 | 160k | } else { |
811 | 160k | if (QDir::isAbsolutePath(fileName)) { |
812 | 0 | const QString canonicalFile = QFileInfo(backingDevicePath).canonicalFilePath(); |
813 | 0 | if (!canonicalFile.isEmpty()) { // empty if it doesn't exist |
814 | 0 | files << canonicalFile; |
815 | 0 | } |
816 | 160k | } else { |
817 | 160k | const QStringList localFilesPath = QStandardPaths::locateAll(resourceType, fileName); |
818 | 160k | for (const QString &f : localFilesPath) { |
819 | 121k | files.prepend(QFileInfo(f).canonicalFilePath()); |
820 | 121k | } |
821 | | |
822 | | // allow fallback to config files bundled in resources |
823 | 160k | const QString resourceFile(QStringLiteral(":/kconfig/") + fileName); |
824 | 160k | if (QFile::exists(resourceFile)) { |
825 | 0 | files.prepend(resourceFile); |
826 | 0 | } |
827 | 160k | } |
828 | 160k | } |
829 | 160k | } else { |
830 | | // this also handles anonymous config case (backingDevicePath == "") and not file QIODevice case |
831 | 0 | files << backingDevicePath; |
832 | 0 | } |
833 | 160k | if (!isSimple()) { |
834 | 160k | files = QList<QString>(extraFiles.cbegin(), extraFiles.cend()) + files; |
835 | 160k | } |
836 | | |
837 | 160k | const QByteArray utf8Locale = locale.toUtf8(); |
838 | 160k | for (const QString &file : std::as_const(files)) { |
839 | 121k | if (file.compare(backingDevicePath, sPathCaseSensitivity) == 0) { |
840 | 121k | switch (mBackend.parseConfig(utf8Locale, entryMap, KConfigIniBackend::ParseExpansions)) { |
841 | 121k | case KConfigIniBackend::ParseOk: |
842 | 121k | break; |
843 | 0 | case KConfigIniBackend::ParseImmutable: |
844 | 0 | bFileImmutable = true; |
845 | 0 | break; |
846 | 0 | case KConfigIniBackend::ParseOpenError: |
847 | 0 | configState = KConfigBase::NoAccess; |
848 | 0 | break; |
849 | 121k | } |
850 | 121k | } else { |
851 | 0 | KConfigIniBackend backend(std::make_unique<KConfigIniBackendPathDevice>(file)); |
852 | 0 | constexpr auto parseOpts = KConfigIniBackend::ParseDefaults | KConfigIniBackend::ParseExpansions; |
853 | 0 | bFileImmutable = backend.parseConfig(utf8Locale, entryMap, parseOpts) == KConfigIniBackend::ParseImmutable; |
854 | 0 | } |
855 | | |
856 | 121k | if (bFileImmutable) { |
857 | 0 | break; |
858 | 0 | } |
859 | 121k | } |
860 | 160k | } |
861 | | |
862 | | KConfig::AccessMode KConfig::accessMode() const |
863 | 174k | { |
864 | 174k | Q_D(const KConfig); |
865 | 174k | return d->configState; |
866 | 174k | } |
867 | | |
868 | | void KConfig::addConfigSources(const QStringList &files) |
869 | 0 | { |
870 | 0 | Q_D(KConfig); |
871 | 0 | for (const QString &file : files) { |
872 | 0 | d->extraFiles.push(file); |
873 | 0 | } |
874 | |
|
875 | 0 | if (!files.isEmpty()) { |
876 | 0 | reparseConfiguration(); |
877 | 0 | } |
878 | 0 | } |
879 | | |
880 | | QStringList KConfig::additionalConfigSources() const |
881 | 0 | { |
882 | 0 | Q_D(const KConfig); |
883 | 0 | return d->extraFiles.toList(); |
884 | 0 | } |
885 | | |
886 | | QString KConfig::locale() const |
887 | 1 | { |
888 | 1 | Q_D(const KConfig); |
889 | 1 | return d->locale; |
890 | 1 | } |
891 | | |
892 | | bool KConfigPrivate::setLocale(const QString &aLocale) |
893 | 121k | { |
894 | 121k | if (aLocale != locale) { |
895 | 121k | locale = aLocale; |
896 | 121k | return true; |
897 | 121k | } |
898 | 0 | return false; |
899 | 121k | } |
900 | | |
901 | | bool KConfig::setLocale(const QString &locale) |
902 | 0 | { |
903 | 0 | Q_D(KConfig); |
904 | 0 | if (d->setLocale(locale)) { |
905 | 0 | reparseConfiguration(); |
906 | 0 | return true; |
907 | 0 | } |
908 | 0 | return false; |
909 | 0 | } |
910 | | |
911 | | void KConfig::setReadDefaults(bool b) |
912 | 8 | { |
913 | 8 | Q_D(KConfig); |
914 | 8 | d->bReadDefaults = b; |
915 | 8 | } |
916 | | |
917 | | bool KConfig::readDefaults() const |
918 | 38.8k | { |
919 | 38.8k | Q_D(const KConfig); |
920 | 38.8k | return d->bReadDefaults; |
921 | 38.8k | } |
922 | | |
923 | | bool KConfig::isImmutable() const |
924 | 296k | { |
925 | 296k | Q_D(const KConfig); |
926 | 296k | return d->bFileImmutable; |
927 | 296k | } |
928 | | |
929 | | bool KConfig::isGroupImmutableImpl(const QString &aGroup) const |
930 | 174k | { |
931 | 174k | Q_D(const KConfig); |
932 | 174k | return isImmutable() || d->entryMap.getEntryOption(aGroup, {}, {}, KEntryMap::EntryImmutable); |
933 | 174k | } |
934 | | |
935 | | KConfigGroup KConfig::groupImpl(const QString &group) |
936 | 0 | { |
937 | 0 | return KConfigGroup(this, group); |
938 | 0 | } |
939 | | |
940 | | const KConfigGroup KConfig::groupImpl(const QString &group) const |
941 | 0 | { |
942 | 0 | return KConfigGroup(this, group); |
943 | 0 | } |
944 | | |
945 | | KEntryMap::EntryOptions convertToOptions(KConfig::WriteConfigFlags flags) |
946 | 121k | { |
947 | 121k | KEntryMap::EntryOptions options = {}; |
948 | | |
949 | 121k | if (flags & KConfig::Persistent) { |
950 | 121k | options |= KEntryMap::EntryDirty; |
951 | 121k | } |
952 | 121k | if (flags & KConfig::Global) { |
953 | 0 | options |= KEntryMap::EntryGlobal; |
954 | 0 | } |
955 | 121k | if (flags & KConfig::Localized) { |
956 | 0 | options |= KEntryMap::EntryLocalized; |
957 | 0 | } |
958 | 121k | if (flags.testFlag(KConfig::Notify)) { |
959 | 0 | options |= KEntryMap::EntryNotify; |
960 | 0 | } |
961 | 121k | return options; |
962 | 121k | } |
963 | | |
964 | | void KConfig::deleteGroupImpl(const QString &aGroup, WriteConfigFlags flags) |
965 | 0 | { |
966 | 0 | Q_D(KConfig); |
967 | 0 | KEntryMap::EntryOptions options = convertToOptions(flags) | KEntryMap::EntryDeleted; |
968 | |
|
969 | 0 | const QSet<QString> groups = d->allSubGroups(aGroup); |
970 | 0 | for (const QString &group : groups) { |
971 | 0 | const QList<QByteArray> keys = d->keyListImpl(group); |
972 | 0 | for (const QByteArray &key : keys) { |
973 | 0 | if (d->canWriteEntry(group, key)) { |
974 | 0 | d->entryMap.setEntry(group, key, QByteArray(), options); |
975 | 0 | d->bDirty = true; |
976 | 0 | } |
977 | 0 | } |
978 | 0 | } |
979 | 0 | } |
980 | | |
981 | | bool KConfig::isConfigWritable(bool warnUser) |
982 | 14 | { |
983 | 14 | Q_D(KConfig); |
984 | 14 | bool allWritable = d->mBackend.isWritable(); |
985 | | |
986 | 14 | if (warnUser && !allWritable) { |
987 | 0 | QString errorMsg; |
988 | 0 | errorMsg = d->mBackend.nonWritableErrorMessage(); |
989 | | |
990 | | // Note: We don't ask the user if we should not ask this question again because we can't save the answer. |
991 | 0 | errorMsg += QCoreApplication::translate("KConfig", "Please contact your system administrator."); |
992 | 0 | QString cmdToExec = QStandardPaths::findExecutable(QStringLiteral("kdialog")); |
993 | 0 | if (!cmdToExec.isEmpty()) { |
994 | 0 | QProcess::execute(cmdToExec, QStringList{QStringLiteral("--title"), QCoreApplication::applicationName(), QStringLiteral("--msgbox"), errorMsg}); |
995 | 0 | } |
996 | 0 | } |
997 | | |
998 | 14 | d->configState = allWritable ? ReadWrite : ReadOnly; // update the read/write status |
999 | | |
1000 | 14 | return allWritable; |
1001 | 14 | } |
1002 | | |
1003 | | bool KConfig::hasGroupImpl(const QString &aGroup) const |
1004 | 0 | { |
1005 | 0 | Q_D(const KConfig); |
1006 | | |
1007 | | // No need to look for the actual group entry anymore, or for subgroups: |
1008 | | // a group exists if it contains any non-deleted entry. |
1009 | |
|
1010 | 0 | return d->hasNonDeletedEntries(aGroup); |
1011 | 0 | } |
1012 | | |
1013 | | bool KConfigPrivate::canWriteEntry(const QString &group, QAnyStringView key, bool isDefault) const |
1014 | 38.8k | { |
1015 | 38.8k | if (bFileImmutable || entryMap.getEntryOption(group, key, KEntryMap::SearchLocalized, KEntryMap::EntryImmutable)) { |
1016 | 0 | return isDefault; |
1017 | 0 | } |
1018 | 38.8k | return true; |
1019 | 38.8k | } |
1020 | | |
1021 | | void KConfigPrivate::putData(const QString &group, const char *key, const QByteArray &value, KConfigBase::WriteConfigFlags flags, bool expand) |
1022 | 121k | { |
1023 | 121k | KEntryMap::EntryOptions options = convertToOptions(flags); |
1024 | | |
1025 | 121k | if (bForceGlobal) { |
1026 | 0 | options |= KEntryMap::EntryGlobal; |
1027 | 0 | } |
1028 | 121k | if (expand) { |
1029 | 0 | options |= KEntryMap::EntryExpansion; |
1030 | 0 | } |
1031 | | |
1032 | 121k | if (value.isNull()) { // deleting entry |
1033 | 0 | options |= KEntryMap::EntryDeleted; |
1034 | 0 | } |
1035 | | |
1036 | 121k | bool dirtied = entryMap.setEntry(group, key, value, options); |
1037 | 121k | if (dirtied && (flags & KConfigBase::Persistent)) { |
1038 | 1 | bDirty = true; |
1039 | 1 | } |
1040 | 121k | } |
1041 | | |
1042 | | void KConfigPrivate::revertEntry(const QString &group, QAnyStringView key, KConfigBase::WriteConfigFlags flags) |
1043 | 0 | { |
1044 | 0 | KEntryMap::EntryOptions options = convertToOptions(flags); |
1045 | |
|
1046 | 0 | bool dirtied = entryMap.revertEntry(group, key, options); |
1047 | 0 | if (dirtied) { |
1048 | 0 | bDirty = true; |
1049 | 0 | } |
1050 | 0 | } |
1051 | | |
1052 | | QByteArray KConfigPrivate::lookupData(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags) const |
1053 | 52.7k | { |
1054 | 52.7k | return lookupInternalEntry(group, key, flags).mValue; |
1055 | 52.7k | } |
1056 | | |
1057 | | KEntry KConfigPrivate::lookupInternalEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags) const |
1058 | 52.7k | { |
1059 | 52.7k | if (bReadDefaults) { |
1060 | 4 | flags |= KEntryMap::SearchDefaults; |
1061 | 4 | } |
1062 | 52.7k | const auto it = entryMap.constFindEntry(group, key, flags); |
1063 | 52.7k | if (it == entryMap.cend()) { |
1064 | 38.8k | return {}; |
1065 | 38.8k | } |
1066 | 13.9k | return it->second; |
1067 | 52.7k | } |
1068 | | |
1069 | | QString KConfigPrivate::lookupData(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags, bool *expand) const |
1070 | 0 | { |
1071 | 0 | if (bReadDefaults) { |
1072 | 0 | flags |= KEntryMap::SearchDefaults; |
1073 | 0 | } |
1074 | 0 | return entryMap.getEntry(group, key, QString(), flags, expand); |
1075 | 0 | } |
1076 | | |
1077 | | QStandardPaths::StandardLocation KConfig::locationType() const |
1078 | 13.9k | { |
1079 | 13.9k | Q_D(const KConfig); |
1080 | 13.9k | return d->resourceType; |
1081 | 13.9k | } |
1082 | | |
1083 | | void KConfig::virtual_hook(int /*id*/, void * /*data*/) |
1084 | 0 | { |
1085 | | /* nothing */ |
1086 | 0 | } |