1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qdebug.h>
5#include "qplatformdefs.h"
6#include "qsettings.h"
7
8#include "qsettings_p.h"
9#include "qcache.h"
10#include "qfile.h"
11#include "qdir.h"
12#include "qfileinfo.h"
13#include "qmutex.h"
14#include "private/qlocking_p.h"
15#include "private/qtools_p.h"
16#include "qlibraryinfo.h"
17#include "qtemporaryfile.h"
18#include "qstandardpaths.h"
19#include <qdatastream.h>
20#include "private/qstringconverter_p.h"
21
22#ifndef QT_NO_GEOM_VARIANT
23#include "qsize.h"
24#include "qpoint.h"
25#include "qrect.h"
26#endif // !QT_NO_GEOM_VARIANT
27
28#include "qcoreapplication.h"
29
30#ifndef QT_BOOTSTRAPPED
31#include "qsavefile.h"
32#include "qlockfile.h"
33#endif
34
35#ifdef Q_OS_VXWORKS
36# include <ioLib.h>
37#endif
38
39#include <algorithm>
40#include <stdlib.h>
41
42#ifdef Q_OS_WIN // for homedirpath reading from registry
43# include <qt_windows.h>
44# include <shlobj.h>
45#endif
46
47#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
48#define Q_XDG_PLATFORM
49#endif
50
51#if !defined(QT_NO_STANDARDPATHS) \
52 && (defined(Q_XDG_PLATFORM) || defined(QT_PLATFORM_UIKIT) || defined(Q_OS_ANDROID))
53# define QSETTINGS_USE_QSTANDARDPATHS
54#endif
55
56// ************************************************************************
57// QConfFile
58
59/*
60 QConfFile objects are explicitly shared within the application.
61 This ensures that modification to the settings done through one
62 QSettings object are immediately reflected in other setting
63 objects of the same application.
64*/
65
66QT_BEGIN_NAMESPACE
67
68using namespace Qt::StringLiterals;
69using namespace QtMiscUtils;
70
71struct QConfFileCustomFormat
72{
73 QString extension;
74 QSettings::ReadFunc readFunc;
75 QSettings::WriteFunc writeFunc;
76 Qt::CaseSensitivity caseSensitivity;
77};
78Q_DECLARE_TYPEINFO(QConfFileCustomFormat, Q_RELOCATABLE_TYPE);
79
80typedef QHash<QString, QConfFile *> ConfFileHash;
81typedef QCache<QString, QConfFile> ConfFileCache;
82namespace {
83 struct Path
84 {
85 // Note: Defining constructors explicitly because of buggy C++11
86 // implementation in MSVC (uniform initialization).
87 Path() {}
88 Path(const QString & p, bool ud) : path(p), userDefined(ud) {}
89 QString path;
90 bool userDefined = false; //!< true - user defined, overridden by setPath
91 };
92}
93typedef QHash<int, Path> PathHash;
94typedef QList<QConfFileCustomFormat> CustomFormatVector;
95
96Q_GLOBAL_STATIC(ConfFileHash, usedHashFunc)
97Q_GLOBAL_STATIC(ConfFileCache, unusedCacheFunc)
98Q_GLOBAL_STATIC(PathHash, pathHashFunc)
99Q_GLOBAL_STATIC(CustomFormatVector, customFormatVectorFunc)
100
101Q_CONSTINIT static QBasicMutex settingsGlobalMutex;
102
103Q_CONSTINIT static QSettings::Format globalDefaultFormat = QSettings::NativeFormat;
104
105QConfFile::QConfFile(const QString &fileName, bool _userPerms)
106 : name(fileName), size(0), ref(1), userPerms(_userPerms)
107{
108 usedHashFunc()->insert(key: name, value: this);
109}
110
111QConfFile::~QConfFile()
112{
113 if (usedHashFunc())
114 usedHashFunc()->remove(key: name);
115}
116
117ParsedSettingsMap QConfFile::mergedKeyMap() const
118{
119 ParsedSettingsMap result = originalKeys;
120
121 for (auto i = removedKeys.begin(); i != removedKeys.end(); ++i)
122 result.remove(key: i.key());
123 for (auto i = addedKeys.begin(); i != addedKeys.end(); ++i)
124 result.insert(key: i.key(), value: i.value());
125 return result;
126}
127
128bool QConfFile::isWritable() const
129{
130 QFileInfo fileInfo(name);
131
132#if QT_CONFIG(temporaryfile)
133 if (fileInfo.exists()) {
134#endif
135 QFile file(name);
136 return file.open(flags: QFile::ReadWrite);
137#if QT_CONFIG(temporaryfile)
138 } else {
139 // Create the directories to the file.
140 QDir dir(fileInfo.absolutePath());
141 if (!dir.exists()) {
142 if (!dir.mkpath(dirPath: dir.absolutePath()))
143 return false;
144 }
145
146 // we use a temporary file to avoid race conditions
147 QTemporaryFile file(name);
148 return file.open();
149 }
150#endif
151}
152
153QConfFile *QConfFile::fromName(const QString &fileName, bool _userPerms)
154{
155 QString absPath = QFileInfo(fileName).absoluteFilePath();
156
157 ConfFileHash *usedHash = usedHashFunc();
158 ConfFileCache *unusedCache = unusedCacheFunc();
159
160 QConfFile *confFile = nullptr;
161 const auto locker = qt_scoped_lock(mutex&: settingsGlobalMutex);
162
163 if (!(confFile = usedHash->value(key: absPath))) {
164 if ((confFile = unusedCache->take(key: absPath)))
165 usedHash->insert(key: absPath, value: confFile);
166 }
167 if (confFile) {
168 confFile->ref.ref();
169 return confFile;
170 }
171 return new QConfFile(absPath, _userPerms);
172}
173
174void QConfFile::clearCache()
175{
176 const auto locker = qt_scoped_lock(mutex&: settingsGlobalMutex);
177 unusedCacheFunc()->clear();
178}
179
180// ************************************************************************
181// QSettingsPrivate
182
183QSettingsPrivate::QSettingsPrivate(QSettings::Format format)
184 : format(format), scope(QSettings::UserScope /* nothing better to put */), fallbacks(true),
185 pendingChanges(false), status(QSettings::NoError)
186{
187}
188
189QSettingsPrivate::QSettingsPrivate(QSettings::Format format, QSettings::Scope scope,
190 const QString &organization, const QString &application)
191 : format(format), scope(scope), organizationName(organization), applicationName(application),
192 fallbacks(true), pendingChanges(false), status(QSettings::NoError)
193{
194}
195
196QSettingsPrivate::~QSettingsPrivate()
197{
198}
199
200QString QSettingsPrivate::actualKey(QAnyStringView key) const
201{
202 auto n = normalizedKey(key);
203 Q_ASSERT_X(!n.isEmpty(), "QSettings", "empty key");
204 return groupPrefix + n;
205}
206
207namespace {
208 // ### this needs some public API (QStringConverter?)
209 QChar *write(QChar *out, QUtf8StringView v)
210 {
211 return QUtf8::convertToUnicode(buffer: out, in: QByteArrayView(v));
212 }
213 QChar *write(QChar *out, QLatin1StringView v)
214 {
215 return QLatin1::convertToUnicode(buffer: out, in: v);
216 }
217 QChar *write(QChar *out, QStringView v)
218 {
219 memcpy(dest: out, src: v.data(), n: v.size() * sizeof(QChar));
220 return out + v.size();
221 }
222}
223
224/*
225 Returns a string that never starts nor ends with a slash (or an
226 empty string). Examples:
227
228 "foo" becomes "foo"
229 "/foo//bar///" becomes "foo/bar"
230 "///" becomes ""
231*/
232QString QSettingsPrivate::normalizedKey(QAnyStringView key)
233{
234 QString result(key.size(), Qt::Uninitialized);
235 auto out = const_cast<QChar*>(result.constData()); // don't detach
236
237 const bool maybeEndsInSlash = key.visit(v: [&out](auto key) {
238 using View = decltype(key);
239
240 auto it = key.begin();
241 const auto end = key.end();
242
243 while (it != end) {
244 while (*it == u'/') {
245 ++it;
246 if (it == end)
247 return true;
248 }
249 auto mark = it;
250 while (*it != u'/') {
251 ++it;
252 if (it == end)
253 break;
254 }
255 out = write(out, View{mark, it});
256 if (it == end)
257 return false;
258 Q_ASSERT(*it == u'/');
259 *out++ = u'/';
260 ++it;
261 }
262 return true;
263 });
264
265 if (maybeEndsInSlash && out != result.constData())
266 --out; // remove the trailing slash
267 result.truncate(pos: out - result.constData());
268 return result;
269}
270
271// see also qsettings_win.cpp and qsettings_mac.cpp
272
273#if !defined(Q_OS_WIN) && !defined(Q_OS_DARWIN) && !defined(Q_OS_WASM)
274QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
275 const QString &organization, const QString &application)
276{
277 return new QConfFileSettingsPrivate(format, scope, organization, application);
278}
279#endif
280
281#if !defined(Q_OS_WIN)
282QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
283{
284 return new QConfFileSettingsPrivate(fileName, format);
285}
286#endif
287
288void QSettingsPrivate::processChild(QStringView key, ChildSpec spec, QStringList &result)
289{
290 if (spec != AllKeys) {
291 qsizetype slashPos = key.indexOf(c: u'/');
292 if (slashPos == -1) {
293 if (spec != ChildKeys)
294 return;
295 } else {
296 if (spec != ChildGroups)
297 return;
298 key.truncate(n: slashPos);
299 }
300 }
301 result.append(t: key.toString());
302}
303
304void QSettingsPrivate::beginGroupOrArray(const QSettingsGroup &group)
305{
306 groupStack.push(t: group);
307 const QString name = group.name();
308 if (!name.isEmpty())
309 groupPrefix += name + u'/';
310}
311
312/*
313 We only set an error if there isn't one set already. This way the user always gets the
314 first error that occurred. We always allow clearing errors.
315*/
316
317void QSettingsPrivate::setStatus(QSettings::Status status) const
318{
319 if (status == QSettings::NoError || this->status == QSettings::NoError)
320 this->status = status;
321}
322
323void QSettingsPrivate::update()
324{
325 flush();
326 pendingChanges = false;
327}
328
329void QSettingsPrivate::requestUpdate()
330{
331 if (!pendingChanges) {
332 pendingChanges = true;
333#ifndef QT_NO_QOBJECT
334 Q_Q(QSettings);
335 QCoreApplication::postEvent(receiver: q, event: new QEvent(QEvent::UpdateRequest));
336#else
337 update();
338#endif
339 }
340}
341
342QStringList QSettingsPrivate::variantListToStringList(const QVariantList &l)
343{
344 QStringList result;
345 result.reserve(asize: l.size());
346 for (auto v : l)
347 result.append(t: variantToString(v));
348 return result;
349}
350
351QVariant QSettingsPrivate::stringListToVariantList(const QStringList &l)
352{
353 QStringList outStringList = l;
354 for (qsizetype i = 0; i < outStringList.size(); ++i) {
355 const QString &str = outStringList.at(i);
356
357 if (str.startsWith(c: u'@')) {
358 if (str.size() < 2 || str.at(i: 1) != u'@') {
359 QVariantList variantList;
360 variantList.reserve(asize: l.size());
361 for (const auto &s : l)
362 variantList.append(t: stringToVariant(s));
363 return variantList;
364 }
365 outStringList[i].remove(i: 0, len: 1);
366 }
367 }
368 return outStringList;
369}
370
371QString QSettingsPrivate::variantToString(const QVariant &v)
372{
373 QString result;
374
375 switch (v.metaType().id()) {
376 case QMetaType::UnknownType:
377 result = "@Invalid()"_L1;
378 break;
379
380 case QMetaType::QByteArray: {
381 QByteArray a = v.toByteArray();
382 result = "@ByteArray("_L1 + QLatin1StringView(a) + u')';
383 break;
384 }
385
386#if QT_CONFIG(shortcut)
387 case QMetaType::QKeySequence:
388#endif
389 case QMetaType::QString:
390 case QMetaType::LongLong:
391 case QMetaType::ULongLong:
392 case QMetaType::Int:
393 case QMetaType::UInt:
394 case QMetaType::Bool:
395 case QMetaType::Float:
396 case QMetaType::Double: {
397 result = v.toString();
398 if (result.contains(c: QChar::Null))
399 result = "@String("_L1 + result + u')';
400 else if (result.startsWith(c: u'@'))
401 result.prepend(c: u'@');
402 break;
403 }
404#ifndef QT_NO_GEOM_VARIANT
405 case QMetaType::QRect: {
406 QRect r = qvariant_cast<QRect>(v);
407 result = QString::asprintf(format: "@Rect(%d %d %d %d)", r.x(), r.y(), r.width(), r.height());
408 break;
409 }
410 case QMetaType::QSize: {
411 QSize s = qvariant_cast<QSize>(v);
412 result = QString::asprintf(format: "@Size(%d %d)", s.width(), s.height());
413 break;
414 }
415 case QMetaType::QPoint: {
416 QPoint p = qvariant_cast<QPoint>(v);
417 result = QString::asprintf(format: "@Point(%d %d)", p.x(), p.y());
418 break;
419 }
420#endif // !QT_NO_GEOM_VARIANT
421
422 default: {
423#ifndef QT_NO_DATASTREAM
424 QDataStream::Version version;
425 const char *typeSpec;
426 if (v.userType() == QMetaType::QDateTime) {
427 version = QDataStream::Qt_5_6;
428 typeSpec = "@DateTime(";
429 } else {
430 version = QDataStream::Qt_4_0;
431 typeSpec = "@Variant(";
432 }
433 QByteArray a;
434 {
435 QDataStream s(&a, QIODevice::WriteOnly);
436 s.setVersion(version);
437 s << v;
438 }
439
440 result = QLatin1StringView(typeSpec)
441 + QLatin1StringView(a.constData(), a.size())
442 + u')';
443#else
444 Q_ASSERT(!"QSettings: Cannot save custom types without QDataStream support");
445#endif
446 break;
447 }
448 }
449
450 return result;
451}
452
453
454QVariant QSettingsPrivate::stringToVariant(const QString &s)
455{
456 if (s.startsWith(c: u'@')) {
457 if (s.endsWith(c: u')')) {
458 if (s.startsWith(s: "@ByteArray("_L1)) {
459 return QVariant(QStringView{s}.sliced(pos: 11).chopped(n: 1).toLatin1());
460 } else if (s.startsWith(s: "@String("_L1)) {
461 return QVariant(QStringView{s}.sliced(pos: 8).chopped(n: 1).toString());
462 } else if (s.startsWith(s: "@Variant("_L1)
463 || s.startsWith(s: "@DateTime("_L1)) {
464#ifndef QT_NO_DATASTREAM
465 QDataStream::Version version;
466 int offset;
467 if (s.at(i: 1) == u'D') {
468 version = QDataStream::Qt_5_6;
469 offset = 10;
470 } else {
471 version = QDataStream::Qt_4_0;
472 offset = 9;
473 }
474 QByteArray a = QStringView{s}.sliced(pos: offset).toLatin1();
475 QDataStream stream(&a, QIODevice::ReadOnly);
476 stream.setVersion(version);
477 QVariant result;
478 stream >> result;
479 return result;
480#else
481 Q_ASSERT(!"QSettings: Cannot load custom types without QDataStream support");
482#endif
483#ifndef QT_NO_GEOM_VARIANT
484 } else if (s.startsWith(s: "@Rect("_L1)) {
485 QStringList args = QSettingsPrivate::splitArgs(s, idx: 5);
486 if (args.size() == 4)
487 return QVariant(QRect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()));
488 } else if (s.startsWith(s: "@Size("_L1)) {
489 QStringList args = QSettingsPrivate::splitArgs(s, idx: 5);
490 if (args.size() == 2)
491 return QVariant(QSize(args[0].toInt(), args[1].toInt()));
492 } else if (s.startsWith(s: "@Point("_L1)) {
493 QStringList args = QSettingsPrivate::splitArgs(s, idx: 6);
494 if (args.size() == 2)
495 return QVariant(QPoint(args[0].toInt(), args[1].toInt()));
496#endif
497 } else if (s == "@Invalid()"_L1) {
498 return QVariant();
499 }
500
501 }
502 if (s.startsWith(s: "@@"_L1))
503 return QVariant(s.sliced(pos: 1));
504 }
505
506 return QVariant(s);
507}
508
509void QSettingsPrivate::iniEscapedKey(const QString &key, QByteArray &result)
510{
511 result.reserve(asize: result.size() + key.size() * 3 / 2);
512 for (qsizetype i = 0; i < key.size(); ++i) {
513 uint ch = key.at(i).unicode();
514
515 if (ch == '/') {
516 result += '\\';
517 } else if (isAsciiLetterOrNumber(c: ch) || ch == '_' || ch == '-' || ch == '.') {
518 result += (char)ch;
519 } else if (ch <= 0xFF) {
520 result += '%';
521 result += QtMiscUtils::toHexUpper(value: ch / 16);
522 result += QtMiscUtils::toHexUpper(value: ch % 16);
523 } else {
524 result += "%U";
525 QByteArray hexCode;
526 for (int j = 0; j < 4; ++j) {
527 hexCode.prepend(c: QtMiscUtils::toHexUpper(value: ch % 16));
528 ch >>= 4;
529 }
530 result += hexCode;
531 }
532 }
533}
534
535bool QSettingsPrivate::iniUnescapedKey(QByteArrayView key, QString &result)
536{
537 const QString decoded = QString::fromUtf8(utf8: key);
538 const qsizetype size = decoded.size();
539 result.reserve(asize: result.size() + size);
540 qsizetype i = 0;
541 bool lowercaseOnly = true;
542 while (i < size) {
543 char16_t ch = decoded.at(i).unicode();
544
545 if (ch == '\\') {
546 result += u'/';
547 ++i;
548 continue;
549 }
550
551 if (ch != '%' || i == size - 1) {
552 QChar qch(ch);
553 if (qch.isUpper())
554 lowercaseOnly = false;
555 result += qch;
556 ++i;
557 continue;
558 }
559
560 int numDigits = 2;
561 qsizetype firstDigitPos = i + 1;
562
563 ch = decoded.at(i: i + 1).unicode();
564 if (ch == 'U') {
565 ++firstDigitPos;
566 numDigits = 4;
567 }
568
569 if (firstDigitPos + numDigits > size) {
570 result += u'%';
571 ++i;
572 continue;
573 }
574
575 bool ok;
576 ch = QStringView(decoded).sliced(pos: firstDigitPos, n: numDigits).toUShort(ok: &ok, base: 16);
577 if (!ok) {
578 result += u'%';
579 ++i;
580 continue;
581 }
582
583 QChar qch(ch);
584 if (qch.isUpper())
585 lowercaseOnly = false;
586 result += qch;
587 i = firstDigitPos + numDigits;
588 }
589 return lowercaseOnly;
590}
591
592void QSettingsPrivate::iniEscapedString(const QString &str, QByteArray &result)
593{
594 bool needsQuotes = false;
595 bool escapeNextIfDigit = false;
596 const bool useCodec = !(str.startsWith(s: "@ByteArray("_L1)
597 || str.startsWith(s: "@Variant("_L1)
598 || str.startsWith(s: "@DateTime("_L1));
599 const qsizetype startPos = result.size();
600
601 QStringEncoder toUtf8(QStringEncoder::Utf8);
602
603 result.reserve(asize: startPos + str.size() * 3 / 2);
604 for (QChar qch : str) {
605 uint ch = qch.unicode();
606 if (ch == ';' || ch == ',' || ch == '=')
607 needsQuotes = true;
608
609 if (escapeNextIfDigit && isHexDigit(c: ch)) {
610 result += "\\x" + QByteArray::number(ch, base: 16);
611 continue;
612 }
613
614 escapeNextIfDigit = false;
615
616 switch (ch) {
617 case '\0':
618 result += "\\0";
619 escapeNextIfDigit = true;
620 break;
621 case '\a':
622 result += "\\a";
623 break;
624 case '\b':
625 result += "\\b";
626 break;
627 case '\f':
628 result += "\\f";
629 break;
630 case '\n':
631 result += "\\n";
632 break;
633 case '\r':
634 result += "\\r";
635 break;
636 case '\t':
637 result += "\\t";
638 break;
639 case '\v':
640 result += "\\v";
641 break;
642 case '"':
643 case '\\':
644 result += '\\';
645 result += (char)ch;
646 break;
647 default:
648 if (ch <= 0x1F || (ch >= 0x7F && !useCodec)) {
649 result += "\\x" + QByteArray::number(ch, base: 16);
650 escapeNextIfDigit = true;
651 } else if (useCodec) {
652 // slow
653 result += toUtf8(qch);
654 } else {
655 result += (char)ch;
656 }
657 }
658 }
659
660 if (needsQuotes
661 || (startPos < result.size() && (result.at(i: startPos) == ' '
662 || result.at(i: result.size() - 1) == ' '))) {
663 result.insert(i: startPos, c: '"');
664 result += '"';
665 }
666}
667
668inline static void iniChopTrailingSpaces(QString &str, qsizetype limit)
669{
670 qsizetype n = str.size() - 1;
671 QChar ch;
672 while (n >= limit && ((ch = str.at(i: n)) == u' ' || ch == u'\t'))
673 str.truncate(pos: n--);
674}
675
676void QSettingsPrivate::iniEscapedStringList(const QStringList &strs, QByteArray &result)
677{
678 if (strs.isEmpty()) {
679 /*
680 We need to distinguish between empty lists and one-item
681 lists that contain an empty string. Ideally, we'd have a
682 @EmptyList() symbol but that would break compatibility
683 with Qt 4.0. @Invalid() stands for QVariant(), and
684 QVariant().toStringList() returns an empty QStringList,
685 so we're in good shape.
686 */
687 result += "@Invalid()";
688 } else {
689 for (qsizetype i = 0; i < strs.size(); ++i) {
690 if (i != 0)
691 result += ", ";
692 iniEscapedString(str: strs.at(i), result);
693 }
694 }
695}
696
697bool QSettingsPrivate::iniUnescapedStringList(QByteArrayView str,
698 QString &stringResult, QStringList &stringListResult)
699{
700 static const char escapeCodes[][2] =
701 {
702 { 'a', '\a' },
703 { 'b', '\b' },
704 { 'f', '\f' },
705 { 'n', '\n' },
706 { 'r', '\r' },
707 { 't', '\t' },
708 { 'v', '\v' },
709 { '"', '"' },
710 { '?', '?' },
711 { '\'', '\'' },
712 { '\\', '\\' }
713 };
714
715 bool isStringList = false;
716 bool inQuotedString = false;
717 bool currentValueIsQuoted = false;
718 char16_t escapeVal = 0;
719 qsizetype i = 0;
720 char ch;
721 QStringDecoder fromUtf8(QStringDecoder::Utf8);
722
723StSkipSpaces:
724 while (i < str.size() && ((ch = str.at(n: i)) == ' ' || ch == '\t'))
725 ++i;
726 // fallthrough
727
728StNormal:
729 qsizetype chopLimit = stringResult.size();
730 while (i < str.size()) {
731 switch (str.at(n: i)) {
732 case '\\':
733 ++i;
734 if (i >= str.size())
735 goto end;
736
737 ch = str.at(n: i++);
738 for (const auto &escapeCode : escapeCodes) {
739 if (ch == escapeCode[0]) {
740 stringResult += QLatin1Char(escapeCode[1]);
741 goto StNormal;
742 }
743 }
744
745 if (ch == 'x') {
746 escapeVal = 0;
747
748 if (i >= str.size())
749 goto end;
750
751 ch = str.at(n: i);
752 if (isHexDigit(c: ch))
753 goto StHexEscape;
754 } else if (const int o = fromOct(c: ch); o != -1) {
755 escapeVal = o;
756 goto StOctEscape;
757 } else if (ch == '\n' || ch == '\r') {
758 if (i < str.size()) {
759 char ch2 = str.at(n: i);
760 // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files
761 if ((ch2 == '\n' || ch2 == '\r') && ch2 != ch)
762 ++i;
763 }
764 } else {
765 // the character is skipped
766 }
767 chopLimit = stringResult.size();
768 break;
769 case '"':
770 ++i;
771 currentValueIsQuoted = true;
772 inQuotedString = !inQuotedString;
773 if (!inQuotedString)
774 goto StSkipSpaces;
775 break;
776 case ',':
777 if (!inQuotedString) {
778 if (!currentValueIsQuoted)
779 iniChopTrailingSpaces(str&: stringResult, limit: chopLimit);
780 if (!isStringList) {
781 isStringList = true;
782 stringListResult.clear();
783 stringResult.squeeze();
784 }
785 stringListResult.append(t: stringResult);
786 stringResult.clear();
787 currentValueIsQuoted = false;
788 ++i;
789 goto StSkipSpaces;
790 }
791 Q_FALLTHROUGH();
792 default: {
793 qsizetype j = i + 1;
794 while (j < str.size()) {
795 ch = str.at(n: j);
796 if (ch == '\\' || ch == '"' || ch == ',')
797 break;
798 ++j;
799 }
800
801 stringResult += fromUtf8(str.first(n: j).sliced(pos: i));
802 i = j;
803 }
804 }
805 }
806 if (!currentValueIsQuoted)
807 iniChopTrailingSpaces(str&: stringResult, limit: chopLimit);
808 goto end;
809
810StHexEscape:
811 if (i >= str.size()) {
812 stringResult += escapeVal;
813 goto end;
814 }
815
816 ch = str.at(n: i);
817 if (const int h = fromHex(c: ch); h != -1) {
818 escapeVal <<= 4;
819 escapeVal += h;
820 ++i;
821 goto StHexEscape;
822 } else {
823 stringResult += escapeVal;
824 goto StNormal;
825 }
826
827StOctEscape:
828 if (i >= str.size()) {
829 stringResult += escapeVal;
830 goto end;
831 }
832
833 ch = str.at(n: i);
834 if (const int o = fromOct(c: ch); o != -1) {
835 escapeVal <<= 3;
836 escapeVal += o;
837 ++i;
838 goto StOctEscape;
839 } else {
840 stringResult += escapeVal;
841 goto StNormal;
842 }
843
844end:
845 if (isStringList)
846 stringListResult.append(t: stringResult);
847 return isStringList;
848}
849
850QStringList QSettingsPrivate::splitArgs(const QString &s, qsizetype idx)
851{
852 qsizetype l = s.size();
853 Q_ASSERT(l > 0);
854 Q_ASSERT(s.at(idx) == u'(');
855 Q_ASSERT(s.at(l - 1) == u')');
856
857 QStringList result;
858 QString item;
859
860 for (++idx; idx < l; ++idx) {
861 QChar c = s.at(i: idx);
862 if (c == u')') {
863 Q_ASSERT(idx == l - 1);
864 result.append(t: item);
865 } else if (c == u' ') {
866 result.append(t: item);
867 item.clear();
868 } else {
869 item.append(c);
870 }
871 }
872
873 return result;
874}
875
876// ************************************************************************
877// QConfFileSettingsPrivate
878
879void QConfFileSettingsPrivate::initFormat()
880{
881#if defined(Q_OS_WASM)
882 extension = (format == QSettings::NativeFormat || format == QSettings::WebIndexedDBFormat)
883 ? ".conf"_L1
884 : ".ini"_L1;
885#else
886 extension = (format == QSettings::NativeFormat) ? ".conf"_L1 : ".ini"_L1;
887#endif
888 readFunc = nullptr;
889 writeFunc = nullptr;
890#if defined(Q_OS_DARWIN)
891 caseSensitivity = (format == QSettings::NativeFormat) ? Qt::CaseSensitive : IniCaseSensitivity;
892#else
893 caseSensitivity = IniCaseSensitivity;
894#endif
895
896#if defined Q_OS_WASM
897 if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat) {
898#else
899 if (format > QSettings::IniFormat) {
900#endif
901 const auto locker = qt_scoped_lock(mutex&: settingsGlobalMutex);
902 const CustomFormatVector *customFormatVector = customFormatVectorFunc();
903
904 qsizetype i = qsizetype(format) - qsizetype(QSettings::CustomFormat1);
905 if (i >= 0 && i < customFormatVector->size()) {
906 QConfFileCustomFormat info = customFormatVector->at(i);
907 extension = info.extension;
908 readFunc = info.readFunc;
909 writeFunc = info.writeFunc;
910 caseSensitivity = info.caseSensitivity;
911 }
912 }
913}
914
915void QConfFileSettingsPrivate::initAccess()
916{
917 if (!confFiles.isEmpty()) {
918#if defined Q_OS_WASM
919 if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat) {
920#else
921 if (format > QSettings::IniFormat) {
922#endif
923 if (!readFunc)
924 setStatus(QSettings::AccessError);
925 }
926 }
927
928 sync(); // loads the files the first time
929}
930
931#if defined(Q_OS_WIN)
932static QString windowsConfigPath(const KNOWNFOLDERID &type)
933{
934 QString result;
935
936 PWSTR path = nullptr;
937 if (SHGetKnownFolderPath(type, KF_FLAG_DONT_VERIFY, NULL, &path) == S_OK) {
938 result = QString::fromWCharArray(path);
939 CoTaskMemFree(path);
940 }
941
942 if (result.isEmpty()) {
943 if (type == FOLDERID_ProgramData) {
944 result = "C:\\temp\\qt-common"_L1;
945 } else if (type == FOLDERID_RoamingAppData) {
946 result = "C:\\temp\\qt-user"_L1;
947 }
948 }
949
950 return result;
951}
952#endif // Q_OS_WIN
953
954static inline int pathHashKey(QSettings::Format format, QSettings::Scope scope)
955{
956 return int((uint(format) << 1) | uint(scope == QSettings::SystemScope));
957}
958
959#ifndef Q_OS_WIN
960static constexpr QChar sep = u'/';
961
962#if !defined(QSETTINGS_USE_QSTANDARDPATHS) || defined(Q_OS_ANDROID)
963static QString make_user_path_without_qstandard_paths()
964{
965 QByteArray env = qgetenv("XDG_CONFIG_HOME");
966 if (env.isEmpty()) {
967 return QDir::homePath() + "/.config/"_L1;
968 } else if (env.startsWith('/')) {
969 return QFile::decodeName(env) + sep;
970 }
971
972 return QDir::homePath() + sep + QFile::decodeName(env) + sep;
973}
974#endif // !QSETTINGS_USE_QSTANDARDPATHS || Q_OS_ANDROID
975
976static QString make_user_path()
977{
978#ifndef QSETTINGS_USE_QSTANDARDPATHS
979 // Non XDG platforms (OS X, iOS, Android...) have used this code path erroneously
980 // for some time now. Moving away from that would require migrating existing settings.
981 // The migration has already been done for Android.
982 return make_user_path_without_qstandard_paths();
983#else
984
985#ifdef Q_OS_ANDROID
986 // If an old settings path exists, use it instead of creating a new one
987 QString ret = make_user_path_without_qstandard_paths();
988 if (QFile(ret).exists())
989 return ret;
990#endif // Q_OS_ANDROID
991
992 // When using a proper XDG platform or Android platform, use QStandardPaths rather than the
993 // above hand-written code. It makes the use of test mode from unit tests possible.
994 // Ideally all platforms should use this, but see above for the migration issue.
995 return QStandardPaths::writableLocation(type: QStandardPaths::GenericConfigLocation) + sep;
996#endif // !QSETTINGS_USE_QSTANDARDPATHS
997}
998#endif // !Q_OS_WIN
999
1000static std::unique_lock<QBasicMutex> initDefaultPaths(std::unique_lock<QBasicMutex> locker)
1001{
1002 PathHash *pathHash = pathHashFunc();
1003
1004 locker.unlock();
1005
1006 /*
1007 QLibraryInfo::path() uses QSettings, so in order to
1008 avoid a dead-lock, we can't hold the global mutex while
1009 calling it.
1010 */
1011 QString systemPath = QLibraryInfo::path(p: QLibraryInfo::SettingsPath) + u'/';
1012
1013 locker.lock();
1014 if (pathHash->isEmpty()) {
1015 /*
1016 Lazy initialization of pathHash. We initialize the
1017 IniFormat paths and (on Unix) the NativeFormat paths.
1018 (The NativeFormat paths are not configurable for the
1019 Windows registry and the Mac CFPreferences.)
1020 */
1021#ifdef Q_OS_WIN
1022 const QString roamingAppDataFolder = windowsConfigPath(FOLDERID_RoamingAppData);
1023 const QString programDataFolder = windowsConfigPath(FOLDERID_ProgramData);
1024 pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope),
1025 Path(roamingAppDataFolder + QDir::separator(), false));
1026 pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope),
1027 Path(programDataFolder + QDir::separator(), false));
1028#else
1029 const QString userPath = make_user_path();
1030 pathHash->insert(key: pathHashKey(format: QSettings::IniFormat, scope: QSettings::UserScope), value: Path(userPath, false));
1031 pathHash->insert(key: pathHashKey(format: QSettings::IniFormat, scope: QSettings::SystemScope), value: Path(systemPath, false));
1032#ifndef Q_OS_DARWIN
1033 pathHash->insert(key: pathHashKey(format: QSettings::NativeFormat, scope: QSettings::UserScope), value: Path(userPath, false));
1034 pathHash->insert(key: pathHashKey(format: QSettings::NativeFormat, scope: QSettings::SystemScope), value: Path(systemPath, false));
1035#endif
1036#endif // Q_OS_WIN
1037 }
1038
1039 return locker;
1040}
1041
1042static Path getPath(QSettings::Format format, QSettings::Scope scope)
1043{
1044 Q_ASSERT(int(QSettings::NativeFormat) == 0);
1045 Q_ASSERT(int(QSettings::IniFormat) == 1);
1046
1047 auto locker = qt_unique_lock(mutex&: settingsGlobalMutex);
1048 PathHash *pathHash = pathHashFunc();
1049 if (pathHash->isEmpty())
1050 locker = initDefaultPaths(locker: std::move(locker));
1051
1052 Path result = pathHash->value(key: pathHashKey(format, scope));
1053 if (!result.path.isEmpty())
1054 return result;
1055
1056 // fall back on INI path
1057 return pathHash->value(key: pathHashKey(format: QSettings::IniFormat, scope));
1058}
1059
1060#if defined(QT_BUILD_INTERNAL) && defined(Q_XDG_PLATFORM) && !defined(QT_NO_STANDARDPATHS)
1061// Note: Suitable only for autotests.
1062void Q_AUTOTEST_EXPORT clearDefaultPaths()
1063{
1064 const auto locker = qt_scoped_lock(mutex&: settingsGlobalMutex);
1065 pathHashFunc()->clear();
1066}
1067#endif // QT_BUILD_INTERNAL && Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS
1068
1069QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format,
1070 QSettings::Scope scope,
1071 const QString &organization,
1072 const QString &application)
1073 : QSettingsPrivate(format, scope, organization, application),
1074 nextPosition(0x40000000) // big positive number
1075{
1076 initFormat();
1077
1078 QString org = organization;
1079 if (org.isEmpty()) {
1080 setStatus(QSettings::AccessError);
1081 org = "Unknown Organization"_L1;
1082 }
1083
1084 QString appFile = org + QDir::separator() + application + extension;
1085 QString orgFile = org + extension;
1086
1087 if (scope == QSettings::UserScope) {
1088 Path userPath = getPath(format, scope: QSettings::UserScope);
1089 if (!application.isEmpty())
1090 confFiles.append(t: QConfFile::fromName(fileName: userPath.path + appFile, userPerms: true));
1091 confFiles.append(t: QConfFile::fromName(fileName: userPath.path + orgFile, userPerms: true));
1092 }
1093
1094 Path systemPath = getPath(format, scope: QSettings::SystemScope);
1095#if defined(Q_XDG_PLATFORM) && !defined(QT_NO_STANDARDPATHS)
1096 // check if the systemPath wasn't overridden by QSettings::setPath()
1097 if (!systemPath.userDefined) {
1098 // Note: We can't use QStandardPaths::locateAll() as we need all the
1099 // possible files (not just the existing ones) and there is no way
1100 // to exclude user specific (XDG_CONFIG_HOME) directory from the search.
1101 QStringList dirs = QStandardPaths::standardLocations(type: QStandardPaths::GenericConfigLocation);
1102 // remove the QStandardLocation::writableLocation() (XDG_CONFIG_HOME)
1103 if (!dirs.isEmpty())
1104 dirs.takeFirst();
1105 QStringList paths;
1106 if (!application.isEmpty()) {
1107 paths.reserve(asize: dirs.size() * 2);
1108 for (const auto &dir : std::as_const(t&: dirs))
1109 paths.append(t: dir + u'/' + appFile);
1110 } else {
1111 paths.reserve(asize: dirs.size());
1112 }
1113 for (const auto &dir : std::as_const(t&: dirs))
1114 paths.append(t: dir + u'/' + orgFile);
1115
1116 // Note: No check for existence of files is done intentionally.
1117 for (const auto &path : std::as_const(t&: paths))
1118 confFiles.append(t: QConfFile::fromName(fileName: path, userPerms: false));
1119 } else
1120#endif // Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS
1121 {
1122 if (!application.isEmpty())
1123 confFiles.append(t: QConfFile::fromName(fileName: systemPath.path + appFile, userPerms: false));
1124 confFiles.append(t: QConfFile::fromName(fileName: systemPath.path + orgFile, userPerms: false));
1125 }
1126
1127 initAccess();
1128}
1129
1130QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName,
1131 QSettings::Format format)
1132 : QSettingsPrivate(format),
1133 nextPosition(0x40000000) // big positive number
1134{
1135 initFormat();
1136
1137 confFiles.append(t: QConfFile::fromName(fileName, userPerms: true));
1138
1139 initAccess();
1140}
1141
1142QConfFileSettingsPrivate::~QConfFileSettingsPrivate()
1143{
1144 const auto locker = qt_scoped_lock(mutex&: settingsGlobalMutex);
1145 ConfFileHash *usedHash = usedHashFunc();
1146 ConfFileCache *unusedCache = unusedCacheFunc();
1147
1148 for (auto conf_file : std::as_const(t&: confFiles)) {
1149 if (!conf_file->ref.deref()) {
1150 if (conf_file->size == 0) {
1151 delete conf_file;
1152 } else {
1153 if (usedHash)
1154 usedHash->remove(key: conf_file->name);
1155 if (unusedCache) {
1156 QT_TRY {
1157 // compute a better size?
1158 unusedCache->insert(key: conf_file->name, object: conf_file,
1159 cost: 10 + (conf_file->originalKeys.size() / 4));
1160 } QT_CATCH(...) {
1161 // out of memory. Do not cache the file.
1162 delete conf_file;
1163 }
1164 } else {
1165 // unusedCache is gone - delete the entry to prevent a memory leak
1166 delete conf_file;
1167 }
1168 }
1169 }
1170 }
1171}
1172
1173void QConfFileSettingsPrivate::remove(const QString &key)
1174{
1175 if (confFiles.isEmpty())
1176 return;
1177
1178 // Note: First config file is always the most specific.
1179 QConfFile *confFile = confFiles.at(i: 0);
1180
1181 QSettingsKey theKey(key, caseSensitivity);
1182 QSettingsKey prefix(key + u'/', caseSensitivity);
1183 const auto locker = qt_scoped_lock(mutex&: confFile->mutex);
1184
1185 ensureSectionParsed(confFile, key: theKey);
1186 ensureSectionParsed(confFile, key: prefix);
1187
1188 auto i = confFile->addedKeys.lowerBound(key: prefix);
1189 while (i != confFile->addedKeys.end() && i.key().startsWith(s: prefix))
1190 i = confFile->addedKeys.erase(it: i);
1191 confFile->addedKeys.remove(key: theKey);
1192
1193 auto j = const_cast<const ParsedSettingsMap *>(&confFile->originalKeys)->lowerBound(key: prefix);
1194 while (j != confFile->originalKeys.constEnd() && j.key().startsWith(s: prefix)) {
1195 confFile->removedKeys.insert(key: j.key(), value: QVariant());
1196 ++j;
1197 }
1198 if (confFile->originalKeys.contains(key: theKey))
1199 confFile->removedKeys.insert(key: theKey, value: QVariant());
1200}
1201
1202void QConfFileSettingsPrivate::set(const QString &key, const QVariant &value)
1203{
1204 if (confFiles.isEmpty())
1205 return;
1206
1207 // Note: First config file is always the most specific.
1208 QConfFile *confFile = confFiles.at(i: 0);
1209
1210 QSettingsKey theKey(key, caseSensitivity, nextPosition++);
1211 const auto locker = qt_scoped_lock(mutex&: confFile->mutex);
1212 confFile->removedKeys.remove(key: theKey);
1213 confFile->addedKeys.insert(key: theKey, value);
1214}
1215
1216std::optional<QVariant> QConfFileSettingsPrivate::get(const QString &key) const
1217{
1218 QSettingsKey theKey(key, caseSensitivity);
1219 ParsedSettingsMap::const_iterator j;
1220 bool found = false;
1221
1222 for (auto confFile : std::as_const(t: confFiles)) {
1223 const auto locker = qt_scoped_lock(mutex&: confFile->mutex);
1224
1225 if (!confFile->addedKeys.isEmpty()) {
1226 j = confFile->addedKeys.constFind(key: theKey);
1227 found = (j != confFile->addedKeys.constEnd());
1228 }
1229 if (!found) {
1230 ensureSectionParsed(confFile, key: theKey);
1231 j = confFile->originalKeys.constFind(key: theKey);
1232 found = (j != confFile->originalKeys.constEnd()
1233 && !confFile->removedKeys.contains(key: theKey));
1234 }
1235
1236 if (found)
1237 return *j;
1238 if (!fallbacks)
1239 break;
1240 }
1241 return std::nullopt;
1242}
1243
1244QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec spec) const
1245{
1246 QStringList result;
1247
1248 QSettingsKey thePrefix(prefix, caseSensitivity);
1249 qsizetype startPos = prefix.size();
1250
1251 for (auto confFile : std::as_const(t: confFiles)) {
1252 const auto locker = qt_scoped_lock(mutex&: confFile->mutex);
1253
1254 if (thePrefix.isEmpty())
1255 ensureAllSectionsParsed(confFile);
1256 else
1257 ensureSectionParsed(confFile, key: thePrefix);
1258
1259 const auto &originalKeys = confFile->originalKeys;
1260 auto i = originalKeys.lowerBound(key: thePrefix);
1261 while (i != originalKeys.end() && i.key().startsWith(s: thePrefix)) {
1262 if (!confFile->removedKeys.contains(key: i.key()))
1263 processChild(key: QStringView{i.key().originalCaseKey()}.sliced(pos: startPos), spec, result);
1264 ++i;
1265 }
1266
1267 const auto &addedKeys = confFile->addedKeys;
1268 auto j = addedKeys.lowerBound(key: thePrefix);
1269 while (j != addedKeys.end() && j.key().startsWith(s: thePrefix)) {
1270 processChild(key: QStringView{j.key().originalCaseKey()}.sliced(pos: startPos), spec, result);
1271 ++j;
1272 }
1273
1274 if (!fallbacks)
1275 break;
1276 }
1277 std::sort(first: result.begin(), last: result.end());
1278 result.erase(abegin: std::unique(first: result.begin(), last: result.end()),
1279 aend: result.end());
1280 return result;
1281}
1282
1283void QConfFileSettingsPrivate::clear()
1284{
1285 if (confFiles.isEmpty())
1286 return;
1287
1288 // Note: First config file is always the most specific.
1289 QConfFile *confFile = confFiles.at(i: 0);
1290
1291 const auto locker = qt_scoped_lock(mutex&: confFile->mutex);
1292 ensureAllSectionsParsed(confFile);
1293 confFile->addedKeys.clear();
1294 confFile->removedKeys = confFile->originalKeys;
1295}
1296
1297void QConfFileSettingsPrivate::sync()
1298{
1299 // people probably won't be checking the status a whole lot, so in case of
1300 // error we just try to go on and make the best of it
1301
1302 for (auto confFile : std::as_const(t&: confFiles)) {
1303 const auto locker = qt_scoped_lock(mutex&: confFile->mutex);
1304 syncConfFile(confFile);
1305 }
1306}
1307
1308void QConfFileSettingsPrivate::flush()
1309{
1310 sync();
1311}
1312
1313QString QConfFileSettingsPrivate::fileName() const
1314{
1315 if (confFiles.isEmpty())
1316 return QString();
1317
1318 // Note: First config file is always the most specific.
1319 return confFiles.at(i: 0)->name;
1320}
1321
1322bool QConfFileSettingsPrivate::isWritable() const
1323{
1324#if defined(Q_OS_WASM)
1325 if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat && !writeFunc)
1326#else
1327 if (format > QSettings::IniFormat && !writeFunc)
1328#endif
1329 return false;
1330
1331 if (confFiles.isEmpty())
1332 return false;
1333
1334 return confFiles.at(i: 0)->isWritable();
1335}
1336
1337void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
1338{
1339 bool readOnly = confFile->addedKeys.isEmpty() && confFile->removedKeys.isEmpty();
1340
1341 QFileInfo fileInfo(confFile->name);
1342 /*
1343 We can often optimize the read-only case, if the file on disk
1344 hasn't changed.
1345 */
1346 if (readOnly && confFile->size > 0) {
1347 if (confFile->size == fileInfo.size() && confFile->timeStamp == fileInfo.lastModified(tz: QTimeZone::UTC))
1348 return;
1349 }
1350
1351 if (!readOnly && !confFile->isWritable()) {
1352 setStatus(QSettings::AccessError);
1353 return;
1354 }
1355
1356#ifndef QT_BOOTSTRAPPED
1357 QString lockFileName = confFile->name + ".lock"_L1;
1358
1359# if defined(Q_OS_ANDROID) && defined(QSETTINGS_USE_QSTANDARDPATHS)
1360 // On android and if it is a content URL put the lock file in a
1361 // writable location to prevent permissions issues and invalid paths.
1362 if (confFile->name.startsWith("content:"_L1))
1363 lockFileName = make_user_path() + QFileInfo(lockFileName).fileName();
1364# endif
1365 /*
1366 Use a lockfile in order to protect us against other QSettings instances
1367 trying to write the same settings at the same time.
1368
1369 We only need to lock if we are actually writing as only concurrent writes are a problem.
1370 Concurrent read and write are not a problem because the writing operation is atomic.
1371 */
1372 QLockFile lockFile(lockFileName);
1373 if (!readOnly && !lockFile.lock() && atomicSyncOnly) {
1374 setStatus(QSettings::AccessError);
1375 return;
1376 }
1377#endif
1378
1379 /*
1380 We hold the lock. Let's reread the file if it has changed
1381 since last time we read it.
1382 */
1383 fileInfo.refresh();
1384 bool mustReadFile = true;
1385 bool createFile = !fileInfo.exists();
1386
1387 if (!readOnly)
1388 mustReadFile = (confFile->size != fileInfo.size()
1389 || (confFile->size != 0 && confFile->timeStamp != fileInfo.lastModified(tz: QTimeZone::UTC)));
1390
1391 if (mustReadFile) {
1392 confFile->unparsedIniSections.clear();
1393 confFile->originalKeys.clear();
1394
1395 QFile file(confFile->name);
1396 if (!createFile && !file.open(flags: QFile::ReadOnly)) {
1397 setStatus(QSettings::AccessError);
1398 return;
1399 }
1400
1401 /*
1402 Files that we can't read (because of permissions or
1403 because they don't exist) are treated as empty files.
1404 */
1405 if (file.isReadable() && file.size() != 0) {
1406 bool ok = false;
1407
1408#ifdef Q_OS_WASM
1409 if (format == QSettings::WebIndexedDBFormat) {
1410 QByteArray data = file.readAll();
1411 ok = readIniFile(data, &confFile->unparsedIniSections);
1412 } else
1413#endif
1414#ifdef Q_OS_DARWIN
1415 if (format == QSettings::NativeFormat) {
1416 QByteArray data = file.readAll();
1417 ok = readPlistFile(data, &confFile->originalKeys);
1418 } else
1419#endif
1420 if (format <= QSettings::IniFormat) {
1421 QByteArray data = file.readAll();
1422 ok = readIniFile(data, unparsedIniSections: &confFile->unparsedIniSections);
1423 } else if (readFunc) {
1424 QSettings::SettingsMap tempNewKeys;
1425 ok = readFunc(file, tempNewKeys);
1426
1427 if (ok) {
1428 auto i = tempNewKeys.constBegin();
1429 while (i != tempNewKeys.constEnd()) {
1430 confFile->originalKeys.insert(key: QSettingsKey(i.key(), caseSensitivity),
1431 value: i.value());
1432 ++i;
1433 }
1434 }
1435 }
1436
1437 if (!ok)
1438 setStatus(QSettings::FormatError);
1439 }
1440
1441 confFile->size = fileInfo.size();
1442 confFile->timeStamp = fileInfo.lastModified(tz: QTimeZone::UTC);
1443 }
1444
1445 /*
1446 We also need to save the file. We still hold the file lock,
1447 so everything is under control.
1448 */
1449 if (!readOnly) {
1450 bool ok = false;
1451 ensureAllSectionsParsed(confFile);
1452 ParsedSettingsMap mergedKeys = confFile->mergedKeyMap();
1453
1454#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
1455 QSaveFile sf(confFile->name);
1456 sf.setDirectWriteFallback(!atomicSyncOnly);
1457# ifdef Q_OS_ANDROID
1458 // QSaveFile requires direct write when using content scheme URL in Android
1459 if (confFile->name.startsWith("content:"_L1))
1460 sf.setDirectWriteFallback(true);
1461# endif
1462#else
1463 QFile sf(confFile->name);
1464#endif
1465 if (!sf.open(flags: QIODevice::WriteOnly)) {
1466 setStatus(QSettings::AccessError);
1467 return;
1468 }
1469
1470#ifdef Q_OS_WASM
1471 if (format == QSettings::WebIndexedDBFormat) {
1472 ok = writeIniFile(sf, mergedKeys);
1473 } else
1474#endif
1475#ifdef Q_OS_DARWIN
1476 if (format == QSettings::NativeFormat) {
1477 ok = writePlistFile(sf, mergedKeys);
1478 } else
1479#endif
1480 if (format <= QSettings::IniFormat) {
1481 ok = writeIniFile(device&: sf, map: mergedKeys);
1482 } else if (writeFunc) {
1483 QSettings::SettingsMap tempOriginalKeys;
1484
1485 auto i = mergedKeys.constBegin();
1486 while (i != mergedKeys.constEnd()) {
1487 tempOriginalKeys.insert(key: i.key(), value: i.value());
1488 ++i;
1489 }
1490 ok = writeFunc(sf, tempOriginalKeys);
1491 }
1492
1493#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
1494 if (ok)
1495 ok = sf.commit();
1496#endif
1497
1498 if (ok) {
1499 confFile->unparsedIniSections.clear();
1500 confFile->originalKeys = mergedKeys;
1501 confFile->addedKeys.clear();
1502 confFile->removedKeys.clear();
1503
1504 fileInfo.refresh();
1505 confFile->size = fileInfo.size();
1506 confFile->timeStamp = fileInfo.lastModified(tz: QTimeZone::UTC);
1507
1508 // If we have created the file, apply the file perms
1509 if (createFile) {
1510 QFile::Permissions perms = fileInfo.permissions() | QFile::ReadOwner | QFile::WriteOwner;
1511 if (!confFile->userPerms)
1512 perms |= QFile::ReadGroup | QFile::ReadOther;
1513 QFile(confFile->name).setPermissions(perms);
1514 }
1515 } else {
1516 setStatus(QSettings::AccessError);
1517 }
1518 }
1519}
1520
1521namespace SettingsImpl {
1522
1523enum { Space = 0x1, Special = 0x2 };
1524
1525static const char charTraits[256] =
1526{
1527 // Space: '\t', '\n', '\r', ' '
1528 // Special: '\n', '\r', '"', ';', '=', '\\'
1529
1530 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special, 0, 0,
1531 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1532 Space, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1533 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, Special, 0, 0,
1534 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1535 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0,
1536 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1537 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1538
1539 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1540 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1541 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1542 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1543 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1544 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1545 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1546 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1547};
1548
1549} // namespace SettingsImpl
1550
1551using SettingsImpl::charTraits;
1552
1553bool QConfFileSettingsPrivate::readIniLine(QByteArrayView data, qsizetype &dataPos,
1554 qsizetype &lineStart, qsizetype &lineLen,
1555 qsizetype &equalsPos)
1556{
1557 using namespace SettingsImpl;
1558 qsizetype dataLen = data.size();
1559 bool inQuotes = false;
1560
1561 equalsPos = -1;
1562
1563 lineStart = dataPos;
1564 while (lineStart < dataLen && (charTraits[uint(uchar(data.at(n: lineStart)))] & Space))
1565 ++lineStart;
1566
1567 qsizetype i = lineStart;
1568 while (i < dataLen) {
1569 char ch = data.at(n: i);
1570 while (!(charTraits[uchar(ch)] & Special)) {
1571 if (++i == dataLen)
1572 goto break_out_of_outer_loop;
1573 ch = data.at(n: i);
1574 }
1575
1576 ++i;
1577 if (ch == '=') {
1578 if (!inQuotes && equalsPos == -1)
1579 equalsPos = i - 1;
1580 } else if (ch == '\n' || ch == '\r') {
1581 if (i == lineStart + 1) {
1582 ++lineStart;
1583 } else if (!inQuotes) {
1584 --i;
1585 goto break_out_of_outer_loop;
1586 }
1587 } else if (ch == '\\') {
1588 if (i < dataLen) {
1589 char ch = data.at(n: i++);
1590 if (i < dataLen) {
1591 char ch2 = data.at(n: i);
1592 // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files
1593 if ((ch == '\n' && ch2 == '\r') || (ch == '\r' && ch2 == '\n'))
1594 ++i;
1595 }
1596 }
1597 } else if (ch == '"') {
1598 inQuotes = !inQuotes;
1599 } else {
1600 Q_ASSERT(ch == ';');
1601
1602 if (i == lineStart + 1) {
1603 while (i < dataLen && (((ch = data.at(n: i)) != '\n') && ch != '\r'))
1604 ++i;
1605 while (i < dataLen && charTraits[uchar(data.at(n: i))] & Space)
1606 ++i;
1607 lineStart = i;
1608 } else if (!inQuotes) {
1609 --i;
1610 goto break_out_of_outer_loop;
1611 }
1612 }
1613 }
1614
1615break_out_of_outer_loop:
1616 dataPos = i;
1617 lineLen = i - lineStart;
1618 return lineLen > 0;
1619}
1620
1621/*
1622 Returns \c false on parse error. However, as many keys are read as
1623 possible, so if the user doesn't check the status he will get the
1624 most out of the file anyway.
1625*/
1626bool QConfFileSettingsPrivate::readIniFile(QByteArrayView data,
1627 UnparsedSettingsMap *unparsedIniSections)
1628{
1629#define FLUSH_CURRENT_SECTION() \
1630 { \
1631 QByteArray &sectionData = (*unparsedIniSections)[QSettingsKey(currentSection, \
1632 IniCaseSensitivity, \
1633 sectionPosition)]; \
1634 if (!sectionData.isEmpty()) \
1635 sectionData.append('\n'); \
1636 sectionData += data.first(lineStart).sliced(currentSectionStart); \
1637 sectionPosition = ++position; \
1638 }
1639
1640 QString currentSection;
1641 qsizetype currentSectionStart = 0;
1642 qsizetype dataPos = 0;
1643 qsizetype lineStart;
1644 qsizetype lineLen;
1645 qsizetype equalsPos;
1646 qsizetype position = 0;
1647 qsizetype sectionPosition = 0;
1648 bool ok = true;
1649
1650 // Skip possible UTF-8 BOM:
1651 if (data.startsWith(other: "\xef\xbb\xbf"))
1652 data = data.sliced(pos: 3);
1653
1654 while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) {
1655 QByteArrayView line = data.sliced(pos: lineStart, n: lineLen);
1656 if (line.startsWith(c: '[')) {
1657 FLUSH_CURRENT_SECTION();
1658
1659 // This starts a new section.
1660 qsizetype idx = line.indexOf(ch: ']');
1661 Q_ASSERT(idx == -1 || idx > 0); // line[0] is '[', not ']'.
1662 Q_ASSERT(idx < lineLen); // (including -1 < lineLen, if no ']' present.)
1663 if (idx < 0) {
1664 ok = false;
1665 idx = lineLen; // so line.first(idx) is just line
1666 }
1667 QByteArrayView iniSection = line.first(n: idx).sliced(pos: 1).trimmed();
1668
1669 if (iniSection.compare(a: "general", cs: Qt::CaseInsensitive) == 0) {
1670 currentSection.clear();
1671 } else {
1672 if (iniSection.compare(a: "%general", cs: Qt::CaseInsensitive) == 0) {
1673 currentSection = QLatin1StringView(iniSection.constData() + 1, iniSection.size() - 1);
1674 } else {
1675 currentSection.clear();
1676 iniUnescapedKey(key: iniSection, result&: currentSection);
1677 }
1678 currentSection += u'/';
1679 }
1680 currentSectionStart = dataPos;
1681 }
1682 ++position;
1683 }
1684
1685 Q_ASSERT(lineStart == data.size());
1686 FLUSH_CURRENT_SECTION();
1687
1688 return ok;
1689
1690#undef FLUSH_CURRENT_SECTION
1691}
1692
1693bool QConfFileSettingsPrivate::readIniSection(const QSettingsKey &section, QByteArrayView data,
1694 ParsedSettingsMap *settingsMap)
1695{
1696 QStringList strListValue;
1697 bool sectionIsLowercase = (section == section.originalCaseKey());
1698 qsizetype equalsPos;
1699
1700 bool ok = true;
1701 qsizetype dataPos = 0;
1702 qsizetype lineStart;
1703 qsizetype lineLen;
1704 qsizetype position = section.originalKeyPosition();
1705
1706 while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) {
1707 QByteArrayView line = data.sliced(pos: lineStart, n: lineLen);
1708 Q_ASSERT(!line.startsWith('['));
1709
1710 if (equalsPos == -1) {
1711 if (!line.startsWith(c: ';'))
1712 ok = false;
1713 continue;
1714 }
1715 // Shift equalPos indexing to be within line, rather than data:
1716 equalsPos -= lineStart;
1717 // Assured by readIniLine:
1718 Q_ASSERT(equalsPos >= 0 && equalsPos < lineLen);
1719
1720 QByteArrayView key = line.first(n: equalsPos).trimmed();
1721 QByteArrayView value = line.sliced(pos: equalsPos + 1);
1722
1723 QString strKey = section.originalCaseKey();
1724 const Qt::CaseSensitivity casing = iniUnescapedKey(key, result&: strKey) && sectionIsLowercase
1725 ? Qt::CaseSensitive
1726 : IniCaseSensitivity;
1727
1728 QString strValue;
1729 strValue.reserve(asize: value.size());
1730 QVariant variant = iniUnescapedStringList(str: value, stringResult&: strValue, stringListResult&: strListValue)
1731 ? stringListToVariantList(l: strListValue)
1732 : stringToVariant(s: strValue);
1733
1734 /*
1735 We try to avoid the expensive toLower() call in
1736 QSettingsKey by passing Qt::CaseSensitive when the
1737 key is already in lowercase.
1738 */
1739 settingsMap->insert(key: QSettingsKey(strKey, casing, position), value: std::move(variant));
1740 ++position;
1741 }
1742
1743 return ok;
1744}
1745
1746class QSettingsIniKey : public QString
1747{
1748public:
1749 inline QSettingsIniKey() : position(-1) {}
1750 inline QSettingsIniKey(const QString &str, qsizetype pos = -1) : QString(str), position(pos) {}
1751
1752 qsizetype position;
1753};
1754Q_DECLARE_TYPEINFO(QSettingsIniKey, Q_RELOCATABLE_TYPE);
1755
1756static bool operator<(const QSettingsIniKey &k1, const QSettingsIniKey &k2)
1757{
1758 if (k1.position != k2.position)
1759 return k1.position < k2.position;
1760 return static_cast<const QString &>(k1) < static_cast<const QString &>(k2);
1761}
1762
1763typedef QMap<QSettingsIniKey, QVariant> IniKeyMap;
1764
1765struct QSettingsIniSection
1766{
1767 qsizetype position;
1768 IniKeyMap keyMap;
1769
1770 inline QSettingsIniSection() : position(-1) {}
1771};
1772
1773Q_DECLARE_TYPEINFO(QSettingsIniSection, Q_RELOCATABLE_TYPE);
1774
1775typedef QMap<QString, QSettingsIniSection> IniMap;
1776
1777/*
1778 This would be more straightforward if we didn't try to remember the original
1779 key order in the .ini file, but we do.
1780*/
1781bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSettingsMap &map)
1782{
1783 IniMap iniMap;
1784
1785#ifdef Q_OS_WIN
1786 const char * const eol = "\r\n";
1787#else
1788 const char eol = '\n';
1789#endif
1790
1791 for (auto j = map.constBegin(); j != map.constEnd(); ++j) {
1792 QString section;
1793 QSettingsIniKey key(j.key().originalCaseKey(), j.key().originalKeyPosition());
1794 qsizetype slashPos;
1795
1796 if ((slashPos = key.indexOf(c: u'/')) != -1) {
1797 section = key.left(n: slashPos);
1798 key.remove(i: 0, len: slashPos + 1);
1799 }
1800
1801 QSettingsIniSection &iniSection = iniMap[section];
1802
1803 // -1 means infinity
1804 if (size_t(key.position) < size_t(iniSection.position))
1805 iniSection.position = key.position;
1806 iniSection.keyMap[key] = j.value();
1807 }
1808
1809 const qsizetype sectionCount = iniMap.size();
1810 QList<QSettingsIniKey> sections;
1811 sections.reserve(asize: sectionCount);
1812 for (auto i = iniMap.constBegin(); i != iniMap.constEnd(); ++i)
1813 sections.append(t: QSettingsIniKey(i.key(), i.value().position));
1814 std::sort(first: sections.begin(), last: sections.end());
1815
1816 bool writeError = false;
1817 for (qsizetype j = 0; !writeError && j < sectionCount; ++j) {
1818 auto i = iniMap.constFind(key: sections.at(i: j));
1819 Q_ASSERT(i != iniMap.constEnd());
1820
1821 QByteArray realSection;
1822
1823 iniEscapedKey(key: i.key(), result&: realSection);
1824
1825 if (realSection.isEmpty()) {
1826 realSection = "[General]";
1827 } else if (realSection.compare(a: "general", cs: Qt::CaseInsensitive) == 0) {
1828 realSection = "[%General]";
1829 } else {
1830 realSection.prepend(c: '[');
1831 realSection.append(c: ']');
1832 }
1833
1834 if (j != 0)
1835 realSection.prepend(c: eol);
1836 realSection += eol;
1837
1838 device.write(data: realSection);
1839
1840 const IniKeyMap &ents = i.value().keyMap;
1841 for (auto j = ents.constBegin(); j != ents.constEnd(); ++j) {
1842 QByteArray block;
1843 iniEscapedKey(key: j.key(), result&: block);
1844 block += '=';
1845
1846 const QVariant &value = j.value();
1847
1848 /*
1849 The size() != 1 trick is necessary because
1850 QVariant(QString("foo")).toList() returns an empty
1851 list, not a list containing "foo".
1852 */
1853 if (value.metaType().id() == QMetaType::QStringList
1854 || (value.metaType().id() == QMetaType::QVariantList && value.toList().size() != 1)) {
1855 iniEscapedStringList(strs: variantListToStringList(l: value.toList()), result&: block);
1856 } else {
1857 iniEscapedString(str: variantToString(v: value), result&: block);
1858 }
1859 block += eol;
1860 if (device.write(data: block) == -1) {
1861 writeError = true;
1862 break;
1863 }
1864 }
1865 }
1866 return !writeError;
1867}
1868
1869void QConfFileSettingsPrivate::ensureAllSectionsParsed(QConfFile *confFile) const
1870{
1871 auto i = confFile->unparsedIniSections.constBegin();
1872 const auto end = confFile->unparsedIniSections.constEnd();
1873
1874 for (; i != end; ++i) {
1875 if (!QConfFileSettingsPrivate::readIniSection(section: i.key(), data: i.value(), settingsMap: &confFile->originalKeys))
1876 setStatus(QSettings::FormatError);
1877 }
1878 confFile->unparsedIniSections.clear();
1879}
1880
1881void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
1882 const QSettingsKey &key) const
1883{
1884 if (confFile->unparsedIniSections.isEmpty())
1885 return;
1886
1887 UnparsedSettingsMap::iterator i;
1888
1889 qsizetype indexOfSlash = key.indexOf(c: u'/');
1890 if (indexOfSlash != -1) {
1891 i = confFile->unparsedIniSections.upperBound(key);
1892 if (i == confFile->unparsedIniSections.begin())
1893 return;
1894 --i;
1895 if (i.key().isEmpty() || !key.startsWith(s: i.key()))
1896 return;
1897 } else {
1898 i = confFile->unparsedIniSections.begin();
1899 if (i == confFile->unparsedIniSections.end() || !i.key().isEmpty())
1900 return;
1901 }
1902
1903 if (!QConfFileSettingsPrivate::readIniSection(section: i.key(), data: i.value(), settingsMap: &confFile->originalKeys))
1904 setStatus(QSettings::FormatError);
1905 confFile->unparsedIniSections.erase(it: i);
1906}
1907
1908/*!
1909 \class QSettings
1910 \inmodule QtCore
1911 \brief The QSettings class provides persistent platform-independent application settings.
1912
1913 \ingroup io
1914
1915 \reentrant
1916
1917 Users normally expect an application to remember its settings
1918 (window sizes and positions, options, etc.) across sessions. This
1919 information is often stored in the system registry on Windows,
1920 and in property list files on \macos and iOS. On Unix systems, in the
1921 absence of a standard, many applications (including the KDE
1922 applications) use INI text files.
1923
1924 QSettings is an abstraction around these technologies, enabling
1925 you to save and restore application settings in a portable
1926 manner. It also supports \l{registerFormat()}{custom storage
1927 formats}.
1928
1929 QSettings's API is based on QVariant, allowing you to save
1930 most value-based types, such as QString, QRect, and QImage,
1931 with the minimum of effort.
1932
1933 If all you need is a non-persistent memory-based structure,
1934 consider using QMap<QString, QVariant> instead.
1935
1936 \section1 Basic Usage
1937
1938 When creating a QSettings object, you must pass the name of your
1939 company or organization as well as the name of your application.
1940 For example, if your product is called Star Runner and your
1941 company is called MySoft, you would construct the QSettings
1942 object as follows:
1943
1944 \snippet settings/settings.cpp 0
1945
1946 QSettings objects can be created either on the stack or on
1947 the heap (i.e. using \c new). Constructing and destroying a
1948 QSettings object is very fast.
1949
1950 If you use QSettings from many places in your application, you
1951 might want to specify the organization name and the application
1952 name using QCoreApplication::setOrganizationName() and
1953 QCoreApplication::setApplicationName(), and then use the default
1954 QSettings constructor:
1955
1956 \snippet settings/settings.cpp 1
1957 \snippet settings/settings.cpp 2
1958 \snippet settings/settings.cpp 3
1959 \dots
1960 \snippet settings/settings.cpp 4
1961
1962 (Here, we also specify the organization's Internet domain. When
1963 the Internet domain is set, it is used on \macos and iOS instead of the
1964 organization name, since \macos and iOS applications conventionally use
1965 Internet domains to identify themselves. If no domain is set, a
1966 fake domain is derived from the organization name. See the
1967 \l{Platform-Specific Notes} below for details.)
1968
1969 QSettings stores settings. Each setting consists of a QString
1970 that specifies the setting's name (the \e key) and a QVariant
1971 that stores the data associated with the key. To write a setting,
1972 use setValue(). For example:
1973
1974 \snippet settings/settings.cpp 5
1975
1976 If there already exists a setting with the same key, the existing
1977 value is overwritten by the new value. For efficiency, the
1978 changes may not be saved to permanent storage immediately. (You
1979 can always call sync() to commit your changes.)
1980
1981 You can get a setting's value back using value():
1982
1983 \snippet settings/settings.cpp 6
1984
1985 If there is no setting with the specified name, QSettings
1986 returns a null QVariant (which can be converted to the integer 0).
1987 You can specify another default value by passing a second
1988 argument to value():
1989
1990 \snippet settings/settings.cpp 7
1991
1992 To test whether a given key exists, call contains(). To remove
1993 the setting associated with a key, call remove(). To obtain the
1994 list of all keys, call allKeys(). To remove all keys, call
1995 clear().
1996
1997 \section1 QVariant and GUI Types
1998
1999 Because QVariant is part of the Qt Core module, it cannot provide
2000 conversion functions to data types such as QColor, QImage, and
2001 QPixmap, which are part of Qt GUI. In other words, there is no
2002 \c toColor(), \c toImage(), or \c toPixmap() functions in QVariant.
2003
2004 Instead, you can use the QVariant::value() template function.
2005 For example:
2006
2007 \snippet code/src_corelib_io_qsettings.cpp 0
2008
2009 The inverse conversion (e.g., from QColor to QVariant) is
2010 automatic for all data types supported by QVariant, including
2011 GUI-related types:
2012
2013 \snippet code/src_corelib_io_qsettings.cpp 1
2014
2015 Custom types registered using qRegisterMetaType() that have
2016 operators for streaming to and from a QDataStream can be stored
2017 using QSettings.
2018
2019 \section1 Section and Key Syntax
2020
2021 Setting keys can contain any Unicode characters. The Windows
2022 registry and INI files use case-insensitive keys, whereas the
2023 CFPreferences API on \macos and iOS uses case-sensitive keys. To
2024 avoid portability problems, follow these simple rules:
2025
2026 \list 1
2027 \li Always refer to the same key using the same case. For example,
2028 if you refer to a key as "text fonts" in one place in your
2029 code, don't refer to it as "Text Fonts" somewhere else.
2030
2031 \li Avoid key names that are identical except for the case. For
2032 example, if you have a key called "MainWindow", don't try to
2033 save another key as "mainwindow".
2034
2035 \li Do not use slashes ('/' and '\\') in section or key names; the
2036 backslash character is used to separate sub keys (see below). On
2037 windows '\\' are converted by QSettings to '/', which makes
2038 them identical.
2039 \endlist
2040
2041 You can form hierarchical keys using the '/' character as a
2042 separator, similar to Unix file paths. For example:
2043
2044 \snippet settings/settings.cpp 8
2045 \snippet settings/settings.cpp 9
2046 \snippet settings/settings.cpp 10
2047
2048 If you want to save or restore many settings with the same
2049 prefix, you can specify the prefix using beginGroup() and call
2050 endGroup() at the end. Here's the same example again, but this
2051 time using the group mechanism:
2052
2053 \snippet settings/settings.cpp 11
2054 \codeline
2055 \snippet settings/settings.cpp 12
2056
2057 If a group is set using beginGroup(), the behavior of most
2058 functions changes consequently. Groups can be set recursively.
2059
2060 In addition to groups, QSettings also supports an "array"
2061 concept. See beginReadArray() and beginWriteArray() for details.
2062
2063 \section1 Fallback Mechanism
2064
2065 Let's assume that you have created a QSettings object with the
2066 organization name MySoft and the application name Star Runner.
2067 When you look up a value, up to four locations are searched in
2068 that order:
2069
2070 \list 1
2071 \li a user-specific location for the Star Runner application
2072 \li a user-specific location for all applications by MySoft
2073 \li a system-wide location for the Star Runner application
2074 \li a system-wide location for all applications by MySoft
2075 \endlist
2076
2077 (See \l{Platform-Specific Notes} below for information on what
2078 these locations are on the different platforms supported by Qt.)
2079
2080 If a key cannot be found in the first location, the search goes
2081 on in the second location, and so on. This enables you to store
2082 system-wide or organization-wide settings and to override them on
2083 a per-user or per-application basis. To turn off this mechanism,
2084 call setFallbacksEnabled(false).
2085
2086 Although keys from all four locations are available for reading,
2087 only the first file (the user-specific location for the
2088 application at hand) is accessible for writing. To write to any
2089 of the other files, omit the application name and/or specify
2090 QSettings::SystemScope (as opposed to QSettings::UserScope, the
2091 default).
2092
2093 Let's see with an example:
2094
2095 \snippet settings/settings.cpp 13
2096 \snippet settings/settings.cpp 14
2097
2098 The table below summarizes which QSettings objects access
2099 which location. "\b{X}" means that the location is the main
2100 location associated to the QSettings object and is used both
2101 for reading and for writing; "o" means that the location is used
2102 as a fallback when reading.
2103
2104 \table
2105 \header \li Locations \li \c{obj1} \li \c{obj2} \li \c{obj3} \li \c{obj4}
2106 \row \li 1. User, Application \li \b{X} \li \li \li
2107 \row \li 2. User, Organization \li o \li \b{X} \li \li
2108 \row \li 3. System, Application \li o \li \li \b{X} \li
2109 \row \li 4. System, Organization \li o \li o \li o \li \b{X}
2110 \endtable
2111
2112 The beauty of this mechanism is that it works on all platforms
2113 supported by Qt and that it still gives you a lot of flexibility,
2114 without requiring you to specify any file names or registry
2115 paths.
2116
2117 If you want to use INI files on all platforms instead of the
2118 native API, you can pass QSettings::IniFormat as the first
2119 argument to the QSettings constructor, followed by the scope, the
2120 organization name, and the application name:
2121
2122 \snippet settings/settings.cpp 15
2123
2124 Note that INI files lose the distinction between numeric data and the
2125 strings used to encode them, so values written as numbers shall be read back
2126 as QString. The numeric value can be recovered using \l QString::toInt(), \l
2127 QString::toDouble() and related functions.
2128
2129 \section1 Restoring the State of a GUI Application
2130
2131 QSettings is often used to store the state of a GUI
2132 application. The following example illustrates how to use QSettings
2133 to save and restore the geometry of an application's main window.
2134
2135 \snippet settings/settings.cpp 16
2136 \codeline
2137 \snippet settings/settings.cpp 17
2138
2139 See \l{Window Geometry} for a discussion on why it is better to
2140 call QWidget::resize() and QWidget::move() rather than QWidget::setGeometry()
2141 to restore a window's geometry.
2142
2143 The \c readSettings() and \c writeSettings() functions must be
2144 called from the main window's constructor and close event handler
2145 as follows:
2146
2147 \snippet settings/settings.cpp 18
2148 \dots
2149 \snippet settings/settings.cpp 19
2150 \snippet settings/settings.cpp 20
2151 \codeline
2152 \snippet settings/settings.cpp 21
2153
2154 \section1 Accessing Settings from Multiple Threads or Processes Simultaneously
2155
2156 QSettings is \l{reentrant}. This means that you can use
2157 distinct QSettings object in different threads
2158 simultaneously. This guarantee stands even when the QSettings
2159 objects refer to the same files on disk (or to the same entries
2160 in the system registry). If a setting is modified through one
2161 QSettings object, the change will immediately be visible in
2162 any other QSettings objects that operate on the same location
2163 and that live in the same process.
2164
2165 QSettings can safely be used from different processes (which can
2166 be different instances of your application running at the same
2167 time or different applications altogether) to read and write to
2168 the same system locations, provided certain conditions are met. For
2169 QSettings::IniFormat, it uses advisory file locking and a smart merging
2170 algorithm to ensure data integrity. The condition for that to work is that
2171 the writeable configuration file must be a regular file and must reside in
2172 a directory that the current user can create new, temporary files in. If
2173 that is not the case, then one must use setAtomicSyncRequired() to turn the
2174 safety off.
2175
2176 Note that sync() imports changes made by other processes (in addition to
2177 writing the changes from this QSettings).
2178
2179 \section1 Platform-Specific Notes
2180
2181 \section2 Locations Where Application Settings Are Stored
2182
2183 As mentioned in the \l{Fallback Mechanism} section, QSettings
2184 stores settings for an application in up to four locations,
2185 depending on whether the settings are user-specific or
2186 system-wide and whether the settings are application-specific
2187 or organization-wide. For simplicity, we're assuming the
2188 organization is called MySoft and the application is called Star
2189 Runner.
2190
2191 On Unix systems, if the file format is NativeFormat, the
2192 following files are used by default:
2193
2194 \list 1
2195 \li \c{$HOME/.config/MySoft/Star Runner.conf}
2196 \li \c{$HOME/.config/MySoft.conf}
2197 \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.conf}
2198 \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.conf}
2199 \endlist
2200 \note If XDG_CONFIG_DIRS is unset, the default value of \c{/etc/xdg} is used.
2201
2202 On \macos and iOS, if the file format is NativeFormat, these files are used by
2203 default:
2204
2205 \list 1
2206 \li \c{$HOME/Library/Preferences/com.MySoft.Star Runner.plist}
2207 \li \c{$HOME/Library/Preferences/com.MySoft.plist}
2208 \li \c{/Library/Preferences/com.MySoft.Star Runner.plist}
2209 \li \c{/Library/Preferences/com.MySoft.plist}
2210 \endlist
2211
2212 On Windows, NativeFormat settings are stored in the following
2213 registry paths:
2214
2215 \list 1
2216 \li \c{HKEY_CURRENT_USER\Software\MySoft\Star Runner}
2217 \li \c{HKEY_CURRENT_USER\Software\MySoft\OrganizationDefaults}
2218 \li \c{HKEY_LOCAL_MACHINE\Software\MySoft\Star Runner}
2219 \li \c{HKEY_LOCAL_MACHINE\Software\MySoft\OrganizationDefaults}
2220 \endlist
2221
2222 \note On Windows, for 32-bit programs running in WOW64 mode, settings are
2223 stored in the following registry path:
2224 \c{HKEY_LOCAL_MACHINE\Software\WOW6432node}.
2225
2226 If the file format is NativeFormat, this is "Settings/MySoft/Star Runner.conf"
2227 in the application's home directory.
2228
2229 If the file format is IniFormat, the following files are
2230 used on Unix, \macos, and iOS:
2231
2232 \list 1
2233 \li \c{$HOME/.config/MySoft/Star Runner.ini}
2234 \li \c{$HOME/.config/MySoft.ini}
2235 \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.ini}
2236 \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.ini}
2237 \endlist
2238 \note If XDG_CONFIG_DIRS is unset, the default value of \c{/etc/xdg} is used.
2239
2240 On Windows, the following files are used:
2241
2242 \list 1
2243 \li \c{FOLDERID_RoamingAppData\MySoft\Star Runner.ini}
2244 \li \c{FOLDERID_RoamingAppData\MySoft.ini}
2245 \li \c{FOLDERID_ProgramData\MySoft\Star Runner.ini}
2246 \li \c{FOLDERID_ProgramData\MySoft.ini}
2247 \endlist
2248
2249 The identifiers prefixed by \c{FOLDERID_} are special item ID lists to be passed
2250 to the Win32 API function \c{SHGetKnownFolderPath()} to obtain the
2251 corresponding path.
2252
2253 \c{FOLDERID_RoamingAppData} usually points to \tt{C:\\Users\\\e{User Name}\\AppData\\Roaming},
2254 also shown by the environment variable \c{%APPDATA%}.
2255
2256 \c{FOLDERID_ProgramData} usually points to \tt{C:\\ProgramData}.
2257
2258 If the file format is IniFormat, this is "Settings/MySoft/Star Runner.ini"
2259 in the application's home directory.
2260
2261 The paths for the \c .ini and \c .conf files can be changed using
2262 setPath(). On Unix, \macos, and iOS the user can override them by
2263 setting the \c XDG_CONFIG_HOME environment variable; see
2264 setPath() for details.
2265
2266 \section2 Accessing INI and .plist Files Directly
2267
2268 Sometimes you do want to access settings stored in a specific
2269 file or registry path. On all platforms, if you want to read an
2270 INI file directly, you can use the QSettings constructor that
2271 takes a file name as first argument and pass QSettings::IniFormat
2272 as second argument. For example:
2273
2274 \snippet code/src_corelib_io_qsettings.cpp 2
2275
2276 You can then use the QSettings object to read and write settings
2277 in the file.
2278
2279 On \macos and iOS, you can access property list \c .plist files by passing
2280 QSettings::NativeFormat as second argument. For example:
2281
2282 \snippet code/src_corelib_io_qsettings.cpp 3
2283
2284 \section2 Accessing the Windows Registry Directly
2285
2286 On Windows, QSettings lets you access settings that have been
2287 written with QSettings (or settings in a supported format, e.g., string
2288 data) in the system registry. This is done by constructing a QSettings
2289 object with a path in the registry and QSettings::NativeFormat.
2290
2291 For example:
2292
2293 \snippet code/src_corelib_io_qsettings.cpp 4
2294
2295 All the registry entries that appear under the specified path can
2296 be read or written through the QSettings object as usual (using
2297 forward slashes instead of backslashes). For example:
2298
2299 \snippet code/src_corelib_io_qsettings.cpp 5
2300
2301 Note that the backslash character is, as mentioned, used by
2302 QSettings to separate subkeys. As a result, you cannot read or
2303 write windows registry entries that contain slashes or
2304 backslashes; you should use a native windows API if you need to do
2305 so.
2306
2307 \section2 Accessing Common Registry Settings on Windows
2308
2309 On Windows, it is possible for a key to have both a value and subkeys.
2310 Its default value is accessed by using "Default" or "." in
2311 place of a subkey:
2312
2313 \snippet code/src_corelib_io_qsettings.cpp 6
2314
2315 On other platforms than Windows, "Default" and "." would be
2316 treated as regular subkeys.
2317
2318 \section2 Platform Limitations
2319
2320 While QSettings attempts to smooth over the differences between
2321 the different supported platforms, there are still a few
2322 differences that you should be aware of when porting your
2323 application:
2324
2325 \list
2326 \li The Windows system registry has the following limitations: A
2327 subkey may not exceed 255 characters, an entry's value may
2328 not exceed 16,383 characters, and all the values of a key may
2329 not exceed 65,535 characters. One way to work around these
2330 limitations is to store the settings using the IniFormat
2331 instead of the NativeFormat.
2332
2333 \li On Windows, when the Windows system registry is used, QSettings
2334 does not preserve the original type of the value. Therefore,
2335 the type of the value might change when a new value is set. For
2336 example, a value with type \c REG_EXPAND_SZ will change to \c REG_SZ.
2337
2338 \li On \macos and iOS, allKeys() will return some extra keys for global
2339 settings that apply to all applications. These keys can be
2340 read using value() but cannot be changed, only shadowed.
2341 Calling setFallbacksEnabled(false) will hide these global
2342 settings.
2343
2344 \li On \macos and iOS, the CFPreferences API used by QSettings expects
2345 Internet domain names rather than organization names. To
2346 provide a uniform API, QSettings derives a fake domain name
2347 from the organization name (unless the organization name
2348 already is a domain name, e.g. OpenOffice.org). The algorithm
2349 appends ".com" to the company name and replaces spaces and
2350 other illegal characters with hyphens. If you want to specify
2351 a different domain name, call
2352 QCoreApplication::setOrganizationDomain(),
2353 QCoreApplication::setOrganizationName(), and
2354 QCoreApplication::setApplicationName() in your \c main()
2355 function and then use the default QSettings constructor.
2356 Another solution is to use preprocessor directives, for
2357 example:
2358
2359 \snippet code/src_corelib_io_qsettings.cpp 7
2360
2361 \li On \macos, permissions to access settings not belonging to the
2362 current user (i.e. SystemScope) have changed with 10.7 (Lion). Prior to
2363 that version, users having admin rights could access these. For 10.7 and
2364 10.8 (Mountain Lion), only root can. However, 10.9 (Mavericks) changes
2365 that rule again but only for the native format (plist files).
2366
2367 \endlist
2368
2369 \sa QVariant, QSessionManager
2370*/
2371
2372/*! \enum QSettings::Status
2373
2374 The following status values are possible:
2375
2376 \value NoError No error occurred.
2377 \value AccessError An access error occurred (e.g. trying to write to a read-only file).
2378 \value FormatError A format error occurred (e.g. loading a malformed INI file).
2379
2380 \sa status()
2381*/
2382
2383/*! \enum QSettings::Format
2384
2385 This enum type specifies the storage format used by QSettings.
2386
2387 \value NativeFormat Store the settings using the most
2388 appropriate storage format for the platform.
2389 On Windows, this means the system registry;
2390 on \macos and iOS, this means the CFPreferences
2391 API; on Unix, this means textual
2392 configuration files in INI format.
2393 \value Registry32Format Windows only: Explicitly access the 32-bit system registry
2394 from a 64-bit application running on 64-bit Windows.
2395 On 32-bit Windows or from a 32-bit application on 64-bit Windows,
2396 this works the same as specifying NativeFormat.
2397 This enum value was added in Qt 5.7.
2398 \value Registry64Format Windows only: Explicitly access the 64-bit system registry
2399 from a 32-bit application running on 64-bit Windows.
2400 On 32-bit Windows or from a 64-bit application on 64-bit Windows,
2401 this works the same as specifying NativeFormat.
2402 This enum value was added in Qt 5.7.
2403 \value IniFormat Store the settings in INI files. Note that INI files
2404 lose the distinction between numeric data and the
2405 strings used to encode them, so values written as
2406 numbers shall be read back as QString.
2407 \value WebLocalStorageFormat
2408 WASM only: Store the settings in window.localStorage for the current
2409 origin. If cookies are not allowed, this falls back to the INI format.
2410 This provides up to 5MiB storage per origin, but access to it is
2411 synchronous and JSPI is not required.
2412 \value WebIndexedDBFormat
2413 WASM only: Store the settings in an Indexed DB for the current
2414 origin. If cookies are not allowed, this falls back to the INI format.
2415 This requires JSPI, but provides more storage than
2416 WebLocalStorageFormat.
2417
2418 \value InvalidFormat Special value returned by registerFormat().
2419 \omitvalue CustomFormat1
2420 \omitvalue CustomFormat2
2421 \omitvalue CustomFormat3
2422 \omitvalue CustomFormat4
2423 \omitvalue CustomFormat5
2424 \omitvalue CustomFormat6
2425 \omitvalue CustomFormat7
2426 \omitvalue CustomFormat8
2427 \omitvalue CustomFormat9
2428 \omitvalue CustomFormat10
2429 \omitvalue CustomFormat11
2430 \omitvalue CustomFormat12
2431 \omitvalue CustomFormat13
2432 \omitvalue CustomFormat14
2433 \omitvalue CustomFormat15
2434 \omitvalue CustomFormat16
2435
2436 On Unix, NativeFormat and IniFormat mean the same thing, except
2437 that the file extension is different (\c .conf for NativeFormat,
2438 \c .ini for IniFormat).
2439
2440 The INI file format is a Windows file format that Qt supports on
2441 all platforms. In the absence of an INI standard, we try to
2442 follow what Microsoft does, with the following exceptions:
2443
2444 \list
2445 \li If you store types that QVariant can't convert to QString
2446 (e.g., QPoint, QRect, and QSize), Qt uses an \c{@}-based
2447 syntax to encode the type. For example:
2448
2449 \snippet code/src_corelib_io_qsettings.cpp 8
2450
2451 To minimize compatibility issues, any \c @ that doesn't
2452 appear at the first position in the value or that isn't
2453 followed by a Qt type (\c Point, \c Rect, \c Size, etc.) is
2454 treated as a normal character.
2455
2456 \li Although backslash is a special character in INI files, most
2457 Windows applications don't escape backslashes (\c{\}) in file
2458 paths:
2459
2460 \snippet code/src_corelib_io_qsettings.cpp 9
2461
2462 QSettings always treats backslash as a special character and
2463 provides no API for reading or writing such entries.
2464
2465 \li The INI file format has severe restrictions on the syntax of
2466 a key. Qt works around this by using \c % as an escape
2467 character in keys. In addition, if you save a top-level
2468 setting (a key with no slashes in it, e.g., "someKey"), it
2469 will appear in the INI file's "General" section. To avoid
2470 overwriting other keys, if you save something using a key
2471 such as "General/someKey", the key will be located in the
2472 "%General" section, \e not in the "General" section.
2473
2474 \li In line with most implementations today, QSettings will assume that
2475 \e values in the INI file are utf-8 encoded. This means that \e values
2476 will be decoded as utf-8 encoded entries and written back as utf-8.
2477 To retain backward compatibility with older Qt versions, \e keys in the
2478 INI file are written in %-encoded format, but can be read in both
2479 %-encoded and utf-8 formats.
2480
2481 \endlist
2482
2483 \section2 Compatibility with older Qt versions
2484
2485 Please note that this behavior is different to how QSettings behaved
2486 in versions of Qt prior to Qt 6. INI files written with Qt 5 or earlier are
2487 however fully readable by a Qt 6 based application (unless a ini codec
2488 different from utf8 had been set). But INI files written with Qt 6
2489 will only be readable by older Qt versions if you set the "iniCodec" to
2490 a utf-8 textcodec.
2491
2492 \sa registerFormat(), setPath()
2493*/
2494
2495/*! \enum QSettings::Scope
2496
2497 This enum specifies whether settings are user-specific or shared
2498 by all users of the same system.
2499
2500 \value UserScope Store settings in a location specific to the
2501 current user (e.g., in the user's home
2502 directory).
2503 \value SystemScope Store settings in a global location, so that
2504 all users on the same machine access the same
2505 set of settings.
2506
2507 \sa setPath()
2508*/
2509
2510#ifndef QT_NO_QOBJECT
2511/*!
2512 Constructs a QSettings object for accessing settings of the
2513 application called \a application from the organization called \a
2514 organization, and with parent \a parent.
2515
2516 Example:
2517 \snippet code/src_corelib_io_qsettings.cpp 10
2518
2519 The scope is set to QSettings::UserScope, and the format is
2520 set to QSettings::NativeFormat (i.e. calling setDefaultFormat()
2521 before calling this constructor has no effect).
2522
2523 \sa setDefaultFormat(), {Fallback Mechanism}
2524*/
2525QSettings::QSettings(const QString &organization, const QString &application, QObject *parent)
2526 : QObject(*QSettingsPrivate::create(format: NativeFormat, scope: UserScope, organization, application),
2527 parent)
2528{
2529}
2530
2531/*!
2532 Constructs a QSettings object for accessing settings of the
2533 application called \a application from the organization called \a
2534 organization, and with parent \a parent.
2535
2536 If \a scope is QSettings::UserScope, the QSettings object searches
2537 user-specific settings first, before it searches system-wide
2538 settings as a fallback. If \a scope is QSettings::SystemScope, the
2539 QSettings object ignores user-specific settings and provides
2540 access to system-wide settings.
2541
2542 The storage format is set to QSettings::NativeFormat (i.e. calling
2543 setDefaultFormat() before calling this constructor has no effect).
2544
2545 If no application name is given, the QSettings object will
2546 only access the organization-wide \l{Fallback Mechanism}{locations}.
2547
2548 \sa setDefaultFormat()
2549*/
2550QSettings::QSettings(Scope scope, const QString &organization, const QString &application,
2551 QObject *parent)
2552 : QObject(*QSettingsPrivate::create(format: NativeFormat, scope, organization, application), parent)
2553{
2554}
2555
2556/*!
2557 Constructs a QSettings object for accessing settings of the
2558 application called \a application from the organization called
2559 \a organization, and with parent \a parent.
2560
2561 If \a scope is QSettings::UserScope, the QSettings object searches
2562 user-specific settings first, before it searches system-wide
2563 settings as a fallback. If \a scope is
2564 QSettings::SystemScope, the QSettings object ignores user-specific
2565 settings and provides access to system-wide settings.
2566
2567 If \a format is QSettings::NativeFormat, the native API is used for
2568 storing settings. If \a format is QSettings::IniFormat, the INI format
2569 is used.
2570
2571 If no application name is given, the QSettings object will
2572 only access the organization-wide \l{Fallback Mechanism}{locations}.
2573*/
2574QSettings::QSettings(Format format, Scope scope, const QString &organization,
2575 const QString &application, QObject *parent)
2576 : QObject(*QSettingsPrivate::create(format, scope, organization, application), parent)
2577{
2578}
2579
2580/*!
2581 Constructs a QSettings object for accessing the settings
2582 stored in the file called \a fileName, with parent \a parent. If
2583 the file doesn't already exist, it is created.
2584
2585 If \a format is QSettings::NativeFormat, the meaning of \a
2586 fileName depends on the platform. On Unix, \a fileName is the
2587 name of an INI file. On \macos and iOS, \a fileName is the name of a
2588 \c .plist file. On Windows, \a fileName is a path in the system
2589 registry.
2590
2591 If \a format is QSettings::IniFormat, \a fileName is the name of an INI
2592 file.
2593
2594 \warning This function is provided for convenience. It works well for
2595 accessing INI or \c .plist files generated by Qt, but might fail on some
2596 syntaxes found in such files originated by other programs. In particular,
2597 be aware of the following limitations:
2598
2599 \list
2600 \li QSettings provides no way of reading INI "path" entries, i.e., entries
2601 with unescaped slash characters. (This is because these entries are
2602 ambiguous and cannot be resolved automatically.)
2603 \li In INI files, QSettings uses the \c @ character as a metacharacter in some
2604 contexts, to encode Qt-specific data types (e.g., \c @Rect), and might
2605 therefore misinterpret it when it occurs in pure INI files.
2606 \endlist
2607
2608 \sa fileName()
2609*/
2610QSettings::QSettings(const QString &fileName, Format format, QObject *parent)
2611 : QObject(*QSettingsPrivate::create(fileName, format), parent)
2612{
2613}
2614
2615/*!
2616 Constructs a QSettings object for accessing settings of the
2617 application and organization set previously with a call to
2618 QCoreApplication::setOrganizationName(),
2619 QCoreApplication::setOrganizationDomain(), and
2620 QCoreApplication::setApplicationName().
2621
2622 The scope is QSettings::UserScope and the format is
2623 defaultFormat() (QSettings::NativeFormat by default).
2624 Use setDefaultFormat() before calling this constructor
2625 to change the default format used by this constructor.
2626
2627 The code
2628
2629 \snippet code/src_corelib_io_qsettings.cpp 11
2630
2631 is equivalent to
2632
2633 \snippet code/src_corelib_io_qsettings.cpp 12
2634
2635 If QCoreApplication::setOrganizationName() and
2636 QCoreApplication::setApplicationName() has not been previously
2637 called, the QSettings object will not be able to read or write
2638 any settings, and status() will return AccessError.
2639
2640 You should supply both the domain (used by default on \macos and iOS) and
2641 the name (used by default elsewhere), although the code will cope if you
2642 supply only one, which will then be used (on all platforms), at odds with
2643 the usual naming of the file on platforms for which it isn't the default.
2644
2645 \sa QCoreApplication::setOrganizationName(),
2646 QCoreApplication::setOrganizationDomain(),
2647 QCoreApplication::setApplicationName(),
2648 setDefaultFormat()
2649*/
2650QSettings::QSettings(QObject *parent)
2651 : QSettings(UserScope, parent)
2652{
2653}
2654
2655/*!
2656 \since 5.13
2657
2658 Constructs a QSettings object in the same way as
2659 QSettings(QObject *parent) but with the given \a scope.
2660
2661 \sa QSettings(QObject *parent)
2662*/
2663QSettings::QSettings(Scope scope, QObject *parent)
2664 : QObject(*QSettingsPrivate::create(format: globalDefaultFormat, scope,
2665#ifdef Q_OS_DARWIN
2666 QCoreApplication::organizationDomain().isEmpty()
2667 ? QCoreApplication::organizationName()
2668 : QCoreApplication::organizationDomain()
2669#else
2670 organization: QCoreApplication::organizationName().isEmpty()
2671 ? QCoreApplication::organizationDomain()
2672 : QCoreApplication::organizationName()
2673#endif
2674 , application: QCoreApplication::applicationName()),
2675 parent)
2676{
2677}
2678
2679#else
2680QSettings::QSettings(const QString &organization, const QString &application)
2681 : d_ptr(QSettingsPrivate::create(globalDefaultFormat, QSettings::UserScope, organization, application))
2682{
2683 d_ptr->q_ptr = this;
2684}
2685
2686QSettings::QSettings(Scope scope, const QString &organization, const QString &application)
2687 : d_ptr(QSettingsPrivate::create(globalDefaultFormat, scope, organization, application))
2688{
2689 d_ptr->q_ptr = this;
2690}
2691
2692QSettings::QSettings(Format format, Scope scope, const QString &organization,
2693 const QString &application)
2694 : d_ptr(QSettingsPrivate::create(format, scope, organization, application))
2695{
2696 d_ptr->q_ptr = this;
2697}
2698
2699QSettings::QSettings(const QString &fileName, Format format)
2700 : d_ptr(QSettingsPrivate::create(fileName, format))
2701{
2702 d_ptr->q_ptr = this;
2703}
2704
2705QSettings::QSettings(Scope scope)
2706 : d_ptr(QSettingsPrivate::create(globalDefaultFormat, scope,
2707# ifdef Q_OS_DARWIN
2708 QCoreApplication::organizationDomain().isEmpty()
2709 ? QCoreApplication::organizationName()
2710 : QCoreApplication::organizationDomain()
2711# else
2712 QCoreApplication::organizationName().isEmpty()
2713 ? QCoreApplication::organizationDomain()
2714 : QCoreApplication::organizationName()
2715# endif
2716 , QCoreApplication::applicationName())
2717 )
2718{
2719 d_ptr->q_ptr = this;
2720}
2721#endif
2722
2723/*!
2724 Destroys the QSettings object.
2725
2726 Any unsaved changes will eventually be written to permanent
2727 storage.
2728
2729 \sa sync()
2730*/
2731QSettings::~QSettings()
2732{
2733 Q_D(QSettings);
2734 if (d->pendingChanges) {
2735 // Don't cause a failing flush() to std::terminate() the whole
2736 // application - dtors are implicitly noexcept!
2737 QT_TRY {
2738 d->flush();
2739 } QT_CATCH(...) {
2740 }
2741 }
2742}
2743
2744/*!
2745 Removes all entries in the primary location associated to this
2746 QSettings object.
2747
2748 Entries in fallback locations are not removed.
2749
2750 If you only want to remove the entries in the current group(),
2751 use remove("") instead.
2752
2753 \sa remove(), setFallbacksEnabled()
2754*/
2755void QSettings::clear()
2756{
2757 Q_D(QSettings);
2758 d->clear();
2759 d->requestUpdate();
2760}
2761
2762/*!
2763 Writes any unsaved changes to permanent storage, and reloads any
2764 settings that have been changed in the meantime by another
2765 application.
2766
2767 This function is called automatically from QSettings's destructor and
2768 by the event loop at regular intervals, so you normally don't need to
2769 call it yourself.
2770
2771 \sa status()
2772*/
2773void QSettings::sync()
2774{
2775 Q_D(QSettings);
2776 d->sync();
2777 d->pendingChanges = false;
2778}
2779
2780/*!
2781 Returns the path where settings written using this QSettings
2782 object are stored.
2783
2784 On Windows, if the format is QSettings::NativeFormat, the return value
2785 is a system registry path, not a file path.
2786
2787 \sa isWritable(), format()
2788*/
2789QString QSettings::fileName() const
2790{
2791 Q_D(const QSettings);
2792 return d->fileName();
2793}
2794
2795/*!
2796 \since 4.4
2797
2798 Returns the format used for storing the settings.
2799
2800 \sa defaultFormat(), fileName(), scope(), organizationName(), applicationName()
2801*/
2802QSettings::Format QSettings::format() const
2803{
2804 Q_D(const QSettings);
2805 return d->format;
2806}
2807
2808/*!
2809 \since 4.4
2810
2811 Returns the scope used for storing the settings.
2812
2813 \sa format(), organizationName(), applicationName()
2814*/
2815QSettings::Scope QSettings::scope() const
2816{
2817 Q_D(const QSettings);
2818 return d->scope;
2819}
2820
2821/*!
2822 \since 4.4
2823
2824 Returns the organization name used for storing the settings.
2825
2826 \sa QCoreApplication::organizationName(), format(), scope(), applicationName()
2827*/
2828QString QSettings::organizationName() const
2829{
2830 Q_D(const QSettings);
2831 return d->organizationName;
2832}
2833
2834/*!
2835 \since 4.4
2836
2837 Returns the application name used for storing the settings.
2838
2839 \sa QCoreApplication::applicationName(), format(), scope(), organizationName()
2840*/
2841QString QSettings::applicationName() const
2842{
2843 Q_D(const QSettings);
2844 return d->applicationName;
2845}
2846
2847/*!
2848 Returns a status code indicating the first error that was met by
2849 QSettings, or QSettings::NoError if no error occurred.
2850
2851 Be aware that QSettings delays performing some operations. For this
2852 reason, you might want to call sync() to ensure that the data stored
2853 in QSettings is written to disk before calling status().
2854
2855 \sa sync()
2856*/
2857QSettings::Status QSettings::status() const
2858{
2859 Q_D(const QSettings);
2860 return d->status;
2861}
2862
2863/*!
2864 \since 5.10
2865
2866 Returns \c true if QSettings is only allowed to perform atomic saving and
2867 reloading (synchronization) of the settings. Returns \c false if it is
2868 allowed to save the settings contents directly to the configuration file.
2869
2870 The default is \c true.
2871
2872 \sa setAtomicSyncRequired(), QSaveFile
2873*/
2874bool QSettings::isAtomicSyncRequired() const
2875{
2876 Q_D(const QSettings);
2877 return d->atomicSyncOnly;
2878}
2879
2880/*!
2881 \since 5.10
2882
2883 Configures whether QSettings is required to perform atomic saving and
2884 reloading (synchronization) of the settings. If the \a enable argument is
2885 \c true (the default), sync() will only perform synchronization operations
2886 that are atomic. If this is not possible, sync() will fail and status()
2887 will be an error condition.
2888
2889 Setting this property to \c false will allow QSettings to write directly to
2890 the configuration file and ignore any errors trying to lock it against
2891 other processes trying to write at the same time. Because of the potential
2892 for corruption, this option should be used with care, but is required in
2893 certain conditions, like a QSettings::IniFormat configuration file that
2894 exists in an otherwise non-writeable directory or NTFS Alternate Data
2895 Streams.
2896
2897 See \l QSaveFile for more information on the feature.
2898
2899 \sa isAtomicSyncRequired(), QSaveFile
2900*/
2901void QSettings::setAtomicSyncRequired(bool enable)
2902{
2903 Q_D(QSettings);
2904 d->atomicSyncOnly = enable;
2905}
2906
2907/*!
2908 Appends \a prefix to the current group.
2909
2910 The current group is automatically prepended to all keys
2911 specified to QSettings. In addition, query functions such as
2912 childGroups(), childKeys(), and allKeys() are based on the group.
2913 By default, no group is set.
2914
2915 Groups are useful to avoid typing in the same setting paths over
2916 and over. For example:
2917
2918 \snippet code/src_corelib_io_qsettings.cpp 13
2919
2920 This will set the value of three settings:
2921
2922 \list
2923 \li \c mainwindow/size
2924 \li \c mainwindow/fullScreen
2925 \li \c outputpanel/visible
2926 \endlist
2927
2928 Call endGroup() to reset the current group to what it was before
2929 the corresponding beginGroup() call. Groups can be nested.
2930
2931 \note In Qt versions prior to 6.4, this function took QString, not
2932 QAnyStringView.
2933
2934 \sa endGroup(), group()
2935*/
2936void QSettings::beginGroup(QAnyStringView prefix)
2937{
2938 Q_D(QSettings);
2939 d->beginGroupOrArray(group: QSettingsGroup(d->normalizedKey(key: prefix)));
2940}
2941
2942/*!
2943 Resets the group to what it was before the corresponding
2944 beginGroup() call.
2945
2946 Example:
2947
2948 \snippet code/src_corelib_io_qsettings.cpp 14
2949
2950 \sa beginGroup(), group()
2951*/
2952void QSettings::endGroup()
2953{
2954 Q_D(QSettings);
2955 if (d->groupStack.isEmpty()) {
2956 qWarning(msg: "QSettings::endGroup: No matching beginGroup()");
2957 return;
2958 }
2959
2960 QSettingsGroup group = d->groupStack.pop();
2961 qsizetype len = group.toString().size();
2962 if (len > 0)
2963 d->groupPrefix.truncate(pos: d->groupPrefix.size() - (len + 1));
2964
2965 if (group.isArray())
2966 qWarning(msg: "QSettings::endGroup: Expected endArray() instead");
2967}
2968
2969/*!
2970 Returns the current group.
2971
2972 \sa beginGroup(), endGroup()
2973*/
2974QString QSettings::group() const
2975{
2976 Q_D(const QSettings);
2977 return d->groupPrefix.left(n: d->groupPrefix.size() - 1);
2978}
2979
2980/*!
2981 Adds \a prefix to the current group and starts reading from an
2982 array. Returns the size of the array.
2983
2984 Example:
2985
2986 \snippet code/src_corelib_io_qsettings.cpp 15
2987
2988 Use beginWriteArray() to write the array in the first place.
2989
2990 \note In Qt versions prior to 6.4, this function took QString, not
2991 QAnyStringView.
2992
2993 \sa beginWriteArray(), endArray(), setArrayIndex()
2994*/
2995int QSettings::beginReadArray(QAnyStringView prefix)
2996{
2997 Q_D(QSettings);
2998 d->beginGroupOrArray(group: QSettingsGroup(d->normalizedKey(key: prefix), false));
2999 return value(key: "size"_L1).toInt();
3000}
3001
3002/*!
3003 Adds \a prefix to the current group and starts writing an array
3004 of size \a size. If \a size is -1 (the default), it is automatically
3005 determined based on the indexes of the entries written.
3006
3007 If you have many occurrences of a certain set of keys, you can
3008 use arrays to make your life easier. For example, let's suppose
3009 that you want to save a variable-length list of user names and
3010 passwords. You could then write:
3011
3012 \snippet code/src_corelib_io_qsettings.cpp 16
3013
3014 The generated keys will have the form
3015
3016 \list
3017 \li \c logins/size
3018 \li \c logins/1/userName
3019 \li \c logins/1/password
3020 \li \c logins/2/userName
3021 \li \c logins/2/password
3022 \li \c logins/3/userName
3023 \li \c logins/3/password
3024 \li ...
3025 \endlist
3026
3027 To read back an array, use beginReadArray().
3028
3029 \note In Qt versions prior to 6.4, this function took QString, not
3030 QAnyStringView.
3031
3032 \sa beginReadArray(), endArray(), setArrayIndex()
3033*/
3034void QSettings::beginWriteArray(QAnyStringView prefix, int size)
3035{
3036 Q_D(QSettings);
3037 d->beginGroupOrArray(group: QSettingsGroup(d->normalizedKey(key: prefix), size < 0));
3038
3039 if (size < 0)
3040 remove(key: "size"_L1);
3041 else
3042 setValue(key: "size"_L1, value: size);
3043}
3044
3045/*!
3046 Closes the array that was started using beginReadArray() or
3047 beginWriteArray().
3048
3049 \sa beginReadArray(), beginWriteArray()
3050*/
3051void QSettings::endArray()
3052{
3053 Q_D(QSettings);
3054 if (d->groupStack.isEmpty()) {
3055 qWarning(msg: "QSettings::endArray: No matching beginArray()");
3056 return;
3057 }
3058
3059 QSettingsGroup group = d->groupStack.top();
3060 qsizetype len = group.toString().size();
3061 d->groupStack.pop();
3062 if (len > 0)
3063 d->groupPrefix.truncate(pos: d->groupPrefix.size() - (len + 1));
3064
3065 if (group.arraySizeGuess() != -1)
3066 setValue(key: group.name() + "/size"_L1, value: group.arraySizeGuess());
3067
3068 if (!group.isArray())
3069 qWarning(msg: "QSettings::endArray: Expected endGroup() instead");
3070}
3071
3072/*!
3073 Sets the current array index to \a i. Calls to functions such as
3074 setValue(), value(), remove(), and contains() will operate on the
3075 array entry at that index.
3076
3077 You must call beginReadArray() or beginWriteArray() before you
3078 can call this function.
3079*/
3080void QSettings::setArrayIndex(int i)
3081{
3082 Q_D(QSettings);
3083 if (d->groupStack.isEmpty() || !d->groupStack.top().isArray()) {
3084 qWarning(msg: "QSettings::setArrayIndex: Missing beginArray()");
3085 return;
3086 }
3087
3088 QSettingsGroup &top = d->groupStack.top();
3089 qsizetype len = top.toString().size();
3090 top.setArrayIndex(qMax(a: i, b: 0));
3091 d->groupPrefix.replace(i: d->groupPrefix.size() - len - 1, len, after: top.toString());
3092}
3093
3094/*!
3095 Returns a list of all keys, including subkeys, that can be read
3096 using the QSettings object.
3097
3098 Example:
3099
3100 \snippet code/src_corelib_io_qsettings.cpp 17
3101
3102 If a group is set using beginGroup(), only the keys in the group
3103 are returned, without the group prefix:
3104
3105 \snippet code/src_corelib_io_qsettings.cpp 18
3106
3107 \sa childGroups(), childKeys()
3108*/
3109QStringList QSettings::allKeys() const
3110{
3111 Q_D(const QSettings);
3112 return d->children(prefix: d->groupPrefix, spec: QSettingsPrivate::AllKeys);
3113}
3114
3115/*!
3116 Returns a list of all top-level keys that can be read using the
3117 QSettings object.
3118
3119 Example:
3120
3121 \snippet code/src_corelib_io_qsettings.cpp 19
3122
3123 If a group is set using beginGroup(), the top-level keys in that
3124 group are returned, without the group prefix:
3125
3126 \snippet code/src_corelib_io_qsettings.cpp 20
3127
3128 You can navigate through the entire setting hierarchy using
3129 childKeys() and childGroups() recursively.
3130
3131 \sa childGroups(), allKeys()
3132*/
3133QStringList QSettings::childKeys() const
3134{
3135 Q_D(const QSettings);
3136 return d->children(prefix: d->groupPrefix, spec: QSettingsPrivate::ChildKeys);
3137}
3138
3139/*!
3140 Returns a list of all key top-level groups that contain keys that
3141 can be read using the QSettings object.
3142
3143 Example:
3144
3145 \snippet code/src_corelib_io_qsettings.cpp 21
3146
3147 If a group is set using beginGroup(), the first-level keys in
3148 that group are returned, without the group prefix.
3149
3150 \snippet code/src_corelib_io_qsettings.cpp 22
3151
3152 You can navigate through the entire setting hierarchy using
3153 childKeys() and childGroups() recursively.
3154
3155 \sa childKeys(), allKeys()
3156*/
3157QStringList QSettings::childGroups() const
3158{
3159 Q_D(const QSettings);
3160 return d->children(prefix: d->groupPrefix, spec: QSettingsPrivate::ChildGroups);
3161}
3162
3163/*!
3164 Returns \c true if settings can be written using this QSettings
3165 object; returns \c false otherwise.
3166
3167 One reason why isWritable() might return false is if
3168 QSettings operates on a read-only file.
3169
3170 \warning This function is not perfectly reliable, because the
3171 file permissions can change at any time.
3172
3173 \sa fileName(), status(), sync()
3174*/
3175bool QSettings::isWritable() const
3176{
3177 Q_D(const QSettings);
3178 return d->isWritable();
3179}
3180
3181/*!
3182
3183 Sets the value of setting \a key to \a value. If the \a key already
3184 exists, the previous value is overwritten.
3185
3186 Note that the Windows registry and INI files use case-insensitive
3187 keys, whereas the CFPreferences API on \macos and iOS uses
3188 case-sensitive keys. To avoid portability problems, see the
3189 \l{Section and Key Syntax} rules.
3190
3191 Example:
3192
3193 \snippet code/src_corelib_io_qsettings.cpp 23
3194
3195 \note In Qt versions prior to 6.4, this function took QString, not
3196 QAnyStringView.
3197
3198 \sa value(), remove(), contains()
3199*/
3200void QSettings::setValue(QAnyStringView key, const QVariant &value)
3201{
3202 Q_D(QSettings);
3203 if (key.isEmpty()) {
3204 qWarning(msg: "QSettings::setValue: Empty key passed");
3205 return;
3206 }
3207 d->set(key: d->actualKey(key), value);
3208 d->requestUpdate();
3209}
3210
3211/*!
3212 Removes the setting \a key and any sub-settings of \a key.
3213
3214 Example:
3215
3216 \snippet code/src_corelib_io_qsettings.cpp 24
3217
3218 Be aware that if one of the fallback locations contains a setting
3219 with the same key, that setting will be visible after calling
3220 remove().
3221
3222 If \a key is an empty string, all keys in the current group() are
3223 removed. For example:
3224
3225 \snippet code/src_corelib_io_qsettings.cpp 25
3226
3227 Note that the Windows registry and INI files use case-insensitive
3228 keys, whereas the CFPreferences API on \macos and iOS uses
3229 case-sensitive keys. To avoid portability problems, see the
3230 \l{Section and Key Syntax} rules.
3231
3232 \note In Qt versions prior to 6.4, this function took QString, not
3233 QAnyStringView.
3234
3235 \sa setValue(), value(), contains()
3236*/
3237void QSettings::remove(QAnyStringView key)
3238{
3239 Q_D(QSettings);
3240 /*
3241 We cannot use actualKey(), because remove() supports empty
3242 keys. The code is also tricky because of slash handling.
3243 */
3244 QString theKey = d->normalizedKey(key);
3245 if (theKey.isEmpty())
3246 theKey = group();
3247 else
3248 theKey.prepend(s: d->groupPrefix);
3249
3250 if (theKey.isEmpty()) {
3251 d->clear();
3252 } else {
3253 d->remove(key: theKey);
3254 }
3255 d->requestUpdate();
3256}
3257
3258/*!
3259 Returns \c true if there exists a setting called \a key; returns
3260 false otherwise.
3261
3262 If a group is set using beginGroup(), \a key is taken to be
3263 relative to that group.
3264
3265 Note that the Windows registry and INI files use case-insensitive
3266 keys, whereas the CFPreferences API on \macos and iOS uses
3267 case-sensitive keys. To avoid portability problems, see the
3268 \l{Section and Key Syntax} rules.
3269
3270 \note In Qt versions prior to 6.4, this function took QString, not
3271 QAnyStringView.
3272
3273 \sa value(), setValue()
3274*/
3275bool QSettings::contains(QAnyStringView key) const
3276{
3277 Q_D(const QSettings);
3278 return d->get(key: d->actualKey(key)) != std::nullopt;
3279}
3280
3281/*!
3282 Sets whether fallbacks are enabled to \a b.
3283
3284 By default, fallbacks are enabled.
3285
3286 \sa fallbacksEnabled()
3287*/
3288void QSettings::setFallbacksEnabled(bool b)
3289{
3290 Q_D(QSettings);
3291 d->fallbacks = !!b;
3292}
3293
3294/*!
3295 Returns \c true if fallbacks are enabled; returns \c false otherwise.
3296
3297 By default, fallbacks are enabled.
3298
3299 \sa setFallbacksEnabled()
3300*/
3301bool QSettings::fallbacksEnabled() const
3302{
3303 Q_D(const QSettings);
3304 return d->fallbacks;
3305}
3306
3307#ifndef QT_NO_QOBJECT
3308/*!
3309 \reimp
3310*/
3311bool QSettings::event(QEvent *event)
3312{
3313 Q_D(QSettings);
3314 if (event->type() == QEvent::UpdateRequest) {
3315 d->update();
3316 return true;
3317 }
3318 return QObject::event(event);
3319}
3320#endif
3321
3322/*!
3323 \fn QSettings::value(QAnyStringView key) const
3324 \fn QSettings::value(QAnyStringView key, const QVariant &defaultValue) const
3325
3326 Returns the value for setting \a key. If the setting doesn't
3327 exist, returns \a defaultValue.
3328
3329 If no default value is specified, a default QVariant is
3330 returned.
3331
3332 Note that the Windows registry and INI files use case-insensitive
3333 keys, whereas the CFPreferences API on \macos and iOS uses
3334 case-sensitive keys. To avoid portability problems, see the
3335 \l{Section and Key Syntax} rules.
3336
3337 Example:
3338
3339 \snippet code/src_corelib_io_qsettings.cpp 26
3340
3341 \note In Qt versions prior to 6.4, this function took QString, not
3342 QAnyStringView.
3343
3344 \sa setValue(), contains(), remove()
3345*/
3346QVariant QSettings::value(QAnyStringView key) const
3347{
3348 Q_D(const QSettings);
3349 return d->value(key, defaultValue: nullptr);
3350}
3351
3352QVariant QSettings::value(QAnyStringView key, const QVariant &defaultValue) const
3353{
3354 Q_D(const QSettings);
3355 return d->value(key, defaultValue: &defaultValue);
3356}
3357
3358QVariant QSettingsPrivate::value(QAnyStringView key, const QVariant *defaultValue) const
3359{
3360 if (key.isEmpty()) {
3361 qWarning(msg: "QSettings::value: Empty key passed");
3362 return QVariant();
3363 }
3364 if (std::optional r = get(key: actualKey(key)))
3365 return std::move(*r);
3366 if (defaultValue)
3367 return *defaultValue;
3368 return QVariant();
3369}
3370
3371/*!
3372 \since 4.4
3373
3374 Sets the default file format to the given \a format, which is used
3375 for storing settings for the QSettings(QObject *) constructor.
3376
3377 If no default format is set, QSettings::NativeFormat is used. See
3378 the documentation for the QSettings constructor you are using to
3379 see if that constructor will ignore this function.
3380
3381 \sa format()
3382*/
3383void QSettings::setDefaultFormat(Format format)
3384{
3385 globalDefaultFormat = format;
3386}
3387
3388/*!
3389 \since 4.4
3390
3391 Returns default file format used for storing settings for the QSettings(QObject *) constructor.
3392 If no default format is set, QSettings::NativeFormat is used.
3393
3394 \sa format()
3395*/
3396QSettings::Format QSettings::defaultFormat()
3397{
3398 return globalDefaultFormat;
3399}
3400
3401/*!
3402 \since 4.1
3403
3404 Sets the path used for storing settings for the given \a format
3405 and \a scope, to \a path. The \a format can be a custom format.
3406
3407 The table below summarizes the default values:
3408
3409 \table
3410 \header \li Platform \li Format \li Scope \li Path
3411 \row \li{1,2} Windows \li{1,2} IniFormat \li UserScope \li \c FOLDERID_RoamingAppData
3412 \row \li SystemScope \li \c FOLDERID_ProgramData
3413 \row \li{1,2} Unix \li{1,2} NativeFormat, IniFormat \li UserScope \li \c $HOME/.config
3414 \row \li SystemScope \li \c /etc/xdg
3415 \row \li{1,2} \macos and iOS \li{1,2} IniFormat \li UserScope \li \c $HOME/.config
3416 \row \li SystemScope \li \c /etc/xdg
3417 \endtable
3418
3419 The default UserScope paths on Unix, \macos, and iOS (\c
3420 $HOME/.config or $HOME/Settings) can be overridden by the user by setting the
3421 \c XDG_CONFIG_HOME environment variable. The default SystemScope
3422 paths on Unix, \macos, and iOS (\c /etc/xdg) can be overridden when
3423 building the Qt library using the \c configure script's \c
3424 -sysconfdir flag (see QLibraryInfo for details).
3425
3426 Setting the NativeFormat paths on Windows, \macos, and iOS has no
3427 effect.
3428
3429 \warning This function doesn't affect existing QSettings objects.
3430
3431 \sa registerFormat()
3432*/
3433void QSettings::setPath(Format format, Scope scope, const QString &path)
3434{
3435 auto locker = qt_unique_lock(mutex&: settingsGlobalMutex);
3436 PathHash *pathHash = pathHashFunc();
3437 if (pathHash->isEmpty())
3438 locker = initDefaultPaths(locker: std::move(locker));
3439 pathHash->insert(key: pathHashKey(format, scope), value: Path(path + QDir::separator(), true));
3440}
3441
3442/*!
3443 \typedef QSettings::SettingsMap
3444
3445 Typedef for QMap<QString, QVariant>.
3446
3447 \sa registerFormat()
3448*/
3449
3450/*!
3451 \typedef QSettings::ReadFunc
3452
3453 Typedef for a pointer to a function with the following signature:
3454
3455 \snippet code/src_corelib_io_qsettings.cpp 27
3456
3457 \c ReadFunc is used in \c registerFormat() as a pointer to a function
3458 that reads a set of key/value pairs. \c ReadFunc should read all the
3459 options in one pass, and return all the settings in the \c SettingsMap
3460 container, which is initially empty.
3461
3462 \sa WriteFunc, registerFormat()
3463*/
3464
3465/*!
3466 \typedef QSettings::WriteFunc
3467
3468 Typedef for a pointer to a function with the following signature:
3469
3470 \snippet code/src_corelib_io_qsettings.cpp 28
3471
3472 \c WriteFunc is used in \c registerFormat() as a pointer to a function
3473 that writes a set of key/value pairs. \c WriteFunc is only called once,
3474 so you need to output the settings in one go.
3475
3476 \sa ReadFunc, registerFormat()
3477*/
3478
3479/*!
3480 \since 4.1
3481 \threadsafe
3482
3483 Registers a custom storage format. On success, returns a special
3484 Format value that can then be passed to the QSettings constructor.
3485 On failure, returns InvalidFormat.
3486
3487 The \a extension is the file
3488 extension associated to the format (without the '.').
3489
3490 The \a readFunc and \a writeFunc parameters are pointers to
3491 functions that read and write a set of key/value pairs. The
3492 QIODevice parameter to the read and write functions is always
3493 opened in binary mode (i.e., without the QIODevice::Text flag).
3494
3495 The \a caseSensitivity parameter specifies whether keys are case
3496 sensitive or not. This makes a difference when looking up values
3497 using QSettings. The default is case sensitive.
3498
3499 By default, if you use one of the constructors that work in terms
3500 of an organization name and an application name, the file system
3501 locations used are the same as for IniFormat. Use setPath() to
3502 specify other locations.
3503
3504 Example:
3505
3506 \snippet code/src_corelib_io_qsettings.cpp 29
3507
3508 \sa setPath()
3509*/
3510QSettings::Format QSettings::registerFormat(const QString &extension, ReadFunc readFunc,
3511 WriteFunc writeFunc,
3512 Qt::CaseSensitivity caseSensitivity)
3513{
3514#ifdef QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER
3515 Q_ASSERT(caseSensitivity == Qt::CaseSensitive);
3516#endif
3517
3518 const auto locker = qt_scoped_lock(mutex&: settingsGlobalMutex);
3519 CustomFormatVector *customFormatVector = customFormatVectorFunc();
3520 qsizetype index = customFormatVector->size();
3521 if (index == 16) // the QSettings::Format enum has room for 16 custom formats
3522 return QSettings::InvalidFormat;
3523
3524 QConfFileCustomFormat info;
3525 info.extension = u'.' + extension;
3526 info.readFunc = readFunc;
3527 info.writeFunc = writeFunc;
3528 info.caseSensitivity = caseSensitivity;
3529 customFormatVector->append(t: info);
3530
3531 return QSettings::Format(int(QSettings::CustomFormat1) + index);
3532}
3533
3534QT_END_NAMESPACE
3535
3536#ifndef QT_BOOTSTRAPPED
3537#include "moc_qsettings.cpp"
3538#endif
3539

source code of qtbase/src/corelib/io/qsettings.cpp