diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/io/qstorageinfo_unix.cpp | 235 | ||||
-rw-r--r-- | src/corelib/kernel/qsystemerror_p.h | 52 | ||||
-rw-r--r-- | src/corelib/mimetypes/mimetypes.qrc | 2 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimedatabase.cpp | 235 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimedatabase_p.h | 27 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimeglobpattern.cpp | 5 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimeglobpattern_p.h | 2 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimemagicrule.cpp | 2 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimeprovider.cpp | 521 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimeprovider_p.h | 64 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimetype.cpp | 23 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimetype_p.h | 3 | ||||
-rw-r--r-- | src/corelib/mimetypes/qmimetypeparser.cpp | 1 | ||||
-rw-r--r-- | src/corelib/tools/qsimd_p.h | 6 | ||||
-rw-r--r-- | src/gui/kernel/qplatformtheme.cpp | 3 | ||||
-rw-r--r-- | src/plugins/styles/mac/qmacstyle_mac.mm | 3 |
16 files changed, 704 insertions, 480 deletions
diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index cdaa6329f9..6d8b6ee1a0 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -45,6 +45,7 @@ #include <QtCore/qtextstream.h> #include <QtCore/private/qcore_unix_p.h> +#include <QtCore/private/qlocale_tools_p.h> #include <errno.h> #include <sys/stat.h> @@ -121,6 +122,7 @@ public: inline QByteArray fileSystemType() const; inline QByteArray device() const; inline QByteArray options() const; + inline QByteArray subvolume() const; private: #if defined(Q_OS_BSD4) QT_STATFSBUF *stat_buf; @@ -136,9 +138,36 @@ private: QByteArray m_device; QByteArray m_options; #elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) + struct mountinfoent : public mntent { + // Details from proc(5) section from /proc/<pid>/mountinfo: + //(1) mount ID: a unique ID for the mount (may be reused after umount(2)). + int mount_id; + //(2) parent ID: the ID of the parent mount (or of self for the top of the mount tree). +// int parent_id; + //(3) major:minor: the value of st_dev for files on this filesystem (see stat(2)). +// dev_t rdev; + //(4) root: the pathname of the directory in the filesystem which forms the root of this mount. + char *subvolume; + //(5) mount point: the pathname of the mount point relative to the process's root directory. +// char *mnt_dir; // in mntent + //(6) mount options: per-mount options. +// char *mnt_opts; // in mntent + //(7) optional fields: zero or more fields of the form "tag[:value]"; see below. +// int flags; + //(8) separator: the end of the optional fields is marked by a single hyphen. + + //(9) filesystem type: the filesystem type in the form "type[.subtype]". +// char *mnt_type; // in mntent + //(10) mount source: filesystem-specific information or "none". +// char *mnt_fsname; // in mntent + //(11) super options: per-superblock options. + char *superopts; + }; + FILE *fp; - mntent mnt; QByteArray buffer; + mountinfoent mnt; + bool usingMountinfo; #elif defined(Q_OS_HAIKU) BVolumeRoster m_volumeRoster; @@ -239,6 +268,10 @@ inline QByteArray QStorageIterator::options() const return QByteArray(); } +inline QByteArray QStorageIterator::subvolume() const +{ + return QByteArray(); +} #elif defined(Q_OS_SOLARIS) static const char pathMounted[] = "/etc/mnttab"; @@ -280,6 +313,10 @@ inline QByteArray QStorageIterator::device() const return QByteArray(mnt.mnt_mntopts); } +inline QByteArray QStorageIterator::subvolume() const +{ + return QByteArray(); +} #elif defined(Q_OS_ANDROID) static const QLatin1String pathMounted("/proc/mounts"); @@ -337,6 +374,10 @@ inline QByteArray QStorageIterator::options() const return m_options; } +inline QByteArray QStorageIterator::subvolume() const +{ + return QByteArray(); +} #elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) static const char pathMounted[] = "/etc/mtab"; @@ -346,13 +387,28 @@ static const int bufferSize = 1024; // 2 paths (mount point+device) and metainfo inline QStorageIterator::QStorageIterator() : buffer(QByteArray(bufferSize, 0)) { - fp = ::setmntent(pathMounted, "r"); + fp = nullptr; + +#ifdef Q_OS_LINUX + // first, try to open /proc/self/mountinfo, which has more details + fp = ::fopen("/proc/self/mountinfo", "re"); +#endif + if (fp) { + usingMountinfo = true; + } else { + usingMountinfo = false; + fp = ::setmntent(pathMounted, "r"); + } } inline QStorageIterator::~QStorageIterator() { - if (fp) - ::endmntent(fp); + if (fp) { + if (usingMountinfo) + ::fclose(fp); + else + ::endmntent(fp); + } } inline bool QStorageIterator::isValid() const @@ -362,7 +418,127 @@ inline bool QStorageIterator::isValid() const inline bool QStorageIterator::next() { - return ::getmntent_r(fp, &mnt, buffer.data(), buffer.size()) != nullptr; + mnt.subvolume = nullptr; + mnt.superopts = nullptr; + if (!usingMountinfo) + return ::getmntent_r(fp, &mnt, buffer.data(), buffer.size()) != nullptr; + + // Helper function to parse paths that the kernel inserts escape sequences + // for. The unescaped string is left at \a src and is properly + // NUL-terminated. Returns a pointer to the delimiter that terminated the + // path, or nullptr if it failed. + auto parseMangledPath = [](char *src) { + // The kernel escapes with octal the following characters: + // space ' ', tab '\t', backslask '\\', and newline '\n' + char *dst = src; + while (*src) { + switch (*src) { + case ' ': + // Unescaped space: end of the field. + *dst = '\0'; + return src; + + default: + *dst++ = *src++; + break; + + case '\\': + // It always uses exactly three octal characters. + ++src; + char c = (*src++ - '0') << 6; + c |= (*src++ - '0') << 3; + c |= (*src++ - '0'); + *dst++ = c; + break; + } + } + + // Found a NUL before the end of the field. + src = nullptr; + return src; + }; + + char *ptr = buffer.data(); + if (fgets(ptr, buffer.size(), fp) == nullptr) + return false; + + size_t len = strlen(buffer.data()); + if (len == 0) + return false; + if (ptr[len - 1] == '\n') + ptr[len - 1] = '\0'; + + // parse the line + bool ok; + mnt.mnt_freq = 0; + mnt.mnt_passno = 0; + + mnt.mount_id = qstrtoll(ptr, const_cast<const char **>(&ptr), 10, &ok); + if (!ptr || !ok) + return false; + + int parent_id = qstrtoll(ptr, const_cast<const char **>(&ptr), 10, &ok); + Q_UNUSED(parent_id); + if (!ptr || !ok) + return false; + + int rdevmajor = qstrtoll(ptr, const_cast<const char **>(&ptr), 10, &ok); + if (!ptr || !ok) + return false; + if (*ptr != ':') + return false; + int rdevminor = qstrtoll(ptr + 1, const_cast<const char **>(&ptr), 10, &ok); + if (!ptr || !ok) + return false; + Q_UNUSED(rdevmajor); + Q_UNUSED(rdevminor); + + if (*ptr != ' ') + return false; + + mnt.subvolume = ++ptr; + ptr = parseMangledPath(ptr); + if (!ptr) + return false; + + // unset a subvolume of "/" -- it's not a *sub* volume + if (mnt.subvolume + 1 == ptr) + *mnt.subvolume = '\0'; + + mnt.mnt_dir = ++ptr; + ptr = parseMangledPath(ptr); + if (!ptr) + return false; + + mnt.mnt_opts = ++ptr; + ptr = strchr(ptr, ' '); + if (!ptr) + return false; + + // we don't parse the flags, so just find the separator + if (char *const dashed = strstr(ptr, " - ")) { + *ptr = '\0'; + ptr = dashed + strlen(" - ") - 1; + } else { + return false; + } + + mnt.mnt_type = ++ptr; + ptr = strchr(ptr, ' '); + if (!ptr) + return false; + *ptr = '\0'; + + mnt.mnt_fsname = ++ptr; + ptr = parseMangledPath(ptr); + if (!ptr) + return false; + + mnt.superopts = ++ptr; + ptr += strcspn(ptr, " \n"); + *ptr = '\0'; + + return true; } inline QString QStorageIterator::rootPath() const @@ -382,9 +558,28 @@ inline QByteArray QStorageIterator::device() const inline QByteArray QStorageIterator::options() const { + // Merge the two options, starting with the superblock options and letting + // the per-mount options override. + const char *superopts = mnt.superopts; + + // Both mnt_opts and superopts start with "ro" or "rw", so we can skip the + // superblock's field (see show_mountinfo() in fs/proc_namespace.c). + if (superopts && superopts[0] == 'r') { + if (superopts[2] == '\0') // no other superopts besides "ro" / "rw"? + superopts = nullptr; + else if (superopts[2] == ',') + superopts += 3; + } + + if (superopts) + return QByteArray(superopts) + ',' + mnt.mnt_opts; return QByteArray(mnt.mnt_opts); } +inline QByteArray QStorageIterator::subvolume() const +{ + return QByteArray(mnt.subvolume); +} #elif defined(Q_OS_HAIKU) inline QStorageIterator::QStorageIterator() { @@ -447,6 +642,10 @@ inline QByteArray QStorageIterator::options() const return QByteArray(); } +inline QByteArray QStorageIterator::subvolume() const +{ + return QByteArray(); +} #else inline QStorageIterator::QStorageIterator() @@ -487,31 +686,11 @@ inline QByteArray QStorageIterator::options() const return QByteArray(); } -#endif - -static QByteArray extractSubvolume(const QStorageIterator &it) +inline QByteArray QStorageIterator::subvolume() const { -#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; - } -#else - Q_UNUSED(it); -#endif return QByteArray(); } +#endif void QStorageInfoPrivate::initRootPath() { @@ -539,7 +718,7 @@ void QStorageInfoPrivate::initRootPath() rootPath = mountDir; device = it.device(); fileSystemType = fsName; - subvolume = extractSubvolume(it); + subvolume = it.subvolume(); } } } diff --git a/src/corelib/kernel/qsystemerror_p.h b/src/corelib/kernel/qsystemerror_p.h index 1d8c253f53..abc2f1efc1 100644 --- a/src/corelib/kernel/qsystemerror_p.h +++ b/src/corelib/kernel/qsystemerror_p.h @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE -class Q_CORE_EXPORT QSystemError +class QSystemError { public: enum ErrorScope @@ -66,51 +66,27 @@ public: NativeError }; - inline QSystemError(int error, ErrorScope scope); - inline QSystemError(); + Q_DECL_CONSTEXPR explicit QSystemError(int error, ErrorScope scope) + : errorCode(error), errorScope(scope) + { + } + Q_DECL_CONSTEXPR QSystemError() = default; - inline QString toString() const; - inline ErrorScope scope() const; - inline int error() const; + QString toString() const { return string(errorScope, errorCode); } + Q_DECL_CONSTEXPR ErrorScope scope() const { return errorScope; } + Q_DECL_CONSTEXPR int error() const { return errorCode; } - static QString string(ErrorScope errorScope, int errorCode); - static QString stdString(int errorCode = -1); + static Q_CORE_EXPORT QString string(ErrorScope errorScope, int errorCode); + static Q_CORE_EXPORT QString stdString(int errorCode = -1); #ifdef Q_OS_WIN - static QString windowsString(int errorCode = -1); + static Q_CORE_EXPORT QString windowsString(int errorCode = -1); #endif //data members - int errorCode; - ErrorScope errorScope; + int errorCode = 0; + ErrorScope errorScope = NoError; }; -QSystemError::QSystemError(int error, QSystemError::ErrorScope scope) -: errorCode(error), errorScope(scope) -{ - -} - -QSystemError::QSystemError() -: errorCode(0), errorScope(NoError) -{ - -} - -QString QSystemError::toString() const -{ - return string(errorScope, errorCode); -} - -QSystemError::ErrorScope QSystemError::scope() const -{ - return errorScope; -} - -int QSystemError::error() const -{ - return errorCode; -} - QT_END_NAMESPACE #endif // QSYSTEMERROR_P_H diff --git a/src/corelib/mimetypes/mimetypes.qrc b/src/corelib/mimetypes/mimetypes.qrc index f0cf47cd49..19bc1d3e2a 100644 --- a/src/corelib/mimetypes/mimetypes.qrc +++ b/src/corelib/mimetypes/mimetypes.qrc @@ -1,5 +1,5 @@ <RCC> - <qresource prefix="/qt-project.org/qmime"> + <qresource prefix="/qt-project.org/qmime/packages"> <file alias="freedesktop.org.xml">mime/packages/freedesktop.org.xml</file> </qresource> </RCC> diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp index 687f0b3e03..f90fd12e3b 100644 --- a/src/corelib/mimetypes/qmimedatabase.cpp +++ b/src/corelib/mimetypes/qmimedatabase.cpp @@ -51,6 +51,7 @@ #include <QtCore/QFile> #include <QtCore/QFileInfo> #include <QtCore/QSet> +#include <QtCore/QStandardPaths> #include <QtCore/QBuffer> #include <QtCore/QUrl> #include <QtCore/QDebug> @@ -69,34 +70,102 @@ QMimeDatabasePrivate *QMimeDatabasePrivate::instance() } QMimeDatabasePrivate::QMimeDatabasePrivate() - : m_provider(0), m_defaultMimeType(QLatin1String("application/octet-stream")) + : m_defaultMimeType(QLatin1String("application/octet-stream")) { } QMimeDatabasePrivate::~QMimeDatabasePrivate() { - delete m_provider; - m_provider = 0; + qDeleteAll(m_providers); } -QMimeProviderBase *QMimeDatabasePrivate::provider() +Q_CORE_EXPORT int qmime_secondsBetweenChecks = 5; // exported for the unit test + +bool QMimeDatabasePrivate::shouldCheck() +{ + if (m_lastCheck.isValid() && m_lastCheck.elapsed() < qmime_secondsBetweenChecks * 1000) + return false; + m_lastCheck.start(); + return true; +} + +#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) +#define QT_USE_MMAP +#endif + +void QMimeDatabasePrivate::loadProviders() { - if (!m_provider) { - QMimeProviderBase *binaryProvider = new QMimeBinaryProvider(this); - if (binaryProvider->isValid()) { - m_provider = binaryProvider; + // We use QStandardPaths every time to check if new files appeared + QStringList mimeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory); + const auto fdoIterator = std::find_if(mimeDirs.constBegin(), mimeDirs.constEnd(), [](const QString &mimeDir) -> bool { + return QFileInfo::exists(mimeDir + QStringLiteral("/packages/freedesktop.org.xml")); } + ); + if (fdoIterator == mimeDirs.constEnd()) + mimeDirs.prepend(QLatin1String(":/qt-project.org/qmime")); + //qDebug() << "mime dirs:" << mimeDirs; + + QVector<QMimeProviderBase *> currentProviders = m_providers; + m_providers.clear(); + m_providers.reserve(mimeDirs.size()); + for (const QString &mimeDir : qAsConst(mimeDirs)) { + const QString cacheFile = mimeDir + QStringLiteral("/mime.cache"); + QFileInfo fileInfo(cacheFile); + // Check if we already have a provider for this dir + const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), [mimeDir](QMimeProviderBase *prov) { return prov->directory() == mimeDir; }); + if (it == currentProviders.end()) { + QMimeProviderBase *provider = nullptr; +#if defined(QT_USE_MMAP) + if (qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE") && fileInfo.exists()) { + provider = new QMimeBinaryProvider(this, mimeDir); + //qDebug() << "Created binary provider for" << mimeDir; + if (!provider->isValid()) { + delete provider; + provider = nullptr; + } + } +#endif + if (!provider) { + provider = new QMimeXMLProvider(this, mimeDir); + //qDebug() << "Created XML provider for" << mimeDir; + } + m_providers.append(provider); } else { - delete binaryProvider; - m_provider = new QMimeXMLProvider(this); + QMimeProviderBase *provider = *it; + currentProviders.erase(it); + provider->ensureLoaded(); + if (!provider->isValid()) { + delete provider; + provider = new QMimeXMLProvider(this, mimeDir); + //qDebug() << "Created XML provider to replace binary provider for" << mimeDir; + } + m_providers.append(provider); } } - return m_provider; + qDeleteAll(currentProviders); } -void QMimeDatabasePrivate::setProvider(QMimeProviderBase *theProvider) +QVector<QMimeProviderBase *> QMimeDatabasePrivate::providers() { - delete m_provider; - m_provider = theProvider; + Q_ASSERT(!mutex.tryLock()); // caller should have locked mutex + if (m_providers.isEmpty()) { + loadProviders(); + m_lastCheck.start(); + } else { + if (shouldCheck()) + loadProviders(); + } + return m_providers; +} + +QString QMimeDatabasePrivate::resolveAlias(const QString &nameOrAlias) +{ + const auto allProviders = providers(); + for (QMimeProviderBase *provider : allProviders) { + const QString ret = provider->resolveAlias(nameOrAlias); + if (!ret.isEmpty()) + return ret; + } + return nameOrAlias; } /*! @@ -105,7 +174,14 @@ void QMimeDatabasePrivate::setProvider(QMimeProviderBase *theProvider) */ QMimeType QMimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias) { - return provider()->mimeTypeForName(provider()->resolveAlias(nameOrAlias)); + const auto allProviders = providers(); + const QString mimeName = resolveAlias(nameOrAlias); + for (QMimeProviderBase *provider : allProviders) { + const QMimeType mime = provider->mimeTypeForName(mimeName); + if (mime.isValid()) + return mime; + } + return {}; } QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName) @@ -113,11 +189,113 @@ QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName) if (fileName.endsWith(QLatin1Char('/'))) return QStringList() << QLatin1String("inode/directory"); - QStringList matchingMimeTypes = provider()->findByFileName(QFileInfo(fileName).fileName()).m_matchingMimeTypes; + const QString shortName = QFileInfo(fileName).fileName(); + const QMimeGlobMatchResult result = findByFileName(shortName); + QStringList matchingMimeTypes = result.m_matchingMimeTypes; matchingMimeTypes.sort(); // make it deterministic return matchingMimeTypes; } +QMimeGlobMatchResult QMimeDatabasePrivate::findByFileName(const QString &fileName) +{ + QMimeGlobMatchResult result; + // TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly. + const auto allProviders = providers(); + for (QMimeProviderBase *provider : allProviders) + provider->addFileNameMatches(fileName, result); + return result; +} + +void QMimeDatabasePrivate::loadMimeTypePrivate(QMimeTypePrivate &mimePrivate) +{ + QMutexLocker locker(&mutex); + if (!mimePrivate.loaded) { // XML provider sets loaded=true, binary provider does this on demand + Q_ASSERT(mimePrivate.fromCache); + QMimeBinaryProvider::loadMimeTypePrivate(mimePrivate); + } +} + +void QMimeDatabasePrivate::loadGenericIcon(QMimeTypePrivate &mimePrivate) +{ + QMutexLocker locker(&mutex); + if (mimePrivate.fromCache) { + mimePrivate.genericIconName.clear(); + const auto allProviders = providers(); + for (QMimeProviderBase *provider : allProviders) { + provider->loadGenericIcon(mimePrivate); + if (!mimePrivate.genericIconName.isEmpty()) + break; + } + } +} + +void QMimeDatabasePrivate::loadIcon(QMimeTypePrivate &mimePrivate) +{ + QMutexLocker locker(&mutex); + if (mimePrivate.fromCache) { + mimePrivate.iconName.clear(); + const auto allProviders = providers(); + for (QMimeProviderBase *provider : allProviders) { + provider->loadIcon(mimePrivate); + if (!mimePrivate.iconName.isEmpty()) + break; + } + } +} + +static QString fallbackParent(const QString &mimeTypeName) +{ + const QStringRef myGroup = mimeTypeName.leftRef(mimeTypeName.indexOf(QLatin1Char('/'))); + // All text/* types are subclasses of text/plain. + if (myGroup == QLatin1String("text") && mimeTypeName != QLatin1String("text/plain")) + return QLatin1String("text/plain"); + // All real-file mimetypes implicitly derive from application/octet-stream + if (myGroup != QLatin1String("inode") && + // ignore non-file extensions + myGroup != QLatin1String("all") && myGroup != QLatin1String("fonts") && myGroup != QLatin1String("print") && myGroup != QLatin1String("uri") + && mimeTypeName != QLatin1String("application/octet-stream")) { + return QLatin1String("application/octet-stream"); + } + return QString(); +} + +QStringList QMimeDatabasePrivate::mimeParents(const QString &mimeName) +{ + QMutexLocker locker(&mutex); + return parents(mimeName); +} + +QStringList QMimeDatabasePrivate::parents(const QString &mimeName) +{ + Q_ASSERT(!mutex.tryLock()); + QStringList result; + const auto allProviders = providers(); + for (QMimeProviderBase *provider : allProviders) + provider->addParents(mimeName, result); + if (result.isEmpty()) { + const QString parent = fallbackParent(mimeName); + if (!parent.isEmpty()) + result.append(parent); + } + return result; +} + +QStringList QMimeDatabasePrivate::listAliases(const QString &mimeName) +{ + QMutexLocker locker(&mutex); + QStringList result; + const auto allProviders = providers(); + for (QMimeProviderBase *provider : allProviders) + provider->addAliases(mimeName, result); + return result; +} + +bool QMimeDatabasePrivate::mimeInherits(const QString &mime, const QString &parent) +{ + QMutexLocker locker(&mutex); + return inherits(mime, parent); +} + static inline bool isTextFile(const QByteArray &data) { // UTF16 byte order marks @@ -145,7 +323,10 @@ QMimeType QMimeDatabasePrivate::findByData(const QByteArray &data, int *accuracy } *accuracyPtr = 0; - QMimeType candidate = provider()->findByMagic(data, accuracyPtr); + QMimeType candidate; + const auto allProviders = providers(); + for (QMimeProviderBase *provider : allProviders) + provider->findByMagic(data, accuracyPtr, candidate); if (candidate.isValid()) return candidate; @@ -172,7 +353,7 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa if (fileName.endsWith(QLatin1Char('/'))) candidatesByName.addMatch(QLatin1String("inode/directory"), 100, QString()); else - candidatesByName = provider()->findByFileName(QFileInfo(fileName).fileName()); + candidatesByName = findByFileName(QFileInfo(fileName).fileName()); if (candidatesByName.m_allMatchingMimeTypes.count() == 1) { *accuracyPtr = 100; const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0)); @@ -222,21 +403,25 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa QList<QMimeType> QMimeDatabasePrivate::allMimeTypes() { - return provider()->allMimeTypes(); + QList<QMimeType> result; + const auto allProviders = providers(); + for (QMimeProviderBase *provider : allProviders) + provider->addAllMimeTypes(result); + return result; } bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent) { - const QString resolvedParent = provider()->resolveAlias(parent); - //Q_ASSERT(provider()->resolveAlias(mime) == mime); + const QString resolvedParent = resolveAlias(parent); std::stack<QString, QStringList> toCheck; toCheck.push(mime); while (!toCheck.empty()) { if (toCheck.top() == resolvedParent) return true; - const auto parents = provider()->parents(toCheck.top()); + const QString mimeName = toCheck.top(); toCheck.pop(); - for (const QString &par : parents) + const auto parentList = parents(mimeName); + for (const QString &par : parentList) toCheck.push(par); } return false; @@ -460,7 +645,7 @@ QList<QMimeType> QMimeDatabase::mimeTypesForFileName(const QString &fileName) co QString QMimeDatabase::suffixForFileName(const QString &fileName) const { QMutexLocker locker(&d->mutex); - return d->provider()->findByFileName(QFileInfo(fileName).fileName()).m_foundSuffix; + return d->findByFileName(QFileInfo(fileName).fileName()).m_foundSuffix; } /*! @@ -550,6 +735,7 @@ QMimeType QMimeDatabase::mimeTypeForUrl(const QUrl &url) const */ QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const { + QMutexLocker locker(&d->mutex); int accuracy = 0; const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly); const QMimeType result = d->mimeTypeForFileNameAndData(fileName, device, &accuracy); @@ -576,6 +762,7 @@ QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, QIO */ QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const { + QMutexLocker locker(&d->mutex); QBuffer buffer(const_cast<QByteArray *>(&data)); buffer.open(QIODevice::ReadOnly); int accuracy = 0; diff --git a/src/corelib/mimetypes/qmimedatabase_p.h b/src/corelib/mimetypes/qmimedatabase_p.h index 3f63f5f103..1c38f46115 100644 --- a/src/corelib/mimetypes/qmimedatabase_p.h +++ b/src/corelib/mimetypes/qmimedatabase_p.h @@ -59,7 +59,9 @@ #include "qmimetype_p.h" #include "qmimeglobpattern_p.h" +#include <QtCore/qelapsedtimer.h> #include <QtCore/qmutex.h> +#include <QtCore/qvector.h> QT_BEGIN_NAMESPACE @@ -77,22 +79,37 @@ public: static QMimeDatabasePrivate *instance(); - QMimeProviderBase *provider(); - void setProvider(QMimeProviderBase *theProvider); - inline QString defaultMimeType() const { return m_defaultMimeType; } bool inherits(const QString &mime, const QString &parent); QList<QMimeType> allMimeTypes(); - + QString resolveAlias(const QString &nameOrAlias); + QStringList parents(const QString &mimeName); QMimeType mimeTypeForName(const QString &nameOrAlias); QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device, int *priorityPtr); QMimeType findByData(const QByteArray &data, int *priorityPtr); QStringList mimeTypeForFileName(const QString &fileName); + QMimeGlobMatchResult findByFileName(const QString &fileName); + + // API for QMimeType. Takes care of locking the mutex. + void loadMimeTypePrivate(QMimeTypePrivate &mimePrivate); + void loadGenericIcon(QMimeTypePrivate &mimePrivate); + void loadIcon(QMimeTypePrivate &mimePrivate); + QStringList mimeParents(const QString &mimeName); + QStringList listAliases(const QString &mimeName); + bool mimeInherits(const QString &mime, const QString &parent); - mutable QMimeProviderBase *m_provider; +private: + QVector<QMimeProviderBase *> providers(); + bool shouldCheck(); + void loadProviders(); + + mutable QVector<QMimeProviderBase *> m_providers; + QElapsedTimer m_lastCheck; + +public: const QString m_defaultMimeType; QMutex mutex; }; diff --git a/src/corelib/mimetypes/qmimeglobpattern.cpp b/src/corelib/mimetypes/qmimeglobpattern.cpp index a4d2b046fa..e7b2b879a1 100644 --- a/src/corelib/mimetypes/qmimeglobpattern.cpp +++ b/src/corelib/mimetypes/qmimeglobpattern.cpp @@ -206,10 +206,9 @@ void QMimeGlobPatternList::match(QMimeGlobMatchResult &result, } } -QMimeGlobMatchResult QMimeAllGlobPatterns::matchingGlobs(const QString &fileName) const +void QMimeAllGlobPatterns::matchingGlobs(const QString &fileName, QMimeGlobMatchResult &result) const { // First try the high weight matches (>50), if any. - QMimeGlobMatchResult result; m_highWeightGlobs.match(result, fileName); // Now use the "fast patterns" dict, for simple *.foo patterns with weight 50 @@ -230,8 +229,6 @@ QMimeGlobMatchResult QMimeAllGlobPatterns::matchingGlobs(const QString &fileName // Finally, try the low weight matches (<=50) m_lowWeightGlobs.match(result, fileName); - - return result; } void QMimeAllGlobPatterns::clear() diff --git a/src/corelib/mimetypes/qmimeglobpattern_p.h b/src/corelib/mimetypes/qmimeglobpattern_p.h index 21332e71bc..103729c4ff 100644 --- a/src/corelib/mimetypes/qmimeglobpattern_p.h +++ b/src/corelib/mimetypes/qmimeglobpattern_p.h @@ -152,7 +152,7 @@ public: void addGlob(const QMimeGlobPattern &glob); void removeMimeType(const QString &mimeType); - QMimeGlobMatchResult matchingGlobs(const QString &fileName) const; + void matchingGlobs(const QString &fileName, QMimeGlobMatchResult &result) const; void clear(); PatternsMap m_fastPatterns; // example: "doc" -> "application/msword", "text/plain" diff --git a/src/corelib/mimetypes/qmimemagicrule.cpp b/src/corelib/mimetypes/qmimemagicrule.cpp index 5bbf1bba9d..6356a3581b 100644 --- a/src/corelib/mimetypes/qmimemagicrule.cpp +++ b/src/corelib/mimetypes/qmimemagicrule.cpp @@ -65,7 +65,7 @@ static const char magicRuleTypes_string[] = "\0"; static const int magicRuleTypes_indices[] = { - 0, 8, 15, 22, 29, 35, 41, 50, 59, 65, 0 + 0, 8, 15, 22, 29, 35, 41, 50, 59, 64, 0 }; QMimeMagicRule::Type QMimeMagicRule::type(const QByteArray &theTypeName) diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp index ed7ebb2ef5..ec0a6bf0ef 100644 --- a/src/corelib/mimetypes/qmimeprovider.cpp +++ b/src/corelib/mimetypes/qmimeprovider.cpp @@ -61,46 +61,18 @@ static void initResources() QT_BEGIN_NAMESPACE -static QString fallbackParent(const QString &mimeTypeName) -{ - const QStringRef myGroup = mimeTypeName.leftRef(mimeTypeName.indexOf(QLatin1Char('/'))); - // All text/* types are subclasses of text/plain. - if (myGroup == QLatin1String("text") && mimeTypeName != QLatin1String("text/plain")) - return QLatin1String("text/plain"); - // All real-file mimetypes implicitly derive from application/octet-stream - if (myGroup != QLatin1String("inode") && - // ignore non-file extensions - myGroup != QLatin1String("all") && myGroup != QLatin1String("fonts") && myGroup != QLatin1String("print") && myGroup != QLatin1String("uri") - && mimeTypeName != QLatin1String("application/octet-stream")) { - return QLatin1String("application/octet-stream"); - } - return QString(); -} - -QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db) - : m_db(db) +QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory) + : m_db(db), m_directory(directory) { } -Q_CORE_EXPORT int qmime_secondsBetweenChecks = 5; // exported for the unit test -bool QMimeProviderBase::shouldCheck() -{ - if (m_lastCheck.isValid() && m_lastCheck.elapsed() < qmime_secondsBetweenChecks * 1000) - return false; - m_lastCheck.start(); - return true; -} - -QMimeBinaryProvider::QMimeBinaryProvider(QMimeDatabasePrivate *db) - : QMimeProviderBase(db), m_mimetypeListLoaded(false) +QMimeBinaryProvider::QMimeBinaryProvider(QMimeDatabasePrivate *db, const QString &directory) + : QMimeProviderBase(db, directory), m_mimetypeListLoaded(false) { + ensureLoaded(); } -#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) -#define QT_USE_MMAP -#endif - struct QMimeBinaryProvider::CacheFile { CacheFile(const QString &fileName); @@ -154,7 +126,6 @@ bool QMimeBinaryProvider::CacheFile::load() bool QMimeBinaryProvider::CacheFile::reload() { - //qDebug() << "reload!" << file->fileName(); m_valid = false; if (file.isOpen()) { file.close(); @@ -163,18 +134,14 @@ bool QMimeBinaryProvider::CacheFile::reload() return load(); } -QMimeBinaryProvider::CacheFile *QMimeBinaryProvider::CacheFileList::findCacheFile(const QString &fileName) const +QMimeBinaryProvider::~QMimeBinaryProvider() { - for (const_iterator it = begin(); it != end(); ++it) { - if ((*it)->file.fileName() == fileName) - return *it; - } - return 0; + delete m_cacheFile; } -QMimeBinaryProvider::~QMimeBinaryProvider() +bool QMimeBinaryProvider::isValid() { - qDeleteAll(m_cacheFiles); + return m_cacheFile != nullptr; } // Position of the "list offsets" values, at the beginning of the mime.cache file @@ -190,79 +157,33 @@ enum { PosGenericIconsListOffset = 36 }; -bool QMimeBinaryProvider::isValid() +bool QMimeBinaryProvider::checkCacheChanged() { -#if defined(QT_USE_MMAP) - if (!qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE")) - return false; - - Q_ASSERT(m_cacheFiles.isEmpty()); // this method is only ever called once - checkCache(); - - if (m_cacheFiles.count() > 1) + QFileInfo fileInfo(m_cacheFile->file); + if (fileInfo.lastModified() > m_cacheFile->m_mtime) { + // Deletion can't happen by just running update-mime-database. + // But the user could use rm -rf :-) + m_cacheFile->reload(); // will mark itself as invalid on failure return true; - if (m_cacheFiles.isEmpty()) - return false; - - // We found exactly one file; is it the user-modified mimes, or a system file? - const QString foundFile = m_cacheFiles.constFirst()->file.fileName(); - const QString localCacheFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/mime/mime.cache"); - - return foundFile != localCacheFile; -#else - return false; -#endif -} - -bool QMimeBinaryProvider::CacheFileList::checkCacheChanged() -{ - bool somethingChanged = false; - for (CacheFile *cacheFile : qAsConst(*this)) { - QFileInfo fileInfo(cacheFile->file); - if (!fileInfo.exists() || fileInfo.lastModified() > cacheFile->m_mtime) { - // Deletion can't happen by just running update-mime-database. - // But the user could use rm -rf :-) - cacheFile->reload(); // will mark itself as invalid on failure - somethingChanged = true; - } - } - if (somethingChanged) { - auto deleteIfNoLongerValid = [](CacheFile *cacheFile) -> bool { - const bool invalid = !cacheFile->isValid(); - if (invalid) - delete cacheFile; - return invalid; - }; - erase(std::remove_if(begin(), end(), deleteIfNoLongerValid), end()); } - return somethingChanged; + return false; } -void QMimeBinaryProvider::checkCache() +void QMimeBinaryProvider::ensureLoaded() { - if (!shouldCheck()) - return; - - // First iterate over existing known cache files and check for uptodate - if (m_cacheFiles.checkCacheChanged()) - m_mimetypeListLoaded = false; - - // Then check if new cache files appeared - const QStringList cacheFileNames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/mime.cache")); - if (cacheFileNames != m_cacheFileNames) { - for (const QString &cacheFileName : cacheFileNames) { - CacheFile *cacheFile = m_cacheFiles.findCacheFile(cacheFileName); - if (!cacheFile) { - //qDebug() << "new file:" << cacheFileName; - cacheFile = new CacheFile(cacheFileName); - if (cacheFile->isValid()) // verify version - m_cacheFiles.append(cacheFile); - else - delete cacheFile; - } - } - m_cacheFileNames = cacheFileNames; + if (!m_cacheFile) { + const QString cacheFileName = m_directory + QLatin1String("/mime.cache"); + m_cacheFile = new CacheFile(cacheFileName); m_mimetypeListLoaded = false; + } else { + if (checkCacheChanged()) + m_mimetypeListLoaded = false; + else + return; // nothing to do + } + if (!m_cacheFile->isValid()) { // verify existence and version + delete m_cacheFile; + m_cacheFile = nullptr; } } @@ -270,6 +191,7 @@ static QMimeType mimeTypeForNameUnchecked(const QString &name) { QMimeTypePrivate data; data.name = name; + data.fromCache = true; // The rest is retrieved on demand. // comment and globPatterns: in loadMimeTypePrivate // iconName: in loadIcon @@ -279,7 +201,6 @@ static QMimeType mimeTypeForNameUnchecked(const QString &name) QMimeType QMimeBinaryProvider::mimeTypeForName(const QString &name) { - checkCache(); if (!m_mimetypeListLoaded) loadMimeTypeList(); if (!m_mimetypeNames.contains(name)) @@ -287,28 +208,23 @@ QMimeType QMimeBinaryProvider::mimeTypeForName(const QString &name) return mimeTypeForNameUnchecked(name); } -QMimeGlobMatchResult QMimeBinaryProvider::findByFileName(const QString &fileName) +void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) { - checkCache(); - QMimeGlobMatchResult result; if (fileName.isEmpty()) - return result; + return; + Q_ASSERT(m_cacheFile); const QString lowerFileName = fileName.toLower(); - // TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly. - for (CacheFile *cacheFile : qAsConst(m_cacheFiles)) { - // Check literals (e.g. "Makefile") - matchGlobList(result, cacheFile, cacheFile->getUint32(PosLiteralListOffset), fileName); - // Check complex globs (e.g. "callgrind.out[0-9]*") - matchGlobList(result, cacheFile, cacheFile->getUint32(PosGlobListOffset), fileName); - // Check the very common *.txt cases with the suffix tree - const int reverseSuffixTreeOffset = cacheFile->getUint32(PosReverseSuffixTreeOffset); - const int numRoots = cacheFile->getUint32(reverseSuffixTreeOffset); - const int firstRootOffset = cacheFile->getUint32(reverseSuffixTreeOffset + 4); - matchSuffixTree(result, cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false); - if (result.m_matchingMimeTypes.isEmpty()) - matchSuffixTree(result, cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); - } - return result; + // Check literals (e.g. "Makefile") + matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosLiteralListOffset), fileName); + // Check complex globs (e.g. "callgrind.out[0-9]*") + matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); + // Check the very common *.txt cases with the suffix tree + const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); + const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); + const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); + matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false); + if (result.m_matchingMimeTypes.isEmpty()) + matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); } void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) @@ -406,124 +322,106 @@ bool QMimeBinaryProvider::matchMagicRule(QMimeBinaryProvider::CacheFile *cacheFi return false; } -QMimeType QMimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr) -{ - checkCache(); - for (CacheFile *cacheFile : qAsConst(m_cacheFiles)) { - const int magicListOffset = cacheFile->getUint32(PosMagicListOffset); - const int numMatches = cacheFile->getUint32(magicListOffset); - //const int maxExtent = cacheFile->getUint32(magicListOffset + 4); - const int firstMatchOffset = cacheFile->getUint32(magicListOffset + 8); - - for (int i = 0; i < numMatches; ++i) { - const int off = firstMatchOffset + i * 16; - const int numMatchlets = cacheFile->getUint32(off + 8); - const int firstMatchletOffset = cacheFile->getUint32(off + 12); - if (matchMagicRule(cacheFile, numMatchlets, firstMatchletOffset, data)) { - const int mimeTypeOffset = cacheFile->getUint32(off + 4); - const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); - *accuracyPtr = cacheFile->getUint32(off); - // Return the first match. We have no rules for conflicting magic data... - // (mime.cache itself is sorted, but what about local overrides with a lower prio?) - return mimeTypeForNameUnchecked(QLatin1String(mimeType)); - } +void QMimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) +{ + const int magicListOffset = m_cacheFile->getUint32(PosMagicListOffset); + const int numMatches = m_cacheFile->getUint32(magicListOffset); + //const int maxExtent = cacheFile->getUint32(magicListOffset + 4); + const int firstMatchOffset = m_cacheFile->getUint32(magicListOffset + 8); + + for (int i = 0; i < numMatches; ++i) { + const int off = firstMatchOffset + i * 16; + const int numMatchlets = m_cacheFile->getUint32(off + 8); + const int firstMatchletOffset = m_cacheFile->getUint32(off + 12); + if (matchMagicRule(m_cacheFile, numMatchlets, firstMatchletOffset, data)) { + const int mimeTypeOffset = m_cacheFile->getUint32(off + 4); + const char *mimeType = m_cacheFile->getCharStar(mimeTypeOffset); + *accuracyPtr = m_cacheFile->getUint32(off); + // Return the first match. We have no rules for conflicting magic data... + // (mime.cache itself is sorted, but what about local overrides with a lower prio?) + candidate = mimeTypeForNameUnchecked(QLatin1String(mimeType)); + return; } } - return QMimeType(); } -QStringList QMimeBinaryProvider::parents(const QString &mime) +void QMimeBinaryProvider::addParents(const QString &mime, QStringList &result) { - checkCache(); const QByteArray mimeStr = mime.toLatin1(); - QStringList result; - for (CacheFile *cacheFile : qAsConst(m_cacheFiles)) { - const int parentListOffset = cacheFile->getUint32(PosParentListOffset); - const int numEntries = cacheFile->getUint32(parentListOffset); - - int begin = 0; - int end = numEntries - 1; - while (begin <= end) { - const int medium = (begin + end) / 2; - const int off = parentListOffset + 4 + 8 * medium; - const int mimeOffset = cacheFile->getUint32(off); - const char *aMime = cacheFile->getCharStar(mimeOffset); - const int cmp = qstrcmp(aMime, mimeStr); - if (cmp < 0) { - begin = medium + 1; - } else if (cmp > 0) { - end = medium - 1; - } else { - const int parentsOffset = cacheFile->getUint32(off + 4); - const int numParents = cacheFile->getUint32(parentsOffset); - for (int i = 0; i < numParents; ++i) { - const int parentOffset = cacheFile->getUint32(parentsOffset + 4 + 4 * i); - const char *aParent = cacheFile->getCharStar(parentOffset); - result.append(QString::fromLatin1(aParent)); - } - break; + const int parentListOffset = m_cacheFile->getUint32(PosParentListOffset); + const int numEntries = m_cacheFile->getUint32(parentListOffset); + + int begin = 0; + int end = numEntries - 1; + while (begin <= end) { + const int medium = (begin + end) / 2; + const int off = parentListOffset + 4 + 8 * medium; + const int mimeOffset = m_cacheFile->getUint32(off); + const char *aMime = m_cacheFile->getCharStar(mimeOffset); + const int cmp = qstrcmp(aMime, mimeStr); + if (cmp < 0) { + begin = medium + 1; + } else if (cmp > 0) { + end = medium - 1; + } else { + const int parentsOffset = m_cacheFile->getUint32(off + 4); + const int numParents = m_cacheFile->getUint32(parentsOffset); + for (int i = 0; i < numParents; ++i) { + const int parentOffset = m_cacheFile->getUint32(parentsOffset + 4 + 4 * i); + const char *aParent = m_cacheFile->getCharStar(parentOffset); + const QString strParent = QString::fromLatin1(aParent); + if (!result.contains(strParent)) + result.append(strParent); } + break; } } - if (result.isEmpty()) { - const QString parent = fallbackParent(mime); - if (!parent.isEmpty()) - result.append(parent); - } - return result; } QString QMimeBinaryProvider::resolveAlias(const QString &name) { - checkCache(); const QByteArray input = name.toLatin1(); - for (CacheFile *cacheFile : qAsConst(m_cacheFiles)) { - const int aliasListOffset = cacheFile->getUint32(PosAliasListOffset); - const int numEntries = cacheFile->getUint32(aliasListOffset); - int begin = 0; - int end = numEntries - 1; - while (begin <= end) { - const int medium = (begin + end) / 2; - const int off = aliasListOffset + 4 + 8 * medium; - const int aliasOffset = cacheFile->getUint32(off); - const char *alias = cacheFile->getCharStar(aliasOffset); - const int cmp = qstrcmp(alias, input); - if (cmp < 0) { - begin = medium + 1; - } else if (cmp > 0) { - end = medium - 1; - } else { - const int mimeOffset = cacheFile->getUint32(off + 4); - const char *mimeType = cacheFile->getCharStar(mimeOffset); - return QLatin1String(mimeType); - } + const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset); + const int numEntries = m_cacheFile->getUint32(aliasListOffset); + int begin = 0; + int end = numEntries - 1; + while (begin <= end) { + const int medium = (begin + end) / 2; + const int off = aliasListOffset + 4 + 8 * medium; + const int aliasOffset = m_cacheFile->getUint32(off); + const char *alias = m_cacheFile->getCharStar(aliasOffset); + const int cmp = qstrcmp(alias, input); + if (cmp < 0) { + begin = medium + 1; + } else if (cmp > 0) { + end = medium - 1; + } else { + const int mimeOffset = m_cacheFile->getUint32(off + 4); + const char *mimeType = m_cacheFile->getCharStar(mimeOffset); + return QLatin1String(mimeType); } } - return name; } -QStringList QMimeBinaryProvider::listAliases(const QString &name) +void QMimeBinaryProvider::addAliases(const QString &name, QStringList &result) { - checkCache(); - QStringList result; const QByteArray input = name.toLatin1(); - for (CacheFile *cacheFile : qAsConst(m_cacheFiles)) { - const int aliasListOffset = cacheFile->getUint32(PosAliasListOffset); - const int numEntries = cacheFile->getUint32(aliasListOffset); - for (int pos = 0; pos < numEntries; ++pos) { - const int off = aliasListOffset + 4 + 8 * pos; - const int mimeOffset = cacheFile->getUint32(off + 4); - const char *mimeType = cacheFile->getCharStar(mimeOffset); - - if (input == mimeType) { - const int aliasOffset = cacheFile->getUint32(off); - const char *alias = cacheFile->getCharStar(aliasOffset); - result.append(QString::fromLatin1(alias)); - } + const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset); + const int numEntries = m_cacheFile->getUint32(aliasListOffset); + for (int pos = 0; pos < numEntries; ++pos) { + const int off = aliasListOffset + 4 + 8 * pos; + const int mimeOffset = m_cacheFile->getUint32(off + 4); + const char *mimeType = m_cacheFile->getCharStar(mimeOffset); + + if (input == mimeType) { + const int aliasOffset = m_cacheFile->getUint32(off); + const char *alias = m_cacheFile->getCharStar(aliasOffset); + const QString strAlias = QString::fromLatin1(alias); + if (!result.contains(strAlias)) + result.append(strAlias); } } - return result; } void QMimeBinaryProvider::loadMimeTypeList() @@ -533,31 +431,30 @@ void QMimeBinaryProvider::loadMimeTypeList() m_mimetypeNames.clear(); // Unfortunately mime.cache doesn't have a full list of all mimetypes. // So we have to parse the plain-text files called "types". - const QStringList typesFilenames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/types")); - for (const QString &typeFilename : typesFilenames) { - QFile file(typeFilename); - if (file.open(QIODevice::ReadOnly)) { - QTextStream stream(&file); - stream.setCodec("ISO 8859-1"); - QString line; - while (stream.readLineInto(&line)) - m_mimetypeNames.insert(line); - } + QFile file(m_directory + QStringLiteral("/types")); + if (file.open(QIODevice::ReadOnly)) { + QTextStream stream(&file); + stream.setCodec("ISO 8859-1"); + QString line; + while (stream.readLineInto(&line)) + m_mimetypeNames.insert(line); } } } -QList<QMimeType> QMimeBinaryProvider::allMimeTypes() +void QMimeBinaryProvider::addAllMimeTypes(QList<QMimeType> &result) { - QList<QMimeType> result; loadMimeTypeList(); - result.reserve(m_mimetypeNames.count()); - - for (QSet<QString>::const_iterator it = m_mimetypeNames.constBegin(); - it != m_mimetypeNames.constEnd(); ++it) - result.append(mimeTypeForNameUnchecked(*it)); - - return result; + if (result.isEmpty()) { + result.reserve(m_mimetypeNames.count()); + for (const QString &name : m_mimetypeNames) + result.append(mimeTypeForNameUnchecked(name)); + } else { + for (const QString &name : m_mimetypeNames) + if (std::find_if(result.constBegin(), result.constEnd(), [name](const QMimeType &mime) -> bool { return mime.name() == name; }) + == result.constEnd()) + result.append(mimeTypeForNameUnchecked(name)); + } } void QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate &data) @@ -681,36 +578,29 @@ QLatin1String QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posList void QMimeBinaryProvider::loadIcon(QMimeTypePrivate &data) { - checkCache(); const QByteArray inputMime = data.name.toLatin1(); - for (CacheFile *cacheFile : qAsConst(m_cacheFiles)) { - const QLatin1String icon = iconForMime(cacheFile, PosIconsListOffset, inputMime); - if (!icon.isEmpty()) { - data.iconName = icon; - return; - } + const QLatin1String icon = iconForMime(m_cacheFile, PosIconsListOffset, inputMime); + if (!icon.isEmpty()) { + data.iconName = icon; } } void QMimeBinaryProvider::loadGenericIcon(QMimeTypePrivate &data) { - checkCache(); const QByteArray inputMime = data.name.toLatin1(); - for (CacheFile *cacheFile : qAsConst(m_cacheFiles)) { - const QLatin1String icon = iconForMime(cacheFile, PosGenericIconsListOffset, inputMime); - if (!icon.isEmpty()) { - data.genericIconName = icon; - return; - } + const QLatin1String icon = iconForMime(m_cacheFile, PosGenericIconsListOffset, inputMime); + if (!icon.isEmpty()) { + data.genericIconName = icon; } } //// -QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db) - : QMimeProviderBase(db), m_loaded(false) +QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db, const QString &directory) + : QMimeProviderBase(db, directory) { initResources(); + ensureLoaded(); } QMimeXMLProvider::~QMimeXMLProvider() @@ -719,81 +609,63 @@ QMimeXMLProvider::~QMimeXMLProvider() bool QMimeXMLProvider::isValid() { + // If you change this method, adjust the logic in QMimeDatabasePrivate::loadProviders, + // which assumes isValid==false is only possible in QMimeBinaryProvider. return true; } QMimeType QMimeXMLProvider::mimeTypeForName(const QString &name) { - ensureLoaded(); - return m_nameMimeTypeMap.value(name); } -QMimeGlobMatchResult QMimeXMLProvider::findByFileName(const QString &fileName) +void QMimeXMLProvider::addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) { - ensureLoaded(); - - return m_mimeTypeGlobs.matchingGlobs(fileName); + m_mimeTypeGlobs.matchingGlobs(fileName, result); } -QMimeType QMimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr) +void QMimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) { - ensureLoaded(); - - QString candidate; - + QString candidateName; + bool foundOne = false; for (const QMimeMagicRuleMatcher &matcher : qAsConst(m_magicMatchers)) { if (matcher.matches(data)) { const int priority = matcher.priority(); if (priority > *accuracyPtr) { *accuracyPtr = priority; - candidate = matcher.mimetype(); + candidateName = matcher.mimetype(); + foundOne = true; } } } - return mimeTypeForName(candidate); + if (foundOne) + candidate = mimeTypeForName(candidateName); } void QMimeXMLProvider::ensureLoaded() { - if (!m_loaded || shouldCheck()) { - bool fdoXmlFound = false; - QStringList allFiles; - - const QStringList packageDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/packages"), QStandardPaths::LocateDirectory); - //qDebug() << "packageDirs=" << packageDirs; - for (const QString &packageDir : packageDirs) { - QDir dir(packageDir); - const QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); - //qDebug() << static_cast<const void *>(this) << packageDir << files; - if (!fdoXmlFound) - fdoXmlFound = files.contains(QLatin1String("freedesktop.org.xml")); - QStringList::const_iterator endIt(files.constEnd()); - for (QStringList::const_iterator it(files.constBegin()); it != endIt; ++it) { - allFiles.append(packageDir + QLatin1Char('/') + *it); - } - } - - if (!fdoXmlFound) { - // We could instead install the file as part of installing Qt? - allFiles.prepend(QLatin1String(":/qt-project.org/qmime/freedesktop.org.xml")); - } + QStringList allFiles; + const QString packageDir = m_directory + QStringLiteral("/packages"); + QDir dir(packageDir); + const QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); + allFiles.reserve(files.count()); + for (const QString &xmlFile : files) + allFiles.append(packageDir + QLatin1Char('/') + xmlFile); - if (m_allFiles == allFiles) - return; - m_allFiles = allFiles; + if (m_allFiles == allFiles) + return; + m_allFiles = allFiles; - m_nameMimeTypeMap.clear(); - m_aliases.clear(); - m_parents.clear(); - m_mimeTypeGlobs.clear(); - m_magicMatchers.clear(); + m_nameMimeTypeMap.clear(); + m_aliases.clear(); + m_parents.clear(); + m_mimeTypeGlobs.clear(); + m_magicMatchers.clear(); - //qDebug() << "Loading" << m_allFiles; + //qDebug() << "Loading" << m_allFiles; - for (const QString &file : qAsConst(allFiles)) - load(file); - } + for (const QString &file : qAsConst(allFiles)) + load(file); } void QMimeXMLProvider::load(const QString &fileName) @@ -805,8 +677,6 @@ void QMimeXMLProvider::load(const QString &fileName) bool QMimeXMLProvider::load(const QString &fileName, QString *errorMessage) { - m_loaded = true; - QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (errorMessage) @@ -828,19 +698,16 @@ void QMimeXMLProvider::addGlobPattern(const QMimeGlobPattern &glob) void QMimeXMLProvider::addMimeType(const QMimeType &mt) { + Q_ASSERT(!mt.d.data()->fromCache); m_nameMimeTypeMap.insert(mt.name(), mt); } -QStringList QMimeXMLProvider::parents(const QString &mime) +void QMimeXMLProvider::addParents(const QString &mime, QStringList &result) { - ensureLoaded(); - QStringList result = m_parents.value(mime); - if (result.isEmpty()) { - const QString parent = fallbackParent(mime); - if (!parent.isEmpty()) + for (const QString &parent : m_parents.value(mime)) { + if (!result.contains(parent)) result.append(parent); } - return result; } void QMimeXMLProvider::addParent(const QString &child, const QString &parent) @@ -848,16 +715,20 @@ void QMimeXMLProvider::addParent(const QString &child, const QString &parent) m_parents[child].append(parent); } -QStringList QMimeXMLProvider::listAliases(const QString &name) +void QMimeXMLProvider::addAliases(const QString &name, QStringList &result) { - ensureLoaded(); // Iterate through the whole hash. This method is rarely used. - return m_aliases.keys(name); + for (auto it = m_aliases.constBegin(), end = m_aliases.constEnd() ; it != end ; ++it) { + if (it.value() == name) { + if (!result.contains(it.key())) + result.append(it.key()); + } + } + } QString QMimeXMLProvider::resolveAlias(const QString &name) { - ensureLoaded(); return m_aliases.value(name, name); } @@ -866,10 +737,18 @@ void QMimeXMLProvider::addAlias(const QString &alias, const QString &name) m_aliases.insert(alias, name); } -QList<QMimeType> QMimeXMLProvider::allMimeTypes() +void QMimeXMLProvider::addAllMimeTypes(QList<QMimeType> &result) { - ensureLoaded(); - return m_nameMimeTypeMap.values(); + if (result.isEmpty()) { // fast path + result = m_nameMimeTypeMap.values(); + } else { + for (auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd() ; it != end ; ++it) { + const QString newMime = it.key(); + if (std::find_if(result.constBegin(), result.constEnd(), [newMime](const QMimeType &mime) -> bool { return mime.name() == newMime; }) + == result.constEnd()) + result.append(it.value()); + } + } } void QMimeXMLProvider::addMagicMatcher(const QMimeMagicRuleMatcher &matcher) diff --git a/src/corelib/mimetypes/qmimeprovider_p.h b/src/corelib/mimetypes/qmimeprovider_p.h index 3eae672823..b2be545cf8 100644 --- a/src/corelib/mimetypes/qmimeprovider_p.h +++ b/src/corelib/mimetypes/qmimeprovider_p.h @@ -59,7 +59,6 @@ #include "qmimeglobpattern_p.h" #include <QtCore/qdatetime.h> #include <QtCore/qset.h> -#include <QtCore/qelapsedtimer.h> QT_BEGIN_NAMESPACE @@ -68,25 +67,25 @@ class QMimeMagicRuleMatcher; class QMimeProviderBase { public: - QMimeProviderBase(QMimeDatabasePrivate *db); + QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory); virtual ~QMimeProviderBase() {} virtual bool isValid() = 0; virtual QMimeType mimeTypeForName(const QString &name) = 0; - virtual QMimeGlobMatchResult findByFileName(const QString &fileName) = 0; - virtual QStringList parents(const QString &mime) = 0; + virtual void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) = 0; + virtual void addParents(const QString &mime, QStringList &result) = 0; virtual QString resolveAlias(const QString &name) = 0; - virtual QStringList listAliases(const QString &name) = 0; - virtual QMimeType findByMagic(const QByteArray &data, int *accuracyPtr) = 0; - virtual QList<QMimeType> allMimeTypes() = 0; - virtual void loadMimeTypePrivate(QMimeTypePrivate &) {} + virtual void addAliases(const QString &name, QStringList &result) = 0; + virtual void findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) = 0; + virtual void addAllMimeTypes(QList<QMimeType> &result) = 0; virtual void loadIcon(QMimeTypePrivate &) {} virtual void loadGenericIcon(QMimeTypePrivate &) {} + virtual void ensureLoaded() {} + + QString directory() const { return m_directory; } QMimeDatabasePrivate *m_db; -protected: - bool shouldCheck(); - QElapsedTimer m_lastCheck; + QString m_directory; }; /* @@ -95,20 +94,21 @@ protected: class QMimeBinaryProvider : public QMimeProviderBase { public: - QMimeBinaryProvider(QMimeDatabasePrivate *db); + QMimeBinaryProvider(QMimeDatabasePrivate *db, const QString &directory); virtual ~QMimeBinaryProvider(); virtual bool isValid() override; virtual QMimeType mimeTypeForName(const QString &name) override; - virtual QMimeGlobMatchResult findByFileName(const QString &fileName) override; - virtual QStringList parents(const QString &mime) override; + void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) override; + void addParents(const QString &mime, QStringList &result) override; virtual QString resolveAlias(const QString &name) override; - virtual QStringList listAliases(const QString &name) override; - virtual QMimeType findByMagic(const QByteArray &data, int *accuracyPtr) override; - virtual QList<QMimeType> allMimeTypes() override; - virtual void loadMimeTypePrivate(QMimeTypePrivate &) override; + void addAliases(const QString &name, QStringList &result) override; + void findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) override; + void addAllMimeTypes(QList<QMimeType> &result) override; + static void loadMimeTypePrivate(QMimeTypePrivate &); virtual void loadIcon(QMimeTypePrivate &) override; virtual void loadGenericIcon(QMimeTypePrivate &) override; + void ensureLoaded() override; private: struct CacheFile; @@ -118,15 +118,9 @@ private: bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data); QLatin1String iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime); void loadMimeTypeList(); - void checkCache(); - - class CacheFileList : public QList<CacheFile *> - { - public: - CacheFile *findCacheFile(const QString &fileName) const; - bool checkCacheChanged(); - }; - CacheFileList m_cacheFiles; + bool checkCacheChanged(); + + CacheFile *m_cacheFile = nullptr; QStringList m_cacheFileNames; QSet<QString> m_mimetypeNames; bool m_mimetypeListLoaded; @@ -138,17 +132,18 @@ private: class QMimeXMLProvider : public QMimeProviderBase { public: - QMimeXMLProvider(QMimeDatabasePrivate *db); + QMimeXMLProvider(QMimeDatabasePrivate *db, const QString &directory); ~QMimeXMLProvider(); virtual bool isValid() override; virtual QMimeType mimeTypeForName(const QString &name) override; - virtual QMimeGlobMatchResult findByFileName(const QString &fileName) override; - virtual QStringList parents(const QString &mime) override; + void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) override; + void addParents(const QString &mime, QStringList &result) override; virtual QString resolveAlias(const QString &name) override; - virtual QStringList listAliases(const QString &name) override; - virtual QMimeType findByMagic(const QByteArray &data, int *accuracyPtr) override; - virtual QList<QMimeType> allMimeTypes() override; + void addAliases(const QString &name, QStringList &result) override; + void findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) override; + void addAllMimeTypes(QList<QMimeType> &result) override; + void ensureLoaded() override; bool load(const QString &fileName, QString *errorMessage); @@ -160,11 +155,8 @@ public: void addMagicMatcher(const QMimeMagicRuleMatcher &matcher); private: - void ensureLoaded(); void load(const QString &fileName); - bool m_loaded; - typedef QHash<QString, QMimeType> NameMimeTypeMap; NameMimeTypeMap m_nameMimeTypeMap; diff --git a/src/corelib/mimetypes/qmimetype.cpp b/src/corelib/mimetypes/qmimetype.cpp index 28113babfe..8f6237c1cb 100644 --- a/src/corelib/mimetypes/qmimetype.cpp +++ b/src/corelib/mimetypes/qmimetype.cpp @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE QMimeTypePrivate::QMimeTypePrivate() - : loaded(false) + : loaded(false), fromCache(false) {} QMimeTypePrivate::QMimeTypePrivate(const QMimeType &other) @@ -76,7 +76,6 @@ void QMimeTypePrivate::clear() genericIconName.clear(); iconName.clear(); globPatterns.clear(); - loaded = false; } void QMimeTypePrivate::addGlobPattern(const QString &pattern) @@ -256,7 +255,7 @@ QString QMimeType::name() const */ QString QMimeType::comment() const { - QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d); + QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d); QStringList languageList; languageList << QLocale().name(); @@ -296,7 +295,7 @@ QString QMimeType::comment() const */ QString QMimeType::genericIconName() const { - QMimeDatabasePrivate::instance()->provider()->loadGenericIcon(*d); + QMimeDatabasePrivate::instance()->loadGenericIcon(*d); if (d->genericIconName.isEmpty()) { // From the spec: // If the generic icon name is empty (not specified by the mimetype definition) @@ -324,7 +323,7 @@ QString QMimeType::genericIconName() const */ QString QMimeType::iconName() const { - QMimeDatabasePrivate::instance()->provider()->loadIcon(*d); + QMimeDatabasePrivate::instance()->loadIcon(*d); if (d->iconName.isEmpty()) { // Make default icon name from the mimetype name d->iconName = name(); @@ -344,7 +343,7 @@ QString QMimeType::iconName() const */ QStringList QMimeType::globPatterns() const { - QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d); + QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d); return d->globPatterns; } @@ -368,12 +367,12 @@ QStringList QMimeType::globPatterns() const */ QStringList QMimeType::parentMimeTypes() const { - return QMimeDatabasePrivate::instance()->provider()->parents(d->name); + return QMimeDatabasePrivate::instance()->mimeParents(d->name); } static void collectParentMimeTypes(const QString &mime, QStringList &allParents) { - const QStringList parents = QMimeDatabasePrivate::instance()->provider()->parents(mime); + const QStringList parents = QMimeDatabasePrivate::instance()->mimeParents(mime); for (const QString &parent : parents) { // I would use QSet, but since order matters I better not if (!allParents.contains(parent)) @@ -425,7 +424,7 @@ QStringList QMimeType::allAncestors() const */ QStringList QMimeType::aliases() const { - return QMimeDatabasePrivate::instance()->provider()->listAliases(d->name); + return QMimeDatabasePrivate::instance()->listAliases(d->name); } /*! @@ -439,7 +438,7 @@ QStringList QMimeType::aliases() const */ QStringList QMimeType::suffixes() const { - QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d); + QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d); QStringList result; for (const QString &pattern : qAsConst(d->globPatterns)) { @@ -480,7 +479,7 @@ QString QMimeType::preferredSuffix() const */ QString QMimeType::filterString() const { - QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d); + QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d); QString filter; if (!d->globPatterns.empty()) { @@ -508,7 +507,7 @@ bool QMimeType::inherits(const QString &mimeTypeName) const { if (d->name == mimeTypeName) return true; - return QMimeDatabasePrivate::instance()->inherits(d->name, mimeTypeName); + return QMimeDatabasePrivate::instance()->mimeInherits(d->name, mimeTypeName); } #ifndef QT_NO_DEBUG_STREAM diff --git a/src/corelib/mimetypes/qmimetype_p.h b/src/corelib/mimetypes/qmimetype_p.h index b0bfad2f65..aa38a1adf5 100644 --- a/src/corelib/mimetypes/qmimetype_p.h +++ b/src/corelib/mimetypes/qmimetype_p.h @@ -74,6 +74,7 @@ public: void addGlobPattern(const QString &pattern); bool loaded; // QSharedData leaves a 4 byte gap, so don't put 8 byte members first + bool fromCache; // true if this comes from the binary provider QString name; LocaleHash localeComments; QString genericIconName; @@ -94,6 +95,7 @@ QT_END_NAMESPACE { \ QMimeTypePrivate qMimeTypeData; \ qMimeTypeData.name = name; \ + qMimeTypeData.loaded = true; \ qMimeTypeData.genericIconName = genericIconName; \ qMimeTypeData.iconName = iconName; \ qMimeTypeData.globPatterns = globPatterns; \ @@ -112,6 +114,7 @@ QT_END_NAMESPACE ) \ { \ QMimeTypePrivate qMimeTypeData; \ + qMimeTypeData.loaded = true; \ qMimeTypeData.name = std::move(name); \ qMimeTypeData.genericIconName = std::move(genericIconName); \ qMimeTypeData.iconName = std::move(iconName); \ diff --git a/src/corelib/mimetypes/qmimetypeparser.cpp b/src/corelib/mimetypes/qmimetypeparser.cpp index 5ecd339908..0a55560ab0 100644 --- a/src/corelib/mimetypes/qmimetypeparser.cpp +++ b/src/corelib/mimetypes/qmimetypeparser.cpp @@ -201,6 +201,7 @@ bool QMimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString return false; #else QMimeTypePrivate data; + data.loaded = true; int priority = 50; QStack<QMimeMagicRule *> currentRules; // stack for the nesting of rules QList<QMimeMagicRule> rules; // toplevel rules diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h index a462d7bb58..fafc3e37b0 100644 --- a/src/corelib/tools/qsimd_p.h +++ b/src/corelib/tools/qsimd_p.h @@ -164,11 +164,7 @@ # if !defined(__MIPS_DSPR2__) && defined(__mips_dspr2) && defined(Q_PROCESSOR_MIPS_32) # define __MIPS_DSPR2__ # endif -#elif (defined(Q_CC_INTEL) || defined(Q_CC_MSVC) \ - || (defined(Q_CC_GNU) && !defined(Q_CC_CLANG) && Q_CC_GNU >= 409) \ - || (defined(Q_CC_CLANG) && Q_CC_CLANG >= 308)) \ - && !defined(QT_BOOTSTRAPPED) -# define QT_COMPILER_SUPPORTS_SIMD_ALWAYS +#elif defined(Q_PROCESSOR_X86) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS) # define QT_COMPILER_SUPPORTS_HERE(x) ((__ ## x ## __) || QT_COMPILER_SUPPORTS(x)) # if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) /* GCC requires attributes for a function */ diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index cd3966fb47..c8ba86bc9a 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -747,8 +747,7 @@ QString QPlatformTheme::removeMnemonics(const QString &original) int currPos = 0; int l = original.length(); while (l) { - if (original.at(currPos) == QLatin1Char('&') - && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { + if (original.at(currPos) == QLatin1Char('&')) { ++currPos; --l; if (l == 0) diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index c6ae7c1b79..2067c86023 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -596,8 +596,7 @@ static QString qt_mac_removeMnemonics(const QString &original) int currPos = 0; int l = original.length(); while (l) { - if (original.at(currPos) == QLatin1Char('&') - && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { + if (original.at(currPos) == QLatin1Char('&')) { ++currPos; --l; if (l == 0) |