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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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