1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
4 SPDX-FileCopyrightText: 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "ksharedconfig.h"
10#include "kconfig_p.h"
11#include "kconfiggroup.h"
12#include <QCoreApplication>
13#include <QThread>
14#include <QThreadStorage>
15
16void _k_globalMainConfigSync();
17
18using SharedConfigList = QList<KSharedConfig *>;
19
20class GlobalSharedConfig
21{
22public:
23 GlobalSharedConfig()
24 : wasTestModeEnabled(false)
25 {
26 if (!qApp || QThread::currentThread() == qApp->thread()) {
27 // We want to force the sync() before the QCoreApplication
28 // instance is gone. Otherwise we trigger a QLockFile::lock()
29 // after QCoreApplication is gone, calling qAppName() for a non
30 // existent app...
31 qAddPostRoutine(&_k_globalMainConfigSync);
32 }
33 // In other threads, QThreadStorage takes care of deleting the GlobalSharedConfigList when
34 // the thread exits.
35 }
36
37 SharedConfigList configList;
38 // in addition to the list, we need to hold the main config,
39 // so that it's not created and destroyed all the time.
40 KSharedConfigPtr mainConfig;
41 bool wasTestModeEnabled;
42};
43
44static QThreadStorage<GlobalSharedConfig *> s_storage;
45template<typename T>
46T *perThreadGlobalStatic()
47{
48 if (!s_storage.hasLocalData()) {
49 s_storage.setLocalData(new T);
50 }
51 return s_storage.localData();
52}
53
54// Q_GLOBAL_STATIC(GlobalSharedConfigList, globalSharedConfigList), but per thread:
55static GlobalSharedConfig *globalSharedConfig()
56{
57 return perThreadGlobalStatic<GlobalSharedConfig>();
58}
59
60void _k_globalMainConfigSync()
61{
62 if (KSharedConfigPtr mainConfig = globalSharedConfig()->mainConfig) {
63 mainConfig->sync();
64 }
65}
66
67KSharedConfigPtr KSharedConfig::openConfig(const QString &_fileName, OpenFlags flags, QStandardPaths::StandardLocation resType)
68{
69 QString fileName(_fileName);
70 GlobalSharedConfig *global = globalSharedConfig();
71 if (fileName.isEmpty() && !flags.testFlag(flag: KConfig::SimpleConfig)) {
72 // Determine the config file name that KConfig will make up (see KConfigPrivate::changeFileName)
73 fileName = KConfig::mainConfigName();
74 }
75
76 if (!global->wasTestModeEnabled && QStandardPaths::isTestModeEnabled()) {
77 global->wasTestModeEnabled = true;
78 global->configList.clear();
79 global->mainConfig = nullptr;
80 }
81
82 for (auto *cfg : std::as_const(t&: global->configList)) {
83 if (cfg->name() == fileName && cfg->d_ptr->openFlags == flags && cfg->locationType() == resType) {
84 return KSharedConfigPtr(cfg);
85 }
86 }
87
88 KSharedConfigPtr ptr(new KSharedConfig(fileName, flags, resType));
89
90 if (_fileName.isEmpty() && flags == FullConfig && resType == QStandardPaths::GenericConfigLocation) {
91 global->mainConfig = ptr;
92
93 const bool isMainThread = !qApp || QThread::currentThread() == qApp->thread();
94 static bool userWarned = false;
95 if (isMainThread && !userWarned) {
96 userWarned = true;
97 const bool isReadOnly = qEnvironmentVariableIsEmpty(varName: "KDE_HOME_READONLY");
98 if (isReadOnly && QCoreApplication::applicationName() != QLatin1String("kdialog")) {
99 if (ptr->group(QStringLiteral("General")).readEntry(QStringLiteral("warn_unwritable_config"), aDefault: true)) {
100 ptr->isConfigWritable(warnUser: true);
101 }
102 }
103 }
104 }
105
106 return ptr;
107}
108
109KSharedConfig::Ptr KSharedConfig::openStateConfig(const QString &_fileName)
110{
111 // KF6 TODO: port this to XDG_STATE_HOME (default ~/.local/state)
112 // See https://gitlab.freedesktop.org/xdg/xdg-specs/-/blob/master/basedir/basedir-spec.xml
113 QString fileName(_fileName);
114
115 if (fileName.isEmpty()) {
116 fileName = QCoreApplication::applicationName() + QLatin1String("staterc");
117 }
118
119 return openConfig(fileName: fileName, flags: SimpleConfig, resType: QStandardPaths::AppDataLocation);
120}
121
122KSharedConfig::KSharedConfig(const QString &fileName, OpenFlags flags, QStandardPaths::StandardLocation resType)
123 : KConfig(fileName, flags, resType)
124{
125 globalSharedConfig()->configList.append(t: this);
126}
127
128KSharedConfig::~KSharedConfig()
129{
130 if (s_storage.hasLocalData()) {
131 globalSharedConfig()->configList.removeAll(t: this);
132 }
133}
134
135KConfigGroup KSharedConfig::groupImpl(const QString &groupName)
136{
137 KSharedConfigPtr ptr(this);
138 return KConfigGroup(ptr, groupName);
139}
140
141const KConfigGroup KSharedConfig::groupImpl(const QString &groupName) const
142{
143 const KSharedConfigPtr ptr(const_cast<KSharedConfig *>(this));
144 return KConfigGroup(ptr, groupName);
145}
146

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