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

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