summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qsettings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qsettings.cpp')
-rw-r--r--src/corelib/io/qsettings.cpp812
1 files changed, 449 insertions, 363 deletions
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp
index 8f4bf42422..6934ca4404 100644
--- a/src/corelib/io/qsettings.cpp
+++ b/src/corelib/io/qsettings.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qdebug.h>
#include "qplatformdefs.h"
@@ -48,11 +12,12 @@
#include "qfileinfo.h"
#include "qmutex.h"
#include "private/qlocking_p.h"
+#include "private/qtools_p.h"
#include "qlibraryinfo.h"
#include "qtemporaryfile.h"
#include "qstandardpaths.h"
#include <qdatastream.h>
-#include <qstringconverter.h>
+#include "private/qstringconverter_p.h"
#ifndef QT_NO_GEOM_VARIANT
#include "qsize.h"
@@ -79,12 +44,13 @@
# include <shlobj.h>
#endif
-#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
+#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
#define Q_XDG_PLATFORM
#endif
-#if !defined(QT_NO_STANDARDPATHS) && (defined(Q_XDG_PLATFORM) || defined(QT_PLATFORM_UIKIT))
-#define QSETTINGS_USE_QSTANDARDPATHS
+#if !defined(QT_NO_STANDARDPATHS) \
+ && (defined(Q_XDG_PLATFORM) || defined(QT_PLATFORM_UIKIT) || defined(Q_OS_ANDROID))
+# define QSETTINGS_USE_QSTANDARDPATHS
#endif
// ************************************************************************
@@ -99,6 +65,9 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace QtMiscUtils;
+
struct QConfFileCustomFormat
{
QString extension;
@@ -129,9 +98,9 @@ Q_GLOBAL_STATIC(ConfFileCache, unusedCacheFunc)
Q_GLOBAL_STATIC(PathHash, pathHashFunc)
Q_GLOBAL_STATIC(CustomFormatVector, customFormatVectorFunc)
-static QBasicMutex settingsGlobalMutex;
+Q_CONSTINIT static QBasicMutex settingsGlobalMutex;
-static QSettings::Format globalDefaultFormat = QSettings::NativeFormat;
+Q_CONSTINIT static QSettings::Format globalDefaultFormat = QSettings::NativeFormat;
QConfFile::QConfFile(const QString &fileName, bool _userPerms)
: name(fileName), size(0), ref(1), userPerms(_userPerms)
@@ -148,11 +117,10 @@ QConfFile::~QConfFile()
ParsedSettingsMap QConfFile::mergedKeyMap() const
{
ParsedSettingsMap result = originalKeys;
- ParsedSettingsMap::const_iterator i;
- for (i = removedKeys.begin(); i != removedKeys.end(); ++i)
+ for (auto i = removedKeys.begin(); i != removedKeys.end(); ++i)
result.remove(i.key());
- for (i = addedKeys.begin(); i != addedKeys.end(); ++i)
+ for (auto i = addedKeys.begin(); i != addedKeys.end(); ++i)
result.insert(i.key(), i.value());
return result;
}
@@ -161,12 +129,12 @@ bool QConfFile::isWritable() const
{
QFileInfo fileInfo(name);
-#ifndef QT_NO_TEMPORARYFILE
+#if QT_CONFIG(temporaryfile)
if (fileInfo.exists()) {
#endif
QFile file(name);
return file.open(QFile::ReadWrite);
-#ifndef QT_NO_TEMPORARYFILE
+#if QT_CONFIG(temporaryfile)
} else {
// Create the directories to the file.
QDir dir(fileInfo.absolutePath());
@@ -229,13 +197,30 @@ QSettingsPrivate::~QSettingsPrivate()
{
}
-QString QSettingsPrivate::actualKey(const QString &key) const
+QString QSettingsPrivate::actualKey(QAnyStringView key) const
{
auto n = normalizedKey(key);
Q_ASSERT_X(!n.isEmpty(), "QSettings", "empty key");
return groupPrefix + n;
}
+namespace {
+ // ### this needs some public API (QStringConverter?)
+ QChar *write(QChar *out, QUtf8StringView v)
+ {
+ return QUtf8::convertToUnicode(out, QByteArrayView(v));
+ }
+ QChar *write(QChar *out, QLatin1StringView v)
+ {
+ return QLatin1::convertToUnicode(out, v);
+ }
+ QChar *write(QChar *out, QStringView v)
+ {
+ memcpy(out, v.data(), v.size() * sizeof(QChar));
+ return out + v.size();
+ }
+}
+
/*
Returns a string that never starts nor ends with a slash (or an
empty string). Examples:
@@ -243,38 +228,49 @@ QString QSettingsPrivate::actualKey(const QString &key) const
"foo" becomes "foo"
"/foo//bar///" becomes "foo/bar"
"///" becomes ""
-
- This function is optimized to avoid a QString deep copy in the
- common case where the key is already normalized.
*/
-QString QSettingsPrivate::normalizedKey(const QString &key)
+QString QSettingsPrivate::normalizedKey(QAnyStringView key)
{
- QString result = key;
+ QString result(key.size(), Qt::Uninitialized);
+ auto out = const_cast<QChar*>(result.constData()); // don't detach
- int i = 0;
- while (i < result.size()) {
- while (result.at(i) == QLatin1Char('/')) {
- result.remove(i, 1);
- if (i == result.size())
- goto after_loop;
- }
- while (result.at(i) != QLatin1Char('/')) {
- ++i;
- if (i == result.size())
- return result;
+ const bool maybeEndsInSlash = key.visit([&out](auto key) {
+ using View = decltype(key);
+
+ auto it = key.begin();
+ const auto end = key.end();
+
+ while (it != end) {
+ while (*it == u'/') {
+ ++it;
+ if (it == end)
+ return true;
+ }
+ auto mark = it;
+ while (*it != u'/') {
+ ++it;
+ if (it == end)
+ break;
+ }
+ out = write(out, View{mark, it});
+ if (it == end)
+ return false;
+ Q_ASSERT(*it == u'/');
+ *out++ = u'/';
+ ++it;
}
- ++i; // leave the slash alone
- }
+ return true;
+ });
-after_loop:
- if (!result.isEmpty())
- result.truncate(i - 1); // remove the trailing slash
+ if (maybeEndsInSlash && out != result.constData())
+ --out; // remove the trailing slash
+ result.truncate(out - result.constData());
return result;
}
// see also qsettings_win.cpp and qsettings_mac.cpp
-#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
+#if !defined(Q_OS_WIN) && !defined(Q_OS_DARWIN) && !defined(Q_OS_WASM)
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
const QString &organization, const QString &application)
{
@@ -292,7 +288,7 @@ QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::F
void QSettingsPrivate::processChild(QStringView key, ChildSpec spec, QStringList &result)
{
if (spec != AllKeys) {
- int slashPos = key.indexOf(QLatin1Char('/'));
+ qsizetype slashPos = key.indexOf(u'/');
if (slashPos == -1) {
if (spec != ChildKeys)
return;
@@ -310,7 +306,7 @@ void QSettingsPrivate::beginGroupOrArray(const QSettingsGroup &group)
groupStack.push(group);
const QString name = group.name();
if (!name.isEmpty())
- groupPrefix += name + QLatin1Char('/');
+ groupPrefix += name + u'/';
}
/*
@@ -346,30 +342,27 @@ void QSettingsPrivate::requestUpdate()
QStringList QSettingsPrivate::variantListToStringList(const QVariantList &l)
{
QStringList result;
- result.reserve(l.count());
- QVariantList::const_iterator it = l.constBegin();
- for (; it != l.constEnd(); ++it)
- result.append(variantToString(*it));
+ result.reserve(l.size());
+ for (auto v : l)
+ result.append(variantToString(v));
return result;
}
QVariant QSettingsPrivate::stringListToVariantList(const QStringList &l)
{
QStringList outStringList = l;
- for (int i = 0; i < outStringList.count(); ++i) {
+ for (qsizetype i = 0; i < outStringList.size(); ++i) {
const QString &str = outStringList.at(i);
- if (str.startsWith(QLatin1Char('@'))) {
- if (str.length() >= 2 && str.at(1) == QLatin1Char('@')) {
- outStringList[i].remove(0, 1);
- } else {
+ if (str.startsWith(u'@')) {
+ if (str.size() < 2 || str.at(1) != u'@') {
QVariantList variantList;
- const int stringCount = l.count();
- variantList.reserve(stringCount);
- for (int j = 0; j < stringCount; ++j)
- variantList.append(stringToVariant(l.at(j)));
+ variantList.reserve(l.size());
+ for (const auto &s : l)
+ variantList.append(stringToVariant(s));
return variantList;
}
+ outStringList[i].remove(0, 1);
}
}
return outStringList;
@@ -381,14 +374,12 @@ QString QSettingsPrivate::variantToString(const QVariant &v)
switch (v.metaType().id()) {
case QMetaType::UnknownType:
- result = QLatin1String("@Invalid()");
+ result = "@Invalid()"_L1;
break;
case QMetaType::QByteArray: {
QByteArray a = v.toByteArray();
- result = QLatin1String("@ByteArray(")
- + QLatin1String(a.constData(), a.size())
- + QLatin1Char(')');
+ result = "@ByteArray("_L1 + QLatin1StringView(a) + u')';
break;
}
@@ -405,9 +396,9 @@ QString QSettingsPrivate::variantToString(const QVariant &v)
case QMetaType::Double: {
result = v.toString();
if (result.contains(QChar::Null))
- result = QLatin1String("@String(") + result + QLatin1Char(')');
- else if (result.startsWith(QLatin1Char('@')))
- result.prepend(QLatin1Char('@'));
+ result = "@String("_L1 + result + u')';
+ else if (result.startsWith(u'@'))
+ result.prepend(u'@');
break;
}
#ifndef QT_NO_GEOM_VARIANT
@@ -446,9 +437,9 @@ QString QSettingsPrivate::variantToString(const QVariant &v)
s << v;
}
- result = QLatin1String(typeSpec)
- + QLatin1String(a.constData(), a.size())
- + QLatin1Char(')');
+ result = QLatin1StringView(typeSpec)
+ + QLatin1StringView(a.constData(), a.size())
+ + u')';
#else
Q_ASSERT(!"QSettings: Cannot save custom types without QDataStream support");
#endif
@@ -462,25 +453,25 @@ QString QSettingsPrivate::variantToString(const QVariant &v)
QVariant QSettingsPrivate::stringToVariant(const QString &s)
{
- if (s.startsWith(QLatin1Char('@'))) {
- if (s.endsWith(QLatin1Char(')'))) {
- if (s.startsWith(QLatin1String("@ByteArray("))) {
- return QVariant(QStringView{s}.mid(11, s.size() - 12).toLatin1());
- } else if (s.startsWith(QLatin1String("@String("))) {
- return QVariant(QStringView{s}.mid(8, s.size() - 9).toString());
- } else if (s.startsWith(QLatin1String("@Variant("))
- || s.startsWith(QLatin1String("@DateTime("))) {
+ if (s.startsWith(u'@')) {
+ if (s.endsWith(u')')) {
+ if (s.startsWith("@ByteArray("_L1)) {
+ return QVariant(QStringView{s}.sliced(11).chopped(1).toLatin1());
+ } else if (s.startsWith("@String("_L1)) {
+ return QVariant(QStringView{s}.sliced(8).chopped(1).toString());
+ } else if (s.startsWith("@Variant("_L1)
+ || s.startsWith("@DateTime("_L1)) {
#ifndef QT_NO_DATASTREAM
QDataStream::Version version;
int offset;
- if (s.at(1) == QLatin1Char('D')) {
+ if (s.at(1) == u'D') {
version = QDataStream::Qt_5_6;
offset = 10;
} else {
version = QDataStream::Qt_4_0;
offset = 9;
}
- QByteArray a = QStringView{s}.mid(offset).toLatin1();
+ QByteArray a = QStringView{s}.sliced(offset).toLatin1();
QDataStream stream(&a, QIODevice::ReadOnly);
stream.setVersion(version);
QVariant result;
@@ -490,53 +481,50 @@ QVariant QSettingsPrivate::stringToVariant(const QString &s)
Q_ASSERT(!"QSettings: Cannot load custom types without QDataStream support");
#endif
#ifndef QT_NO_GEOM_VARIANT
- } else if (s.startsWith(QLatin1String("@Rect("))) {
+ } else if (s.startsWith("@Rect("_L1)) {
QStringList args = QSettingsPrivate::splitArgs(s, 5);
if (args.size() == 4)
return QVariant(QRect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()));
- } else if (s.startsWith(QLatin1String("@Size("))) {
+ } else if (s.startsWith("@Size("_L1)) {
QStringList args = QSettingsPrivate::splitArgs(s, 5);
if (args.size() == 2)
return QVariant(QSize(args[0].toInt(), args[1].toInt()));
- } else if (s.startsWith(QLatin1String("@Point("))) {
+ } else if (s.startsWith("@Point("_L1)) {
QStringList args = QSettingsPrivate::splitArgs(s, 6);
if (args.size() == 2)
return QVariant(QPoint(args[0].toInt(), args[1].toInt()));
#endif
- } else if (s == QLatin1String("@Invalid()")) {
+ } else if (s == "@Invalid()"_L1) {
return QVariant();
}
}
- if (s.startsWith(QLatin1String("@@")))
- return QVariant(s.mid(1));
+ if (s.startsWith("@@"_L1))
+ return QVariant(s.sliced(1));
}
return QVariant(s);
}
-static const char hexDigits[] = "0123456789ABCDEF";
-
void QSettingsPrivate::iniEscapedKey(const QString &key, QByteArray &result)
{
- result.reserve(result.length() + key.length() * 3 / 2);
- for (int i = 0; i < key.size(); ++i) {
+ result.reserve(result.size() + key.size() * 3 / 2);
+ for (qsizetype i = 0; i < key.size(); ++i) {
uint ch = key.at(i).unicode();
if (ch == '/') {
result += '\\';
- } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')
- || ch == '_' || ch == '-' || ch == '.') {
+ } else if (isAsciiLetterOrNumber(ch) || ch == '_' || ch == '-' || ch == '.') {
result += (char)ch;
} else if (ch <= 0xFF) {
result += '%';
- result += hexDigits[ch / 16];
- result += hexDigits[ch % 16];
+ result += QtMiscUtils::toHexUpper(ch / 16);
+ result += QtMiscUtils::toHexUpper(ch % 16);
} else {
result += "%U";
QByteArray hexCode;
- for (int i = 0; i < 4; ++i) {
- hexCode.prepend(hexDigits[ch % 16]);
+ for (int j = 0; j < 4; ++j) {
+ hexCode.prepend(QtMiscUtils::toHexUpper(ch % 16));
ch >>= 4;
}
result += hexCode;
@@ -544,49 +532,50 @@ void QSettingsPrivate::iniEscapedKey(const QString &key, QByteArray &result)
}
}
-bool QSettingsPrivate::iniUnescapedKey(const QByteArray &key, int from, int to, QString &result)
+bool QSettingsPrivate::iniUnescapedKey(QByteArrayView key, QString &result)
{
+ const QString decoded = QString::fromUtf8(key);
+ const qsizetype size = decoded.size();
+ result.reserve(result.size() + size);
+ qsizetype i = 0;
bool lowercaseOnly = true;
- int i = from;
- result.reserve(result.length() + (to - from));
- while (i < to) {
- char16_t ch = (uchar)key.at(i);
+ while (i < size) {
+ char16_t ch = decoded.at(i).unicode();
if (ch == '\\') {
- result += QLatin1Char('/');
+ result += u'/';
++i;
continue;
}
- if (ch != '%' || i == to - 1) {
- if (uint(ch - 'A') <= 'Z' - 'A') // only for ASCII
+ if (ch != '%' || i == size - 1) {
+ QChar qch(ch);
+ if (qch.isUpper())
lowercaseOnly = false;
- result += ch;
+ result += qch;
++i;
continue;
}
int numDigits = 2;
- int firstDigitPos = i + 1;
+ qsizetype firstDigitPos = i + 1;
- ch = key.at(i + 1);
+ ch = decoded.at(i + 1).unicode();
if (ch == 'U') {
++firstDigitPos;
numDigits = 4;
}
- if (firstDigitPos + numDigits > to) {
- result += QLatin1Char('%');
- // ### missing U
+ if (firstDigitPos + numDigits > size) {
+ result += u'%';
++i;
continue;
}
bool ok;
- ch = key.mid(firstDigitPos, numDigits).toUShort(&ok, 16);
+ ch = QStringView(decoded).sliced(firstDigitPos, numDigits).toUShort(&ok, 16);
if (!ok) {
- result += QLatin1Char('%');
- // ### missing U
+ result += u'%';
++i;
continue;
}
@@ -604,25 +593,20 @@ void QSettingsPrivate::iniEscapedString(const QString &str, QByteArray &result)
{
bool needsQuotes = false;
bool escapeNextIfDigit = false;
- bool useCodec = !str.startsWith(QLatin1String("@ByteArray("))
- && !str.startsWith(QLatin1String("@Variant("));
-
- int i;
- int startPos = result.size();
+ const bool useCodec = !(str.startsWith("@ByteArray("_L1)
+ || str.startsWith("@Variant("_L1)
+ || str.startsWith("@DateTime("_L1));
+ const qsizetype startPos = result.size();
QStringEncoder toUtf8(QStringEncoder::Utf8);
result.reserve(startPos + str.size() * 3 / 2);
- const QChar *unicode = str.unicode();
- for (i = 0; i < str.size(); ++i) {
- uint ch = unicode[i].unicode();
+ for (QChar qch : str) {
+ uint ch = qch.unicode();
if (ch == ';' || ch == ',' || ch == '=')
needsQuotes = true;
- if (escapeNextIfDigit
- && ((ch >= '0' && ch <= '9')
- || (ch >= 'a' && ch <= 'f')
- || (ch >= 'A' && ch <= 'F'))) {
+ if (escapeNextIfDigit && isHexDigit(ch)) {
result += "\\x" + QByteArray::number(ch, 16);
continue;
}
@@ -666,7 +650,7 @@ void QSettingsPrivate::iniEscapedString(const QString &str, QByteArray &result)
escapeNextIfDigit = true;
} else if (useCodec) {
// slow
- result += toUtf8(unicode[i]);
+ result += toUtf8(qch);
} else {
result += (char)ch;
}
@@ -681,11 +665,11 @@ void QSettingsPrivate::iniEscapedString(const QString &str, QByteArray &result)
}
}
-inline static void iniChopTrailingSpaces(QString &str, int limit)
+inline static void iniChopTrailingSpaces(QString &str, qsizetype limit)
{
- int n = str.size() - 1;
+ qsizetype n = str.size() - 1;
QChar ch;
- while (n >= limit && ((ch = str.at(n)) == QLatin1Char(' ') || ch == QLatin1Char('\t')))
+ while (n >= limit && ((ch = str.at(n)) == u' ' || ch == u'\t'))
str.truncate(n--);
}
@@ -702,7 +686,7 @@ void QSettingsPrivate::iniEscapedStringList(const QStringList &strs, QByteArray
*/
result += "@Invalid()";
} else {
- for (int i = 0; i < strs.size(); ++i) {
+ for (qsizetype i = 0; i < strs.size(); ++i) {
if (i != 0)
result += ", ";
iniEscapedString(strs.at(i), result);
@@ -710,7 +694,7 @@ void QSettingsPrivate::iniEscapedStringList(const QStringList &strs, QByteArray
}
}
-bool QSettingsPrivate::iniUnescapedStringList(const QByteArray &str, int from, int to,
+bool QSettingsPrivate::iniUnescapedStringList(QByteArrayView str,
QString &stringResult, QStringList &stringListResult)
{
static const char escapeCodes[][2] =
@@ -732,22 +716,22 @@ bool QSettingsPrivate::iniUnescapedStringList(const QByteArray &str, int from, i
bool inQuotedString = false;
bool currentValueIsQuoted = false;
char16_t escapeVal = 0;
- int i = from;
+ qsizetype i = 0;
char ch;
QStringDecoder fromUtf8(QStringDecoder::Utf8);
StSkipSpaces:
- while (i < to && ((ch = str.at(i)) == ' ' || ch == '\t'))
+ while (i < str.size() && ((ch = str.at(i)) == ' ' || ch == '\t'))
++i;
// fallthrough
StNormal:
- int chopLimit = stringResult.length();
- while (i < to) {
+ qsizetype chopLimit = stringResult.size();
+ while (i < str.size()) {
switch (str.at(i)) {
case '\\':
++i;
- if (i >= to)
+ if (i >= str.size())
goto end;
ch = str.at(i++);
@@ -761,17 +745,17 @@ StNormal:
if (ch == 'x') {
escapeVal = 0;
- if (i >= to)
+ if (i >= str.size())
goto end;
ch = str.at(i);
- if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
+ if (isHexDigit(ch))
goto StHexEscape;
- } else if (ch >= '0' && ch <= '7') {
- escapeVal = ch - '0';
+ } else if (const int o = fromOct(ch); o != -1) {
+ escapeVal = o;
goto StOctEscape;
} else if (ch == '\n' || ch == '\r') {
- if (i < to) {
+ if (i < str.size()) {
char ch2 = str.at(i);
// \n, \r, \r\n, and \n\r are legitimate line terminators in INI files
if ((ch2 == '\n' || ch2 == '\r') && ch2 != ch)
@@ -780,7 +764,7 @@ StNormal:
} else {
// the character is skipped
}
- chopLimit = stringResult.length();
+ chopLimit = stringResult.size();
break;
case '"':
++i;
@@ -806,15 +790,15 @@ StNormal:
}
Q_FALLTHROUGH();
default: {
- int j = i + 1;
- while (j < to) {
+ qsizetype j = i + 1;
+ while (j < str.size()) {
ch = str.at(j);
if (ch == '\\' || ch == '"' || ch == ',')
break;
++j;
}
- stringResult += fromUtf8(QByteArrayView(str).first(j).sliced(i));
+ stringResult += fromUtf8(str.first(j).sliced(i));
i = j;
}
}
@@ -824,17 +808,15 @@ StNormal:
goto end;
StHexEscape:
- if (i >= to) {
+ if (i >= str.size()) {
stringResult += escapeVal;
goto end;
}
ch = str.at(i);
- if (ch >= 'a')
- ch -= 'a' - 'A';
- if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
+ if (const int h = fromHex(ch); h != -1) {
escapeVal <<= 4;
- escapeVal += strchr(hexDigits, ch) - hexDigits;
+ escapeVal += h;
++i;
goto StHexEscape;
} else {
@@ -843,15 +825,15 @@ StHexEscape:
}
StOctEscape:
- if (i >= to) {
+ if (i >= str.size()) {
stringResult += escapeVal;
goto end;
}
ch = str.at(i);
- if (ch >= '0' && ch <= '7') {
+ if (const int o = fromOct(ch); o != -1) {
escapeVal <<= 3;
- escapeVal += ch - '0';
+ escapeVal += o;
++i;
goto StOctEscape;
} else {
@@ -865,22 +847,22 @@ end:
return isStringList;
}
-QStringList QSettingsPrivate::splitArgs(const QString &s, int idx)
+QStringList QSettingsPrivate::splitArgs(const QString &s, qsizetype idx)
{
- int l = s.length();
+ qsizetype l = s.size();
Q_ASSERT(l > 0);
- Q_ASSERT(s.at(idx) == QLatin1Char('('));
- Q_ASSERT(s.at(l - 1) == QLatin1Char(')'));
+ Q_ASSERT(s.at(idx) == u'(');
+ Q_ASSERT(s.at(l - 1) == u')');
QStringList result;
QString item;
for (++idx; idx < l; ++idx) {
QChar c = s.at(idx);
- if (c == QLatin1Char(')')) {
+ if (c == u')') {
Q_ASSERT(idx == l - 1);
result.append(item);
- } else if (c == QLatin1Char(' ')) {
+ } else if (c == u' ') {
result.append(item);
item.clear();
} else {
@@ -896,20 +878,30 @@ QStringList QSettingsPrivate::splitArgs(const QString &s, int idx)
void QConfFileSettingsPrivate::initFormat()
{
- extension = (format == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini");
+#if defined(Q_OS_WASM)
+ extension = (format == QSettings::NativeFormat || format == QSettings::WebIndexedDBFormat)
+ ? ".conf"_L1
+ : ".ini"_L1;
+#else
+ extension = (format == QSettings::NativeFormat) ? ".conf"_L1 : ".ini"_L1;
+#endif
readFunc = nullptr;
writeFunc = nullptr;
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_DARWIN)
caseSensitivity = (format == QSettings::NativeFormat) ? Qt::CaseSensitive : IniCaseSensitivity;
#else
caseSensitivity = IniCaseSensitivity;
#endif
+#if defined Q_OS_WASM
+ if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat) {
+#else
if (format > QSettings::IniFormat) {
+#endif
const auto locker = qt_scoped_lock(settingsGlobalMutex);
const CustomFormatVector *customFormatVector = customFormatVectorFunc();
- int i = (int)format - (int)QSettings::CustomFormat1;
+ qsizetype i = qsizetype(format) - qsizetype(QSettings::CustomFormat1);
if (i >= 0 && i < customFormatVector->size()) {
QConfFileCustomFormat info = customFormatVector->at(i);
extension = info.extension;
@@ -923,7 +915,11 @@ void QConfFileSettingsPrivate::initFormat()
void QConfFileSettingsPrivate::initAccess()
{
if (!confFiles.isEmpty()) {
+#if defined Q_OS_WASM
+ if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat) {
+#else
if (format > QSettings::IniFormat) {
+#endif
if (!readFunc)
setStatus(QSettings::AccessError);
}
@@ -945,9 +941,9 @@ static QString windowsConfigPath(const KNOWNFOLDERID &type)
if (result.isEmpty()) {
if (type == FOLDERID_ProgramData) {
- result = QLatin1String("C:\\temp\\qt-common");
+ result = "C:\\temp\\qt-common"_L1;
} else if (type == FOLDERID_RoamingAppData) {
- result = QLatin1String("C:\\temp\\qt-user");
+ result = "C:\\temp\\qt-user"_L1;
}
}
@@ -961,26 +957,43 @@ static inline int pathHashKey(QSettings::Format format, QSettings::Scope scope)
}
#ifndef Q_OS_WIN
-static QString make_user_path()
+static constexpr QChar sep = u'/';
+
+#if !defined(QSETTINGS_USE_QSTANDARDPATHS) || defined(Q_OS_ANDROID)
+static QString make_user_path_without_qstandard_paths()
{
- static constexpr QChar sep = QLatin1Char('/');
-#ifndef QSETTINGS_USE_QSTANDARDPATHS
- // Non XDG platforms (OS X, iOS, Android...) have used this code path erroneously
- // for some time now. Moving away from that would require migrating existing settings.
QByteArray env = qgetenv("XDG_CONFIG_HOME");
if (env.isEmpty()) {
- return QDir::homePath() + QLatin1String("/.config/");
+ return QDir::homePath() + "/.config/"_L1;
} else if (env.startsWith('/')) {
return QFile::decodeName(env) + sep;
- } else {
- return QDir::homePath() + sep + QFile::decodeName(env) + sep;
}
+
+ return QDir::homePath() + sep + QFile::decodeName(env) + sep;
+}
+#endif // !QSETTINGS_USE_QSTANDARDPATHS || Q_OS_ANDROID
+
+static QString make_user_path()
+{
+#ifndef QSETTINGS_USE_QSTANDARDPATHS
+ // Non XDG platforms (OS X, iOS, Android...) have used this code path erroneously
+ // for some time now. Moving away from that would require migrating existing settings.
+ // The migration has already been done for Android.
+ return make_user_path_without_qstandard_paths();
#else
- // When using a proper XDG platform, use QStandardPaths rather than the above hand-written code;
- // it makes the use of test mode from unit tests possible.
+
+#ifdef Q_OS_ANDROID
+ // If an old settings path exists, use it instead of creating a new one
+ QString ret = make_user_path_without_qstandard_paths();
+ if (QFile(ret).exists())
+ return ret;
+#endif // Q_OS_ANDROID
+
+ // When using a proper XDG platform or Android platform, use QStandardPaths rather than the
+ // above hand-written code. It makes the use of test mode from unit tests possible.
// Ideally all platforms should use this, but see above for the migration issue.
return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + sep;
-#endif
+#endif // !QSETTINGS_USE_QSTANDARDPATHS
}
#endif // !Q_OS_WIN
@@ -995,7 +1008,7 @@ static std::unique_lock<QBasicMutex> initDefaultPaths(std::unique_lock<QBasicMut
avoid a dead-lock, we can't hold the global mutex while
calling it.
*/
- QString systemPath = QLibraryInfo::path(QLibraryInfo::SettingsPath) + QLatin1Char('/');
+ QString systemPath = QLibraryInfo::path(QLibraryInfo::SettingsPath) + u'/';
locker.lock();
if (pathHash->isEmpty()) {
@@ -1016,7 +1029,7 @@ static std::unique_lock<QBasicMutex> initDefaultPaths(std::unique_lock<QBasicMut
const QString userPath = make_user_path();
pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), Path(userPath, false));
pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), Path(systemPath, false));
-#ifndef Q_OS_MAC
+#ifndef Q_OS_DARWIN
pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), Path(userPath, false));
pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), Path(systemPath, false));
#endif
@@ -1028,8 +1041,8 @@ static std::unique_lock<QBasicMutex> initDefaultPaths(std::unique_lock<QBasicMut
static Path getPath(QSettings::Format format, QSettings::Scope scope)
{
- Q_ASSERT((int)QSettings::NativeFormat == 0);
- Q_ASSERT((int)QSettings::IniFormat == 1);
+ Q_ASSERT(int(QSettings::NativeFormat) == 0);
+ Q_ASSERT(int(QSettings::IniFormat) == 1);
auto locker = qt_unique_lock(settingsGlobalMutex);
PathHash *pathHash = pathHashFunc();
@@ -1065,7 +1078,7 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format,
QString org = organization;
if (org.isEmpty()) {
setStatus(QSettings::AccessError);
- org = QLatin1String("Unknown Organization");
+ org = "Unknown Organization"_L1;
}
QString appFile = org + QDir::separator() + application + extension;
@@ -1092,16 +1105,16 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format,
QStringList paths;
if (!application.isEmpty()) {
paths.reserve(dirs.size() * 2);
- for (const auto &dir : qAsConst(dirs))
- paths.append(dir + QLatin1Char('/') + appFile);
+ for (const auto &dir : std::as_const(dirs))
+ paths.append(dir + u'/' + appFile);
} else {
paths.reserve(dirs.size());
}
- for (const auto &dir : qAsConst(dirs))
- paths.append(dir + QLatin1Char('/') + orgFile);
+ for (const auto &dir : std::as_const(dirs))
+ paths.append(dir + u'/' + orgFile);
- // Note: No check for existence of files is done intentionaly.
- for (const auto &path : qAsConst(paths))
+ // Note: No check for existence of files is done intentionally.
+ for (const auto &path : std::as_const(paths))
confFiles.append(QConfFile::fromName(path, false));
} else
#endif // Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS
@@ -1111,9 +1124,7 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format,
confFiles.append(QConfFile::fromName(systemPath.path + orgFile, false));
}
-#ifndef Q_OS_WASM // wasm needs to delay access until after file sync
initAccess();
-#endif
}
QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName,
@@ -1134,7 +1145,7 @@ QConfFileSettingsPrivate::~QConfFileSettingsPrivate()
ConfFileHash *usedHash = usedHashFunc();
ConfFileCache *unusedCache = unusedCacheFunc();
- for (auto conf_file : qAsConst(confFiles)) {
+ for (auto conf_file : std::as_const(confFiles)) {
if (!conf_file->ref.deref()) {
if (conf_file->size == 0) {
delete conf_file;
@@ -1168,18 +1179,18 @@ void QConfFileSettingsPrivate::remove(const QString &key)
QConfFile *confFile = confFiles.at(0);
QSettingsKey theKey(key, caseSensitivity);
- QSettingsKey prefix(key + QLatin1Char('/'), caseSensitivity);
+ QSettingsKey prefix(key + u'/', caseSensitivity);
const auto locker = qt_scoped_lock(confFile->mutex);
ensureSectionParsed(confFile, theKey);
ensureSectionParsed(confFile, prefix);
- ParsedSettingsMap::iterator i = confFile->addedKeys.lowerBound(prefix);
+ auto i = confFile->addedKeys.lowerBound(prefix);
while (i != confFile->addedKeys.end() && i.key().startsWith(prefix))
i = confFile->addedKeys.erase(i);
confFile->addedKeys.remove(theKey);
- ParsedSettingsMap::const_iterator j = const_cast<const ParsedSettingsMap *>(&confFile->originalKeys)->lowerBound(prefix);
+ auto j = const_cast<const ParsedSettingsMap *>(&confFile->originalKeys)->lowerBound(prefix);
while (j != confFile->originalKeys.constEnd() && j.key().startsWith(prefix)) {
confFile->removedKeys.insert(j.key(), QVariant());
++j;
@@ -1208,7 +1219,7 @@ std::optional<QVariant> QConfFileSettingsPrivate::get(const QString &key) const
ParsedSettingsMap::const_iterator j;
bool found = false;
- for (auto confFile : qAsConst(confFiles)) {
+ for (auto confFile : std::as_const(confFiles)) {
const auto locker = qt_scoped_lock(confFile->mutex);
if (!confFile->addedKeys.isEmpty()) {
@@ -1233,12 +1244,11 @@ std::optional<QVariant> QConfFileSettingsPrivate::get(const QString &key) const
QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec spec) const
{
QStringList result;
- ParsedSettingsMap::const_iterator j;
QSettingsKey thePrefix(prefix, caseSensitivity);
- int startPos = prefix.size();
+ qsizetype startPos = prefix.size();
- for (auto confFile : qAsConst(confFiles)) {
+ for (auto confFile : std::as_const(confFiles)) {
const auto locker = qt_scoped_lock(confFile->mutex);
if (thePrefix.isEmpty())
@@ -1246,18 +1256,18 @@ QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec
else
ensureSectionParsed(confFile, thePrefix);
- j = const_cast<const ParsedSettingsMap *>(
- &confFile->originalKeys)->lowerBound( thePrefix);
- while (j != confFile->originalKeys.constEnd() && j.key().startsWith(thePrefix)) {
- if (!confFile->removedKeys.contains(j.key()))
- processChild(QStringView{j.key().originalCaseKey()}.mid(startPos), spec, result);
- ++j;
+ const auto &originalKeys = confFile->originalKeys;
+ auto i = originalKeys.lowerBound(thePrefix);
+ while (i != originalKeys.end() && i.key().startsWith(thePrefix)) {
+ if (!confFile->removedKeys.contains(i.key()))
+ processChild(QStringView{i.key().originalCaseKey()}.sliced(startPos), spec, result);
+ ++i;
}
- j = const_cast<const ParsedSettingsMap *>(
- &confFile->addedKeys)->lowerBound(thePrefix);
- while (j != confFile->addedKeys.constEnd() && j.key().startsWith(thePrefix)) {
- processChild(QStringView{j.key().originalCaseKey()}.mid(startPos), spec, result);
+ const auto &addedKeys = confFile->addedKeys;
+ auto j = addedKeys.lowerBound(thePrefix);
+ while (j != addedKeys.end() && j.key().startsWith(thePrefix)) {
+ processChild(QStringView{j.key().originalCaseKey()}.sliced(startPos), spec, result);
++j;
}
@@ -1289,7 +1299,7 @@ void QConfFileSettingsPrivate::sync()
// people probably won't be checking the status a whole lot, so in case of
// error we just try to go on and make the best of it
- for (auto confFile : qAsConst(confFiles)) {
+ for (auto confFile : std::as_const(confFiles)) {
const auto locker = qt_scoped_lock(confFile->mutex);
syncConfFile(confFile);
}
@@ -1311,7 +1321,11 @@ QString QConfFileSettingsPrivate::fileName() const
bool QConfFileSettingsPrivate::isWritable() const
{
+#if defined(Q_OS_WASM)
+ if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat && !writeFunc)
+#else
if (format > QSettings::IniFormat && !writeFunc)
+#endif
return false;
if (confFiles.isEmpty())
@@ -1324,13 +1338,13 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
{
bool readOnly = confFile->addedKeys.isEmpty() && confFile->removedKeys.isEmpty();
+ QFileInfo fileInfo(confFile->name);
/*
We can often optimize the read-only case, if the file on disk
hasn't changed.
*/
if (readOnly && confFile->size > 0) {
- QFileInfo fileInfo(confFile->name);
- if (confFile->size == fileInfo.size() && confFile->timeStamp == fileInfo.lastModified())
+ if (confFile->size == fileInfo.size() && confFile->timeStamp == fileInfo.lastModified(QTimeZone::UTC))
return;
}
@@ -1340,6 +1354,14 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
}
#ifndef QT_BOOTSTRAPPED
+ QString lockFileName = confFile->name + ".lock"_L1;
+
+# if defined(Q_OS_ANDROID) && defined(QSETTINGS_USE_QSTANDARDPATHS)
+ // On android and if it is a content URL put the lock file in a
+ // writable location to prevent permissions issues and invalid paths.
+ if (confFile->name.startsWith("content:"_L1))
+ lockFileName = make_user_path() + QFileInfo(lockFileName).fileName();
+# endif
/*
Use a lockfile in order to protect us against other QSettings instances
trying to write the same settings at the same time.
@@ -1347,7 +1369,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
We only need to lock if we are actually writing as only concurrent writes are a problem.
Concurrent read and write are not a problem because the writing operation is atomic.
*/
- QLockFile lockFile(confFile->name + QLatin1String(".lock"));
+ QLockFile lockFile(lockFileName);
if (!readOnly && !lockFile.lock() && atomicSyncOnly) {
setStatus(QSettings::AccessError);
return;
@@ -1358,13 +1380,13 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
We hold the lock. Let's reread the file if it has changed
since last time we read it.
*/
- QFileInfo fileInfo(confFile->name);
+ fileInfo.refresh();
bool mustReadFile = true;
bool createFile = !fileInfo.exists();
if (!readOnly)
mustReadFile = (confFile->size != fileInfo.size()
- || (confFile->size != 0 && confFile->timeStamp != fileInfo.lastModified()));
+ || (confFile->size != 0 && confFile->timeStamp != fileInfo.lastModified(QTimeZone::UTC)));
if (mustReadFile) {
confFile->unparsedIniSections.clear();
@@ -1382,7 +1404,14 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
*/
if (file.isReadable() && file.size() != 0) {
bool ok = false;
-#ifdef Q_OS_MAC
+
+#ifdef Q_OS_WASM
+ if (format == QSettings::WebIndexedDBFormat) {
+ QByteArray data = file.readAll();
+ ok = readIniFile(data, &confFile->unparsedIniSections);
+ } else
+#endif
+#ifdef Q_OS_DARWIN
if (format == QSettings::NativeFormat) {
QByteArray data = file.readAll();
ok = readPlistFile(data, &confFile->originalKeys);
@@ -1396,7 +1425,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
ok = readFunc(file, tempNewKeys);
if (ok) {
- QSettings::SettingsMap::const_iterator i = tempNewKeys.constBegin();
+ auto i = tempNewKeys.constBegin();
while (i != tempNewKeys.constEnd()) {
confFile->originalKeys.insert(QSettingsKey(i.key(), caseSensitivity),
i.value());
@@ -1410,7 +1439,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
}
confFile->size = fileInfo.size();
- confFile->timeStamp = fileInfo.lastModified();
+ confFile->timeStamp = fileInfo.lastModified(QTimeZone::UTC);
}
/*
@@ -1425,6 +1454,11 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
QSaveFile sf(confFile->name);
sf.setDirectWriteFallback(!atomicSyncOnly);
+# ifdef Q_OS_ANDROID
+ // QSaveFile requires direct write when using content scheme URL in Android
+ if (confFile->name.startsWith("content:"_L1))
+ sf.setDirectWriteFallback(true);
+# endif
#else
QFile sf(confFile->name);
#endif
@@ -1433,7 +1467,12 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
return;
}
-#ifdef Q_OS_MAC
+#ifdef Q_OS_WASM
+ if (format == QSettings::WebIndexedDBFormat) {
+ ok = writeIniFile(sf, mergedKeys);
+ } else
+#endif
+#ifdef Q_OS_DARWIN
if (format == QSettings::NativeFormat) {
ok = writePlistFile(sf, mergedKeys);
} else
@@ -1443,7 +1482,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
} else if (writeFunc) {
QSettings::SettingsMap tempOriginalKeys;
- ParsedSettingsMap::const_iterator i = mergedKeys.constBegin();
+ auto i = mergedKeys.constBegin();
while (i != mergedKeys.constEnd()) {
tempOriginalKeys.insert(i.key(), i.value());
++i;
@@ -1462,9 +1501,9 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
confFile->addedKeys.clear();
confFile->removedKeys.clear();
- QFileInfo fileInfo(confFile->name);
+ fileInfo.refresh();
confFile->size = fileInfo.size();
- confFile->timeStamp = fileInfo.lastModified();
+ confFile->timeStamp = fileInfo.lastModified(QTimeZone::UTC);
// If we have created the file, apply the file perms
if (createFile) {
@@ -1479,6 +1518,8 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
}
}
+namespace SettingsImpl {
+
enum { Space = 0x1, Special = 0x2 };
static const char charTraits[256] =
@@ -1505,10 +1546,16 @@ static const char charTraits[256] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-bool QConfFileSettingsPrivate::readIniLine(const QByteArray &data, int &dataPos,
- int &lineStart, int &lineLen, int &equalsPos)
+} // namespace SettingsImpl
+
+using SettingsImpl::charTraits;
+
+bool QConfFileSettingsPrivate::readIniLine(QByteArrayView data, qsizetype &dataPos,
+ qsizetype &lineStart, qsizetype &lineLen,
+ qsizetype &equalsPos)
{
- int dataLen = data.length();
+ using namespace SettingsImpl;
+ qsizetype dataLen = data.size();
bool inQuotes = false;
equalsPos = -1;
@@ -1517,7 +1564,7 @@ bool QConfFileSettingsPrivate::readIniLine(const QByteArray &data, int &dataPos,
while (lineStart < dataLen && (charTraits[uint(uchar(data.at(lineStart)))] & Space))
++lineStart;
- int i = lineStart;
+ qsizetype i = lineStart;
while (i < dataLen) {
char ch = data.at(i);
while (!(charTraits[uchar(ch)] & Special)) {
@@ -1576,7 +1623,7 @@ break_out_of_outer_loop:
possible, so if the user doesn't check the status he will get the
most out of the file anyway.
*/
-bool QConfFileSettingsPrivate::readIniFile(const QByteArray &data,
+bool QConfFileSettingsPrivate::readIniFile(QByteArrayView data,
UnparsedSettingsMap *unparsedIniSections)
{
#define FLUSH_CURRENT_SECTION() \
@@ -1586,59 +1633,56 @@ bool QConfFileSettingsPrivate::readIniFile(const QByteArray &data,
sectionPosition)]; \
if (!sectionData.isEmpty()) \
sectionData.append('\n'); \
- sectionData += data.mid(currentSectionStart, lineStart - currentSectionStart); \
+ sectionData += data.first(lineStart).sliced(currentSectionStart); \
sectionPosition = ++position; \
}
QString currentSection;
- int currentSectionStart = 0;
- int dataPos = 0;
- int lineStart;
- int lineLen;
- int equalsPos;
- int position = 0;
- int sectionPosition = 0;
+ qsizetype currentSectionStart = 0;
+ qsizetype dataPos = 0;
+ qsizetype lineStart;
+ qsizetype lineLen;
+ qsizetype equalsPos;
+ qsizetype position = 0;
+ qsizetype sectionPosition = 0;
bool ok = true;
- // skip potential utf8 BOM
- const uchar *dd = (const uchar *)data.constData();
- if (data.size() >= 3 && dd[0] == 0xef && dd[1] == 0xbb && dd[2] == 0xbf)
- dataPos = 3;
+ // Skip possible UTF-8 BOM:
+ if (data.startsWith("\xef\xbb\xbf"))
+ data = data.sliced(3);
while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) {
- char ch = data.at(lineStart);
- if (ch == '[') {
+ QByteArrayView line = data.sliced(lineStart, lineLen);
+ if (line.startsWith('[')) {
FLUSH_CURRENT_SECTION();
- // this is a section
- QByteArray iniSection;
- int idx = data.indexOf(']', lineStart);
- if (idx == -1 || idx >= lineStart + lineLen) {
+ // This starts a new section.
+ qsizetype idx = line.indexOf(']');
+ Q_ASSERT(idx == -1 || idx > 0); // line[0] is '[', not ']'.
+ Q_ASSERT(idx < lineLen); // (including -1 < lineLen, if no ']' present.)
+ if (idx < 0) {
ok = false;
- iniSection = data.mid(lineStart + 1, lineLen - 1);
- } else {
- iniSection = data.mid(lineStart + 1, idx - lineStart - 1);
+ idx = lineLen; // so line.first(idx) is just line
}
-
- iniSection = iniSection.trimmed();
+ QByteArrayView iniSection = line.first(idx).sliced(1).trimmed();
if (iniSection.compare("general", Qt::CaseInsensitive) == 0) {
currentSection.clear();
} else {
if (iniSection.compare("%general", Qt::CaseInsensitive) == 0) {
- currentSection = QLatin1String(iniSection.constData() + 1);
+ currentSection = QLatin1StringView(iniSection.constData() + 1, iniSection.size() - 1);
} else {
currentSection.clear();
- iniUnescapedKey(iniSection, 0, iniSection.size(), currentSection);
+ iniUnescapedKey(iniSection, currentSection);
}
- currentSection += QLatin1Char('/');
+ currentSection += u'/';
}
currentSectionStart = dataPos;
}
++position;
}
- Q_ASSERT(lineStart == data.length());
+ Q_ASSERT(lineStart == data.size());
FLUSH_CURRENT_SECTION();
return ok;
@@ -1646,57 +1690,53 @@ bool QConfFileSettingsPrivate::readIniFile(const QByteArray &data,
#undef FLUSH_CURRENT_SECTION
}
-bool QConfFileSettingsPrivate::readIniSection(const QSettingsKey &section, const QByteArray &data,
+bool QConfFileSettingsPrivate::readIniSection(const QSettingsKey &section, QByteArrayView data,
ParsedSettingsMap *settingsMap)
{
QStringList strListValue;
bool sectionIsLowercase = (section == section.originalCaseKey());
- int equalsPos;
+ qsizetype equalsPos;
bool ok = true;
- int dataPos = 0;
- int lineStart;
- int lineLen;
- int position = section.originalKeyPosition();
+ qsizetype dataPos = 0;
+ qsizetype lineStart;
+ qsizetype lineLen;
+ qsizetype position = section.originalKeyPosition();
while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) {
- char ch = data.at(lineStart);
- Q_ASSERT(ch != '[');
+ QByteArrayView line = data.sliced(lineStart, lineLen);
+ Q_ASSERT(!line.startsWith('['));
if (equalsPos == -1) {
- if (ch != ';')
+ if (!line.startsWith(';'))
ok = false;
continue;
}
+ // Shift equalPos indexing to be within line, rather than data:
+ equalsPos -= lineStart;
+ // Assured by readIniLine:
+ Q_ASSERT(equalsPos >= 0 && equalsPos < lineLen);
- int keyEnd = equalsPos;
- while (keyEnd > lineStart && ((ch = data.at(keyEnd - 1)) == ' ' || ch == '\t'))
- --keyEnd;
- int valueStart = equalsPos + 1;
+ QByteArrayView key = line.first(equalsPos).trimmed();
+ QByteArrayView value = line.sliced(equalsPos + 1);
- QString key = section.originalCaseKey();
- bool keyIsLowercase = (iniUnescapedKey(data, lineStart, keyEnd, key) && sectionIsLowercase);
+ QString strKey = section.originalCaseKey();
+ const Qt::CaseSensitivity casing = iniUnescapedKey(key, strKey) && sectionIsLowercase
+ ? Qt::CaseSensitive
+ : IniCaseSensitivity;
QString strValue;
- strValue.reserve(lineLen - (valueStart - lineStart));
- bool isStringList = iniUnescapedStringList(data, valueStart, lineStart + lineLen,
- strValue, strListValue);
- QVariant variant;
- if (isStringList) {
- variant = stringListToVariantList(strListValue);
- } else {
- variant = stringToVariant(strValue);
- }
+ strValue.reserve(value.size());
+ QVariant variant = iniUnescapedStringList(value, strValue, strListValue)
+ ? stringListToVariantList(strListValue)
+ : stringToVariant(strValue);
/*
We try to avoid the expensive toLower() call in
QSettingsKey by passing Qt::CaseSensitive when the
key is already in lowercase.
*/
- settingsMap->insert(QSettingsKey(key, keyIsLowercase ? Qt::CaseSensitive
- : IniCaseSensitivity,
- position),
- variant);
+ settingsMap->insert(QSettingsKey(strKey, casing, position), std::move(variant));
++position;
}
@@ -1707,9 +1747,9 @@ class QSettingsIniKey : public QString
{
public:
inline QSettingsIniKey() : position(-1) {}
- inline QSettingsIniKey(const QString &str, int pos = -1) : QString(str), position(pos) {}
+ inline QSettingsIniKey(const QString &str, qsizetype pos = -1) : QString(str), position(pos) {}
- int position;
+ qsizetype position;
};
Q_DECLARE_TYPEINFO(QSettingsIniKey, Q_RELOCATABLE_TYPE);
@@ -1724,7 +1764,7 @@ typedef QMap<QSettingsIniKey, QVariant> IniKeyMap;
struct QSettingsIniSection
{
- int position;
+ qsizetype position;
IniKeyMap keyMap;
inline QSettingsIniSection() : position(-1) {}
@@ -1741,7 +1781,6 @@ typedef QMap<QString, QSettingsIniSection> IniMap;
bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSettingsMap &map)
{
IniMap iniMap;
- IniMap::const_iterator i;
#ifdef Q_OS_WIN
const char * const eol = "\r\n";
@@ -1749,12 +1788,12 @@ bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSetti
const char eol = '\n';
#endif
- for (ParsedSettingsMap::const_iterator j = map.constBegin(); j != map.constEnd(); ++j) {
+ for (auto j = map.constBegin(); j != map.constEnd(); ++j) {
QString section;
QSettingsIniKey key(j.key().originalCaseKey(), j.key().originalKeyPosition());
- int slashPos;
+ qsizetype slashPos;
- if ((slashPos = key.indexOf(QLatin1Char('/'))) != -1) {
+ if ((slashPos = key.indexOf(u'/')) != -1) {
section = key.left(slashPos);
key.remove(0, slashPos + 1);
}
@@ -1762,21 +1801,21 @@ bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSetti
QSettingsIniSection &iniSection = iniMap[section];
// -1 means infinity
- if (uint(key.position) < uint(iniSection.position))
+ if (size_t(key.position) < size_t(iniSection.position))
iniSection.position = key.position;
iniSection.keyMap[key] = j.value();
}
- const int sectionCount = iniMap.size();
+ const qsizetype sectionCount = iniMap.size();
QList<QSettingsIniKey> sections;
sections.reserve(sectionCount);
- for (i = iniMap.constBegin(); i != iniMap.constEnd(); ++i)
+ for (auto i = iniMap.constBegin(); i != iniMap.constEnd(); ++i)
sections.append(QSettingsIniKey(i.key(), i.value().position));
std::sort(sections.begin(), sections.end());
bool writeError = false;
- for (int j = 0; !writeError && j < sectionCount; ++j) {
- i = iniMap.constFind(sections.at(j));
+ for (qsizetype j = 0; !writeError && j < sectionCount; ++j) {
+ auto i = iniMap.constFind(sections.at(j));
Q_ASSERT(i != iniMap.constEnd());
QByteArray realSection;
@@ -1799,7 +1838,7 @@ bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSetti
device.write(realSection);
const IniKeyMap &ents = i.value().keyMap;
- for (IniKeyMap::const_iterator j = ents.constBegin(); j != ents.constEnd(); ++j) {
+ for (auto j = ents.constBegin(); j != ents.constEnd(); ++j) {
QByteArray block;
iniEscapedKey(j.key(), block);
block += '=';
@@ -1829,8 +1868,8 @@ bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSetti
void QConfFileSettingsPrivate::ensureAllSectionsParsed(QConfFile *confFile) const
{
- UnparsedSettingsMap::const_iterator i = confFile->unparsedIniSections.constBegin();
- const UnparsedSettingsMap::const_iterator end = confFile->unparsedIniSections.constEnd();
+ auto i = confFile->unparsedIniSections.constBegin();
+ const auto end = confFile->unparsedIniSections.constEnd();
for (; i != end; ++i) {
if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys))
@@ -1847,7 +1886,7 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
UnparsedSettingsMap::iterator i;
- int indexOfSlash = key.indexOf(QLatin1Char('/'));
+ qsizetype indexOfSlash = key.indexOf(u'/');
if (indexOfSlash != -1) {
i = confFile->unparsedIniSections.upperBound(key);
if (i == confFile->unparsedIniSections.begin())
@@ -2084,12 +2123,10 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
\snippet settings/settings.cpp 15
- Note that type information is not preserved when reading settings from INI
- files; all values will be returned as QString.
-
- The \l{tools/settingseditor}{Settings Editor} example lets you
- experiment with different settings location and with fallbacks
- turned on or off.
+ Note that INI files lose the distinction between numeric data and the
+ strings used to encode them, so values written as numbers shall be read back
+ as QString. The numeric value can be recovered using \l QString::toInt(), \l
+ QString::toDouble() and related functions.
\section1 Restoring the State of a GUI Application
@@ -2116,9 +2153,6 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
\codeline
\snippet settings/settings.cpp 21
- See the \l{mainwindows/application}{Application} example for a
- self-contained example that uses QSettings.
-
\section1 Accessing Settings from Multiple Threads or Processes Simultaneously
QSettings is \l{reentrant}. This means that you can use
@@ -2160,8 +2194,8 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
following files are used by default:
\list 1
- \li \c{$HOME/.config/MySoft/Star Runner.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.conf})
- \li \c{$HOME/.config/MySoft.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.conf})
+ \li \c{$HOME/.config/MySoft/Star Runner.conf}
+ \li \c{$HOME/.config/MySoft.conf}
\li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.conf}
\li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.conf}
\endlist
@@ -2198,8 +2232,8 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
used on Unix, \macos, and iOS:
\list 1
- \li \c{$HOME/.config/MySoft/Star Runner.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.ini})
- \li \c{$HOME/.config/MySoft.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.ini})
+ \li \c{$HOME/.config/MySoft/Star Runner.ini}
+ \li \c{$HOME/.config/MySoft.ini}
\li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.ini}
\li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.ini}
\endlist
@@ -2334,7 +2368,7 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
\endlist
- \sa QVariant, QSessionManager, {Settings Editor Example}, {Qt Widgets - Application Example}
+ \sa QVariant, QSessionManager
*/
/*! \enum QSettings::Status
@@ -2368,9 +2402,20 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
On 32-bit Windows or from a 64-bit application on 64-bit Windows,
this works the same as specifying NativeFormat.
This enum value was added in Qt 5.7.
- \value IniFormat Store the settings in INI files. Note that type information
- is not preserved when reading settings from INI files;
- all values will be returned as QString.
+ \value IniFormat Store the settings in INI files. Note that INI files
+ lose the distinction between numeric data and the
+ strings used to encode them, so values written as
+ numbers shall be read back as QString.
+ \value WebLocalStorageFormat
+ WASM only: Store the settings in window.localStorage for the current
+ origin. If cookies are not allowed, this falls back to the INI format.
+ This provides up to 5MiB storage per origin, but access to it is
+ synchronous and JSPI is not required.
+ \value WebIndexedDBFormat
+ WASM only: Store the settings in an Indexed DB for the current
+ origin. If cookies are not allowed, this falls back to the INI format.
+ This requires JSPI, but provides more storage than
+ WebLocalStorageFormat.
\value InvalidFormat Special value returned by registerFormat().
\omitvalue CustomFormat1
@@ -2428,16 +2473,19 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
such as "General/someKey", the key will be located in the
"%General" section, \e not in the "General" section.
- \li In line with most implementations today, QSettings will
- assume the INI file is utf-8 encoded. This means that keys and values
+ \li In line with most implementations today, QSettings will assume that
+ \e values in the INI file are utf-8 encoded. This means that \e values
will be decoded as utf-8 encoded entries and written back as utf-8.
+ To retain backward compatibility with older Qt versions, \e keys in the
+ INI file are written in %-encoded format, but can be read in both
+ %-encoded and utf-8 formats.
\endlist
\section2 Compatibility with older Qt versions
Please note that this behavior is different to how QSettings behaved
- in versions of Qt prior to Qt 6. INI files written with Qt 5 or earlier aree
+ in versions of Qt prior to Qt 6. INI files written with Qt 5 or earlier are
however fully readable by a Qt 6 based application (unless a ini codec
different from utf8 had been set). But INI files written with Qt 6
will only be readable by older Qt versions if you set the "iniCodec" to
@@ -2686,10 +2734,11 @@ QSettings::~QSettings()
{
Q_D(QSettings);
if (d->pendingChanges) {
+ // Don't cause a failing flush() to std::terminate() the whole
+ // application - dtors are implicitly noexcept!
QT_TRY {
d->flush();
} QT_CATCH(...) {
- ; // ok. then don't flush but at least don't throw in the destructor
}
}
}
@@ -2881,9 +2930,12 @@ void QSettings::setAtomicSyncRequired(bool enable)
Call endGroup() to reset the current group to what it was before
the corresponding beginGroup() call. Groups can be nested.
+ \note In Qt versions prior to 6.4, this function took QString, not
+ QAnyStringView.
+
\sa endGroup(), group()
*/
-void QSettings::beginGroup(const QString &prefix)
+void QSettings::beginGroup(QAnyStringView prefix)
{
Q_D(QSettings);
d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix)));
@@ -2908,7 +2960,7 @@ void QSettings::endGroup()
}
QSettingsGroup group = d->groupStack.pop();
- int len = group.toString().size();
+ qsizetype len = group.toString().size();
if (len > 0)
d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1));
@@ -2937,13 +2989,16 @@ QString QSettings::group() const
Use beginWriteArray() to write the array in the first place.
+ \note In Qt versions prior to 6.4, this function took QString, not
+ QAnyStringView.
+
\sa beginWriteArray(), endArray(), setArrayIndex()
*/
-int QSettings::beginReadArray(const QString &prefix)
+int QSettings::beginReadArray(QAnyStringView prefix)
{
Q_D(QSettings);
d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), false));
- return value(QLatin1String("size")).toInt();
+ return value("size"_L1).toInt();
}
/*!
@@ -2973,17 +3028,20 @@ int QSettings::beginReadArray(const QString &prefix)
To read back an array, use beginReadArray().
+ \note In Qt versions prior to 6.4, this function took QString, not
+ QAnyStringView.
+
\sa beginReadArray(), endArray(), setArrayIndex()
*/
-void QSettings::beginWriteArray(const QString &prefix, int size)
+void QSettings::beginWriteArray(QAnyStringView prefix, int size)
{
Q_D(QSettings);
d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), size < 0));
if (size < 0)
- remove(QLatin1String("size"));
+ remove("size"_L1);
else
- setValue(QLatin1String("size"), size);
+ setValue("size"_L1, size);
}
/*!
@@ -3001,13 +3059,13 @@ void QSettings::endArray()
}
QSettingsGroup group = d->groupStack.top();
- int len = group.toString().size();
+ qsizetype len = group.toString().size();
d->groupStack.pop();
if (len > 0)
d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1));
if (group.arraySizeGuess() != -1)
- setValue(group.name() + QLatin1String("/size"), group.arraySizeGuess());
+ setValue(group.name() + "/size"_L1, group.arraySizeGuess());
if (!group.isArray())
qWarning("QSettings::endArray: Expected endGroup() instead");
@@ -3030,7 +3088,7 @@ void QSettings::setArrayIndex(int i)
}
QSettingsGroup &top = d->groupStack.top();
- int len = top.toString().size();
+ qsizetype len = top.toString().size();
top.setArrayIndex(qMax(i, 0));
d->groupPrefix.replace(d->groupPrefix.size() - len - 1, len, top.toString());
}
@@ -3136,9 +3194,12 @@ bool QSettings::isWritable() const
\snippet code/src_corelib_io_qsettings.cpp 23
+ \note In Qt versions prior to 6.4, this function took QString, not
+ QAnyStringView.
+
\sa value(), remove(), contains()
*/
-void QSettings::setValue(const QString &key, const QVariant &value)
+void QSettings::setValue(QAnyStringView key, const QVariant &value)
{
Q_D(QSettings);
if (key.isEmpty()) {
@@ -3170,9 +3231,12 @@ void QSettings::setValue(const QString &key, const QVariant &value)
case-sensitive keys. To avoid portability problems, see the
\l{Section and Key Syntax} rules.
+ \note In Qt versions prior to 6.4, this function took QString, not
+ QAnyStringView.
+
\sa setValue(), value(), contains()
*/
-void QSettings::remove(const QString &key)
+void QSettings::remove(QAnyStringView key)
{
Q_D(QSettings);
/*
@@ -3205,9 +3269,12 @@ void QSettings::remove(const QString &key)
case-sensitive keys. To avoid portability problems, see the
\l{Section and Key Syntax} rules.
+ \note In Qt versions prior to 6.4, this function took QString, not
+ QAnyStringView.
+
\sa value(), setValue()
*/
-bool QSettings::contains(const QString &key) const
+bool QSettings::contains(QAnyStringView key) const
{
Q_D(const QSettings);
return d->get(d->actualKey(key)) != std::nullopt;
@@ -3255,6 +3322,9 @@ bool QSettings::event(QEvent *event)
#endif
/*!
+ \fn QSettings::value(QAnyStringView key) const
+ \fn QSettings::value(QAnyStringView key, const QVariant &defaultValue) const
+
Returns the value for setting \a key. If the setting doesn't
exist, returns \a defaultValue.
@@ -3270,16 +3340,34 @@ bool QSettings::event(QEvent *event)
\snippet code/src_corelib_io_qsettings.cpp 26
+ \note In Qt versions prior to 6.4, this function took QString, not
+ QAnyStringView.
+
\sa setValue(), contains(), remove()
*/
-QVariant QSettings::value(const QString &key, const QVariant &defaultValue) const
+QVariant QSettings::value(QAnyStringView key) const
{
Q_D(const QSettings);
+ return d->value(key, nullptr);
+}
+
+QVariant QSettings::value(QAnyStringView key, const QVariant &defaultValue) const
+{
+ Q_D(const QSettings);
+ return d->value(key, &defaultValue);
+}
+
+QVariant QSettingsPrivate::value(QAnyStringView key, const QVariant *defaultValue) const
+{
if (key.isEmpty()) {
qWarning("QSettings::value: Empty key passed");
return QVariant();
}
- return d->get(d->actualKey(key)).value_or(defaultValue);
+ if (std::optional r = get(actualKey(key)))
+ return std::move(*r);
+ if (defaultValue)
+ return *defaultValue;
+ return QVariant();
}
/*!
@@ -3326,8 +3414,6 @@ QSettings::Format QSettings::defaultFormat()
\row \li SystemScope \li \c FOLDERID_ProgramData
\row \li{1,2} Unix \li{1,2} NativeFormat, IniFormat \li UserScope \li \c $HOME/.config
\row \li SystemScope \li \c /etc/xdg
- \row \li{1,2} Qt for Embedded Linux \li{1,2} NativeFormat, IniFormat \li UserScope \li \c $HOME/Settings
- \row \li SystemScope \li \c /etc/xdg
\row \li{1,2} \macos and iOS \li{1,2} IniFormat \li UserScope \li \c $HOME/.config
\row \li SystemScope \li \c /etc/xdg
\endtable
@@ -3433,18 +3519,18 @@ QSettings::Format QSettings::registerFormat(const QString &extension, ReadFunc r
const auto locker = qt_scoped_lock(settingsGlobalMutex);
CustomFormatVector *customFormatVector = customFormatVectorFunc();
- int index = customFormatVector->size();
+ qsizetype index = customFormatVector->size();
if (index == 16) // the QSettings::Format enum has room for 16 custom formats
return QSettings::InvalidFormat;
QConfFileCustomFormat info;
- info.extension = QLatin1Char('.') + extension;
+ info.extension = u'.' + extension;
info.readFunc = readFunc;
info.writeFunc = writeFunc;
info.caseSensitivity = caseSensitivity;
customFormatVector->append(info);
- return QSettings::Format((int)QSettings::CustomFormat1 + index);
+ return QSettings::Format(int(QSettings::CustomFormat1) + index);
}
QT_END_NAMESPACE