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

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