diff options
Diffstat (limited to 'src/corelib/mimetypes/qmimedatabase.cpp')
-rw-r--r-- | src/corelib/mimetypes/qmimedatabase.cpp | 237 |
1 files changed, 109 insertions, 128 deletions
diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp index 67d062bb9e..d52ccacbe7 100644 --- a/src/corelib/mimetypes/qmimedatabase.cpp +++ b/src/corelib/mimetypes/qmimedatabase.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qplatformdefs.h> // always first @@ -46,6 +10,7 @@ #include "qmimeprovider_p.h" #include "qmimetype_p.h" +#include <private/qduplicatetracker_p.h> #include <private/qfilesystementry_p.h> #include <QtCore/QFile> @@ -61,6 +26,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + static QString directoryMimeType() { return QStringLiteral("inode/directory"); @@ -86,6 +53,7 @@ QMimeDatabasePrivate::~QMimeDatabasePrivate() { } +Q_CONSTINIT #ifdef QT_BUILD_INTERNAL Q_CORE_EXPORT #else @@ -106,7 +74,7 @@ static QStringList locateMimeDirectories() return QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("mime"), QStandardPaths::LocateDirectory); } -#if defined(Q_OS_UNIX) && !defined(Q_OS_NACL) && !defined(Q_OS_INTEGRITY) +#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) # define QT_USE_MMAP #endif @@ -115,7 +83,7 @@ void QMimeDatabasePrivate::loadProviders() // We use QStandardPaths every time to check if new files appeared const QStringList mimeDirs = locateMimeDirectories(); const auto fdoIterator = std::find_if(mimeDirs.constBegin(), mimeDirs.constEnd(), [](const QString &mimeDir) -> bool { - return QFileInfo::exists(mimeDir + QLatin1String("/packages/freedesktop.org.xml")); } + return QFileInfo::exists(mimeDir + "/packages/freedesktop.org.xml"_L1); } ); const bool needInternalDB = QMimeXMLProvider::InternalDatabaseAvailable && fdoIterator == mimeDirs.constEnd(); //qDebug() << "mime dirs:" << mimeDirs; @@ -126,7 +94,7 @@ void QMimeDatabasePrivate::loadProviders() m_providers.reserve(mimeDirs.size() + (needInternalDB ? 1 : 0)); for (const QString &mimeDir : mimeDirs) { - const QString cacheFile = mimeDir + QLatin1String("/mime.cache"); + const QString cacheFile = mimeDir + "/mime.cache"_L1; // Check if we already have a provider for this dir const auto predicate = [mimeDir](const std::unique_ptr<QMimeProviderBase> &prov) { @@ -174,6 +142,13 @@ void QMimeDatabasePrivate::loadProviders() m_providers.push_back(std::move(*it)); } } + + auto it = m_providers.begin(); + (*it)->setOverrideProvider(nullptr); + ++it; + const auto end = m_providers.end(); + for (; it != end; ++it) + (*it)->setOverrideProvider((it - 1)->get()); } const QMimeDatabasePrivate::Providers &QMimeDatabasePrivate::providers() @@ -209,16 +184,15 @@ QMimeType QMimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias) { const QString mimeName = resolveAlias(nameOrAlias); for (const auto &provider : providers()) { - const QMimeType mime = provider->mimeTypeForName(mimeName); - if (mime.isValid()) - return mime; + if (provider->knowsMimeType(mimeName)) + return QMimeType(QMimeTypePrivate(mimeName)); } return {}; } QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName) { - if (fileName.endsWith(QLatin1Char('/'))) + if (fileName.endsWith(u'/')) return { directoryMimeType() }; const QMimeGlobMatchResult result = findByFileName(fileName); @@ -236,66 +210,66 @@ QMimeGlobMatchResult QMimeDatabasePrivate::findByFileName(const QString &fileNam return result; } -void QMimeDatabasePrivate::loadMimeTypePrivate(QMimeTypePrivate &mimePrivate) +QMimeTypePrivate::LocaleHash QMimeDatabasePrivate::localeComments(const QString &name) { QMutexLocker locker(&mutex); - if (mimePrivate.name.isEmpty()) - return; // invalid mimetype - if (!mimePrivate.loaded) { // XML provider sets loaded=true, binary provider does this on demand - Q_ASSERT(mimePrivate.fromCache); - bool found = false; - for (const auto &provider : providers()) { - if (provider->loadMimeTypePrivate(mimePrivate)) { - found = true; - break; - } - } - if (!found) { - const QString file = mimePrivate.name + QLatin1String(".xml"); - qWarning() << "No file found for" << file << ", even though update-mime-info said it would exist.\n" - "Either it was just removed, or the directory doesn't have executable permission..." - << locateMimeDirectories(); - } - mimePrivate.loaded = true; + for (const auto &provider : providers()) { + auto comments = provider->localeComments(name); + if (!comments.isEmpty()) + return comments; // maybe we want to merge in comments from more global providers, in + // case of more translations? } + return {}; } -void QMimeDatabasePrivate::loadGenericIcon(QMimeTypePrivate &mimePrivate) +QStringList QMimeDatabasePrivate::globPatterns(const QString &name) { QMutexLocker locker(&mutex); - if (mimePrivate.fromCache) { - mimePrivate.genericIconName.clear(); - for (const auto &provider : providers()) { - provider->loadGenericIcon(mimePrivate); - if (!mimePrivate.genericIconName.isEmpty()) - break; - } + QStringList patterns; + const auto &providerList = providers(); + // reverse iteration because we start from most global, add up, clear if delete-all, and add up + // again. + for (auto rit = providerList.rbegin(); rit != providerList.rend(); ++rit) { + auto *provider = rit->get(); + if (provider->hasGlobDeleteAll(name)) + patterns.clear(); + patterns += provider->globPatterns(name); } + return patterns; } -void QMimeDatabasePrivate::loadIcon(QMimeTypePrivate &mimePrivate) +QString QMimeDatabasePrivate::genericIcon(const QString &name) { QMutexLocker locker(&mutex); - if (mimePrivate.fromCache) { - mimePrivate.iconName.clear(); - for (const auto &provider : providers()) { - provider->loadIcon(mimePrivate); - if (!mimePrivate.iconName.isEmpty()) - break; - } + for (const auto &provider : providers()) { + QString genericIconName = provider->genericIcon(name); + if (!genericIconName.isEmpty()) + return genericIconName; + } + return {}; +} + +QString QMimeDatabasePrivate::icon(const QString &name) +{ + QMutexLocker locker(&mutex); + for (const auto &provider : providers()) { + QString iconName = provider->icon(name); + if (!iconName.isEmpty()) + return iconName; } + return {}; } QString QMimeDatabasePrivate::fallbackParent(const QString &mimeTypeName) const { - const QStringView myGroup = QStringView{mimeTypeName}.left(mimeTypeName.indexOf(QLatin1Char('/'))); + const QStringView myGroup = QStringView{mimeTypeName}.left(mimeTypeName.indexOf(u'/')); // All text/* types are subclasses of text/plain. - if (myGroup == QLatin1String("text") && mimeTypeName != plainTextMimeType()) + if (myGroup == "text"_L1 && mimeTypeName != plainTextMimeType()) return plainTextMimeType(); // All real-file mimetypes implicitly derive from application/octet-stream - if (myGroup != QLatin1String("inode") && + if (myGroup != "inode"_L1 && // ignore non-file extensions - myGroup != QLatin1String("all") && myGroup != QLatin1String("fonts") && myGroup != QLatin1String("print") && myGroup != QLatin1String("uri") + myGroup != "all"_L1 && myGroup != "fonts"_L1 && myGroup != "print"_L1 && myGroup != "uri"_L1 && mimeTypeName != defaultMimeType()) { return defaultMimeType(); } @@ -363,13 +337,14 @@ QMimeType QMimeDatabasePrivate::findByData(const QByteArray &data, int *accuracy return mimeTypeForName(QStringLiteral("application/x-zerosize")); } - *accuracyPtr = 0; - QMimeType candidate; + QMimeMagicResult result; for (const auto &provider : providers()) - provider->findByMagic(data, accuracyPtr, candidate); + provider->findByMagic(data, result); - if (candidate.isValid()) - return candidate; + if (result.isValid()) { + *accuracyPtr = result.accuracy; + return QMimeType(QMimeTypePrivate(result.candidate)); + } if (isTextFile(data)) { *accuracyPtr = 5; @@ -389,7 +364,7 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa // Pass 1) Try to match on the file name QMimeGlobMatchResult candidatesByName = findByFileName(fileName); - if (candidatesByName.m_allMatchingMimeTypes.count() == 1) { + if (candidatesByName.m_allMatchingMimeTypes.size() == 1) { const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0)); if (mime.isValid()) return mime; @@ -399,11 +374,15 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa // Extension is unknown, or matches multiple mimetypes. // Pass 2) Match on content, if we can read the data const auto matchOnContent = [this, &candidatesByName](QIODevice *device) { + const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly); if (device->isOpen()) { // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h). // This is much faster than seeking back and forth into QIODevice. const QByteArray data = device->peek(16384); + if (openedByUs) + device->close(); + int magicAccuracy = 0; QMimeType candidateByData(findByData(data, &magicAccuracy)); @@ -414,7 +393,7 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime)) { return candidateByData; } - for (const QString &m : qAsConst(candidatesByName.m_allMatchingMimeTypes)) { + for (const QString &m : std::as_const(candidatesByName.m_allMatchingMimeTypes)) { if (inherits(m, sniffedMime)) { // We have magic + pattern pointing to this, so it's a pretty good match return mimeTypeForName(m); @@ -427,7 +406,7 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa } } - if (candidatesByName.m_allMatchingMimeTypes.count() > 1) { + if (candidatesByName.m_allMatchingMimeTypes.size() > 1) { candidatesByName.m_matchingMimeTypes.sort(); // make it deterministic const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0)); if (mime.isValid()) @@ -441,7 +420,6 @@ QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileNa return matchOnContent(device); QFile fallbackFile(fileName); - fallbackFile.open(QIODevice::ReadOnly); // error handling: matchOnContent() will check isOpen() return matchOnContent(&fallbackFile); } @@ -473,31 +451,32 @@ QMimeType QMimeDatabasePrivate::mimeTypeForData(QIODevice *device) } QMimeType QMimeDatabasePrivate::mimeTypeForFile(const QString &fileName, - [[maybe_unused]] const QFileInfo *fileInfo, + const QFileInfo &fileInfo, QMimeDatabase::MatchMode mode) { + if (false) { #ifdef Q_OS_UNIX - // Cannot access statBuf.st_mode from the filesystem engine, so we have to stat again. - // In addition we want to follow symlinks. - const QByteArray nativeFilePath = QFile::encodeName(fileName); - QT_STATBUF statBuffer; - if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) { - if (S_ISDIR(statBuffer.st_mode)) - return mimeTypeForName(directoryMimeType()); - if (S_ISCHR(statBuffer.st_mode)) - return mimeTypeForName(QStringLiteral("inode/chardevice")); - if (S_ISBLK(statBuffer.st_mode)) - return mimeTypeForName(QStringLiteral("inode/blockdevice")); - if (S_ISFIFO(statBuffer.st_mode)) - return mimeTypeForName(QStringLiteral("inode/fifo")); - if (S_ISSOCK(statBuffer.st_mode)) - return mimeTypeForName(QStringLiteral("inode/socket")); - } -#else - const bool isDirectory = fileInfo ? fileInfo->isDir() : QFileInfo(fileName).isDir(); - if (isDirectory) - return mimeTypeForName(directoryMimeType()); + } else if (fileInfo.isNativePath()) { + // If this is a local file, we'll want to do a stat() ourselves so we can + // detect additional inode types. In addition we want to follow symlinks. + const QByteArray nativeFilePath = QFile::encodeName(fileName); + QT_STATBUF statBuffer; + if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) { + if (S_ISDIR(statBuffer.st_mode)) + return mimeTypeForName(directoryMimeType()); + if (S_ISCHR(statBuffer.st_mode)) + return mimeTypeForName(QStringLiteral("inode/chardevice")); + if (S_ISBLK(statBuffer.st_mode)) + return mimeTypeForName(QStringLiteral("inode/blockdevice")); + if (S_ISFIFO(statBuffer.st_mode)) + return mimeTypeForName(QStringLiteral("inode/fifo")); + if (S_ISSOCK(statBuffer.st_mode)) + return mimeTypeForName(QStringLiteral("inode/socket")); + } #endif + } else if (fileInfo.isDir()) { + return mimeTypeForName(directoryMimeType()); + } switch (mode) { case QMimeDatabase::MatchDefault: @@ -524,6 +503,7 @@ QList<QMimeType> QMimeDatabasePrivate::allMimeTypes() bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent) { const QString resolvedParent = resolveAlias(parent); + QDuplicateTracker<QString> seen; std::stack<QString, QStringList> toCheck; toCheck.push(mime); while (!toCheck.empty()) { @@ -532,8 +512,11 @@ bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent) const QString mimeName = toCheck.top(); toCheck.pop(); const auto parentList = parents(mimeName); - for (const QString &par : parentList) - toCheck.push(resolveAlias(par)); + for (const QString &par : parentList) { + const QString resolvedPar = resolveAlias(par); + if (!seen.hasSeen(resolvedPar)) + toCheck.push(resolvedPar); + } } return false; } @@ -575,7 +558,7 @@ bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent) \snippet code/src_corelib_mimetype_qmimedatabase.cpp 0 - \sa QMimeType, {MIME Type Browser Example} + \sa QMimeType, {MIME Type Browser} */ /*! @@ -644,7 +627,7 @@ QMimeType QMimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mo { QMutexLocker locker(&d->mutex); - return d->mimeTypeForFile(fileInfo.filePath(), &fileInfo, mode); + return d->mimeTypeForFile(fileInfo.filePath(), fileInfo, mode); } /*! @@ -659,7 +642,8 @@ QMimeType QMimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode if (mode == MatchExtension) { return d->mimeTypeForFileExtension(fileName); } else { - return d->mimeTypeForFile(fileName, nullptr, mode); + QFileInfo fileInfo(fileName); + return d->mimeTypeForFile(fileName, fileInfo, mode); } } @@ -681,7 +665,7 @@ QList<QMimeType> QMimeDatabase::mimeTypesForFileName(const QString &fileName) co const QStringList matches = d->mimeTypeForFileName(fileName); QList<QMimeType> mimes; - mimes.reserve(matches.count()); + mimes.reserve(matches.size()); for (const QString &mime : matches) mimes.append(d->mimeTypeForName(mime)); return mimes; @@ -695,7 +679,7 @@ QList<QMimeType> QMimeDatabase::mimeTypesForFileName(const QString &fileName) co QString QMimeDatabase::suffixForFileName(const QString &fileName) const { QMutexLocker locker(&d->mutex); - const int suffixLength = d->findByFileName(fileName).m_knownSuffixLength; + const qsizetype suffixLength = d->findByFileName(fileName).m_knownSuffixLength; return fileName.right(suffixLength); } @@ -748,7 +732,7 @@ QMimeType QMimeDatabase::mimeTypeForUrl(const QUrl &url) const return mimeTypeForFile(url.toLocalFile()); const QString scheme = url.scheme(); - if (scheme.startsWith(QLatin1String("http")) || scheme == QLatin1String("mailto")) + if (scheme.startsWith("http"_L1) || scheme == "mailto"_L1) return mimeTypeForName(d->defaultMimeType()); return mimeTypeForFile(url.path(), MatchExtension); @@ -777,13 +761,10 @@ QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, QIO { QMutexLocker locker(&d->mutex); - if (fileName.endsWith(QLatin1Char('/'))) + if (fileName.endsWith(u'/')) return d->mimeTypeForName(directoryMimeType()); - const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly); const QMimeType result = d->mimeTypeForFileNameAndData(fileName, device); - if (openedByUs) - device->close(); return result; } @@ -807,7 +788,7 @@ QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, con { QMutexLocker locker(&d->mutex); - if (fileName.endsWith(QLatin1Char('/'))) + if (fileName.endsWith(u'/')) return d->mimeTypeForName(directoryMimeType()); QBuffer buffer(const_cast<QByteArray *>(&data)); |