KConfig

kconfig.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <[email protected]>
4  SPDX-FileCopyrightText: 1999 Preston Brown <[email protected]>
5  SPDX-FileCopyrightText: 1997-1999 Matthias Kalle Dalheimer <[email protected]>
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 "kconfig_core_log_settings.h"
15 
16 #include <cstdlib>
17 #include <fcntl.h>
18 
19 #include "kconfigbackend_p.h"
20 #include "kconfiggroup.h"
21 
22 #include <QCoreApplication>
23 #include <QProcess>
24 #include <QStandardPaths>
25 #include <QByteArray>
26 #include <QFile>
27 #include <QLocale>
28 #include <QDir>
29 #include <QProcess>
30 #include <QSet>
31 #include <QBasicMutex>
32 #include <QMutexLocker>
33 
34 #if KCONFIG_USE_DBUS
35 #include <QDBusMessage>
36 #include <QDBusConnection>
37 #include <QDBusMetaType>
38 #endif
39 
40 bool KConfigPrivate::mappingsRegistered = false;
41 
42 // For caching purposes
43 static bool s_wasTestModeEnabled = false;
44 
45 Q_GLOBAL_STATIC(QStringList, s_globalFiles) // For caching purposes.
46 static QBasicMutex s_globalFilesMutex;
47 Q_GLOBAL_STATIC_WITH_ARGS(QString, sGlobalFileName, (QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/kdeglobals")))
48 
49 #ifndef Q_OS_WIN
50 static const Qt::CaseSensitivity sPathCaseSensitivity = Qt::CaseSensitive;
51 #else
52 static const Qt::CaseSensitivity sPathCaseSensitivity = Qt::CaseInsensitive;
53 #endif
54 
55 KConfigPrivate::KConfigPrivate(KConfig::OpenFlags flags,
57  : openFlags(flags), resourceType(resourceType), mBackend(nullptr),
58  bDynamicBackend(true), bDirty(false), bReadDefaults(false),
59  bFileImmutable(false), bForceGlobal(false), bSuppressGlobal(false),
60  configState(KConfigBase::NoAccess)
61 {
62  const bool isTestMode = QStandardPaths::isTestModeEnabled();
63  // If sGlobalFileName was initialised and testMode has been toggled,
64  // sGlobalFileName may need to be updated to point to the correct kdeglobals file
65  if (sGlobalFileName.exists() && s_wasTestModeEnabled != isTestMode) {
66  s_wasTestModeEnabled = isTestMode;
68  }
69 
70  static QBasicAtomicInt use_etc_kderc = Q_BASIC_ATOMIC_INITIALIZER(-1);
71 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
72  if (use_etc_kderc.load() < 0) {
73  use_etc_kderc.store( !qEnvironmentVariableIsSet("KDE_SKIP_KDERC")); // for unit tests
74  }
75  if (use_etc_kderc.load()) {
76 #else
77  if (use_etc_kderc.loadRelaxed() < 0) {
78  use_etc_kderc.storeRelaxed( !qEnvironmentVariableIsSet("KDE_SKIP_KDERC")); // for unit tests
79  }
80  if (use_etc_kderc.loadRelaxed()) {
81 #endif
82  etc_kderc =
83 #ifdef Q_OS_WIN
84  QFile::decodeName(qgetenv("WINDIR") + "/kde5rc");
85 #else
86  QStringLiteral("/etc/kde5rc");
87 #endif
88  if (!QFileInfo(etc_kderc).isReadable()) {
89 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
90  use_etc_kderc.store(false);
91 #else
92  use_etc_kderc.storeRelaxed(false);
93 #endif
94  etc_kderc.clear();
95  }
96  }
97 
98 // if (!mappingsRegistered) {
99 // KEntryMap tmp;
100 // if (!etc_kderc.isEmpty()) {
101 // QExplicitlySharedDataPointer<KConfigBackend> backend = KConfigBackend::create(etc_kderc, QLatin1String("INI"));
102 // backend->parseConfig( "en_US", tmp, KConfigBackend::ParseDefaults);
103 // }
104 // const QString kde5rc(QDir::home().filePath(".kde5rc"));
105 // if (KStandardDirs::checkAccess(kde5rc, R_OK)) {
106 // QExplicitlySharedDataPointer<KConfigBackend> backend = KConfigBackend::create(kde5rc, QLatin1String("INI"));
107 // backend->parseConfig( "en_US", tmp, KConfigBackend::ParseOptions());
108 // }
109 // KConfigBackend::registerMappings(tmp);
110 // mappingsRegistered = true;
111 // }
112 
113  setLocale(QLocale().name());
114 }
115 
116 bool KConfigPrivate::lockLocal()
117 {
118  if (mBackend) {
119  return mBackend->lock();
120  }
121  // anonymous object - pretend we locked it
122  return true;
123 }
124 
125 void KConfigPrivate::copyGroup(const QByteArray &source, const QByteArray &destination,
126  KConfigGroup *otherGroup, KConfigBase::WriteConfigFlags flags) const
127 {
128  KEntryMap &otherMap = otherGroup->config()->d_ptr->entryMap;
129  const int len = source.length();
130  const bool sameName = (destination == source);
131 
132  // we keep this bool outside the foreach loop so that if
133  // the group is empty, we don't end up marking the other config
134  // as dirty erroneously
135  bool dirtied = false;
136 
137  for (KEntryMap::ConstIterator entryMapIt(entryMap.constBegin()); entryMapIt != entryMap.constEnd(); ++entryMapIt) {
138  const QByteArray &group = entryMapIt.key().mGroup;
139 
140  if (!group.startsWith(source)) { // nothing to do
141  continue;
142  }
143 
144  // don't copy groups that start with the same prefix, but are not sub-groups
145  if (group.length() > len && group[len] != '\x1d') {
146  continue;
147  }
148 
149  KEntryKey newKey = entryMapIt.key();
150 
151  if (flags & KConfigBase::Localized) {
152  newKey.bLocal = true;
153  }
154 
155  if (!sameName) {
156  newKey.mGroup.replace(0, len, destination);
157  }
158 
159  KEntry entry = entryMap[ entryMapIt.key() ];
160  dirtied = entry.bDirty = flags & KConfigBase::Persistent;
161 
162  if (flags & KConfigBase::Global) {
163  entry.bGlobal = true;
164  }
165 
166  otherMap[newKey] = entry;
167  }
168 
169  if (dirtied) {
170  otherGroup->config()->d_ptr->bDirty = true;
171  }
172 }
173 
174 QString KConfigPrivate::expandString(const QString &value)
175 {
176  QString aValue = value;
177 
178  // check for environment variables and make necessary translations
179  int nDollarPos = aValue.indexOf(QLatin1Char('$'));
180  while (nDollarPos != -1 && nDollarPos + 1 < aValue.length()) {
181  // there is at least one $
182  if (aValue[nDollarPos + 1] != QLatin1Char('$')) {
183  int nEndPos = nDollarPos + 1;
184  // the next character is not $
185  QStringRef aVarName;
186  if (aValue[nEndPos] == QLatin1Char('{')) {
187  while ((nEndPos <= aValue.length()) && (aValue[nEndPos] != QLatin1Char('}'))) {
188  nEndPos++;
189  }
190  nEndPos++;
191  aVarName = aValue.midRef(nDollarPos + 2, nEndPos - nDollarPos - 3);
192  } else {
193  while (nEndPos <= aValue.length() &&
194  (aValue[nEndPos].isNumber() ||
195  aValue[nEndPos].isLetter() ||
196  aValue[nEndPos] == QLatin1Char('_'))) {
197  nEndPos++;
198  }
199  aVarName = aValue.midRef(nDollarPos + 1, nEndPos - nDollarPos - 1);
200  }
201  QString env;
202  if (!aVarName.isEmpty()) {
203 #ifdef Q_OS_WIN
204  if (aVarName == QLatin1String("HOME")) {
205  env = QDir::homePath();
206  } else
207 #endif
208  {
209  QByteArray pEnv = qgetenv(aVarName.toLatin1().constData());
210  if (!pEnv.isEmpty()) {
211  env = QString::fromLocal8Bit(pEnv.constData());
212  } else {
213  if (aVarName == QLatin1String("QT_DATA_HOME")) {
215  } else if (aVarName == QLatin1String("QT_CONFIG_HOME")) {
217  } else if (aVarName == QLatin1String("QT_CACHE_HOME")) {
219  }
220  }
221  }
222  aValue.replace(nDollarPos, nEndPos - nDollarPos, env);
223  nDollarPos += env.length();
224  } else {
225  aValue.remove(nDollarPos, nEndPos - nDollarPos);
226  }
227  } else {
228  // remove one of the dollar signs
229  aValue.remove(nDollarPos, 1);
230  nDollarPos++;
231  }
232  nDollarPos = aValue.indexOf(QLatin1Char('$'), nDollarPos);
233  }
234 
235  return aValue;
236 }
237 
238 KConfig::KConfig(const QString &file, OpenFlags mode,
240  : d_ptr(new KConfigPrivate(mode, resourceType))
241 {
242  d_ptr->changeFileName(file); // set the local file name
243 
244  // read initial information off disk
246 }
247 
248 KConfig::KConfig(const QString &file, const QString &backend, QStandardPaths::StandardLocation resourceType)
249  : d_ptr(new KConfigPrivate(SimpleConfig, resourceType))
250 {
251  d_ptr->mBackend = KConfigBackend::create(file, backend);
252  d_ptr->bDynamicBackend = false;
253  d_ptr->changeFileName(file); // set the local file name
254 
255  // read initial information off disk
257 }
258 
259 KConfig::KConfig(KConfigPrivate &d)
260  : d_ptr(&d)
261 {
262 }
263 
264 KConfig::~KConfig()
265 {
266  Q_D(KConfig);
267 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
268  if (d->bDirty && (d->mBackend && d->mBackend->ref.load() == 1)) {
269 #else
270  if (d->bDirty && (d->mBackend && d->mBackend->ref.loadRelaxed() == 1)) {
271 #endif
272  sync();
273  }
274  delete d;
275 }
276 
278 {
279  Q_D(const KConfig);
280  QSet<QString> groups;
281 
282  for (KEntryMap::ConstIterator entryMapIt(d->entryMap.constBegin()); entryMapIt != d->entryMap.constEnd(); ++entryMapIt) {
283  const KEntryKey &key = entryMapIt.key();
284  const QByteArray group = key.mGroup;
285  if (key.mKey.isNull() && !group.isEmpty() && group != "<default>" && group != "$Version") {
286  const QString groupname = QString::fromUtf8(group);
287  groups << groupname.left(groupname.indexOf(QLatin1Char('\x1d')));
288  }
289  }
290 
291  return groups.values();
292 }
293 
294 QStringList KConfigPrivate::groupList(const QByteArray &group) const
295 {
296  QByteArray theGroup = group + '\x1d';
297  QSet<QString> groups;
298 
299  for (KEntryMap::ConstIterator entryMapIt(entryMap.constBegin()); entryMapIt != entryMap.constEnd(); ++entryMapIt) {
300  const KEntryKey &key = entryMapIt.key();
301  if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) {
302  const QString groupname = QString::fromUtf8(key.mGroup.mid(theGroup.length()));
303  groups << groupname.left(groupname.indexOf(QLatin1Char('\x1d')));
304  }
305  }
306 
307  return groups.values();
308 }
309 
310 static bool isGroupOrSubGroupMatch(const QByteArray &potentialGroup, const QByteArray &group)
311 {
312  if (!potentialGroup.startsWith(group)) {
313  return false;
314  }
315  return potentialGroup.length() == group.length() || potentialGroup[group.length()] == '\x1d';
316 }
317 
318 // List all sub groups, including subsubgroups
319 QSet<QByteArray> KConfigPrivate::allSubGroups(const QByteArray &parentGroup) const
320 {
321  QSet<QByteArray> groups;
322 
323  for (KEntryMap::const_iterator entryMapIt = entryMap.begin(); entryMapIt != entryMap.end(); ++entryMapIt) {
324  const KEntryKey &key = entryMapIt.key();
325  if (key.mKey.isNull() && isGroupOrSubGroupMatch(key.mGroup, parentGroup)) {
326  groups << key.mGroup;
327  }
328  }
329  return groups;
330 }
331 
332 bool KConfigPrivate::hasNonDeletedEntries(const QByteArray &group) const
333 {
334  for (KEntryMap::const_iterator it = entryMap.begin(); it != entryMap.end(); ++it) {
335  const KEntryKey &key = it.key();
336  // Check for any non-deleted entry
337  if (isGroupOrSubGroupMatch(key.mGroup, group) && !key.mKey.isNull() && !it->bDeleted) {
338  return true;
339  }
340  }
341  return false;
342 }
343 
344 QStringList KConfigPrivate::keyListImpl(const QByteArray &theGroup) const
345 {
346  QStringList keys;
347 
348  const KEntryMapConstIterator theEnd = entryMap.constEnd();
349  KEntryMapConstIterator it = entryMap.findEntry(theGroup);
350  if (it != theEnd) {
351  ++it; // advance past the special group entry marker
352 
353  QSet<QString> tmp;
354  for (; it != theEnd && it.key().mGroup == theGroup; ++it) {
355  const KEntryKey &key = it.key();
356  if (!key.mKey.isNull() && !it->bDeleted) {
357  tmp << QString::fromUtf8(key.mKey);
358  }
359  }
360  keys = tmp.values();
361  }
362 
363  return keys;
364 }
365 
366 QStringList KConfig::keyList(const QString &aGroup) const
367 {
368  Q_D(const KConfig);
369  const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8());
370  return d->keyListImpl(theGroup);
371 }
372 
373 QMap<QString, QString> KConfig::entryMap(const QString &aGroup) const
374 {
375  Q_D(const KConfig);
376  QMap<QString, QString> theMap;
377  const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8());
378 
379  const KEntryMapConstIterator theEnd = d->entryMap.constEnd();
380  KEntryMapConstIterator it = d->entryMap.findEntry(theGroup, {}, {});
381  if (it != theEnd) {
382  ++it; // advance past the special group entry marker
383 
384  for (; it != theEnd && it.key().mGroup == theGroup; ++it) {
385  // leave the default values and deleted entries out
386  if (!it->bDeleted && !it.key().bDefault) {
387  const QString key = QString::fromUtf8(it.key().mKey.constData());
388  // the localized entry should come first, so don't overwrite it
389  // with the non-localized entry
390  if (!theMap.contains(key)) {
391  if (it->bExpand) {
392  theMap.insert(key, KConfigPrivate::expandString(QString::fromUtf8(it->mValue.constData())));
393  } else {
394  theMap.insert(key, QString::fromUtf8(it->mValue.constData()));
395  }
396  }
397  }
398  }
399  }
400 
401  return theMap;
402 }
403 
405 {
406  Q_D(KConfig);
407 
408  if (isImmutable() || name().isEmpty()) {
409  // can't write to an immutable or anonymous file.
410  return false;
411  }
412 
413  QHash<QString, QByteArrayList> notifyGroupsLocal;
414  QHash<QString, QByteArrayList> notifyGroupsGlobal;
415 
416  if (d->bDirty && d->mBackend) {
417  const QByteArray utf8Locale(locale().toUtf8());
418 
419  // Create the containing dir, maybe it wasn't there
420  d->mBackend->createEnclosing();
421 
422  // lock the local file
423  if (d->configState == ReadWrite && !d->lockLocal()) {
424  qCWarning(KCONFIG_CORE_LOG) << "couldn't lock local file";
425  return false;
426  }
427 
428  // Rewrite global/local config only if there is a dirty entry in it.
429  bool writeGlobals = false;
430  bool writeLocals = false;
431 
432  for (auto it = d->entryMap.constBegin(); it != d->entryMap.constEnd(); ++it) {
433  auto e = it.value();
434  if (e.bDirty) {
435  if (e.bGlobal) {
436  writeGlobals = true;
437  if (e.bNotify) {
438  notifyGroupsGlobal[QString::fromUtf8(it.key().mGroup)] << it.key().mKey;
439  }
440  } else {
441  writeLocals = true;
442  if (e.bNotify) {
443  notifyGroupsLocal[QString::fromUtf8(it.key().mGroup)] << it.key().mKey;
444  }
445  }
446  }
447  }
448 
449  d->bDirty = false; // will revert to true if a config write fails
450 
451  if (d->wantGlobals() && writeGlobals) {
452  QExplicitlySharedDataPointer<KConfigBackend> tmp = KConfigBackend::create(*sGlobalFileName);
453  if (d->configState == ReadWrite && !tmp->lock()) {
454  qCWarning(KCONFIG_CORE_LOG) << "couldn't lock global file";
455 
456  //unlock the local config if we're returning early
457  if (d->mBackend->isLocked()) {
458  d->mBackend->unlock();
459  }
460 
461  d->bDirty = true;
462  return false;
463  }
464  if (!tmp->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteGlobal)) {
465  d->bDirty = true;
466  }
467  if (tmp->isLocked()) {
468  tmp->unlock();
469  }
470  }
471 
472  if (writeLocals) {
473  if (!d->mBackend->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteOptions())) {
474  d->bDirty = true;
475  }
476  }
477  if (d->mBackend->isLocked()) {
478  d->mBackend->unlock();
479  }
480  }
481 
482  if (!notifyGroupsLocal.isEmpty()) {
483  d->notifyClients(notifyGroupsLocal, QLatin1Char('/') + name());
484  }
485  if (!notifyGroupsGlobal.isEmpty()) {
486  d->notifyClients(notifyGroupsGlobal, QStringLiteral("/kdeglobals"));
487  }
488 
489  return !d->bDirty;
490 }
491 
492 void KConfigPrivate::notifyClients(const QHash<QString, QByteArrayList> &changes, const QString &path)
493 {
494 #if KCONFIG_USE_DBUS
495  qDBusRegisterMetaType<QByteArrayList>();
496 
497  qDBusRegisterMetaType<QHash<QString, QByteArrayList>>();
498 
500  QStringLiteral("org.kde.kconfig.notify"),
501  QStringLiteral("ConfigChanged"));
502  message.setArguments({QVariant::fromValue(changes)});
504 #else
505  Q_UNUSED(changes)
506  Q_UNUSED(path)
507 #endif
508 }
509 
511 {
512  Q_D(KConfig);
513  d->bDirty = false;
514 
515  // clear any dirty flags that entries might have set
516  const KEntryMapIterator theEnd = d->entryMap.end();
517  for (KEntryMapIterator it = d->entryMap.begin(); it != theEnd; ++it) {
518  it->bDirty = false;
519  it->bNotify = false;
520  }
521 }
522 
523 bool KConfig::isDirty() const
524 {
525  Q_D(const KConfig);
526  return d->bDirty;
527 }
528 
529 void KConfig::checkUpdate(const QString &id, const QString &updateFile)
530 {
531  const KConfigGroup cg(this, "$Version");
532  const QString cfg_id = updateFile + QLatin1Char(':') + id;
533  const QStringList ids = cg.readEntry("update_info", QStringList());
534  if (!ids.contains(cfg_id)) {
535  QProcess::execute(QStringLiteral(KCONF_UPDATE_INSTALL_LOCATION), QStringList { QStringLiteral("--check"), updateFile });
537  }
538 }
539 
540 KConfig *KConfig::copyTo(const QString &file, KConfig *config) const
541 {
542  Q_D(const KConfig);
543  if (!config) {
544  config = new KConfig(QString(), SimpleConfig, d->resourceType);
545  }
546  config->d_func()->changeFileName(file);
547  config->d_func()->entryMap = d->entryMap;
548  config->d_func()->bFileImmutable = false;
549 
550  const KEntryMapIterator theEnd = config->d_func()->entryMap.end();
551  for (KEntryMapIterator it = config->d_func()->entryMap.begin(); it != theEnd; ++it) {
552  it->bDirty = true;
553  }
554  config->d_ptr->bDirty = true;
555 
556  return config;
557 }
558 
559 QString KConfig::name() const
560 {
561  Q_D(const KConfig);
562  return d->fileName;
563 }
564 
565 
567 {
568  Q_D(const KConfig);
569  return d->openFlags;
570 }
571 
572 struct KConfigStaticData
573 {
574  QString globalMainConfigName;
575  // Keep a copy so we can use it in global dtors, after qApp is gone
576  QStringList appArgs;
577 };
578 Q_GLOBAL_STATIC(KConfigStaticData, globalData)
579 
580 void KConfig::setMainConfigName(const QString &str)
581 {
582  globalData()->globalMainConfigName = str;
583 }
584 
585 QString KConfig::mainConfigName()
586 {
587  KConfigStaticData* data = globalData();
588  if (data->appArgs.isEmpty())
589  data->appArgs = QCoreApplication::arguments();
590 
591  // --config on the command line overrides everything else
592  const QStringList args = data->appArgs;
593  for (int i = 1; i < args.count(); ++i) {
594  if (args.at(i) == QLatin1String("--config") && i < args.count() - 1) {
595  return args.at(i + 1);
596  }
597  }
598  const QString globalName = data->globalMainConfigName;
599  if (!globalName.isEmpty()) {
600  return globalName;
601  }
602 
604  return appName + QLatin1String("rc");
605 }
606 
607 void KConfigPrivate::changeFileName(const QString &name)
608 {
609  fileName = name;
610 
611  QString file;
612  if (name.isEmpty()) {
613  if (wantDefaults()) { // accessing default app-specific config "appnamerc"
614  fileName = KConfig::mainConfigName();
615  file = QStandardPaths::writableLocation(resourceType) + QLatin1Char('/') + fileName;
616  } else if (wantGlobals()) { // accessing "kdeglobals" by specifying no filename and NoCascade - XXX used anywhere?
618  fileName = QStringLiteral("kdeglobals");
619  file = *sGlobalFileName;
620  } else {
621  // anonymous config
623  return;
624  }
625  } else if (QDir::isAbsolutePath(fileName)) {
626  fileName = QFileInfo(fileName).canonicalFilePath();
627  if (fileName.isEmpty()) { // file doesn't exist (yet)
628  fileName = name;
629  }
630  file = fileName;
631  } else {
632  file = QStandardPaths::writableLocation(resourceType) + QLatin1Char('/') + fileName;
633  }
634 
635  Q_ASSERT(!file.isEmpty());
636 
637  bSuppressGlobal = (file.compare(*sGlobalFileName, sPathCaseSensitivity) == 0);
638 
639  if (bDynamicBackend || !mBackend) { // allow dynamic changing of backend
640  mBackend = KConfigBackend::create(file);
641  } else {
642  mBackend->setFilePath(file);
643  }
644 
645  configState = mBackend->accessMode();
646 }
647 
649 {
650  Q_D(KConfig);
651  if (d->fileName.isEmpty()) {
652  return;
653  }
654 
655  // Don't lose pending changes
656  if (!d->isReadOnly() && d->bDirty) {
657  sync();
658  }
659 
660  d->entryMap.clear();
661 
662  d->bFileImmutable = false;
663 
664  {
665  QMutexLocker locker(&s_globalFilesMutex);
666  s_globalFiles()->clear();
667  }
668 
669  // Parse all desired files from the least to the most specific.
670  if (d->wantGlobals()) {
671  d->parseGlobalFiles();
672  }
673 
674  d->parseConfigFiles();
675 }
676 
677 QStringList KConfigPrivate::getGlobalFiles() const
678 {
679  QMutexLocker locker(&s_globalFilesMutex);
680  if (s_globalFiles()->isEmpty()) {
681  const QStringList paths1 = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("kdeglobals"));
682  const QStringList paths2 = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("system.kdeglobals"));
683 
684  const bool useEtcKderc = !etc_kderc.isEmpty();
685  s_globalFiles()->reserve(paths1.size() + paths2.size() + (useEtcKderc ? 1 : 0));
686 
687  for (const QString &dir1 : paths1) {
688  s_globalFiles()->push_front(dir1);
689  }
690  for (const QString &dir2 : paths2) {
691  s_globalFiles()->push_front(dir2);
692  }
693 
694  if (useEtcKderc) {
695  s_globalFiles()->push_front(etc_kderc);
696  }
697  }
698 
699  return *s_globalFiles();
700 }
701 
702 void KConfigPrivate::parseGlobalFiles()
703 {
704  const QStringList globalFiles = getGlobalFiles();
705 // qDebug() << "parsing global files" << globalFiles;
706 
707  // TODO: can we cache the values in etc_kderc / other global files
708  // on a per-application basis?
709  const QByteArray utf8Locale = locale.toUtf8();
710  for (const QString &file : globalFiles) {
711  KConfigBackend::ParseOptions parseOpts = KConfigBackend::ParseGlobal | KConfigBackend::ParseExpansions;
712 
713  if (file.compare(*sGlobalFileName, sPathCaseSensitivity) != 0)
714  parseOpts |= KConfigBackend::ParseDefaults;
715 
716  QExplicitlySharedDataPointer<KConfigBackend> backend = KConfigBackend::create(file);
717  if (backend->parseConfig(utf8Locale, entryMap, parseOpts) == KConfigBackend::ParseImmutable) {
718  break;
719  }
720  }
721 }
722 
723 void KConfigPrivate::parseConfigFiles()
724 {
725  // can only read the file if there is a backend and a file name
726  if (mBackend && !fileName.isEmpty()) {
727 
728  bFileImmutable = false;
729 
730  QList<QString> files;
731  if (wantDefaults()) {
732  if (bSuppressGlobal) {
733  files = getGlobalFiles();
734  } else {
735  if (QDir::isAbsolutePath(fileName)) {
736  const QString canonicalFile = QFileInfo(fileName).canonicalFilePath();
737  if (!canonicalFile.isEmpty()) { // empty if it doesn't exist
738  files << canonicalFile;
739  }
740  } else {
741  const QStringList localFilesPath = QStandardPaths::locateAll(resourceType, fileName);
742  for (const QString &f : localFilesPath) {
743  files.prepend(QFileInfo(f).canonicalFilePath());
744  }
745 
746  // allow fallback to config files bundled in resources
747  const QString resourceFile(QStringLiteral(":/kconfig/") + fileName);
748  if (QFile::exists(resourceFile)) {
749  files.prepend(resourceFile);
750  }
751  }
752  }
753  } else {
754  files << mBackend->filePath();
755  }
756  if (!isSimple()) {
757  files = extraFiles.toList() + files;
758  }
759 
760 // qDebug() << "parsing local files" << files;
761 
762  const QByteArray utf8Locale = locale.toUtf8();
763  for (const QString &file : qAsConst(files)) {
764  if (file.compare(mBackend->filePath(), sPathCaseSensitivity) == 0) {
765  switch (mBackend->parseConfig(utf8Locale, entryMap, KConfigBackend::ParseExpansions)) {
766  case KConfigBackend::ParseOk:
767  break;
768  case KConfigBackend::ParseImmutable:
769  bFileImmutable = true;
770  break;
771  case KConfigBackend::ParseOpenError:
772  configState = KConfigBase::NoAccess;
773  break;
774  }
775  } else {
776  QExplicitlySharedDataPointer<KConfigBackend> backend = KConfigBackend::create(file);
777  bFileImmutable = (backend->parseConfig(utf8Locale, entryMap,
778  KConfigBackend::ParseDefaults | KConfigBackend::ParseExpansions)
779  == KConfigBackend::ParseImmutable);
780  }
781 
782  if (bFileImmutable) {
783  break;
784  }
785  }
786  }
787 }
788 
790 {
791  Q_D(const KConfig);
792  return d->configState;
793 }
794 
796 {
797  Q_D(KConfig);
798  for (const QString &file : files) {
799  d->extraFiles.push(file);
800  }
801 
802  if (!files.isEmpty()) {
804  }
805 }
806 
808 {
809  Q_D(const KConfig);
810  return d->extraFiles.toList();
811 }
812 
813 QString KConfig::locale() const
814 {
815  Q_D(const KConfig);
816  return d->locale;
817 }
818 
819 bool KConfigPrivate::setLocale(const QString &aLocale)
820 {
821  if (aLocale != locale) {
822  locale = aLocale;
823  return true;
824  }
825  return false;
826 }
827 
828 bool KConfig::setLocale(const QString &locale)
829 {
830  Q_D(KConfig);
831  if (d->setLocale(locale)) {
833  return true;
834  }
835  return false;
836 }
837 
839 {
840  Q_D(KConfig);
841  d->bReadDefaults = b;
842 }
843 
845 {
846  Q_D(const KConfig);
847  return d->bReadDefaults;
848 }
849 
851 {
852  Q_D(const KConfig);
853  return d->bFileImmutable;
854 }
855 
856 bool KConfig::isGroupImmutableImpl(const QByteArray &aGroup) const
857 {
858  Q_D(const KConfig);
859  return isImmutable() || d->entryMap.getEntryOption(aGroup, {},{}, KEntryMap::EntryImmutable);
860 }
861 
862 #if KCONFIGCORE_BUILD_DEPRECATED_SINCE(4, 0)
864 {
865  Q_D(KConfig);
866  d->bForceGlobal = b;
867 }
868 #endif
869 
870 #if KCONFIGCORE_BUILD_DEPRECATED_SINCE(4, 0)
872 {
873  Q_D(const KConfig);
874  return d->bForceGlobal;
875 }
876 #endif
877 
879 {
880  return KConfigGroup(this, group.constData());
881 }
882 
883 const KConfigGroup KConfig::groupImpl(const QByteArray &group) const
884 {
885  return KConfigGroup(this, group.constData());
886 }
887 
889 {
890  KEntryMap::EntryOptions options = {};
891 
892  if (flags & KConfig::Persistent) {
893  options |= KEntryMap::EntryDirty;
894  }
895  if (flags & KConfig::Global) {
896  options |= KEntryMap::EntryGlobal;
897  }
898  if (flags & KConfig::Localized) {
899  options |= KEntryMap::EntryLocalized;
900  }
901  if (flags.testFlag(KConfig::Notify)) {
902  options |= KEntryMap::EntryNotify;
903  }
904  return options;
905 }
906 
908 {
909  Q_D(KConfig);
910  KEntryMap::EntryOptions options = convertToOptions(flags) | KEntryMap::EntryDeleted;
911 
912  const QSet<QByteArray> groups = d->allSubGroups(aGroup);
913  for (const QByteArray &group : groups) {
914  const QStringList keys = d->keyListImpl(group);
915  for (const QString &_key : keys) {
916  const QByteArray &key = _key.toUtf8();
917  if (d->canWriteEntry(group, key.constData())) {
918  d->entryMap.setEntry(group, key, QByteArray(), options);
919  d->bDirty = true;
920  }
921  }
922  }
923 }
924 
925 bool KConfig::isConfigWritable(bool warnUser)
926 {
927  Q_D(KConfig);
928  bool allWritable = (d->mBackend ? d->mBackend->isWritable() : false);
929 
930  if (warnUser && !allWritable) {
931  QString errorMsg;
932  if (d->mBackend) { // TODO how can be it be null? Set errorMsg appropriately
933  errorMsg = d->mBackend->nonWritableErrorMessage();
934  }
935 
936  // Note: We don't ask the user if we should not ask this question again because we can't save the answer.
937  errorMsg += QCoreApplication::translate("KConfig", "Please contact your system administrator.");
938  QString cmdToExec = QStandardPaths::findExecutable(QStringLiteral("kdialog"));
939  if (!cmdToExec.isEmpty()) {
940  QProcess::execute(cmdToExec, QStringList()
941  << QStringLiteral("--title") << QCoreApplication::applicationName()
942  << QStringLiteral("--msgbox") << errorMsg);
943  }
944  }
945 
946  d->configState = allWritable ? ReadWrite : ReadOnly; // update the read/write status
947 
948  return allWritable;
949 }
950 
951 bool KConfig::hasGroupImpl(const QByteArray &aGroup) const
952 {
953  Q_D(const KConfig);
954 
955  // No need to look for the actual group entry anymore, or for subgroups:
956  // a group exists if it contains any non-deleted entry.
957 
958  return d->hasNonDeletedEntries(aGroup);
959 }
960 
961 bool KConfigPrivate::canWriteEntry(const QByteArray &group, const char *key, bool isDefault) const
962 {
963  if (bFileImmutable ||
964  entryMap.getEntryOption(group, key, KEntryMap::SearchLocalized, KEntryMap::EntryImmutable)) {
965  return isDefault;
966  }
967  return true;
968 }
969 
970 void KConfigPrivate::putData(const QByteArray &group, const char *key,
971  const QByteArray &value, KConfigBase::WriteConfigFlags flags, bool expand)
972 {
973  KEntryMap::EntryOptions options = convertToOptions(flags);
974 
975  if (bForceGlobal) {
976  options |= KEntryMap::EntryGlobal;
977  }
978  if (expand) {
979  options |= KEntryMap::EntryExpansion;
980  }
981 
982  if (value.isNull()) { // deleting entry
983  options |= KEntryMap::EntryDeleted;
984  }
985 
986  bool dirtied = entryMap.setEntry(group, key, value, options);
987  if (dirtied && (flags & KConfigBase::Persistent)) {
988  bDirty = true;
989  }
990 }
991 
992 void KConfigPrivate::revertEntry(const QByteArray &group, const char *key, KConfigBase::WriteConfigFlags flags)
993 {
994  KEntryMap::EntryOptions options = convertToOptions(flags);
995 
996  bool dirtied = entryMap.revertEntry(group, key, options);
997  if (dirtied) {
998  bDirty = true;
999  }
1000 }
1001 
1002 QByteArray KConfigPrivate::lookupData(const QByteArray &group, const char *key,
1003  KEntryMap::SearchFlags flags) const
1004 {
1005  if (bReadDefaults) {
1006  flags |= KEntryMap::SearchDefaults;
1007  }
1008  const KEntryMapConstIterator it = entryMap.findEntry(group, key, flags);
1009  if (it == entryMap.constEnd()) {
1010  return QByteArray();
1011  }
1012  return it->mValue;
1013 }
1014 
1015 QString KConfigPrivate::lookupData(const QByteArray &group, const char *key,
1016  KEntryMap::SearchFlags flags, bool *expand) const
1017 {
1018  if (bReadDefaults) {
1019  flags |= KEntryMap::SearchDefaults;
1020  }
1021  return entryMap.getEntry(group, key, QString(), flags, expand);
1022 }
1023 
1025 {
1026  Q_D(const KConfig);
1027  return d->resourceType;
1028 }
1029 
1030 void KConfig::virtual_hook(int /*id*/, void * /*data*/)
1031 {
1032  /* nothing */
1033 }
int execute(const QString &program, const QStringList &arguments)
void checkUpdate(const QString &id, const QString &updateFile)
Ensures that the configuration file contains a certain update.
Definition: kconfig.cpp:529
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
Notify remote KConfigWatchers of changes (requires DBus support) Implied persistent.
Definition: kconfigbase.h:51
Save this entry when saving the config object.
Definition: kconfigbase.h:38
void deleteGroupImpl(const QByteArray &group, WriteConfigFlags flags=Normal) override
Definition: kconfig.cpp:907
void markAsClean() override
Definition: kconfig.cpp:510
QString name(const QVariant &location)
QString writableLocation(QStandardPaths::StandardLocation type)
bool contains(const Key &key) const const
QString name() const
Returns the filename used to store the configuration.
Definition: kconfig.cpp:559
const Key key(const T &value) const const
QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
void setReadDefaults(bool b)
defaults
Definition: kconfig.cpp:838
map/dict/list config node entry.
Definition: kconfigdata.h:22
Save the entry to the global KDE config file instead of the application specific config file...
Definition: kconfigbase.h:42
bool isGroupImmutableImpl(const QByteArray &aGroup) const override
Definition: kconfig.cpp:856
QMap::const_iterator constBegin() const const
const T & at(int i) const const
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
bool isNull() const const
void virtual_hook(int id, void *data) override
Virtual hook, used to add new "virtual" functions while maintaining binary compatibility.
Definition: kconfig.cpp:1030
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool startsWith(const QByteArray &ba) const const
QString findExecutable(const QString &executableName, const QStringList &paths)
void setForceGlobal(bool force)
global
Definition: kconfig.cpp:863
Just a single config file.
Definition: kconfig.h:86
QDBusConnection sessionBus()
bool isDirty() const
Returns true if sync has any changes to write out.
Definition: kconfig.cpp:523
KConfigGroup groupImpl(const QByteArray &b) override
Definition: kconfig.cpp:878
int length() const const
bool exists() const const
QString & remove(int position, int n)
KConfig(const QString &file=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
Creates a KConfig object to manipulate a configuration file for the current application.
Definition: kconfig.cpp:238
QString homePath()
bool isImmutable() const override
Definition: kconfig.cpp:850
int size() const const
QMap< QString, QString > entryMap(const QString &aGroup=QString()) const
Returns a map (tree) of entries in a particular group.
Definition: kconfig.cpp:373
QString locale() const
locales
Definition: kconfig.cpp:813
QString canonicalFilePath() const const
int count(const T &value) const const
QString fromLocal8Bit(const char *str, int size)
bool sync() override
Definition: kconfig.cpp:404
OpenFlags openFlags() const
Definition: kconfig.cpp:566
QString fromUtf8(const char *str, int size)
key structure holding both the actual key and the group to which it belongs.
Definition: kconfigdata.h:88
QByteArray mGroup
The "group" to which this EntryKey belongs.
Definition: kconfigdata.h:99
void addConfigSources(const QStringList &sources)
extra config files
Definition: kconfig.cpp:795
QList< T > values() const const
bool setLocale(const QString &aLocale)
Sets the locale to aLocale.
Definition: kconfig.cpp:828
CaseSensitivity
QDBusMessage createSignal(const QString &path, const QString &interface, const QString &name)
bool send(const QDBusMessage &message) const const
bool isEmpty() const const
QMap::const_iterator constEnd() const const
const char * constData() const const
QByteArray & replace(int pos, int len, const char *after)
bool bLocal
Entry is localised or not.
Definition: kconfigdata.h:107
QMap::iterator end()
QByteArray mid(int pos, int len) const const
KConfig * copyTo(const QString &file, KConfig *config=nullptr) const
Copies all entries from this config object to a new config object that will save itself to file...
Definition: kconfig.cpp:540
Interface to interact with configuration.
Definition: kconfigbase.h:30
QMap::iterator begin()
QStringList additionalConfigSources() const
Returns a list of the additional configuration sources used in this object.
Definition: kconfig.cpp:807
QStringRef midRef(int position, int n) const const
QVariant fromValue(const T &value)
A class for one specific group in a KConfig object.
Definition: kconfiggroup.h:38
QString & replace(int position, int n, QChar after)
bool bGlobal
Entry should be written to the global config file.
Definition: kconfigdata.h:37
The central class of the KDE configuration data system.
Definition: kconfig.h:56
KConfig * config()
Return the config object that this group belongs to.
bool isAbsolutePath(const QString &path)
QStandardPaths::StandardLocation locationType() const
Returns the standard location enum passed to the constructor.
Definition: kconfig.cpp:1024
bool isEmpty() const const
bool hasGroupImpl(const QByteArray &group) const override
Definition: kconfig.cpp:951
AccessMode accessMode() const override
Definition: kconfig.cpp:789
void reparseConfiguration()
Updates the state of this object to match the persistent storage.
Definition: kconfig.cpp:648
QByteArray mKey
The actual key of the entry in question.
Definition: kconfigdata.h:103
QByteArray toLatin1() const const
QStringList groupList() const override
Definition: kconfig.cpp:277
AccessMode
Possible return values for accessMode().
Definition: kconfigbase.h:175
int length() const const
static void setMainConfigName(const QString &str)
Sets the name of the application config file.
Definition: kconfig.cpp:580
QString left(int n) const const
bool isEmpty() const const
QByteArray::const_iterator constEnd() const const
void prepend(const T &value)
QMap::iterator insert(const Key &key, const T &value)
QStringList arguments()
bool forceGlobal() const
Returns whether all entries are being written to kdeglobals.
Definition: kconfig.cpp:871
int compare(const QString &other, Qt::CaseSensitivity cs) const const
bool testFlag(Enum flag) const const
T readEntry(const QString &key, const T &aDefault) const
Reads the value of an entry specified by pKey in the current group.
Definition: kconfiggroup.h:250
Add the locale tag to the key when writing it.
Definition: kconfigbase.h:47
bool bDirty
Must the entry be written back to disk?
Definition: kconfigdata.h:33
QString decodeName(const QByteArray &localFileName)
bool isConfigWritable(bool warnUser)
Whether the configuration can be written to.
Definition: kconfig.cpp:925
QString applicationName()
bool readDefaults() const
Definition: kconfig.cpp:844
QCA_EXPORT QString appName()
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Aug 13 2020 22:48:14 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.