Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsettings.cpp
Go to the documentation of this file.
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
67
68using namespace Qt::StringLiterals;
69using namespace QtMiscUtils;
70
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) {}
90 bool userDefined = false;
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
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(name, this);
109}
110
112{
113 if (usedHashFunc())
114 usedHashFunc()->remove(name);
115}
116
118{
120
121 for (auto i = removedKeys.begin(); i != removedKeys.end(); ++i)
122 result.remove(i.key());
123 for (auto i = addedKeys.begin(); i != addedKeys.end(); ++i)
124 result.insert(i.key(), i.value());
125 return result;
126}
127
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(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(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
154{
156
157 ConfFileHash *usedHash = usedHashFunc();
158 ConfFileCache *unusedCache = unusedCacheFunc();
159
160 QConfFile *confFile = nullptr;
161 const auto locker = qt_scoped_lock(settingsGlobalMutex);
162
163 if (!(confFile = usedHash->value(absPath))) {
164 if ((confFile = unusedCache->take(absPath)))
165 usedHash->insert(absPath, confFile);
166 }
167 if (confFile) {
168 confFile->ref.ref();
169 return confFile;
170 }
171 return new QConfFile(absPath, _userPerms);
172}
173
175{
176 const auto locker = qt_scoped_lock(settingsGlobalMutex);
177 unusedCacheFunc()->clear();
178}
179
180// ************************************************************************
181// QSettingsPrivate
182
184 : format(format), scope(QSettings::UserScope /* nothing better to put */), fallbacks(true),
185 pendingChanges(false), status(QSettings::NoError)
186{
187}
188
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
199
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?)
210 {
212 }
214 {
216 }
218 {
219 memcpy(out, v.data(), 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*/
233{
235 auto out = const_cast<QChar*>(result.constData()); // don't detach
236
237 const bool maybeEndsInSlash = key.visit([&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(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)
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)
286#endif
287
289{
290 if (spec != AllKeys) {
291 qsizetype slashPos = key.indexOf(u'/');
292 if (slashPos == -1) {
293 if (spec != ChildKeys)
294 return;
295 } else {
296 if (spec != ChildGroups)
297 return;
298 key.truncate(slashPos);
299 }
300 }
301 result.append(key.toString());
302}
303
305{
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
318{
319 if (status == QSettings::NoError || this->status == QSettings::NoError)
320 this->status = status;
321}
322
324{
325 flush();
326 pendingChanges = false;
327}
328
330{
331 if (!pendingChanges) {
332 pendingChanges = true;
333#ifndef QT_NO_QOBJECT
334 Q_Q(QSettings);
336#else
337 update();
338#endif
339 }
340}
341
343{
345 result.reserve(l.size());
346 for (auto v : l)
347 result.append(variantToString(v));
348 return result;
349}
350
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(u'@')) {
358 if (str.size() < 2 || str.at(1) != u'@') {
359 QVariantList variantList;
360 variantList.reserve(l.size());
361 for (const auto &s : l)
362 variantList.append(stringToVariant(s));
363 return variantList;
364 }
365 outStringList[i].remove(0, 1);
366 }
367 }
368 return outStringList;
369}
370
372{
374
375 switch (v.metaType().id()) {
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(QChar::Null))
399 result = "@String("_L1 + result + u')';
400 else if (result.startsWith(u'@'))
401 result.prepend(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("@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("@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("@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 }
434 {
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
455{
456 if (s.startsWith(u'@')) {
457 if (s.endsWith(u')')) {
458 if (s.startsWith("@ByteArray("_L1)) {
459 return QVariant(QStringView{s}.sliced(11).chopped(1).toLatin1());
460 } else if (s.startsWith("@String("_L1)) {
461 return QVariant(QStringView{s}.sliced(8).chopped(1).toString());
462 } else if (s.startsWith("@Variant("_L1)
463 || s.startsWith("@DateTime("_L1)) {
464#ifndef QT_NO_DATASTREAM
465 QDataStream::Version version;
466 int offset;
467 if (s.at(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(offset).toLatin1();
476 stream.setVersion(version);
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("@Rect("_L1)) {
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("@Size("_L1)) {
490 if (args.size() == 2)
491 return QVariant(QSize(args[0].toInt(), args[1].toInt()));
492 } else if (s.startsWith("@Point("_L1)) {
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("@@"_L1))
503 return QVariant(s.sliced(1));
504 }
505
506 return QVariant(s);
507}
508
510{
511 result.reserve(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(ch) || ch == '_' || ch == '-' || ch == '.') {
518 result += (char)ch;
519 } else if (ch <= 0xFF) {
520 result += '%';
523 } else {
524 result += "%U";
525 QByteArray hexCode;
526 for (int j = 0; j < 4; ++j) {
527 hexCode.prepend(QtMiscUtils::toHexUpper(ch % 16));
528 ch >>= 4;
529 }
530 result += hexCode;
531 }
532 }
533}
534
536{
537 const QString decoded = QString::fromUtf8(key);
538 const qsizetype size = decoded.size();
539 result.reserve(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 + 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(firstDigitPos, numDigits).toUShort(&ok, 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
593{
594 bool needsQuotes = false;
595 bool escapeNextIfDigit = false;
596 const bool useCodec = !(str.startsWith("@ByteArray("_L1)
597 || str.startsWith("@Variant("_L1)
598 || str.startsWith("@DateTime("_L1));
599 const qsizetype startPos = result.size();
600
602
603 result.reserve(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(ch)) {
610 result += "\\x" + QByteArray::number(ch, 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, 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(startPos) == ' '
662 || result.at(result.size() - 1) == ' '))) {
663 result.insert(startPos, '"');
664 result += '"';
665 }
666}
667
669{
670 qsizetype n = str.size() - 1;
671 QChar ch;
672 while (n >= limit && ((ch = str.at(n)) == u' ' || ch == u'\t'))
673 str.truncate(n--);
674}
675
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(strs.at(i), result);
693 }
694 }
695}
696
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;
722
723StSkipSpaces:
724 while (i < str.size() && ((ch = str.at(i)) == ' ' || ch == '\t'))
725 ++i;
726 // fallthrough
727
728StNormal:
729 qsizetype chopLimit = stringResult.size();
730 while (i < str.size()) {
731 switch (str.at(i)) {
732 case '\\':
733 ++i;
734 if (i >= str.size())
735 goto end;
736
737 ch = str.at(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(i);
752 if (isHexDigit(ch))
753 goto StHexEscape;
754 } else if (const int o = fromOct(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(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(stringResult, chopLimit);
780 if (!isStringList) {
781 isStringList = true;
782 stringListResult.clear();
783 stringResult.squeeze();
784 }
785 stringListResult.append(stringResult);
786 stringResult.clear();
787 currentValueIsQuoted = false;
788 ++i;
789 goto StSkipSpaces;
790 }
792 default: {
793 qsizetype j = i + 1;
794 while (j < str.size()) {
795 ch = str.at(j);
796 if (ch == '\\' || ch == '"' || ch == ',')
797 break;
798 ++j;
799 }
800
801 stringResult += fromUtf8(str.first(j).sliced(i));
802 i = j;
803 }
804 }
805 }
806 if (!currentValueIsQuoted)
807 iniChopTrailingSpaces(stringResult, chopLimit);
808 goto end;
809
810StHexEscape:
811 if (i >= str.size()) {
812 stringResult += escapeVal;
813 goto end;
814 }
815
816 ch = str.at(i);
817 if (const int h = fromHex(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(i);
834 if (const int o = fromOct(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(stringResult);
847 return isStringList;
848}
849
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
859
860 for (++idx; idx < l; ++idx) {
861 QChar c = s.at(idx);
862 if (c == u')') {
863 Q_ASSERT(idx == l - 1);
864 result.append(item);
865 } else if (c == u' ') {
866 result.append(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)
892#else
893 caseSensitivity = IniCaseSensitivity;
894#endif
895
896#if defined Q_OS_WASM
897 if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat) {
898#else
900#endif
901 const auto locker = qt_scoped_lock(settingsGlobalMutex);
902 const CustomFormatVector *customFormatVector = customFormatVectorFunc();
903
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
916{
917 if (!confFiles.isEmpty()) {
918#if defined Q_OS_WASM
919 if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat) {
920#else
922#endif
923 if (!readFunc)
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{
935
936 PWSTR path = nullptr;
937 if (SHGetKnownFolderPath(type, KF_FLAG_DONT_VERIFY, NULL, &path) == S_OK) {
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
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)
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
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.
983#else
984
985#ifdef Q_OS_ANDROID
986 // If an old settings path exists, use it instead of creating a new one
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.
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 */
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);
1025 Path(roamingAppDataFolder + QDir::separator(), false));
1027 Path(programDataFolder + QDir::separator(), false));
1028#else
1029 const QString userPath = make_user_path();
1030 pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), Path(userPath, false));
1031 pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), Path(systemPath, false));
1032#ifndef Q_OS_DARWIN
1033 pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), Path(userPath, false));
1034 pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), Path(systemPath, false));
1035#endif
1036#endif // Q_OS_WIN
1037 }
1038
1039 return locker;
1040}
1041
1043{
1045 Q_ASSERT(int(QSettings::IniFormat) == 1);
1046
1047 auto locker = qt_unique_lock(settingsGlobalMutex);
1048 PathHash *pathHash = pathHashFunc();
1049 if (pathHash->isEmpty())
1050 locker = initDefaultPaths(std::move(locker));
1051
1052 Path result = pathHash->value(pathHashKey(format, scope));
1053 if (!result.path.isEmpty())
1054 return result;
1055
1056 // fall back on INI path
1057 return pathHash->value(pathHashKey(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(settingsGlobalMutex);
1065 pathHashFunc()->clear();
1066}
1067#endif // QT_BUILD_INTERNAL && Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS
1068
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()) {
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) {
1089 if (!application.isEmpty())
1090 confFiles.append(QConfFile::fromName(userPath.path + appFile, true));
1091 confFiles.append(QConfFile::fromName(userPath.path + orgFile, true));
1092 }
1093
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.
1102 // remove the QStandardLocation::writableLocation() (XDG_CONFIG_HOME)
1103 if (!dirs.isEmpty())
1104 dirs.takeFirst();
1106 if (!application.isEmpty()) {
1107 paths.reserve(dirs.size() * 2);
1108 for (const auto &dir : std::as_const(dirs))
1109 paths.append(dir + u'/' + appFile);
1110 } else {
1111 paths.reserve(dirs.size());
1112 }
1113 for (const auto &dir : std::as_const(dirs))
1114 paths.append(dir + u'/' + orgFile);
1115
1116 // Note: No check for existence of files is done intentionally.
1117 for (const auto &path : std::as_const(paths))
1118 confFiles.append(QConfFile::fromName(path, false));
1119 } else
1120#endif // Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS
1121 {
1122 if (!application.isEmpty())
1123 confFiles.append(QConfFile::fromName(systemPath.path + appFile, false));
1124 confFiles.append(QConfFile::fromName(systemPath.path + orgFile, false));
1125 }
1126
1127 initAccess();
1128}
1129
1133 nextPosition(0x40000000) // big positive number
1134{
1135 initFormat();
1136
1137 confFiles.append(QConfFile::fromName(fileName, true));
1138
1139 initAccess();
1140}
1141
1143{
1144 const auto locker = qt_scoped_lock(settingsGlobalMutex);
1145 ConfFileHash *usedHash = usedHashFunc();
1146 ConfFileCache *unusedCache = unusedCacheFunc();
1147
1148 for (auto conf_file : std::as_const(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(conf_file->name);
1155 if (unusedCache) {
1156 QT_TRY {
1157 // compute a better size?
1158 unusedCache->insert(conf_file->name, conf_file,
1159 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
1174{
1175 if (confFiles.isEmpty())
1176 return;
1177
1178 // Note: First config file is always the most specific.
1179 QConfFile *confFile = confFiles.at(0);
1180
1181 QSettingsKey theKey(key, caseSensitivity);
1182 QSettingsKey prefix(key + u'/', caseSensitivity);
1183 const auto locker = qt_scoped_lock(confFile->mutex);
1184
1185 ensureSectionParsed(confFile, theKey);
1186 ensureSectionParsed(confFile, prefix);
1187
1188 auto i = confFile->addedKeys.lowerBound(prefix);
1189 while (i != confFile->addedKeys.end() && i.key().startsWith(prefix))
1190 i = confFile->addedKeys.erase(i);
1191 confFile->addedKeys.remove(theKey);
1192
1193 auto j = const_cast<const ParsedSettingsMap *>(&confFile->originalKeys)->lowerBound(prefix);
1194 while (j != confFile->originalKeys.constEnd() && j.key().startsWith(prefix)) {
1195 confFile->removedKeys.insert(j.key(), QVariant());
1196 ++j;
1197 }
1198 if (confFile->originalKeys.contains(theKey))
1199 confFile->removedKeys.insert(theKey, QVariant());
1200}
1201
1203{
1204 if (confFiles.isEmpty())
1205 return;
1206
1207 // Note: First config file is always the most specific.
1208 QConfFile *confFile = confFiles.at(0);
1209
1210 QSettingsKey theKey(key, caseSensitivity, nextPosition++);
1211 const auto locker = qt_scoped_lock(confFile->mutex);
1212 confFile->removedKeys.remove(theKey);
1213 confFile->addedKeys.insert(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(confFiles)) {
1223 const auto locker = qt_scoped_lock(confFile->mutex);
1224
1225 if (!confFile->addedKeys.isEmpty()) {
1226 j = confFile->addedKeys.constFind(theKey);
1227 found = (j != confFile->addedKeys.constEnd());
1228 }
1229 if (!found) {
1230 ensureSectionParsed(confFile, theKey);
1231 j = confFile->originalKeys.constFind(theKey);
1232 found = (j != confFile->originalKeys.constEnd()
1233 && !confFile->removedKeys.contains(theKey));
1234 }
1235
1236 if (found)
1237 return *j;
1238 if (!fallbacks)
1239 break;
1240 }
1241 return std::nullopt;
1242}
1243
1245{
1247
1248 QSettingsKey thePrefix(prefix, caseSensitivity);
1249 qsizetype startPos = prefix.size();
1250
1251 for (auto confFile : std::as_const(confFiles)) {
1252 const auto locker = qt_scoped_lock(confFile->mutex);
1253
1254 if (thePrefix.isEmpty())
1255 ensureAllSectionsParsed(confFile);
1256 else
1257 ensureSectionParsed(confFile, thePrefix);
1258
1259 const auto &originalKeys = confFile->originalKeys;
1260 auto i = originalKeys.lowerBound(thePrefix);
1261 while (i != originalKeys.end() && i.key().startsWith(thePrefix)) {
1262 if (!confFile->removedKeys.contains(i.key()))
1263 processChild(QStringView{i.key().originalCaseKey()}.sliced(startPos), spec, result);
1264 ++i;
1265 }
1266
1267 const auto &addedKeys = confFile->addedKeys;
1268 auto j = addedKeys.lowerBound(thePrefix);
1269 while (j != addedKeys.end() && j.key().startsWith(thePrefix)) {
1270 processChild(QStringView{j.key().originalCaseKey()}.sliced(startPos), spec, result);
1271 ++j;
1272 }
1273
1274 if (!fallbacks)
1275 break;
1276 }
1277 std::sort(result.begin(), result.end());
1278 result.erase(std::unique(result.begin(), result.end()),
1279 result.end());
1280 return result;
1281}
1282
1284{
1285 if (confFiles.isEmpty())
1286 return;
1287
1288 // Note: First config file is always the most specific.
1289 QConfFile *confFile = confFiles.at(0);
1290
1291 const auto locker = qt_scoped_lock(confFile->mutex);
1292 ensureAllSectionsParsed(confFile);
1293 confFile->addedKeys.clear();
1294 confFile->removedKeys = confFile->originalKeys;
1295}
1296
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(confFiles)) {
1303 const auto locker = qt_scoped_lock(confFile->mutex);
1304 syncConfFile(confFile);
1305 }
1306}
1307
1309{
1310 sync();
1311}
1312
1314{
1315 if (confFiles.isEmpty())
1316 return QString();
1317
1318 // Note: First config file is always the most specific.
1319 return confFiles.at(0)->name;
1320}
1321
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(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(QTimeZone::UTC))
1348 return;
1349 }
1350
1351 if (!readOnly && !confFile->isWritable()) {
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))
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) {
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(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(QFile::ReadOnly)) {
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) {
1411 ok = readIniFile(data, &confFile->unparsedIniSections);
1412 } else
1413#endif
1414#ifdef Q_OS_DARWIN
1417 ok = readPlistFile(data, &confFile->originalKeys);
1418 } else
1419#endif
1422 ok = readIniFile(data, &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(QSettingsKey(i.key(), caseSensitivity),
1431 i.value());
1432 ++i;
1433 }
1434 }
1435 }
1436
1437 if (!ok)
1439 }
1440
1441 confFile->size = fileInfo.size();
1442 confFile->timeStamp = fileInfo.lastModified(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(QIODevice::WriteOnly)) {
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
1477 ok = writePlistFile(sf, mergedKeys);
1478 } else
1479#endif
1481 ok = writeIniFile(sf, mergedKeys);
1482 } else if (writeFunc) {
1483 QSettings::SettingsMap tempOriginalKeys;
1484
1485 auto i = mergedKeys.constBegin();
1486 while (i != mergedKeys.constEnd()) {
1487 tempOriginalKeys.insert(i.key(), 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(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)
1513 QFile(confFile->name).setPermissions(perms);
1514 }
1515 } else {
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
1552
1554 qsizetype &lineStart, qsizetype &lineLen,
1555 qsizetype &equalsPos)
1556{
1557 using namespace SettingsImpl;
1558 qsizetype dataLen = data.size();
1559 bool inQuotes = false;
1560
1561 equalsPos = -1;
1562
1563 lineStart = dataPos;
1564 while (lineStart < dataLen && (charTraits[uint(uchar(data.at(lineStart)))] & Space))
1565 ++lineStart;
1566
1567 qsizetype i = lineStart;
1568 while (i < dataLen) {
1569 char ch = data.at(i);
1570 while (!(charTraits[uchar(ch)] & Special)) {
1571 if (++i == dataLen)
1572 goto break_out_of_outer_loop;
1573 ch = data.at(i);
1574 }
1575
1576 ++i;
1577 if (ch == '=') {
1578 if (!inQuotes && equalsPos == -1)
1579 equalsPos = i - 1;
1580 } else if (ch == '\n' || ch == '\r') {
1581 if (i == lineStart + 1) {
1582 ++lineStart;
1583 } else if (!inQuotes) {
1584 --i;
1585 goto break_out_of_outer_loop;
1586 }
1587 } else if (ch == '\\') {
1588 if (i < dataLen) {
1589 char ch = data.at(i++);
1590 if (i < dataLen) {
1591 char ch2 = data.at(i);
1592 // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files
1593 if ((ch == '\n' && ch2 == '\r') || (ch == '\r' && ch2 == '\n'))
1594 ++i;
1595 }
1596 }
1597 } else if (ch == '"') {
1598 inQuotes = !inQuotes;
1599 } else {
1600 Q_ASSERT(ch == ';');
1601
1602 if (i == lineStart + 1) {
1603 while (i < dataLen && (((ch = data.at(i)) != '\n') && ch != '\r'))
1604 ++i;
1605 while (i < dataLen && charTraits[uchar(data.at(i))] & Space)
1606 ++i;
1607 lineStart = i;
1608 } else if (!inQuotes) {
1609 --i;
1610 goto break_out_of_outer_loop;
1611 }
1612 }
1613 }
1614
1615break_out_of_outer_loop:
1616 dataPos = i;
1617 lineLen = i - lineStart;
1618 return lineLen > 0;
1619}
1620
1621/*
1622 Returns \c false on parse error. However, as many keys are read as
1623 possible, so if the user doesn't check the status he will get the
1624 most out of the file anyway.
1625*/
1627 UnparsedSettingsMap *unparsedIniSections)
1628{
1629#define FLUSH_CURRENT_SECTION() \
1630 { \
1631 QByteArray &sectionData = (*unparsedIniSections)[QSettingsKey(currentSection, \
1632 IniCaseSensitivity, \
1633 sectionPosition)]; \
1634 if (!sectionData.isEmpty()) \
1635 sectionData.append('\n'); \
1636 sectionData += data.first(lineStart).sliced(currentSectionStart); \
1637 sectionPosition = ++position; \
1638 }
1639
1640 QString currentSection;
1641 qsizetype currentSectionStart = 0;
1642 qsizetype dataPos = 0;
1643 qsizetype lineStart;
1644 qsizetype lineLen;
1645 qsizetype equalsPos;
1646 qsizetype position = 0;
1647 qsizetype sectionPosition = 0;
1648 bool ok = true;
1649
1650 // Skip possible UTF-8 BOM:
1651 if (data.startsWith("\xef\xbb\xbf"))
1652 data = data.sliced(3);
1653
1654 while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) {
1655 QByteArrayView line = data.sliced(lineStart, lineLen);
1656 if (line.startsWith('[')) {
1658
1659 // This starts a new section.
1660 qsizetype idx = line.indexOf(']');
1661 Q_ASSERT(idx == -1 || idx > 0); // line[0] is '[', not ']'.
1662 Q_ASSERT(idx < lineLen); // (including -1 < lineLen, if no ']' present.)
1663 if (idx < 0) {
1664 ok = false;
1665 idx = lineLen; // so line.first(idx) is just line
1666 }
1667 QByteArrayView iniSection = line.first(idx).sliced(1).trimmed();
1668
1669 if (iniSection.compare("general", Qt::CaseInsensitive) == 0) {
1670 currentSection.clear();
1671 } else {
1672 if (iniSection.compare("%general", Qt::CaseInsensitive) == 0) {
1673 currentSection = QLatin1StringView(iniSection.constData() + 1, iniSection.size() - 1);
1674 } else {
1675 currentSection.clear();
1676 iniUnescapedKey(iniSection, currentSection);
1677 }
1678 currentSection += u'/';
1679 }
1680 currentSectionStart = dataPos;
1681 }
1682 ++position;
1683 }
1684
1685 Q_ASSERT(lineStart == data.size());
1687
1688 return ok;
1689
1690#undef FLUSH_CURRENT_SECTION
1691}
1692
1694 ParsedSettingsMap *settingsMap)
1695{
1696 QStringList strListValue;
1697 bool sectionIsLowercase = (section == section.originalCaseKey());
1698 qsizetype equalsPos;
1699
1700 bool ok = true;
1701 qsizetype dataPos = 0;
1702 qsizetype lineStart;
1703 qsizetype lineLen;
1705
1706 while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) {
1707 QByteArrayView line = data.sliced(lineStart, lineLen);
1708 Q_ASSERT(!line.startsWith('['));
1709
1710 if (equalsPos == -1) {
1711 if (!line.startsWith(';'))
1712 ok = false;
1713 continue;
1714 }
1715 // Shift equalPos indexing to be within line, rather than data:
1716 equalsPos -= lineStart;
1717 // Assured by readIniLine:
1718 Q_ASSERT(equalsPos >= 0 && equalsPos < lineLen);
1719
1720 QByteArrayView key = line.first(equalsPos).trimmed();
1721 QByteArrayView value = line.sliced(equalsPos + 1);
1722
1723 QString strKey = section.originalCaseKey();
1724 const Qt::CaseSensitivity casing = iniUnescapedKey(key, strKey) && sectionIsLowercase
1727
1728 QString strValue;
1729 strValue.reserve(value.size());
1730 QVariant variant = iniUnescapedStringList(value, strValue, strListValue)
1731 ? stringListToVariantList(strListValue)
1732 : stringToVariant(strValue);
1733
1734 /*
1735 We try to avoid the expensive toLower() call in
1736 QSettingsKey by passing Qt::CaseSensitive when the
1737 key is already in lowercase.
1738 */
1739 settingsMap->insert(QSettingsKey(strKey, casing, position), std::move(variant));
1740 ++position;
1741 }
1742
1743 return ok;
1744}
1745
1747{
1748public:
1749 inline QSettingsIniKey() : position(-1) {}
1751
1753};
1755
1756static bool operator<(const QSettingsIniKey &k1, const QSettingsIniKey &k2)
1757{
1758 if (k1.position != k2.position)
1759 return k1.position < k2.position;
1760 return static_cast<const QString &>(k1) < static_cast<const QString &>(k2);
1761}
1762
1763typedef QMap<QSettingsIniKey, QVariant> IniKeyMap;
1764
1772
1774
1775typedef QMap<QString, QSettingsIniSection> IniMap;
1776
1777/*
1778 This would be more straightforward if we didn't try to remember the original
1779 key order in the .ini file, but we do.
1780*/
1781bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSettingsMap &map)
1782{
1783 IniMap iniMap;
1784
1785#ifdef Q_OS_WIN
1786 const char * const eol = "\r\n";
1787#else
1788 const char eol = '\n';
1789#endif
1790
1791 for (auto j = map.constBegin(); j != map.constEnd(); ++j) {
1792 QString section;
1793 QSettingsIniKey key(j.key().originalCaseKey(), j.key().originalKeyPosition());
1794 qsizetype slashPos;
1795
1796 if ((slashPos = key.indexOf(u'/')) != -1) {
1797 section = key.left(slashPos);
1798 key.remove(0, slashPos + 1);
1799 }
1800
1801 QSettingsIniSection &iniSection = iniMap[section];
1802
1803 // -1 means infinity
1804 if (size_t(key.position) < size_t(iniSection.position))
1805 iniSection.position = key.position;
1806 iniSection.keyMap[key] = j.value();
1807 }
1808
1809 const qsizetype sectionCount = iniMap.size();
1810 QList<QSettingsIniKey> sections;
1811 sections.reserve(sectionCount);
1812 for (auto i = iniMap.constBegin(); i != iniMap.constEnd(); ++i)
1813 sections.append(QSettingsIniKey(i.key(), i.value().position));
1814 std::sort(sections.begin(), sections.end());
1815
1816 bool writeError = false;
1817 for (qsizetype j = 0; !writeError && j < sectionCount; ++j) {
1818 auto i = iniMap.constFind(sections.at(j));
1819 Q_ASSERT(i != iniMap.constEnd());
1820
1821 QByteArray realSection;
1822
1823 iniEscapedKey(i.key(), realSection);
1824
1825 if (realSection.isEmpty()) {
1826 realSection = "[General]";
1827 } else if (realSection.compare("general", Qt::CaseInsensitive) == 0) {
1828 realSection = "[%General]";
1829 } else {
1830 realSection.prepend('[');
1831 realSection.append(']');
1832 }
1833
1834 if (j != 0)
1835 realSection.prepend(eol);
1836 realSection += eol;
1837
1838 device.write(realSection);
1839
1840 const IniKeyMap &ents = i.value().keyMap;
1841 for (auto j = ents.constBegin(); j != ents.constEnd(); ++j) {
1842 QByteArray block;
1843 iniEscapedKey(j.key(), block);
1844 block += '=';
1845
1846 const QVariant &value = j.value();
1847
1848 /*
1849 The size() != 1 trick is necessary because
1850 QVariant(QString("foo")).toList() returns an empty
1851 list, not a list containing "foo".
1852 */
1853 if (value.metaType().id() == QMetaType::QStringList
1854 || (value.metaType().id() == QMetaType::QVariantList && value.toList().size() != 1)) {
1856 } else {
1858 }
1859 block += eol;
1860 if (device.write(block) == -1) {
1861 writeError = true;
1862 break;
1863 }
1864 }
1865 }
1866 return !writeError;
1867}
1868
1869void QConfFileSettingsPrivate::ensureAllSectionsParsed(QConfFile *confFile) const
1870{
1871 auto i = confFile->unparsedIniSections.constBegin();
1872 const auto end = confFile->unparsedIniSections.constEnd();
1873
1874 for (; i != end; ++i) {
1875 if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys))
1877 }
1878 confFile->unparsedIniSections.clear();
1879}
1880
1881void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
1882 const QSettingsKey &key) const
1883{
1884 if (confFile->unparsedIniSections.isEmpty())
1885 return;
1886
1887 UnparsedSettingsMap::iterator i;
1888
1889 qsizetype indexOfSlash = key.indexOf(u'/');
1890 if (indexOfSlash != -1) {
1891 i = confFile->unparsedIniSections.upperBound(key);
1892 if (i == confFile->unparsedIniSections.begin())
1893 return;
1894 --i;
1895 if (i.key().isEmpty() || !key.startsWith(i.key()))
1896 return;
1897 } else {
1898 i = confFile->unparsedIniSections.begin();
1899 if (i == confFile->unparsedIniSections.end() || !i.key().isEmpty())
1900 return;
1901 }
1902
1903 if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys))
1905 confFile->unparsedIniSections.erase(i);
1906}
1907
2512#ifndef QT_NO_QOBJECT
2527QSettings::QSettings(const QString &organization, const QString &application, QObject *parent)
2528 : QObject(*QSettingsPrivate::create(NativeFormat, UserScope, organization, application),
2529 parent)
2530{
2531}
2532
2552QSettings::QSettings(Scope scope, const QString &organization, const QString &application,
2553 QObject *parent)
2554 : QObject(*QSettingsPrivate::create(NativeFormat, scope, organization, application), parent)
2555{
2556}
2557
2576QSettings::QSettings(Format format, Scope scope, const QString &organization,
2577 const QString &application, QObject *parent)
2578 : QObject(*QSettingsPrivate::create(format, scope, organization, application), parent)
2579{
2580}
2581
2616
2653 : QSettings(UserScope, parent)
2654{
2655}
2656
2667#ifdef Q_OS_DARWIN
2668 QCoreApplication::organizationDomain().isEmpty()
2669 ? QCoreApplication::organizationName()
2670 : QCoreApplication::organizationDomain()
2671#else
2672 QCoreApplication::organizationName().isEmpty()
2673 ? QCoreApplication::organizationDomain()
2674 : QCoreApplication::organizationName()
2675#endif
2676 , QCoreApplication::applicationName()),
2677 parent)
2678{
2679}
2680
2681#else
2682QSettings::QSettings(const QString &organization, const QString &application)
2683 : d_ptr(QSettingsPrivate::create(globalDefaultFormat, QSettings::UserScope, organization, application))
2684{
2685 d_ptr->q_ptr = this;
2686}
2687
2688QSettings::QSettings(Scope scope, const QString &organization, const QString &application)
2689 : d_ptr(QSettingsPrivate::create(globalDefaultFormat, scope, organization, application))
2690{
2691 d_ptr->q_ptr = this;
2692}
2693
2694QSettings::QSettings(Format format, Scope scope, const QString &organization,
2695 const QString &application)
2696 : d_ptr(QSettingsPrivate::create(format, scope, organization, application))
2697{
2698 d_ptr->q_ptr = this;
2699}
2700
2703{
2704 d_ptr->q_ptr = this;
2705}
2706
2707QSettings::QSettings(Scope scope)
2709# ifdef Q_OS_DARWIN
2710 QCoreApplication::organizationDomain().isEmpty()
2711 ? QCoreApplication::organizationName()
2712 : QCoreApplication::organizationDomain()
2713# else
2714 QCoreApplication::organizationName().isEmpty()
2715 ? QCoreApplication::organizationDomain()
2716 : QCoreApplication::organizationName()
2717# endif
2718 , QCoreApplication::applicationName())
2719 )
2720{
2721 d_ptr->q_ptr = this;
2722}
2723#endif
2724
2734{
2735 Q_D(QSettings);
2736 if (d->pendingChanges) {
2737 // Don't cause a failing flush() to std::terminate() the whole
2738 // application - dtors are implicitly noexcept!
2739 QT_TRY {
2740 d->flush();
2741 } QT_CATCH(...) {
2742 }
2743 }
2744}
2745
2758{
2759 Q_D(QSettings);
2760 d->clear();
2761 d->requestUpdate();
2762}
2763
2776{
2777 Q_D(QSettings);
2778 d->sync();
2779 d->pendingChanges = false;
2780}
2781
2792{
2793 Q_D(const QSettings);
2794 return d->fileName();
2795}
2796
2805{
2806 Q_D(const QSettings);
2807 return d->format;
2808}
2809
2818{
2819 Q_D(const QSettings);
2820 return d->scope;
2821}
2822
2831{
2832 Q_D(const QSettings);
2833 return d->organizationName;
2834}
2835
2844{
2845 Q_D(const QSettings);
2846 return d->applicationName;
2847}
2848
2860{
2861 Q_D(const QSettings);
2862 return d->status;
2863}
2864
2877{
2878 Q_D(const QSettings);
2879 return d->atomicSyncOnly;
2880}
2881
2904{
2905 Q_D(QSettings);
2906 d->atomicSyncOnly = enable;
2907}
2908
2939{
2940 Q_D(QSettings);
2941 d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix)));
2942}
2943
2955{
2956 Q_D(QSettings);
2957 if (d->groupStack.isEmpty()) {
2958 qWarning("QSettings::endGroup: No matching beginGroup()");
2959 return;
2960 }
2961
2962 QSettingsGroup group = d->groupStack.pop();
2964 if (len > 0)
2965 d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1));
2966
2967 if (group.isArray())
2968 qWarning("QSettings::endGroup: Expected endArray() instead");
2969}
2970
2977{
2978 Q_D(const QSettings);
2979 return d->groupPrefix.left(d->groupPrefix.size() - 1);
2980}
2981
2998{
2999 Q_D(QSettings);
3000 d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), false));
3001 return value("size"_L1).toInt();
3002}
3003
3037{
3038 Q_D(QSettings);
3039 d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), size < 0));
3040
3041 if (size < 0)
3042 remove("size"_L1);
3043 else
3044 setValue("size"_L1, size);
3045}
3046
3054{
3055 Q_D(QSettings);
3056 if (d->groupStack.isEmpty()) {
3057 qWarning("QSettings::endArray: No matching beginArray()");
3058 return;
3059 }
3060
3061 QSettingsGroup group = d->groupStack.top();
3063 d->groupStack.pop();
3064 if (len > 0)
3065 d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1));
3066
3067 if (group.arraySizeGuess() != -1)
3068 setValue(group.name() + "/size"_L1, group.arraySizeGuess());
3069
3070 if (!group.isArray())
3071 qWarning("QSettings::endArray: Expected endGroup() instead");
3072}
3073
3083{
3084 Q_D(QSettings);
3085 if (d->groupStack.isEmpty() || !d->groupStack.top().isArray()) {
3086 qWarning("QSettings::setArrayIndex: Missing beginArray()");
3087 return;
3088 }
3089
3090 QSettingsGroup &top = d->groupStack.top();
3092 top.setArrayIndex(qMax(i, 0));
3093 d->groupPrefix.replace(d->groupPrefix.size() - len - 1, len, top.toString());
3094}
3095
3112{
3113 Q_D(const QSettings);
3114 return d->children(d->groupPrefix, QSettingsPrivate::AllKeys);
3115}
3116
3136{
3137 Q_D(const QSettings);
3138 return d->children(d->groupPrefix, QSettingsPrivate::ChildKeys);
3139}
3140
3160{
3161 Q_D(const QSettings);
3162 return d->children(d->groupPrefix, QSettingsPrivate::ChildGroups);
3163}
3164
3178{
3179 Q_D(const QSettings);
3180 return d->isWritable();
3181}
3182
3203{
3204 Q_D(QSettings);
3205 if (key.isEmpty()) {
3206 qWarning("QSettings::setValue: Empty key passed");
3207 return;
3208 }
3209 d->set(d->actualKey(key), value);
3210 d->requestUpdate();
3211}
3212
3240{
3241 Q_D(QSettings);
3242 /*
3243 We cannot use actualKey(), because remove() supports empty
3244 keys. The code is also tricky because of slash handling.
3245 */
3246 QString theKey = d->normalizedKey(key);
3247 if (theKey.isEmpty())
3248 theKey = group();
3249 else
3250 theKey.prepend(d->groupPrefix);
3251
3252 if (theKey.isEmpty()) {
3253 d->clear();
3254 } else {
3255 d->remove(theKey);
3256 }
3257 d->requestUpdate();
3258}
3259
3278{
3279 Q_D(const QSettings);
3280 return d->get(d->actualKey(key)) != std::nullopt;
3281}
3282
3291{
3292 Q_D(QSettings);
3293 d->fallbacks = !!b;
3294}
3295
3304{
3305 Q_D(const QSettings);
3306 return d->fallbacks;
3307}
3308
3309#ifndef QT_NO_QOBJECT
3314{
3315 Q_D(QSettings);
3316 if (event->type() == QEvent::UpdateRequest) {
3317 d->update();
3318 return true;
3319 }
3320 return QObject::event(event);
3321}
3322#endif
3323
3349{
3350 Q_D(const QSettings);
3351 return d->value(key, nullptr);
3352}
3353
3355{
3356 Q_D(const QSettings);
3357 return d->value(key, &defaultValue);
3358}
3359
3361{
3362 if (key.isEmpty()) {
3363 qWarning("QSettings::value: Empty key passed");
3364 return QVariant();
3365 }
3366 if (std::optional r = get(actualKey(key)))
3367 return std::move(*r);
3368 if (defaultValue)
3369 return *defaultValue;
3370 return QVariant();
3371}
3372
3389
3402
3436{
3437 auto locker = qt_unique_lock(settingsGlobalMutex);
3438 PathHash *pathHash = pathHashFunc();
3439 if (pathHash->isEmpty())
3440 locker = initDefaultPaths(std::move(locker));
3441 pathHash->insert(pathHashKey(format, scope), Path(path + QDir::separator(), true));
3442}
3443
3513 WriteFunc writeFunc,
3514 Qt::CaseSensitivity caseSensitivity)
3515{
3516#ifdef QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER
3517 Q_ASSERT(caseSensitivity == Qt::CaseSensitive);
3518#endif
3519
3520 const auto locker = qt_scoped_lock(settingsGlobalMutex);
3521 CustomFormatVector *customFormatVector = customFormatVectorFunc();
3522 qsizetype index = customFormatVector->size();
3523 if (index == 16) // the QSettings::Format enum has room for 16 custom formats
3525
3527 info.extension = u'.' + extension;
3528 info.readFunc = readFunc;
3529 info.writeFunc = writeFunc;
3530 info.caseSensitivity = caseSensitivity;
3531 customFormatVector->append(info);
3532
3534}
3535
3537
3538#ifndef QT_BOOTSTRAPPED
3539#include "moc_qsettings.cpp"
3540#endif
IOBluetoothDevice * device
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
QByteArray & prepend(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:280
bool startsWith(QByteArrayView bv) const
Definition qbytearray.h:223
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
\inmodule QtCore
static bool readIniSection(const QSettingsKey &section, QByteArrayView data, ParsedSettingsMap *settingsMap)
void set(const QString &key, const QVariant &value) override
virtual void initAccess()
bool readIniFile(QByteArrayView data, UnparsedSettingsMap *unparsedIniSections)
bool isWritable() const override
QString fileName() const override
QConfFileSettingsPrivate(QSettings::Format format, QSettings::Scope scope, const QString &organization, const QString &application)
void remove(const QString &key) override
static bool readIniLine(QByteArrayView data, qsizetype &dataPos, qsizetype &lineStart, qsizetype &lineLen, qsizetype &equalsPos)
std::optional< QVariant > get(const QString &key) const override
QString name
ParsedSettingsMap originalKeys
static Q_AUTOTEST_EXPORT void clearCache()
ParsedSettingsMap removedKeys
ParsedSettingsMap mergedKeyMap() const
static QConfFile * fromName(const QString &name, bool _userPerms)
bool isWritable() const
ParsedSettingsMap addedKeys
\inmodule QtCore
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\inmodule QtCore
Definition qdir.h:20
static QChar separator()
Returns the native directory separator: "/" under Unix and "\\" under Windows.
Definition qdir.h:209
static QString homePath()
Returns the absolute path of the user's home directory.
Definition qdir.cpp:2103
\inmodule QtCore
Definition qcoreevent.h:45
@ UpdateRequest
Definition qcoreevent.h:113
QString fileName() const
QString absoluteFilePath() const
QString absolutePath() const
Returns the absolute path of the file system entry this QFileInfo refers to, excluding the entry's na...
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
bool setPermissions(Permissions permissionSpec) override
Sets the permissions for the file to the permissions specified.
Definition qfile.cpp:1159
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
qint64 size() const override
\reimp
Definition qfile.cpp:1179
\inmodule QtCore \reentrant
Definition qiodevice.h:34
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
bool isReadable() const
Returns true if data can be read from the device; otherwise returns false.
QString toString() const
Definition qstring.h:1108
static QString path(LibraryPath p)
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void reserve(qsizetype size)
Definition qlist.h:753
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qlockfile.h:17
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:357
size_type remove(const Key &key)
Definition qmap.h:300
iterator begin()
Definition qmap.h:598
iterator end()
Definition qmap.h:602
const_iterator constBegin() const
Definition qmap.h:600
const_iterator constEnd() const
Definition qmap.h:604
\inmodule QtCore
Definition qmutex.h:281
QObject * q_ptr
Definition qobject.h:72
QObjectList children
Definition qobject.h:74
\inmodule QtCore
Definition qobject.h:103
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qobject.cpp:1389
QScopedPointer< QObjectData > d_ptr
Definition qobject.h:373
\inmodule QtCore\reentrant
Definition qpoint.h:25
Represents an immutable JsonPath like path in the Qml code model (from a DomItem to another DomItem)
\inmodule QtCore\reentrant
Definition qrect.h:30
iterator begin()
Definition qset.h:136
QString toString() const
qsizetype position
QSettingsIniKey(const QString &str, qsizetype pos=-1)
QString originalCaseKey() const
Definition qsettings_p.h:50
qsizetype originalKeyPosition() const
Definition qsettings_p.h:51
static void iniEscapedKey(const QString &key, QByteArray &result)
virtual std::optional< QVariant > get(const QString &key) const =0
virtual QString fileName() const =0
QVariant value(QAnyStringView key, const QVariant *defaultValue) const
static bool iniUnescapedStringList(QByteArrayView str, QString &stringResult, QStringList &stringListResult)
QSettingsPrivate(QSettings::Format format)
QSettings::Status status
static QStringList variantListToStringList(const QVariantList &l)
static QSettingsPrivate * create(QSettings::Format format, QSettings::Scope scope, const QString &organization, const QString &application)
static void iniEscapedString(const QString &str, QByteArray &result)
void beginGroupOrArray(const QSettingsGroup &group)
void setStatus(QSettings::Status status) const
static QVariant stringListToVariantList(const QStringList &l)
static QVariant stringToVariant(const QString &s)
virtual ~QSettingsPrivate()
static void iniEscapedStringList(const QStringList &strs, QByteArray &result)
QString actualKey(QAnyStringView key) const
static QString variantToString(const QVariant &v)
static bool iniUnescapedKey(QByteArrayView key, QString &result)
static QString normalizedKey(QAnyStringView key)
virtual void flush()=0
static QStringList splitArgs(const QString &s, qsizetype idx)
QStack< QSettingsGroup > groupStack
QSettings::Scope scope
static void processChild(QStringView key, ChildSpec spec, QStringList &result)
\inmodule QtCore
Definition qsettings.h:30
static void setDefaultFormat(Format format)
void endGroup()
Resets the group to what it was before the corresponding beginGroup() call.
void beginWriteArray(QAnyStringView prefix, int size=-1)
Adds prefix to the current group and starts writing an array of size size.
void endArray()
Closes the array that was started using beginReadArray() or beginWriteArray().
QSettings(const QString &organization, const QString &application=QString(), QObject *parent=nullptr)
Constructs a QSettings object for accessing settings of the application called application from the o...
static Format defaultFormat()
Format
This enum type specifies the storage format used by QSettings.
Definition qsettings.h:48
@ CustomFormat1
Definition qsettings.h:63
@ NativeFormat
Definition qsettings.h:49
@ InvalidFormat
Definition qsettings.h:62
Scope
This enum specifies whether settings are user-specific or shared by all users of the same system.
Definition qsettings.h:84
@ SystemScope
Definition qsettings.h:86
void clear()
Removes all entries in the primary location associated to this QSettings object.
Format format() const
QString fileName() const
Returns the path where settings written using this QSettings object are stored.
bool contains(QAnyStringView key) const
Returns true if there exists a setting called key; returns false otherwise.
~QSettings()
Destroys the QSettings object.
void setAtomicSyncRequired(bool enable)
bool event(QEvent *event) override
\reimp
QString organizationName() const
void setValue(QAnyStringView key, const QVariant &value)
Sets the value of setting key to value.
void remove(QAnyStringView key)
Removes the setting key and any sub-settings of key.
QVariant value(QAnyStringView key, const QVariant &defaultValue) const
Returns the value for setting key.
int beginReadArray(QAnyStringView prefix)
Adds prefix to the current group and starts reading from an array.
QStringList childKeys() const
Returns a list of all top-level keys that can be read using the QSettings object.
bool(* WriteFunc)(QIODevice &device, const SettingsMap &map)
Typedef for a pointer to a function with the following signature:
Definition qsettings.h:172
static Format registerFormat(const QString &extension, ReadFunc readFunc, WriteFunc writeFunc, Qt::CaseSensitivity caseSensitivity=Qt::CaseSensitive)
QString applicationName() const
QStringList allKeys() const
Returns a list of all keys, including subkeys, that can be read using the QSettings object.
void sync()
Writes any unsaved changes to permanent storage, and reloads any settings that have been changed in t...
Status
The following status values are possible:
Definition qsettings.h:39
@ FormatError
Definition qsettings.h:42
@ AccessError
Definition qsettings.h:41
Scope scope() const
bool isWritable() const
Returns true if settings can be written using this QSettings object; returns false otherwise.
bool(* ReadFunc)(QIODevice &device, SettingsMap &map)
Typedef for a pointer to a function with the following signature:
Definition qsettings.h:171
static void setPath(Format format, Scope scope, const QString &path)
bool isAtomicSyncRequired() const
QStringList childGroups() const
Returns a list of all key top-level groups that contain keys that can be read using the QSettings obj...
void beginGroup(QAnyStringView prefix)
Appends prefix to the current group.
void setArrayIndex(int i)
Sets the current array index to i.
QString group() const
Returns the current group.
Status status() const
Returns a status code indicating the first error that was met by QSettings, or QSettings::NoError if ...
bool fallbacksEnabled() const
Returns true if fallbacks are enabled; returns false otherwise.
void setFallbacksEnabled(bool b)
Sets whether fallbacks are enabled to b.
\inmodule QtCore
Definition qsize.h:25
void push(const T &t)
Adds element t to the top of the stack.
Definition qstack.h:17
static QStringList standardLocations(StandardLocation type)
static QString writableLocation(StandardLocation type)
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView sliced(qsizetype pos) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1325
QString sliced(qsizetype pos) const &
Definition qstring.h:394
void truncate(qsizetype pos)
Truncates the string at the given position index.
Definition qstring.cpp:6319
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString first(qsizetype n) const &
Definition qstring.h:390
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
QString trimmed() const &
Definition qstring.h:447
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3466
QString & prepend(QChar c)
Definition qstring.h:478
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
Format
Definition ddsheader.h:14
void extension()
[6]
Definition dialogs.cpp:230
QString str
[2]
QMap< QString, QString > map
[6]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Lock qt_scoped_lock(Mutex &mutex)
Definition qlocking_p.h:58
constexpr int fromOct(char32_t c) noexcept
Definition qtools_p.h:62
constexpr bool isAsciiLetterOrNumber(char32_t c) noexcept
Definition qtools_p.h:82
constexpr char toHexUpper(char32_t value) noexcept
Definition qtools_p.h:27
constexpr bool isHexDigit(char32_t c) noexcept
Definition qtools_p.h:37
constexpr int fromHex(char32_t c) noexcept
Definition qtools_p.h:44
CaseSensitivity
@ CaseInsensitive
@ CaseSensitive
constexpr Initialization Uninitialized
static const char charTraits[256]
#define Q_FALLTHROUGH()
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define QT_CATCH(A)
#define QT_TRY
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
@ Space
#define qWarning
Definition qlogging.h:166
return ret
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLdouble GLdouble GLdouble GLdouble top
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLsizei const GLuint * paths
GLboolean GLuint group
GLboolean enable
GLenum GLuint GLintptr offset
GLint ref
GLuint name
GLfloat n
GLint GLsizei GLsizei GLenum format
GLfloat GLfloat GLfloat GLfloat h
struct _cl_event * event
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLint limit
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLsizei len
static QString lockFileName(const QString &name)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
static Q_CONSTINIT QSettings::Format globalDefaultFormat
QHash< int, Path > PathHash
Definition qsettings.cpp:93
QList< QConfFileCustomFormat > CustomFormatVector
Definition qsettings.cpp:94
static bool operator<(const QSettingsIniKey &k1, const QSettingsIniKey &k2)
static Path getPath(QSettings::Format format, QSettings::Scope scope)
static int pathHashKey(QSettings::Format format, QSettings::Scope scope)
static QString make_user_path()
static std::unique_lock< QBasicMutex > initDefaultPaths(std::unique_lock< QBasicMutex > locker)
static QString make_user_path_without_qstandard_paths()
static Q_CONSTINIT QBasicMutex settingsGlobalMutex
QMap< QSettingsIniKey, QVariant > IniKeyMap
static constexpr QChar sep
QHash< QString, QConfFile * > ConfFileHash
Definition qsettings.cpp:80
#define FLUSH_CURRENT_SECTION()
QMap< QString, QSettingsIniSection > IniMap
static void iniChopTrailingSpaces(QString &str, qsizetype limit)
QCache< QString, QConfFile > ConfFileCache
Definition qsettings.cpp:81
static const Qt::CaseSensitivity IniCaseSensitivity
Definition qsettings_p.h:42
#define k1
@ NoError
Definition main.cpp:34
#define Q_AUTOTEST_EXPORT
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
@ Q_RELOCATABLE_TYPE
Definition qtypeinfo.h:158
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:180
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
static int toInt(const QChar &qc, int R)
static int numDigits(qlonglong n)
QFile file
[0]
gzip write("uncompressed data")
QTextStream out(stdout)
[7]
QVariant variant
[1]
QString dir
[11]
QGraphicsItem * item
QHostInfo info
[0]
view create()
char * toString(const MyType &t)
[31]
QJSValueList args
Qt::CaseSensitivity caseSensitivity
Definition qsettings.cpp:76
QSettings::WriteFunc writeFunc
Definition qsettings.cpp:75
QSettings::ReadFunc readFunc
Definition qsettings.cpp:74
\inmodule QtCore \reentrant
Definition qchar.h:18
static char16_t * convertToUnicode(char16_t *dst, QLatin1StringView in) noexcept
Definition qstring.cpp:5687
static QChar * convertToUnicode(QChar *buffer, QByteArrayView in) noexcept