diff options
Diffstat (limited to 'src/corelib/io')
41 files changed, 1022 insertions, 509 deletions
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 0414ae966a..78416cdf5e 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -146,6 +146,8 @@ win32 { } mac { SOURCES += io/qstorageinfo_mac.cpp + qtConfig(processenvironment): \ + OBJECTIVE_SOURCES += io/qprocess_darwin.mm OBJECTIVE_SOURCES += io/qstandardpaths_mac.mm osx { OBJECTIVE_SOURCES += io/qfilesystemwatcher_fsevents.mm diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp index b4eb98e062..2369fe4726 100644 --- a/src/corelib/io/qdatastream.cpp +++ b/src/corelib/io/qdatastream.cpp @@ -42,6 +42,7 @@ #if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) #include "qbuffer.h" +#include "qfloat16.h" #include "qstring.h" #include <stdio.h> #include <ctype.h> @@ -448,6 +449,9 @@ QDataStream::FloatingPointPrecision QDataStream::floatingPointPrecision() const The default is DoublePrecision. + Note that this property does not affect the serialization or deserialization of \c qfloat16 + instances. + \warning This property must be set to the same value on the object that writes and the object that reads the data stream. @@ -557,6 +561,7 @@ void QDataStream::setByteOrder(ByteOrder bo) \value Qt_5_6 Version 17 (Qt 5.6) \value Qt_5_7 Same as Qt_5_6 \value Qt_5_8 Same as Qt_5_6 + \value Qt_5_9 Same as Qt_5_6 \omitvalue Qt_DefaultCompiledVersion \sa setVersion(), version() @@ -972,6 +977,20 @@ QDataStream &QDataStream::operator>>(double &f) /*! \overload + \since 5.9 + + Reads a floating point number from the stream into \a f, + using the standard IEEE 754 format. Returns a reference to the + stream. +*/ +QDataStream &QDataStream::operator>>(qfloat16 &f) +{ + return *this >> reinterpret_cast<qint16&>(f); +} + + +/*! + \overload Reads the '\\0'-terminated string \a s from the stream and returns a reference to the stream. @@ -1259,6 +1278,19 @@ QDataStream &QDataStream::operator<<(double f) /*! + \fn QDataStream &QDataStream::operator<<(qfloat16 f) + \overload + \since 5.9 + + Writes a floating point number, \a f, to the stream using + the standard IEEE 754 format. Returns a reference to the stream. +*/ +QDataStream &QDataStream::operator<<(qfloat16 f) +{ + return *this << reinterpret_cast<qint16&>(f); +} + +/*! \overload Writes the '\\0'-terminated string \a s to the stream and returns a @@ -1281,7 +1313,6 @@ QDataStream &QDataStream::operator<<(const char *s) return *this; } - /*! Writes the length specifier \a len and the buffer \a s to the stream and returns a reference to the stream. diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h index ac58677b77..db1bbfbd63 100644 --- a/src/corelib/io/qdatastream.h +++ b/src/corelib/io/qdatastream.h @@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE - +class qfloat16; class QByteArray; class QIODevice; @@ -95,10 +95,11 @@ public: Qt_5_6 = 17, Qt_5_7 = Qt_5_6, Qt_5_8 = Qt_5_7, -#if QT_VERSION >= 0x050900 + Qt_5_9 = Qt_5_8, +#if QT_VERSION >= 0x050a00 #error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion #endif - Qt_DefaultCompiledVersion = Qt_5_8 + Qt_DefaultCompiledVersion = Qt_5_9 }; enum ByteOrder { @@ -153,6 +154,7 @@ public: QDataStream &operator>>(quint64 &i); QDataStream &operator>>(bool &i); + QDataStream &operator>>(qfloat16 &f); QDataStream &operator>>(float &f); QDataStream &operator>>(double &f); QDataStream &operator>>(char *&str); @@ -166,6 +168,7 @@ public: QDataStream &operator<<(qint64 i); QDataStream &operator<<(quint64 i); QDataStream &operator<<(bool i); + QDataStream &operator<<(qfloat16 f); QDataStream &operator<<(float f); QDataStream &operator<<(double f); QDataStream &operator<<(const char *str); @@ -222,6 +225,98 @@ private: QDataStream::Status oldStatus; }; +template <typename Container> +QDataStream &readArrayBasedContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + c.reserve(n); + for (quint32 i = 0; i < n; ++i) { + typename Container::value_type t; + s >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c.append(t); + } + + return s; +} + +template <typename Container> +QDataStream &readListBasedContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + for (quint32 i = 0; i < n; ++i) { + typename Container::value_type t; + s >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c << t; + } + + return s; +} + +template <typename Container> +QDataStream &readAssociativeContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + for (quint32 i = 0; i < n; ++i) { + typename Container::key_type k; + typename Container::mapped_type t; + s >> k >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c.insertMulti(k, t); + } + + return s; +} + +template <typename Container> +QDataStream &writeSequentialContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); + for (const typename Container::value_type &t : c) + s << t; + + return s; +} + +template <typename Container> +QDataStream &writeAssociativeContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); + // Deserialization should occur in the reverse order. + // Otherwise, value() will return the least recently inserted + // value instead of the most recently inserted one. + auto it = c.constEnd(); + auto begin = c.constBegin(); + while (it != begin) { + --it; + s << it.key() << it.value(); + } + + return s; +} + } // QtPrivate namespace /***************************************************************************** @@ -264,210 +359,84 @@ inline QDataStream &QDataStream::operator<<(quint32 i) inline QDataStream &QDataStream::operator<<(quint64 i) { return *this << qint64(i); } -template <typename T> -QDataStream& operator>>(QDataStream& s, QList<T>& l) -{ - QtPrivate::StreamStateSaver stateSaver(&s); +template <typename Enum> +inline QDataStream &operator<<(QDataStream &s, QFlags<Enum> e) +{ return s << e.i; } - l.clear(); - quint32 c; - s >> c; - l.reserve(c); - for(quint32 i = 0; i < c; ++i) - { - T t; - s >> t; - if (s.status() != QDataStream::Ok) { - l.clear(); - break; - } - l.append(t); - } +template <typename Enum> +inline QDataStream &operator>>(QDataStream &s, QFlags<Enum> &e) +{ return s >> e.i; } - return s; +template <typename T> +inline QDataStream &operator>>(QDataStream &s, QList<T> &l) +{ + return QtPrivate::readArrayBasedContainer(s, l); } template <typename T> -QDataStream& operator<<(QDataStream& s, const QList<T>& l) +inline QDataStream &operator<<(QDataStream &s, const QList<T> &l) { - s << quint32(l.size()); - for (int i = 0; i < l.size(); ++i) - s << l.at(i); - return s; + return QtPrivate::writeSequentialContainer(s, l); } template <typename T> -QDataStream& operator>>(QDataStream& s, QLinkedList<T>& l) +inline QDataStream &operator>>(QDataStream &s, QLinkedList<T> &l) { - QtPrivate::StreamStateSaver stateSaver(&s); - - l.clear(); - quint32 c; - s >> c; - for(quint32 i = 0; i < c; ++i) - { - T t; - s >> t; - if (s.status() != QDataStream::Ok) { - l.clear(); - break; - } - l.append(t); - } - - return s; + return QtPrivate::readListBasedContainer(s, l); } template <typename T> -QDataStream& operator<<(QDataStream& s, const QLinkedList<T>& l) +inline QDataStream &operator<<(QDataStream &s, const QLinkedList<T> &l) { - s << quint32(l.size()); - typename QLinkedList<T>::ConstIterator it = l.constBegin(); - for(; it != l.constEnd(); ++it) - s << *it; - return s; + return QtPrivate::writeSequentialContainer(s, l); } template<typename T> -QDataStream& operator>>(QDataStream& s, QVector<T>& v) +inline QDataStream &operator>>(QDataStream &s, QVector<T> &v) { - QtPrivate::StreamStateSaver stateSaver(&s); - - v.clear(); - quint32 c; - s >> c; - v.resize(c); - for(quint32 i = 0; i < c; ++i) { - T t; - s >> t; - if (s.status() != QDataStream::Ok) { - v.clear(); - break; - } - v[i] = t; - } - - return s; + return QtPrivate::readArrayBasedContainer(s, v); } template<typename T> -QDataStream& operator<<(QDataStream& s, const QVector<T>& v) +inline QDataStream &operator<<(QDataStream &s, const QVector<T> &v) { - s << quint32(v.size()); - for (typename QVector<T>::const_iterator it = v.begin(); it != v.end(); ++it) - s << *it; - return s; + return QtPrivate::writeSequentialContainer(s, v); } template <typename T> -QDataStream &operator>>(QDataStream &in, QSet<T> &set) +inline QDataStream &operator>>(QDataStream &s, QSet<T> &set) { - QtPrivate::StreamStateSaver stateSaver(&in); - - set.clear(); - quint32 c; - in >> c; - for (quint32 i = 0; i < c; ++i) { - T t; - in >> t; - if (in.status() != QDataStream::Ok) { - set.clear(); - break; - } - set << t; - } - - return in; + return QtPrivate::readListBasedContainer(s, set); } template <typename T> -QDataStream& operator<<(QDataStream &out, const QSet<T> &set) +inline QDataStream &operator<<(QDataStream &s, const QSet<T> &set) { - out << quint32(set.size()); - typename QSet<T>::const_iterator i = set.constBegin(); - while (i != set.constEnd()) { - out << *i; - ++i; - } - return out; + return QtPrivate::writeSequentialContainer(s, set); } template <class Key, class T> -Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QHash<Key, T> &hash) +inline QDataStream &operator>>(QDataStream &s, QHash<Key, T> &hash) { - QtPrivate::StreamStateSaver stateSaver(&in); - - hash.clear(); - quint32 n; - in >> n; - - for (quint32 i = 0; i < n; ++i) { - if (in.status() != QDataStream::Ok) - break; - - Key k; - T t; - in >> k >> t; - hash.insertMulti(k, t); - } - - if (in.status() != QDataStream::Ok) - hash.clear(); - return in; + return QtPrivate::readAssociativeContainer(s, hash); } template <class Key, class T> -Q_OUTOFLINE_TEMPLATE QDataStream &operator<<(QDataStream &out, const QHash<Key, T>& hash) +inline QDataStream &operator<<(QDataStream &s, const QHash<Key, T> &hash) { - out << quint32(hash.size()); - typename QHash<Key, T>::ConstIterator it = hash.end(); - typename QHash<Key, T>::ConstIterator begin = hash.begin(); - while (it != begin) { - --it; - out << it.key() << it.value(); - } - return out; + return QtPrivate::writeAssociativeContainer(s, hash); } -#ifdef Q_QDOC + template <class Key, class T> -Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<Key, T> &map) -#else -template <class aKey, class aT> -Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<aKey, aT> &map) -#endif +inline QDataStream &operator>>(QDataStream &s, QMap<Key, T> &map) { - QtPrivate::StreamStateSaver stateSaver(&in); - - map.clear(); - quint32 n; - in >> n; - - map.detach(); - for (quint32 i = 0; i < n; ++i) { - if (in.status() != QDataStream::Ok) - break; - - aKey key; - aT value; - in >> key >> value; - map.insertMulti(key, value); - } - if (in.status() != QDataStream::Ok) - map.clear(); - return in; + return QtPrivate::readAssociativeContainer(s, map); } template <class Key, class T> -Q_OUTOFLINE_TEMPLATE QDataStream &operator<<(QDataStream &out, const QMap<Key, T> &map) +inline QDataStream &operator<<(QDataStream &s, const QMap<Key, T> &map) { - out << quint32(map.size()); - typename QMap<Key, T>::ConstIterator it = map.end(); - typename QMap<Key, T>::ConstIterator begin = map.begin(); - while (it != begin) { - --it; - out << it.key() << it.value(); - } - return out; + return QtPrivate::writeAssociativeContainer(s, map); } #ifndef QT_NO_DATASTREAM diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index abc2abeaec..61059dd694 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -365,7 +365,7 @@ Q_CORE_EXPORT QDebug qt_QMetaEnum_debugOperator(QDebug&, int value, const QMetaO Q_CORE_EXPORT QDebug qt_QMetaEnum_flagDebugOperator(QDebug &dbg, quint64 value, const QMetaObject *meta, const char *name); template<typename T> -typename QtPrivate::QEnableIf<QtPrivate::IsQEnumHelper<T>::Value, QDebug>::Type +typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, QDebug>::type operator<<(QDebug dbg, T value) { const QMetaObject *obj = qt_getEnumMetaObject(value); @@ -374,9 +374,9 @@ operator<<(QDebug dbg, T value) } template <class T> -inline typename QtPrivate::QEnableIf< +inline typename std::enable_if< QtPrivate::IsQEnumHelper<T>::Value || QtPrivate::IsQEnumHelper<QFlags<T> >::Value, - QDebug>::Type + QDebug>::type qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags<T> &flags) { const QMetaObject *obj = qt_getEnumMetaObject(T()); @@ -385,9 +385,9 @@ qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags<T> &flags) } template <class T> -inline typename QtPrivate::QEnableIf< +inline typename std::enable_if< !QtPrivate::IsQEnumHelper<T>::Value && !QtPrivate::IsQEnumHelper<QFlags<T> >::Value, - QDebug>::Type + QDebug>::type qt_QMetaEnum_flagDebugOperator_helper(QDebug debug, const QFlags<T> &flags) #else // !QT_NO_QOBJECT && !Q_QDOC template <class T> @@ -402,7 +402,7 @@ template<typename T> inline QDebug operator<<(QDebug debug, const QFlags<T> &flags) { // We have to use an indirection otherwise specialisation of some other overload of the - // operator<< the compiler would try to instantiate QFlags<T> for the QEnableIf + // operator<< the compiler would try to instantiate QFlags<T> for the std::enable_if return qt_QMetaEnum_flagDebugOperator_helper(debug, flags); } diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 437f774547..bfb91c131f 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -1843,6 +1843,26 @@ bool QDir::exists(const QString &name) const } /*! + Returns whether the directory is empty. + + Equivalent to \c{count() == 0} with filters + \c{QDir::AllEntries | QDir::NoDotAndDotDot}, but faster as it just checks + whether the directory contains at least one entry. + + \note Unless you set the \a filters flags to include \c{QDir::NoDotAndDotDot} + (as the default value does), no directory is empty. + + \sa count(), entryList(), setFilter() + \since 5.9 +*/ +bool QDir::isEmpty(Filters filters) const +{ + const auto d = d_ptr.constData(); + QDirIterator it(d->dirEntry.filePath(), d->nameFilters, filters); + return !it.hasNext(); +} + +/*! Returns a list of the root directories on this system. On Windows this returns a list of QFileInfo objects containing "C:/", diff --git a/src/corelib/io/qdir.h b/src/corelib/io/qdir.h index ef7ec2c701..950a26f327 100644 --- a/src/corelib/io/qdir.h +++ b/src/corelib/io/qdir.h @@ -144,6 +144,8 @@ public: void setSorting(SortFlags sort); uint count() const; + bool isEmpty(Filters filters = Filters(AllEntries | NoDotAndDotDot)) const; + QString operator[](int) const; static QStringList nameFiltersFromString(const QString &nameFilter); diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index 5acee25d02..12fd7d3048 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -1295,7 +1295,7 @@ qint64 QFileInfo::size() const } /*! - Returns the date and time when the file was created. + Returns the date and local time when the file was created. On most Unix systems, this function returns the time of the last status change. A status change occurs when the file is created, @@ -1316,13 +1316,13 @@ QDateTime QFileInfo::created() const if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::CreationTime)) if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::CreationTime)) return QDateTime(); - return d->metaData.creationTime(); + return d->metaData.creationTime().toLocalTime(); } - return d->getFileTime(QAbstractFileEngine::CreationTime); + return d->getFileTime(QAbstractFileEngine::CreationTime).toLocalTime(); } /*! - Returns the date and time when the file was last modified. + Returns the date and local time when the file was last modified. \sa created(), lastRead() */ @@ -1335,13 +1335,13 @@ QDateTime QFileInfo::lastModified() const if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ModificationTime)) if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ModificationTime)) return QDateTime(); - return d->metaData.modificationTime(); + return d->metaData.modificationTime().toLocalTime(); } - return d->getFileTime(QAbstractFileEngine::ModificationTime); + return d->getFileTime(QAbstractFileEngine::ModificationTime).toLocalTime(); } /*! - Returns the date and time when the file was last read (accessed). + Returns the date and local time when the file was last read (accessed). On platforms where this information is not available, returns the same as lastModified(). @@ -1357,9 +1357,9 @@ QDateTime QFileInfo::lastRead() const if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::AccessTime)) if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::AccessTime)) return QDateTime(); - return d->metaData.accessTime(); + return d->metaData.accessTime().toLocalTime(); } - return d->getFileTime(QAbstractFileEngine::AccessTime); + return d->getFileTime(QAbstractFileEngine::AccessTime).toLocalTime(); } /*! diff --git a/src/corelib/io/qfileselector.cpp b/src/corelib/io/qfileselector.cpp index 920281cef7..cb4f5c4b07 100644 --- a/src/corelib/io/qfileselector.cpp +++ b/src/corelib/io/qfileselector.cpp @@ -133,9 +133,9 @@ QFileSelectorPrivate::QFileSelectorPrivate() With those files available, you would select a different file on the android platform, but only if the locale was en_GB. - QFileSelector will not attempt to select if the base file does not exist. For error handling in - the case no valid selectors are present, it is recommended to have a default or error-handling - file in the base file location even if you expect selectors to be present for all deployments. + For error handling in the case no valid selectors are present, it is recommended to have a default or + error-handling file in the base file location even if you expect selectors to be present for all + deployments. In a future version, some may be marked as deploy-time static and be moved during the deployment step as an optimization. As selectors come with a performance cost, it is @@ -298,9 +298,6 @@ QString QFileSelectorPrivate::select(const QString &filePath) const { Q_Q(const QFileSelector); QFileInfo fi(filePath); - // If file doesn't exist, don't select - if (!fi.exists()) - return filePath; QString ret = selectionHelper(fi.path().isEmpty() ? QString() : fi.path() + QLatin1Char('/'), fi.fileName(), q->allSelectors()); @@ -368,14 +365,10 @@ QStringList QFileSelectorPrivate::platformSelectors() // similar, but not identical to QSysInfo::osType QStringList ret; #if defined(Q_OS_WIN) - // can't fall back to QSysInfo because we need both "winphone" and "winrt" for the Windows Phone case ret << QStringLiteral("windows"); ret << QSysInfo::kernelType(); // "winnt" # if defined(Q_OS_WINRT) ret << QStringLiteral("winrt"); -# if defined(Q_OS_WINPHONE) - ret << QStringLiteral("winphone"); -# endif # endif #elif defined(Q_OS_UNIX) ret << QStringLiteral("unix"); diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index 96829b3b03..3cb412e47c 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -122,13 +122,10 @@ static bool isPackage(const QFileSystemMetaData &data, const QFileSystemEntry &e if (CFBundleGetPackageInfoInDirectory(url, &type, &creator)) return true; -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS // Find if an application other than Finder claims to know how to handle the package - QCFType<CFURLRef> application; - LSGetApplicationForURL(url, - kLSRolesEditor|kLSRolesViewer, - NULL, - &application); + QCFType<CFURLRef> application = LSCopyDefaultApplicationURLForURL(url, + kLSRolesEditor | kLSRolesViewer, nullptr); if (application) { QCFType<CFBundleRef> bundle = CFBundleCreate(kCFAllocatorDefault, application); diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp index cdb64d08e1..90708b0473 100644 --- a/src/corelib/io/qfilesystemengine_win.cpp +++ b/src/corelib/io/qfilesystemengine_win.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qfilesystemengine_p.h" - +#include "qoperatingsystemversion.h" #include "qplatformdefs.h" #include "qsysinfo.h" #include "private/qabstractfileengine_p.h" @@ -81,11 +81,6 @@ using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Storage; using namespace ABI::Windows::ApplicationModel; - -#if _MSC_VER < 1900 -#define Q_OS_WINRT_WIN81 -#endif - #endif // Q_OS_WINRT #ifndef SPI_GETPLATFORMTYPE @@ -157,7 +152,6 @@ QT_BEGIN_NAMESPACE Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0; #if defined(Q_OS_WINRT) -static QString qfsPrivateCurrentDir = QLatin1String(""); // As none of the functions we try to resolve do exist on WinRT // we use QT_NO_LIBRARY to shorten everything up a little bit. # ifndef QT_NO_LIBRARY @@ -503,7 +497,6 @@ QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path) { // can be //server or //server/share QString absPath; -#if !defined(Q_OS_WINRT_WIN81) QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1)); wchar_t *fileName = 0; DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName); @@ -523,12 +516,7 @@ QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path) if (absPath.size() < rootPath.size() && rootPath.startsWith(absPath)) absPath = rootPath; # endif // Q_OS_WINRT -#else // !Q_OS_WINRT_WIN81 - if (QDir::isRelativePath(path)) - absPath = QDir::toNativeSeparators(QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + path)); - else - absPath = QDir::toNativeSeparators(QDir::cleanPath(path)); -#endif // Q_OS_WINRT_WIN81 + // This is really ugly, but GetFullPathName strips off whitespace at the end. // If you for instance write ". " in the lineedit of QFileDialog, // (which is an invalid filename) this function will strip the space off and viola, @@ -550,14 +538,7 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) else ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(entry.filePath())); } else { -#ifndef Q_OS_WINRT_WIN81 ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + entry.filePath()); -#else - // Some WinRT APIs do not support absolute paths (due to sandboxing). - // Thus the port uses the executable's directory as its root directory - // and treats paths relative to that as absolute paths. - ret = QDir::cleanPath(QDir::current().relativeFilePath(entry.filePath())); -#endif } #ifndef Q_OS_WINRT @@ -637,7 +618,7 @@ QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry) FILE_SHARE_READ, OPEN_EXISTING, NULL); #endif // Q_OS_WINRT if (handle) { - result = QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS8 ? + result = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ? fileIdWin8(handle) : fileId(handle); CloseHandle(handle); } @@ -1218,9 +1199,6 @@ QString QFileSystemEngine::tempPath() ret = QDir::fromNativeSeparators(ret); } #else // !Q_OS_WINRT - // According to http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.applicationdata.temporaryfolder.aspx - // the API is not available on winphone which should cause one of the functions - // below to fail ComPtr<IApplicationDataStatics> applicationDataStatics; if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_ApplicationData).Get(), &applicationDataStatics))) return ret; @@ -1252,20 +1230,14 @@ bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry) if(!(meta.exists() && meta.isDirectory())) return false; -#if !defined(Q_OS_WINRT_WIN81) //TODO: this should really be using nativeFilePath(), but that returns a path in long format \\?\c:\foo //which causes many problems later on when it's returned through currentPath() return ::SetCurrentDirectory(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(entry.filePath()).utf16())) != 0; -#else - qfsPrivateCurrentDir = entry.filePath(); - return true; -#endif } QFileSystemEntry QFileSystemEngine::currentPath() { QString ret; -#if !defined(Q_OS_WINRT_WIN81) DWORD size = 0; wchar_t currentName[PATH_MAX]; size = ::GetCurrentDirectory(PATH_MAX, currentName); @@ -1281,13 +1253,6 @@ QFileSystemEntry QFileSystemEngine::currentPath() } if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. -#else // !Q_OS_WINRT_WIN81 - //TODO - a race condition exists when using currentPath / setCurrentPath from multiple threads - if (qfsPrivateCurrentDir.isEmpty()) - qfsPrivateCurrentDir = QDir::rootPath(); - - ret = qfsPrivateCurrentDir; -#endif // Q_OS_WINRT_WIN81 return QFileSystemEntry(ret, QFileSystemEntry::FromNativePath()); } @@ -1368,15 +1333,11 @@ bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Per static inline QDateTime fileTimeToQDateTime(const FILETIME *time) { - QDateTime ret; - - SYSTEMTIME sTime, lTime; + SYSTEMTIME sTime; FileTimeToSystemTime(time, &sTime); - SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime); - ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay)); - ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds)); - - return ret; + return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay), + QTime(sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds), + Qt::UTC); } QDateTime QFileSystemMetaData::creationTime() const diff --git a/src/corelib/io/qfilesystementry.cpp b/src/corelib/io/qfilesystementry.cpp index 2e92f8fbba..de4c852068 100644 --- a/src/corelib/io/qfilesystementry.cpp +++ b/src/corelib/io/qfilesystementry.cpp @@ -175,10 +175,8 @@ void QFileSystemEntry::resolveNativeFilePath() const // WinRT/MSVC2015 allows a maximum of 256 characters for a filepath // unless //?/ is prepended which extends the rule to have a maximum // of 256 characters in the filename plus the preprending path -#if _MSC_VER >= 1900 m_nativeFilePath.prepend("\\\\?\\"); #endif -#endif } } diff --git a/src/corelib/io/qfilesystemiterator_win.cpp b/src/corelib/io/qfilesystemiterator_win.cpp index 2ce7bd7a4b..2905a8e54e 100644 --- a/src/corelib/io/qfilesystemiterator_win.cpp +++ b/src/corelib/io/qfilesystemiterator_win.cpp @@ -39,6 +39,7 @@ #include "qfilesystemiterator_p.h" #include "qfilesystemengine_p.h" +#include "qoperatingsystemversion.h" #include "qplatformdefs.h" #include "qvector.h" @@ -68,10 +69,6 @@ QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Fi nativePath.append(QLatin1Char('\\')); nativePath.append(QLatin1Char('*')); // In MSVC2015+ case we prepend //?/ for longer file-name support -#if defined(Q_OS_WINRT) && _MSC_VER < 1900 - if (nativePath.startsWith(QLatin1Char('\\'))) - nativePath.remove(0, 1); -#endif if (!dirPath.endsWith(QLatin1Char('/'))) dirPath.append(QLatin1Char('/')); if ((filters & (QDir::Dirs|QDir::Drives)) && (!(filters & (QDir::Files)))) @@ -93,7 +90,7 @@ bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaDa haveData = true; int infoLevel = 0 ; // FindExInfoStandard; DWORD dwAdditionalFlags = 0; - if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows7) { dwAdditionalFlags = 2; // FIND_FIRST_EX_LARGE_FETCH infoLevel = 1 ; // FindExInfoBasic; } diff --git a/src/corelib/io/qfilesystemmetadata_p.h b/src/corelib/io/qfilesystemmetadata_p.h index 091552f86e..b09223d656 100644 --- a/src/corelib/io/qfilesystemmetadata_p.h +++ b/src/corelib/io/qfilesystemmetadata_p.h @@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE class QFileSystemEngine; -class QFileSystemMetaData +class Q_AUTOTEST_EXPORT QFileSystemMetaData { public: QFileSystemMetaData() diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index a1d90c76f4..612b3fa57c 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -64,6 +64,9 @@ # include "qfilesystemwatcher_fsevents_p.h" #endif +#include <algorithm> +#include <iterator> + QT_BEGIN_NAMESPACE QFileSystemWatcherEngine *QFileSystemWatcherPrivate::createNativeEngine(QObject *parent) @@ -102,6 +105,17 @@ void QFileSystemWatcherPrivate::init() SIGNAL(directoryChanged(QString,bool)), q, SLOT(_q_directoryChanged(QString,bool))); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + QObject::connect(static_cast<QWindowsFileSystemWatcherEngine *>(native), + &QWindowsFileSystemWatcherEngine::driveLockForRemoval, + q, [this] (const QString &p) { _q_winDriveLockForRemoval(p); }); + QObject::connect(static_cast<QWindowsFileSystemWatcherEngine *>(native), + &QWindowsFileSystemWatcherEngine::driveLockForRemovalFailed, + q, [this] (const QString &p) { _q_winDriveLockForRemovalFailed(p); }); + QObject::connect(static_cast<QWindowsFileSystemWatcherEngine *>(native), + &QWindowsFileSystemWatcherEngine::driveRemoved, + q, [this] (const QString &p) { _q_winDriveRemoved(p); }); +#endif // !Q_OS_WINRT } } @@ -146,7 +160,46 @@ void QFileSystemWatcherPrivate::_q_directoryChanged(const QString &path, bool re emit q->directoryChanged(path, QFileSystemWatcher::QPrivateSignal()); } +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + +void QFileSystemWatcherPrivate::_q_winDriveLockForRemoval(const QString &path) +{ + // Windows: Request to lock a (removable/USB) drive for removal, release + // its paths under watch, temporarily storing them should the lock fail. + Q_Q(QFileSystemWatcher); + QStringList pathsToBeRemoved; + auto pred = [&path] (const QString &f) { return !f.startsWith(path, Qt::CaseInsensitive); }; + std::remove_copy_if(files.cbegin(), files.cend(), + std::back_inserter(pathsToBeRemoved), pred); + std::remove_copy_if(directories.cbegin(), directories.cend(), + std::back_inserter(pathsToBeRemoved), pred); + if (!pathsToBeRemoved.isEmpty()) { + q->removePaths(pathsToBeRemoved); + temporarilyRemovedPaths.insert(path.at(0), pathsToBeRemoved); + } +} + +void QFileSystemWatcherPrivate::_q_winDriveLockForRemovalFailed(const QString &path) +{ + // Windows: Request to lock a (removable/USB) drive failed (blocked by other + // application), restore the watched paths. + Q_Q(QFileSystemWatcher); + if (!path.isEmpty()) { + const auto it = temporarilyRemovedPaths.find(path.at(0)); + if (it != temporarilyRemovedPaths.end()) { + q->addPaths(it.value()); + temporarilyRemovedPaths.erase(it); + } + } +} +void QFileSystemWatcherPrivate::_q_winDriveRemoved(const QString &path) +{ + // Windows: Drive finally removed, clear out paths stored in lock request. + if (!path.isEmpty()) + temporarilyRemovedPaths.remove(path.at(0)); +} +#endif // Q_OS_WIN && !Q_OS_WINRT /*! \class QFileSystemWatcher diff --git a/src/corelib/io/qfilesystemwatcher_p.h b/src/corelib/io/qfilesystemwatcher_p.h index 6c64411f92..4220c1db28 100644 --- a/src/corelib/io/qfilesystemwatcher_p.h +++ b/src/corelib/io/qfilesystemwatcher_p.h @@ -58,6 +58,7 @@ #include <private/qobject_p.h> #include <QtCore/qstringlist.h> +#include <QtCore/qhash.h> QT_BEGIN_NAMESPACE @@ -106,6 +107,15 @@ public: // private slots void _q_fileChanged(const QString &path, bool removed); void _q_directoryChanged(const QString &path, bool removed); + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + void _q_winDriveLockForRemoval(const QString &); + void _q_winDriveLockForRemovalFailed(const QString &); + void _q_winDriveRemoved(const QString &); + +private: + QHash<QChar, QStringList> temporarilyRemovedPaths; +#endif // Q_OS_WIN && !Q_OS_WINRT }; diff --git a/src/corelib/io/qfilesystemwatcher_win.cpp b/src/corelib/io/qfilesystemwatcher_win.cpp index be56d8dd1d..c385a82fc5 100644 --- a/src/corelib/io/qfilesystemwatcher_win.cpp +++ b/src/corelib/io/qfilesystemwatcher_win.cpp @@ -52,6 +52,16 @@ #include <qt_windows.h> +#ifndef Q_OS_WINRT +# include <qabstractnativeeventfilter.h> +# include <qcoreapplication.h> +# include <qdir.h> +# include <private/qeventdispatcher_win_p.h> +# include <dbt.h> +# include <algorithm> +# include <vector> +#endif // !Q_OS_WINRT + QT_BEGIN_NAMESPACE // #define WINQFSW_DEBUG @@ -61,11 +71,275 @@ QT_BEGIN_NAMESPACE # define DEBUG if (false) qDebug #endif +#ifndef Q_OS_WINRT +/////////// +// QWindowsRemovableDriveListener +// Listen for the various WM_DEVICECHANGE message indicating drive addition/removal +// requests and removals. +/////////// +class QWindowsRemovableDriveListener : public QObject, public QAbstractNativeEventFilter +{ + Q_OBJECT +public: + // Device UUids as declared in ioevent.h (GUID_IO_VOLUME_LOCK, ...) + enum VolumeUuid { UnknownUuid, UuidIoVolumeLock, UuidIoVolumeLockFailed, + UuidIoVolumeUnlock, UuidIoMediaRemoval }; + + struct RemovableDriveEntry { + HDEVNOTIFY devNotify; + wchar_t drive; + }; + + explicit QWindowsRemovableDriveListener(QObject *parent = nullptr); + ~QWindowsRemovableDriveListener(); + + // Call from QFileSystemWatcher::addPaths() to set up notifications on drives + void addPath(const QString &path); + + bool nativeEventFilter(const QByteArray &, void *messageIn, long *) override; + +signals: + void driveAdded(); + void driveRemoved(const QString &); + void driveLockForRemoval(const QString &); + void driveLockForRemovalFailed(const QString &); + +private: + static VolumeUuid volumeUuid(const UUID &needle); + void handleDbtCustomEvent(const MSG *msg); + void handleDbtDriveArrivalRemoval(const MSG *msg); + + std::vector<RemovableDriveEntry> m_removableDrives; + quintptr m_lastMessageHash; +}; + +static inline QEventDispatcherWin32 *winEventDispatcher() +{ + return static_cast<QEventDispatcherWin32 *>(QCoreApplication::instance()->eventDispatcher()); +} + +QWindowsRemovableDriveListener::QWindowsRemovableDriveListener(QObject *parent) + : QObject(parent) + , m_lastMessageHash(0) +{ + winEventDispatcher()->installNativeEventFilter(this); +} + +static void stopDeviceNotification(QWindowsRemovableDriveListener::RemovableDriveEntry &e) +{ + UnregisterDeviceNotification(e.devNotify); + e.devNotify = 0; +} + +template <class Iterator> // Search sequence of RemovableDriveEntry for HDEVNOTIFY. +static inline Iterator findByHDevNotify(Iterator i1, Iterator i2, HDEVNOTIFY hdevnotify) +{ + return std::find_if(i1, i2, + [hdevnotify] (const QWindowsRemovableDriveListener::RemovableDriveEntry &e) { return e.devNotify == hdevnotify; }); +} + +QWindowsRemovableDriveListener::~QWindowsRemovableDriveListener() +{ + std::for_each(m_removableDrives.begin(), m_removableDrives.end(), stopDeviceNotification); +} + +static QString pathFromEntry(const QWindowsRemovableDriveListener::RemovableDriveEntry &re) +{ + QString path = QStringLiteral("A:/"); + path[0] = QChar::fromLatin1(re.drive); + return path; +} + +// Handle WM_DEVICECHANGE+DBT_CUSTOMEVENT, which is sent based on the registration +// on the volume handle with QEventDispatcherWin32's message window in the class. +// Capture the GUID_IO_VOLUME_LOCK indicating the drive is to be removed. +QWindowsRemovableDriveListener::VolumeUuid QWindowsRemovableDriveListener::volumeUuid(const UUID &needle) +{ + static const struct VolumeUuidMapping // UUIDs from IoEvent.h (missing in MinGW) + { + VolumeUuid v; + UUID uuid; + } mapping[] = { + { UuidIoVolumeLock, // GUID_IO_VOLUME_LOCK + {0x50708874, 0xc9af, 0x11d1, {0x8f, 0xef, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} }, + { UuidIoVolumeLockFailed, // GUID_IO_VOLUME_LOCK_FAILED + {0xae2eed10, 0x0ba8, 0x11d2, {0x8f, 0xfb, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} }, + { UuidIoVolumeUnlock, // GUID_IO_VOLUME_UNLOCK + {0x9a8c3d68, 0xd0cb, 0x11d1, {0x8f, 0xef, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} }, + { UuidIoMediaRemoval, // GUID_IO_MEDIA_REMOVAL + {0xd07433c1, 0xa98e, 0x11d2, {0x91, 0x7a, 0x0, 0xa0, 0xc9, 0x06, 0x8f, 0xf3}} } + }; + + static const VolumeUuidMapping *end = mapping + sizeof(mapping) / sizeof(mapping[0]); + const VolumeUuidMapping *m = + std::find_if(mapping, end, [&needle] (const VolumeUuidMapping &m) { return IsEqualGUID(m.uuid, needle); }); + return m != end ? m->v : UnknownUuid; +} + +inline void QWindowsRemovableDriveListener::handleDbtCustomEvent(const MSG *msg) +{ + const DEV_BROADCAST_HDR *broadcastHeader = reinterpret_cast<const DEV_BROADCAST_HDR *>(msg->lParam); + if (broadcastHeader->dbch_devicetype != DBT_DEVTYP_HANDLE) + return; + const DEV_BROADCAST_HANDLE *broadcastHandle = reinterpret_cast<const DEV_BROADCAST_HANDLE *>(broadcastHeader); + const auto it = findByHDevNotify(m_removableDrives.cbegin(), m_removableDrives.cend(), + broadcastHandle->dbch_hdevnotify); + if (it == m_removableDrives.cend()) + return; + switch (volumeUuid(broadcastHandle->dbch_eventguid)) { + case UuidIoVolumeLock: // Received for removable USB media + emit driveLockForRemoval(pathFromEntry(*it)); + break; + case UuidIoVolumeLockFailed: + emit driveLockForRemovalFailed(pathFromEntry(*it)); + break; + case UuidIoVolumeUnlock: + break; + case UuidIoMediaRemoval: // Received for optical drives + break; + default: + break; + } +} + +// Handle WM_DEVICECHANGE+DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE which are +// sent to all top level windows and cannot be registered for (that is, their +// triggering depends on top level windows being present) +inline void QWindowsRemovableDriveListener::handleDbtDriveArrivalRemoval(const MSG *msg) +{ + const DEV_BROADCAST_HDR *broadcastHeader = reinterpret_cast<const DEV_BROADCAST_HDR *>(msg->lParam); + switch (broadcastHeader->dbch_devicetype) { + case DBT_DEVTYP_HANDLE: // WM_DEVICECHANGE/DBT_DEVTYP_HANDLE is sent for our registered drives. + if (msg->wParam == DBT_DEVICEREMOVECOMPLETE) { + const DEV_BROADCAST_HANDLE *broadcastHandle = reinterpret_cast<const DEV_BROADCAST_HANDLE *>(broadcastHeader); + const auto it = findByHDevNotify(m_removableDrives.begin(), m_removableDrives.end(), + broadcastHandle->dbch_hdevnotify); + // Emit for removable USB drives we were registered for. + if (it != m_removableDrives.end()) { + emit driveRemoved(pathFromEntry(*it)); + stopDeviceNotification(*it); + m_removableDrives.erase(it); + } + } + break; + case DBT_DEVTYP_VOLUME: { + const DEV_BROADCAST_VOLUME *broadcastVolume = reinterpret_cast<const DEV_BROADCAST_VOLUME *>(broadcastHeader); + // WM_DEVICECHANGE/DBT_DEVTYP_VOLUME messages are sent to all toplevel windows. Compare a hash value to ensure + // it is handled only once. + const quintptr newHash = reinterpret_cast<quintptr>(broadcastVolume) + msg->wParam + + quintptr(broadcastVolume->dbcv_flags) + quintptr(broadcastVolume->dbcv_unitmask); + if (newHash == m_lastMessageHash) + return; + m_lastMessageHash = newHash; + // Check for DBTF_MEDIA (inserted/Removed Optical drives). Ignore for now. + if (broadcastVolume->dbcv_flags & DBTF_MEDIA) + return; + // Continue with plugged in USB media where dbcv_flags=0. + switch (msg->wParam) { + case DBT_DEVICEARRIVAL: + emit driveAdded(); + break; + case DBT_DEVICEREMOVECOMPLETE: // handled by DBT_DEVTYP_HANDLE above + break; + } + } + break; + } +} + +bool QWindowsRemovableDriveListener::nativeEventFilter(const QByteArray &, void *messageIn, long *) +{ + const MSG *msg = reinterpret_cast<const MSG *>(messageIn); + if (msg->message == WM_DEVICECHANGE) { + switch (msg->wParam) { + case DBT_CUSTOMEVENT: + handleDbtCustomEvent(msg); + break; + case DBT_DEVICEARRIVAL: + case DBT_DEVICEREMOVECOMPLETE: + handleDbtDriveArrivalRemoval(msg); + break; + } + } + return false; +} + +// Set up listening for WM_DEVICECHANGE+DBT_CUSTOMEVENT for a removable drive path, +void QWindowsRemovableDriveListener::addPath(const QString &p) +{ + const wchar_t drive = p.size() >= 2 && p.at(0).isLetter() && p.at(1) == QLatin1Char(':') + ? wchar_t(p.at(0).toUpper().unicode()) : L'\0'; + if (!drive) + return; + // Already listening? + if (std::any_of(m_removableDrives.cbegin(), m_removableDrives.cend(), + [drive](const RemovableDriveEntry &e) { return e.drive == drive; })) { + return; + } + + wchar_t devicePath[8] = L"\\\\.\\A:\\"; + devicePath[4] = drive; + RemovableDriveEntry re; + re.drive = drive; + if (GetDriveTypeW(devicePath + 4) != DRIVE_REMOVABLE) + return; + const HANDLE volumeHandle = + CreateFile(devicePath, FILE_READ_ATTRIBUTES, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, // Volume requires BACKUP_SEMANTICS + 0); + if (volumeHandle == INVALID_HANDLE_VALUE) { + qErrnoWarning("CreateFile %s failed.", + qPrintable(QString::fromWCharArray(devicePath))); + return; + } + + DEV_BROADCAST_HANDLE notify; + ZeroMemory(¬ify, sizeof(notify)); + notify.dbch_size = sizeof(notify); + notify.dbch_devicetype = DBT_DEVTYP_HANDLE; + notify.dbch_handle = volumeHandle; + re.devNotify = RegisterDeviceNotification(winEventDispatcher()->internalHwnd(), + ¬ify, DEVICE_NOTIFY_WINDOW_HANDLE); + // Empirically found: The notifications also work when the handle is immediately + // closed. Do it here to avoid having to close/reopen in lock message handling. + CloseHandle(volumeHandle); + if (!re.devNotify) { + qErrnoWarning("RegisterDeviceNotification %s failed.", + qPrintable(QString::fromWCharArray(devicePath))); + return; + } + + m_removableDrives.push_back(re); +} +#endif // !Q_OS_WINRT + +/////////// +// QWindowsFileSystemWatcherEngine +/////////// QWindowsFileSystemWatcherEngine::Handle::Handle() : handle(INVALID_HANDLE_VALUE), flags(0u) { } +QWindowsFileSystemWatcherEngine::QWindowsFileSystemWatcherEngine(QObject *parent) + : QFileSystemWatcherEngine(parent) +#ifndef Q_OS_WINRT + , m_driveListener(new QWindowsRemovableDriveListener(this)) +#endif +{ +#ifndef Q_OS_WINRT + parent->setProperty("_q_driveListener", + QVariant::fromValue(static_cast<QObject *>(m_driveListener))); + QObject::connect(m_driveListener, &QWindowsRemovableDriveListener::driveLockForRemoval, + this, &QWindowsFileSystemWatcherEngine::driveLockForRemoval); + QObject::connect(m_driveListener, &QWindowsRemovableDriveListener::driveLockForRemovalFailed, + this, &QWindowsFileSystemWatcherEngine::driveLockForRemovalFailed); + QObject::connect(m_driveListener, &QWindowsRemovableDriveListener::driveRemoved, + this, &QWindowsFileSystemWatcherEngine::driveRemoved); +#endif // !Q_OS_WINRT +} + QWindowsFileSystemWatcherEngine::~QWindowsFileSystemWatcherEngine() { for (auto *thread : qAsConst(threads)) @@ -210,6 +484,13 @@ QStringList QWindowsFileSystemWatcherEngine::addPaths(const QStringList &paths, } } } + +#ifndef Q_OS_WINRT + for (const QString &path : paths) { + if (!p.contains(path)) + m_driveListener->addPath(path); + } +#endif // !Q_OS_WINRT return p; } @@ -439,4 +720,9 @@ void QWindowsFileSystemWatcherEngineThread::wakeup() } QT_END_NAMESPACE + +#ifndef Q_OS_WINRT +# include "qfilesystemwatcher_win.moc" +#endif + #endif // QT_NO_FILESYSTEMWATCHER diff --git a/src/corelib/io/qfilesystemwatcher_win_p.h b/src/corelib/io/qfilesystemwatcher_win_p.h index e8f5c49dec..51c7082483 100644 --- a/src/corelib/io/qfilesystemwatcher_win_p.h +++ b/src/corelib/io/qfilesystemwatcher_win_p.h @@ -66,6 +66,7 @@ QT_BEGIN_NAMESPACE class QWindowsFileSystemWatcherEngineThread; +class QWindowsRemovableDriveListener; // Even though QWindowsFileSystemWatcherEngine is derived of QThread // via QFileSystemWatcher, it does not start a thread. @@ -75,9 +76,7 @@ class QWindowsFileSystemWatcherEngine : public QFileSystemWatcherEngine { Q_OBJECT public: - inline QWindowsFileSystemWatcherEngine(QObject *parent) - : QFileSystemWatcherEngine(parent) - { } + explicit QWindowsFileSystemWatcherEngine(QObject *parent); ~QWindowsFileSystemWatcherEngine(); QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); @@ -121,9 +120,17 @@ public: || lastModified != fileInfo.lastModified()); } }; + +signals: + void driveLockForRemoval(const QString &); + void driveLockForRemovalFailed(const QString &); + void driveRemoved(const QString &); + private: QList<QWindowsFileSystemWatcherEngineThread *> threads; - +#ifndef Q_OS_WINRT + QWindowsRemovableDriveListener *m_driveListener; +#endif }; class QFileSystemWatcherPathKey : public QString diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index bf2f47d399..ca5af924e9 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -144,7 +144,7 @@ static inline bool setCloseOnExec(int fd) static inline QString msgOpenDirectory() { const char message[] = QT_TRANSLATE_NOOP("QIODevice", "file to open is a directory"); -#ifndef QT_BOOTSTRAPPED +#if QT_CONFIG(translation) return QIODevice::tr(message); #else return QLatin1String(message); @@ -629,7 +629,7 @@ QString QFSFileEngine::fileName(FileName file) const bool QFSFileEngine::isRelativePath() const { Q_D(const QFSFileEngine); - return d->fileEntry.filePath().length() ? d->fileEntry.filePath()[0] != QLatin1Char('/') : true; + return d->fileEntry.filePath().length() ? d->fileEntry.filePath().at(0) != QLatin1Char('/') : true; } uint QFSFileEngine::ownerId(FileOwner own) const diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index 117c224318..a7fd50df83 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -850,7 +850,6 @@ QDateTime QFSFileEngine::fileTime(FileTime time) const uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags) { -#ifndef Q_OS_WINPHONE Q_Q(QFSFileEngine); Q_UNUSED(flags); if (openMode == QFile::NotOpen) { @@ -960,18 +959,11 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, ::CloseHandle(mapHandle); mapHandle = NULL; -#else // !Q_OS_WINPHONE - Q_UNUSED(offset); - Q_UNUSED(size); - Q_UNUSED(flags); - Q_UNIMPLEMENTED(); -#endif // Q_OS_WINPHONE return 0; } bool QFSFileEnginePrivate::unmap(uchar *ptr) { -#ifndef Q_OS_WINPHONE Q_Q(QFSFileEngine); if (!maps.contains(ptr)) { q->setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED)); @@ -990,11 +982,6 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr) } return true; -#else // !Q_OS_WINPHONE - Q_UNUSED(ptr); - Q_UNIMPLEMENTED(); - return false; -#endif // Q_OS_WINPHONE } QT_END_NAMESPACE diff --git a/src/corelib/io/qloggingregistry.cpp b/src/corelib/io/qloggingregistry.cpp index 77a8c891b4..4f7bc95330 100644 --- a/src/corelib/io/qloggingregistry.cpp +++ b/src/corelib/io/qloggingregistry.cpp @@ -371,7 +371,12 @@ void QLoggingRegistry::setApiRules(const QString &content) */ void QLoggingRegistry::updateRules() { - rules = qtConfigRules + configRules + apiRules + envRules; + rules.clear(); + rules.reserve(qtConfigRules.size() + configRules.size() + apiRules.size() + envRules.size()), + rules += qtConfigRules; + rules += configRules; + rules += apiRules; + rules += envRules; for (auto it = categories.keyBegin(), end = categories.keyEnd(); it != end; ++it) (*categoryFilter)(*it); diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index c7d6cb426d..c27484acbe 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -99,7 +99,7 @@ QT_END_NAMESPACE #include <private/qcore_unix_p.h> #endif -#ifndef QT_NO_PROCESS +#if QT_CONFIG(processenvironment) QT_BEGIN_NAMESPACE @@ -430,6 +430,10 @@ void QProcessEnvironment::insert(const QProcessEnvironment &e) d->insert(*e.d); } +#endif // QT_CONFIG(processenvironment) + +#if QT_CONFIG(process) + void QProcessPrivate::Channel::clear() { switch (type) { @@ -1529,7 +1533,7 @@ void QProcess::setStandardOutputProcess(QProcess *destination) dto->stdinChannel.pipeFrom(dfrom); } -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) /*! \since 4.7 diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index 4ce0503761..37e71aef5d 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -48,10 +48,11 @@ QT_BEGIN_NAMESPACE +class QProcessPrivate; -#ifndef QT_NO_PROCESS +#if QT_CONFIG(processenvironment) -#if !defined(Q_OS_WIN) || defined(Q_QDOC) +#if !defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) typedef qint64 Q_PID; #else QT_END_NAMESPACE @@ -61,7 +62,6 @@ typedef struct _STARTUPINFOW Q_STARTUPINFO; QT_BEGIN_NAMESPACE #endif -class QProcessPrivate; class QProcessEnvironmentPrivate; class Q_CORE_EXPORT QProcessEnvironment @@ -105,6 +105,10 @@ private: Q_DECLARE_SHARED(QProcessEnvironment) +#endif // QT_CONFIG(processenvironment) + +#if QT_CONFIG(process) + class Q_CORE_EXPORT QProcess : public QIODevice { Q_OBJECT @@ -187,7 +191,7 @@ public: void setStandardErrorFile(const QString &fileName, OpenMode mode = Truncate); void setStandardOutputProcess(QProcess *destination); -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) QString nativeArguments() const; void setNativeArguments(const QString &arguments); struct CreateProcessArguments @@ -206,7 +210,7 @@ public: typedef std::function<void(CreateProcessArguments *)> CreateProcessArgumentModifier; CreateProcessArgumentModifier createProcessArgumentsModifier() const; void setCreateProcessArgumentsModifier(CreateProcessArgumentModifier modifier); -#endif // Q_OS_WIN +#endif // Q_OS_WIN || Q_CLANG_QDOC QString workingDirectory() const; void setWorkingDirectory(const QString &dir); diff --git a/src/corelib/io/qprocess_darwin.mm b/src/corelib/io/qprocess_darwin.mm new file mode 100644 index 0000000000..dd7a8275b9 --- /dev/null +++ b/src/corelib/io/qprocess_darwin.mm @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qprocess_p.h> + +#import <Foundation/Foundation.h> + +QT_BEGIN_NAMESPACE + +QProcessEnvironment QProcessEnvironment::systemEnvironment() +{ + __block QProcessEnvironment env; + [[[NSProcessInfo processInfo] environment] + enumerateKeysAndObjectsUsingBlock:^(NSString *name, NSString *value, BOOL *__unused stop) { + env.d->hash.insert( + QProcessEnvironmentPrivate::Key(QString::fromNSString(name).toLocal8Bit()), + QProcessEnvironmentPrivate::Value(QString::fromNSString(value).toLocal8Bit())); + }]; + return env; +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index ae236c8c60..92b747f6ba 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -70,8 +70,6 @@ typedef int Q_PIPE; #define INVALID_Q_PIPE -1 #endif -#ifndef QT_NO_PROCESS - QT_BEGIN_NAMESPACE class QSocketNotifier; @@ -80,6 +78,8 @@ class QWindowsPipeWriter; class QWinEventNotifier; class QTimer; +#if QT_CONFIG(processenvironment) + #ifdef Q_OS_WIN class QProcEnvKey : public QString { @@ -233,6 +233,10 @@ template<> Q_INLINE_TEMPLATE void QSharedDataPointer<QProcessEnvironmentPrivate> d = x; } +#endif // QT_CONFIG(processenvironment) + +#if QT_CONFIG(process) + class QProcessPrivate : public QIODevicePrivate { public: @@ -386,8 +390,8 @@ public: void setErrorAndEmit(QProcess::ProcessError error, const QString &description = QString()); }; -QT_END_NAMESPACE - #endif // QT_NO_PROCESS +QT_END_NAMESPACE + #endif // QPROCESS_P_H diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index b39816dd7d..deca5c50ff 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -41,9 +41,7 @@ //#define QPROCESS_DEBUG #include "qdebug.h" -#ifndef QT_NO_PROCESS - -#if defined QPROCESS_DEBUG +#if QT_CONFIG(process) && defined(QPROCESS_DEBUG) #include "private/qtools_p.h" #include <ctype.h> @@ -114,10 +112,36 @@ QT_END_NAMESPACE #include <errno.h> #include <stdlib.h> #include <string.h> + +#if QT_CONFIG(process) #include <forkfd.h> +#endif QT_BEGIN_NAMESPACE +#if QT_CONFIG(processenvironment) && !defined(Q_OS_DARWIN) + +QProcessEnvironment QProcessEnvironment::systemEnvironment() +{ + QProcessEnvironment env; + const char *entry; + for (int count = 0; (entry = environ[count]); ++count) { + const char *equal = strchr(entry, '='); + if (!equal) + continue; + + QByteArray name(entry, equal - entry); + QByteArray value(equal + 1); + env.d->hash.insert(QProcessEnvironmentPrivate::Key(name), + QProcessEnvironmentPrivate::Value(value)); + } + return env; +} + +#endif // QT_CONFIG(processenvironment) && !defined(Q_OS_DARWIN) + +#if QT_CONFIG(process) + // POSIX requires PIPE_BUF to be 512 or larger // so we will use 512 static const int errorBufferMax = 512; @@ -310,34 +334,6 @@ bool QProcessPrivate::openChannel(Channel &channel) } } -QT_BEGIN_INCLUDE_NAMESPACE -#if defined(Q_OS_MACX) -# include <crt_externs.h> -# define environ (*_NSGetEnviron()) -#else - extern char **environ; -#endif -QT_END_INCLUDE_NAMESPACE - -QProcessEnvironment QProcessEnvironment::systemEnvironment() -{ - QProcessEnvironment env; -#if !defined(QT_PLATFORM_UIKIT) - const char *entry; - for (int count = 0; (entry = environ[count]); ++count) { - const char *equal = strchr(entry, '='); - if (!equal) - continue; - - QByteArray name(entry, equal - entry); - QByteArray value(equal + 1); - env.d->hash.insert(QProcessEnvironmentPrivate::Key(name), - QProcessEnvironmentPrivate::Value(value)); - } -#endif - return env; -} - static char **_q_dupEnvironment(const QProcessEnvironmentPrivate::Hash &environment, int *envc) { *envc = 0; @@ -1044,6 +1040,6 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a return success; } -QT_END_NAMESPACE - #endif // QT_NO_PROCESS + +QT_END_NAMESPACE diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index d0e922bf2b..aa69e9e1db 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -38,6 +38,7 @@ ** ****************************************************************************/ +//#define QPROCESS_DEBUG #include "qprocess.h" #include "qprocess_p.h" #include "qwindowspipereader_p.h" @@ -59,11 +60,35 @@ #define PIPE_REJECT_REMOTE_CLIENTS 0x08 #endif -#ifndef QT_NO_PROCESS - QT_BEGIN_NAMESPACE -//#define QPROCESS_DEBUG +#if QT_CONFIG(processenvironment) + +QProcessEnvironment QProcessEnvironment::systemEnvironment() +{ + QProcessEnvironment env; + // Calls to setenv() affect the low-level environment as well. + // This is not the case the other way round. + if (wchar_t *envStrings = GetEnvironmentStringsW()) { + for (const wchar_t *entry = envStrings; *entry; ) { + const int entryLen = int(wcslen(entry)); + // + 1 to permit magic cmd variable names starting with = + if (const wchar_t *equal = wcschr(entry + 1, L'=')) { + int nameLen = equal - entry; + QString name = QString::fromWCharArray(entry, nameLen); + QString value = QString::fromWCharArray(equal + 1, entryLen - nameLen - 1); + env.d->hash.insert(QProcessEnvironmentPrivate::Key(name), value); + } + entry += entryLen + 1; + } + FreeEnvironmentStringsW(envStrings); + } + return env; +} + +#endif // QT_CONFIG(processenvironment) + +#if QT_CONFIG(process) static void qt_create_pipe(Q_PIPE *pipe, bool isInputPipe) { @@ -369,28 +394,6 @@ static QString qt_create_commandline(const QString &program, const QStringList & return args; } -QProcessEnvironment QProcessEnvironment::systemEnvironment() -{ - QProcessEnvironment env; - // Calls to setenv() affect the low-level environment as well. - // This is not the case the other way round. - if (wchar_t *envStrings = GetEnvironmentStringsW()) { - for (const wchar_t *entry = envStrings; *entry; ) { - const int entryLen = int(wcslen(entry)); - // + 1 to permit magic cmd variable names starting with = - if (const wchar_t *equal = wcschr(entry + 1, L'=')) { - int nameLen = equal - entry; - QString name = QString::fromWCharArray(entry, nameLen); - QString value = QString::fromWCharArray(equal + 1, entryLen - nameLen - 1); - env.d->hash.insert(QProcessEnvironmentPrivate::Key(name), value); - } - entry += entryLen + 1; - } - FreeEnvironmentStringsW(envStrings); - } - return env; -} - static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Hash &environment) { QByteArray envlist; @@ -892,6 +895,6 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a return success; } -QT_END_NAMESPACE - #endif // QT_NO_PROCESS + +QT_END_NAMESPACE diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp index 0254eb984f..d8166014db 100644 --- a/src/corelib/io/qsavefile.cpp +++ b/src/corelib/io/qsavefile.cpp @@ -104,13 +104,14 @@ QSaveFilePrivate::~QSaveFilePrivate() \sa QTextStream, QDataStream, QFileInfo, QDir, QFile, QTemporaryFile */ -/*! - Constructs a new file object with the given \a parent. -*/ -QSaveFile::QSaveFile(QObject *parent) - : QFileDevice(*new QSaveFilePrivate, parent) +#ifdef QT_NO_QOBJECT +QSaveFile::QSaveFile(const QString &name) + : QFileDevice(*new QSaveFilePrivate) { + Q_D(QSaveFile); + d->fileName = name; } +#else /*! Constructs a new file object to represent the file with the given \a name. */ @@ -120,6 +121,14 @@ QSaveFile::QSaveFile(const QString &name) Q_D(QSaveFile); d->fileName = name; } + +/*! + Constructs a new file object with the given \a parent. +*/ +QSaveFile::QSaveFile(QObject *parent) + : QFileDevice(*new QSaveFilePrivate, parent) +{ +} /*! Constructs a new file object with the given \a parent to represent the file with the specified \a name. @@ -130,6 +139,7 @@ QSaveFile::QSaveFile(const QString &name, QObject *parent) Q_D(QSaveFile); d->fileName = name; } +#endif /*! Destroys the file object, discarding the saved contents unless commit() was called. diff --git a/src/corelib/io/qsavefile.h b/src/corelib/io/qsavefile.h index 10857c27d1..09d6e29272 100644 --- a/src/corelib/io/qsavefile.h +++ b/src/corelib/io/qsavefile.h @@ -58,14 +58,18 @@ class QSaveFilePrivate; class Q_CORE_EXPORT QSaveFile : public QFileDevice { +#ifndef QT_NO_QOBJECT Q_OBJECT +#endif Q_DECLARE_PRIVATE(QSaveFile) public: explicit QSaveFile(const QString &name); +#ifndef QT_NO_QOBJECT explicit QSaveFile(QObject *parent = Q_NULLPTR); explicit QSaveFile(const QString &name, QObject *parent); +#endif ~QSaveFile(); QString fileName() const Q_DECL_OVERRIDE; @@ -84,6 +88,9 @@ protected: private: void close() Q_DECL_OVERRIDE; +#if !QT_CONFIG(translation) + static QString tr(const char *string) { return QString::fromLatin1(string); } +#endif private: Q_DISABLE_COPY(QSaveFile) diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index e12da68671..16dab38a60 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -128,7 +128,18 @@ Q_DECLARE_TYPEINFO(QConfFileCustomFormat, Q_MOVABLE_TYPE); typedef QHash<QString, QConfFile *> ConfFileHash; typedef QCache<QString, QConfFile> ConfFileCache; -typedef QHash<int, QString> PathHash; +namespace { + struct Path + { + // Note: Defining constructors explicitly because of buggy C++11 + // implementation in MSVC (uniform initialization). + Path() {} + Path(const QString & p, bool ud) : path(p), userDefined(ud) {} + QString path; + bool userDefined; //!< true - user defined, overridden by setPath + }; +} +typedef QHash<int, Path> PathHash; typedef QVector<QConfFileCustomFormat> CustomFormatVector; Q_GLOBAL_STATIC(ConfFileHash, usedHashFunc) @@ -220,7 +231,7 @@ void QConfFile::clearCache() // QSettingsPrivate QSettingsPrivate::QSettingsPrivate(QSettings::Format format) - : format(format), scope(QSettings::UserScope /* nothing better to put */), iniCodec(0), spec(0), fallbacks(true), + : format(format), scope(QSettings::UserScope /* nothing better to put */), iniCodec(0), fallbacks(true), pendingChanges(false), status(QSettings::NoError) { } @@ -228,7 +239,7 @@ QSettingsPrivate::QSettingsPrivate(QSettings::Format format) 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) + iniCodec(0), fallbacks(true), pendingChanges(false), status(QSettings::NoError) { } @@ -940,7 +951,7 @@ void QConfFileSettingsPrivate::initFormat() void QConfFileSettingsPrivate::initAccess() { - if (confFiles[spec]) { + if (!confFiles.isEmpty()) { if (format > QSettings::IniFormat) { if (!readFunc) setStatus(QSettings::AccessError); @@ -1070,22 +1081,22 @@ static void initDefaultPaths(QMutexLocker *locker) const QString programDataFolder = windowsConfigPath(FOLDERID_ProgramData); # endif pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), - roamingAppDataFolder + QDir::separator()); + Path(roamingAppDataFolder + QDir::separator(), false)); pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), - programDataFolder + QDir::separator()); + Path(programDataFolder + QDir::separator(), false)); #else const QString userPath = make_user_path(); - pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), userPath); - pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), systemPath); + pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), Path(userPath, false)); + pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), Path(systemPath, false)); #ifndef Q_OS_MAC - pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), userPath); - pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), systemPath); + pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), Path(userPath, false)); + pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), Path(systemPath, false)); #endif #endif // Q_OS_WIN } } -static QString getPath(QSettings::Format format, QSettings::Scope scope) +static Path getPath(QSettings::Format format, QSettings::Scope scope) { Q_ASSERT((int)QSettings::NativeFormat == 0); Q_ASSERT((int)QSettings::IniFormat == 1); @@ -1095,14 +1106,23 @@ static QString getPath(QSettings::Format format, QSettings::Scope scope) if (pathHash->isEmpty()) initDefaultPaths(&locker); - QString result = pathHash->value(pathHashKey(format, scope)); - if (!result.isEmpty()) + Path result = pathHash->value(pathHashKey(format, scope)); + if (!result.path.isEmpty()) return result; // fall back on INI path return pathHash->value(pathHashKey(QSettings::IniFormat, scope)); } +#if defined(QT_BUILD_INTERNAL) && defined(Q_XDG_PLATFORM) && !defined(QT_NO_STANDARDPATHS) +// Note: Suitable only for autotests. +void Q_AUTOTEST_EXPORT clearDefaultPaths() +{ + QMutexLocker locker(&settingsGlobalMutex); + pathHashFunc()->clear(); +} +#endif // QT_BUILD_INTERNAL && Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS + QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, QSettings::Scope scope, const QString &organization, @@ -1110,7 +1130,6 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, : QSettingsPrivate(format, scope, organization, application), nextPosition(0x40000000) // big positive number { - int i; initFormat(); QString org = organization; @@ -1123,22 +1142,43 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, QString orgFile = org + extension; if (scope == QSettings::UserScope) { - QString userPath = getPath(format, QSettings::UserScope); + Path userPath = getPath(format, QSettings::UserScope); if (!application.isEmpty()) - confFiles[F_User | F_Application].reset(QConfFile::fromName(userPath + appFile, true)); - confFiles[F_User | F_Organization].reset(QConfFile::fromName(userPath + orgFile, true)); + confFiles.append(QConfFile::fromName(userPath.path + appFile, true)); + confFiles.append(QConfFile::fromName(userPath.path + orgFile, true)); } - QString systemPath = getPath(format, QSettings::SystemScope); - if (!application.isEmpty()) - confFiles[F_System | F_Application].reset(QConfFile::fromName(systemPath + appFile, false)); - confFiles[F_System | F_Organization].reset(QConfFile::fromName(systemPath + orgFile, false)); - - for (i = 0; i < NumConfFiles; ++i) { - if (confFiles[i]) { - spec = i; - break; + Path systemPath = getPath(format, QSettings::SystemScope); +#if defined(Q_XDG_PLATFORM) && !defined(QT_NO_STANDARDPATHS) + // check if the systemPath wasn't overridden by QSettings::setPath() + if (!systemPath.userDefined) { + // Note: We can't use QStandardPaths::locateAll() as we need all the + // possible files (not just the existing ones) and there is no way + // to exclude user specific (XDG_CONFIG_HOME) directory from the search. + QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation); + // remove the QStandardLocation::writableLocation() (XDG_CONFIG_HOME) + if (!dirs.isEmpty()) + dirs.takeFirst(); + QStringList paths; + if (!application.isEmpty()) { + paths.reserve(dirs.size() * 2); + for (const auto &dir : qAsConst(dirs)) + paths.append(dir + QLatin1Char('/') + appFile); + } else { + paths.reserve(dirs.size()); } + for (const auto &dir : qAsConst(dirs)) + paths.append(dir + QLatin1Char('/') + orgFile); + + // Note: No check for existence of files is done intentionaly. + for (const auto &path : qAsConst(paths)) + confFiles.append(QConfFile::fromName(path, false)); + } else +#endif // Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS + { + if (!application.isEmpty()) + confFiles.append(QConfFile::fromName(systemPath.path + appFile, false)); + confFiles.append(QConfFile::fromName(systemPath.path + orgFile, false)); } initAccess(); @@ -1151,7 +1191,7 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName, { initFormat(); - confFiles[0].reset(QConfFile::fromName(fileName, true)); + confFiles.append(QConfFile::fromName(fileName, true)); initAccess(); } @@ -1162,40 +1202,39 @@ QConfFileSettingsPrivate::~QConfFileSettingsPrivate() ConfFileHash *usedHash = usedHashFunc(); ConfFileCache *unusedCache = unusedCacheFunc(); - for (int i = 0; i < NumConfFiles; ++i) { - if (confFiles[i] && !confFiles[i]->ref.deref()) { - if (confFiles[i]->size == 0) { - delete confFiles[i].take(); + for (auto conf_file : qAsConst(confFiles)) { + if (!conf_file->ref.deref()) { + if (conf_file->size == 0) { + delete conf_file; } else { if (usedHash) - usedHash->remove(confFiles[i]->name); + usedHash->remove(conf_file->name); if (unusedCache) { QT_TRY { // compute a better size? - unusedCache->insert(confFiles[i]->name, confFiles[i].data(), - 10 + (confFiles[i]->originalKeys.size() / 4)); - confFiles[i].take(); + unusedCache->insert(conf_file->name, conf_file, + 10 + (conf_file->originalKeys.size() / 4)); } QT_CATCH(...) { // out of memory. Do not cache the file. - delete confFiles[i].take(); + delete conf_file; } } else { // unusedCache is gone - delete the entry to prevent a memory leak - delete confFiles[i].take(); + delete conf_file; } } } - // prevent the ScopedPointer to deref it again. - confFiles[i].take(); } } void QConfFileSettingsPrivate::remove(const QString &key) { - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return; + // Note: First config file is always the most specific. + QConfFile *confFile = confFiles.at(0); + QSettingsKey theKey(key, caseSensitivity); QSettingsKey prefix(key + QLatin1Char('/'), caseSensitivity); QMutexLocker locker(&confFile->mutex); @@ -1219,10 +1258,12 @@ void QConfFileSettingsPrivate::remove(const QString &key) void QConfFileSettingsPrivate::set(const QString &key, const QVariant &value) { - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return; + // Note: First config file is always the most specific. + QConfFile *confFile = confFiles.at(0); + QSettingsKey theKey(key, caseSensitivity, nextPosition++); QMutexLocker locker(&confFile->mutex); confFile->removedKeys.remove(theKey); @@ -1235,29 +1276,27 @@ bool QConfFileSettingsPrivate::get(const QString &key, QVariant *value) const ParsedSettingsMap::const_iterator j; bool found = false; - for (int i = 0; i < NumConfFiles; ++i) { - if (QConfFile *confFile = confFiles[i].data()) { - QMutexLocker locker(&confFile->mutex); + for (auto confFile : qAsConst(confFiles)) { + 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 (!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 && value) + *value = *j; - if (found) - return true; - if (!fallbacks) - break; - } + if (found) + return true; + if (!fallbacks) + break; } return false; } @@ -1270,34 +1309,31 @@ QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec QSettingsKey thePrefix(prefix, caseSensitivity); int startPos = prefix.size(); - for (int i = 0; i < NumConfFiles; ++i) { - if (QConfFile *confFile = confFiles[i].data()) { - QMutexLocker locker(&confFile->mutex); + for (auto confFile : qAsConst(confFiles)) { + 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().midRef(startPos), spec, result); - ++j; - } + if (thePrefix.isEmpty()) + ensureAllSectionsParsed(confFile); + else + ensureSectionParsed(confFile, thePrefix); - j = const_cast<const ParsedSettingsMap *>( - &confFile->addedKeys)->lowerBound(thePrefix); - while (j != confFile->addedKeys.constEnd() && j.key().startsWith(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().midRef(startPos), spec, result); - ++j; - } + ++j; + } - if (!fallbacks) - break; + j = const_cast<const ParsedSettingsMap *>( + &confFile->addedKeys)->lowerBound(thePrefix); + while (j != confFile->addedKeys.constEnd() && j.key().startsWith(thePrefix)) { + processChild(j.key().originalCaseKey().midRef(startPos), spec, result); + ++j; } + + if (!fallbacks) + break; } std::sort(result.begin(), result.end()); result.erase(std::unique(result.begin(), result.end()), @@ -1307,10 +1343,12 @@ QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec void QConfFileSettingsPrivate::clear() { - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return; + // Note: First config file is always the most specific. + QConfFile *confFile = confFiles.at(0); + QMutexLocker locker(&confFile->mutex); ensureAllSectionsParsed(confFile); confFile->addedKeys.clear(); @@ -1322,12 +1360,9 @@ 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].data(); - if (confFile) { - QMutexLocker locker(&confFile->mutex); - syncConfFile(i); - } + for (auto confFile : qAsConst(confFiles)) { + QMutexLocker locker(&confFile->mutex); + syncConfFile(confFile); } } @@ -1338,10 +1373,11 @@ void QConfFileSettingsPrivate::flush() QString QConfFileSettingsPrivate::fileName() const { - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return QString(); - return confFile->name; + + // Note: First config file is always the most specific. + return confFiles.at(0)->name; } bool QConfFileSettingsPrivate::isWritable() const @@ -1349,16 +1385,14 @@ bool QConfFileSettingsPrivate::isWritable() const if (format > QSettings::IniFormat && !writeFunc) return false; - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return false; - return confFile->isWritable(); + return confFiles.at(0)->isWritable(); } -void QConfFileSettingsPrivate::syncConfFile(int confFileNo) +void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) { - QConfFile *confFile = confFiles[confFileNo].data(); bool readOnly = confFile->addedKeys.isEmpty() && confFile->removedKeys.isEmpty(); /* @@ -2188,9 +2222,10 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, \list 1 \li \c{$HOME/.config/MySoft/Star Runner.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.conf}) \li \c{$HOME/.config/MySoft.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.conf}) - \li \c{/etc/xdg/MySoft/Star Runner.conf} - \li \c{/etc/xdg/MySoft.conf} + \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.conf} + \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.conf} \endlist + \note If XDG_CONFIG_DIRS is unset, the default value of \c{/etc/xdg} is used. On \macos versions 10.2 and 10.3, these files are used by default: @@ -2225,9 +2260,10 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, \list 1 \li \c{$HOME/.config/MySoft/Star Runner.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.ini}) \li \c{$HOME/.config/MySoft.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.ini}) - \li \c{/etc/xdg/MySoft/Star Runner.ini} - \li \c{/etc/xdg/MySoft.ini} + \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.ini} + \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.ini} \endlist + \note If XDG_CONFIG_DIRS is unset, the default value of \c{/etc/xdg} is used. On Windows, the following files are used: @@ -3380,7 +3416,7 @@ void QSettings::setPath(Format format, Scope scope, const QString &path) PathHash *pathHash = pathHashFunc(); if (pathHash->isEmpty()) initDefaultPaths(&locker); - pathHash->insert(pathHashKey(format, scope), path + QDir::separator()); + pathHash->insert(pathHashKey(format, scope), Path(path + QDir::separator(), true)); } /*! diff --git a/src/corelib/io/qsettings_mac.cpp b/src/corelib/io/qsettings_mac.cpp index 0e3520441e..e4631bad12 100644 --- a/src/corelib/io/qsettings_mac.cpp +++ b/src/corelib/io/qsettings_mac.cpp @@ -424,20 +424,15 @@ QMacSettingsPrivate::QMacSettingsPrivate(QSettings::Scope scope, const QString & javaPackageName.prepend(QLatin1String("com.")); suiteId = javaPackageName; - if (scope == QSettings::SystemScope) - spec |= F_System; - - if (application.isEmpty()) { - spec |= F_Organization; - } else { + if (!application.isEmpty()) { javaPackageName += QLatin1Char('.'); javaPackageName += application; applicationId = javaPackageName; } numDomains = 0; - for (int i = (spec & F_System) ? 1 : 0; i < 2; ++i) { - for (int j = (spec & F_Organization) ? 1 : 0; j < 3; ++j) { + for (int i = (scope == QSettings::SystemScope) ? 1 : 0; i < 2; ++i) { + for (int j = (application.isEmpty()) ? 1 : 0; j < 3; ++j) { SearchDomain &domain = domains[numDomains++]; domain.userName = (i == 0) ? kCFPreferencesCurrentUser : kCFPreferencesAnyUser; if (j == 0) @@ -578,7 +573,7 @@ bool QMacSettingsPrivate::isWritable() const QString QMacSettingsPrivate::fileName() const { QString result; - if ((spec & F_System) == 0) + if (scope == QSettings::UserScope) result = QDir::homePath(); result += QLatin1String("/Library/Preferences/"); result += QString::fromCFString(domains[0].applicationOrSuiteId); diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h index e6d3413bab..639605d8c4 100644 --- a/src/corelib/io/qsettings_p.h +++ b/src/corelib/io/qsettings_p.h @@ -236,19 +236,6 @@ public: QTextCodec *codec); static QStringList splitArgs(const QString &s, int idx); - /* - The numeric values of these enums define their search order. For example, - F_User | F_Organization is searched before F_System | F_Application, - because their values are respectively 1 and 2. - */ - enum { - F_Application = 0x0, - F_Organization = 0x1, - F_User = 0x0, - F_System = 0x2, - NumConfFiles = 4 - }; - QSettings::Format format; QSettings::Scope scope; QString organizationName; @@ -258,7 +245,6 @@ public: protected: QStack<QSettingsGroup> groupStack; QString groupPrefix; - int spec; bool fallbacks; bool pendingChanges; mutable QSettings::Status status; @@ -293,7 +279,7 @@ public: private: void initFormat(); void initAccess(); - void syncConfFile(int confFileNo); + void syncConfFile(QConfFile *confFile); bool writeIniFile(QIODevice &device, const ParsedSettingsMap &map); #ifdef Q_OS_MAC bool readPlistFile(const QByteArray &data, ParsedSettingsMap *map) const; @@ -302,7 +288,7 @@ private: void ensureAllSectionsParsed(QConfFile *confFile) const; void ensureSectionParsed(QConfFile *confFile, const QSettingsKey &key) const; - QScopedSharedPointer<QConfFile> confFiles[NumConfFiles]; + QVector<QConfFile *> confFiles; QSettings::ReadFunc readFunc; QSettings::WriteFunc writeFunc; QString extension; diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp index 1c10548cbc..edcae16776 100644 --- a/src/corelib/io/qsettings_win.cpp +++ b/src/corelib/io/qsettings_win.cpp @@ -138,21 +138,6 @@ static void mergeKeySets(NameSet *dest, const QStringList &src) ** Wrappers for the insane windows registry API */ -static QString errorCodeToString(DWORD errorCode) -{ - wchar_t *data = 0; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, errorCode, 0, data, 0, 0); - QString result = QString::fromWCharArray(data); - - if (data != 0) - LocalFree(data); - - if (result.endsWith(QLatin1Char('\n'))) - result.truncate(result.length() - 1); - - return result; -} - // Open a key with the specified "perms". // "access" is to explicitly use the 32- or 64-bit branch. static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey, REGSAM access = 0) @@ -184,7 +169,7 @@ static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSub return resultHandle; //qWarning("QSettings: Failed to create subkey \"%s\": %s", - // rSubKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + // qPrintable(rSubKey), qPrintable(qt_error_string(int(res)))); return 0; } @@ -224,7 +209,7 @@ static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildS &numKeys, &maxKeySize, 0, 0, 0); if (res != ERROR_SUCCESS) { - qWarning("QSettings: RegQueryInfoKey() failed: %s", errorCodeToString(res).toLatin1().data()); + qWarning("QSettings: RegQueryInfoKey() failed: %s", qPrintable(qt_error_string(int(res)))); return result; } @@ -258,7 +243,7 @@ static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildS item = QString::fromWCharArray((const wchar_t *)buff.constData(), l); if (res != ERROR_SUCCESS) { - qWarning("QSettings: RegEnumValue failed: %s", errorCodeToString(res).toLatin1().data()); + qWarning("QSettings: RegEnumValue failed: %s", qPrintable(qt_error_string(int(res)))); continue; } if (item.isEmpty()) @@ -313,7 +298,7 @@ static void deleteChildGroups(HKEY parentHandle, REGSAM access = 0) LONG res = RegDeleteKey(parentHandle, reinterpret_cast<const wchar_t *>(group.utf16())); if (res != ERROR_SUCCESS) { qWarning("QSettings: RegDeleteKey failed on subkey \"%s\": %s", - group.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(group), qPrintable(qt_error_string(int(res)))); return; } } @@ -613,7 +598,7 @@ QWinSettingsPrivate::~QWinSettingsPrivate() DWORD res = RegDeleteKey(writeHandle(), reinterpret_cast<const wchar_t *>(emptyKey.utf16())); if (res != ERROR_SUCCESS) { qWarning("QSettings: Failed to delete key \"%s\": %s", - regList.at(0).key().toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(regList.at(0).key()), qPrintable(qt_error_string(int(res)))); } } @@ -652,7 +637,7 @@ void QWinSettingsPrivate::remove(const QString &uKey) LONG res = RegDeleteValue(handle, reinterpret_cast<const wchar_t *>(group.utf16())); if (res != ERROR_SUCCESS) { qWarning("QSettings: RegDeleteValue failed on subkey \"%s\": %s", - group.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(group), qPrintable(qt_error_string(int(res)))); } } } else { @@ -660,7 +645,7 @@ void QWinSettingsPrivate::remove(const QString &uKey) if (res != ERROR_SUCCESS) { qWarning("QSettings: RegDeleteKey failed on key \"%s\": %s", - rKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(rKey), qPrintable(qt_error_string(int(res)))); } } RegCloseKey(handle); @@ -758,7 +743,7 @@ void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value) deleteWriteHandleOnExit = false; } else { qWarning("QSettings: failed to set subkey \"%s\": %s", - rKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(rKey), qPrintable(qt_error_string(int(res)))); setStatus(QSettings::AccessError); } diff --git a/src/corelib/io/qstorageinfo.cpp b/src/corelib/io/qstorageinfo.cpp index 3773b72606..27f0552a31 100644 --- a/src/corelib/io/qstorageinfo.cpp +++ b/src/corelib/io/qstorageinfo.cpp @@ -260,7 +260,7 @@ QByteArray QStorageInfo::fileSystemType() const devpath like \c /dev/sda0 for local storages. On Windows, it returns the UNC path starting with \c \\\\?\\ for local storages (in other words, the volume GUID). - \sa rootPath() + \sa rootPath(), subvolume() */ QByteArray QStorageInfo::device() const { @@ -268,6 +268,26 @@ QByteArray QStorageInfo::device() const } /*! + \since 5.9 + Returns the subvolume name for this volume. + + Some filesystem types allow multiple subvolumes inside one device, which + may be mounted in different paths. If the subvolume could be detected, it + is returned here. The format of the subvolume name is specific to each + filesystem type. + + If this volume was not mounted from a subvolume of a larger filesystem or + if the subvolume could not be detected, this function returns an empty byte + array. + + \sa device() +*/ +QByteArray QStorageInfo::subvolume() const +{ + return d->subvolume; +} + +/*! Returns the human-readable name of a filesystem, usually called \c label. Not all filesystems support this feature. In this case, the value returned by diff --git a/src/corelib/io/qstorageinfo.h b/src/corelib/io/qstorageinfo.h index 8941c11a16..e2d9747ceb 100644 --- a/src/corelib/io/qstorageinfo.h +++ b/src/corelib/io/qstorageinfo.h @@ -71,6 +71,7 @@ public: QString rootPath() const; QByteArray device() const; + QByteArray subvolume() const; QByteArray fileSystemType() const; QString name() const; QString displayName() const; @@ -100,7 +101,7 @@ inline bool operator==(const QStorageInfo &first, const QStorageInfo &second) { if (first.d == second.d) return true; - return first.device() == second.device(); + return first.device() == second.device() && first.rootPath() == second.rootPath(); } inline bool operator!=(const QStorageInfo &first, const QStorageInfo &second) diff --git a/src/corelib/io/qstorageinfo_p.h b/src/corelib/io/qstorageinfo_p.h index a14fa8480a..ec5bb785e3 100644 --- a/src/corelib/io/qstorageinfo_p.h +++ b/src/corelib/io/qstorageinfo_p.h @@ -85,6 +85,7 @@ protected: public: QString rootPath; QByteArray device; + QByteArray subvolume; QByteArray fileSystemType; QString name; diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index ae5c42ffd1..fcc7b8ca50 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -120,6 +120,7 @@ public: inline QString rootPath() const; inline QByteArray fileSystemType() const; inline QByteArray device() const; + inline QByteArray options() const; private: #if defined(Q_OS_BSD4) QT_STATFSBUF *stat_buf; @@ -133,6 +134,7 @@ private: QByteArray m_rootPath; QByteArray m_fileSystemType; QByteArray m_device; + QByteArray m_options; #elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) FILE *fp; mntent mnt; @@ -228,6 +230,11 @@ inline QByteArray QStorageIterator::device() const return QByteArray(stat_buf[currentIndex].f_mntfromname); } +inline QByteArray QStorageIterator::options() const +{ + return QByteArray(); +} + #elif defined(Q_OS_SOLARIS) static const char pathMounted[] = "/etc/mnttab"; @@ -301,6 +308,7 @@ inline bool QStorageIterator::next() m_device = data.at(0); m_rootPath = data.at(1); m_fileSystemType = data.at(2); + m_options = data.at(3); return true; } @@ -320,6 +328,11 @@ inline QByteArray QStorageIterator::device() const return m_device; } +inline QByteArray QStorageIterator::options() const +{ + return m_options; +} + #elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) static const char pathMounted[] = "/etc/mtab"; @@ -363,6 +376,11 @@ inline QByteArray QStorageIterator::device() const return QByteArray(mnt.mnt_fsname); } +inline QByteArray QStorageIterator::options() const +{ + return QByteArray(mnt.mnt_opts); +} + #elif defined(Q_OS_HAIKU) inline QStorageIterator::QStorageIterator() { @@ -420,6 +438,11 @@ inline QByteArray QStorageIterator::device() const return m_device; } +inline QByteArray QStorageIterator::options() const +{ + return QByteArray(); +} + #else inline QStorageIterator::QStorageIterator() @@ -455,8 +478,35 @@ inline QByteArray QStorageIterator::device() const return QByteArray(); } +inline QByteArray QStorageIterator::options() const +{ + return QByteArray(); +} + #endif +static QByteArray extractSubvolume(const QStorageIterator &it) +{ +#ifdef Q_OS_LINUX + if (it.fileSystemType() == "btrfs") { + const QByteArrayList opts = it.options().split(','); + QByteArray id; + for (const QByteArray &opt : opts) { + static const char subvol[] = "subvol="; + static const char subvolid[] = "subvolid="; + if (opt.startsWith(subvol)) + return std::move(opt).mid(strlen(subvol)); + if (opt.startsWith(subvolid)) + id = std::move(opt).mid(strlen(subvolid)); + } + + // if we didn't find the subvolume name, return the subvolume ID + return id; + } +#endif + return QByteArray(); +} + void QStorageInfoPrivate::initRootPath() { rootPath = QFileInfo(rootPath).canonicalFilePath(); @@ -483,6 +533,7 @@ void QStorageInfoPrivate::initRootPath() rootPath = mountDir; device = it.device(); fileSystemType = fsName; + subvolume = extractSubvolume(it); } } } diff --git a/src/corelib/io/qtemporarydir.cpp b/src/corelib/io/qtemporarydir.cpp index 6e50a8513e..b2bf9fce97 100644 --- a/src/corelib/io/qtemporarydir.cpp +++ b/src/corelib/io/qtemporarydir.cpp @@ -305,6 +305,33 @@ QString QTemporaryDir::path() const } /*! + \since 5.9 + + Returns the path name of a file in the temporary directory. + Does \e not check if the file actually exists in the directory. + Redundant multiple separators or "." and ".." directories in + \a fileName are not removed (see QDir::cleanPath()). Absolute + paths are not allowed. +*/ +QString QTemporaryDir::filePath(const QString &fileName) const +{ + if (QDir::isAbsolutePath(fileName)) { + qWarning("QTemporaryDir::filePath: Absolute paths are not allowed: %s", qUtf8Printable(fileName)); + return QString(); + } + + if (!d_ptr->success) + return QString(); + + QString ret = d_ptr->pathOrError; + if (!fileName.isEmpty()) { + ret += QLatin1Char('/'); + ret += fileName; + } + return ret; +} + +/*! Returns \c true if the QTemporaryDir is in auto remove mode. Auto-remove mode will automatically delete the directory from disk upon destruction. This makes it very easy to create your diff --git a/src/corelib/io/qtemporarydir.h b/src/corelib/io/qtemporarydir.h index 2e70d34ae4..3f6b70a2eb 100644 --- a/src/corelib/io/qtemporarydir.h +++ b/src/corelib/io/qtemporarydir.h @@ -65,6 +65,7 @@ public: bool remove(); QString path() const; + QString filePath(const QString &fileName) const; private: QScopedPointer<QTemporaryDirPrivate> d_ptr; diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index 4fdb1505ff..9b565bff9d 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -2547,6 +2547,7 @@ QTextStream &QTextStream::operator<<(double f) } uint flags = 0; + const QLocale::NumberOptions numberOptions = locale().numberOptions(); if (numberFlags() & ShowBase) flags |= QLocaleData::ShowBase; if (numberFlags() & ForceSign) @@ -2555,12 +2556,18 @@ QTextStream &QTextStream::operator<<(double f) flags |= QLocaleData::UppercaseBase; if (numberFlags() & UppercaseDigits) flags |= QLocaleData::CapitalEorX; - if (numberFlags() & ForcePoint) - flags |= QLocaleData::Alternate; - if (locale() != QLocale::c() && !(locale().numberOptions() & QLocale::OmitGroupSeparator)) + if (numberFlags() & ForcePoint) { + flags |= QLocaleData::ForcePoint; + + // Only for backwards compatibility + flags |= QLocaleData::AddTrailingZeroes | QLocaleData::ShowBase; + } + if (locale() != QLocale::c() && !(numberOptions & QLocale::OmitGroupSeparator)) flags |= QLocaleData::ThousandsGroup; - if (!(locale().numberOptions() & QLocale::OmitLeadingZeroInExponent)) + if (!(numberOptions & QLocale::OmitLeadingZeroInExponent)) flags |= QLocaleData::ZeroPadExponent; + if (numberOptions & QLocale::IncludeTrailingZeroesAfterDot) + flags |= QLocaleData::AddTrailingZeroes; const QLocaleData *dd = d->locale.d->m_data; QString num = dd->doubleToString(f, d->params.realNumberPrecision, form, -1, flags); diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 520ad2e5d3..18ad59f1cb 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -3182,8 +3182,7 @@ QUrl QUrl::resolved(const QUrl &relative) const if (!relative.d) return *this; QUrl t; - // Compatibility hack (mostly for qtdeclarative) : treat "file:relative.txt" as relative even though QUrl::isRelative() says false - if (!relative.d->scheme.isEmpty() && (!relative.isLocalFile() || QDir::isAbsolutePath(relative.d->path))) { + if (!relative.d->scheme.isEmpty()) { t = relative; t.detach(); } else { diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h index a554a3b07e..c16825a033 100644 --- a/src/corelib/io/qurl.h +++ b/src/corelib/io/qurl.h @@ -69,8 +69,8 @@ public: Q_DECL_CONSTEXPR inline QUrlTwoFlags(E1 f) : i(f) {} Q_DECL_CONSTEXPR inline QUrlTwoFlags(E2 f) : i(f) {} Q_DECL_CONSTEXPR inline QUrlTwoFlags(QFlag f) : i(f) {} - Q_DECL_CONSTEXPR inline QUrlTwoFlags(QFlags<E1> f) : i(f.operator int()) {} - Q_DECL_CONSTEXPR inline QUrlTwoFlags(QFlags<E2> f) : i(f.operator int()) {} + Q_DECL_CONSTEXPR inline QUrlTwoFlags(QFlags<E1> f) : i(f.operator typename QFlags<E1>::Int()) {} + Q_DECL_CONSTEXPR inline QUrlTwoFlags(QFlags<E2> f) : i(f.operator typename QFlags<E2>::Int()) {} Q_DECL_CONSTEXPR inline QUrlTwoFlags(Zero = 0) : i(0) {} inline QUrlTwoFlags &operator&=(int mask) { i &= mask; return *this; } |