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 Matthias Kalle Dalheimer <kalle@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "kconfiggroup.h"
11#include "kconfiggroup_p.h"
12
13#include "kconfig.h"
14#include "kconfig_core_log_settings.h"
15#include "kconfig_p.h"
16#include "kconfigdata_p.h"
17#include "ksharedconfig.h"
18
19#include <QDate>
20#include <QDir>
21#include <QFile>
22#include <QPoint>
23#include <QRect>
24#include <QSharedData>
25#include <QString>
26#include <QTextStream>
27#include <QUrl>
28#include <QUuid>
29
30#include <algorithm>
31#include <array>
32#include <math.h>
33#include <stdlib.h>
34
35class KConfigGroupPrivate : public QSharedData
36{
37public:
38 KConfigGroupPrivate(KConfig *owner, bool isImmutable, bool isConst, const QString &name)
39 : mOwner(owner)
40 , mName(name)
41 , bImmutable(isImmutable)
42 , bConst(isConst)
43 {
44 if (Q_UNLIKELY(!mOwner->name().isEmpty() && mOwner->accessMode() == KConfigBase::NoAccess)) {
45 qCWarning(KCONFIG_CORE_LOG) << "Created a KConfigGroup on an inaccessible config location" << mOwner->name() << name;
46 }
47 }
48
49 KConfigGroupPrivate(const KSharedConfigPtr &owner, const QString &name)
50 : sOwner(owner)
51 , mOwner(sOwner.data())
52 , mName(name)
53 , bImmutable(name.isEmpty() ? owner->isImmutable() : owner->isGroupImmutable(group: name))
54 , bConst(false)
55 {
56 if (Q_UNLIKELY(!mOwner->name().isEmpty() && mOwner->accessMode() == KConfigBase::NoAccess)) {
57 qCWarning(KCONFIG_CORE_LOG) << "Created a KConfigGroup on an inaccessible config location" << mOwner->name() << name;
58 }
59 }
60
61 KConfigGroupPrivate(KConfigGroup *parent, bool isImmutable, bool isConst, const QString &name)
62 : sOwner(parent->d->sOwner)
63 , mOwner(parent->d->mOwner)
64 , mName(name)
65 , bImmutable(isImmutable)
66 , bConst(isConst)
67 {
68 if (!parent->d->mName.isEmpty()) {
69 mParent = parent->d;
70 }
71 }
72
73 KConfigGroupPrivate(const KConfigGroupPrivate *other, bool isImmutable, const QString &name)
74 : sOwner(other->sOwner)
75 , mOwner(other->mOwner)
76 , mName(name)
77 , bImmutable(isImmutable)
78 , bConst(other->bConst)
79 {
80 if (!other->mName.isEmpty()) {
81 mParent = const_cast<KConfigGroupPrivate *>(other);
82 }
83 }
84
85 KSharedConfig::Ptr sOwner;
86 KConfig *mOwner;
87 QExplicitlySharedDataPointer<KConfigGroupPrivate> mParent;
88 QString mName;
89
90 /* bitfield */
91 const bool bImmutable : 1; // is this group immutable?
92 const bool bConst : 1; // is this group read-only?
93
94 QString fullName() const
95 {
96 if (!mParent) {
97 return name();
98 }
99 return mParent->fullName(aGroup: mName);
100 }
101
102 QString name() const
103 {
104 if (mName.isEmpty()) {
105 return QStringLiteral("<default>");
106 }
107 return mName;
108 }
109
110 QString fullName(const QString &aGroup) const
111 {
112 if (mName.isEmpty()) {
113 return aGroup;
114 }
115 return fullName() + QLatin1Char('\x1d') + aGroup;
116 }
117
118 static QExplicitlySharedDataPointer<KConfigGroupPrivate> create(KConfigBase *master, const QString &name, bool isImmutable, bool isConst)
119 {
120 QExplicitlySharedDataPointer<KConfigGroupPrivate> data;
121 if (dynamic_cast<KConfigGroup *>(master)) {
122 data = new KConfigGroupPrivate(static_cast<KConfigGroup *>(master), isImmutable, isConst, name);
123 } else {
124 data = new KConfigGroupPrivate(dynamic_cast<KConfig *>(master), isImmutable, isConst, name);
125 }
126 return data;
127 }
128
129 static QByteArray serializeList(const QList<QByteArray> &list);
130 static QStringList deserializeList(const QString &data);
131};
132
133QByteArray KConfigGroupPrivate::serializeList(const QList<QByteArray> &list)
134{
135 QByteArray value;
136
137 if (!list.isEmpty()) {
138 auto it = list.cbegin();
139 const auto end = list.cend();
140
141 value = QByteArray(*it).replace(before: '\\', QByteArrayLiteral("\\\\")).replace(before: ',', QByteArrayLiteral("\\,"));
142
143 while (++it != end) {
144 // In the loop, so it is not done when there is only one element.
145 // Doing it repeatedly is a pretty cheap operation.
146 value.reserve(asize: 4096);
147
148 value += ',';
149 value += QByteArray(*it).replace(before: '\\', QByteArrayLiteral("\\\\")).replace(before: ',', QByteArrayLiteral("\\,"));
150 }
151
152 // To be able to distinguish an empty list from a list with one empty element.
153 if (value.isEmpty()) {
154 value = QByteArrayLiteral("\\0");
155 }
156 }
157
158 return value;
159}
160
161QStringList KConfigGroupPrivate::deserializeList(const QString &data)
162{
163 if (data.isEmpty()) {
164 return QStringList();
165 }
166 if (data == QLatin1String("\\0")) {
167 return QStringList(QString());
168 }
169 QStringList value;
170 QString val;
171 val.reserve(asize: data.size());
172 bool quoted = false;
173 for (int p = 0; p < data.length(); p++) {
174 if (quoted) {
175 val += data[p];
176 quoted = false;
177 } else if (data[p].unicode() == '\\') {
178 quoted = true;
179 } else if (data[p].unicode() == ',') {
180 val.squeeze(); // release any unused memory
181 value.append(t: val);
182 val.clear();
183 val.reserve(asize: data.size() - p);
184 } else {
185 val += data[p];
186 }
187 }
188 value.append(t: val);
189 return value;
190}
191
192static QList<int> asIntList(const QByteArray &string)
193{
194 const auto &splitString = string.split(sep: ',');
195
196 QList<int> list;
197 list.reserve(asize: splitString.count());
198 for (const QByteArray &s : splitString) {
199 list << s.toInt();
200 }
201 return list;
202}
203
204static QList<qreal> asRealList(const QByteArray &string)
205{
206 const auto &splitString = string.split(sep: ',');
207
208 QList<qreal> list;
209 list.reserve(asize: splitString.count());
210 for (const QByteArray &s : splitString) {
211 list << s.toDouble();
212 }
213 return list;
214}
215
216static QString errString(const char *pKey, const QByteArray &value, const QVariant &aDefault)
217{
218 return QStringLiteral("\"%1\" - conversion of \"%3\" to %2 failed")
219 .arg(args: QString::fromLatin1(ba: pKey), args: QString::fromLatin1(ba: aDefault.typeName()), args: QString::fromLatin1(ba: value));
220}
221
222static QString formatError(int expected, int got)
223{
224 return QStringLiteral(" (wrong format: expected %1 items, got %2)").arg(a: expected).arg(a: got);
225}
226
227QVariant KConfigGroup::convertToQVariant(const char *pKey, const QByteArray &value, const QVariant &aDefault)
228{
229 // if a type handler is added here you must add a QVConversions definition
230 // to kconfigconversioncheck_p.h, or KConfigConversionCheck::to_QVariant will not allow
231 // readEntry<T> to convert to QVariant.
232 switch (static_cast<QMetaType::Type>(aDefault.userType())) {
233 case QMetaType::UnknownType:
234 return QVariant();
235 case QMetaType::QString:
236 // this should return the raw string not the dollar expanded string.
237 // imho if processed string is wanted should call
238 // readEntry(key, QString) not readEntry(key, QVariant)
239 return QString::fromUtf8(ba: value);
240 case QMetaType::QUuid:
241 return QUuid::fromString(string: value);
242 case QMetaType::QVariantList:
243 case QMetaType::QStringList:
244 return KConfigGroupPrivate::deserializeList(data: QString::fromUtf8(ba: value));
245 case QMetaType::QByteArray:
246 return value;
247 case QMetaType::Bool: {
248 static const std::array<const char *, 4> negatives = {"false", "no", "off", "0"};
249
250 return std::all_of(first: negatives.begin(), last: negatives.end(), pred: [value](const char *negativeString) {
251 return value.compare(a: negativeString, cs: Qt::CaseInsensitive) != 0;
252 });
253 }
254 case QMetaType::Double:
255 case QMetaType::Float:
256 case QMetaType::Int:
257 case QMetaType::UInt:
258 case QMetaType::LongLong:
259 case QMetaType::ULongLong: {
260 QVariant tmp = value;
261 if (!tmp.convert(type: aDefault.metaType())) {
262 tmp = aDefault;
263 }
264 return tmp;
265 }
266 case QMetaType::QPoint: {
267 const auto list = asIntList(string: value);
268
269 if (list.count() != 2) {
270 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(expected: 2, got: list.count());
271 return aDefault;
272 }
273 return QPoint(list.at(i: 0), list.at(i: 1));
274 }
275 case QMetaType::QPointF: {
276 const auto list = asRealList(string: value);
277
278 if (list.count() != 2) {
279 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(expected: 2, got: list.count());
280 return aDefault;
281 }
282 return QPointF(list.at(i: 0), list.at(i: 1));
283 }
284 case QMetaType::QRect: {
285 const auto list = asIntList(string: value);
286
287 if (list.count() != 4) {
288 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(expected: 4, got: list.count());
289 return aDefault;
290 }
291 const QRect rect(list.at(i: 0), list.at(i: 1), list.at(i: 2), list.at(i: 3));
292 if (!rect.isValid()) {
293 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
294 return aDefault;
295 }
296 return rect;
297 }
298 case QMetaType::QRectF: {
299 const auto list = asRealList(string: value);
300
301 if (list.count() != 4) {
302 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(expected: 4, got: list.count());
303 return aDefault;
304 }
305 const QRectF rect(list.at(i: 0), list.at(i: 1), list.at(i: 2), list.at(i: 3));
306 if (!rect.isValid()) {
307 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
308 return aDefault;
309 }
310 return rect;
311 }
312 case QMetaType::QSize: {
313 const auto list = asIntList(string: value);
314
315 if (list.count() != 2) {
316 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(expected: 2, got: list.count());
317 return aDefault;
318 }
319 const QSize size(list.at(i: 0), list.at(i: 1));
320 if (!size.isValid()) {
321 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
322 return aDefault;
323 }
324 return size;
325 }
326 case QMetaType::QSizeF: {
327 const auto list = asRealList(string: value);
328
329 if (list.count() != 2) {
330 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(expected: 2, got: list.count());
331 return aDefault;
332 }
333 const QSizeF size(list.at(i: 0), list.at(i: 1));
334 if (!size.isValid()) {
335 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
336 return aDefault;
337 }
338 return size;
339 }
340 case QMetaType::QDateTime: {
341 const auto list = asRealList(string: value);
342 if (list.count() < 6) {
343 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(expected: 6, got: list.count());
344 return aDefault;
345 }
346 const QDate date(list.at(i: 0), list.at(i: 1), list.at(i: 2));
347 const qreal totalSeconds = list.at(i: 5);
348 qreal seconds;
349 const qreal fractional = modf(x: totalSeconds, iptr: &seconds);
350 const qreal milliseconds = round(x: fractional * 1000.0);
351 const QTime time(list.at(i: 3), list.at(i: 4), seconds, milliseconds);
352 const QDateTime dt(date, time);
353 if (!dt.isValid()) {
354 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
355 return aDefault;
356 }
357 return dt;
358 }
359 case QMetaType::QDate: {
360 auto list = asIntList(string: value);
361 if (list.count() == 6) {
362 list = list.mid(pos: 0, len: 3); // don't break config files that stored QDate as QDateTime
363 }
364 if (list.count() != 3) {
365 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(expected: 3, got: list.count());
366 return aDefault;
367 }
368 const QDate date(list.at(i: 0), list.at(i: 1), list.at(i: 2));
369 if (!date.isValid()) {
370 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
371 return aDefault;
372 }
373 return date;
374 }
375 case QMetaType::QColor:
376 case QMetaType::QFont:
377 qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::readEntry was passed GUI type '" << aDefault.typeName()
378 << "' but KConfigGui isn't linked! If it is linked to your program, "
379 "this is a platform bug. Please inform the KDE developers";
380 break;
381 case QMetaType::QUrl:
382 return QUrl(QString::fromUtf8(ba: value));
383
384 default:
385 break;
386 }
387
388 qCWarning(KCONFIG_CORE_LOG) << "unhandled type " << aDefault.typeName();
389 return QVariant();
390}
391
392static bool cleanHomeDirPath(QString &path, const QString &homeDir)
393{
394#ifdef Q_OS_WIN // safer
395 if (!QDir::toNativeSeparators(path).startsWith(QDir::toNativeSeparators(homeDir))) {
396 return false;
397 }
398#else
399 if (!path.startsWith(s: homeDir)) {
400 return false;
401 }
402#endif
403
404 int len = homeDir.length();
405 // replace by "$HOME" if possible
406 if (len && (path.length() == len || path[len] == QLatin1Char('/'))) {
407 path.replace(i: 0, len, QStringLiteral("$HOME"));
408 return true;
409 }
410
411 return false;
412}
413
414static QString translatePath(QString path) // krazy:exclude=passbyvalue
415{
416 if (path.isEmpty()) {
417 return path;
418 }
419
420 // only "our" $HOME should be interpreted
421 path.replace(c: QLatin1Char('$'), after: QLatin1String("$$"));
422
423 const bool startsWithFile = path.startsWith(s: QLatin1String("file:"), cs: Qt::CaseInsensitive);
424 path = startsWithFile ? QUrl(path).toLocalFile() : path;
425
426 if (QDir::isRelativePath(path)) {
427 return path;
428 }
429
430 // Use the same thing as what expandString() will do, to keep data intact
431#ifdef Q_OS_WIN
432 const QString homeDir = QDir::homePath();
433#else
434 const QString homeDir = QFile::decodeName(localFileName: qgetenv(varName: "HOME"));
435#endif
436 (void)cleanHomeDirPath(path, homeDir);
437
438 if (startsWithFile) {
439 path = QUrl::fromLocalFile(localfile: path).toString();
440 }
441
442 return path;
443}
444
445KConfigGroup::KConfigGroup()
446 : d()
447{
448}
449
450bool KConfigGroup::isValid() const
451{
452 return bool(d);
453}
454
455KConfigGroupGui _kde_internal_KConfigGroupGui;
456static inline bool readEntryGui(const QByteArray &data, const char *key, const QVariant &input, QVariant &output)
457{
458 if (_kde_internal_KConfigGroupGui.readEntryGui) {
459 return _kde_internal_KConfigGroupGui.readEntryGui(data, key, input, output);
460 }
461 return false;
462}
463
464static inline bool writeEntryGui(KConfigGroup *cg, const char *key, const QVariant &input, KConfigGroup::WriteConfigFlags flags)
465{
466 if (_kde_internal_KConfigGroupGui.writeEntryGui) {
467 return _kde_internal_KConfigGroupGui.writeEntryGui(cg, key, input, flags);
468 }
469 return false;
470}
471
472KConfigGroup::KConfigGroup(KConfigBase *master, const QString &_group)
473 : d(KConfigGroupPrivate::create(master, name: _group, isImmutable: master->isGroupImmutable(group: _group), isConst: false))
474{
475}
476
477KConfigGroup::KConfigGroup(const KConfigBase *master, const QString &_group)
478 : d(KConfigGroupPrivate::create(master: const_cast<KConfigBase *>(master), name: _group, isImmutable: master->isGroupImmutable(group: _group), isConst: true))
479{
480}
481
482KConfigGroup::KConfigGroup(const KSharedConfigPtr &master, const QString &_group)
483 : d(new KConfigGroupPrivate(master, _group))
484{
485}
486
487KConfigGroup &KConfigGroup::operator=(const KConfigGroup &rhs)
488{
489 d = rhs.d;
490 return *this;
491}
492
493KConfigGroup::KConfigGroup(const KConfigGroup &rhs)
494 : d(rhs.d)
495{
496}
497
498KConfigGroup::~KConfigGroup()
499{
500 d.reset();
501}
502
503KConfigGroup KConfigGroup::groupImpl(const QString &aGroup)
504{
505 Q_ASSERT_X(isValid(), "KConfigGroup::groupImpl", "accessing an invalid group");
506 Q_ASSERT_X(!aGroup.isEmpty(), "KConfigGroup::groupImpl", "can not have an unnamed child group");
507
508 KConfigGroup newGroup;
509
510 newGroup.d = new KConfigGroupPrivate(this, isGroupImmutableImpl(groupName: aGroup), d->bConst, aGroup);
511
512 return newGroup;
513}
514
515const KConfigGroup KConfigGroup::groupImpl(const QString &aGroup) const
516{
517 Q_ASSERT_X(isValid(), "KConfigGroup::groupImpl", "accessing an invalid group");
518 Q_ASSERT_X(!aGroup.isEmpty(), "KConfigGroup::groupImpl", "can not have an unnamed child group");
519
520 KConfigGroup newGroup;
521
522 newGroup.d = new KConfigGroupPrivate(const_cast<KConfigGroup *>(this), isGroupImmutableImpl(groupName: aGroup), true, aGroup);
523
524 return newGroup;
525}
526
527KConfigGroup KConfigGroup::parent() const
528{
529 Q_ASSERT_X(isValid(), "KConfigGroup::parent", "accessing an invalid group");
530
531 KConfigGroup parentGroup;
532
533 if (d->mParent) {
534 parentGroup.d = d->mParent;
535 } else {
536 parentGroup.d = new KConfigGroupPrivate(d->mOwner, d->mOwner->isImmutable(), d->bConst, QString());
537 // make sure we keep the refcount up on the KConfig object
538 parentGroup.d->sOwner = d->sOwner;
539 }
540
541 return parentGroup;
542}
543
544void KConfigGroup::deleteGroup(WriteConfigFlags flags)
545{
546 Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroup", "accessing an invalid group");
547 Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroup", "deleting a read-only group");
548
549 config()->deleteGroup(group: d->fullName(), flags);
550}
551
552QString KConfigGroup::name() const
553{
554 Q_ASSERT_X(isValid(), "KConfigGroup::name", "accessing an invalid group");
555
556 return d->name();
557}
558
559bool KConfigGroup::exists() const
560{
561 Q_ASSERT_X(isValid(), "KConfigGroup::exists", "accessing an invalid group");
562
563 return config()->hasGroup(group: d->fullName());
564}
565
566bool KConfigGroup::sync()
567{
568 Q_ASSERT_X(isValid(), "KConfigGroup::sync", "accessing an invalid group");
569
570 if (!d->bConst) {
571 return config()->sync();
572 }
573
574 return false;
575}
576
577QMap<QString, QString> KConfigGroup::entryMap() const
578{
579 Q_ASSERT_X(isValid(), "KConfigGroup::entryMap", "accessing an invalid group");
580
581 return config()->entryMap(aGroup: d->fullName());
582}
583
584KConfig *KConfigGroup::config()
585{
586 Q_ASSERT_X(isValid(), "KConfigGroup::config", "accessing an invalid group");
587
588 return d->mOwner;
589}
590
591const KConfig *KConfigGroup::config() const
592{
593 Q_ASSERT_X(isValid(), "KConfigGroup::config", "accessing an invalid group");
594
595 return d->mOwner;
596}
597
598bool KConfigGroup::isEntryImmutable(const char *key) const
599{
600 Q_ASSERT_X(isValid(), "KConfigGroup::isEntryImmutable", "accessing an invalid group");
601
602 return (isImmutable() || !config()->d_func()->canWriteEntry(group: d->fullName(), key, isDefault: config()->readDefaults()));
603}
604
605bool KConfigGroup::isEntryImmutable(const QString &key) const
606{
607 return isEntryImmutable(key: key.toUtf8().constData());
608}
609
610QString KConfigGroup::readEntryUntranslated(const QString &pKey, const QString &aDefault) const
611{
612 return readEntryUntranslated(key: pKey.toUtf8().constData(), aDefault);
613}
614
615QString KConfigGroup::readEntryUntranslated(const char *key, const QString &aDefault) const
616{
617 Q_ASSERT_X(isValid(), "KConfigGroup::readEntryUntranslated", "accessing an invalid group");
618
619 QString result = config()->d_func()->lookupData(group: d->fullName(), key, flags: KEntryMap::SearchFlags(), expand: nullptr);
620 if (result.isNull()) {
621 return aDefault;
622 }
623 return result;
624}
625
626QString KConfigGroup::readEntry(const char *key, const char *aDefault) const
627{
628 return readEntry(key, aDefault: QString::fromUtf8(utf8: aDefault));
629}
630
631QString KConfigGroup::readEntry(const QString &key, const char *aDefault) const
632{
633 return readEntry(key: key.toUtf8().constData(), aDefault);
634}
635
636QString KConfigGroup::readEntry(const char *key, const QString &aDefault) const
637{
638 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
639
640 bool expand = false;
641
642 // read value from the entry map
643 QString aValue = config()->d_func()->lookupData(group: d->fullName(), key, flags: KEntryMap::SearchLocalized, expand: &expand);
644 if (aValue.isNull()) {
645 aValue = aDefault;
646 }
647
648 if (expand) {
649 return KConfigPrivate::expandString(value: aValue);
650 }
651
652 return aValue;
653}
654
655QString KConfigGroup::readEntry(const QString &key, const QString &aDefault) const
656{
657 return readEntry(key: key.toUtf8().constData(), aDefault);
658}
659
660QStringList KConfigGroup::readEntry(const char *key, const QStringList &aDefault) const
661{
662 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
663
664 const QString data = readEntry(key, aDefault: QString());
665 if (data.isNull()) {
666 return aDefault;
667 }
668
669 return KConfigGroupPrivate::deserializeList(data);
670}
671
672QStringList KConfigGroup::readEntry(const QString &key, const QStringList &aDefault) const
673{
674 return readEntry(key: key.toUtf8().constData(), aDefault);
675}
676
677QVariant KConfigGroup::readEntry(const char *key, const QVariant &aDefault) const
678{
679 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
680
681 const QByteArray data = config()->d_func()->lookupData(group: d->fullName(), key, flags: KEntryMap::SearchLocalized);
682 if (data.isNull()) {
683 return aDefault;
684 }
685
686 QVariant value;
687 if (!readEntryGui(data, key, input: aDefault, output&: value)) {
688 return convertToQVariant(pKey: key, value: data, aDefault);
689 }
690
691 return value;
692}
693
694QVariant KConfigGroup::readEntry(const QString &key, const QVariant &aDefault) const
695{
696 return readEntry(key: key.toUtf8().constData(), aDefault);
697}
698
699QVariantList KConfigGroup::readEntry(const char *key, const QVariantList &aDefault) const
700{
701 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
702
703 const QString data = readEntry(key, aDefault: QString());
704 if (data.isNull()) {
705 return aDefault;
706 }
707
708 const auto &list = KConfigGroupPrivate::deserializeList(data);
709
710 QVariantList value;
711 value.reserve(asize: list.count());
712 for (const QString &v : list) {
713 value << v;
714 }
715
716 return value;
717}
718
719QVariantList KConfigGroup::readEntry(const QString &key, const QVariantList &aDefault) const
720{
721 return readEntry(key: key.toUtf8().constData(), aDefault);
722}
723
724QStringList KConfigGroup::readXdgListEntry(const QString &key, const QStringList &aDefault) const
725{
726 return readXdgListEntry(key: key.toUtf8().constData(), aDefault);
727}
728
729QStringList KConfigGroup::readXdgListEntry(const char *key, const QStringList &aDefault) const
730{
731 Q_ASSERT_X(isValid(), "KConfigGroup::readXdgListEntry", "accessing an invalid group");
732
733 const QString data = readEntry(key, aDefault: QString());
734 if (data.isNull()) {
735 return aDefault;
736 }
737
738 QStringList value;
739 QString val;
740 val.reserve(asize: data.size());
741 // XXX List serialization being a separate layer from low-level parsing is
742 // probably a bug. No affected entries are defined, though.
743 bool quoted = false;
744 for (int p = 0; p < data.length(); p++) {
745 if (quoted) {
746 val += data[p];
747 quoted = false;
748 } else if (data[p] == QLatin1Char('\\')) {
749 quoted = true;
750 } else if (data[p] == QLatin1Char(';')) {
751 value.append(t: val);
752 val.clear();
753 val.reserve(asize: data.size() - p);
754 } else {
755 val += data[p];
756 }
757 }
758 if (!val.isEmpty()) {
759 value.append(t: val);
760 }
761 return value;
762}
763
764QString KConfigGroup::readPathEntry(const QString &pKey, const QString &aDefault) const
765{
766 return readPathEntry(key: pKey.toUtf8().constData(), aDefault);
767}
768
769QString KConfigGroup::readPathEntry(const char *key, const QString &aDefault) const
770{
771 Q_ASSERT_X(isValid(), "KConfigGroup::readPathEntry", "accessing an invalid group");
772
773 bool expand = false;
774
775 QString aValue = config()->d_func()->lookupData(group: d->fullName(), key, flags: KEntryMap::SearchLocalized, expand: &expand);
776 if (aValue.isNull()) {
777 aValue = aDefault;
778 }
779
780 return KConfigPrivate::expandString(value: aValue);
781}
782
783QStringList KConfigGroup::readPathEntry(const QString &pKey, const QStringList &aDefault) const
784{
785 return readPathEntry(key: pKey.toUtf8().constData(), aDefault);
786}
787
788QStringList KConfigGroup::readPathEntry(const char *key, const QStringList &aDefault) const
789{
790 Q_ASSERT_X(isValid(), "KConfigGroup::readPathEntry", "accessing an invalid group");
791
792 const QString data = readPathEntry(key, aDefault: QString());
793 if (data.isNull()) {
794 return aDefault;
795 }
796
797 return KConfigGroupPrivate::deserializeList(data);
798}
799
800void KConfigGroup::writeEntry(const char *key, const QString &value, WriteConfigFlags flags)
801{
802 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
803 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
804
805 writeEntry(key, value: value.toUtf8(), pFlags: flags);
806}
807
808void KConfigGroup::writeEntry(const QString &key, const QString &value, WriteConfigFlags flags)
809{
810 writeEntry(key: key.toUtf8().constData(), value, flags);
811}
812
813void KConfigGroup::writeEntry(const QString &key, const char *value, WriteConfigFlags pFlags)
814{
815 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
816 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
817
818 writeEntry(key: key.toUtf8().constData(), value: QVariant(QString::fromLatin1(ba: value)), pFlags);
819}
820
821void KConfigGroup::writeEntry(const char *key, const char *value, WriteConfigFlags pFlags)
822{
823 writeEntry(key, value: QVariant(QString::fromLatin1(ba: value)), pFlags);
824}
825
826void KConfigGroup::writeEntry(const char *key, const QByteArray &value, WriteConfigFlags flags)
827{
828 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
829 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
830
831 config()->d_func()->putData(groupName: d->fullName(), key, value: value.isNull() ? QByteArray("") : value, flags);
832}
833
834void KConfigGroup::writeEntry(const QString &key, const QByteArray &value, WriteConfigFlags pFlags)
835{
836 writeEntry(key: key.toUtf8().constData(), value, flags: pFlags);
837}
838
839void KConfigGroup::writeEntry(const char *key, const QStringList &list, WriteConfigFlags flags)
840{
841 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
842 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
843
844 QList<QByteArray> balist;
845 balist.reserve(asize: list.count());
846
847 for (const QString &entry : list) {
848 balist.append(t: entry.toUtf8());
849 }
850
851 writeEntry(key, value: KConfigGroupPrivate::serializeList(list: balist), flags);
852}
853
854void KConfigGroup::writeEntry(const QString &key, const QStringList &list, WriteConfigFlags flags)
855{
856 writeEntry(key: key.toUtf8().constData(), list, flags);
857}
858
859void KConfigGroup::writeEntry(const char *key, const QVariantList &list, WriteConfigFlags flags)
860{
861 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
862 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
863
864 QList<QByteArray> data;
865 data.reserve(asize: list.count());
866
867 for (const QVariant &v : list) {
868 if (v.userType() == QMetaType::QByteArray) {
869 data << v.toByteArray();
870 } else {
871 data << v.toString().toUtf8();
872 }
873 }
874
875 writeEntry(key, value: KConfigGroupPrivate::serializeList(list: data), flags);
876}
877
878void KConfigGroup::writeEntry(const char *key, const QVariant &value, WriteConfigFlags flags)
879{
880 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
881 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
882
883 if (writeEntryGui(cg: this, key, input: value, flags)) {
884 return; // GUI type that was handled
885 }
886
887 QByteArray data;
888 // if a type handler is added here you must add a QVConversions definition
889 // to kconfigconversioncheck_p.h, or KConfigConversionCheck::to_QVariant will not allow
890 // writeEntry<T> to convert to QVariant.
891 switch (static_cast<QMetaType::Type>(value.userType())) {
892 case QMetaType::UnknownType:
893 data = "";
894 break;
895 case QMetaType::QByteArray:
896 data = value.toByteArray();
897 break;
898 case QMetaType::QString:
899 case QMetaType::Int:
900 case QMetaType::UInt:
901 case QMetaType::Double:
902 case QMetaType::Float:
903 case QMetaType::Bool:
904 case QMetaType::LongLong:
905 case QMetaType::ULongLong:
906 data = value.toString().toUtf8();
907 break;
908 case QMetaType::QVariantList:
909 if (!value.canConvert<QStringList>()) {
910 qCWarning(KCONFIG_CORE_LOG) << "not all types in \"" << key
911 << "\" can convert to QString,"
912 " information will be lost";
913 }
914 Q_FALLTHROUGH();
915 case QMetaType::QStringList:
916 writeEntry(key, list: value.toList(), flags);
917 return;
918 case QMetaType::QPoint: {
919 const QPoint rPoint = value.toPoint();
920
921 const QVariantList list{rPoint.x(), rPoint.y()};
922
923 writeEntry(key, list, flags);
924 return;
925 }
926 case QMetaType::QPointF: {
927 const QPointF point = value.toPointF();
928
929 const QVariantList list{point.x(), point.y()};
930
931 writeEntry(key, list, flags);
932 return;
933 }
934 case QMetaType::QRect: {
935 const QRect rRect = value.toRect();
936
937 const QVariantList list{rRect.left(), rRect.top(), rRect.width(), rRect.height()};
938
939 writeEntry(key, list, flags);
940 return;
941 }
942 case QMetaType::QRectF: {
943 const QRectF rRectF = value.toRectF();
944
945 const QVariantList list{rRectF.left(), rRectF.top(), rRectF.width(), rRectF.height()};
946
947 writeEntry(key, list, flags);
948 return;
949 }
950 case QMetaType::QSize: {
951 const QSize rSize = value.toSize();
952
953 const QVariantList list{rSize.width(), rSize.height()};
954
955 writeEntry(key, list, flags);
956 return;
957 }
958 case QMetaType::QUuid: {
959 writeEntry(key, value: value.toString(), flags);
960 return;
961 }
962 case QMetaType::QSizeF: {
963 const QSizeF rSizeF = value.toSizeF();
964
965 const QVariantList list{rSizeF.width(), rSizeF.height()};
966
967 writeEntry(key, list, flags);
968 return;
969 }
970 case QMetaType::QDate: {
971 const QDate date = value.toDate();
972
973 const QVariantList list{date.year(), date.month(), date.day()};
974
975 writeEntry(key, list, flags);
976 return;
977 }
978 case QMetaType::QDateTime: {
979 const QDateTime rDateTime = value.toDateTime();
980
981 const QTime time = rDateTime.time();
982 const QDate date = rDateTime.date();
983
984 const QVariantList list{
985 date.year(),
986 date.month(),
987 date.day(),
988
989 time.hour(),
990 time.minute(),
991 time.second() + time.msec() / 1000.0,
992 };
993
994 writeEntry(key, list, flags);
995 return;
996 }
997
998 case QMetaType::QColor:
999 case QMetaType::QFont:
1000 qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::writeEntry was passed GUI type '" << value.typeName()
1001 << "' but KConfigGui isn't linked! If it is linked to your program, this is a platform bug. "
1002 "Please inform the KDE developers";
1003 break;
1004 case QMetaType::QUrl:
1005 data = QUrl(value.toUrl()).toString().toUtf8();
1006 break;
1007 default:
1008 qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::writeEntry - unhandled type" << value.typeName() << "in group" << name();
1009 }
1010
1011 writeEntry(key, value: data, flags);
1012}
1013
1014void KConfigGroup::writeEntry(const QString &key, const QVariant &value, WriteConfigFlags flags)
1015{
1016 writeEntry(key: key.toUtf8().constData(), value, flags);
1017}
1018
1019void KConfigGroup::writeEntry(const QString &key, const QVariantList &list, WriteConfigFlags flags)
1020{
1021 writeEntry(key: key.toUtf8().constData(), list, flags);
1022}
1023
1024void KConfigGroup::writeXdgListEntry(const QString &key, const QStringList &value, WriteConfigFlags pFlags)
1025{
1026 writeXdgListEntry(key: key.toUtf8().constData(), value, pFlags);
1027}
1028
1029void KConfigGroup::writeXdgListEntry(const char *key, const QStringList &list, WriteConfigFlags flags)
1030{
1031 Q_ASSERT_X(isValid(), "KConfigGroup::writeXdgListEntry", "accessing an invalid group");
1032 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeXdgListEntry", "writing to a read-only group");
1033
1034 QString value;
1035 value.reserve(asize: 4096);
1036
1037 // XXX List serialization being a separate layer from low-level escaping is
1038 // probably a bug. No affected entries are defined, though.
1039 for (QString val : list) { // clazy:exclude=range-loop
1040 val.replace(c: QLatin1Char('\\'), after: QLatin1String("\\\\")).replace(c: QLatin1Char(';'), after: QLatin1String("\\;"));
1041 value += val + QLatin1Char(';');
1042 }
1043
1044 writeEntry(key, value, flags);
1045}
1046
1047void KConfigGroup::writePathEntry(const QString &pKey, const QString &path, WriteConfigFlags pFlags)
1048{
1049 writePathEntry(Key: pKey.toUtf8().constData(), path, pFlags);
1050}
1051
1052void KConfigGroup::writePathEntry(const char *pKey, const QString &path, WriteConfigFlags pFlags)
1053{
1054 Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group");
1055 Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group");
1056
1057 config()->d_func()->putData(groupName: d->fullName(), key: pKey, value: translatePath(path).toUtf8(), flags: pFlags, expand: true);
1058}
1059
1060void KConfigGroup::writePathEntry(const QString &pKey, const QStringList &value, WriteConfigFlags pFlags)
1061{
1062 writePathEntry(key: pKey.toUtf8().constData(), value, pFlags);
1063}
1064
1065void KConfigGroup::writePathEntry(const char *pKey, const QStringList &value, WriteConfigFlags pFlags)
1066{
1067 Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group");
1068 Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group");
1069
1070 QList<QByteArray> list;
1071 list.reserve(asize: value.length());
1072 for (const QString &path : value) {
1073 list << translatePath(path).toUtf8();
1074 }
1075
1076 config()->d_func()->putData(groupName: d->fullName(), key: pKey, value: KConfigGroupPrivate::serializeList(list), flags: pFlags, expand: true);
1077}
1078
1079void KConfigGroup::deleteEntry(const char *key, WriteConfigFlags flags)
1080{
1081 Q_ASSERT_X(isValid(), "KConfigGroup::deleteEntry", "accessing an invalid group");
1082 Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteEntry", "deleting from a read-only group");
1083
1084 config()->d_func()->putData(groupName: d->fullName(), key, value: QByteArray(), flags);
1085}
1086
1087void KConfigGroup::deleteEntry(const QString &key, WriteConfigFlags flags)
1088{
1089 deleteEntry(key: key.toUtf8().constData(), flags);
1090}
1091
1092void KConfigGroup::revertToDefault(const char *key, WriteConfigFlags flags)
1093{
1094 Q_ASSERT_X(isValid(), "KConfigGroup::revertToDefault", "accessing an invalid group");
1095 Q_ASSERT_X(!d->bConst, "KConfigGroup::revertToDefault", "writing to a read-only group");
1096
1097 config()->d_func()->revertEntry(group: d->fullName(), key, flags);
1098}
1099
1100void KConfigGroup::revertToDefault(const QString &key, WriteConfigFlags flags)
1101{
1102 revertToDefault(key: key.toUtf8().constData(), flags);
1103}
1104
1105bool KConfigGroup::hasDefault(const char *key) const
1106{
1107 Q_ASSERT_X(isValid(), "KConfigGroup::hasDefault", "accessing an invalid group");
1108
1109 KEntryMap::SearchFlags flags = KEntryMap::SearchDefaults | KEntryMap::SearchLocalized;
1110
1111 return !config()->d_func()->lookupData(group: d->fullName(), key, flags).isNull();
1112}
1113
1114bool KConfigGroup::hasDefault(const QString &key) const
1115{
1116 return hasDefault(key: key.toUtf8().constData());
1117}
1118
1119bool KConfigGroup::hasKey(const char *key) const
1120{
1121 Q_ASSERT_X(isValid(), "KConfigGroup::hasKey", "accessing an invalid group");
1122
1123 KEntryMap::SearchFlags flags = KEntryMap::SearchLocalized;
1124 if (config()->readDefaults()) {
1125 flags |= KEntryMap::SearchDefaults;
1126 }
1127
1128 return !config()->d_func()->lookupData(group: d->fullName(), key, flags).isNull();
1129}
1130
1131bool KConfigGroup::hasKey(const QString &key) const
1132{
1133 return hasKey(key: key.toUtf8().constData());
1134}
1135
1136bool KConfigGroup::isImmutable() const
1137{
1138 Q_ASSERT_X(isValid(), "KConfigGroup::isImmutable", "accessing an invalid group");
1139
1140 return d->bImmutable;
1141}
1142
1143QStringList KConfigGroup::groupList() const
1144{
1145 Q_ASSERT_X(isValid(), "KConfigGroup::groupList", "accessing an invalid group");
1146
1147 return config()->d_func()->groupList(groupName: d->fullName());
1148}
1149
1150QStringList KConfigGroup::keyList() const
1151{
1152 Q_ASSERT_X(isValid(), "KConfigGroup::keyList", "accessing an invalid group");
1153
1154 return config()->d_func()->usedKeyList(theGroup: d->fullName());
1155}
1156
1157void KConfigGroup::markAsClean()
1158{
1159 Q_ASSERT_X(isValid(), "KConfigGroup::markAsClean", "accessing an invalid group");
1160
1161 config()->markAsClean();
1162}
1163
1164KConfigGroup::AccessMode KConfigGroup::accessMode() const
1165{
1166 Q_ASSERT_X(isValid(), "KConfigGroup::accessMode", "accessing an invalid group");
1167
1168 return config()->accessMode();
1169}
1170
1171bool KConfigGroup::hasGroupImpl(const QString &b) const
1172{
1173 Q_ASSERT_X(isValid(), "KConfigGroup::hasGroupImpl", "accessing an invalid group");
1174
1175 return config()->hasGroup(group: d->fullName(aGroup: b));
1176}
1177
1178void KConfigGroup::deleteGroupImpl(const QString &b, WriteConfigFlags flags)
1179{
1180 Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroupImpl", "accessing an invalid group");
1181 Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroupImpl", "deleting from a read-only group");
1182
1183 config()->deleteGroup(group: d->fullName(aGroup: b), flags);
1184}
1185
1186bool KConfigGroup::isGroupImmutableImpl(const QString &groupName) const
1187{
1188 Q_ASSERT_X(isValid(), "KConfigGroup::isGroupImmutableImpl", "accessing an invalid group");
1189
1190 if (!hasGroupImpl(b: groupName)) { // group doesn't exist yet
1191 return d->bImmutable; // child groups are immutable if the parent is immutable.
1192 }
1193
1194 return config()->isGroupImmutable(group: d->fullName(aGroup: groupName));
1195}
1196
1197void KConfigGroup::copyTo(KConfigBase *other, WriteConfigFlags pFlags) const
1198{
1199 Q_ASSERT_X(isValid(), "KConfigGroup::copyTo", "accessing an invalid group");
1200 Q_ASSERT(other != nullptr);
1201
1202 if (KConfigGroup *otherGroup = dynamic_cast<KConfigGroup *>(other)) {
1203 config()->d_func()->copyGroup(source: d->fullName(), destination: otherGroup->d->fullName(), otherGroup, flags: pFlags);
1204 } else if (KConfig *otherConfig = dynamic_cast<KConfig *>(other)) {
1205 KConfigGroup newGroup = otherConfig->group(group: d->fullName());
1206 otherConfig->d_func()->copyGroup(source: d->fullName(), destination: d->fullName(), otherGroup: &newGroup, flags: pFlags);
1207 } else {
1208 Q_ASSERT_X(false, "KConfigGroup::copyTo", "unknown type of KConfigBase");
1209 }
1210}
1211
1212void KConfigGroup::reparent(KConfigBase *parent, WriteConfigFlags pFlags)
1213{
1214 Q_ASSERT_X(isValid(), "KConfigGroup::reparent", "accessing an invalid group");
1215 Q_ASSERT_X(!d->bConst, "KConfigGroup::reparent", "reparenting a read-only group");
1216 Q_ASSERT_X(!d->bImmutable, "KConfigGroup::reparent", "reparenting an immutable group");
1217 Q_ASSERT(parent != nullptr);
1218
1219 KConfigGroup oldGroup(*this);
1220
1221 d = KConfigGroupPrivate::create(master: parent, name: d->mName, isImmutable: false, isConst: false);
1222 oldGroup.copyTo(other: this, pFlags);
1223 oldGroup.deleteGroup(); // so that the entries with the old group name are deleted on sync
1224}
1225
1226void KConfigGroup::moveValuesTo(const QList<const char *> &keys, KConfigGroup &other, WriteConfigFlags pFlags)
1227{
1228 Q_ASSERT(isValid());
1229 Q_ASSERT(other.isValid());
1230
1231 for (const auto key : keys) {
1232 const QString groupName = name();
1233 const auto entry = config()->d_ptr->lookupInternalEntry(group: groupName, key, flags: KEntryMap::SearchLocalized);
1234
1235 // Only write the entry if it is not null, if it is a global enry there is no point in moving it
1236 if (!entry.mValue.isNull() && !entry.bGlobal) {
1237 deleteEntry(key, flags: pFlags);
1238 KEntryMap::EntryOptions options = KEntryMap::EntryOption::EntryDirty;
1239 if (entry.bDeleted) {
1240 options |= KEntryMap::EntryDeleted;
1241 }
1242
1243 if (entry.bExpand) {
1244 options |= KEntryMap::EntryExpansion;
1245 }
1246
1247 other.config()->d_ptr->setEntryData(groupName: other.name(), key, value: entry.mValue, flags: options);
1248 }
1249 }
1250}
1251

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