forked from qt-creator/qt-creator
Mimetypes v3: Copy version from Qt 6.3
State from qtbase/67b2eb8b302c19c9b3a80028e20141bde9aef01a from 6.3 branch Change-Id: If1be497af3907f31f1388e9db42150cf9ae60d66 Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
832
src/libs/utils/mimetypes2/qmimedatabase.cpp
Normal file
832
src/libs/utils/mimetypes2/qmimedatabase.cpp
Normal file
@@ -0,0 +1,832 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qplatformdefs.h> // always first
|
||||
|
||||
#include "qmimedatabase.h"
|
||||
#include "qmimedatabase_p.h"
|
||||
|
||||
#include "qmimeprovider_p.h"
|
||||
#include "qmimetype_p.h"
|
||||
|
||||
#include <private/qfilesystementry_p.h>
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_GLOBAL_STATIC(QMimeDatabasePrivate, staticQMimeDatabase)
|
||||
|
||||
QMimeDatabasePrivate *QMimeDatabasePrivate::instance()
|
||||
{
|
||||
return staticQMimeDatabase();
|
||||
}
|
||||
|
||||
QMimeDatabasePrivate::QMimeDatabasePrivate()
|
||||
: m_defaultMimeType(QLatin1String("application/octet-stream"))
|
||||
{
|
||||
}
|
||||
|
||||
QMimeDatabasePrivate::~QMimeDatabasePrivate()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
Q_CORE_EXPORT
|
||||
#else
|
||||
static const
|
||||
#endif
|
||||
int qmime_secondsBetweenChecks = 5;
|
||||
|
||||
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_NACL) && !defined(Q_OS_INTEGRITY)
|
||||
# define QT_USE_MMAP
|
||||
#endif
|
||||
|
||||
void QMimeDatabasePrivate::loadProviders()
|
||||
{
|
||||
// We use QStandardPaths every time to check if new files appeared
|
||||
const 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")); }
|
||||
);
|
||||
const bool needInternalDB = QMimeXMLProvider::InternalDatabaseAvailable && fdoIterator == mimeDirs.constEnd();
|
||||
//qDebug() << "mime dirs:" << mimeDirs;
|
||||
|
||||
Providers currentProviders;
|
||||
std::swap(m_providers, currentProviders);
|
||||
|
||||
m_providers.reserve(mimeDirs.size() + (needInternalDB ? 1 : 0));
|
||||
|
||||
for (const QString &mimeDir : mimeDirs) {
|
||||
const QString cacheFile = mimeDir + QStringLiteral("/mime.cache");
|
||||
// Check if we already have a provider for this dir
|
||||
const auto predicate = [mimeDir](const std::unique_ptr<QMimeProviderBase> &prov)
|
||||
{
|
||||
return prov && prov->directory() == mimeDir;
|
||||
};
|
||||
const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), predicate);
|
||||
if (it == currentProviders.end()) {
|
||||
std::unique_ptr<QMimeProviderBase> provider;
|
||||
#if defined(QT_USE_MMAP)
|
||||
if (qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE") && QFileInfo::exists(cacheFile)) {
|
||||
provider.reset(new QMimeBinaryProvider(this, mimeDir));
|
||||
//qDebug() << "Created binary provider for" << mimeDir;
|
||||
if (!provider->isValid()) {
|
||||
provider.reset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!provider) {
|
||||
provider.reset(new QMimeXMLProvider(this, mimeDir));
|
||||
//qDebug() << "Created XML provider for" << mimeDir;
|
||||
}
|
||||
m_providers.push_back(std::move(provider));
|
||||
} else {
|
||||
auto provider = std::move(*it); // take provider out of the vector
|
||||
provider->ensureLoaded();
|
||||
if (!provider->isValid()) {
|
||||
provider.reset(new QMimeXMLProvider(this, mimeDir));
|
||||
//qDebug() << "Created XML provider to replace binary provider for" << mimeDir;
|
||||
}
|
||||
m_providers.push_back(std::move(provider));
|
||||
}
|
||||
}
|
||||
// mimeDirs is sorted "most local first, most global last"
|
||||
// so the internal XML DB goes at the end
|
||||
if (needInternalDB) {
|
||||
// Check if we already have a provider for the InternalDatabase
|
||||
const auto isInternal = [](const std::unique_ptr<QMimeProviderBase> &prov)
|
||||
{
|
||||
return prov && prov->isInternalDatabase();
|
||||
};
|
||||
const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), isInternal);
|
||||
if (it == currentProviders.end()) {
|
||||
m_providers.push_back(Providers::value_type(new QMimeXMLProvider(this, QMimeXMLProvider::InternalDatabase)));
|
||||
} else {
|
||||
m_providers.push_back(std::move(*it));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QMimeDatabasePrivate::Providers &QMimeDatabasePrivate::providers()
|
||||
{
|
||||
#ifndef Q_OS_WASM // stub implementation always returns true
|
||||
Q_ASSERT(!mutex.tryLock()); // caller should have locked mutex
|
||||
#endif
|
||||
if (m_providers.empty()) {
|
||||
loadProviders();
|
||||
m_lastCheck.start();
|
||||
} else {
|
||||
if (shouldCheck())
|
||||
loadProviders();
|
||||
}
|
||||
return m_providers;
|
||||
}
|
||||
|
||||
QString QMimeDatabasePrivate::resolveAlias(const QString &nameOrAlias)
|
||||
{
|
||||
for (const auto &provider : providers()) {
|
||||
const QString ret = provider->resolveAlias(nameOrAlias);
|
||||
if (!ret.isEmpty())
|
||||
return ret;
|
||||
}
|
||||
return nameOrAlias;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Returns a MIME type or an invalid one if none found
|
||||
*/
|
||||
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;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName)
|
||||
{
|
||||
if (fileName.endsWith(QLatin1Char('/')))
|
||||
return QStringList() << QLatin1String("inode/directory");
|
||||
|
||||
const QMimeGlobMatchResult result = findByFileName(fileName);
|
||||
QStringList matchingMimeTypes = result.m_matchingMimeTypes;
|
||||
matchingMimeTypes.sort(); // make it deterministic
|
||||
return matchingMimeTypes;
|
||||
}
|
||||
|
||||
QMimeGlobMatchResult QMimeDatabasePrivate::findByFileName(const QString &fileName)
|
||||
{
|
||||
QMimeGlobMatchResult result;
|
||||
const QString fileNameExcludingPath = QFileSystemEntry(fileName).fileName();
|
||||
for (const auto &provider : providers())
|
||||
provider->addFileNameMatches(fileNameExcludingPath, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void QMimeDatabasePrivate::loadMimeTypePrivate(QMimeTypePrivate &mimePrivate)
|
||||
{
|
||||
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..."
|
||||
<< QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory);
|
||||
}
|
||||
mimePrivate.loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeDatabasePrivate::loadGenericIcon(QMimeTypePrivate &mimePrivate)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (mimePrivate.fromCache) {
|
||||
mimePrivate.genericIconName.clear();
|
||||
for (const auto &provider : providers()) {
|
||||
provider->loadGenericIcon(mimePrivate);
|
||||
if (!mimePrivate.genericIconName.isEmpty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeDatabasePrivate::loadIcon(QMimeTypePrivate &mimePrivate)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (mimePrivate.fromCache) {
|
||||
mimePrivate.iconName.clear();
|
||||
for (const auto &provider : providers()) {
|
||||
provider->loadIcon(mimePrivate);
|
||||
if (!mimePrivate.iconName.isEmpty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static QString fallbackParent(const QString &mimeTypeName)
|
||||
{
|
||||
const QStringView myGroup = QStringView{mimeTypeName}.left(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;
|
||||
for (const auto &provider : providers())
|
||||
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;
|
||||
for (const auto &provider : providers())
|
||||
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
|
||||
static const char bigEndianBOM[] = "\xFE\xFF";
|
||||
static const char littleEndianBOM[] = "\xFF\xFE";
|
||||
if (data.startsWith(bigEndianBOM) || data.startsWith(littleEndianBOM))
|
||||
return true;
|
||||
|
||||
// Check the first 128 bytes (see shared-mime spec)
|
||||
const char *p = data.constData();
|
||||
const char *e = p + qMin(128, data.size());
|
||||
for ( ; p < e; ++p) {
|
||||
if (static_cast<unsigned char>(*p) < 32 && *p != 9 && *p !=10 && *p != 13)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QMimeType QMimeDatabasePrivate::findByData(const QByteArray &data, int *accuracyPtr)
|
||||
{
|
||||
if (data.isEmpty()) {
|
||||
*accuracyPtr = 100;
|
||||
return mimeTypeForName(QLatin1String("application/x-zerosize"));
|
||||
}
|
||||
|
||||
*accuracyPtr = 0;
|
||||
QMimeType candidate;
|
||||
for (const auto &provider : providers())
|
||||
provider->findByMagic(data, accuracyPtr, candidate);
|
||||
|
||||
if (candidate.isValid())
|
||||
return candidate;
|
||||
|
||||
if (isTextFile(data)) {
|
||||
*accuracyPtr = 5;
|
||||
return mimeTypeForName(QLatin1String("text/plain"));
|
||||
}
|
||||
|
||||
return mimeTypeForName(defaultMimeType());
|
||||
}
|
||||
|
||||
QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device, int *accuracyPtr)
|
||||
{
|
||||
// First, glob patterns are evaluated. If there is a match with max weight,
|
||||
// this one is selected and we are done. Otherwise, the file contents are
|
||||
// evaluated and the match with the highest value (either a magic priority or
|
||||
// a glob pattern weight) is selected. Matching starts from max level (most
|
||||
// specific) in both cases, even when there is already a suffix matching candidate.
|
||||
*accuracyPtr = 0;
|
||||
|
||||
// Pass 1) Try to match on the file name
|
||||
QMimeGlobMatchResult candidatesByName = findByFileName(fileName);
|
||||
if (candidatesByName.m_allMatchingMimeTypes.count() == 1) {
|
||||
*accuracyPtr = 100;
|
||||
const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
|
||||
if (mime.isValid())
|
||||
return mime;
|
||||
candidatesByName = {};
|
||||
}
|
||||
|
||||
// Extension is unknown, or matches multiple mimetypes.
|
||||
// Pass 2) Match on content, if we can read the data
|
||||
const auto matchOnContent = [this, accuracyPtr, &candidatesByName](QIODevice *device) {
|
||||
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);
|
||||
|
||||
int magicAccuracy = 0;
|
||||
QMimeType candidateByData(findByData(data, &magicAccuracy));
|
||||
|
||||
// Disambiguate conflicting extensions (if magic matching found something)
|
||||
if (candidateByData.isValid() && magicAccuracy > 0) {
|
||||
const QString sniffedMime = candidateByData.name();
|
||||
// If the sniffedMime matches a highest-weight glob match, use it
|
||||
if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime)) {
|
||||
*accuracyPtr = 100;
|
||||
return candidateByData;
|
||||
}
|
||||
for (const QString &m : qAsConst(candidatesByName.m_allMatchingMimeTypes)) {
|
||||
if (inherits(m, sniffedMime)) {
|
||||
// We have magic + pattern pointing to this, so it's a pretty good match
|
||||
*accuracyPtr = 100;
|
||||
return mimeTypeForName(m);
|
||||
}
|
||||
}
|
||||
if (candidatesByName.m_allMatchingMimeTypes.isEmpty()) {
|
||||
// No glob, use magic
|
||||
*accuracyPtr = magicAccuracy;
|
||||
return candidateByData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidatesByName.m_allMatchingMimeTypes.count() > 1) {
|
||||
candidatesByName.m_matchingMimeTypes.sort(); // make it deterministic
|
||||
*accuracyPtr = 20;
|
||||
const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
|
||||
if (mime.isValid())
|
||||
return mime;
|
||||
}
|
||||
|
||||
return mimeTypeForName(defaultMimeType());
|
||||
};
|
||||
|
||||
if (device)
|
||||
return matchOnContent(device);
|
||||
|
||||
QFile fallbackFile(fileName);
|
||||
fallbackFile.open(QIODevice::ReadOnly); // error handling: matchOnContent() will check isOpen()
|
||||
return matchOnContent(&fallbackFile);
|
||||
}
|
||||
|
||||
QList<QMimeType> QMimeDatabasePrivate::allMimeTypes()
|
||||
{
|
||||
QList<QMimeType> result;
|
||||
for (const auto &provider : providers())
|
||||
provider->addAllMimeTypes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent)
|
||||
{
|
||||
const QString resolvedParent = resolveAlias(parent);
|
||||
std::stack<QString, QStringList> toCheck;
|
||||
toCheck.push(mime);
|
||||
while (!toCheck.empty()) {
|
||||
if (toCheck.top() == resolvedParent)
|
||||
return true;
|
||||
const QString mimeName = toCheck.top();
|
||||
toCheck.pop();
|
||||
const auto parentList = parents(mimeName);
|
||||
for (const QString &par : parentList)
|
||||
toCheck.push(resolveAlias(par));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QMimeDatabase
|
||||
\inmodule QtCore
|
||||
\brief The QMimeDatabase class maintains a database of MIME types.
|
||||
|
||||
\since 5.0
|
||||
|
||||
The MIME type database is provided by the freedesktop.org shared-mime-info
|
||||
project. If the MIME type database cannot be found on the system, as is the case
|
||||
on most Windows, \macos, and iOS systems, Qt will use its own copy of it.
|
||||
|
||||
Applications which want to define custom MIME types need to install an
|
||||
XML file into the locations searched for MIME definitions.
|
||||
These locations can be queried with
|
||||
\snippet code/src_corelib_mimetype_qmimedatabase.cpp 1
|
||||
On a typical Unix system, this will be /usr/share/mime/packages/, but it is also
|
||||
possible to extend the list of directories by setting the environment variable
|
||||
\c XDG_DATA_DIRS. For instance adding /opt/myapp/share to \c XDG_DATA_DIRS will result
|
||||
in /opt/myapp/share/mime/packages/ being searched for MIME definitions.
|
||||
|
||||
Here is an example of MIME XML:
|
||||
\snippet code/src_corelib_mimetype_qmimedatabase.cpp 2
|
||||
|
||||
For more details about the syntax of XML MIME definitions, including defining
|
||||
"magic" in order to detect MIME types based on data as well, read the
|
||||
Shared Mime Info specification at
|
||||
http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||
|
||||
On Unix systems, a binary cache is used for more performance. This cache is generated
|
||||
by the command "update-mime-database path", where path would be /opt/myapp/share/mime
|
||||
in the above example. Make sure to run this command when installing the MIME type
|
||||
definition file.
|
||||
|
||||
\threadsafe
|
||||
|
||||
\snippet code/src_corelib_mimetype_qmimedatabase.cpp 0
|
||||
|
||||
\sa QMimeType, {MIME Type Browser Example}
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QMimeDatabase::QMimeDatabase();
|
||||
Constructs a QMimeDatabase object.
|
||||
|
||||
It is perfectly OK to create an instance of QMimeDatabase every time you need to
|
||||
perform a lookup.
|
||||
The parsing of mimetypes is done on demand (when shared-mime-info is installed)
|
||||
or when the very first instance is constructed (when parsing XML files directly).
|
||||
*/
|
||||
QMimeDatabase::QMimeDatabase() :
|
||||
d(staticQMimeDatabase())
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeDatabase::~QMimeDatabase();
|
||||
Destroys the QMimeDatabase object.
|
||||
*/
|
||||
QMimeDatabase::~QMimeDatabase()
|
||||
{
|
||||
d = nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const;
|
||||
Returns a MIME type for \a nameOrAlias or an invalid one if none found.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
return d->mimeTypeForName(nameOrAlias);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for \a fileInfo.
|
||||
|
||||
A valid MIME type is always returned.
|
||||
|
||||
The default matching algorithm looks at both the file name and the file
|
||||
contents, if necessary. The file extension has priority over the contents,
|
||||
but the contents will be used if the file extension is unknown, or
|
||||
matches multiple MIME types.
|
||||
If \a fileInfo is a Unix symbolic link, the file that it refers to
|
||||
will be used instead.
|
||||
If the file doesn't match any known pattern or data, the default MIME type
|
||||
(application/octet-stream) is returned.
|
||||
|
||||
When \a mode is set to MatchExtension, only the file name is used, not
|
||||
the file contents. The file doesn't even have to exist. If the file name
|
||||
doesn't match any known pattern, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
If multiple MIME types match this file, the first one (alphabetically) is returned.
|
||||
|
||||
When \a mode is set to MatchContent, and the file is readable, only the
|
||||
file contents are used to determine the MIME type. This is equivalent to
|
||||
calling mimeTypeForData with a QFile as input device.
|
||||
|
||||
\a fileInfo may refer to an absolute or relative path.
|
||||
|
||||
\sa QMimeType::isDefault(), mimeTypeForData()
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
if (fileInfo.isDir())
|
||||
return d->mimeTypeForName(QLatin1String("inode/directory"));
|
||||
|
||||
const QString filePath = fileInfo.filePath();
|
||||
|
||||
#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(filePath);
|
||||
QT_STATBUF statBuffer;
|
||||
if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) {
|
||||
if (S_ISCHR(statBuffer.st_mode))
|
||||
return d->mimeTypeForName(QLatin1String("inode/chardevice"));
|
||||
if (S_ISBLK(statBuffer.st_mode))
|
||||
return d->mimeTypeForName(QLatin1String("inode/blockdevice"));
|
||||
if (S_ISFIFO(statBuffer.st_mode))
|
||||
return d->mimeTypeForName(QLatin1String("inode/fifo"));
|
||||
if (S_ISSOCK(statBuffer.st_mode))
|
||||
return d->mimeTypeForName(QLatin1String("inode/socket"));
|
||||
}
|
||||
#endif
|
||||
|
||||
int priority = 0;
|
||||
switch (mode) {
|
||||
case MatchDefault:
|
||||
return d->mimeTypeForFileNameAndData(filePath, nullptr, &priority);
|
||||
case MatchExtension:
|
||||
locker.unlock();
|
||||
return mimeTypeForFile(filePath, mode);
|
||||
case MatchContent: {
|
||||
QFile file(filePath);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
locker.unlock();
|
||||
return mimeTypeForData(&file);
|
||||
} else {
|
||||
return d->mimeTypeForName(d->defaultMimeType());
|
||||
}
|
||||
}
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
return d->mimeTypeForName(d->defaultMimeType());
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for the file named \a fileName using \a mode.
|
||||
|
||||
\overload
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) const
|
||||
{
|
||||
if (mode == MatchExtension) {
|
||||
QMutexLocker locker(&d->mutex);
|
||||
const QStringList matches = d->mimeTypeForFileName(fileName);
|
||||
const int matchCount = matches.count();
|
||||
if (matchCount == 0) {
|
||||
return d->mimeTypeForName(d->defaultMimeType());
|
||||
} else if (matchCount == 1) {
|
||||
return d->mimeTypeForName(matches.first());
|
||||
} else {
|
||||
// We have to pick one.
|
||||
return d->mimeTypeForName(matches.first());
|
||||
}
|
||||
} else {
|
||||
// Implemented as a wrapper around mimeTypeForFile(QFileInfo), so no mutex.
|
||||
QFileInfo fileInfo(fileName);
|
||||
return mimeTypeForFile(fileInfo, mode);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the MIME types for the file name \a fileName.
|
||||
|
||||
If the file name doesn't match any known pattern, an empty list is returned.
|
||||
If multiple MIME types match this file, they are all returned.
|
||||
|
||||
This function does not try to open the file. To also use the content
|
||||
when determining the MIME type, use mimeTypeForFile() or
|
||||
mimeTypeForFileNameAndData() instead.
|
||||
|
||||
\sa mimeTypeForFile()
|
||||
*/
|
||||
QList<QMimeType> QMimeDatabase::mimeTypesForFileName(const QString &fileName) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
const QStringList matches = d->mimeTypeForFileName(fileName);
|
||||
QList<QMimeType> mimes;
|
||||
mimes.reserve(matches.count());
|
||||
for (const QString &mime : matches)
|
||||
mimes.append(d->mimeTypeForName(mime));
|
||||
return mimes;
|
||||
}
|
||||
/*!
|
||||
Returns the suffix for the file \a fileName, as known by the MIME database.
|
||||
|
||||
This allows to pre-select "tar.bz2" for foo.tar.bz2, but still only
|
||||
"txt" for my.file.with.dots.txt.
|
||||
*/
|
||||
QString QMimeDatabase::suffixForFileName(const QString &fileName) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
const int suffixLength = d->findByFileName(fileName).m_knownSuffixLength;
|
||||
return fileName.right(suffixLength);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for \a data.
|
||||
|
||||
A valid MIME type is always returned. If \a data doesn't match any
|
||||
known MIME type data, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForData(const QByteArray &data) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
int accuracy = 0;
|
||||
return d->findByData(data, &accuracy);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for the data in \a device.
|
||||
|
||||
A valid MIME type is always returned. If the data in \a device doesn't match any
|
||||
known MIME type data, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForData(QIODevice *device) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
int accuracy = 0;
|
||||
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);
|
||||
const QMimeType result = d->findByData(data, &accuracy);
|
||||
if (openedByUs)
|
||||
device->close();
|
||||
return result;
|
||||
}
|
||||
return d->mimeTypeForName(d->defaultMimeType());
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for \a url.
|
||||
|
||||
If the URL is a local file, this calls mimeTypeForFile.
|
||||
|
||||
Otherwise the matching is done based on the file name only,
|
||||
except for schemes where file names don't mean much, like HTTP.
|
||||
This method always returns the default mimetype for HTTP URLs,
|
||||
use QNetworkAccessManager to handle HTTP URLs properly.
|
||||
|
||||
A valid MIME type is always returned. If \a url doesn't match any
|
||||
known MIME type data, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForUrl(const QUrl &url) const
|
||||
{
|
||||
if (url.isLocalFile())
|
||||
return mimeTypeForFile(url.toLocalFile());
|
||||
|
||||
const QString scheme = url.scheme();
|
||||
if (scheme.startsWith(QLatin1String("http")) || scheme == QLatin1String("mailto"))
|
||||
return mimeTypeForName(d->defaultMimeType());
|
||||
|
||||
return mimeTypeForFile(url.path(), MatchExtension);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for the given \a fileName and \a device data.
|
||||
|
||||
This overload can be useful when the file is remote, and we started to
|
||||
download some of its data in a device. This allows to do full MIME type
|
||||
matching for remote files as well.
|
||||
|
||||
If the device is not open, it will be opened by this function, and closed
|
||||
after the MIME type detection is completed.
|
||||
|
||||
A valid MIME type is always returned. If \a device data doesn't match any
|
||||
known MIME type data, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
|
||||
This method looks at both the file name and the file contents,
|
||||
if necessary. The file extension has priority over the contents,
|
||||
but the contents will be used if the file extension is unknown, or
|
||||
matches multiple MIME types.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
if (fileName.endsWith(QLatin1Char('/')))
|
||||
return d->mimeTypeForName(QLatin1String("inode/directory"));
|
||||
|
||||
int accuracy = 0;
|
||||
const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
|
||||
const QMimeType result = d->mimeTypeForFileNameAndData(fileName, device, &accuracy);
|
||||
if (openedByUs)
|
||||
device->close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for the given \a fileName and device \a data.
|
||||
|
||||
This overload can be useful when the file is remote, and we started to
|
||||
download some of its data. This allows to do full MIME type matching for
|
||||
remote files as well.
|
||||
|
||||
A valid MIME type is always returned. If \a data doesn't match any
|
||||
known MIME type data, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
|
||||
This method looks at both the file name and the file contents,
|
||||
if necessary. The file extension has priority over the contents,
|
||||
but the contents will be used if the file extension is unknown, or
|
||||
matches multiple MIME types.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
if (fileName.endsWith(QLatin1Char('/')))
|
||||
return d->mimeTypeForName(QLatin1String("inode/directory"));
|
||||
|
||||
QBuffer buffer(const_cast<QByteArray *>(&data));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
int accuracy = 0;
|
||||
return d->mimeTypeForFileNameAndData(fileName, &buffer, &accuracy);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the list of all available MIME types.
|
||||
|
||||
This can be useful for showing all MIME types to the user, for instance
|
||||
in a MIME type editor. Do not use unless really necessary in other cases
|
||||
though, prefer using the \l {mimeTypeForData()}{mimeTypeForXxx()} methods for performance reasons.
|
||||
*/
|
||||
QList<QMimeType> QMimeDatabase::allMimeTypes() const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
return d->allMimeTypes();
|
||||
}
|
||||
|
||||
/*!
|
||||
\enum QMimeDatabase::MatchMode
|
||||
|
||||
This enum specifies how matching a file to a MIME type is performed.
|
||||
|
||||
\value MatchDefault Both the file name and content are used to look for a match
|
||||
|
||||
\value MatchExtension Only the file name is used to look for a match
|
||||
|
||||
\value MatchContent The file content is used to look for a match
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
95
src/libs/utils/mimetypes2/qmimedatabase.h
Normal file
95
src/libs/utils/mimetypes2/qmimedatabase.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMEDATABASE_H
|
||||
#define QMIMEDATABASE_H
|
||||
|
||||
#include <QtCore/qmimetype.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include <QtCore/qstringlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QByteArray;
|
||||
class QFileInfo;
|
||||
class QIODevice;
|
||||
class QUrl;
|
||||
|
||||
class QMimeDatabasePrivate;
|
||||
class Q_CORE_EXPORT QMimeDatabase
|
||||
{
|
||||
Q_DISABLE_COPY(QMimeDatabase)
|
||||
|
||||
public:
|
||||
QMimeDatabase();
|
||||
~QMimeDatabase();
|
||||
|
||||
QMimeType mimeTypeForName(const QString &nameOrAlias) const;
|
||||
|
||||
enum MatchMode {
|
||||
MatchDefault = 0x0,
|
||||
MatchExtension = 0x1,
|
||||
MatchContent = 0x2
|
||||
};
|
||||
|
||||
QMimeType mimeTypeForFile(const QString &fileName, MatchMode mode = MatchDefault) const;
|
||||
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode = MatchDefault) const;
|
||||
QList<QMimeType> mimeTypesForFileName(const QString &fileName) const;
|
||||
|
||||
QMimeType mimeTypeForData(const QByteArray &data) const;
|
||||
QMimeType mimeTypeForData(QIODevice *device) const;
|
||||
|
||||
QMimeType mimeTypeForUrl(const QUrl &url) const;
|
||||
QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const;
|
||||
QMimeType mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const;
|
||||
|
||||
QString suffixForFileName(const QString &fileName) const;
|
||||
|
||||
QList<QMimeType> allMimeTypes() const;
|
||||
|
||||
private:
|
||||
QMimeDatabasePrivate *d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEDATABASE_H
|
123
src/libs/utils/mimetypes2/qmimedatabase_p.h
Normal file
123
src/libs/utils/mimetypes2/qmimedatabase_p.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMEDATABASE_P_H
|
||||
#define QMIMEDATABASE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qmimetype.h"
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include "qmimetype_p.h"
|
||||
#include "qmimeglobpattern_p.h"
|
||||
|
||||
#include <QtCore/qelapsedtimer.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QIODevice;
|
||||
class QMimeDatabase;
|
||||
class QMimeProviderBase;
|
||||
|
||||
class QMimeDatabasePrivate
|
||||
{
|
||||
public:
|
||||
Q_DISABLE_COPY_MOVE(QMimeDatabasePrivate)
|
||||
|
||||
QMimeDatabasePrivate();
|
||||
~QMimeDatabasePrivate();
|
||||
|
||||
static QMimeDatabasePrivate *instance();
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
using Providers = std::vector<std::unique_ptr<QMimeProviderBase>>;
|
||||
const Providers &providers();
|
||||
bool shouldCheck();
|
||||
void loadProviders();
|
||||
|
||||
mutable Providers m_providers;
|
||||
QElapsedTimer m_lastCheck;
|
||||
|
||||
public:
|
||||
const QString m_defaultMimeType;
|
||||
QMutex mutex;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEDATABASE_P_H
|
304
src/libs/utils/mimetypes2/qmimeglobpattern.cpp
Normal file
304
src/libs/utils/mimetypes2/qmimeglobpattern.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmimeglobpattern_p.h"
|
||||
|
||||
#if QT_CONFIG(regularexpression)
|
||||
#include <QRegularExpression>
|
||||
#endif
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\class QMimeGlobMatchResult
|
||||
\inmodule QtCore
|
||||
\brief The QMimeGlobMatchResult class accumulates results from glob matching.
|
||||
|
||||
Handles glob weights, and preferring longer matches over shorter matches.
|
||||
*/
|
||||
|
||||
void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const QString &pattern, int knownSuffixLength)
|
||||
{
|
||||
if (m_allMatchingMimeTypes.contains(mimeType))
|
||||
return;
|
||||
// Is this a lower-weight pattern than the last match? Skip this match then.
|
||||
if (weight < m_weight) {
|
||||
m_allMatchingMimeTypes.append(mimeType);
|
||||
return;
|
||||
}
|
||||
bool replace = weight > m_weight;
|
||||
if (!replace) {
|
||||
// Compare the length of the match
|
||||
if (pattern.length() < m_matchingPatternLength)
|
||||
return; // too short, ignore
|
||||
else if (pattern.length() > m_matchingPatternLength) {
|
||||
// longer: clear any previous match (like *.bz2, when pattern is *.tar.bz2)
|
||||
replace = true;
|
||||
}
|
||||
}
|
||||
if (replace) {
|
||||
m_matchingMimeTypes.clear();
|
||||
// remember the new "longer" length
|
||||
m_matchingPatternLength = pattern.length();
|
||||
m_weight = weight;
|
||||
}
|
||||
if (!m_matchingMimeTypes.contains(mimeType)) {
|
||||
m_matchingMimeTypes.append(mimeType);
|
||||
if (replace)
|
||||
m_allMatchingMimeTypes.prepend(mimeType); // highest-weight first
|
||||
else
|
||||
m_allMatchingMimeTypes.append(mimeType);
|
||||
m_knownSuffixLength = knownSuffixLength;
|
||||
}
|
||||
}
|
||||
|
||||
QMimeGlobPattern::PatternType QMimeGlobPattern::detectPatternType(const QString &pattern) const
|
||||
{
|
||||
const int patternLength = pattern.length();
|
||||
if (!patternLength)
|
||||
return OtherPattern;
|
||||
|
||||
const int starCount = pattern.count(QLatin1Char('*'));
|
||||
const bool hasSquareBracket = pattern.indexOf(QLatin1Char('[')) != -1;
|
||||
const bool hasQuestionMark = pattern.indexOf(QLatin1Char('?')) != -1;
|
||||
|
||||
if (!hasSquareBracket && !hasQuestionMark) {
|
||||
if (starCount == 1) {
|
||||
// Patterns like "*~", "*.extension"
|
||||
if (pattern.at(0) == QLatin1Char('*'))
|
||||
return SuffixPattern;
|
||||
// Patterns like "README*" (well this is currently the only one like that...)
|
||||
if (pattern.at(patternLength - 1) == QLatin1Char('*'))
|
||||
return PrefixPattern;
|
||||
} else if (starCount == 0) {
|
||||
// Names without any wildcards like "README"
|
||||
return LiteralPattern;
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern == QLatin1String("[0-9][0-9][0-9].vdr"))
|
||||
return VdrPattern;
|
||||
|
||||
if (pattern == QLatin1String("*.anim[1-9j]"))
|
||||
return AnimPattern;
|
||||
|
||||
return OtherPattern;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\class QMimeGlobPattern
|
||||
\inmodule QtCore
|
||||
\brief The QMimeGlobPattern class contains the glob pattern for file names for MIME type matching.
|
||||
|
||||
\sa QMimeType, QMimeDatabase, QMimeMagicRuleMatcher, QMimeMagicRule
|
||||
*/
|
||||
|
||||
bool QMimeGlobPattern::matchFileName(const QString &inputFileName) const
|
||||
{
|
||||
// "Applications MUST match globs case-insensitively, except when the case-sensitive
|
||||
// attribute is set to true."
|
||||
// The constructor takes care of putting case-insensitive patterns in lowercase.
|
||||
const QString fileName = m_caseSensitivity == Qt::CaseInsensitive
|
||||
? inputFileName.toLower() : inputFileName;
|
||||
|
||||
const int patternLength = m_pattern.length();
|
||||
if (!patternLength)
|
||||
return false;
|
||||
const int fileNameLength = fileName.length();
|
||||
|
||||
switch (m_patternType) {
|
||||
case SuffixPattern: {
|
||||
if (fileNameLength + 1 < patternLength)
|
||||
return false;
|
||||
|
||||
const QChar *c1 = m_pattern.unicode() + patternLength - 1;
|
||||
const QChar *c2 = fileName.unicode() + fileNameLength - 1;
|
||||
int cnt = 1;
|
||||
while (cnt < patternLength && *c1-- == *c2--)
|
||||
++cnt;
|
||||
return cnt == patternLength;
|
||||
}
|
||||
case PrefixPattern: {
|
||||
if (fileNameLength + 1 < patternLength)
|
||||
return false;
|
||||
|
||||
const QChar *c1 = m_pattern.unicode();
|
||||
const QChar *c2 = fileName.unicode();
|
||||
int cnt = 1;
|
||||
while (cnt < patternLength && *c1++ == *c2++)
|
||||
++cnt;
|
||||
return cnt == patternLength;
|
||||
}
|
||||
case LiteralPattern:
|
||||
return (m_pattern == fileName);
|
||||
case VdrPattern: // "[0-9][0-9][0-9].vdr" case
|
||||
return fileNameLength == 7
|
||||
&& fileName.at(0).isDigit() && fileName.at(1).isDigit() && fileName.at(2).isDigit()
|
||||
&& QStringView{fileName}.mid(3, 4) == QLatin1String(".vdr");
|
||||
case AnimPattern: { // "*.anim[1-9j]" case
|
||||
if (fileNameLength < 6)
|
||||
return false;
|
||||
const QChar lastChar = fileName.at(fileNameLength - 1);
|
||||
const bool lastCharOK = (lastChar.isDigit() && lastChar != QLatin1Char('0'))
|
||||
|| lastChar == QLatin1Char('j');
|
||||
return lastCharOK && QStringView{fileName}.mid(fileNameLength - 6, 5) == QLatin1String(".anim");
|
||||
}
|
||||
case OtherPattern:
|
||||
// Other fallback patterns: slow but correct method
|
||||
#if QT_CONFIG(regularexpression)
|
||||
auto rx = QRegularExpression::fromWildcard(m_pattern);
|
||||
return rx.match(fileName).hasMatch();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isSimplePattern(const QString &pattern)
|
||||
{
|
||||
// starts with "*.", has no other '*'
|
||||
return pattern.lastIndexOf(QLatin1Char('*')) == 0
|
||||
&& pattern.length() > 1
|
||||
&& pattern.at(1) == QLatin1Char('.') // (other dots are OK, like *.tar.bz2)
|
||||
// and contains no other special character
|
||||
&& !pattern.contains(QLatin1Char('?'))
|
||||
&& !pattern.contains(QLatin1Char('['))
|
||||
;
|
||||
}
|
||||
|
||||
static bool isFastPattern(const QString &pattern)
|
||||
{
|
||||
// starts with "*.", has no other '*' and no other '.'
|
||||
return pattern.lastIndexOf(QLatin1Char('*')) == 0
|
||||
&& pattern.lastIndexOf(QLatin1Char('.')) == 1
|
||||
// and contains no other special character
|
||||
&& !pattern.contains(QLatin1Char('?'))
|
||||
&& !pattern.contains(QLatin1Char('['))
|
||||
;
|
||||
}
|
||||
|
||||
void QMimeAllGlobPatterns::addGlob(const QMimeGlobPattern &glob)
|
||||
{
|
||||
const QString &pattern = glob.pattern();
|
||||
Q_ASSERT(!pattern.isEmpty());
|
||||
|
||||
// Store each patterns into either m_fastPatternDict (*.txt, *.html etc. with default weight 50)
|
||||
// or for the rest, like core.*, *.tar.bz2, *~, into highWeightPatternOffset (>50)
|
||||
// or lowWeightPatternOffset (<=50)
|
||||
|
||||
if (glob.weight() == 50 && isFastPattern(pattern) && !glob.isCaseSensitive()) {
|
||||
// The bulk of the patterns is *.foo with weight 50 --> those go into the fast patterns hash.
|
||||
const QString extension = pattern.mid(2).toLower();
|
||||
QStringList &patterns = m_fastPatterns[extension]; // find or create
|
||||
if (!patterns.contains(glob.mimeType()))
|
||||
patterns.append(glob.mimeType());
|
||||
} else {
|
||||
if (glob.weight() > 50) {
|
||||
if (!m_highWeightGlobs.hasPattern(glob.mimeType(), glob.pattern()))
|
||||
m_highWeightGlobs.append(glob);
|
||||
} else {
|
||||
if (!m_lowWeightGlobs.hasPattern(glob.mimeType(), glob.pattern()))
|
||||
m_lowWeightGlobs.append(glob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeAllGlobPatterns::removeMimeType(const QString &mimeType)
|
||||
{
|
||||
for (auto &x : m_fastPatterns)
|
||||
x.removeAll(mimeType);
|
||||
m_highWeightGlobs.removeMimeType(mimeType);
|
||||
m_lowWeightGlobs.removeMimeType(mimeType);
|
||||
}
|
||||
|
||||
void QMimeGlobPatternList::match(QMimeGlobMatchResult &result,
|
||||
const QString &fileName) const
|
||||
{
|
||||
|
||||
QMimeGlobPatternList::const_iterator it = this->constBegin();
|
||||
const QMimeGlobPatternList::const_iterator endIt = this->constEnd();
|
||||
for (; it != endIt; ++it) {
|
||||
const QMimeGlobPattern &glob = *it;
|
||||
if (glob.matchFileName(fileName)) {
|
||||
const QString pattern = glob.pattern();
|
||||
const int suffixLen = isSimplePattern(pattern) ? pattern.length() - 2 : 0;
|
||||
result.addMatch(glob.mimeType(), glob.weight(), pattern, suffixLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeAllGlobPatterns::matchingGlobs(const QString &fileName, QMimeGlobMatchResult &result) const
|
||||
{
|
||||
// First try the high weight matches (>50), if any.
|
||||
m_highWeightGlobs.match(result, fileName);
|
||||
|
||||
// Now use the "fast patterns" dict, for simple *.foo patterns with weight 50
|
||||
// (which is most of them, so this optimization is definitely worth it)
|
||||
const int lastDot = fileName.lastIndexOf(QLatin1Char('.'));
|
||||
if (lastDot != -1) { // if no '.', skip the extension lookup
|
||||
const int ext_len = fileName.length() - lastDot - 1;
|
||||
const QString simpleExtension = fileName.right(ext_len).toLower();
|
||||
// (toLower because fast patterns are always case-insensitive and saved as lowercase)
|
||||
|
||||
const QStringList matchingMimeTypes = m_fastPatterns.value(simpleExtension);
|
||||
const QString simplePattern = QLatin1String("*.") + simpleExtension;
|
||||
for (const QString &mime : matchingMimeTypes)
|
||||
result.addMatch(mime, 50, simplePattern, simpleExtension.size());
|
||||
// Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway,
|
||||
// at least those with weight 50.
|
||||
}
|
||||
|
||||
// Finally, try the low weight matches (<=50)
|
||||
m_lowWeightGlobs.match(result, fileName);
|
||||
}
|
||||
|
||||
void QMimeAllGlobPatterns::clear()
|
||||
{
|
||||
m_fastPatterns.clear();
|
||||
m_highWeightGlobs.clear();
|
||||
m_lowWeightGlobs.clear();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
176
src/libs/utils/mimetypes2/qmimeglobpattern_p.h
Normal file
176
src/libs/utils/mimetypes2/qmimeglobpattern_p.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMEGLOBPATTERN_P_H
|
||||
#define QMIMEGLOBPATTERN_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/private/qglobal_p.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qhash.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct QMimeGlobMatchResult
|
||||
{
|
||||
void addMatch(const QString &mimeType, int weight, const QString &pattern, int knownSuffixLength = 0);
|
||||
|
||||
QStringList m_matchingMimeTypes; // only those with highest weight
|
||||
QStringList m_allMatchingMimeTypes;
|
||||
int m_weight = 0;
|
||||
int m_matchingPatternLength = 0;
|
||||
int m_knownSuffixLength = 0;
|
||||
};
|
||||
|
||||
class QMimeGlobPattern
|
||||
{
|
||||
public:
|
||||
static const unsigned MaxWeight = 100;
|
||||
static const unsigned DefaultWeight = 50;
|
||||
static const unsigned MinWeight = 1;
|
||||
|
||||
explicit QMimeGlobPattern(const QString &thePattern, const QString &theMimeType, unsigned theWeight = DefaultWeight, Qt::CaseSensitivity s = Qt::CaseInsensitive) :
|
||||
m_pattern(s == Qt::CaseInsensitive ? thePattern.toLower() : thePattern),
|
||||
m_mimeType(theMimeType),
|
||||
m_weight(theWeight),
|
||||
m_caseSensitivity(s),
|
||||
m_patternType(detectPatternType(m_pattern))
|
||||
{
|
||||
}
|
||||
|
||||
void swap(QMimeGlobPattern &other) noexcept
|
||||
{
|
||||
qSwap(m_pattern, other.m_pattern);
|
||||
qSwap(m_mimeType, other.m_mimeType);
|
||||
qSwap(m_weight, other.m_weight);
|
||||
qSwap(m_caseSensitivity, other.m_caseSensitivity);
|
||||
qSwap(m_patternType, other.m_patternType);
|
||||
}
|
||||
|
||||
bool matchFileName(const QString &inputFileName) const;
|
||||
|
||||
inline const QString &pattern() const { return m_pattern; }
|
||||
inline unsigned weight() const { return m_weight; }
|
||||
inline const QString &mimeType() const { return m_mimeType; }
|
||||
inline bool isCaseSensitive() const { return m_caseSensitivity == Qt::CaseSensitive; }
|
||||
|
||||
private:
|
||||
enum PatternType {
|
||||
SuffixPattern,
|
||||
PrefixPattern,
|
||||
LiteralPattern,
|
||||
VdrPattern, // special handling for "[0-9][0-9][0-9].vdr" pattern
|
||||
AnimPattern, // special handling for "*.anim[1-9j]" pattern
|
||||
OtherPattern
|
||||
};
|
||||
PatternType detectPatternType(const QString &pattern) const;
|
||||
|
||||
QString m_pattern;
|
||||
QString m_mimeType;
|
||||
int m_weight;
|
||||
Qt::CaseSensitivity m_caseSensitivity;
|
||||
PatternType m_patternType;
|
||||
};
|
||||
Q_DECLARE_SHARED(QMimeGlobPattern)
|
||||
|
||||
class QMimeGlobPatternList : public QList<QMimeGlobPattern>
|
||||
{
|
||||
public:
|
||||
bool hasPattern(const QString &mimeType, const QString &pattern) const
|
||||
{
|
||||
const_iterator it = begin();
|
||||
const const_iterator myend = end();
|
||||
for (; it != myend; ++it)
|
||||
if ((*it).pattern() == pattern && (*it).mimeType() == mimeType)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
"noglobs" is very rare occurrence, so it's ok if it's slow
|
||||
*/
|
||||
void removeMimeType(const QString &mimeType)
|
||||
{
|
||||
auto isMimeTypeEqual = [&mimeType](const QMimeGlobPattern &pattern) {
|
||||
return pattern.mimeType() == mimeType;
|
||||
};
|
||||
removeIf(isMimeTypeEqual);
|
||||
}
|
||||
|
||||
void match(QMimeGlobMatchResult &result, const QString &fileName) const;
|
||||
};
|
||||
|
||||
/*!
|
||||
Result of the globs parsing, as data structures ready for efficient MIME type matching.
|
||||
This contains:
|
||||
1) a map of fast regular patterns (e.g. *.txt is stored as "txt" in a qhash's key)
|
||||
2) a linear list of high-weight globs
|
||||
3) a linear list of low-weight globs
|
||||
*/
|
||||
class QMimeAllGlobPatterns
|
||||
{
|
||||
public:
|
||||
typedef QHash<QString, QStringList> PatternsMap; // MIME type -> patterns
|
||||
|
||||
void addGlob(const QMimeGlobPattern &glob);
|
||||
void removeMimeType(const QString &mimeType);
|
||||
void matchingGlobs(const QString &fileName, QMimeGlobMatchResult &result) const;
|
||||
void clear();
|
||||
|
||||
PatternsMap m_fastPatterns; // example: "doc" -> "application/msword", "text/plain"
|
||||
QMimeGlobPatternList m_highWeightGlobs;
|
||||
QMimeGlobPatternList m_lowWeightGlobs; // <= 50, including the non-fast 50 patterns
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEGLOBPATTERN_P_H
|
364
src/libs/utils/mimetypes2/qmimemagicrule.cpp
Normal file
364
src/libs/utils/mimetypes2/qmimemagicrule.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#define QT_NO_CAST_FROM_ASCII
|
||||
|
||||
#include "qmimemagicrule_p.h"
|
||||
|
||||
#include "qmimetypeparser_p.h"
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QDebug>
|
||||
#include <qendian.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// in the same order as Type!
|
||||
static const char magicRuleTypes_string[] =
|
||||
"invalid\0"
|
||||
"string\0"
|
||||
"host16\0"
|
||||
"host32\0"
|
||||
"big16\0"
|
||||
"big32\0"
|
||||
"little16\0"
|
||||
"little32\0"
|
||||
"byte\0"
|
||||
"\0";
|
||||
|
||||
static const int magicRuleTypes_indices[] = {
|
||||
0, 8, 15, 22, 29, 35, 41, 50, 59, 64, 0
|
||||
};
|
||||
|
||||
QMimeMagicRule::Type QMimeMagicRule::type(const QByteArray &theTypeName)
|
||||
{
|
||||
for (int i = String; i <= Byte; ++i) {
|
||||
if (theTypeName == magicRuleTypes_string + magicRuleTypes_indices[i])
|
||||
return Type(i);
|
||||
}
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
QByteArray QMimeMagicRule::typeName(QMimeMagicRule::Type theType)
|
||||
{
|
||||
return magicRuleTypes_string + magicRuleTypes_indices[theType];
|
||||
}
|
||||
|
||||
bool QMimeMagicRule::operator==(const QMimeMagicRule &other) const
|
||||
{
|
||||
return m_type == other.m_type &&
|
||||
m_value == other.m_value &&
|
||||
m_startPos == other.m_startPos &&
|
||||
m_endPos == other.m_endPos &&
|
||||
m_mask == other.m_mask &&
|
||||
m_pattern == other.m_pattern &&
|
||||
m_number == other.m_number &&
|
||||
m_numberMask == other.m_numberMask &&
|
||||
m_matchFunction == other.m_matchFunction;
|
||||
}
|
||||
|
||||
// Used by both providers
|
||||
bool QMimeMagicRule::matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength,
|
||||
int valueLength, const char *valueData, const char *mask)
|
||||
{
|
||||
// Size of searched data.
|
||||
// Example: value="ABC", rangeLength=3 -> we need 3+3-1=5 bytes (ABCxx,xABCx,xxABC would match)
|
||||
const int dataNeeded = qMin(rangeLength + valueLength - 1, dataSize - rangeStart);
|
||||
|
||||
if (!mask) {
|
||||
// callgrind says QByteArray::indexOf is much slower, since our strings are typically too
|
||||
// short for be worth Boyer-Moore matching (1 to 71 bytes, 11 bytes on average).
|
||||
bool found = false;
|
||||
for (int i = rangeStart; i < rangeStart + rangeLength; ++i) {
|
||||
if (i + valueLength > dataSize)
|
||||
break;
|
||||
|
||||
if (memcmp(valueData, dataPtr + i, valueLength) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return false;
|
||||
} else {
|
||||
bool found = false;
|
||||
const char *readDataBase = dataPtr + rangeStart;
|
||||
// Example (continued from above):
|
||||
// deviceSize is 4, so dataNeeded was max'ed to 4.
|
||||
// maxStartPos = 4 - 3 + 1 = 2, and indeed
|
||||
// we need to check for a match a positions 0 and 1 (ABCx and xABC).
|
||||
const int maxStartPos = dataNeeded - valueLength + 1;
|
||||
for (int i = 0; i < maxStartPos; ++i) {
|
||||
const char *d = readDataBase + i;
|
||||
bool valid = true;
|
||||
for (int idx = 0; idx < valueLength; ++idx) {
|
||||
if (((*d++) & mask[idx]) != (valueData[idx] & mask[idx])) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
return false;
|
||||
}
|
||||
//qDebug() << "Found" << value << "in" << searchedData;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QMimeMagicRule::matchString(const QByteArray &data) const
|
||||
{
|
||||
const int rangeLength = m_endPos - m_startPos + 1;
|
||||
return QMimeMagicRule::matchSubstring(data.constData(), data.size(), m_startPos, rangeLength, m_pattern.size(), m_pattern.constData(), m_mask.constData());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool QMimeMagicRule::matchNumber(const QByteArray &data) const
|
||||
{
|
||||
const T value(m_number);
|
||||
const T mask(m_numberMask);
|
||||
|
||||
//qDebug() << "matchNumber" << "0x" << QString::number(m_number, 16) << "size" << sizeof(T);
|
||||
//qDebug() << "mask" << QString::number(m_numberMask, 16);
|
||||
|
||||
const char *p = data.constData() + m_startPos;
|
||||
const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), m_endPos);
|
||||
for ( ; p <= e; ++p) {
|
||||
if ((qFromUnaligned<T>(p) & mask) == (value & mask))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline QByteArray makePattern(const QByteArray &value)
|
||||
{
|
||||
QByteArray pattern(value.size(), Qt::Uninitialized);
|
||||
char *data = pattern.data();
|
||||
|
||||
const char *p = value.constData();
|
||||
const char *e = p + value.size();
|
||||
for ( ; p < e; ++p) {
|
||||
if (*p == '\\' && ++p < e) {
|
||||
if (*p == 'x') { // hex (\\xff)
|
||||
char c = 0;
|
||||
for (int i = 0; i < 2 && p + 1 < e; ++i) {
|
||||
++p;
|
||||
if (*p >= '0' && *p <= '9')
|
||||
c = (c << 4) + *p - '0';
|
||||
else if (*p >= 'a' && *p <= 'f')
|
||||
c = (c << 4) + *p - 'a' + 10;
|
||||
else if (*p >= 'A' && *p <= 'F')
|
||||
c = (c << 4) + *p - 'A' + 10;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
*data++ = c;
|
||||
} else if (*p >= '0' && *p <= '7') { // oct (\\7, or \\77, or \\377)
|
||||
char c = *p - '0';
|
||||
if (p + 1 < e && p[1] >= '0' && p[1] <= '7') {
|
||||
c = (c << 3) + *(++p) - '0';
|
||||
if (p + 1 < e && p[1] >= '0' && p[1] <= '7' && p[-1] <= '3')
|
||||
c = (c << 3) + *(++p) - '0';
|
||||
}
|
||||
*data++ = c;
|
||||
} else if (*p == 'n') {
|
||||
*data++ = '\n';
|
||||
} else if (*p == 'r') {
|
||||
*data++ = '\r';
|
||||
} else if (*p == 't') {
|
||||
*data++ = '\t';
|
||||
} else { // escaped
|
||||
*data++ = *p;
|
||||
}
|
||||
} else {
|
||||
*data++ = *p;
|
||||
}
|
||||
}
|
||||
pattern.truncate(data - pattern.data());
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
// Evaluate a magic match rule like
|
||||
// <match value="must be converted with BinHex" type="string" offset="11"/>
|
||||
// <match value="0x9501" type="big16" offset="0:64"/>
|
||||
|
||||
QMimeMagicRule::QMimeMagicRule(const QString &type,
|
||||
const QByteArray &value,
|
||||
const QString &offsets,
|
||||
const QByteArray &mask,
|
||||
QString *errorString)
|
||||
: m_type(QMimeMagicRule::type(type.toLatin1())),
|
||||
m_value(value),
|
||||
m_mask(mask),
|
||||
m_matchFunction(nullptr)
|
||||
{
|
||||
if (Q_UNLIKELY(m_type == Invalid))
|
||||
*errorString = QLatin1String("Type ") + type + QLatin1String(" is not supported");
|
||||
|
||||
// Parse for offset as "1" or "1:10"
|
||||
const int colonIndex = offsets.indexOf(QLatin1Char(':'));
|
||||
const QStringView startPosStr = QStringView{offsets}.mid(0, colonIndex); // \ These decay to returning 'offsets'
|
||||
const QStringView endPosStr = QStringView{offsets}.mid(colonIndex + 1);// / unchanged when colonIndex == -1
|
||||
if (Q_UNLIKELY(!QMimeTypeParserBase::parseNumber(startPosStr, &m_startPos, errorString)) ||
|
||||
Q_UNLIKELY(!QMimeTypeParserBase::parseNumber(endPosStr, &m_endPos, errorString))) {
|
||||
m_type = Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(m_value.isEmpty())) {
|
||||
m_type = Invalid;
|
||||
if (errorString)
|
||||
*errorString = QStringLiteral("Invalid empty magic rule value");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_type >= Host16 && m_type <= Byte) {
|
||||
bool ok;
|
||||
m_number = m_value.toUInt(&ok, 0); // autodetect base
|
||||
if (Q_UNLIKELY(!ok)) {
|
||||
m_type = Invalid;
|
||||
if (errorString)
|
||||
*errorString = QLatin1String("Invalid magic rule value \"") + QLatin1String(m_value) + QLatin1Char('"');
|
||||
return;
|
||||
}
|
||||
m_numberMask = !m_mask.isEmpty() ? m_mask.toUInt(&ok, 0) : 0; // autodetect base
|
||||
}
|
||||
|
||||
switch (m_type) {
|
||||
case String:
|
||||
m_pattern = makePattern(m_value);
|
||||
m_pattern.squeeze();
|
||||
if (!m_mask.isEmpty()) {
|
||||
if (Q_UNLIKELY(m_mask.size() < 4 || !m_mask.startsWith("0x"))) {
|
||||
m_type = Invalid;
|
||||
if (errorString)
|
||||
*errorString = QLatin1String("Invalid magic rule mask \"") + QLatin1String(m_mask) + QLatin1Char('"');
|
||||
return;
|
||||
}
|
||||
const QByteArray &tempMask = QByteArray::fromHex(QByteArray::fromRawData(
|
||||
m_mask.constData() + 2, m_mask.size() - 2));
|
||||
if (Q_UNLIKELY(tempMask.size() != m_pattern.size())) {
|
||||
m_type = Invalid;
|
||||
if (errorString)
|
||||
*errorString = QLatin1String("Invalid magic rule mask size \"") + QLatin1String(m_mask) + QLatin1Char('"');
|
||||
return;
|
||||
}
|
||||
m_mask = tempMask;
|
||||
} else {
|
||||
m_mask.fill(char(-1), m_pattern.size());
|
||||
}
|
||||
m_mask.squeeze();
|
||||
m_matchFunction = &QMimeMagicRule::matchString;
|
||||
break;
|
||||
case Byte:
|
||||
if (m_number <= quint8(-1)) {
|
||||
if (m_numberMask == 0)
|
||||
m_numberMask = quint8(-1);
|
||||
m_matchFunction = &QMimeMagicRule::matchNumber<quint8>;
|
||||
}
|
||||
break;
|
||||
case Big16:
|
||||
case Little16:
|
||||
if (m_number <= quint16(-1)) {
|
||||
m_number = m_type == Little16 ? qFromLittleEndian<quint16>(m_number) : qFromBigEndian<quint16>(m_number);
|
||||
if (m_numberMask != 0)
|
||||
m_numberMask = m_type == Little16 ? qFromLittleEndian<quint16>(m_numberMask) : qFromBigEndian<quint16>(m_numberMask);
|
||||
}
|
||||
Q_FALLTHROUGH();
|
||||
case Host16:
|
||||
if (m_number <= quint16(-1)) {
|
||||
if (m_numberMask == 0)
|
||||
m_numberMask = quint16(-1);
|
||||
m_matchFunction = &QMimeMagicRule::matchNumber<quint16>;
|
||||
}
|
||||
break;
|
||||
case Big32:
|
||||
case Little32:
|
||||
m_number = m_type == Little32 ? qFromLittleEndian<quint32>(m_number) : qFromBigEndian<quint32>(m_number);
|
||||
if (m_numberMask != 0)
|
||||
m_numberMask = m_type == Little32 ? qFromLittleEndian<quint32>(m_numberMask) : qFromBigEndian<quint32>(m_numberMask);
|
||||
Q_FALLTHROUGH();
|
||||
case Host32:
|
||||
if (m_numberMask == 0)
|
||||
m_numberMask = quint32(-1);
|
||||
m_matchFunction = &QMimeMagicRule::matchNumber<quint32>;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray QMimeMagicRule::mask() const
|
||||
{
|
||||
QByteArray result = m_mask;
|
||||
if (m_type == String) {
|
||||
// restore '0x'
|
||||
result = "0x" + result.toHex();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QMimeMagicRule::matches(const QByteArray &data) const
|
||||
{
|
||||
const bool ok = m_matchFunction && (this->*m_matchFunction)(data);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
// No submatch? Then we are done.
|
||||
if (m_subMatches.isEmpty())
|
||||
return true;
|
||||
|
||||
//qDebug() << "Checking" << m_subMatches.count() << "sub-rules";
|
||||
// Check that one of the submatches matches too
|
||||
for ( QList<QMimeMagicRule>::const_iterator it = m_subMatches.begin(), end = m_subMatches.end() ;
|
||||
it != end ; ++it ) {
|
||||
if ((*it).matches(data)) {
|
||||
// One of the hierarchies matched -> mimetype recognized.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
128
src/libs/utils/mimetypes2/qmimemagicrule_p.h
Normal file
128
src/libs/utils/mimetypes2/qmimemagicrule_p.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMEMAGICRULE_P_H
|
||||
#define QMIMEMAGICRULE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/private/qglobal_p.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeMagicRule
|
||||
{
|
||||
public:
|
||||
enum Type { Invalid = 0, String, Host16, Host32, Big16, Big32, Little16, Little32, Byte };
|
||||
|
||||
QMimeMagicRule(const QString &typeStr, const QByteArray &value, const QString &offsets,
|
||||
const QByteArray &mask, QString *errorString);
|
||||
|
||||
void swap(QMimeMagicRule &other) noexcept
|
||||
{
|
||||
qSwap(m_type, other.m_type);
|
||||
qSwap(m_value, other.m_value);
|
||||
qSwap(m_startPos, other.m_startPos);
|
||||
qSwap(m_endPos, other.m_endPos);
|
||||
qSwap(m_mask, other.m_mask);
|
||||
qSwap(m_pattern, other.m_pattern);
|
||||
qSwap(m_number, other.m_number);
|
||||
qSwap(m_numberMask, other.m_numberMask);
|
||||
qSwap(m_matchFunction, other.m_matchFunction);
|
||||
}
|
||||
|
||||
bool operator==(const QMimeMagicRule &other) const;
|
||||
|
||||
Type type() const { return m_type; }
|
||||
QByteArray value() const { return m_value; }
|
||||
int startPos() const { return m_startPos; }
|
||||
int endPos() const { return m_endPos; }
|
||||
QByteArray mask() const;
|
||||
|
||||
bool isValid() const { return m_matchFunction != nullptr; }
|
||||
|
||||
bool matches(const QByteArray &data) const;
|
||||
|
||||
QList<QMimeMagicRule> m_subMatches;
|
||||
|
||||
static Type type(const QByteArray &type);
|
||||
static QByteArray typeName(Type type);
|
||||
|
||||
static bool matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, int valueLength, const char *valueData, const char *mask);
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
QByteArray m_value;
|
||||
int m_startPos;
|
||||
int m_endPos;
|
||||
QByteArray m_mask;
|
||||
|
||||
QByteArray m_pattern;
|
||||
quint32 m_number;
|
||||
quint32 m_numberMask;
|
||||
|
||||
typedef bool (QMimeMagicRule::*MatchFunction)(const QByteArray &data) const;
|
||||
MatchFunction m_matchFunction;
|
||||
|
||||
private:
|
||||
// match functions
|
||||
bool matchString(const QByteArray &data) const;
|
||||
template <typename T>
|
||||
bool matchNumber(const QByteArray &data) const;
|
||||
};
|
||||
Q_DECLARE_SHARED(QMimeMagicRule)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEMAGICRULE_H
|
106
src/libs/utils/mimetypes2/qmimemagicrulematcher.cpp
Normal file
106
src/libs/utils/mimetypes2/qmimemagicrulematcher.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#define QT_NO_CAST_FROM_ASCII
|
||||
|
||||
#include "qmimemagicrulematcher_p.h"
|
||||
|
||||
#include "qmimetype_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\class QMimeMagicRuleMatcher
|
||||
\inmodule QtCore
|
||||
|
||||
\brief The QMimeMagicRuleMatcher class checks a number of rules based on operator "or".
|
||||
|
||||
It is used for rules parsed from XML files.
|
||||
|
||||
\sa QMimeType, QMimeDatabase, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
||||
\sa QMimeTypeParserBase, MimeTypeParser
|
||||
*/
|
||||
|
||||
QMimeMagicRuleMatcher::QMimeMagicRuleMatcher(const QString &mime, unsigned thePriority) :
|
||||
m_list(),
|
||||
m_priority(thePriority),
|
||||
m_mimetype(mime)
|
||||
{
|
||||
}
|
||||
|
||||
bool QMimeMagicRuleMatcher::operator==(const QMimeMagicRuleMatcher &other) const
|
||||
{
|
||||
return m_list == other.m_list &&
|
||||
m_priority == other.m_priority;
|
||||
}
|
||||
|
||||
void QMimeMagicRuleMatcher::addRule(const QMimeMagicRule &rule)
|
||||
{
|
||||
m_list.append(rule);
|
||||
}
|
||||
|
||||
void QMimeMagicRuleMatcher::addRules(const QList<QMimeMagicRule> &rules)
|
||||
{
|
||||
m_list.append(rules);
|
||||
}
|
||||
|
||||
QList<QMimeMagicRule> QMimeMagicRuleMatcher::magicRules() const
|
||||
{
|
||||
return m_list;
|
||||
}
|
||||
|
||||
// Check for a match on contents of a file
|
||||
bool QMimeMagicRuleMatcher::matches(const QByteArray &data) const
|
||||
{
|
||||
for (const QMimeMagicRule &magicRule : m_list) {
|
||||
if (magicRule.matches(data))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return a priority value from 1..100
|
||||
unsigned QMimeMagicRuleMatcher::priority() const
|
||||
{
|
||||
return m_priority;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
97
src/libs/utils/mimetypes2/qmimemagicrulematcher_p.h
Normal file
97
src/libs/utils/mimetypes2/qmimemagicrulematcher_p.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMEMAGICRULEMATCHER_P_H
|
||||
#define QMIMEMAGICRULEMATCHER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qmimemagicrule_p.h"
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qstring.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeMagicRuleMatcher
|
||||
{
|
||||
public:
|
||||
explicit QMimeMagicRuleMatcher(const QString &mime, unsigned priority = 65535);
|
||||
|
||||
void swap(QMimeMagicRuleMatcher &other) noexcept
|
||||
{
|
||||
qSwap(m_list, other.m_list);
|
||||
qSwap(m_priority, other.m_priority);
|
||||
qSwap(m_mimetype, other.m_mimetype);
|
||||
}
|
||||
|
||||
bool operator==(const QMimeMagicRuleMatcher &other) const;
|
||||
|
||||
void addRule(const QMimeMagicRule &rule);
|
||||
void addRules(const QList<QMimeMagicRule> &rules);
|
||||
QList<QMimeMagicRule> magicRules() const;
|
||||
|
||||
bool matches(const QByteArray &data) const;
|
||||
|
||||
unsigned priority() const;
|
||||
|
||||
QString mimetype() const { return m_mimetype; }
|
||||
|
||||
private:
|
||||
QList<QMimeMagicRule> m_list;
|
||||
unsigned m_priority;
|
||||
QString m_mimetype;
|
||||
};
|
||||
Q_DECLARE_SHARED(QMimeMagicRuleMatcher)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEMAGICRULEMATCHER_P_H
|
855
src/libs/utils/mimetypes2/qmimeprovider.cpp
Normal file
855
src/libs/utils/mimetypes2/qmimeprovider.cpp
Normal file
@@ -0,0 +1,855 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Copyright (C) 2018 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com>
|
||||
** Copyright (C) 2019 Intel Corporation.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmimeprovider_p.h"
|
||||
|
||||
#include "qmimetypeparser_p.h"
|
||||
#include <qstandardpaths.h>
|
||||
#include "qmimemagicrulematcher_p.h"
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
#include <QBuffer>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QByteArrayMatcher>
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QtEndian>
|
||||
|
||||
#if QT_CONFIG(mimetype_database)
|
||||
# if defined(Q_CC_MSVC)
|
||||
# pragma section(".qtmimedatabase", read, shared)
|
||||
__declspec(allocate(".qtmimedatabase")) __declspec(align(4096))
|
||||
# elif defined(Q_OS_DARWIN)
|
||||
__attribute__((section("__TEXT,.qtmimedatabase"), aligned(4096)))
|
||||
# elif (defined(Q_OF_ELF) || defined(Q_OS_WIN)) && defined(Q_CC_GNU)
|
||||
__attribute__((section(".qtmimedatabase"), aligned(4096)))
|
||||
# endif
|
||||
|
||||
# include "qmimeprovider_database.cpp"
|
||||
|
||||
# ifdef MIME_DATABASE_IS_ZSTD
|
||||
# if !QT_CONFIG(zstd)
|
||||
# error "MIME database is zstd but no support compiled in!"
|
||||
# endif
|
||||
# include <zstd.h>
|
||||
# endif
|
||||
# ifdef MIME_DATABASE_IS_GZIP
|
||||
# ifdef QT_NO_COMPRESS
|
||||
# error "MIME database is zlib but no support compiled in!"
|
||||
# endif
|
||||
# define ZLIB_CONST
|
||||
# include <zconf.h>
|
||||
# include <zlib.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory)
|
||||
: m_db(db), m_directory(directory)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QMimeBinaryProvider::QMimeBinaryProvider(QMimeDatabasePrivate *db, const QString &directory)
|
||||
: QMimeProviderBase(db, directory), m_mimetypeListLoaded(false)
|
||||
{
|
||||
ensureLoaded();
|
||||
}
|
||||
|
||||
struct QMimeBinaryProvider::CacheFile
|
||||
{
|
||||
CacheFile(const QString &fileName);
|
||||
~CacheFile();
|
||||
|
||||
bool isValid() const { return m_valid; }
|
||||
inline quint16 getUint16(int offset) const
|
||||
{
|
||||
return qFromBigEndian(*reinterpret_cast<quint16 *>(data + offset));
|
||||
}
|
||||
inline quint32 getUint32(int offset) const
|
||||
{
|
||||
return qFromBigEndian(*reinterpret_cast<quint32 *>(data + offset));
|
||||
}
|
||||
inline const char *getCharStar(int offset) const
|
||||
{
|
||||
return reinterpret_cast<const char *>(data + offset);
|
||||
}
|
||||
bool load();
|
||||
bool reload();
|
||||
|
||||
QFile file;
|
||||
uchar *data;
|
||||
QDateTime m_mtime;
|
||||
bool m_valid;
|
||||
};
|
||||
|
||||
QMimeBinaryProvider::CacheFile::CacheFile(const QString &fileName)
|
||||
: file(fileName), m_valid(false)
|
||||
{
|
||||
load();
|
||||
}
|
||||
|
||||
QMimeBinaryProvider::CacheFile::~CacheFile()
|
||||
{
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::CacheFile::load()
|
||||
{
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
data = file.map(0, file.size());
|
||||
if (data) {
|
||||
const int major = getUint16(0);
|
||||
const int minor = getUint16(2);
|
||||
m_valid = (major == 1 && minor >= 1 && minor <= 2);
|
||||
}
|
||||
m_mtime = QFileInfo(file).lastModified();
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::CacheFile::reload()
|
||||
{
|
||||
m_valid = false;
|
||||
if (file.isOpen()) {
|
||||
file.close();
|
||||
}
|
||||
data = nullptr;
|
||||
return load();
|
||||
}
|
||||
|
||||
QMimeBinaryProvider::~QMimeBinaryProvider()
|
||||
{
|
||||
delete m_cacheFile;
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::isValid()
|
||||
{
|
||||
return m_cacheFile != nullptr;
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::isInternalDatabase() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Position of the "list offsets" values, at the beginning of the mime.cache file
|
||||
enum {
|
||||
PosAliasListOffset = 4,
|
||||
PosParentListOffset = 8,
|
||||
PosLiteralListOffset = 12,
|
||||
PosReverseSuffixTreeOffset = 16,
|
||||
PosGlobListOffset = 20,
|
||||
PosMagicListOffset = 24,
|
||||
// PosNamespaceListOffset = 28,
|
||||
PosIconsListOffset = 32,
|
||||
PosGenericIconsListOffset = 36
|
||||
};
|
||||
|
||||
bool QMimeBinaryProvider::checkCacheChanged()
|
||||
{
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::ensureLoaded()
|
||||
{
|
||||
if (!m_cacheFile) {
|
||||
const QString cacheFileName = m_directory + QLatin1String("/mime.cache");
|
||||
m_cacheFile = new CacheFile(cacheFileName);
|
||||
m_mimetypeListLoaded = false;
|
||||
m_mimetypeExtra.clear();
|
||||
} else {
|
||||
if (checkCacheChanged()) {
|
||||
m_mimetypeListLoaded = false;
|
||||
m_mimetypeExtra.clear();
|
||||
} else {
|
||||
return; // nothing to do
|
||||
}
|
||||
}
|
||||
if (!m_cacheFile->isValid()) { // verify existence and version
|
||||
delete m_cacheFile;
|
||||
m_cacheFile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// genericIconName: in loadGenericIcon
|
||||
return QMimeType(data);
|
||||
}
|
||||
|
||||
QMimeType QMimeBinaryProvider::mimeTypeForName(const QString &name)
|
||||
{
|
||||
if (!m_mimetypeListLoaded)
|
||||
loadMimeTypeList();
|
||||
if (!m_mimetypeNames.contains(name))
|
||||
return QMimeType(); // unknown mimetype
|
||||
return mimeTypeForNameUnchecked(name);
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result)
|
||||
{
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
Q_ASSERT(m_cacheFile);
|
||||
const QString lowerFileName = fileName.toLower();
|
||||
// Check literals (e.g. "Makefile")
|
||||
matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosLiteralListOffset), fileName);
|
||||
// Check the very common *.txt cases with the suffix tree
|
||||
if (result.m_matchingMimeTypes.isEmpty()) {
|
||||
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);
|
||||
}
|
||||
// Check complex globs (e.g. "callgrind.out[0-9]*" or "README*")
|
||||
if (result.m_matchingMimeTypes.isEmpty())
|
||||
matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName);
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName)
|
||||
{
|
||||
const int numGlobs = cacheFile->getUint32(off);
|
||||
//qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset;
|
||||
for (int i = 0; i < numGlobs; ++i) {
|
||||
const int globOffset = cacheFile->getUint32(off + 4 + 12 * i);
|
||||
const int mimeTypeOffset = cacheFile->getUint32(off + 4 + 12 * i + 4);
|
||||
const int flagsAndWeight = cacheFile->getUint32(off + 4 + 12 * i + 8);
|
||||
const int weight = flagsAndWeight & 0xff;
|
||||
const bool caseSensitive = flagsAndWeight & 0x100;
|
||||
const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||
const QString pattern = QLatin1String(cacheFile->getCharStar(globOffset));
|
||||
|
||||
const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
|
||||
//qDebug() << pattern << mimeType << weight << caseSensitive;
|
||||
QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive);
|
||||
|
||||
if (glob.matchFileName(fileName))
|
||||
result.addMatch(QLatin1String(mimeType), weight, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result, QMimeBinaryProvider::CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck)
|
||||
{
|
||||
QChar fileChar = fileName[charPos];
|
||||
int min = 0;
|
||||
int max = numEntries - 1;
|
||||
while (min <= max) {
|
||||
const int mid = (min + max) / 2;
|
||||
const int off = firstOffset + 12 * mid;
|
||||
const QChar ch = char16_t(cacheFile->getUint32(off));
|
||||
if (ch < fileChar)
|
||||
min = mid + 1;
|
||||
else if (ch > fileChar)
|
||||
max = mid - 1;
|
||||
else {
|
||||
--charPos;
|
||||
int numChildren = cacheFile->getUint32(off + 4);
|
||||
int childrenOffset = cacheFile->getUint32(off + 8);
|
||||
bool success = false;
|
||||
if (charPos > 0)
|
||||
success = matchSuffixTree(result, cacheFile, numChildren, childrenOffset, fileName, charPos, caseSensitiveCheck);
|
||||
if (!success) {
|
||||
for (int i = 0; i < numChildren; ++i) {
|
||||
const int childOff = childrenOffset + 12 * i;
|
||||
const int mch = cacheFile->getUint32(childOff);
|
||||
if (mch != 0)
|
||||
break;
|
||||
const int mimeTypeOffset = cacheFile->getUint32(childOff + 4);
|
||||
const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
|
||||
const int flagsAndWeight = cacheFile->getUint32(childOff + 8);
|
||||
const int weight = flagsAndWeight & 0xff;
|
||||
const bool caseSensitive = flagsAndWeight & 0x100;
|
||||
if (caseSensitiveCheck || !caseSensitive) {
|
||||
result.addMatch(QLatin1String(mimeType), weight,
|
||||
QLatin1Char('*') + QStringView{fileName}.mid(charPos + 1), fileName.size() - charPos - 2);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::matchMagicRule(QMimeBinaryProvider::CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data)
|
||||
{
|
||||
const char *dataPtr = data.constData();
|
||||
const int dataSize = data.size();
|
||||
for (int matchlet = 0; matchlet < numMatchlets; ++matchlet) {
|
||||
const int off = firstOffset + matchlet * 32;
|
||||
const int rangeStart = cacheFile->getUint32(off);
|
||||
const int rangeLength = cacheFile->getUint32(off + 4);
|
||||
//const int wordSize = cacheFile->getUint32(off + 8);
|
||||
const int valueLength = cacheFile->getUint32(off + 12);
|
||||
const int valueOffset = cacheFile->getUint32(off + 16);
|
||||
const int maskOffset = cacheFile->getUint32(off + 20);
|
||||
const char *mask = maskOffset ? cacheFile->getCharStar(maskOffset) : nullptr;
|
||||
|
||||
if (!QMimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile->getCharStar(valueOffset), mask))
|
||||
continue;
|
||||
|
||||
const int numChildren = cacheFile->getUint32(off + 24);
|
||||
const int firstChildOffset = cacheFile->getUint32(off + 28);
|
||||
if (numChildren == 0) // No submatch? Then we are done.
|
||||
return true;
|
||||
// Check that one of the submatches matches too
|
||||
if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::addParents(const QString &mime, QStringList &result)
|
||||
{
|
||||
const QByteArray mimeStr = mime.toLatin1();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString QMimeBinaryProvider::resolveAlias(const QString &name)
|
||||
{
|
||||
const QByteArray input = name.toLatin1();
|
||||
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 QString();
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::addAliases(const QString &name, QStringList &result)
|
||||
{
|
||||
const QByteArray input = name.toLatin1();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::loadMimeTypeList()
|
||||
{
|
||||
if (!m_mimetypeListLoaded) {
|
||||
m_mimetypeListLoaded = true;
|
||||
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".
|
||||
QFile file(m_directory + QStringLiteral("/types"));
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
while (!file.atEnd()) {
|
||||
QByteArray line = file.readLine();
|
||||
if (line.endsWith('\n'))
|
||||
line.chop(1);
|
||||
m_mimetypeNames.insert(QString::fromLatin1(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::addAllMimeTypes(QList<QMimeType> &result)
|
||||
{
|
||||
loadMimeTypeList();
|
||||
if (result.isEmpty()) {
|
||||
result.reserve(m_mimetypeNames.count());
|
||||
for (const QString &name : qAsConst(m_mimetypeNames))
|
||||
result.append(mimeTypeForNameUnchecked(name));
|
||||
} else {
|
||||
for (const QString &name : qAsConst(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));
|
||||
}
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate &data)
|
||||
{
|
||||
#ifdef QT_NO_XMLSTREAMREADER
|
||||
Q_UNUSED(data);
|
||||
qWarning("Cannot load mime type since QXmlStreamReader is not available.");
|
||||
return false;
|
||||
#else
|
||||
if (data.loaded)
|
||||
return true;
|
||||
|
||||
auto it = m_mimetypeExtra.constFind(data.name);
|
||||
if (it == m_mimetypeExtra.constEnd()) {
|
||||
// load comment and globPatterns
|
||||
|
||||
// shared-mime-info since 1.3 lowercases the xml files
|
||||
QString mimeFile = m_directory + QLatin1Char('/') + data.name.toLower() + QLatin1String(".xml");
|
||||
if (!QFile::exists(mimeFile))
|
||||
mimeFile = m_directory + QLatin1Char('/') + data.name + QLatin1String(".xml"); // pre-1.3
|
||||
|
||||
QFile qfile(mimeFile);
|
||||
if (!qfile.open(QFile::ReadOnly))
|
||||
return false;
|
||||
|
||||
auto insertIt = m_mimetypeExtra.insert(data.name, MimeTypeExtra{});
|
||||
it = insertIt;
|
||||
MimeTypeExtra &extra = insertIt.value();
|
||||
QString mainPattern;
|
||||
|
||||
QXmlStreamReader xml(&qfile);
|
||||
if (xml.readNextStartElement()) {
|
||||
if (xml.name() != QLatin1String("mime-type")) {
|
||||
return false;
|
||||
}
|
||||
const auto name = xml.attributes().value(QLatin1String("type"));
|
||||
if (name.isEmpty())
|
||||
return false;
|
||||
if (name.compare(data.name, Qt::CaseInsensitive))
|
||||
qWarning() << "Got name" << name << "in file" << mimeFile << "expected" << data.name;
|
||||
|
||||
while (xml.readNextStartElement()) {
|
||||
const auto tag = xml.name();
|
||||
if (tag == QLatin1String("comment")) {
|
||||
QString lang = xml.attributes().value(QLatin1String("xml:lang")).toString();
|
||||
const QString text = xml.readElementText();
|
||||
if (lang.isEmpty()) {
|
||||
lang = QLatin1String("default"); // no locale attribute provided, treat it as default.
|
||||
}
|
||||
extra.localeComments.insert(lang, text);
|
||||
continue; // we called readElementText, so we're at the EndElement already.
|
||||
} else if (tag == QLatin1String("glob-deleteall")) { // as written out by shared-mime-info >= 0.70
|
||||
extra.globPatterns.clear();
|
||||
mainPattern.clear();
|
||||
} else if (tag == QLatin1String("glob")) { // as written out by shared-mime-info >= 0.70
|
||||
const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString();
|
||||
if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) {
|
||||
mainPattern = pattern;
|
||||
}
|
||||
if (!extra.globPatterns.contains(pattern))
|
||||
extra.globPatterns.append(pattern);
|
||||
}
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
Q_ASSERT(xml.name() == QLatin1String("mime-type"));
|
||||
}
|
||||
|
||||
// Let's assume that shared-mime-info is at least version 0.70
|
||||
// Otherwise we would need 1) a version check, and 2) code for parsing patterns from the globs file.
|
||||
if (!mainPattern.isEmpty() &&
|
||||
(extra.globPatterns.isEmpty() || extra.globPatterns.constFirst() != mainPattern)) {
|
||||
// ensure it's first in the list of patterns
|
||||
extra.globPatterns.removeAll(mainPattern);
|
||||
extra.globPatterns.prepend(mainPattern);
|
||||
}
|
||||
}
|
||||
const MimeTypeExtra &e = it.value();
|
||||
data.localeComments = e.localeComments;
|
||||
data.globPatterns = e.globPatterns;
|
||||
return true;
|
||||
#endif //QT_NO_XMLSTREAMREADER
|
||||
}
|
||||
|
||||
// Binary search in the icons or generic-icons list
|
||||
QLatin1String QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime)
|
||||
{
|
||||
const int iconsListOffset = cacheFile->getUint32(posListOffset);
|
||||
const int numIcons = cacheFile->getUint32(iconsListOffset);
|
||||
int begin = 0;
|
||||
int end = numIcons - 1;
|
||||
while (begin <= end) {
|
||||
const int medium = (begin + end) / 2;
|
||||
const int off = iconsListOffset + 4 + 8 * medium;
|
||||
const int mimeOffset = cacheFile->getUint32(off);
|
||||
const char *mime = cacheFile->getCharStar(mimeOffset);
|
||||
const int cmp = qstrcmp(mime, inputMime);
|
||||
if (cmp < 0)
|
||||
begin = medium + 1;
|
||||
else if (cmp > 0)
|
||||
end = medium - 1;
|
||||
else {
|
||||
const int iconOffset = cacheFile->getUint32(off + 4);
|
||||
return QLatin1String(cacheFile->getCharStar(iconOffset));
|
||||
}
|
||||
}
|
||||
return QLatin1String();
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::loadIcon(QMimeTypePrivate &data)
|
||||
{
|
||||
const QByteArray inputMime = data.name.toLatin1();
|
||||
const QLatin1String icon = iconForMime(m_cacheFile, PosIconsListOffset, inputMime);
|
||||
if (!icon.isEmpty()) {
|
||||
data.iconName = icon;
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::loadGenericIcon(QMimeTypePrivate &data)
|
||||
{
|
||||
const QByteArray inputMime = data.name.toLatin1();
|
||||
const QLatin1String icon = iconForMime(m_cacheFile, PosGenericIconsListOffset, inputMime);
|
||||
if (!icon.isEmpty()) {
|
||||
data.genericIconName = icon;
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
#if QT_CONFIG(mimetype_database)
|
||||
static QString internalMimeFileName()
|
||||
{
|
||||
return QStringLiteral("<internal MIME data>");
|
||||
}
|
||||
|
||||
QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db, InternalDatabaseEnum)
|
||||
: QMimeProviderBase(db, internalMimeFileName())
|
||||
{
|
||||
static_assert(sizeof(mimetype_database), "Bundled MIME database is empty");
|
||||
static_assert(sizeof(mimetype_database) <= MimeTypeDatabaseOriginalSize,
|
||||
"Compressed MIME database is larger than the original size");
|
||||
static_assert(MimeTypeDatabaseOriginalSize <= 16*1024*1024,
|
||||
"Bundled MIME database is too big");
|
||||
const char *data = reinterpret_cast<const char *>(mimetype_database);
|
||||
qsizetype size = MimeTypeDatabaseOriginalSize;
|
||||
|
||||
#ifdef MIME_DATABASE_IS_ZSTD
|
||||
// uncompress with libzstd
|
||||
std::unique_ptr<char []> uncompressed(new char[size]);
|
||||
size = ZSTD_decompress(uncompressed.get(), size, mimetype_database, sizeof(mimetype_database));
|
||||
Q_ASSERT(!ZSTD_isError(size));
|
||||
data = uncompressed.get();
|
||||
#elif defined(MIME_DATABASE_IS_GZIP)
|
||||
std::unique_ptr<char []> uncompressed(new char[size]);
|
||||
z_stream zs = {};
|
||||
zs.next_in = const_cast<Bytef *>(mimetype_database);
|
||||
zs.avail_in = sizeof(mimetype_database);
|
||||
zs.next_out = reinterpret_cast<Bytef *>(uncompressed.get());
|
||||
zs.avail_out = size;
|
||||
|
||||
int res = inflateInit2(&zs, MAX_WBITS | 32);
|
||||
Q_ASSERT(res == Z_OK);
|
||||
res = inflate(&zs, Z_FINISH);
|
||||
Q_ASSERT(res == Z_STREAM_END);
|
||||
res = inflateEnd(&zs);
|
||||
Q_ASSERT(res == Z_OK);
|
||||
|
||||
data = uncompressed.get();
|
||||
size = zs.total_out;
|
||||
#endif
|
||||
|
||||
load(data, size);
|
||||
}
|
||||
#else // !QT_CONFIG(mimetype_database)
|
||||
// never called in release mode, but some debug builds may need
|
||||
// this to be defined.
|
||||
QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db, InternalDatabaseEnum)
|
||||
: QMimeProviderBase(db, QString())
|
||||
{
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
#endif // QT_CONFIG(mimetype_database)
|
||||
|
||||
QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db, const QString &directory)
|
||||
: QMimeProviderBase(db, directory)
|
||||
{
|
||||
ensureLoaded();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool QMimeXMLProvider::isInternalDatabase() const
|
||||
{
|
||||
#if QT_CONFIG(mimetype_database)
|
||||
return m_directory == internalMimeFileName();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
QMimeType QMimeXMLProvider::mimeTypeForName(const QString &name)
|
||||
{
|
||||
return m_nameMimeTypeMap.value(name);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result)
|
||||
{
|
||||
m_mimeTypeGlobs.matchingGlobs(fileName, result);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &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;
|
||||
candidateName = matcher.mimetype();
|
||||
foundOne = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundOne)
|
||||
candidate = mimeTypeForName(candidateName);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::ensureLoaded()
|
||||
{
|
||||
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;
|
||||
|
||||
m_nameMimeTypeMap.clear();
|
||||
m_aliases.clear();
|
||||
m_parents.clear();
|
||||
m_mimeTypeGlobs.clear();
|
||||
m_magicMatchers.clear();
|
||||
|
||||
//qDebug() << "Loading" << m_allFiles;
|
||||
|
||||
for (const QString &file : qAsConst(allFiles))
|
||||
load(file);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::load(const QString &fileName)
|
||||
{
|
||||
QString errorMessage;
|
||||
if (!load(fileName, &errorMessage))
|
||||
qWarning("QMimeDatabase: Error loading %ls\n%ls", qUtf16Printable(fileName), qUtf16Printable(errorMessage));
|
||||
}
|
||||
|
||||
bool QMimeXMLProvider::load(const QString &fileName, QString *errorMessage)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
if (errorMessage)
|
||||
*errorMessage = QLatin1String("Cannot open ") + fileName + QLatin1String(": ") + file.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (errorMessage)
|
||||
errorMessage->clear();
|
||||
|
||||
QMimeTypeParser parser(*this);
|
||||
return parser.parse(&file, fileName, errorMessage);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(mimetype_database)
|
||||
void QMimeXMLProvider::load(const char *data, qsizetype len)
|
||||
{
|
||||
QBuffer buffer;
|
||||
buffer.setData(QByteArray::fromRawData(data, len));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QString errorMessage;
|
||||
QMimeTypeParser parser(*this);
|
||||
if (!parser.parse(&buffer, internalMimeFileName(), &errorMessage))
|
||||
qWarning("QMimeDatabase: Error loading internal MIME data\n%s", qPrintable(errorMessage));
|
||||
}
|
||||
#endif
|
||||
|
||||
void QMimeXMLProvider::addGlobPattern(const QMimeGlobPattern &glob)
|
||||
{
|
||||
m_mimeTypeGlobs.addGlob(glob);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addMimeType(const QMimeType &mt)
|
||||
{
|
||||
Q_ASSERT(!mt.d.data()->fromCache);
|
||||
m_nameMimeTypeMap.insert(mt.name(), mt);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addParents(const QString &mime, QStringList &result)
|
||||
{
|
||||
for (const QString &parent : m_parents.value(mime)) {
|
||||
if (!result.contains(parent))
|
||||
result.append(parent);
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addParent(const QString &child, const QString &parent)
|
||||
{
|
||||
m_parents[child].append(parent);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addAliases(const QString &name, QStringList &result)
|
||||
{
|
||||
// Iterate through the whole hash. This method is rarely used.
|
||||
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)
|
||||
{
|
||||
return m_aliases.value(name);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addAlias(const QString &alias, const QString &name)
|
||||
{
|
||||
m_aliases.insert(alias, name);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addAllMimeTypes(QList<QMimeType> &result)
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_magicMatchers.append(matcher);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
194
src/libs/utils/mimetypes2/qmimeprovider_p.h
Normal file
194
src/libs/utils/mimetypes2/qmimeprovider_p.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMEPROVIDER_P_H
|
||||
#define QMIMEPROVIDER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qmimedatabase_p.h"
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include "qmimeglobpattern_p.h"
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qset.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeMagicRuleMatcher;
|
||||
|
||||
class QMimeProviderBase
|
||||
{
|
||||
public:
|
||||
QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory);
|
||||
virtual ~QMimeProviderBase() {}
|
||||
|
||||
virtual bool isValid() = 0;
|
||||
virtual bool isInternalDatabase() const = 0;
|
||||
virtual QMimeType mimeTypeForName(const QString &name) = 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 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 bool loadMimeTypePrivate(QMimeTypePrivate &) { return false; }
|
||||
virtual void loadIcon(QMimeTypePrivate &) {}
|
||||
virtual void loadGenericIcon(QMimeTypePrivate &) {}
|
||||
virtual void ensureLoaded() {}
|
||||
|
||||
QString directory() const { return m_directory; }
|
||||
|
||||
QMimeDatabasePrivate *m_db;
|
||||
QString m_directory;
|
||||
};
|
||||
|
||||
/*
|
||||
Parses the files 'mime.cache' and 'types' on demand
|
||||
*/
|
||||
class QMimeBinaryProvider : public QMimeProviderBase
|
||||
{
|
||||
public:
|
||||
QMimeBinaryProvider(QMimeDatabasePrivate *db, const QString &directory);
|
||||
virtual ~QMimeBinaryProvider();
|
||||
|
||||
bool isValid() override;
|
||||
bool isInternalDatabase() const override;
|
||||
QMimeType mimeTypeForName(const QString &name) override;
|
||||
void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) override;
|
||||
void addParents(const QString &mime, QStringList &result) override;
|
||||
QString resolveAlias(const QString &name) 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;
|
||||
bool loadMimeTypePrivate(QMimeTypePrivate &) override;
|
||||
void loadIcon(QMimeTypePrivate &) override;
|
||||
void loadGenericIcon(QMimeTypePrivate &) override;
|
||||
void ensureLoaded() override;
|
||||
|
||||
private:
|
||||
struct CacheFile;
|
||||
|
||||
void matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int offset, const QString &fileName);
|
||||
bool matchSuffixTree(QMimeGlobMatchResult &result, CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck);
|
||||
bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data);
|
||||
QLatin1String iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime);
|
||||
void loadMimeTypeList();
|
||||
bool checkCacheChanged();
|
||||
|
||||
CacheFile *m_cacheFile = nullptr;
|
||||
QStringList m_cacheFileNames;
|
||||
QSet<QString> m_mimetypeNames;
|
||||
bool m_mimetypeListLoaded;
|
||||
struct MimeTypeExtra
|
||||
{
|
||||
QHash<QString, QString> localeComments;
|
||||
QStringList globPatterns;
|
||||
};
|
||||
QMap<QString, MimeTypeExtra> m_mimetypeExtra;
|
||||
};
|
||||
|
||||
/*
|
||||
Parses the raw XML files (slower)
|
||||
*/
|
||||
class QMimeXMLProvider : public QMimeProviderBase
|
||||
{
|
||||
public:
|
||||
enum InternalDatabaseEnum { InternalDatabase };
|
||||
#if QT_CONFIG(mimetype_database)
|
||||
enum : bool { InternalDatabaseAvailable = true };
|
||||
#else
|
||||
enum : bool { InternalDatabaseAvailable = false };
|
||||
#endif
|
||||
QMimeXMLProvider(QMimeDatabasePrivate *db, InternalDatabaseEnum);
|
||||
QMimeXMLProvider(QMimeDatabasePrivate *db, const QString &directory);
|
||||
~QMimeXMLProvider();
|
||||
|
||||
bool isValid() override;
|
||||
bool isInternalDatabase() const override;
|
||||
QMimeType mimeTypeForName(const QString &name) override;
|
||||
void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) override;
|
||||
void addParents(const QString &mime, QStringList &result) override;
|
||||
QString resolveAlias(const QString &name) 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);
|
||||
|
||||
// Called by the mimetype xml parser
|
||||
void addMimeType(const QMimeType &mt);
|
||||
void addGlobPattern(const QMimeGlobPattern &glob);
|
||||
void addParent(const QString &child, const QString &parent);
|
||||
void addAlias(const QString &alias, const QString &name);
|
||||
void addMagicMatcher(const QMimeMagicRuleMatcher &matcher);
|
||||
|
||||
private:
|
||||
void load(const QString &fileName);
|
||||
void load(const char *data, qsizetype len);
|
||||
|
||||
typedef QHash<QString, QMimeType> NameMimeTypeMap;
|
||||
NameMimeTypeMap m_nameMimeTypeMap;
|
||||
|
||||
typedef QHash<QString, QString> AliasHash;
|
||||
AliasHash m_aliases;
|
||||
|
||||
typedef QHash<QString, QStringList> ParentsHash;
|
||||
ParentsHash m_parents;
|
||||
QMimeAllGlobPatterns m_mimeTypeGlobs;
|
||||
|
||||
QList<QMimeMagicRuleMatcher> m_magicMatchers;
|
||||
QStringList m_allFiles;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEPROVIDER_P_H
|
533
src/libs/utils/mimetypes2/qmimetype.cpp
Normal file
533
src/libs/utils/mimetypes2/qmimetype.cpp
Normal file
@@ -0,0 +1,533 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmimetype.h"
|
||||
|
||||
#include "qmimetype_p.h"
|
||||
#include "qmimedatabase_p.h"
|
||||
#include "qmimeprovider_p.h"
|
||||
|
||||
#include "qmimeglobpattern_p.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QLocale>
|
||||
#include <QtCore/QHashFunctions>
|
||||
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QMimeTypePrivate::QMimeTypePrivate()
|
||||
: loaded(false), fromCache(false)
|
||||
{}
|
||||
|
||||
QMimeTypePrivate::QMimeTypePrivate(const QMimeType &other)
|
||||
: loaded(other.d->loaded),
|
||||
name(other.d->name),
|
||||
localeComments(other.d->localeComments),
|
||||
genericIconName(other.d->genericIconName),
|
||||
iconName(other.d->iconName),
|
||||
globPatterns(other.d->globPatterns)
|
||||
{}
|
||||
|
||||
void QMimeTypePrivate::clear()
|
||||
{
|
||||
name.clear();
|
||||
localeComments.clear();
|
||||
genericIconName.clear();
|
||||
iconName.clear();
|
||||
globPatterns.clear();
|
||||
}
|
||||
|
||||
void QMimeTypePrivate::addGlobPattern(const QString &pattern)
|
||||
{
|
||||
globPatterns.append(pattern);
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QMimeType
|
||||
\inmodule QtCore
|
||||
\ingroup shared
|
||||
\brief The QMimeType class describes types of file or data, represented by a MIME type string.
|
||||
|
||||
\since 5.0
|
||||
|
||||
For instance a file named "readme.txt" has the MIME type "text/plain".
|
||||
The MIME type can be determined from the file name, or from the file
|
||||
contents, or from both. MIME type determination can also be done on
|
||||
buffers of data not coming from files.
|
||||
|
||||
Determining the MIME type of a file can be useful to make sure your
|
||||
application supports it. It is also useful in file-manager-like applications
|
||||
or widgets, in order to display an appropriate \l {QMimeType::iconName}{icon} for the file, or even
|
||||
the descriptive \l {QMimeType::comment()}{comment} in detailed views.
|
||||
|
||||
To check if a file has the expected MIME type, you should use inherits()
|
||||
rather than a simple string comparison based on the name(). This is because
|
||||
MIME types can inherit from each other: for instance a C source file is
|
||||
a specific type of plain text file, so text/x-csrc inherits text/plain.
|
||||
|
||||
\sa QMimeDatabase, {MIME Type Browser Example}
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QMimeType &QMimeType::operator=(QMimeType &&other)
|
||||
|
||||
Move-assigns \a other to this QMimeType instance.
|
||||
|
||||
\since 5.2
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QMimeType::QMimeType();
|
||||
Constructs this QMimeType object initialized with default property values that indicate an invalid MIME type.
|
||||
*/
|
||||
QMimeType::QMimeType() :
|
||||
d(new QMimeTypePrivate())
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeType::QMimeType(const QMimeType &other);
|
||||
Constructs this QMimeType object as a copy of \a other.
|
||||
*/
|
||||
QMimeType::QMimeType(const QMimeType &other) :
|
||||
d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeType &QMimeType::operator=(const QMimeType &other);
|
||||
Assigns the data of \a other to this QMimeType object, and returns a reference to this object.
|
||||
*/
|
||||
QMimeType &QMimeType::operator=(const QMimeType &other)
|
||||
{
|
||||
if (d != other.d)
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeType::QMimeType(const QMimeTypePrivate &dd);
|
||||
Assigns the data of the QMimeTypePrivate \a dd to this QMimeType object, and returns a reference to this object.
|
||||
\internal
|
||||
*/
|
||||
QMimeType::QMimeType(const QMimeTypePrivate &dd) :
|
||||
d(new QMimeTypePrivate(dd))
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn void QMimeType::swap(QMimeType &other);
|
||||
Swaps QMimeType \a other with this QMimeType object.
|
||||
|
||||
This operation is very fast and never fails.
|
||||
|
||||
The swap() method helps with the implementation of assignment
|
||||
operators in an exception-safe way. For more information consult
|
||||
\l {http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap}
|
||||
{More C++ Idioms - Copy-and-swap}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QMimeType::~QMimeType();
|
||||
Destroys the QMimeType object, and releases the d pointer.
|
||||
*/
|
||||
QMimeType::~QMimeType()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QMimeType::operator==(const QMimeType &other) const;
|
||||
Returns \c true if \a other equals this QMimeType object, otherwise returns \c false.
|
||||
The name is the unique identifier for a mimetype, so two mimetypes with
|
||||
the same name, are equal.
|
||||
*/
|
||||
bool QMimeType::operator==(const QMimeType &other) const
|
||||
{
|
||||
return d == other.d || d->name == other.d->name;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.6
|
||||
\relates QMimeType
|
||||
|
||||
Returns the hash value for \a key, using
|
||||
\a seed to seed the calculation.
|
||||
*/
|
||||
size_t qHash(const QMimeType &key, size_t seed) noexcept
|
||||
{
|
||||
return qHash(key.d->name, seed);
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QMimeType::operator!=(const QMimeType &other) const;
|
||||
Returns \c true if \a other does not equal this QMimeType object, otherwise returns \c false.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\property QMimeType::valid
|
||||
\brief \c true if the QMimeType object contains valid data, \c false otherwise
|
||||
|
||||
A valid MIME type has a non-empty name().
|
||||
The invalid MIME type is the default-constructed QMimeType.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
bool QMimeType::isValid() const
|
||||
{
|
||||
return !d->name.isEmpty();
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::isDefault
|
||||
\brief \c true if this MIME type is the default MIME type which
|
||||
applies to all files: application/octet-stream.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
bool QMimeType::isDefault() const
|
||||
{
|
||||
return d->name == QMimeDatabasePrivate::instance()->defaultMimeType();
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::name
|
||||
\brief the name of the MIME type
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QString QMimeType::name() const
|
||||
{
|
||||
return d->name;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::comment
|
||||
\brief the description of the MIME type to be displayed on user interfaces
|
||||
|
||||
The default language (QLocale().name()) is used to select the appropriate translation.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QString QMimeType::comment() const
|
||||
{
|
||||
QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
|
||||
|
||||
QStringList languageList;
|
||||
languageList << QLocale().name();
|
||||
languageList << QLocale().uiLanguages();
|
||||
languageList << QLatin1String("default"); // use the default locale if possible.
|
||||
for (const QString &language : qAsConst(languageList)) {
|
||||
const QString lang = language == QLatin1String("C") ? QLatin1String("en_US") : language;
|
||||
const QString comm = d->localeComments.value(lang);
|
||||
if (!comm.isEmpty())
|
||||
return comm;
|
||||
const int pos = lang.indexOf(QLatin1Char('_'));
|
||||
if (pos != -1) {
|
||||
// "pt_BR" not found? try just "pt"
|
||||
const QString shortLang = lang.left(pos);
|
||||
const QString commShort = d->localeComments.value(shortLang);
|
||||
if (!commShort.isEmpty())
|
||||
return commShort;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the mimetype name as fallback
|
||||
return d->name;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::genericIconName
|
||||
\brief the file name of a generic icon that represents the MIME type
|
||||
|
||||
This should be used if the icon returned by iconName() cannot be found on
|
||||
the system. It is used for categories of similar types (like spreadsheets
|
||||
or archives) that can use a common icon.
|
||||
The freedesktop.org Icon Naming Specification lists a set of such icon names.
|
||||
|
||||
The icon name can be given to QIcon::fromTheme() in order to load the icon.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QString QMimeType::genericIconName() const
|
||||
{
|
||||
QMimeDatabasePrivate::instance()->loadGenericIcon(const_cast<QMimeTypePrivate&>(*d));
|
||||
if (d->genericIconName.isEmpty()) {
|
||||
// From the spec:
|
||||
// If the generic icon name is empty (not specified by the mimetype definition)
|
||||
// then the mimetype is used to generate the generic icon by using the top-level
|
||||
// media type (e.g. "video" in "video/ogg") and appending "-x-generic"
|
||||
// (i.e. "video-x-generic" in the previous example).
|
||||
const QString group = name();
|
||||
QStringView groupRef(group);
|
||||
const int slashindex = groupRef.indexOf(QLatin1Char('/'));
|
||||
if (slashindex != -1)
|
||||
groupRef = groupRef.left(slashindex);
|
||||
return groupRef + QLatin1String("-x-generic");
|
||||
}
|
||||
return d->genericIconName;
|
||||
}
|
||||
|
||||
static QString make_default_icon_name_from_mimetype_name(QString iconName)
|
||||
{
|
||||
const int slashindex = iconName.indexOf(QLatin1Char('/'));
|
||||
if (slashindex != -1)
|
||||
iconName[slashindex] = QLatin1Char('-');
|
||||
return iconName;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::iconName
|
||||
\brief the file name of an icon image that represents the MIME type
|
||||
|
||||
The icon name can be given to QIcon::fromTheme() in order to load the icon.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QString QMimeType::iconName() const
|
||||
{
|
||||
QMimeDatabasePrivate::instance()->loadIcon(const_cast<QMimeTypePrivate&>(*d));
|
||||
if (d->iconName.isEmpty()) {
|
||||
return make_default_icon_name_from_mimetype_name(name());
|
||||
}
|
||||
return d->iconName;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::globPatterns
|
||||
\brief the list of glob matching patterns
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QStringList QMimeType::globPatterns() const
|
||||
{
|
||||
QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
|
||||
return d->globPatterns;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::parentMimeTypes
|
||||
\brief the names of parent MIME types
|
||||
|
||||
A type is a subclass of another type if any instance of the first type is
|
||||
also an instance of the second. For example, all image/svg+xml files are also
|
||||
text/xml, text/plain and application/octet-stream files. Subclassing is about
|
||||
the format, rather than the category of the data (for example, there is no
|
||||
'generic spreadsheet' class that all spreadsheets inherit from).
|
||||
Conversely, the parent mimetype of image/svg+xml is text/xml.
|
||||
|
||||
A mimetype can have multiple parents. For instance application/x-perl
|
||||
has two parents: application/x-executable and text/plain. This makes
|
||||
it possible to both execute perl scripts, and to open them in text editors.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QStringList QMimeType::parentMimeTypes() const
|
||||
{
|
||||
return QMimeDatabasePrivate::instance()->mimeParents(d->name);
|
||||
}
|
||||
|
||||
static void collectParentMimeTypes(const QString &mime, QStringList &allParents)
|
||||
{
|
||||
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))
|
||||
allParents.append(parent);
|
||||
}
|
||||
// We want a breadth-first search, so that the least-specific parent (octet-stream) is last
|
||||
// This means iterating twice, unfortunately.
|
||||
for (const QString &parent : parents)
|
||||
collectParentMimeTypes(parent, allParents);
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::allAncestors
|
||||
\brief the names of direct and indirect parent MIME types
|
||||
|
||||
Return all the parent mimetypes of this mimetype, direct and indirect.
|
||||
This includes the parent(s) of its parent(s), etc.
|
||||
|
||||
For instance, for image/svg+xml the list would be:
|
||||
application/xml, text/plain, application/octet-stream.
|
||||
|
||||
Note that application/octet-stream is the ultimate parent for all types
|
||||
of files (but not directories).
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QStringList QMimeType::allAncestors() const
|
||||
{
|
||||
QStringList allParents;
|
||||
collectParentMimeTypes(d->name, allParents);
|
||||
return allParents;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::aliases
|
||||
\brief the list of aliases of this mimetype
|
||||
|
||||
For instance, for text/csv, the returned list would be:
|
||||
text/x-csv, text/x-comma-separated-values.
|
||||
|
||||
Note that all QMimeType instances refer to proper mimetypes,
|
||||
never to aliases directly.
|
||||
|
||||
The order of the aliases in the list is undefined.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QStringList QMimeType::aliases() const
|
||||
{
|
||||
return QMimeDatabasePrivate::instance()->listAliases(d->name);
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::suffixes
|
||||
\brief the known suffixes for the MIME type
|
||||
|
||||
No leading dot is included, so for instance this would return "jpg", "jpeg" for image/jpeg.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QStringList QMimeType::suffixes() const
|
||||
{
|
||||
QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
|
||||
|
||||
QStringList result;
|
||||
for (const QString &pattern : qAsConst(d->globPatterns)) {
|
||||
// Not a simple suffix if it looks like: README or *. or *.* or *.JP*G or *.JP?
|
||||
if (pattern.startsWith(QLatin1String("*.")) &&
|
||||
pattern.length() > 2 &&
|
||||
pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) {
|
||||
const QString suffix = pattern.mid(2);
|
||||
result.append(suffix);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::preferredSuffix
|
||||
\brief the preferred suffix for the MIME type
|
||||
|
||||
No leading dot is included, so for instance this would return "pdf" for application/pdf.
|
||||
The return value can be empty, for mime types which do not have any suffixes associated.
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QString QMimeType::preferredSuffix() const
|
||||
{
|
||||
if (isDefault()) // workaround for unwanted *.bin suffix for octet-stream, https://bugs.freedesktop.org/show_bug.cgi?id=101667, fixed upstream in 1.10
|
||||
return QString();
|
||||
const QStringList suffixList = suffixes();
|
||||
return suffixList.isEmpty() ? QString() : suffixList.at(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QMimeType::filterString
|
||||
\brief a filter string usable for a file dialog
|
||||
|
||||
While this property was introduced in 5.10, the
|
||||
corresponding accessor method has always been there.
|
||||
*/
|
||||
QString QMimeType::filterString() const
|
||||
{
|
||||
QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
|
||||
QString filter;
|
||||
|
||||
if (!d->globPatterns.empty()) {
|
||||
filter += comment() + QLatin1String(" (");
|
||||
for (int i = 0; i < d->globPatterns.size(); ++i) {
|
||||
if (i != 0)
|
||||
filter += QLatin1Char(' ');
|
||||
filter += d->globPatterns.at(i);
|
||||
}
|
||||
filter += QLatin1Char(')');
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QMimeType::inherits(const QString &mimeTypeName) const;
|
||||
Returns \c true if this mimetype is \a mimeTypeName,
|
||||
or inherits \a mimeTypeName (see parentMimeTypes()),
|
||||
or \a mimeTypeName is an alias for this mimetype.
|
||||
|
||||
This method has been made invokable from QML since 5.10.
|
||||
*/
|
||||
bool QMimeType::inherits(const QString &mimeTypeName) const
|
||||
{
|
||||
if (d->name == mimeTypeName)
|
||||
return true;
|
||||
return QMimeDatabasePrivate::instance()->mimeInherits(d->name, mimeTypeName);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
QDebug operator<<(QDebug debug, const QMimeType &mime)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
if (!mime.isValid()) {
|
||||
debug.nospace() << "QMimeType(invalid)";
|
||||
} else {
|
||||
debug.nospace() << "QMimeType(" << mime.name() << ")";
|
||||
}
|
||||
return debug;
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qmimetype.cpp"
|
136
src/libs/utils/mimetypes2/qmimetype.h
Normal file
136
src/libs/utils/mimetypes2/qmimetype.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMETYPE_H
|
||||
#define QMIMETYPE_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include <QtCore/qobjectdefs.h>
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeTypePrivate;
|
||||
class QMimeType;
|
||||
|
||||
Q_CORE_EXPORT size_t qHash(const QMimeType &key, size_t seed = 0) noexcept;
|
||||
|
||||
class Q_CORE_EXPORT QMimeType
|
||||
{
|
||||
Q_GADGET
|
||||
Q_PROPERTY(bool valid READ isValid CONSTANT)
|
||||
Q_PROPERTY(bool isDefault READ isDefault CONSTANT)
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(QString comment READ comment CONSTANT)
|
||||
Q_PROPERTY(QString genericIconName READ genericIconName CONSTANT)
|
||||
Q_PROPERTY(QString iconName READ iconName CONSTANT)
|
||||
Q_PROPERTY(QStringList globPatterns READ globPatterns CONSTANT)
|
||||
Q_PROPERTY(QStringList parentMimeTypes READ parentMimeTypes CONSTANT)
|
||||
Q_PROPERTY(QStringList allAncestors READ allAncestors CONSTANT)
|
||||
Q_PROPERTY(QStringList aliases READ aliases CONSTANT)
|
||||
Q_PROPERTY(QStringList suffixes READ suffixes CONSTANT)
|
||||
Q_PROPERTY(QString preferredSuffix READ preferredSuffix CONSTANT)
|
||||
Q_PROPERTY(QString filterString READ filterString CONSTANT)
|
||||
|
||||
public:
|
||||
QMimeType();
|
||||
QMimeType(const QMimeType &other);
|
||||
QMimeType &operator=(const QMimeType &other);
|
||||
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QMimeType)
|
||||
void swap(QMimeType &other) noexcept
|
||||
{
|
||||
d.swap(other.d);
|
||||
}
|
||||
explicit QMimeType(const QMimeTypePrivate &dd);
|
||||
~QMimeType();
|
||||
|
||||
bool operator==(const QMimeType &other) const;
|
||||
|
||||
inline bool operator!=(const QMimeType &other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
bool isDefault() const;
|
||||
|
||||
QString name() const;
|
||||
QString comment() const;
|
||||
QString genericIconName() const;
|
||||
QString iconName() const;
|
||||
QStringList globPatterns() const;
|
||||
QStringList parentMimeTypes() const;
|
||||
QStringList allAncestors() const;
|
||||
QStringList aliases() const;
|
||||
QStringList suffixes() const;
|
||||
QString preferredSuffix() const;
|
||||
|
||||
Q_INVOKABLE bool inherits(const QString &mimeTypeName) const;
|
||||
|
||||
QString filterString() const;
|
||||
|
||||
protected:
|
||||
friend class QMimeTypeParserBase;
|
||||
friend class MimeTypeMapEntry;
|
||||
friend class QMimeDatabasePrivate;
|
||||
friend class QMimeXMLProvider;
|
||||
friend class QMimeBinaryProvider;
|
||||
friend class QMimeTypePrivate;
|
||||
friend Q_CORE_EXPORT size_t qHash(const QMimeType &key, size_t seed) noexcept;
|
||||
|
||||
QExplicitlySharedDataPointer<QMimeTypePrivate> d;
|
||||
};
|
||||
|
||||
Q_DECLARE_SHARED(QMimeType)
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
class QDebug;
|
||||
Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QMimeType &mime);
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMETYPE_H
|
106
src/libs/utils/mimetypes2/qmimetype_p.h
Normal file
106
src/libs/utils/mimetypes2/qmimetype_p.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMETYPE_P_H
|
||||
#define QMIMETYPE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/private/qglobal_p.h>
|
||||
#include "qmimetype.h"
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_AUTOTEST_EXPORT QMimeTypePrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
typedef QHash<QString, QString> LocaleHash;
|
||||
|
||||
QMimeTypePrivate();
|
||||
explicit QMimeTypePrivate(const QMimeType &other);
|
||||
|
||||
void clear();
|
||||
|
||||
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;
|
||||
QString iconName;
|
||||
QStringList globPatterns;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#define QMIMETYPE_BUILDER_FROM_RVALUE_REFS \
|
||||
QT_BEGIN_NAMESPACE \
|
||||
static QMimeType buildQMimeType ( \
|
||||
QString &&name, \
|
||||
QString &&genericIconName, \
|
||||
QString &&iconName, \
|
||||
QStringList &&globPatterns \
|
||||
) \
|
||||
{ \
|
||||
QMimeTypePrivate qMimeTypeData; \
|
||||
qMimeTypeData.loaded = true; \
|
||||
qMimeTypeData.name = std::move(name); \
|
||||
qMimeTypeData.genericIconName = std::move(genericIconName); \
|
||||
qMimeTypeData.iconName = std::move(iconName); \
|
||||
qMimeTypeData.globPatterns = std::move(globPatterns); \
|
||||
return QMimeType(qMimeTypeData); \
|
||||
} \
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMETYPE_P_H
|
350
src/libs/utils/mimetypes2/qmimetypeparser.cpp
Normal file
350
src/libs/utils/mimetypes2/qmimetypeparser.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#define QT_NO_CAST_FROM_ASCII
|
||||
|
||||
#include "qmimetypeparser_p.h"
|
||||
|
||||
#include "qmimetype_p.h"
|
||||
#include "qmimemagicrulematcher_p.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QXmlStreamReader>
|
||||
#include <QtCore/QXmlStreamWriter>
|
||||
#include <QtCore/QStack>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// XML tags in MIME files
|
||||
static const char mimeInfoTagC[] = "mime-info";
|
||||
static const char mimeTypeTagC[] = "mime-type";
|
||||
static const char mimeTypeAttributeC[] = "type";
|
||||
static const char subClassTagC[] = "sub-class-of";
|
||||
static const char commentTagC[] = "comment";
|
||||
static const char genericIconTagC[] = "generic-icon";
|
||||
static const char iconTagC[] = "icon";
|
||||
static const char nameAttributeC[] = "name";
|
||||
static const char globTagC[] = "glob";
|
||||
static const char globDeleteAllTagC[] = "glob-deleteall";
|
||||
static const char aliasTagC[] = "alias";
|
||||
static const char patternAttributeC[] = "pattern";
|
||||
static const char weightAttributeC[] = "weight";
|
||||
static const char caseSensitiveAttributeC[] = "case-sensitive";
|
||||
static const char localeAttributeC[] = "xml:lang";
|
||||
|
||||
static const char magicTagC[] = "magic";
|
||||
static const char priorityAttributeC[] = "priority";
|
||||
|
||||
static const char matchTagC[] = "match";
|
||||
static const char matchValueAttributeC[] = "value";
|
||||
static const char matchTypeAttributeC[] = "type";
|
||||
static const char matchOffsetAttributeC[] = "offset";
|
||||
static const char matchMaskAttributeC[] = "mask";
|
||||
|
||||
/*!
|
||||
\class QMimeTypeParser
|
||||
\inmodule QtCore
|
||||
\internal
|
||||
\brief The QMimeTypeParser class parses MIME types, and builds a MIME database hierarchy by adding to QMimeDatabase.
|
||||
|
||||
Populates QMimeDataBase
|
||||
|
||||
\sa QMimeDatabase, QMimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
||||
\sa QMimeTypeParser
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QMimeTypeParserBase
|
||||
\inmodule QtCore
|
||||
\internal
|
||||
\brief The QMimeTypeParserBase class parses for a sequence of <mime-type> in a generic way.
|
||||
|
||||
Calls abstract handler function process for QMimeType it finds.
|
||||
|
||||
\sa QMimeDatabase, QMimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
||||
\sa QMimeTypeParser
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual bool QMimeTypeParserBase::process(const QMimeType &t, QString *errorMessage) = 0;
|
||||
Overwrite to process the sequence of parsed data
|
||||
*/
|
||||
|
||||
QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState currentState, QStringView startElement)
|
||||
{
|
||||
switch (currentState) {
|
||||
case ParseBeginning:
|
||||
if (startElement == QLatin1String(mimeInfoTagC))
|
||||
return ParseMimeInfo;
|
||||
if (startElement == QLatin1String(mimeTypeTagC))
|
||||
return ParseMimeType;
|
||||
return ParseError;
|
||||
case ParseMimeInfo:
|
||||
return startElement == QLatin1String(mimeTypeTagC) ? ParseMimeType : ParseError;
|
||||
case ParseMimeType:
|
||||
case ParseComment:
|
||||
case ParseGenericIcon:
|
||||
case ParseIcon:
|
||||
case ParseGlobPattern:
|
||||
case ParseGlobDeleteAll:
|
||||
case ParseSubClass:
|
||||
case ParseAlias:
|
||||
case ParseOtherMimeTypeSubTag:
|
||||
case ParseMagicMatchRule:
|
||||
if (startElement == QLatin1String(mimeTypeTagC)) // Sequence of <mime-type>
|
||||
return ParseMimeType;
|
||||
if (startElement == QLatin1String(commentTagC))
|
||||
return ParseComment;
|
||||
if (startElement == QLatin1String(genericIconTagC))
|
||||
return ParseGenericIcon;
|
||||
if (startElement == QLatin1String(iconTagC))
|
||||
return ParseIcon;
|
||||
if (startElement == QLatin1String(globTagC))
|
||||
return ParseGlobPattern;
|
||||
if (startElement == QLatin1String(globDeleteAllTagC))
|
||||
return ParseGlobDeleteAll;
|
||||
if (startElement == QLatin1String(subClassTagC))
|
||||
return ParseSubClass;
|
||||
if (startElement == QLatin1String(aliasTagC))
|
||||
return ParseAlias;
|
||||
if (startElement == QLatin1String(magicTagC))
|
||||
return ParseMagic;
|
||||
if (startElement == QLatin1String(matchTagC))
|
||||
return ParseMagicMatchRule;
|
||||
return ParseOtherMimeTypeSubTag;
|
||||
case ParseMagic:
|
||||
if (startElement == QLatin1String(matchTagC))
|
||||
return ParseMagicMatchRule;
|
||||
break;
|
||||
case ParseError:
|
||||
break;
|
||||
}
|
||||
return ParseError;
|
||||
}
|
||||
|
||||
// Parse int number from an (attribute) string
|
||||
bool QMimeTypeParserBase::parseNumber(QStringView n, int *target, QString *errorMessage)
|
||||
{
|
||||
bool ok;
|
||||
*target = n.toInt(&ok);
|
||||
if (Q_UNLIKELY(!ok)) {
|
||||
if (errorMessage)
|
||||
*errorMessage = QLatin1String("Not a number '") + n + QLatin1String("'.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_XMLSTREAMREADER
|
||||
struct CreateMagicMatchRuleResult
|
||||
{
|
||||
QString errorMessage; // must be first
|
||||
QMimeMagicRule rule;
|
||||
|
||||
CreateMagicMatchRuleResult(QStringView type, QStringView value, QStringView offsets, QStringView mask)
|
||||
: errorMessage(), rule(type.toString(), value.toUtf8(), offsets.toString(), mask.toLatin1(), &errorMessage)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
static CreateMagicMatchRuleResult createMagicMatchRule(const QXmlStreamAttributes &atts)
|
||||
{
|
||||
const auto type = atts.value(QLatin1String(matchTypeAttributeC));
|
||||
const auto value = atts.value(QLatin1String(matchValueAttributeC));
|
||||
const auto offsets = atts.value(QLatin1String(matchOffsetAttributeC));
|
||||
const auto mask = atts.value(QLatin1String(matchMaskAttributeC));
|
||||
return CreateMagicMatchRuleResult(type, value, offsets, mask);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool QMimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString *errorMessage)
|
||||
{
|
||||
#ifdef QT_NO_XMLSTREAMREADER
|
||||
Q_UNUSED(dev);
|
||||
if (errorMessage)
|
||||
*errorMessage = QString::fromLatin1("QXmlStreamReader is not available, cannot parse '%1'.").arg(fileName);
|
||||
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
|
||||
QXmlStreamReader reader(dev);
|
||||
ParseState ps = ParseBeginning;
|
||||
while (!reader.atEnd()) {
|
||||
switch (reader.readNext()) {
|
||||
case QXmlStreamReader::StartElement: {
|
||||
ps = nextState(ps, reader.name());
|
||||
const QXmlStreamAttributes atts = reader.attributes();
|
||||
switch (ps) {
|
||||
case ParseMimeType: { // start parsing a MIME type name
|
||||
const QString name = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
|
||||
if (name.isEmpty()) {
|
||||
reader.raiseError(QStringLiteral("Missing 'type'-attribute"));
|
||||
} else {
|
||||
data.name = name;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ParseGenericIcon:
|
||||
data.genericIconName = atts.value(QLatin1String(nameAttributeC)).toString();
|
||||
break;
|
||||
case ParseIcon:
|
||||
data.iconName = atts.value(QLatin1String(nameAttributeC)).toString();
|
||||
break;
|
||||
case ParseGlobPattern: {
|
||||
const QString pattern = atts.value(QLatin1String(patternAttributeC)).toString();
|
||||
unsigned weight = atts.value(QLatin1String(weightAttributeC)).toInt();
|
||||
const bool caseSensitive = atts.value(QLatin1String(caseSensitiveAttributeC)) == QLatin1String("true");
|
||||
|
||||
if (weight == 0)
|
||||
weight = QMimeGlobPattern::DefaultWeight;
|
||||
|
||||
Q_ASSERT(!data.name.isEmpty());
|
||||
const QMimeGlobPattern glob(pattern, data.name, weight, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
||||
if (!process(glob, errorMessage)) // for actual glob matching
|
||||
return false;
|
||||
data.addGlobPattern(pattern); // just for QMimeType::globPatterns()
|
||||
}
|
||||
break;
|
||||
case ParseGlobDeleteAll:
|
||||
data.globPatterns.clear();
|
||||
break;
|
||||
case ParseSubClass: {
|
||||
const QString inheritsFrom = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
|
||||
if (!inheritsFrom.isEmpty())
|
||||
processParent(data.name, inheritsFrom);
|
||||
}
|
||||
break;
|
||||
case ParseComment: {
|
||||
// comments have locale attributes.
|
||||
QString locale = atts.value(QLatin1String(localeAttributeC)).toString();
|
||||
const QString comment = reader.readElementText();
|
||||
if (locale.isEmpty())
|
||||
locale = QString::fromLatin1("default");
|
||||
data.localeComments.insert(locale, comment);
|
||||
}
|
||||
break;
|
||||
case ParseAlias: {
|
||||
const QString alias = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
|
||||
if (!alias.isEmpty())
|
||||
processAlias(alias, data.name);
|
||||
}
|
||||
break;
|
||||
case ParseMagic: {
|
||||
priority = 50;
|
||||
const auto priorityS = atts.value(QLatin1String(priorityAttributeC));
|
||||
if (!priorityS.isEmpty()) {
|
||||
if (!parseNumber(priorityS, &priority, errorMessage))
|
||||
return false;
|
||||
|
||||
}
|
||||
currentRules.clear();
|
||||
//qDebug() << "MAGIC start for mimetype" << data.name;
|
||||
}
|
||||
break;
|
||||
case ParseMagicMatchRule: {
|
||||
auto result = createMagicMatchRule(atts);
|
||||
if (Q_UNLIKELY(!result.rule.isValid()))
|
||||
qWarning("QMimeDatabase: Error parsing %ls\n%ls",
|
||||
qUtf16Printable(fileName), qUtf16Printable(result.errorMessage));
|
||||
QList<QMimeMagicRule> *ruleList;
|
||||
if (currentRules.isEmpty())
|
||||
ruleList = &rules;
|
||||
else // nest this rule into the proper parent
|
||||
ruleList = ¤tRules.top()->m_subMatches;
|
||||
ruleList->append(std::move(result.rule));
|
||||
//qDebug() << " MATCH added. Stack size was" << currentRules.size();
|
||||
currentRules.push(&ruleList->last());
|
||||
break;
|
||||
}
|
||||
case ParseError:
|
||||
reader.raiseError(QLatin1String("Unexpected element <") + reader.name() + QLatin1Char('>'));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// continue switch QXmlStreamReader::Token...
|
||||
case QXmlStreamReader::EndElement: // Finished element
|
||||
{
|
||||
const auto elementName = reader.name();
|
||||
if (elementName == QLatin1String(mimeTypeTagC)) {
|
||||
if (!process(QMimeType(data), errorMessage))
|
||||
return false;
|
||||
data.clear();
|
||||
} else if (elementName == QLatin1String(matchTagC)) {
|
||||
// Closing a <match> tag, pop stack
|
||||
currentRules.pop();
|
||||
//qDebug() << " MATCH closed. Stack size is now" << currentRules.size();
|
||||
} else if (elementName == QLatin1String(magicTagC)) {
|
||||
//qDebug() << "MAGIC ended, we got" << rules.count() << "rules, with prio" << priority;
|
||||
// Finished a <magic> sequence
|
||||
QMimeMagicRuleMatcher ruleMatcher(data.name, priority);
|
||||
ruleMatcher.addRules(rules);
|
||||
processMagicMatcher(ruleMatcher);
|
||||
rules.clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(reader.hasError())) {
|
||||
if (errorMessage) {
|
||||
*errorMessage = QString::asprintf("An error has been encountered at line %lld of %ls: %ls:",
|
||||
reader.lineNumber(),
|
||||
qUtf16Printable(fileName),
|
||||
qUtf16Printable(reader.errorString()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif //QT_NO_XMLSTREAMREADER
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
133
src/libs/utils/mimetypes2/qmimetypeparser_p.h
Normal file
133
src/libs/utils/mimetypes2/qmimetypeparser_p.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QMIMETYPEPARSER_P_H
|
||||
#define QMIMETYPEPARSER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qmimedatabase_p.h"
|
||||
|
||||
QT_REQUIRE_CONFIG(mimetype);
|
||||
|
||||
#include "qmimeprovider_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QIODevice;
|
||||
|
||||
class QMimeTypeParserBase
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QMimeTypeParserBase)
|
||||
|
||||
public:
|
||||
QMimeTypeParserBase() {}
|
||||
virtual ~QMimeTypeParserBase() {}
|
||||
|
||||
bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage);
|
||||
|
||||
static bool parseNumber(QStringView n, int *target, QString *errorMessage);
|
||||
|
||||
protected:
|
||||
virtual bool process(const QMimeType &t, QString *errorMessage) = 0;
|
||||
virtual bool process(const QMimeGlobPattern &t, QString *errorMessage) = 0;
|
||||
virtual void processParent(const QString &child, const QString &parent) = 0;
|
||||
virtual void processAlias(const QString &alias, const QString &name) = 0;
|
||||
virtual void processMagicMatcher(const QMimeMagicRuleMatcher &matcher) = 0;
|
||||
|
||||
private:
|
||||
enum ParseState {
|
||||
ParseBeginning,
|
||||
ParseMimeInfo,
|
||||
ParseMimeType,
|
||||
ParseComment,
|
||||
ParseGenericIcon,
|
||||
ParseIcon,
|
||||
ParseGlobPattern,
|
||||
ParseGlobDeleteAll,
|
||||
ParseSubClass,
|
||||
ParseAlias,
|
||||
ParseMagic,
|
||||
ParseMagicMatchRule,
|
||||
ParseOtherMimeTypeSubTag,
|
||||
ParseError
|
||||
};
|
||||
|
||||
static ParseState nextState(ParseState currentState, QStringView startElement);
|
||||
};
|
||||
|
||||
|
||||
class QMimeTypeParser : public QMimeTypeParserBase
|
||||
{
|
||||
public:
|
||||
explicit QMimeTypeParser(QMimeXMLProvider &provider) : m_provider(provider) {}
|
||||
|
||||
protected:
|
||||
inline bool process(const QMimeType &t, QString *) override
|
||||
{ m_provider.addMimeType(t); return true; }
|
||||
|
||||
inline bool process(const QMimeGlobPattern &glob, QString *) override
|
||||
{ m_provider.addGlobPattern(glob); return true; }
|
||||
|
||||
inline void processParent(const QString &child, const QString &parent) override
|
||||
{ m_provider.addParent(child, parent); }
|
||||
|
||||
inline void processAlias(const QString &alias, const QString &name) override
|
||||
{ m_provider.addAlias(alias, name); }
|
||||
|
||||
inline void processMagicMatcher(const QMimeMagicRuleMatcher &matcher) override
|
||||
{ m_provider.addMagicMatcher(matcher); }
|
||||
|
||||
private:
|
||||
QMimeXMLProvider &m_provider;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // MIMETYPEPARSER_P_H
|
Reference in New Issue
Block a user