summaryrefslogtreecommitdiffstats
path: root/src/corelib/mimetypes/qmimedatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/mimetypes/qmimedatabase.cpp')
-rw-r--r--src/corelib/mimetypes/qmimedatabase.cpp237
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));