diff options
Diffstat (limited to 'src/corelib/io/qsettings.cpp')
-rw-r--r-- | src/corelib/io/qsettings.cpp | 3822 |
1 files changed, 3822 insertions, 0 deletions
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp new file mode 100644 index 0000000..62b4ed5 --- /dev/null +++ b/src/corelib/io/qsettings.cpp @@ -0,0 +1,3822 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qdebug.h> +#include "qplatformdefs.h" +#include "qsettings.h" + +#ifndef QT_NO_SETTINGS + +#include "qsettings_p.h" +#include "qcache.h" +#include "qfile.h" +#include "qdir.h" +#include "qfileinfo.h" +#include "qmutex.h" +#include "qlibraryinfo.h" +#include "qtemporaryfile.h" + +#ifndef QT_NO_TEXTCODEC +# include "qtextcodec.h" +#endif + +#ifndef QT_NO_GEOM_VARIANT +#include "qsize.h" +#include "qpoint.h" +#include "qrect.h" +#endif // !QT_NO_GEOM_VARIANT + +#ifndef QT_NO_QOBJECT +#include "qcoreapplication.h" + +#ifdef Q_OS_WIN // for homedirpath reading from registry +#include "qt_windows.h" +#include "qlibrary.h" + +#endif // Q_OS_WIN +#endif // QT_NO_QOBJECT + +#include <stdlib.h> + +#ifndef CSIDL_COMMON_APPDATA +#define CSIDL_COMMON_APPDATA 0x0023 // All Users\Application Data +#endif + +#ifndef CSIDL_APPDATA +#define CSIDL_APPDATA 0x001a // <username>\Application Data +#endif + +// ************************************************************************ +// QConfFile + +/* + QConfFile objects are explicitly shared within the application. + This ensures that modification to the settings done through one + QSettings object are immediately reflected in other setting + objects of the same application. +*/ + +QT_BEGIN_NAMESPACE + +struct QConfFileCustomFormat +{ + QString extension; + QSettings::ReadFunc readFunc; + QSettings::WriteFunc writeFunc; + Qt::CaseSensitivity caseSensitivity; +}; + +typedef QHash<QString, QConfFile *> ConfFileHash; +typedef QCache<QString, QConfFile> ConfFileCache; +typedef QHash<int, QString> PathHash; +typedef QVector<QConfFileCustomFormat> CustomFormatVector; + +Q_GLOBAL_STATIC(ConfFileHash, usedHashFunc) +Q_GLOBAL_STATIC(ConfFileCache, unusedCacheFunc) +Q_GLOBAL_STATIC(PathHash, pathHashFunc) +Q_GLOBAL_STATIC(CustomFormatVector, customFormatVectorFunc) +Q_GLOBAL_STATIC(QMutex, globalMutex) +static QSettings::Format globalDefaultFormat = QSettings::NativeFormat; + +#ifndef Q_OS_WIN +inline bool qt_isEvilFsTypeName(const char *name) +{ + return (qstrncmp(name, "nfs", 3) == 0 + || qstrncmp(name, "autofs", 6) == 0 + || qstrncmp(name, "cachefs", 7) == 0); +} + +#if defined(Q_OS_BSD4) && !defined(Q_OS_NETBSD) +QT_BEGIN_INCLUDE_NAMESPACE +# include <sys/param.h> +# include <sys/mount.h> +QT_END_INCLUDE_NAMESPACE + +static bool isLikelyToBeNfs(int handle) +{ + struct statfs buf; + if (fstatfs(handle, &buf) != 0) + return false; + return qt_isEvilFsTypeName(buf.f_fstypename); +} + +#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) +QT_BEGIN_INCLUDE_NAMESPACE +# include <sys/vfs.h> +# ifdef QT_LINUXBASE + // LSB 3.2 has fstatfs in sys/statfs.h, sys/vfs.h is just an empty dummy header +# include <sys/statfs.h> +# endif +QT_END_INCLUDE_NAMESPACE +# ifndef NFS_SUPER_MAGIC +# define NFS_SUPER_MAGIC 0x00006969 +# endif +# ifndef AUTOFS_SUPER_MAGIC +# define AUTOFS_SUPER_MAGIC 0x00000187 +# endif +# ifndef AUTOFSNG_SUPER_MAGIC +# define AUTOFSNG_SUPER_MAGIC 0x7d92b1a0 +# endif + +static bool isLikelyToBeNfs(int handle) +{ + struct statfs buf; + if (fstatfs(handle, &buf) != 0) + return false; + return buf.f_type == NFS_SUPER_MAGIC + || buf.f_type == AUTOFS_SUPER_MAGIC + || buf.f_type == AUTOFSNG_SUPER_MAGIC; +} + +#elif defined(Q_OS_SOLARIS) || defined(Q_OS_IRIX) || defined(Q_OS_AIX) || defined(Q_OS_HPUX) \ + || defined(Q_OS_OSF) || defined(Q_OS_QNX) || defined(Q_OS_QNX6) || defined(Q_OS_SCO) \ + || defined(Q_OS_UNIXWARE) || defined(Q_OS_RELIANT) || defined(Q_OS_NETBSD) +QT_BEGIN_INCLUDE_NAMESPACE +# include <sys/statvfs.h> +QT_END_INCLUDE_NAMESPACE + +static bool isLikelyToBeNfs(int handle) +{ + struct statvfs buf; + if (fstatvfs(handle, &buf) != 0) + return false; +#if defined(Q_OS_NETBSD) + return qt_isEvilFsTypeName(buf.f_fstypename); +#else + return qt_isEvilFsTypeName(buf.f_basetype); +#endif +} +#else +static inline bool isLikelyToBeNfs(int /* handle */) +{ + return true; +} +#endif + +static bool unixLock(int handle, int lockType) +{ + /* + NFS hangs on the fcntl() call below when statd or lockd isn't + running. There's no way to detect this. Our work-around for + now is to disable locking when we detect NFS (or AutoFS or + CacheFS, which are probably wrapping NFS). + */ + if (isLikelyToBeNfs(handle)) + return false; + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = lockType; + return fcntl(handle, F_SETLKW, &fl) == 0; +} +#endif + +QConfFile::QConfFile(const QString &fileName, bool _userPerms) + : name(fileName), size(0), ref(1), userPerms(_userPerms) +{ + usedHashFunc()->insert(name, this); +} + +ParsedSettingsMap QConfFile::mergedKeyMap() const +{ + ParsedSettingsMap result = originalKeys; + ParsedSettingsMap::const_iterator i; + + for (i = removedKeys.begin(); i != removedKeys.end(); ++i) + result.remove(i.key()); + for (i = addedKeys.begin(); i != addedKeys.end(); ++i) + result.insert(i.key(), i.value()); + return result; +} + +bool QConfFile::isWritable() const +{ + QFileInfo fileInfo(name); + +#ifndef QT_NO_TEMPORARYFILE + if (fileInfo.exists()) { +#endif + QFile file(name); + return file.open(QFile::ReadWrite); +#ifndef QT_NO_TEMPORARYFILE + } else { + // Create the directories to the file. + QDir dir(fileInfo.absolutePath()); + if (dir.exists() && dir.isReadable()) { + return true; + } else { + if (!dir.mkpath(dir.absolutePath())) + return false; + } + + // we use a temporary file to avoid race conditions + QTemporaryFile file(name); + return file.open(); + } +#endif +} + +QConfFile *QConfFile::fromName(const QString &fileName, bool _userPerms) +{ + QString absPath = QFileInfo(fileName).absoluteFilePath(); + + ConfFileHash *usedHash = usedHashFunc(); + ConfFileCache *unusedCache = unusedCacheFunc(); + + QConfFile *confFile; + QMutexLocker locker(globalMutex()); + + if (!(confFile = usedHash->value(absPath))) { + if ((confFile = unusedCache->take(absPath))) + usedHash->insert(absPath, confFile); + } + if (confFile) { + confFile->ref.ref(); + return confFile; + } + return new QConfFile(absPath, _userPerms); +} + +void QConfFile::clearCache() +{ + QMutexLocker locker(globalMutex()); + unusedCacheFunc()->clear(); +} + +// ************************************************************************ +// QSettingsPrivate + +QSettingsPrivate::QSettingsPrivate(QSettings::Format format) + : format(format), scope(QSettings::UserScope /* nothing better to put */), iniCodec(0), spec(0), fallbacks(true), + pendingChanges(false), status(QSettings::NoError) +{ +} + +QSettingsPrivate::QSettingsPrivate(QSettings::Format format, QSettings::Scope scope, + const QString &organization, const QString &application) + : format(format), scope(scope), organizationName(organization), applicationName(application), + iniCodec(0), spec(0), fallbacks(true), pendingChanges(false), status(QSettings::NoError) +{ +} + +QSettingsPrivate::~QSettingsPrivate() +{ +} + +QString QSettingsPrivate::actualKey(const QString &key) const +{ + QString n = normalizedKey(key); + Q_ASSERT_X(!n.isEmpty(), "QSettings", "empty key"); + n.prepend(groupPrefix); + return n; +} + +/* + Returns a string that never starts nor ends with a slash (or an + empty string). Examples: + + "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 result = key; + + 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; + } + ++i; // leave the slash alone + } + +after_loop: + if (!result.isEmpty()) + result.truncate(i - 1); // remove the trailing slash + return result; +} + +// see also qsettings_win.cpp and qsettings_mac.cpp + +#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) +QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope, + const QString &organization, const QString &application) +{ + return new QConfFileSettingsPrivate(format, scope, organization, application); +} +#endif + +#if !defined(Q_OS_WIN) +QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format) +{ + return new QConfFileSettingsPrivate(fileName, format); +} +#endif + +void QSettingsPrivate::processChild(QString key, ChildSpec spec, QMap<QString, QString> &result) +{ + if (spec != AllKeys) { + int slashPos = key.indexOf(QLatin1Char('/')); + if (slashPos == -1) { + if (spec != ChildKeys) + return; + } else { + if (spec != ChildGroups) + return; + key.truncate(slashPos); + } + } + result.insert(key, QString()); +} + +void QSettingsPrivate::beginGroupOrArray(const QSettingsGroup &group) +{ + groupStack.push(group); + if (!group.name().isEmpty()) { + groupPrefix += group.name(); + groupPrefix += QLatin1Char('/'); + } +} + +/* + We only set an error if there isn't one set already. This way the user always gets the + first error that occurred. We always allow clearing errors. +*/ + +void QSettingsPrivate::setStatus(QSettings::Status status) const +{ + if (status == QSettings::NoError || this->status == QSettings::NoError) + this->status = status; +} + +void QSettingsPrivate::update() +{ + flush(); + pendingChanges = false; +} + +void QSettingsPrivate::requestUpdate() +{ + if (!pendingChanges) { + pendingChanges = true; +#ifndef QT_NO_QOBJECT + Q_Q(QSettings); + QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); +#else + update(); +#endif + } +} + +QStringList QSettingsPrivate::variantListToStringList(const QVariantList &l) +{ + QStringList result; + QVariantList::const_iterator it = l.constBegin(); + for (; it != l.constEnd(); ++it) + result.append(variantToString(*it)); + return result; +} + +QVariant QSettingsPrivate::stringListToVariantList(const QStringList &l) +{ + QStringList outStringList = l; + for (int i = 0; i < outStringList.count(); ++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 { + QVariantList variantList; + for (int j = 0; j < l.count(); ++j) + variantList.append(stringToVariant(l.at(j))); + return variantList; + } + } + } + return outStringList; +} + +QString QSettingsPrivate::variantToString(const QVariant &v) +{ + QString result; + + switch (v.type()) { + case QVariant::Invalid: + result = QLatin1String("@Invalid()"); + break; + + case QVariant::ByteArray: { + QByteArray a = v.toByteArray(); + result = QLatin1String("@ByteArray("); + result += QString::fromLatin1(a.constData(), a.size()); + result += QLatin1Char(')'); + break; + } + + case QVariant::String: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Int: + case QVariant::UInt: + case QVariant::Bool: + case QVariant::Double: + case QVariant::KeySequence: { + result = v.toString(); + if (result.startsWith(QLatin1Char('@'))) + result.prepend(QLatin1Char('@')); + break; + } +#ifndef QT_NO_GEOM_VARIANT + case QVariant::Rect: { + QRect r = qvariant_cast<QRect>(v); + result += QLatin1String("@Rect("); + result += QString::number(r.x()); + result += QLatin1Char(' '); + result += QString::number(r.y()); + result += QLatin1Char(' '); + result += QString::number(r.width()); + result += QLatin1Char(' '); + result += QString::number(r.height()); + result += QLatin1Char(')'); + break; + } + case QVariant::Size: { + QSize s = qvariant_cast<QSize>(v); + result += QLatin1String("@Size("); + result += QString::number(s.width()); + result += QLatin1Char(' '); + result += QString::number(s.height()); + result += QLatin1Char(')'); + break; + } + case QVariant::Point: { + QPoint p = qvariant_cast<QPoint>(v); + result += QLatin1String("@Point("); + result += QString::number(p.x()); + result += QLatin1Char(' '); + result += QString::number(p.y()); + result += QLatin1Char(')'); + break; + } +#endif // !QT_NO_GEOM_VARIANT + + default: { +#ifndef QT_NO_DATASTREAM + QByteArray a; + { + QDataStream s(&a, QIODevice::WriteOnly); + s.setVersion(QDataStream::Qt_4_0); + s << v; + } + + result = QLatin1String("@Variant("); + result += QString::fromLatin1(a.constData(), a.size()); + result += QLatin1Char(')'); +#else + Q_ASSERT(!"QSettings: Cannot save custom types without QDataStream support"); +#endif + break; + } + } + + return result; +} + + +QVariant QSettingsPrivate::stringToVariant(const QString &s) +{ + if (s.startsWith(QLatin1Char('@'))) { + if (s.endsWith(QLatin1Char(')'))) { + if (s.startsWith(QLatin1String("@ByteArray("))) { + return QVariant(s.toLatin1().mid(11, s.size() - 12)); + } else if (s.startsWith(QLatin1String("@Variant("))) { +#ifndef QT_NO_DATASTREAM + QByteArray a(s.toLatin1().mid(9)); + QDataStream stream(&a, QIODevice::ReadOnly); + stream.setVersion(QDataStream::Qt_4_0); + QVariant result; + stream >> result; + return result; +#else + Q_ASSERT(!"QSettings: Cannot load custom types without QDataStream support"); +#endif +#ifndef QT_NO_GEOM_VARIANT + } else if (s.startsWith(QLatin1String("@Rect("))) { + 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("))) { + 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("))) { + 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()")) { + return QVariant(); + } + + } + if (s.startsWith(QLatin1String("@@"))) + return QVariant(s.mid(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) { + 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 == '.') { + result += (char)ch; + } else if (ch <= 0xFF) { + result += '%'; + result += hexDigits[ch / 16]; + result += hexDigits[ch % 16]; + } else { + result += "%U"; + QByteArray hexCode; + for (int i = 0; i < 4; ++i) { + hexCode.prepend(hexDigits[ch % 16]); + ch >>= 4; + } + result += hexCode; + } + } +} + +bool QSettingsPrivate::iniUnescapedKey(const QByteArray &key, int from, int to, QString &result) +{ + bool lowercaseOnly = true; + int i = from; + result.reserve(result.length() + (to - from)); + while (i < to) { + int ch = (uchar)key.at(i); + + if (ch == '\\') { + result += QLatin1Char('/'); + ++i; + continue; + } + + if (ch != '%' || i == to - 1) { + if (uint(ch - 'A') <= 'Z' - 'A') // only for ASCII + lowercaseOnly = false; + result += QLatin1Char(ch); + ++i; + continue; + } + + int numDigits = 2; + int firstDigitPos = i + 1; + + ch = key.at(i + 1); + if (ch == 'U') { + ++firstDigitPos; + numDigits = 4; + } + + if (firstDigitPos + numDigits > to) { + result += QLatin1Char('%'); + // ### missing U + ++i; + continue; + } + + bool ok; + ch = key.mid(firstDigitPos, numDigits).toInt(&ok, 16); + if (!ok) { + result += QLatin1Char('%'); + // ### missing U + ++i; + continue; + } + + QChar qch(ch); + if (qch.isUpper()) + lowercaseOnly = false; + result += qch; + i = firstDigitPos + numDigits; + } + return lowercaseOnly; +} + +void QSettingsPrivate::iniEscapedString(const QString &str, QByteArray &result, QTextCodec *codec) +{ + bool needsQuotes = false; + bool escapeNextIfDigit = false; + int i; + int startPos = result.size(); + + result.reserve(startPos + str.size() * 3 / 2); + for (i = 0; i < str.size(); ++i) { + uint ch = str.at(i).unicode(); + if (ch == ';' || ch == ',' || ch == '=') + needsQuotes = true; + + if (escapeNextIfDigit + && ((ch >= '0' && ch <= '9') + || (ch >= 'a' && ch <= 'f') + || (ch >= 'A' && ch <= 'F'))) { + result += "\\x"; + result += QByteArray::number(ch, 16); + continue; + } + + escapeNextIfDigit = false; + + switch (ch) { + case '\0': + result += "\\0"; + escapeNextIfDigit = true; + break; + case '\a': + result += "\\a"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + case '\v': + result += "\\v"; + break; + case '"': + case '\\': + result += '\\'; + result += (char)ch; + break; + default: + if (ch <= 0x1F || (ch >= 0x7F && !codec)) { + result += "\\x"; + result += QByteArray::number(ch, 16); + escapeNextIfDigit = true; +#ifndef QT_NO_TEXTCODEC + } else if (codec) { + // slow + result += codec->fromUnicode(str.at(i)); +#endif + } else { + result += (char)ch; + } + } + } + + if (needsQuotes + || (startPos < result.size() && (result.at(startPos) == ' ' + || result.at(result.size() - 1) == ' '))) { + result.insert(startPos, '"'); + result += '"'; + } +} + +inline static void iniChopTrailingSpaces(QString &str) +{ + int n = str.size() - 1; + QChar ch; + while (n >= 0 && ((ch = str.at(n)) == QLatin1Char(' ') || ch == QLatin1Char('\t'))) + str.truncate(n--); +} + +void QSettingsPrivate::iniEscapedStringList(const QStringList &strs, QByteArray &result, QTextCodec *codec) +{ + if (strs.isEmpty()) { + /* + We need to distinguish between empty lists and one-item + lists that contain an empty string. Ideally, we'd have a + @EmptyList() symbol but that would break compatibility + with Qt 4.0. @Invalid() stands for QVariant(), and + QVariant().toStringList() returns an empty QStringList, + so we're in good shape. + + ### Qt 5: Use a nicer syntax, e.g. @List, for variant lists + */ + result += "@Invalid()"; + } else { + for (int i = 0; i < strs.size(); ++i) { + if (i != 0) + result += ", "; + iniEscapedString(strs.at(i), result, codec); + } + } +} + +bool QSettingsPrivate::iniUnescapedStringList(const QByteArray &str, int from, int to, + QString &stringResult, QStringList &stringListResult, + QTextCodec *codec) +{ + static const char escapeCodes[][2] = + { + { 'a', '\a' }, + { 'b', '\b' }, + { 'f', '\f' }, + { 'n', '\n' }, + { 'r', '\r' }, + { 't', '\t' }, + { 'v', '\v' }, + { '"', '"' }, + { '?', '?' }, + { '\'', '\'' }, + { '\\', '\\' } + }; + static const int numEscapeCodes = sizeof(escapeCodes) / sizeof(escapeCodes[0]); + + bool isStringList = false; + bool inQuotedString = false; + bool currentValueIsQuoted = false; + int escapeVal = 0; + int i = from; + char ch; + +StSkipSpaces: + while (i < to && ((ch = str.at(i)) == ' ' || ch == '\t')) + ++i; + // fallthrough + +StNormal: + while (i < to) { + switch (str.at(i)) { + case '\\': + ++i; + if (i >= to) + goto end; + + ch = str.at(i++); + for (int j = 0; j < numEscapeCodes; ++j) { + if (ch == escapeCodes[j][0]) { + stringResult += QLatin1Char(escapeCodes[j][1]); + goto StNormal; + } + } + + if (ch == 'x') { + escapeVal = 0; + + if (i >= to) + goto end; + + ch = str.at(i); + if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) + goto StHexEscape; + } else if (ch >= '0' && ch <= '7') { + escapeVal = ch - '0'; + goto StOctEscape; + } else if (ch == '\n' || ch == '\r') { + if (i < to) { + 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) + ++i; + } + } else { + // the character is skipped + } + break; + case '"': + ++i; + currentValueIsQuoted = true; + inQuotedString = !inQuotedString; + if (!inQuotedString) + goto StSkipSpaces; + break; + case ',': + if (!inQuotedString) { + if (!currentValueIsQuoted) + iniChopTrailingSpaces(stringResult); + if (!isStringList) { + isStringList = true; + stringListResult.clear(); + stringResult.squeeze(); + } + stringListResult.append(stringResult); + stringResult.clear(); + currentValueIsQuoted = false; + ++i; + goto StSkipSpaces; + } + // fallthrough + default: { + int j = i + 1; + while (j < to) { + ch = str.at(j); + if (ch == '\\' || ch == '"' || ch == ',') + break; + ++j; + } + +#ifndef QT_NO_TEXTCODEC + if (codec) { + stringResult += codec->toUnicode(str.constData() + i, j - i); + } else +#endif + { + int n = stringResult.size(); + stringResult.resize(n + (j - i)); + QChar *resultData = stringResult.data() + n; + for (int k = i; k < j; ++k) + *resultData++ = QLatin1Char(str.at(k)); + } + i = j; + } + } + } + goto end; + +StHexEscape: + if (i >= to) { + stringResult += QChar(escapeVal); + goto end; + } + + ch = str.at(i); + if (ch >= 'a') + ch -= 'a' - 'A'; + if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) { + escapeVal <<= 4; + escapeVal += strchr(hexDigits, ch) - hexDigits; + ++i; + goto StHexEscape; + } else { + stringResult += QChar(escapeVal); + goto StNormal; + } + +StOctEscape: + if (i >= to) { + stringResult += QChar(escapeVal); + goto end; + } + + ch = str.at(i); + if (ch >= '0' && ch <= '7') { + escapeVal <<= 3; + escapeVal += ch - '0'; + ++i; + goto StOctEscape; + } else { + stringResult += QChar(escapeVal); + goto StNormal; + } + +end: + if (!currentValueIsQuoted) + iniChopTrailingSpaces(stringResult); + if (isStringList) + stringListResult.append(stringResult); + return isStringList; +} + +QStringList QSettingsPrivate::splitArgs(const QString &s, int idx) +{ + int l = s.length(); + Q_ASSERT(l > 0); + Q_ASSERT(s.at(idx) == QLatin1Char('(')); + Q_ASSERT(s.at(l - 1) == QLatin1Char(')')); + + QStringList result; + QString item; + + for (++idx; idx < l; ++idx) { + QChar c = s.at(idx); + if (c == QLatin1Char(')')) { + Q_ASSERT(idx == l - 1); + result.append(item); + } else if (c == QLatin1Char(' ')) { + result.append(item); + item.clear(); + } else { + item.append(c); + } + } + + return result; +} + +// ************************************************************************ +// QConfFileSettingsPrivate + +/* + If we don't have the permission to read the file, returns false. + If the file doesn't exist, returns true. +*/ +static bool checkAccess(const QString &name) +{ + QFileInfo fileInfo(name); + + if (fileInfo.exists()) { + QFile file(name); + // if the file exists but we can't open it, report an error + return file.open(QFile::ReadOnly); + } else { + return true; + } +} + +void QConfFileSettingsPrivate::initFormat() +{ + extension = (format == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini"); + readFunc = 0; + writeFunc = 0; +#if defined(Q_OS_MAC) + caseSensitivity = (format == QSettings::NativeFormat) ? Qt::CaseSensitive : Qt::CaseInsensitive; +#else + caseSensitivity = IniCaseSensitivity; +#endif + + if (format > QSettings::IniFormat) { + QMutexLocker locker(globalMutex()); + const CustomFormatVector *customFormatVector = customFormatVectorFunc(); + + int i = (int)format - (int)QSettings::CustomFormat1; + if (i >= 0 && i < customFormatVector->size()) { + QConfFileCustomFormat info = customFormatVector->at(i); + extension = info.extension; + readFunc = info.readFunc; + writeFunc = info.writeFunc; + caseSensitivity = info.caseSensitivity; + } + } +} + +void QConfFileSettingsPrivate::initAccess() +{ + bool readAccess = false; + if (confFiles[spec]) { + readAccess = checkAccess(confFiles[spec]->name); + if (format > QSettings::IniFormat) { + if (!readFunc) + readAccess = false; + } + } + + if (!readAccess) + setStatus(QSettings::AccessError); + + sync(); // loads the files the first time +} + +#ifdef Q_OS_WIN +static QString windowsConfigPath(int type) +{ + QString result; + +#ifndef QT_NO_QOBJECT + // We can't use QLibrary if there is QT_NO_QOBJECT is defined + // This only happens when bootstrapping qmake. +#ifndef Q_OS_WINCE + QLibrary library(QLatin1String("shell32")); + QT_WA( { + typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPTSTR, int, BOOL); + GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathW"); + if (SHGetSpecialFolderPath) { + TCHAR path[MAX_PATH]; + SHGetSpecialFolderPath(0, path, type, FALSE); + result = QString::fromUtf16((ushort*)path); + } + } , { + typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, char*, int, BOOL); + GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathA"); + if (SHGetSpecialFolderPath) { + char path[MAX_PATH]; + SHGetSpecialFolderPath(0, path, type, FALSE); + result = QString::fromLocal8Bit(path); + } + } ); +#else + QLibrary library(QLatin1String("coredll")); + typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPTSTR, int, BOOL); + GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPath"); + if (SHGetSpecialFolderPath) { + wchar_t path[MAX_PATH]; + SHGetSpecialFolderPath(0, path, type, FALSE); + result = QString::fromUtf16((ushort*)path); + } +#endif // Q_OS_WINCE + +#endif // QT_NO_QOBJECT + + if (result.isEmpty()) { + switch (type) { +#ifndef Q_OS_WINCE + case CSIDL_COMMON_APPDATA: + result = QLatin1String("C:\\temp\\qt-common"); + break; + case CSIDL_APPDATA: + result = QLatin1String("C:\\temp\\qt-user"); + break; +#else + case CSIDL_COMMON_APPDATA: + result = QLatin1String("\\Temp\\qt-common"); + break; + case CSIDL_APPDATA: + result = QLatin1String("\\Temp\\qt-user"); + break; +#endif + default: + ; + } + } + + return result; +} +#endif // Q_OS_WIN + +static inline int pathHashKey(QSettings::Format format, QSettings::Scope scope) +{ + return int((uint(format) << 1) | uint(scope == QSettings::SystemScope)); +} + +static QString getPath(QSettings::Format format, QSettings::Scope scope) +{ + Q_ASSERT((int)QSettings::NativeFormat == 0); + Q_ASSERT((int)QSettings::IniFormat == 1); + + QString homePath = QDir::homePath(); + QString systemPath; + + globalMutex()->lock(); + PathHash *pathHash = pathHashFunc(); + bool loadSystemPath = pathHash->isEmpty(); + globalMutex()->unlock(); + + if (loadSystemPath) { + /* + QLibraryInfo::location() uses QSettings, so in order to + avoid a dead-lock, we can't hold the global mutex while + calling it. + */ + systemPath = QLibraryInfo::location(QLibraryInfo::SettingsPath); + systemPath += QLatin1Char('/'); + } + + QMutexLocker locker(globalMutex()); + if (pathHash->isEmpty()) { + /* + Lazy initialization of pathHash. We initialize the + IniFormat paths and (on Unix) the NativeFormat paths. + (The NativeFormat paths are not configurable for the + Windows registry and the Mac CFPreferences.) + */ +#ifdef Q_OS_WIN + pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), + windowsConfigPath(CSIDL_APPDATA) + QDir::separator()); + pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), + windowsConfigPath(CSIDL_COMMON_APPDATA) + QDir::separator()); +#else + QString userPath; + char *env = getenv("XDG_CONFIG_HOME"); + if (env == 0) { + userPath = homePath; + userPath += QLatin1Char('/'); +#ifdef Q_WS_QWS + userPath += QLatin1String("Settings"); +#else + userPath += QLatin1String(".config"); +#endif + } else if (*env == '/') { + userPath = QLatin1String(env); + } else { + userPath = homePath; + userPath += QLatin1Char('/'); + userPath += QLatin1String(env); + } + userPath += QLatin1Char('/'); + + pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), userPath); + pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), systemPath); +#ifndef Q_OS_MAC + pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), userPath); + pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), systemPath); +#endif +#endif + } + + QString result = pathHash->value(pathHashKey(format, scope)); + if (!result.isEmpty()) + return result; + + // fall back on INI path + return pathHash->value(pathHashKey(QSettings::IniFormat, scope)); +} + +QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, + QSettings::Scope scope, + const QString &organization, + const QString &application) + : QSettingsPrivate(format, scope, organization, application), + nextPosition(0x40000000) // big positive number +{ + int i; + initFormat(); + + for (i = 0; i < NumConfFiles; ++i) + confFiles[i] = 0; + + QString org = organization; + if (org.isEmpty()) { + setStatus(QSettings::AccessError); + org = QLatin1String("Unknown Organization"); + } + + QString appFile = org + QDir::separator() + application + extension; + QString orgFile = org + extension; + + if (scope == QSettings::UserScope) { + QString userPath = getPath(format, QSettings::UserScope); + if (!application.isEmpty()) + confFiles[F_User | F_Application] = QConfFile::fromName(userPath + appFile, true); + confFiles[F_User | F_Organization] = QConfFile::fromName(userPath + orgFile, true); + } + + QString systemPath = getPath(format, QSettings::SystemScope); + if (!application.isEmpty()) + confFiles[F_System | F_Application] = QConfFile::fromName(systemPath + appFile, false); + confFiles[F_System | F_Organization] = QConfFile::fromName(systemPath + orgFile, false); + + for (i = 0; i < NumConfFiles; ++i) { + if (confFiles[i]) { + spec = i; + break; + } + } + + initAccess(); +} + +QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName, + QSettings::Format format) + : QSettingsPrivate(format), + nextPosition(0x40000000) // big positive number +{ + initFormat(); + + confFiles[0] = QConfFile::fromName(fileName, true); + for (int i = 1; i < NumConfFiles; ++i) + confFiles[i] = 0; + + initAccess(); +} + +QConfFileSettingsPrivate::~QConfFileSettingsPrivate() +{ + QMutexLocker locker(globalMutex()); + ConfFileHash *usedHash = usedHashFunc(); + ConfFileCache *unusedCache = unusedCacheFunc(); + + for (int i = 0; i < NumConfFiles; ++i) { + if (confFiles[i] && !confFiles[i]->ref.deref()) { + if (usedHash) + usedHash->remove(confFiles[i]->name); + + if (confFiles[i]->size == 0) { + delete confFiles[i]; + } else if (unusedCache) { + // compute a better size? + unusedCache->insert(confFiles[i]->name, confFiles[i], + 10 + (confFiles[i]->originalKeys.size() / 4)); + } + } + } +} + +void QConfFileSettingsPrivate::remove(const QString &key) +{ + QConfFile *confFile = confFiles[spec]; + if (!confFile) + return; + + QSettingsKey theKey(key, caseSensitivity); + QSettingsKey prefix(key + QLatin1Char('/'), caseSensitivity); + QMutexLocker locker(&confFile->mutex); + + ensureSectionParsed(confFile, theKey); + ensureSectionParsed(confFile, prefix); + + ParsedSettingsMap::iterator 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); + while (j != confFile->originalKeys.constEnd() && j.key().startsWith(prefix)) { + confFile->removedKeys.insert(j.key(), QVariant()); + ++j; + } + if (confFile->originalKeys.contains(theKey)) + confFile->removedKeys.insert(theKey, QVariant()); +} + +void QConfFileSettingsPrivate::set(const QString &key, const QVariant &value) +{ + QConfFile *confFile = confFiles[spec]; + if (!confFile) + return; + + QSettingsKey theKey(key, caseSensitivity, nextPosition++); + QMutexLocker locker(&confFile->mutex); + confFile->removedKeys.remove(theKey); + confFile->addedKeys.insert(theKey, value); +} + +bool QConfFileSettingsPrivate::get(const QString &key, QVariant *value) const +{ + QSettingsKey theKey(key, caseSensitivity); + ParsedSettingsMap::const_iterator j; + bool found = false; + + for (int i = 0; i < NumConfFiles; ++i) { + if (QConfFile *confFile = confFiles[i]) { + QMutexLocker locker(&confFile->mutex); + + if (!confFile->addedKeys.isEmpty()) { + j = confFile->addedKeys.constFind(theKey); + found = (j != confFile->addedKeys.constEnd()); + } + if (!found) { + ensureSectionParsed(confFile, theKey); + j = confFile->originalKeys.constFind(theKey); + found = (j != confFile->originalKeys.constEnd() + && !confFile->removedKeys.contains(theKey)); + } + + if (found && value) + *value = *j; + + if (found) + return true; + if (!fallbacks) + break; + } + } + return false; +} + +QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec spec) const +{ + QMap<QString, QString> result; + ParsedSettingsMap::const_iterator j; + + QSettingsKey thePrefix(prefix, caseSensitivity); + int startPos = prefix.size(); + + for (int i = 0; i < NumConfFiles; ++i) { + if (QConfFile *confFile = confFiles[i]) { + QMutexLocker locker(&confFile->mutex); + + if (thePrefix.isEmpty()) { + ensureAllSectionsParsed(confFile); + } 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(j.key().originalCaseKey().mid(startPos), spec, result); + ++j; + } + + j = const_cast<const ParsedSettingsMap *>( + &confFile->addedKeys)->lowerBound(thePrefix); + while (j != confFile->addedKeys.constEnd() && j.key().startsWith(thePrefix)) { + processChild(j.key().originalCaseKey().mid(startPos), spec, result); + ++j; + } + + if (!fallbacks) + break; + } + } + return result.keys(); +} + +void QConfFileSettingsPrivate::clear() +{ + QConfFile *confFile = confFiles[spec]; + if (!confFile) + return; + + QMutexLocker locker(&confFile->mutex); + ensureAllSectionsParsed(confFile); + confFile->addedKeys.clear(); + confFile->removedKeys = confFile->originalKeys; +} + +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 (int i = 0; i < NumConfFiles; ++i) { + QConfFile *confFile = confFiles[i]; + if (confFile) { + QMutexLocker locker(&confFile->mutex); + syncConfFile(i); + } + } +} + +void QConfFileSettingsPrivate::flush() +{ + sync(); +} + +QString QConfFileSettingsPrivate::fileName() const +{ + QConfFile *confFile = confFiles[spec]; + if (!confFile) + return QString(); + return confFile->name; +} + +bool QConfFileSettingsPrivate::isWritable() const +{ + if (format > QSettings::IniFormat && !writeFunc) + return false; + + QConfFile *confFile = confFiles[spec]; + if (!confFile) + return false; + + return confFile->isWritable(); +} + +void QConfFileSettingsPrivate::syncConfFile(int confFileNo) +{ + QConfFile *confFile = confFiles[confFileNo]; + bool readOnly = confFile->addedKeys.isEmpty() && confFile->removedKeys.isEmpty(); + bool ok; + + /* + We can often optimize the read-only case, if the file on disk + hasn't changed. + */ + if (readOnly) { + QFileInfo fileInfo(confFile->name); + if (confFile->size == fileInfo.size() && confFile->timeStamp == fileInfo.lastModified()) + return; + } + + /* + Open the configuration file and try to use it using a named + semaphore on Windows and an advisory lock on Unix-based + systems. This protect us against other QSettings instances + trying to access the same file from other threads or + processes. + + As it stands now, the locking mechanism doesn't work for + .plist files. + */ + QFile file(confFile->name); + bool createFile = !file.exists(); + if (!readOnly && confFile->isWritable()) + file.open(QFile::ReadWrite); + if (!file.isOpen()) + file.open(QFile::ReadOnly); + +#ifdef Q_OS_WIN + HANDLE readSemaphore = 0; + HANDLE writeSemaphore = 0; + static const int FileLockSemMax = 50; + int numReadLocks = readOnly ? 1 : FileLockSemMax; + + if (file.isOpen()) { + // Acquire the write lock if we will be writing + if (!readOnly) { + QString writeSemName = QLatin1String("QSettingsWriteSem "); + writeSemName.append(file.fileName()); + + QT_WA( { + writeSemaphore = CreateSemaphoreW(0, 1, 1, reinterpret_cast<const wchar_t *>(writeSemName.utf16())); + } , { + writeSemaphore = CreateSemaphoreA(0, 1, 1, writeSemName.toLocal8Bit()); + } ); + + if (writeSemaphore) { + WaitForSingleObject(writeSemaphore, INFINITE); + } else { + setStatus(QSettings::AccessError); + return; + } + } + + // Acquire all the read locks if we will be writing, to make sure nobody + // reads while we're writing. If we are only reading, acquire a single + // read lock. + QString readSemName(QLatin1String("QSettingsReadSem ")); + readSemName.append(file.fileName()); + + QT_WA( { + readSemaphore = CreateSemaphoreW(0, FileLockSemMax, FileLockSemMax, reinterpret_cast<const wchar_t *>(readSemName.utf16())); + } , { + readSemaphore = CreateSemaphoreA(0, FileLockSemMax, FileLockSemMax, readSemName.toLocal8Bit()); + } ); + + if (readSemaphore) { + for (int i = 0; i < numReadLocks; ++i) + WaitForSingleObject(readSemaphore, INFINITE); + } else { + setStatus(QSettings::AccessError); + if (writeSemaphore != 0) { + ReleaseSemaphore(writeSemaphore, 1, 0); + CloseHandle(writeSemaphore); + } + return; + } + } +#else + if (file.isOpen()) + unixLock(file.handle(), readOnly ? F_RDLCK : F_WRLCK); +#endif + + // If we have created the file, apply the file perms + if (file.isOpen()) { + if (createFile) { + QFile::Permissions perms = file.permissions() | QFile::ReadOwner | QFile::WriteOwner; + if (!confFile->userPerms) + perms |= QFile::ReadGroup | QFile::ReadOther; + file.setPermissions(perms); + } + } + + /* + We hold the lock. Let's reread the file if it has changed + since last time we read it. + */ + QFileInfo fileInfo(confFile->name); + bool mustReadFile = true; + + if (!readOnly) + mustReadFile = (confFile->size != fileInfo.size() + || (confFile->size != 0 && confFile->timeStamp != fileInfo.lastModified())); + + if (mustReadFile) { + confFile->unparsedIniSections.clear(); + confFile->originalKeys.clear(); + + /* + Files that we can't read (because of permissions or + because they don't exist) are treated as empty files. + */ + if (file.isReadable() && fileInfo.size() != 0) { +#ifdef Q_OS_MAC + if (format == QSettings::NativeFormat) { + ok = readPlistFile(confFile->name, &confFile->originalKeys); + } else +#endif + { + if (format <= QSettings::IniFormat) { + QByteArray data = file.readAll(); + ok = readIniFile(data, &confFile->unparsedIniSections); + } else { + if (readFunc) { + QSettings::SettingsMap tempNewKeys; + ok = readFunc(file, tempNewKeys); + + if (ok) { + QSettings::SettingsMap::const_iterator i = tempNewKeys.constBegin(); + while (i != tempNewKeys.constEnd()) { + confFile->originalKeys.insert(QSettingsKey(i.key(), + caseSensitivity), + i.value()); + ++i; + } + } + } else { + ok = false; + } + } + } + + if (!ok) + setStatus(QSettings::FormatError); + } + + confFile->size = fileInfo.size(); + confFile->timeStamp = fileInfo.lastModified(); + } + + /* + We also need to save the file. We still hold the file lock, + so everything is under control. + */ + if (!readOnly) { + ensureAllSectionsParsed(confFile); + ParsedSettingsMap mergedKeys = confFile->mergedKeyMap(); + + if (file.isWritable()) { +#ifdef Q_OS_MAC + if (format == QSettings::NativeFormat) { + ok = writePlistFile(confFile->name, mergedKeys); + } else +#endif + { + file.seek(0); + file.resize(0); + + if (format <= QSettings::IniFormat) { + ok = writeIniFile(file, mergedKeys); + if (!ok) { + // try to restore old data; might work if the disk was full and the new data + // was larger than the old data + file.seek(0); + file.resize(0); + writeIniFile(file, confFile->originalKeys); + } + } else { + if (writeFunc) { + QSettings::SettingsMap tempOriginalKeys; + + ParsedSettingsMap::const_iterator i = mergedKeys.constBegin(); + while (i != mergedKeys.constEnd()) { + tempOriginalKeys.insert(i.key(), i.value()); + ++i; + } + ok = writeFunc(file, tempOriginalKeys); + } else { + ok = false; + } + } + } + } else { + ok = false; + } + + if (ok) { + confFile->unparsedIniSections.clear(); + confFile->originalKeys = mergedKeys; + confFile->addedKeys.clear(); + confFile->removedKeys.clear(); + + QFileInfo fileInfo(confFile->name); + confFile->size = fileInfo.size(); + confFile->timeStamp = fileInfo.lastModified(); + } else { + setStatus(QSettings::AccessError); + } + } + + /* + Release the file lock. + */ +#ifdef Q_OS_WIN + if (readSemaphore != 0) { + ReleaseSemaphore(readSemaphore, numReadLocks, 0); + CloseHandle(readSemaphore); + } + if (writeSemaphore != 0) { + ReleaseSemaphore(writeSemaphore, 1, 0); + CloseHandle(writeSemaphore); + } +#endif +} + +enum { Space = 0x1, Special = 0x2 }; + +static const char charTraits[256] = +{ + // Space: '\t', '\n', '\r', ' ' + // Special: '\n', '\r', '"', ';', '=', '\\' + + 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Space, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, Special, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 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) +{ + int dataLen = data.length(); + bool inQuotes = false; + + equalsPos = -1; + + lineStart = dataPos; + while (lineStart < dataLen && (charTraits[uint(uchar(data.at(lineStart)))] & Space)) + ++lineStart; + + int i = lineStart; + while (i < dataLen) { + while (!(charTraits[uint(uchar(data.at(i)))] & Special)) { + if (++i == dataLen) + goto break_out_of_outer_loop; + } + + char ch = data.at(i++); + if (ch == '=') { + if (!inQuotes && equalsPos == -1) + equalsPos = i - 1; + } else if (ch == '\n' || ch == '\r') { + if (i == lineStart + 1) { + ++lineStart; + } else if (!inQuotes) { + --i; + goto break_out_of_outer_loop; + } + } else if (ch == '\\') { + if (i < dataLen) { + char ch = data.at(i++); + if (i < dataLen) { + char ch2 = data.at(i); + // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files + if ((ch == '\n' && ch2 == '\r') || (ch == '\r' && ch2 == '\n')) + ++i; + } + } + } else if (ch == '"') { + inQuotes = !inQuotes; + } else { + Q_ASSERT(ch == ';'); + + if (i == lineStart + 1) { + char ch; + while (i < dataLen && ((ch = data.at(i) != '\n') && ch != '\r')) + ++i; + lineStart = i; + } else if (!inQuotes) { + --i; + goto break_out_of_outer_loop; + } + } + } + +break_out_of_outer_loop: + dataPos = i; + lineLen = i - lineStart; + return lineLen > 0; +} + +/* + Returns false on parse error. However, as many keys are read as + 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, + UnparsedSettingsMap *unparsedIniSections) +{ +#define FLUSH_CURRENT_SECTION() \ + { \ + QByteArray §ionData = (*unparsedIniSections)[QSettingsKey(currentSection, \ + IniCaseSensitivity, \ + sectionPosition)]; \ + if (!sectionData.isEmpty()) \ + sectionData.append('\n'); \ + sectionData += data.mid(currentSectionStart, lineStart - currentSectionStart); \ + sectionPosition = ++position; \ + } + + QString currentSection; + int currentSectionStart = 0; + int dataPos = 0; + int lineStart; + int lineLen; + int equalsPos; + int position = 0; + int sectionPosition = 0; + bool ok = true; + + while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) { + char ch = data.at(lineStart); + if (ch == '[') { + FLUSH_CURRENT_SECTION(); + + // this is a section + QByteArray iniSection; + int idx = data.indexOf(']', lineStart); + if (idx == -1 || idx >= lineStart + lineLen) { + ok = false; + iniSection = data.mid(lineStart + 1, lineLen - 1); + } else { + iniSection = data.mid(lineStart + 1, idx - lineStart - 1); + } + + iniSection = iniSection.trimmed(); + + if (qstricmp(iniSection, "general") == 0) { + currentSection.clear(); + } else { + if (qstricmp(iniSection, "%general") == 0) { + currentSection = QLatin1String(iniSection.constData() + 1); + } else { + currentSection.clear(); + iniUnescapedKey(iniSection, 0, iniSection.size(), currentSection); + } + currentSection += QLatin1Char('/'); + } + currentSectionStart = dataPos; + } + ++position; + } + + Q_ASSERT(lineStart == data.length()); + FLUSH_CURRENT_SECTION(); + + return ok; + +#undef FLUSH_CURRENT_SECTION +} + +bool QConfFileSettingsPrivate::readIniSection(const QSettingsKey §ion, const QByteArray &data, + ParsedSettingsMap *settingsMap, QTextCodec *codec) +{ + QStringList strListValue; + bool sectionIsLowercase = (section == section.originalCaseKey()); + int equalsPos; + + bool ok = true; + int dataPos = 0; + int lineStart; + int lineLen; + int position = section.originalKeyPosition(); + + while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) { + char ch = data.at(lineStart); + Q_ASSERT(ch != '['); + + if (equalsPos == -1) { + if (ch != ';') + ok = false; + continue; + } + + int keyEnd = equalsPos; + while (keyEnd > lineStart && ((ch = data.at(keyEnd - 1)) == ' ' || ch == '\t')) + --keyEnd; + int valueStart = equalsPos + 1; + + QString key = section.originalCaseKey(); + bool keyIsLowercase = (iniUnescapedKey(data, lineStart, keyEnd, key) && sectionIsLowercase); + + QString strValue; + strValue.reserve(lineLen - (valueStart - lineStart)); + bool isStringList = iniUnescapedStringList(data, valueStart, lineStart + lineLen, + strValue, strListValue, codec); + QVariant variant; + if (isStringList) { + variant = stringListToVariantList(strListValue); + } else { + variant = 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); + ++position; + } + + return ok; +} + +class QSettingsIniKey : public QString +{ +public: + inline QSettingsIniKey() : position(-1) {} + inline QSettingsIniKey(const QString &str, int pos = -1) : QString(str), position(pos) {} + + int position; +}; + +static bool operator<(const QSettingsIniKey &k1, const QSettingsIniKey &k2) +{ + if (k1.position != k2.position) + return k1.position < k2.position; + return static_cast<const QString &>(k1) < static_cast<const QString &>(k2); +} + +typedef QMap<QSettingsIniKey, QVariant> IniKeyMap; + +struct QSettingsIniSection +{ + int position; + IniKeyMap keyMap; + + inline QSettingsIniSection() : position(-1) {} +}; + +typedef QMap<QString, QSettingsIniSection> IniMap; + +/* + This would be more straightforward if we didn't try to remember the original + key order in the .ini file, but we do. +*/ +bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSettingsMap &map) +{ + IniMap iniMap; + IniMap::const_iterator i; + +#ifdef Q_OS_WIN + const char * const eol = "\r\n"; +#else + const char eol = '\n'; +#endif + + for (ParsedSettingsMap::const_iterator j = map.constBegin(); j != map.constEnd(); ++j) { + QString section; + QSettingsIniKey key(j.key().originalCaseKey(), j.key().originalKeyPosition()); + int slashPos; + + if ((slashPos = key.indexOf(QLatin1Char('/'))) != -1) { + section = key.left(slashPos); + key.remove(0, slashPos + 1); + } + + QSettingsIniSection &iniSection = iniMap[section]; + + // -1 means infinity + if (uint(key.position) < uint(iniSection.position)) + iniSection.position = key.position; + iniSection.keyMap[key] = j.value(); + } + + const int sectionCount = iniMap.size(); + QVector<QSettingsIniKey> sections; + sections.reserve(sectionCount); + for (i = iniMap.constBegin(); i != iniMap.constEnd(); ++i) + sections.append(QSettingsIniKey(i.key(), i.value().position)); + qSort(sections); + + bool writeError = false; + for (int j = 0; !writeError && j < sectionCount; ++j) { + i = iniMap.constFind(sections.at(j)); + Q_ASSERT(i != iniMap.constEnd()); + + QByteArray realSection; + + iniEscapedKey(i.key(), realSection); + + if (realSection.isEmpty()) { + realSection = "[General]"; + } else if (qstricmp(realSection, "general") == 0) { + realSection = "[%General]"; + } else { + realSection.prepend('['); + realSection.append(']'); + } + + if (j != 0) + realSection.prepend(eol); + realSection += eol; + + device.write(realSection); + + const IniKeyMap &ents = i.value().keyMap; + for (IniKeyMap::const_iterator j = ents.constBegin(); j != ents.constEnd(); ++j) { + QByteArray block; + iniEscapedKey(j.key(), block); + block += '='; + + const QVariant &value = j.value(); + + /* + The size() != 1 trick is necessary because + QVariant(QString("foo")).toList() returns an empty + list, not a list containing "foo". + */ + if (value.type() == QVariant::StringList + || (value.type() == QVariant::List && value.toList().size() != 1)) { + iniEscapedStringList(variantListToStringList(value.toList()), block, iniCodec); + } else { + iniEscapedString(variantToString(value), block, iniCodec); + } + block += eol; + if (device.write(block) == -1) { + writeError = true; + break; + } + } + } + return !writeError; +} + +void QConfFileSettingsPrivate::ensureAllSectionsParsed(QConfFile *confFile) const +{ + UnparsedSettingsMap::const_iterator i = confFile->unparsedIniSections.constBegin(); + const UnparsedSettingsMap::const_iterator end = confFile->unparsedIniSections.constEnd(); + + for (; i != end; ++i) { + if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys, iniCodec)) + setStatus(QSettings::FormatError); + } + confFile->unparsedIniSections.clear(); +} + +void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, + const QSettingsKey &key) const +{ + if (confFile->unparsedIniSections.isEmpty()) + return; + + UnparsedSettingsMap::iterator i; + + int indexOfSlash = key.indexOf(QLatin1Char('/')); + if (indexOfSlash != -1) { + i = confFile->unparsedIniSections.upperBound(key); + if (i == confFile->unparsedIniSections.begin()) + return; + --i; + if (i.key().isEmpty() || !key.startsWith(i.key())) + return; + } else { + i = confFile->unparsedIniSections.begin(); + if (i == confFile->unparsedIniSections.end() || !i.key().isEmpty()) + return; + } + + if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys, iniCodec)) + setStatus(QSettings::FormatError); + confFile->unparsedIniSections.erase(i); +} + +/*! + \class QSettings + \brief The QSettings class provides persistent platform-independent application settings. + + \ingroup io + \ingroup misc + \mainclass + \reentrant + + Users normally expect an application to remember its settings + (window sizes and positions, options, etc.) across sessions. This + information is often stored in the system registry on Windows, + and in XML preferences files on Mac OS X. On Unix systems, in the + absence of a standard, many applications (including the KDE + applications) use INI text files. + + QSettings is an abstraction around these technologies, enabling + you to save and restore application settings in a portable + manner. It also supports \l{registerFormat()}{custom storage + formats}. + + QSettings's API is based on QVariant, allowing you to save + most value-based types, such as QString, QRect, and QImage, + with the minimum of effort. + + If all you need is a non-persistent memory-based structure, + consider using QMap<QString, QVariant> instead. + + \tableofcontents section1 + + \section1 Basic Usage + + When creating a QSettings object, you must pass the name of your + company or organization as well as the name of your application. + For example, if your product is called Star Runner and your + company is called MySoft, you would construct the QSettings + object as follows: + + \snippet doc/src/snippets/settings/settings.cpp 0 + + QSettings objects can be created either on the stack or on + the heap (i.e. using \c new). Constructing and destroying a + QSettings object is very fast. + + If you use QSettings from many places in your application, you + might want to specify the organization name and the application + name using QCoreApplication::setOrganizationName() and + QCoreApplication::setApplicationName(), and then use the default + QSettings constructor: + + \snippet doc/src/snippets/settings/settings.cpp 1 + \snippet doc/src/snippets/settings/settings.cpp 2 + \snippet doc/src/snippets/settings/settings.cpp 3 + \dots + \snippet doc/src/snippets/settings/settings.cpp 4 + + (Here, we also specify the organization's Internet domain. When + the Internet domain is set, it is used on Mac OS X instead of the + organization name, since Mac OS X applications conventionally use + Internet domains to identify themselves. If no domain is set, a + fake domain is derived from the organization name. See the + \l{Platform-Specific Notes} below for details.) + + QSettings stores settings. Each setting consists of a QString + that specifies the setting's name (the \e key) and a QVariant + that stores the data associated with the key. To write a setting, + use setValue(). For example: + + \snippet doc/src/snippets/settings/settings.cpp 5 + + If there already exists a setting with the same key, the existing + value is overwritten by the new value. For efficiency, the + changes may not be saved to permanent storage immediately. (You + can always call sync() to commit your changes.) + + You can get a setting's value back using value(): + + \snippet doc/src/snippets/settings/settings.cpp 6 + + If there is no setting with the specified name, QSettings + returns a null QVariant (which can be converted to the integer 0). + You can specify another default value by passing a second + argument to value(): + + \snippet doc/src/snippets/settings/settings.cpp 7 + + To test whether a given key exists, call contains(). To remove + the setting associated with a key, call remove(). To obtain the + list of all keys, call allKeys(). To remove all keys, call + clear(). + + \section1 QVariant and GUI Types + + Because QVariant is part of the \l QtCore library, it cannot provide + conversion functions to data types such as QColor, QImage, and + QPixmap, which are part of \l QtGui. In other words, there is no + \c toColor(), \c toImage(), or \c toPixmap() functions in QVariant. + + Instead, you can use the QVariant::value() or the qVariantValue() + template function. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 0 + + The inverse conversion (e.g., from QColor to QVariant) is + automatic for all data types supported by QVariant, including + GUI-related types: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 1 + + Custom types registered using qRegisterMetaType() and + qRegisterMetaTypeStreamOperators() can be stored using QSettings. + + \section1 Key Syntax + + Setting keys can contain any Unicode characters. The Windows + registry and INI files use case-insensitive keys, whereas the + Carbon Preferences API on Mac OS X uses case-sensitive keys. To + avoid portability problems, follow these two simple rules: + + \list 1 + \o Always refer to the same key using the same case. For example, + if you refer to a key as "text fonts" in one place in your + code, don't refer to it as "Text Fonts" somewhere else. + + \o Avoid key names that are identical except for the case. For + example, if you have a key called "MainWindow", don't try to + save another key as "mainwindow". + + \o Do not use slashes ('/' and '\\') in key names; the + backslash character is used to separate sub keys (see below). On + windows '\\' are converted by QSettings to '/', which makes + them identical. + \endlist + + You can form hierarchical keys using the '/' character as a + separator, similar to Unix file paths. For example: + + \snippet doc/src/snippets/settings/settings.cpp 8 + \snippet doc/src/snippets/settings/settings.cpp 9 + \snippet doc/src/snippets/settings/settings.cpp 10 + + If you want to save or restore many settings with the same + prefix, you can specify the prefix using beginGroup() and call + endGroup() at the end. Here's the same example again, but this + time using the group mechanism: + + \snippet doc/src/snippets/settings/settings.cpp 11 + \codeline + \snippet doc/src/snippets/settings/settings.cpp 12 + + If a group is set using beginGroup(), the behavior of most + functions changes consequently. Groups can be set recursively. + + In addition to groups, QSettings also supports an "array" + concept. See beginReadArray() and beginWriteArray() for details. + + \section1 Fallback Mechanism + + Let's assume that you have created a QSettings object with the + organization name MySoft and the application name Star Runner. + When you look up a value, up to four locations are searched in + that order: + + \list 1 + \o a user-specific location for the Star Runner application + \o a user-specific location for all applications by MySoft + \o a system-wide location for the Star Runner application + \o a system-wide location for all applications by MySoft + \endlist + + (See \l{Platform-Specific Notes} below for information on what + these locations are on the different platforms supported by Qt.) + + If a key cannot be found in the first location, the search goes + on in the second location, and so on. This enables you to store + system-wide or organization-wide settings and to override them on + a per-user or per-application basis. To turn off this mechanism, + call setFallbacksEnabled(false). + + Although keys from all four locations are available for reading, + only the first file (the user-specific location for the + application at hand) is accessible for writing. To write to any + of the other files, omit the application name and/or specify + QSettings::SystemScope (as opposed to QSettings::UserScope, the + default). + + Let's see with an example: + + \snippet doc/src/snippets/settings/settings.cpp 13 + \snippet doc/src/snippets/settings/settings.cpp 14 + + The table below summarizes which QSettings objects access + which location. "\bold{X}" means that the location is the main + location associated to the QSettings object and is used both + for reading and for writing; "o" means that the location is used + as a fallback when reading. + + \table + \header \o Locations \o \c{obj1} \o \c{obj2} \o \c{obj3} \o \c{obj4} + \row \o 1. User, Application \o \bold{X} \o \o \o + \row \o 2. User, Organization \o o \o \bold{X} \o \o + \row \o 3. System, Application \o o \o \o \bold{X} \o + \row \o 4. System, Organization \o o \o o \o o \o \bold{X} + \endtable + + The beauty of this mechanism is that it works on all platforms + supported by Qt and that it still gives you a lot of flexibility, + without requiring you to specify any file names or registry + paths. + + If you want to use INI files on all platforms instead of the + native API, you can pass QSettings::IniFormat as the first + argument to the QSettings constructor, followed by the scope, the + organization name, and the application name: + + \snippet doc/src/snippets/settings/settings.cpp 15 + + The \l{tools/settingseditor}{Settings Editor} example lets you + experiment with different settings location and with fallbacks + turned on or off. + + \section1 Restoring the State of a GUI Application + + QSettings is often used to store the state of a GUI + application. The following example illustrates how to use QSettings + to save and restore the geometry of an application's main window. + + \snippet doc/src/snippets/settings/settings.cpp 16 + \codeline + \snippet doc/src/snippets/settings/settings.cpp 17 + + See \l{Window Geometry} for a discussion on why it is better to + call QWidget::resize() and QWidget::move() rather than QWidget::setGeometry() + to restore a window's geometry. + + The \c readSettings() and \c writeSettings() functions must be + called from the main window's constructor and close event handler + as follows: + + \snippet doc/src/snippets/settings/settings.cpp 18 + \dots + \snippet doc/src/snippets/settings/settings.cpp 19 + \snippet doc/src/snippets/settings/settings.cpp 20 + \codeline + \snippet doc/src/snippets/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 + distinct QSettings object in different threads + simultaneously. This guarantee stands even when the QSettings + objects refer to the same files on disk (or to the same entries + in the system registry). If a setting is modified through one + QSettings object, the change will immediately be visible in + any other QSettings objects that operate on the same location + and that live in the same process. + + QSettings can safely be used from different processes (which can + be different instances of your application running at the same + time or different applications altogether) to read and write to + the same system locations. It uses advisory file locking and a + smart merging algorithm to ensure data integrity. Changes + performed by another process aren't visible in the current + process until sync() is called. + + \section1 Platform-Specific Notes + + \section2 Locations Where Application Settings Are Stored + + As mentioned in the \l{Fallback Mechanism} section, QSettings + stores settings for an application in up to four locations, + depending on whether the settings are user-specific or + system-wide and whether the the settings are application-specific + or organization-wide. For simplicity, we're assuming the + organization is called MySoft and the application is called Star + Runner. + + On Unix systems, if the file format is NativeFormat, the + following files are used by default: + + \list 1 + \o \c{$HOME/.config/MySoft/Star Runner.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.conf}) + \o \c{$HOME/.config/MySoft.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.conf}) + \o \c{/etc/xdg/MySoft/Star Runner.conf} + \o \c{/etc/xdg/MySoft.conf} + \endlist + + On Mac OS X versions 10.2 and 10.3, these files are used by + default: + + \list 1 + \o \c{$HOME/Library/Preferences/com.MySoft.Star Runner.plist} + \o \c{$HOME/Library/Preferences/com.MySoft.plist} + \o \c{/Library/Preferences/com.MySoft.Star Runner.plist} + \o \c{/Library/Preferences/com.MySoft.plist} + \endlist + + On Windows, NativeFormat settings are stored in the following + registry paths: + + \list 1 + \o \c{HKEY_CURRENT_USER\Software\MySoft\Star Runner} + \o \c{HKEY_CURRENT_USER\Software\MySoft} + \o \c{HKEY_LOCAL_MACHINE\Software\MySoft\Star Runner} + \o \c{HKEY_LOCAL_MACHINE\Software\MySoft} + \endlist + + If the file format is IniFormat, the following files are + used on Unix and Mac OS X: + + \list 1 + \o \c{$HOME/.config/MySoft/Star Runner.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.ini}) + \o \c{$HOME/.config/MySoft.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.ini}) + \o \c{/etc/xdg/MySoft/Star Runner.ini} + \o \c{/etc/xdg/MySoft.ini} + \endlist + + On Windows, the following files are used: + + \list 1 + \o \c{%APPDATA%\MySoft\Star Runner.ini} + \o \c{%APPDATA%\MySoft.ini} + \o \c{%COMMON_APPDATA%\MySoft\Star Runner.ini} + \o \c{%COMMON_APPDATA%\MySoft.ini} + \endlist + + The \c %APPDATA% path is usually \tt{C:\\Documents and + Settings\\\e{User Name}\\Application Data}; the \c + %COMMON_APPDATA% path is usually \tt{C:\\Documents and + Settings\\All Users\\Application Data}. + + The paths for the \c .ini and \c .conf files can be changed using + setPath(). On Unix and Mac OS X, the user can override them by by + setting the \c XDG_CONFIG_HOME environment variable; see + setPath() for details. + + \section2 Accessing INI and .plist Files Directly + + Sometimes you do want to access settings stored in a specific + file or registry path. On all platforms, if you want to read an + INI file directly, you can use the QSettings constructor that + takes a file name as first argument and pass QSettings::IniFormat + as second argument. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 2 + + You can then use the QSettings object to read and write settings + in the file. + + On Mac OS X, you can access XML-based \c .plist files by passing + QSettings::NativeFormat as second argument. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 3 + + \section2 Accessing the Windows Registry Directly + + On Windows, QSettings lets you access settings that have been + written with QSettings (or settings in a supported format, e.g., string + data) in the system registry. This is done by constructing a QSettings + object with a path in the registry and QSettings::NativeFormat. + + For example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 4 + + All the registry entries that appear under the specified path can + be read or written through the QSettings object as usual (using + forward slashes instead of backslashes). For example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 5 + + Note that the backslash character is, as mentioned, used by + QSettings to separate subkeys. As a result, you cannot read or + write windows registry entries that contain slashes or + backslashes; you should use a native windows API if you need to do + so. + + \section2 Accessing Common Registry Settings on Windows + + On Windows, it is possible for a key to have both a value and subkeys. + Its default value is accessed by using "Default" or "." in + place of a subkey: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 6 + + On other platforms than Windows, "Default" and "." would be + treated as regular subkeys. + + \section2 Platform Limitations + + While QSettings attempts to smooth over the differences between + the different supported platforms, there are still a few + differences that you should be aware of when porting your + application: + + \list + \o The Windows system registry has the following limitations: A + subkey may not exceed 255 characters, an entry's value may + not exceed 16,383 characters, and all the values of a key may + not exceed 65,535 characters. One way to work around these + limitations is to store the settings using the IniFormat + instead of the NativeFormat. + + \o On Mac OS X, allKeys() will return some extra keys for global + settings that apply to all applications. These keys can be + read using value() but cannot be changed, only shadowed. + Calling setFallbacksEnabled(false) will hide these global + settings. + + \o On Mac OS X, the CFPreferences API used by QSettings expects + Internet domain names rather than organization names. To + provide a uniform API, QSettings derives a fake domain name + from the organization name (unless the organization name + already is a domain name, e.g. OpenOffice.org). The algorithm + appends ".com" to the company name and replaces spaces and + other illegal characters with hyphens. If you want to specify + a different domain name, call + QCoreApplication::setOrganizationDomain(), + QCoreApplication::setOrganizationName(), and + QCoreApplication::setApplicationName() in your \c main() + function and then use the default QSettings constructor. + Another solution is to use preprocessor directives, for + example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 7 + + \o On Unix and Mac OS X systems, the advisory file locking is disabled + if NFS (or AutoFS or CacheFS) is detected to work around a bug in the + NFS fcntl() implementation, which hangs forever if statd or lockd aren't + running. Also, the locking isn't performed when accessing \c .plist + files. + + \endlist + + \sa QVariant, QSessionManager, {Settings Editor Example}, {Application Example} +*/ + +/*! \enum QSettings::Status + + The following status values are possible: + + \value NoError No error occurred. + \value AccessError An access error occurred (e.g. trying to write to a read-only file). + \value FormatError A format error occurred (e.g. loading a malformed INI file). + + \sa status() +*/ + +/*! \enum QSettings::Format + + This enum type specifies the storage format used by QSettings. + + \value NativeFormat Store the settings using the most + appropriate storage format for the platform. + On Windows, this means the system registry; + on Mac OS X, this means the CFPreferences + API; on Unix, this means textual + configuration files in INI format. + \value IniFormat Store the settings in INI files. + \value InvalidFormat Special value returned by registerFormat(). + \omitvalue CustomFormat1 + \omitvalue CustomFormat2 + \omitvalue CustomFormat3 + \omitvalue CustomFormat4 + \omitvalue CustomFormat5 + \omitvalue CustomFormat6 + \omitvalue CustomFormat7 + \omitvalue CustomFormat8 + \omitvalue CustomFormat9 + \omitvalue CustomFormat10 + \omitvalue CustomFormat11 + \omitvalue CustomFormat12 + \omitvalue CustomFormat13 + \omitvalue CustomFormat14 + \omitvalue CustomFormat15 + \omitvalue CustomFormat16 + + On Unix, NativeFormat and IniFormat mean the same thing, except + that the file extension is different (\c .conf for NativeFormat, + \c .ini for IniFormat). + + The INI file format is a Windows file format that Qt supports on + all platforms. In the absence of an INI standard, we try to + follow what Microsoft does, with the following exceptions: + + \list + \o If you store types that QVariant can't convert to QString + (e.g., QPoint, QRect, and QSize), Qt uses an \c{@}-based + syntax to encode the type. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 8 + + To minimize compatibility issues, any \c @ that doesn't + appear at the first position in the value or that isn't + followed by a Qt type (\c Point, \c Rect, \c Size, etc.) is + treated as a normal character. + + \o Although backslash is a special character in INI files, most + Windows applications don't escape backslashes (\c{\}) in file + paths: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 9 + + QSettings always treats backslash as a special character and + provides no API for reading or writing such entries. + + \o The INI file format has severe restrictions on the syntax of + a key. Qt works around this by using \c % as an escape + character in keys. In addition, if you save a top-level + setting (a key with no slashes in it, e.g., "someKey"), it + will appear in the INI file's "General" section. To avoid + overwriting other keys, if you save something using the a key + such as "General/someKey", the key will be located in the + "%General" section, \e not in the "General" section. + + \o Following the philosophy that we should be liberal in what + we accept and conservative in what we generate, QSettings + will accept Latin-1 encoded INI files, but generate pure + ASCII files, where non-ASCII values are encoded using standard + INI escape sequences. To make the INI files more readable (but + potentially less compatible), call setIniCodec(). + \endlist + + \sa registerFormat(), setPath() +*/ + +/*! \enum QSettings::Scope + + This enum specifies whether settings are user-specific or shared + by all users of the same system. + + \value UserScope Store settings in a location specific to the + current user (e.g., in the user's home + directory). + \value SystemScope Store settings in a global location, so that + all users on the same machine access the same + set of settings. + \omitvalue User + \omitvalue Global + + \sa setPath() +*/ + +#ifndef QT_NO_QOBJECT +/*! + Constructs a QSettings object for accessing settings of the + application called \a application from the organization called \a + organization, and with parent \a parent. + + Example: + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 10 + + The scope is QSettings::UserScope and the format is + QSettings::NativeFormat. + + \sa setDefaultFormat(), {Fallback Mechanism} +*/ +QSettings::QSettings(const QString &organization, const QString &application, QObject *parent) + : QObject(*QSettingsPrivate::create(NativeFormat, UserScope, organization, application), + parent) +{ +} + +/*! + Constructs a QSettings object for accessing settings of the + application called \a application from the organization called \a + organization, and with parent \a parent. + + If \a scope is QSettings::UserScope, the QSettings object searches + user-specific settings first, before it searches system-wide + settings as a fallback. If \a scope is + QSettings::SystemScope, the QSettings object ignores user-specific + settings and provides access to system-wide settings. + + The storage format is QSettings::NativeFormat. + + If no application name is given, the QSettings object will + only access the organization-wide \l{Fallback Mechanism}{locations}. + + \sa setDefaultFormat() +*/ +QSettings::QSettings(Scope scope, const QString &organization, const QString &application, + QObject *parent) + : QObject(*QSettingsPrivate::create(NativeFormat, scope, organization, application), parent) +{ +} + +/*! + Constructs a QSettings object for accessing settings of the + application called \a application from the organization called + \a organization, and with parent \a parent. + + If \a scope is QSettings::UserScope, the QSettings object searches + user-specific settings first, before it searches system-wide + settings as a fallback. If \a scope is + QSettings::SystemScope, the QSettings object ignores user-specific + settings and provides access to system-wide settings. + + If \a format is QSettings::NativeFormat, the native API is used for + storing settings. If \a format is QSettings::IniFormat, the INI format + is used. + + If no application name is given, the QSettings object will + only access the organization-wide \l{Fallback Mechanism}{locations}. +*/ +QSettings::QSettings(Format format, Scope scope, const QString &organization, + const QString &application, QObject *parent) + : QObject(*QSettingsPrivate::create(format, scope, organization, application), parent) +{ +} + +/*! + Constructs a QSettings object for accessing the settings + stored in the file called \a fileName, with parent \a parent. If + the file doesn't already exist, it is created. + + If \a format is QSettings::NativeFormat, the meaning of \a + fileName depends on the platform. On Unix, \a fileName is the + name of an INI file. On Mac OS X, \a fileName is the name of a + \c .plist file. On Windows, \a fileName is a path in the system + registry. + + If \a format is QSettings::IniFormat, \a fileName is the name of an INI + file. + + \warning This function is provided for convenience. It works well for + accessing INI or \c .plist files generated by Qt, but might fail on some + syntaxes found in such files originated by other programs. In particular, + be aware of the following limitations: + + \list + \o QSettings provides no way of reading INI "path" entries, i.e., entries + with unescaped slash characters. (This is because these entries are + ambiguous and cannot be resolved automatically.) + \o In INI files, QSettings uses the \c @ character as a metacharacter in some + contexts, to encode Qt-specific data types (e.g., \c @Rect), and might + therefore misinterpret it when it occurs in pure INI files. + \endlist + + \sa fileName() +*/ +QSettings::QSettings(const QString &fileName, Format format, QObject *parent) + : QObject(*QSettingsPrivate::create(fileName, format), parent) +{ +} + +/*! + Constructs a QSettings object for accessing settings of the + application and organization set previously with a call to + QCoreApplication::setOrganizationName(), + QCoreApplication::setOrganizationDomain(), and + QCoreApplication::setApplicationName(). + + The scope is QSettings::UserScope and the format is + defaultFormat() (QSettings::NativeFormat by default). + + The code + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 11 + + is equivalent to + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 12 + + If QCoreApplication::setOrganizationName() and + QCoreApplication::setApplicationName() has not been previously + called, the QSettings object will not be able to read or write + any settings, and status() will return AccessError. + + On Mac OS X, if both a name and an Internet domain are specified + for the organization, the domain is preferred over the name. On + other platforms, the name is preferred over the domain. + + \sa QCoreApplication::setOrganizationName(), + QCoreApplication::setOrganizationDomain(), + QCoreApplication::setApplicationName(), + setDefaultFormat() +*/ +QSettings::QSettings(QObject *parent) + : QObject(*QSettingsPrivate::create(globalDefaultFormat, UserScope, +#ifdef Q_OS_MAC + QCoreApplication::organizationDomain().isEmpty() + ? QCoreApplication::organizationName() + : QCoreApplication::organizationDomain() +#else + QCoreApplication::organizationName().isEmpty() + ? QCoreApplication::organizationDomain() + : QCoreApplication::organizationName() +#endif + , QCoreApplication::applicationName()), + parent) +{ +} + +#else +QSettings::QSettings(const QString &organization, const QString &application) + : d_ptr(QSettingsPrivate::create(globalDefaultFormat, QSettings::UserScope, organization, application)) +{ + d_ptr->q_ptr = this; +} + +QSettings::QSettings(Scope scope, const QString &organization, const QString &application) + : d_ptr(QSettingsPrivate::create(globalDefaultFormat, scope, organization, application)) +{ + d_ptr->q_ptr = this; +} + +QSettings::QSettings(Format format, Scope scope, const QString &organization, + const QString &application) + : d_ptr(QSettingsPrivate::create(format, scope, organization, application)) +{ + d_ptr->q_ptr = this; +} + +QSettings::QSettings(const QString &fileName, Format format) + : d_ptr(QSettingsPrivate::create(fileName, format)) +{ + d_ptr->q_ptr = this; +} +#endif + +/*! + Destroys the QSettings object. + + Any unsaved changes will eventually be written to permanent + storage. + + \sa sync() +*/ +QSettings::~QSettings() +{ + Q_D(QSettings); + if (d->pendingChanges) + d->flush(); +#ifdef QT_NO_QOBJECT + delete d; +#endif +} + +/*! + Removes all entries in the primary location associated to this + QSettings object. + + Entries in fallback locations are not removed. + + If you only want to remove the entries in the current group(), + use remove("") instead. + + \sa remove(), setFallbacksEnabled() +*/ +void QSettings::clear() +{ + Q_D(QSettings); + d->clear(); + d->requestUpdate(); +} + +/*! + Writes any unsaved changes to permanent storage, and reloads any + settings that have been changed in the meantime by another + application. + + This function is called automatically from QSettings's destructor and + by the event loop at regular intervals, so you normally don't need to + call it yourself. + + \sa status() +*/ +void QSettings::sync() +{ + Q_D(QSettings); + d->sync(); +} + +/*! + Returns the path where settings written using this QSettings + object are stored. + + On Windows, if the format is QSettings::NativeFormat, the return value + is a system registry path, not a file path. + + \sa isWritable(), format() +*/ +QString QSettings::fileName() const +{ + Q_D(const QSettings); + return d->fileName(); +} + +/*! + \since 4.4 + + Returns the format used for storing the settings. + + \sa defaultFormat(), fileName(), scope(), organizationName(), applicationName() +*/ +QSettings::Format QSettings::format() const +{ + Q_D(const QSettings); + return d->format; +} + +/*! + \since 4.4 + + Returns the scope used for storing the settings. + + \sa format(), organizationName(), applicationName() +*/ +QSettings::Scope QSettings::scope() const +{ + Q_D(const QSettings); + return d->scope; +} + +/*! + \since 4.4 + + Returns the organization name used for storing the settings. + + \sa QCoreApplication::organizationName(), format(), scope(), applicationName() +*/ +QString QSettings::organizationName() const +{ + Q_D(const QSettings); + return d->organizationName; +} + +/*! + \since 4.4 + + Returns the application name used for storing the settings. + + \sa QCoreApplication::applicationName(), format(), scope(), organizationName() +*/ +QString QSettings::applicationName() const +{ + Q_D(const QSettings); + return d->applicationName; +} + +#ifndef QT_NO_TEXTCODEC + +/*! + \since 4.5 + + Sets the codec for accessing INI files (including \c .conf files on Unix) + to \a codec. The codec is used for decoding any data that is read from + the INI file, and for encoding any data that is written to the file. By + default, no codec is used, and non-ASCII characters are encoded using + standard INI escape sequences. + + \warning The codec must be set immediately after creating the QSettings + object, before accessing any data. + + \sa iniCodec() +*/ +void QSettings::setIniCodec(QTextCodec *codec) +{ + Q_D(QSettings); + d->iniCodec = codec; +} + +/*! + \since 4.5 + \overload + + Sets the codec for accessing INI files (including \c .conf files on Unix) + to the QTextCodec for the encoding specified by \a codecName. Common + values for \c codecName include "ISO 8859-1", "UTF-8", and "UTF-16". + If the encoding isn't recognized, nothing happens. + + \sa QTextCodec::codecForName() +*/ +void QSettings::setIniCodec(const char *codecName) +{ + Q_D(QSettings); + if (QTextCodec *codec = QTextCodec::codecForName(codecName)) + d->iniCodec = codec; +} + +/*! + \since 4.5 + + Returns the codec that is used for accessing INI files. By default, + no codec is used, so a null pointer is returned. +*/ + +QTextCodec *QSettings::iniCodec() const +{ + Q_D(const QSettings); + return d->iniCodec; +} + +#endif // QT_NO_TEXTCODEC + +/*! + Returns a status code indicating the first error that was met by + QSettings, or QSettings::NoError if no error occurred. + + Be aware that QSettings delays performing some operations. For this + reason, you might want to call sync() to ensure that the data stored + in QSettings is written to disk before calling status(). + + \sa sync() +*/ +QSettings::Status QSettings::status() const +{ + Q_D(const QSettings); + return d->status; +} + +/*! + Appends \a prefix to the current group. + + The current group is automatically prepended to all keys + specified to QSettings. In addition, query functions such as + childGroups(), childKeys(), and allKeys() are based on the group. + By default, no group is set. + + Groups are useful to avoid typing in the same setting paths over + and over. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 13 + + This will set the value of three settings: + + \list + \o \c mainwindow/size + \o \c mainwindow/fullScreen + \o \c outputpanel/visible + \endlist + + Call endGroup() to reset the current group to what it was before + the corresponding beginGroup() call. Groups can be nested. + + \sa endGroup(), group() +*/ +void QSettings::beginGroup(const QString &prefix) +{ + Q_D(QSettings); + d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix))); +} + +/*! + Resets the group to what it was before the corresponding + beginGroup() call. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 14 + + \sa beginGroup(), group() +*/ +void QSettings::endGroup() +{ + Q_D(QSettings); + if (d->groupStack.isEmpty()) { + qWarning("QSettings::endGroup: No matching beginGroup()"); + return; + } + + QSettingsGroup group = d->groupStack.pop(); + int len = group.toString().size(); + if (len > 0) + d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1)); + + if (group.isArray()) + qWarning("QSettings::endGroup: Expected endArray() instead"); +} + +/*! + Returns the current group. + + \sa beginGroup(), endGroup() +*/ +QString QSettings::group() const +{ + Q_D(const QSettings); + return d->groupPrefix.left(d->groupPrefix.size() - 1); +} + +/*! + Adds \a prefix to the current group and starts reading from an + array. Returns the size of the array. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 15 + + Use beginWriteArray() to write the array in the first place. + + \sa beginWriteArray(), endArray(), setArrayIndex() +*/ +int QSettings::beginReadArray(const QString &prefix) +{ + Q_D(QSettings); + d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), false)); + return value(QLatin1String("size")).toInt(); +} + +/*! + Adds \a prefix to the current group and starts writing an array + of size \a size. If \a size is -1 (the default), it is automatically + determined based on the indexes of the entries written. + + If you have many occurrences of a certain set of keys, you can + use arrays to make your life easier. For example, let's suppose + that you want to save a variable-length list of user names and + passwords. You could then write: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 16 + + The generated keys will have the form + + \list + \o \c logins/size + \o \c logins/1/userName + \o \c logins/1/password + \o \c logins/2/userName + \o \c logins/2/password + \o \c logins/3/userName + \o \c logins/3/password + \o ... + \endlist + + To read back an array, use beginReadArray(). + + \sa beginReadArray(), endArray(), setArrayIndex() +*/ +void QSettings::beginWriteArray(const QString &prefix, int size) +{ + Q_D(QSettings); + d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), size < 0)); + + if (size < 0) + remove(QLatin1String("size")); + else + setValue(QLatin1String("size"), size); +} + +/*! + Closes the array that was started using beginReadArray() or + beginWriteArray(). + + \sa beginReadArray(), beginWriteArray() +*/ +void QSettings::endArray() +{ + Q_D(QSettings); + if (d->groupStack.isEmpty()) { + qWarning("QSettings::endArray: No matching beginArray()"); + return; + } + + QSettingsGroup group = d->groupStack.top(); + int 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()); + + if (!group.isArray()) + qWarning("QSettings::endArray: Expected endGroup() instead"); +} + +/*! + Sets the current array index to \a i. Calls to functions such as + setValue(), value(), remove(), and contains() will operate on the + array entry at that index. + + You must call beginReadArray() or beginWriteArray() before you + can call this function. +*/ +void QSettings::setArrayIndex(int i) +{ + Q_D(QSettings); + if (d->groupStack.isEmpty() || !d->groupStack.top().isArray()) { + qWarning("QSettings::setArrayIndex: Missing beginArray()"); + return; + } + + QSettingsGroup &top = d->groupStack.top(); + int len = top.toString().size(); + top.setArrayIndex(qMax(i, 0)); + d->groupPrefix.replace(d->groupPrefix.size() - len - 1, len, top.toString()); +} + +/*! + Returns a list of all keys, including subkeys, that can be read + using the QSettings object. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 17 + + If a group is set using beginGroup(), only the keys in the group + are returned, without the group prefix: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 18 + + \sa childGroups(), childKeys() +*/ +QStringList QSettings::allKeys() const +{ + Q_D(const QSettings); + return d->children(d->groupPrefix, QSettingsPrivate::AllKeys); +} + +/*! + Returns a list of all top-level keys that can be read using the + QSettings object. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 19 + + If a group is set using beginGroup(), the top-level keys in that + group are returned, without the group prefix: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 20 + + You can navigate through the entire setting hierarchy using + childKeys() and childGroups() recursively. + + \sa childGroups(), allKeys() +*/ +QStringList QSettings::childKeys() const +{ + Q_D(const QSettings); + return d->children(d->groupPrefix, QSettingsPrivate::ChildKeys); +} + +/*! + Returns a list of all key top-level groups that contain keys that + can be read using the QSettings object. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 21 + + If a group is set using beginGroup(), the first-level keys in + that group are returned, without the group prefix. + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 22 + + You can navigate through the entire setting hierarchy using + childKeys() and childGroups() recursively. + + \sa childKeys(), allKeys() +*/ +QStringList QSettings::childGroups() const +{ + Q_D(const QSettings); + return d->children(d->groupPrefix, QSettingsPrivate::ChildGroups); +} + +/*! + Returns true if settings can be written using this QSettings + object; returns false otherwise. + + One reason why isWritable() might return false is if + QSettings operates on a read-only file. + + \warning This function is not perfectly reliable, because the + file permissions can change at any time. + + \sa fileName(), status(), sync() +*/ +bool QSettings::isWritable() const +{ + Q_D(const QSettings); + return d->isWritable(); +} + +/*! + + Sets the value of setting \a key to \a value. If the \a key already + exists, the previous value is overwritten. + + Note that the Windows registry and INI files use case-insensitive + keys, whereas the Carbon Preferences API on Mac OS X uses + case-sensitive keys. To avoid portability problems, see the \l{Key + Syntax} rules. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 23 + + \sa value(), remove(), contains() +*/ +void QSettings::setValue(const QString &key, const QVariant &value) +{ + Q_D(QSettings); + QString k = d->actualKey(key); + d->set(k, value); + d->requestUpdate(); +} + +/*! + Removes the setting \a key and any sub-settings of \a key. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 24 + + Be aware that if one of the fallback locations contains a setting + with the same key, that setting will be visible after calling + remove(). + + If \a key is an empty string, all keys in the current group() are + removed. For example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 25 + + Note that the Windows registry and INI files use case-insensitive + keys, whereas the Carbon Preferences API on Mac OS X uses + case-sensitive keys. To avoid portability problems, see the \l{Key + Syntax} rules. + + \sa setValue(), value(), contains() +*/ +void QSettings::remove(const QString &key) +{ + Q_D(QSettings); + /* + We cannot use actualKey(), because remove() supports empty + keys. The code is also tricky because of slash handling. + */ + QString theKey = d->normalizedKey(key); + if (theKey.isEmpty()) + theKey = group(); + else + theKey.prepend(d->groupPrefix); + + if (theKey.isEmpty()) { + d->clear(); + } else { + d->remove(theKey); + } + d->requestUpdate(); +} + +/*! + Returns true if there exists a setting called \a key; returns + false otherwise. + + If a group is set using beginGroup(), \a key is taken to be + relative to that group. + + Note that the Windows registry and INI files use case-insensitive + keys, whereas the Carbon Preferences API on Mac OS X uses + case-sensitive keys. To avoid portability problems, see the \l{Key + Syntax} rules. + + \sa value(), setValue() +*/ +bool QSettings::contains(const QString &key) const +{ + Q_D(const QSettings); + QString k = d->actualKey(key); + return d->get(k, 0); +} + +/*! + Sets whether fallbacks are enabled to \a b. + + By default, fallbacks are enabled. + + \sa fallbacksEnabled() +*/ +void QSettings::setFallbacksEnabled(bool b) +{ + Q_D(QSettings); + d->fallbacks = !!b; +} + +/*! + Returns true if fallbacks are enabled; returns false otherwise. + + By default, fallbacks are enabled. + + \sa setFallbacksEnabled() +*/ +bool QSettings::fallbacksEnabled() const +{ + Q_D(const QSettings); + return d->fallbacks; +} + +#ifndef QT_NO_QOBJECT +/*! + \reimp +*/ +bool QSettings::event(QEvent *event) +{ + Q_D(QSettings); + if (event->type() == QEvent::UpdateRequest) { + d->update(); + return true; + } + return QObject::event(event); +} +#endif + +/*! + Returns the value for setting \a key. If the setting doesn't + exist, returns \a defaultValue. + + If no default value is specified, a default QVariant is + returned. + + Note that the Windows registry and INI files use case-insensitive + keys, whereas the Carbon Preferences API on Mac OS X uses + case-sensitive keys. To avoid portability problems, see the \l{Key + Syntax} rules. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 26 + + \sa setValue(), contains(), remove() +*/ +QVariant QSettings::value(const QString &key, const QVariant &defaultValue) const +{ + Q_D(const QSettings); + QVariant result = defaultValue; + QString k = d->actualKey(key); + d->get(k, &result); + return result; +} + +/*! + \since 4.4 + + Sets the default file format to the given \a format, used for storing + settings for the QSettings(QObject *) constructor. + + If no default format is set, QSettings::NativeFormat is used. + + \sa format() +*/ +void QSettings::setDefaultFormat(Format format) +{ + globalDefaultFormat = format; +} + +/*! + \since 4.4 + + Returns default file format used for storing settings for the QSettings(QObject *) constructor. + If no default format is set, QSettings::NativeFormat is used. + + \sa format() +*/ +QSettings::Format QSettings::defaultFormat() +{ + return globalDefaultFormat; +} + +/*! + \obsolete + + Use setPath() instead. + + \oldcode + setSystemIniPath(path); + \newcode + setPath(QSettings::NativeFormat, QSettings::SystemScope, path); + setPath(QSettings::IniFormat, QSettings::SystemScope, path); + \endcode +*/ +void QSettings::setSystemIniPath(const QString &dir) +{ + setPath(IniFormat, SystemScope, dir); +#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) + setPath(NativeFormat, SystemScope, dir); +#endif +} + +/*! + \obsolete + + Use setPath() instead. +*/ + +void QSettings::setUserIniPath(const QString &dir) +{ + setPath(IniFormat, UserScope, dir); +#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) + setPath(NativeFormat, UserScope, dir); +#endif +} + +/*! + \since 4.1 + + Sets the path used for storing settings for the given \a format + and \a scope, to \a path. The \a format can be a custom format. + + The table below summarizes the default values: + + \table + \header \o Platform \o Format \o Scope \o Path + \row \o{1,2} Windows \o{1,2} IniFormat \o UserScope \o \c %APPDATA% + \row \o SystemScope \o \c %COMMON_APPDATA% + \row \o{1,2} Unix \o{1,2} NativeFormat, IniFormat \o UserScope \o \c $HOME/.config + \row \o SystemScope \o \c /etc/xdg + \row \o{1,2} Qt for Embedded Linux \o{1,2} NativeFormat, IniFormat \o UserScope \o \c $HOME/Settings + \row \o SystemScope \o \c /etc/xdg + \row \o{1,2} Mac OS X \o{1,2} IniFormat \o UserScope \o \c $HOME/.config + \row \o SystemScope \o \c /etc/xdg + \endtable + + The default UserScope paths on Unix and Mac OS X (\c + $HOME/.config or $HOME/Settings) can be overridden by the user by setting the + \c XDG_CONFIG_HOME environment variable. The default SystemScope + paths on Unix and Mac OS X (\c /etc/xdg) can be overridden when + building the Qt library using the \c configure script's \c + --sysconfdir flag (see QLibraryInfo for details). + + Setting the NativeFormat paths on Windows and Mac OS X has no + effect. + + \warning This function doesn't affect existing QSettings objects. + + \sa registerFormat() +*/ +void QSettings::setPath(Format format, Scope scope, const QString &path) +{ + QMutexLocker locker(globalMutex()); + PathHash *pathHash = pathHashFunc(); + pathHash->insert(pathHashKey(format, scope), path + QDir::separator()); +} + +/*! + \typedef QSettings::SettingsMap + + Typedef for QMap<QString, QVariant>. + + \sa registerFormat() +*/ + +/*! + \typedef QSettings::ReadFunc + + Typedef for a pointer to a function with the following signature: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 27 + + \sa WriteFunc, registerFormat() +*/ + +/*! + \typedef QSettings::WriteFunc + + Typedef for a pointer to a function with the following signature: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 28 + + \sa ReadFunc, registerFormat() +*/ + +/*! + \since 4.1 + \threadsafe + + Registers a custom storage format. On success, returns a special + Format value that can then be passed to the QSettings constuctor. + On failure, returns InvalidFormat. + + The \a extension is the file + extension associated to the format (without the '.'). + + The \a readFunc and \a writeFunc parameters are pointers to + functions that read and write a set of (key, value) pairs. The + QIODevice parameter to the read and write functions is always + opened in binary mode (i.e., without the QIODevice::Text flag). + + The \a caseSensitivity parameter specifies whether keys are case + sensitive or not. This makes a difference when looking up values + using QSettings. The default is case sensitive. + + By default, if you use one of the constructors that work in terms + of an organization name and an application name, the file system + locations used are the same as for IniFormat. Use setPath() to + specify other locations. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 29 + + \sa setPath() +*/ +QSettings::Format QSettings::registerFormat(const QString &extension, ReadFunc readFunc, + WriteFunc writeFunc, + Qt::CaseSensitivity caseSensitivity) +{ +#ifdef QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER + Q_ASSERT(caseSensitivity == Qt::CaseSensitive); +#endif + + QMutexLocker locker(globalMutex()); + CustomFormatVector *customFormatVector = customFormatVectorFunc(); + int index = customFormatVector->size(); + if (index == 16) // the QSettings::Format enum has room for 16 custom formats + return QSettings::InvalidFormat; + + QConfFileCustomFormat info; + info.extension = QLatin1Char('.'); + info.extension += extension; + info.readFunc = readFunc; + info.writeFunc = writeFunc; + info.caseSensitivity = caseSensitivity; + customFormatVector->append(info); + + return QSettings::Format((int)QSettings::CustomFormat1 + index); +} + +#ifdef QT3_SUPPORT +void QSettings::setPath_helper(Scope scope, const QString &organization, const QString &application) +{ + Q_D(QSettings); + if (d->pendingChanges) + d->flush(); + QSettingsPrivate *oldPriv = d; + QSettingsPrivate *newPriv = QSettingsPrivate::create(oldPriv->format, scope, organization, application); + static_cast<QObjectPrivate &>(*newPriv) = static_cast<QObjectPrivate &>(*oldPriv); // copy the QObject stuff over (hack) + delete oldPriv; + d_ptr = newPriv; +} + +/*! \fn bool QSettings::writeEntry(const QString &key, bool value) + + Sets the value of setting \a key to \a value. + + Use setValue() instead. +*/ + +/*! \fn bool QSettings::writeEntry(const QString &key, double value) + + \overload +*/ + +/*! \fn bool QSettings::writeEntry(const QString &key, int value) + + \overload +*/ + +/*! \fn bool QSettings::writeEntry(const QString &key, const char *value) + + \overload +*/ + +/*! \fn bool QSettings::writeEntry(const QString &key, const QString &value) + + \overload +*/ + +/*! \fn bool QSettings::writeEntry(const QString &key, const QStringList &value) + + \overload +*/ + +/*! \fn bool QSettings::writeEntry(const QString &key, const QStringList &value, QChar separator) + + \overload + + Use setValue(\a key, \a value) instead. You don't need \a separator. +*/ + +/*! \fn QStringList QSettings::readListEntry(const QString &key, bool *ok = 0) + + Returns the value of setting \a key converted to a QStringList. + + If \a ok is not 0, *\a{ok} is set to true if the key exists, + otherwise *\a{ok} is set to false. + + Use value() instead. + + \oldcode + bool ok; + QStringList list = settings.readListEntry("recentFiles", &ok); + \newcode + bool ok = settings.contains("recentFiles"); + QStringList list = settings.value("recentFiles").toStringList(); + \endcode +*/ + +/*! \fn QStringList QSettings::readListEntry(const QString &key, QChar separator, bool *ok) + + Returns the value of setting \a key converted to a QStringList. + \a separator is ignored. + + If \a ok is not 0, *\a{ok} is set to true if the key exists, + otherwise *\a{ok} is set to false. + + Use value() instead. + + \oldcode + bool ok; + QStringList list = settings.readListEntry("recentFiles", ":", &ok); + \newcode + bool ok = settings.contains("recentFiles"); + QStringList list = settings.value("recentFiles").toStringList(); + \endcode +*/ + +/*! \fn QString QSettings::readEntry(const QString &key, const QString &defaultValue, bool *ok) + + Returns the value for setting \a key converted to a QString. If + the setting doesn't exist, returns \a defaultValue. + + If \a ok is not 0, *\a{ok} is set to true if the key exists, + otherwise *\a{ok} is set to false. + + Use value() instead. + + \oldcode + bool ok; + QString str = settings.readEntry("userName", "administrator", &ok); + \newcode + bool ok = settings.contains("userName"); + QString str = settings.value("userName", "administrator").toString(); + \endcode +*/ + +/*! \fn int QSettings::readNumEntry(const QString &key, int defaultValue, bool *ok) + + Returns the value for setting \a key converted to an \c int. If + the setting doesn't exist, returns \a defaultValue. + + If \a ok is not 0, *\a{ok} is set to true if the key exists, + otherwise *\a{ok} is set to false. + + Use value() instead. + + \oldcode + bool ok; + int max = settings.readNumEntry("maxConnections", 30, &ok); + \newcode + bool ok = settings.contains("maxConnections"); + int max = settings.value("maxConnections", 30).toInt(); + \endcode +*/ + +/*! \fn double QSettings::readDoubleEntry(const QString &key, double defaultValue, bool *ok) + + Returns the value for setting \a key converted to a \c double. If + the setting doesn't exist, returns \a defaultValue. + + If \a ok is not 0, *\a{ok} is set to true if the key exists, + otherwise *\a{ok} is set to false. + + Use value() instead. + + \oldcode + bool ok; + double pi = settings.readDoubleEntry("pi", 3.141592, &ok); + \newcode + bool ok = settings.contains("pi"); + double pi = settings.value("pi", 3.141592).toDouble(); + \endcode +*/ + +/*! \fn bool QSettings::readBoolEntry(const QString &key, bool defaultValue, bool *ok) + + Returns the value for setting \a key converted to a \c bool. If + the setting doesn't exist, returns \a defaultValue. + + If \a ok is not 0, *\a{ok} is set to true if the key exists, + otherwise *\a{ok} is set to false. + + Use value() instead. + + \oldcode + bool ok; + bool grid = settings.readBoolEntry("showGrid", true, &ok); + \newcode + bool ok = settings.contains("showGrid"); + bool grid = settings.value("showGrid", true).toBool(); + \endcode +*/ + +/*! \fn bool QSettings::removeEntry(const QString &key) + + Use remove() instead. +*/ + +/*! \enum QSettings::System + \compat + + \value Unix Unix systems (X11 and Embedded Linux) + \value Windows Microsoft Windows systems + \value Mac Mac OS X systems + + \sa insertSearchPath(), removeSearchPath() +*/ + +/*! \fn void QSettings::insertSearchPath(System system, const QString &path) + + This function is implemented as a no-op. It is provided for + source compatibility with Qt 3. The new QSettings class has no + concept of "search path". +*/ + +/*! \fn void QSettings::removeSearchPath(System system, const QString &path) + + This function is implemented as a no-op. It is provided for + source compatibility with Qt 3. The new QSettings class has no + concept of "search path". +*/ + +/*! \fn void QSettings::setPath(const QString &organization, const QString &application, \ + Scope scope) + + Specifies the \a organization, \a application, and \a scope to + use by the QSettings object. + + Use the appropriate constructor instead, with QSettings::UserScope + instead of QSettings::User and QSettings::SystemScope instead of + QSettings::Global. + + \oldcode + QSettings settings; + settings.setPath("twikimaster.com", "Kanooth", QSettings::Global); + \newcode + QSettings settings(QSettings::SystemScope, "twikimaster.com", "Kanooth"); + \endcode +*/ + +/*! \fn void QSettings::resetGroup() + + Sets the current group to be the empty string. + + Use endGroup() instead (possibly multiple times). + + \oldcode + QSettings settings; + settings.beginGroup("mainWindow"); + settings.beginGroup("leftPanel"); + ... + settings.resetGroup(); + \newcode + QSettings settings; + settings.beginGroup("mainWindow"); + settings.beginGroup("leftPanel"); + ... + settings.endGroup(); + settings.endGroup(); + \endcode +*/ + +/*! \fn QStringList QSettings::entryList(const QString &key) const + + Returns a list of all sub-keys of \a key. + + Use childKeys() instead. + + \oldcode + QSettings settings; + QStringList keys = settings.entryList("cities"); + ... + \newcode + QSettings settings; + settings.beginGroup("cities"); + QStringList keys = settings.childKeys(); + ... + settings.endGroup(); + \endcode +*/ + +/*! \fn QStringList QSettings::subkeyList(const QString &key) const + + Returns a list of all sub-keys of \a key. + + Use childGroups() instead. + + \oldcode + QSettings settings; + QStringList groups = settings.entryList("cities"); + ... + \newcode + QSettings settings; + settings.beginGroup("cities"); + QStringList groups = settings.childKeys(); + ... + settings.endGroup(); + \endcode +*/ +#endif + +QT_END_NAMESPACE + +#endif // QT_NO_SETTINGS |