forked from qt-creator/qt-creator
Utils: Remove old MIME database implementation
The new one has been in use for long enough now, that we don't need the old one for regression testing anymore. Change-Id: I20a4a1dae483b1fee345ea4ec8e31509f6877047 Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -195,17 +195,9 @@ add_qtc_library(Utils
|
|||||||
wizardpage.cpp wizardpage.h
|
wizardpage.cpp wizardpage.h
|
||||||
)
|
)
|
||||||
|
|
||||||
option(QTC_USE_NEW_MIMEDATABASE "Use updated MIME database implementation" YES)
|
|
||||||
|
|
||||||
if(QTC_USE_NEW_MIMEDATABASE)
|
|
||||||
set(mime_prefix "mimetypes2")
|
|
||||||
else()
|
|
||||||
set(mime_prefix "mimetypes")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
extend_qtc_library(Utils
|
extend_qtc_library(Utils
|
||||||
SOURCES_PREFIX ${mime_prefix}
|
SOURCES_PREFIX mimetypes2
|
||||||
PUBLIC_INCLUDES ${mime_prefix}
|
PUBLIC_INCLUDES mimetypes2
|
||||||
SOURCES
|
SOURCES
|
||||||
mimedatabase.cpp
|
mimedatabase.cpp
|
||||||
mimedatabase.h
|
mimedatabase.h
|
||||||
|
@@ -1,586 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#include <qplatformdefs.h> // always first
|
|
||||||
|
|
||||||
#include "mimedatabase.h"
|
|
||||||
|
|
||||||
#include "mimedatabase_p.h"
|
|
||||||
|
|
||||||
#include "mimemagicrule_p.h"
|
|
||||||
#include "mimeprovider_p.h"
|
|
||||||
#include "mimetype_p.h"
|
|
||||||
|
|
||||||
#include <utils/fileutils.h>
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QSet>
|
|
||||||
#include <QBuffer>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QStack>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
using namespace Utils::Internal;
|
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(MimeDatabasePrivate, staticMimeDatabase)
|
|
||||||
|
|
||||||
MimeDatabasePrivate *MimeDatabasePrivate::instance()
|
|
||||||
{
|
|
||||||
return staticMimeDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeDatabasePrivate::MimeDatabasePrivate()
|
|
||||||
: m_provider(nullptr), m_defaultMimeType(QLatin1String("application/octet-stream"))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeDatabasePrivate::~MimeDatabasePrivate()
|
|
||||||
{
|
|
||||||
delete m_provider;
|
|
||||||
m_provider = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeProviderBase *MimeDatabasePrivate::provider()
|
|
||||||
{
|
|
||||||
if (!m_provider) {
|
|
||||||
// MimeProviderBase *binaryProvider = new MimeBinaryProvider(this);
|
|
||||||
// if (binaryProvider->isValid()) {
|
|
||||||
// m_provider = binaryProvider;
|
|
||||||
// } else {
|
|
||||||
// delete binaryProvider;
|
|
||||||
m_provider = new MimeXMLProvider(this);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
return m_provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeDatabasePrivate::setProvider(MimeProviderBase *theProvider)
|
|
||||||
{
|
|
||||||
delete m_provider;
|
|
||||||
m_provider = theProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\internal
|
|
||||||
Returns a MIME type or an invalid one if none found
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias)
|
|
||||||
{
|
|
||||||
return provider()->mimeTypeForName(provider()->resolveAlias(nameOrAlias));
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList MimeDatabasePrivate::mimeTypeForFileName(const QString &fileName, QString *foundSuffix)
|
|
||||||
{
|
|
||||||
if (fileName.endsWith(QLatin1Char('/')))
|
|
||||||
return QStringList("inode/directory");
|
|
||||||
|
|
||||||
const QStringList matchingMimeTypes = provider()->findByFileName(QFileInfo(fileName).fileName(), foundSuffix);
|
|
||||||
return matchingMimeTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 32 bytes (see shared-mime spec)
|
|
||||||
const char *p = data.constData();
|
|
||||||
const char *e = p + qMin(32, data.size());
|
|
||||||
for ( ; p < e; ++p) {
|
|
||||||
if ((unsigned char)(*p) < 32 && *p != 9 && *p !=10 && *p != 13)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType MimeDatabasePrivate::findByData(const QByteArray &data, int *accuracyPtr)
|
|
||||||
{
|
|
||||||
// if (data.isEmpty()) {
|
|
||||||
// *accuracyPtr = 100;
|
|
||||||
// return mimeTypeForName(QLatin1String("application/x-zerosize"));
|
|
||||||
// }
|
|
||||||
|
|
||||||
*accuracyPtr = 0;
|
|
||||||
MimeType candidate = provider()->findByMagic(data, accuracyPtr);
|
|
||||||
|
|
||||||
if (candidate.isValid())
|
|
||||||
return candidate;
|
|
||||||
|
|
||||||
if (isTextFile(data)) {
|
|
||||||
*accuracyPtr = 5;
|
|
||||||
return mimeTypeForName(QLatin1String("text/plain"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return mimeTypeForName(defaultMimeType());
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType MimeDatabasePrivate::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
|
|
||||||
QStringList candidatesByName = mimeTypeForFileName(fileName);
|
|
||||||
if (candidatesByName.count() == 1) {
|
|
||||||
*accuracyPtr = 100;
|
|
||||||
const MimeType mime = mimeTypeForName(candidatesByName.at(0));
|
|
||||||
if (mime.isValid())
|
|
||||||
return mime;
|
|
||||||
candidatesByName.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension is unknown, or matches multiple mimetypes.
|
|
||||||
// Pass 2) Match on content, if we can read the data
|
|
||||||
const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
|
|
||||||
if (device->isOpen()) {
|
|
||||||
|
|
||||||
// Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h).
|
|
||||||
// This is much faster than seeking back and forth into QIODevice.
|
|
||||||
const QByteArray data = device->peek(16384);
|
|
||||||
|
|
||||||
if (openedByUs)
|
|
||||||
device->close();
|
|
||||||
|
|
||||||
int magicAccuracy = 0;
|
|
||||||
MimeType 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 glob match, use it
|
|
||||||
if (candidatesByName.contains(sniffedMime)) {
|
|
||||||
*accuracyPtr = 100;
|
|
||||||
return candidateByData;
|
|
||||||
}
|
|
||||||
// If there is a glob match that is a sub class of sniffedMime, use it
|
|
||||||
for (const QString &m : std::as_const(candidatesByName)) {
|
|
||||||
if (inherits(m, sniffedMime)) {
|
|
||||||
// We have magic + pattern pointing to this, so it's a pretty good match
|
|
||||||
*accuracyPtr = 100;
|
|
||||||
return mimeTypeForName(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*accuracyPtr = magicAccuracy;
|
|
||||||
return candidateByData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (candidatesByName.count() > 1) {
|
|
||||||
*accuracyPtr = 20;
|
|
||||||
candidatesByName.sort(); // to make it deterministic
|
|
||||||
const MimeType mime = mimeTypeForName(candidatesByName.at(0));
|
|
||||||
if (mime.isValid())
|
|
||||||
return mime;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mimeTypeForName(defaultMimeType());
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<MimeType> MimeDatabasePrivate::allMimeTypes()
|
|
||||||
{
|
|
||||||
return provider()->allMimeTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MimeDatabasePrivate::inherits(const QString &mime, const QString &parent)
|
|
||||||
{
|
|
||||||
const QString resolvedParent = provider()->resolveAlias(parent);
|
|
||||||
//Q_ASSERT(provider()->resolveAlias(mime) == mime);
|
|
||||||
QStack<QString> toCheck;
|
|
||||||
QSet<QString> seen; // avoid endless loop on bogus mime data
|
|
||||||
toCheck.push(mime);
|
|
||||||
seen.insert(mime);
|
|
||||||
while (!toCheck.isEmpty()) {
|
|
||||||
const QString current = toCheck.pop();
|
|
||||||
if (current == resolvedParent)
|
|
||||||
return true;
|
|
||||||
const QStringList parents = provider()->parents(current);
|
|
||||||
for (const QString &par : parents) {
|
|
||||||
int seenSize = seen.size();
|
|
||||||
seen.insert(par);
|
|
||||||
if (seen.size() != seenSize) // haven't seen before, so add
|
|
||||||
toCheck.push(par);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\class MimeDatabase
|
|
||||||
\inmodule QtCore
|
|
||||||
\brief The MimeDatabase 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 and Mac OS X 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
|
|
||||||
\code
|
|
||||||
QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/packages"),
|
|
||||||
QStandardPaths::LocateDirectory);
|
|
||||||
\endcode
|
|
||||||
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:
|
|
||||||
\code
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
|
||||||
<mime-type type="application/vnd.qt.qmakeprofile">
|
|
||||||
<comment xml:lang="en">Qt qmake Profile</comment>
|
|
||||||
<glob pattern="*.pro" weight="50"/>
|
|
||||||
</mime-type>
|
|
||||||
</mime-info>
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
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 MimeType
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn MimeDatabase::MimeDatabase();
|
|
||||||
Constructs a MimeDatabase object.
|
|
||||||
|
|
||||||
It is perfectly OK to create an instance of MimeDatabase 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).
|
|
||||||
*/
|
|
||||||
MimeDatabase::MimeDatabase() :
|
|
||||||
d(staticMimeDatabase())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn MimeDatabase::~MimeDatabase();
|
|
||||||
Destroys the MimeDatabase object.
|
|
||||||
*/
|
|
||||||
MimeDatabase::~MimeDatabase()
|
|
||||||
{
|
|
||||||
d = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const;
|
|
||||||
Returns a MIME type for \a nameOrAlias or an invalid one if none found.
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
|
|
||||||
if (d->m_startupPhase <= MimeDatabase::PluginsInitializing)
|
|
||||||
qWarning("Accessing MimeDatabase for %s before plugins are initialized", qPrintable(nameOrAlias));
|
|
||||||
|
|
||||||
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 MimeType::isDefault(), mimeTypeForData()
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
|
|
||||||
if (fileInfo.isDir())
|
|
||||||
return d->mimeTypeForName(QLatin1String("inode/directory"));
|
|
||||||
|
|
||||||
QFile file(fileInfo.absoluteFilePath());
|
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
|
||||||
// Cannot access statBuf.st_mode from the filesystem engine, so we have to stat again.
|
|
||||||
const QByteArray nativeFilePath = QFile::encodeName(file.fileName());
|
|
||||||
QT_STATBUF statBuffer;
|
|
||||||
if (QT_LSTAT(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(fileInfo.absoluteFilePath(), &file, &priority);
|
|
||||||
case MatchExtension:
|
|
||||||
locker.unlock();
|
|
||||||
return mimeTypeForFile(fileInfo.absoluteFilePath(), mode);
|
|
||||||
case MatchContent:
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) const
|
|
||||||
{
|
|
||||||
if (mode == MatchExtension) {
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
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.
|
|
||||||
matches.sort(); // Make it deterministic
|
|
||||||
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<MimeType> MimeDatabase::mimeTypesForFileName(const QString &fileName) const
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
|
|
||||||
QStringList matches = d->mimeTypeForFileName(fileName);
|
|
||||||
QList<MimeType> mimes;
|
|
||||||
matches.sort(); // Make it deterministic
|
|
||||||
for (const QString &mime : std::as_const(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 MimeDatabase::suffixForFileName(const QString &fileName) const
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
QString foundSuffix;
|
|
||||||
d->mimeTypeForFileName(fileName, &foundSuffix);
|
|
||||||
return foundSuffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabase::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.
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabase::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 MimeType 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.
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabase::mimeTypeForUrl(const QUrl &url) const
|
|
||||||
{
|
|
||||||
if (url.isLocalFile())
|
|
||||||
return mimeTypeForFile(url.toLocalFile());
|
|
||||||
|
|
||||||
const QString scheme = url.scheme();
|
|
||||||
if (scheme.startsWith(QLatin1String("http")))
|
|
||||||
return mimeTypeForName(d->defaultMimeType());
|
|
||||||
|
|
||||||
return mimeTypeForFile(url.path());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const
|
|
||||||
{
|
|
||||||
int accuracy = 0;
|
|
||||||
const MimeType result = d->mimeTypeForFileNameAndData(fileName, device, &accuracy);
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
MimeType MimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const
|
|
||||||
{
|
|
||||||
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<MimeType> MimeDatabase::allMimeTypes() const
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
|
|
||||||
return d->allMimeTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\enum MimeDatabase::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
|
|
||||||
*/
|
|
@@ -1,57 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mimetype.h"
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QFileInfo;
|
|
||||||
class QIODevice;
|
|
||||||
class QUrl;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
|
|
||||||
class MimeDatabase
|
|
||||||
{
|
|
||||||
Q_DISABLE_COPY(MimeDatabase)
|
|
||||||
|
|
||||||
public:
|
|
||||||
MimeDatabase();
|
|
||||||
~MimeDatabase();
|
|
||||||
|
|
||||||
MimeType mimeTypeForName(const QString &nameOrAlias) const;
|
|
||||||
|
|
||||||
enum MatchMode { MatchDefault = 0x0, MatchExtension = 0x1, MatchContent = 0x2 };
|
|
||||||
|
|
||||||
MimeType mimeTypeForFile(const QString &fileName, MatchMode mode = MatchDefault) const;
|
|
||||||
MimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode = MatchDefault) const;
|
|
||||||
QList<MimeType> mimeTypesForFileName(const QString &fileName) const;
|
|
||||||
|
|
||||||
MimeType mimeTypeForData(const QByteArray &data) const;
|
|
||||||
MimeType mimeTypeForData(QIODevice *device) const;
|
|
||||||
|
|
||||||
MimeType mimeTypeForUrl(const QUrl &url) const;
|
|
||||||
MimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const;
|
|
||||||
MimeType mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const;
|
|
||||||
|
|
||||||
QString suffixForFileName(const QString &fileName) const;
|
|
||||||
|
|
||||||
QList<MimeType> allMimeTypes() const;
|
|
||||||
|
|
||||||
// For debugging purposes.
|
|
||||||
enum StartupPhase {
|
|
||||||
BeforeInitialize,
|
|
||||||
PluginsLoading,
|
|
||||||
PluginsInitializing, // Register up to here.
|
|
||||||
PluginsDelayedInitializing, // Use from here on.
|
|
||||||
UpAndRunning
|
|
||||||
};
|
|
||||||
static void setStartupPhase(StartupPhase);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Internal::MimeDatabasePrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Utils
|
|
@@ -1,74 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// W A R N I N G
|
|
||||||
// -------------
|
|
||||||
//
|
|
||||||
// This file is mostly copied from Qt code and should not be touched
|
|
||||||
// unless really needed.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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/qhash.h>
|
|
||||||
#include <QtCore/qmutex.h>
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QIODevice;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
#include "mimetype.h"
|
|
||||||
#include "mimetype_p.h"
|
|
||||||
#include "mimeglobpattern_p.h"
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class MimeProviderBase;
|
|
||||||
|
|
||||||
class MimeDatabasePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Q_DISABLE_COPY(MimeDatabasePrivate)
|
|
||||||
|
|
||||||
MimeDatabasePrivate();
|
|
||||||
~MimeDatabasePrivate();
|
|
||||||
|
|
||||||
static MimeDatabasePrivate *instance();
|
|
||||||
|
|
||||||
MimeProviderBase *provider();
|
|
||||||
void setProvider(MimeProviderBase *theProvider);
|
|
||||||
|
|
||||||
inline QString defaultMimeType() const { return m_defaultMimeType; }
|
|
||||||
|
|
||||||
bool inherits(const QString &mime, const QString &parent);
|
|
||||||
|
|
||||||
QList<MimeType> allMimeTypes();
|
|
||||||
|
|
||||||
|
|
||||||
MimeType mimeTypeForName(const QString &nameOrAlias);
|
|
||||||
MimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device, int *priorityPtr);
|
|
||||||
MimeType findByData(const QByteArray &data, int *priorityPtr);
|
|
||||||
QStringList mimeTypeForFileName(const QString &fileName, QString *foundSuffix = nullptr);
|
|
||||||
|
|
||||||
mutable MimeProviderBase *m_provider;
|
|
||||||
const QString m_defaultMimeType;
|
|
||||||
QMutex mutex;
|
|
||||||
|
|
||||||
int m_startupPhase = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
} // Utils
|
|
@@ -1,241 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#include "mimeglobpattern_p.h"
|
|
||||||
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
using namespace Utils::Internal;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\internal
|
|
||||||
\class MimeGlobMatchResult
|
|
||||||
\inmodule QtCore
|
|
||||||
\brief The MimeGlobMatchResult class accumulates results from glob matching.
|
|
||||||
|
|
||||||
Handles glob weights, and preferring longer matches over shorter matches.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void MimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const QString &pattern)
|
|
||||||
{
|
|
||||||
// Is this a lower-weight pattern than the last match? Skip this match then.
|
|
||||||
if (weight < m_weight)
|
|
||||||
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 (pattern.startsWith(QLatin1String("*.")))
|
|
||||||
m_foundSuffix = pattern.mid(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeGlobPattern::PatternType MimeGlobPattern::detectPatternType(const QString &pattern) const
|
|
||||||
{
|
|
||||||
const int patternLength = pattern.length();
|
|
||||||
if (!patternLength)
|
|
||||||
return OtherPattern;
|
|
||||||
|
|
||||||
const bool starCount = pattern.count(QLatin1Char('*')) == 1;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
// Names without any wildcards like "README"
|
|
||||||
if (starCount == 0)
|
|
||||||
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 MimeGlobPattern
|
|
||||||
\inmodule QtCore
|
|
||||||
\brief The MimeGlobPattern class contains the glob pattern for file names for MIME type matching.
|
|
||||||
|
|
||||||
\sa MimeType, MimeDatabase, MimeMagicRuleMatcher, MimeMagicRule
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool MimeGlobPattern::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
|
|
||||||
const QRegularExpression rx(QRegularExpression::anchoredPattern(
|
|
||||||
QRegularExpression::wildcardToRegularExpression(m_pattern)));
|
|
||||||
return rx.match(fileName).hasMatch();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 MimeAllGlobPatterns::addGlob(const MimeGlobPattern &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 MimeAllGlobPatterns::removeMimeType(const QString &mimeType)
|
|
||||||
{
|
|
||||||
for (QStringList &x : m_fastPatterns)
|
|
||||||
x.removeAll(mimeType);
|
|
||||||
|
|
||||||
m_highWeightGlobs.removeMimeType(mimeType);
|
|
||||||
m_lowWeightGlobs.removeMimeType(mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeGlobPatternList::match(MimeGlobMatchResult &result,
|
|
||||||
const QString &fileName) const
|
|
||||||
{
|
|
||||||
for (const MimeGlobPattern &glob : *this) {
|
|
||||||
if (glob.matchFileName(fileName))
|
|
||||||
result.addMatch(glob.mimeType(), glob.weight(), glob.pattern());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList MimeAllGlobPatterns::matchingGlobs(const QString &fileName, QString *foundSuffix) const
|
|
||||||
{
|
|
||||||
// First try the high weight matches (>50), if any.
|
|
||||||
MimeGlobMatchResult result;
|
|
||||||
m_highWeightGlobs.match(result, fileName);
|
|
||||||
if (result.m_matchingMimeTypes.isEmpty()) {
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
for (const QString &mime : matchingMimeTypes)
|
|
||||||
result.addMatch(mime, 50, QLatin1String("*.") + simpleExtension);
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
if (foundSuffix)
|
|
||||||
*foundSuffix = result.m_foundSuffix;
|
|
||||||
return result.m_matchingMimeTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeAllGlobPatterns::clear()
|
|
||||||
{
|
|
||||||
m_fastPatterns.clear();
|
|
||||||
m_highWeightGlobs.clear();
|
|
||||||
m_lowWeightGlobs.clear();
|
|
||||||
}
|
|
@@ -1,135 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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/qstringlist.h>
|
|
||||||
#include <QtCore/qhash.h>
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
struct MimeGlobMatchResult
|
|
||||||
{
|
|
||||||
void addMatch(const QString &mimeType, int weight, const QString &pattern);
|
|
||||||
|
|
||||||
QStringList m_matchingMimeTypes;
|
|
||||||
int m_weight = 0;
|
|
||||||
int m_matchingPatternLength = 0;
|
|
||||||
QString m_foundSuffix;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MimeGlobPattern
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static const unsigned MaxWeight = 100;
|
|
||||||
static const unsigned DefaultWeight = 50;
|
|
||||||
static const unsigned MinWeight = 1;
|
|
||||||
|
|
||||||
explicit MimeGlobPattern(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(MimeGlobPattern &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;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MimeGlobPatternList : public QList<MimeGlobPattern>
|
|
||||||
{
|
|
||||||
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 MimeGlobPattern &pattern) {
|
|
||||||
return pattern.mimeType() == mimeType;
|
|
||||||
};
|
|
||||||
erase(std::remove_if(begin(), end(), isMimeTypeEqual), end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void match(MimeGlobMatchResult &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 MimeAllGlobPatterns
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef QHash<QString, QStringList> PatternsMap; // MIME type -> patterns
|
|
||||||
|
|
||||||
void addGlob(const MimeGlobPattern &glob);
|
|
||||||
void removeMimeType(const QString &mimeType);
|
|
||||||
QStringList matchingGlobs(const QString &fileName, QString *foundSuffix) const;
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
PatternsMap m_fastPatterns; // example: "doc" -> "application/msword", "text/plain"
|
|
||||||
MimeGlobPatternList m_highWeightGlobs;
|
|
||||||
MimeGlobPatternList m_lowWeightGlobs; // <= 50, including the non-fast 50 patterns
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
} // Utils
|
|
@@ -1,403 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#include "mimemagicrule_p.h"
|
|
||||||
|
|
||||||
#include <QtCore/QList>
|
|
||||||
#include <QtCore/QRegularExpression>
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <qendian.h>
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
using namespace Utils::Internal;
|
|
||||||
|
|
||||||
// in the same order as Type!
|
|
||||||
static const char magicRuleTypes_string[] =
|
|
||||||
"invalid\0"
|
|
||||||
"string\0"
|
|
||||||
"regexp\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, 36, 42, 48, 57, 66, 71, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
MimeMagicRule::Type MimeMagicRule::type(const QByteArray &theTypeName)
|
|
||||||
{
|
|
||||||
for (int i = String; i <= Byte; ++i) {
|
|
||||||
if (theTypeName == magicRuleTypes_string + magicRuleTypes_indices[i])
|
|
||||||
return Type(i);
|
|
||||||
}
|
|
||||||
return Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray MimeMagicRule::typeName(MimeMagicRule::Type theType)
|
|
||||||
{
|
|
||||||
return magicRuleTypes_string + magicRuleTypes_indices[theType];
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class MimeMagicRulePrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool operator==(const MimeMagicRulePrivate &other) const;
|
|
||||||
|
|
||||||
MimeMagicRule::Type type;
|
|
||||||
QByteArray value;
|
|
||||||
int startPos;
|
|
||||||
int endPos;
|
|
||||||
QByteArray mask;
|
|
||||||
|
|
||||||
QRegularExpression regexp;
|
|
||||||
QByteArray pattern;
|
|
||||||
quint32 number;
|
|
||||||
quint32 numberMask;
|
|
||||||
|
|
||||||
using MatchFunction = bool (*)(const MimeMagicRulePrivate*, const QByteArray&);
|
|
||||||
MatchFunction matchFunction;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool MimeMagicRulePrivate::operator==(const MimeMagicRulePrivate &other) const
|
|
||||||
{
|
|
||||||
return type == other.type &&
|
|
||||||
value == other.value &&
|
|
||||||
startPos == other.startPos &&
|
|
||||||
endPos == other.endPos &&
|
|
||||||
mask == other.mask &&
|
|
||||||
pattern == other.pattern &&
|
|
||||||
number == other.number &&
|
|
||||||
numberMask == other.numberMask &&
|
|
||||||
matchFunction == other.matchFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
} // Utils
|
|
||||||
|
|
||||||
// Used by both providers
|
|
||||||
bool MimeMagicRule::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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool matchString(const MimeMagicRulePrivate *d, const QByteArray &data)
|
|
||||||
{
|
|
||||||
const int rangeLength = d->endPos - d->startPos + 1;
|
|
||||||
return MimeMagicRule::matchSubstring(data.constData(), data.size(), d->startPos, rangeLength, d->pattern.size(), d->pattern.constData(), d->mask.constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool matchRegExp(const MimeMagicRulePrivate *d, const QByteArray &data)
|
|
||||||
{
|
|
||||||
const QString str = QString::fromUtf8(data);
|
|
||||||
int length = d->endPos;
|
|
||||||
if (length == d->startPos)
|
|
||||||
length = -1; // from startPos to end of string
|
|
||||||
const QString subStr = str.left(length);
|
|
||||||
return d->regexp.match(subStr, d->startPos).hasMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static bool matchNumber(const MimeMagicRulePrivate *d, const QByteArray &data)
|
|
||||||
{
|
|
||||||
const T value(d->number);
|
|
||||||
const T mask(d->numberMask);
|
|
||||||
|
|
||||||
//qDebug() << "matchNumber" << "0x" << QString::number(d->number, 16) << "size" << sizeof(T);
|
|
||||||
//qDebug() << "mask" << QString::number(d->numberMask, 16);
|
|
||||||
|
|
||||||
const char *p = data.constData() + d->startPos;
|
|
||||||
const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), d->endPos + 1);
|
|
||||||
for ( ; p <= e; ++p) {
|
|
||||||
if ((*reinterpret_cast<const 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 { // escaped
|
|
||||||
*data++ = *p;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*data++ = *p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pattern.truncate(data - pattern.data());
|
|
||||||
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeMagicRule::MimeMagicRule(MimeMagicRule::Type theType,
|
|
||||||
const QByteArray &theValue,
|
|
||||||
int theStartPos,
|
|
||||||
int theEndPos,
|
|
||||||
const QByteArray &theMask,
|
|
||||||
QString *errorString) :
|
|
||||||
d(new MimeMagicRulePrivate)
|
|
||||||
{
|
|
||||||
d->type = theType;
|
|
||||||
d->value = theValue;
|
|
||||||
d->startPos = theStartPos;
|
|
||||||
d->endPos = theEndPos;
|
|
||||||
d->mask = theMask;
|
|
||||||
d->matchFunction = nullptr;
|
|
||||||
|
|
||||||
if (d->value.isEmpty()) {
|
|
||||||
d->type = Invalid;
|
|
||||||
if (errorString)
|
|
||||||
*errorString = QLatin1String("Invalid empty magic rule value");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d->type >= Host16 && d->type <= Byte) {
|
|
||||||
bool ok;
|
|
||||||
d->number = d->value.toUInt(&ok, 0); // autodetect
|
|
||||||
if (!ok) {
|
|
||||||
d->type = Invalid;
|
|
||||||
if (errorString)
|
|
||||||
*errorString = QString::fromLatin1("Invalid magic rule value \"%1\"").arg(
|
|
||||||
QString::fromLatin1(d->value));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
d->numberMask = !d->mask.isEmpty() ? d->mask.toUInt(&ok, 0) : 0; // autodetect
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (d->type) {
|
|
||||||
case String:
|
|
||||||
d->pattern = makePattern(d->value);
|
|
||||||
d->pattern.squeeze();
|
|
||||||
if (!d->mask.isEmpty()) {
|
|
||||||
if (d->mask.size() < 4 || !d->mask.startsWith("0x")) {
|
|
||||||
d->type = Invalid;
|
|
||||||
if (errorString)
|
|
||||||
*errorString = QString::fromLatin1("Invalid magic rule mask \"%1\"").arg(
|
|
||||||
QString::fromLatin1(d->mask));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const QByteArray &tempMask = QByteArray::fromHex(QByteArray::fromRawData(
|
|
||||||
d->mask.constData() + 2, d->mask.size() - 2));
|
|
||||||
if (tempMask.size() != d->pattern.size()) {
|
|
||||||
d->type = Invalid;
|
|
||||||
if (errorString)
|
|
||||||
*errorString = QString::fromLatin1("Invalid magic rule mask size \"%1\"").arg(
|
|
||||||
QString::fromLatin1(d->mask));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
d->mask = tempMask;
|
|
||||||
} else {
|
|
||||||
d->mask.fill(char(-1), d->pattern.size());
|
|
||||||
}
|
|
||||||
d->mask.squeeze();
|
|
||||||
d->matchFunction = matchString;
|
|
||||||
break;
|
|
||||||
case RegExp:
|
|
||||||
d->regexp.setPatternOptions(QRegularExpression::MultilineOption
|
|
||||||
| QRegularExpression::DotMatchesEverythingOption
|
|
||||||
);
|
|
||||||
d->regexp.setPattern(QString::fromUtf8(d->value));
|
|
||||||
if (!d->regexp.isValid()) {
|
|
||||||
d->type = Invalid;
|
|
||||||
if (errorString)
|
|
||||||
*errorString = QString::fromLatin1("Invalid magic rule regexp value \"%1\"").arg(
|
|
||||||
QString::fromLatin1(d->value));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
d->matchFunction = matchRegExp;
|
|
||||||
break;
|
|
||||||
case Byte:
|
|
||||||
if (d->number <= quint8(-1)) {
|
|
||||||
if (d->numberMask == 0)
|
|
||||||
d->numberMask = quint8(-1);
|
|
||||||
d->matchFunction = matchNumber<quint8>;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Big16:
|
|
||||||
case Host16:
|
|
||||||
case Little16:
|
|
||||||
if (d->number <= quint16(-1)) {
|
|
||||||
d->number = d->type == Little16 ? qFromLittleEndian<quint16>(d->number) : qFromBigEndian<quint16>(d->number);
|
|
||||||
if (d->numberMask == 0)
|
|
||||||
d->numberMask = quint16(-1);
|
|
||||||
d->matchFunction = matchNumber<quint16>;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Big32:
|
|
||||||
case Host32:
|
|
||||||
case Little32:
|
|
||||||
if (d->number <= quint32(-1)) {
|
|
||||||
d->number = d->type == Little32 ? qFromLittleEndian<quint32>(d->number) : qFromBigEndian<quint32>(d->number);
|
|
||||||
if (d->numberMask == 0)
|
|
||||||
d->numberMask = quint32(-1);
|
|
||||||
d->matchFunction = matchNumber<quint32>;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeMagicRule::MimeMagicRule(const MimeMagicRule &other)
|
|
||||||
: m_subMatches(other.m_subMatches)
|
|
||||||
, d(new MimeMagicRulePrivate(*other.d))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeMagicRule::~MimeMagicRule() = default;
|
|
||||||
|
|
||||||
MimeMagicRule &MimeMagicRule::operator=(const MimeMagicRule &other)
|
|
||||||
{
|
|
||||||
*d = *other.d;
|
|
||||||
m_subMatches = other.m_subMatches;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MimeMagicRule::operator==(const MimeMagicRule &other) const
|
|
||||||
{
|
|
||||||
return (d == other.d || *d == *other.d) && m_subMatches == other.m_subMatches;
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeMagicRule::Type MimeMagicRule::type() const
|
|
||||||
{
|
|
||||||
return d->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray MimeMagicRule::value() const
|
|
||||||
{
|
|
||||||
return d->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MimeMagicRule::startPos() const
|
|
||||||
{
|
|
||||||
return d->startPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MimeMagicRule::endPos() const
|
|
||||||
{
|
|
||||||
return d->endPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray MimeMagicRule::mask() const
|
|
||||||
{
|
|
||||||
QByteArray result = d->mask;
|
|
||||||
if (d->type == String) {
|
|
||||||
// restore '0x'
|
|
||||||
result = "0x" + result.toHex();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MimeMagicRule::isValid() const
|
|
||||||
{
|
|
||||||
return d->matchFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MimeMagicRule::matches(const QByteArray &data) const
|
|
||||||
{
|
|
||||||
const bool ok = d->matchFunction && d->matchFunction(d.data(), 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<MimeMagicRule>::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;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@@ -1,71 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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 <utils/utils_global.h>
|
|
||||||
|
|
||||||
#include <QtCore/qbytearray.h>
|
|
||||||
#include <QtCore/qscopedpointer.h>
|
|
||||||
#include <QtCore/qlist.h>
|
|
||||||
#include <QtCore/qmap.h>
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
|
|
||||||
class MimeType;
|
|
||||||
|
|
||||||
namespace Internal {
|
|
||||||
class MimeMagicRulePrivate;
|
|
||||||
}
|
|
||||||
|
|
||||||
class QTCREATOR_UTILS_EXPORT MimeMagicRule
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum Type { Invalid = 0, String, RegExp, Host16, Host32, Big16, Big32, Little16, Little32, Byte };
|
|
||||||
|
|
||||||
MimeMagicRule(Type type, const QByteArray &value, int startPos, int endPos,
|
|
||||||
const QByteArray &mask = QByteArray(), QString *errorString = nullptr);
|
|
||||||
MimeMagicRule(const MimeMagicRule &other);
|
|
||||||
~MimeMagicRule();
|
|
||||||
|
|
||||||
MimeMagicRule &operator=(const MimeMagicRule &other);
|
|
||||||
|
|
||||||
bool operator==(const MimeMagicRule &other) const;
|
|
||||||
|
|
||||||
Type type() const;
|
|
||||||
QByteArray value() const;
|
|
||||||
int startPos() const;
|
|
||||||
int endPos() const;
|
|
||||||
QByteArray mask() const;
|
|
||||||
|
|
||||||
bool isValid() const;
|
|
||||||
|
|
||||||
bool matches(const QByteArray &data) const;
|
|
||||||
|
|
||||||
QList<MimeMagicRule> 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:
|
|
||||||
const QScopedPointer<Internal::MimeMagicRulePrivate> d;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Utils
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
Q_DECLARE_TYPEINFO(Utils::MimeMagicRule, Q_MOVABLE_TYPE);
|
|
||||||
QT_END_NAMESPACE
|
|
@@ -1,67 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#include "mimemagicrulematcher_p.h"
|
|
||||||
|
|
||||||
#include "mimetype_p.h"
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
using namespace Utils::Internal;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\internal
|
|
||||||
\class MimeMagicRuleMatcher
|
|
||||||
\inmodule QtCore
|
|
||||||
|
|
||||||
\brief The MimeMagicRuleMatcher class checks a number of rules based on operator "or".
|
|
||||||
|
|
||||||
It is used for rules parsed from XML files.
|
|
||||||
|
|
||||||
\sa MimeType, MimeDatabase, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
|
||||||
\sa MimeTypeParserBase, MimeTypeParser
|
|
||||||
*/
|
|
||||||
|
|
||||||
MimeMagicRuleMatcher::MimeMagicRuleMatcher(const QString &mime, unsigned thePriority) :
|
|
||||||
m_list(),
|
|
||||||
m_priority(thePriority),
|
|
||||||
m_mimetype(mime)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MimeMagicRuleMatcher::operator==(const MimeMagicRuleMatcher &other) const
|
|
||||||
{
|
|
||||||
return m_list == other.m_list &&
|
|
||||||
m_priority == other.m_priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeMagicRuleMatcher::addRule(const MimeMagicRule &rule)
|
|
||||||
{
|
|
||||||
m_list.append(rule);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeMagicRuleMatcher::addRules(const QList<MimeMagicRule> &rules)
|
|
||||||
{
|
|
||||||
m_list.append(rules);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<MimeMagicRule> MimeMagicRuleMatcher::magicRules() const
|
|
||||||
{
|
|
||||||
return m_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a match on contents of a file
|
|
||||||
bool MimeMagicRuleMatcher::matches(const QByteArray &data) const
|
|
||||||
{
|
|
||||||
for (const MimeMagicRule &magicRule : m_list) {
|
|
||||||
if (magicRule.matches(data))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a priority value from 1..100
|
|
||||||
unsigned MimeMagicRuleMatcher::priority() const
|
|
||||||
{
|
|
||||||
return m_priority;
|
|
||||||
}
|
|
@@ -1,51 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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 "mimemagicrule_p.h"
|
|
||||||
|
|
||||||
#include <QtCore/qbytearray.h>
|
|
||||||
#include <QtCore/qlist.h>
|
|
||||||
#include <QtCore/qstring.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class MimeMagicRuleMatcher
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit MimeMagicRuleMatcher(const QString &mime, unsigned priority = 65535);
|
|
||||||
|
|
||||||
bool operator==(const MimeMagicRuleMatcher &other) const;
|
|
||||||
|
|
||||||
void addRule(const MimeMagicRule &rule);
|
|
||||||
void addRules(const QList<MimeMagicRule> &rules);
|
|
||||||
QList<MimeMagicRule> magicRules() const;
|
|
||||||
|
|
||||||
bool matches(const QByteArray &data) const;
|
|
||||||
|
|
||||||
unsigned priority() const;
|
|
||||||
|
|
||||||
QString mimetype() const { return m_mimetype; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<MimeMagicRule> m_list;
|
|
||||||
unsigned m_priority;
|
|
||||||
QString m_mimetype;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
} // Utils
|
|
@@ -1,871 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#include "mimeprovider_p.h"
|
|
||||||
|
|
||||||
#include "mimetypeparser_p.h"
|
|
||||||
#include <qstandardpaths.h>
|
|
||||||
#include "mimemagicrulematcher_p.h"
|
|
||||||
|
|
||||||
#include <QXmlStreamReader>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QByteArrayMatcher>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QtEndian>
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
using namespace Utils::Internal;
|
|
||||||
|
|
||||||
static QString fallbackParent(const QString &mimeTypeName)
|
|
||||||
{
|
|
||||||
const QString myGroup = 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeProviderBase::MimeProviderBase(MimeDatabasePrivate *db)
|
|
||||||
: m_db(db)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mime_secondsBetweenChecks = 5;
|
|
||||||
|
|
||||||
bool MimeProviderBase::shouldCheck()
|
|
||||||
{
|
|
||||||
const QDateTime now = QDateTime::currentDateTime();
|
|
||||||
if (m_lastCheck.isValid() && m_lastCheck.secsTo(now) < mime_secondsBetweenChecks)
|
|
||||||
return false;
|
|
||||||
m_lastCheck = now;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//MimeBinaryProvider::MimeBinaryProvider(MimeDatabasePrivate *db)
|
|
||||||
// : MimeProviderBase(db), m_mimetypeListLoaded(false)
|
|
||||||
//{
|
|
||||||
//}
|
|
||||||
|
|
||||||
//#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
|
|
||||||
//#define QT_USE_MMAP
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
//struct MimeBinaryProvider::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;
|
|
||||||
//};
|
|
||||||
|
|
||||||
//MimeBinaryProvider::CacheFile::CacheFile(const QString &fileName)
|
|
||||||
// : file(fileName), m_valid(false)
|
|
||||||
//{
|
|
||||||
// load();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//MimeBinaryProvider::CacheFile::~CacheFile()
|
|
||||||
//{
|
|
||||||
//}
|
|
||||||
|
|
||||||
//bool MimeBinaryProvider::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 MimeBinaryProvider::CacheFile::reload()
|
|
||||||
//{
|
|
||||||
// //qDebug() << "reload!" << file->fileName();
|
|
||||||
// m_valid = false;
|
|
||||||
// if (file.isOpen()) {
|
|
||||||
// file.close();
|
|
||||||
// }
|
|
||||||
// data = 0;
|
|
||||||
// return load();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//MimeBinaryProvider::CacheFile *MimeBinaryProvider::CacheFileList::findCacheFile(const QString &fileName) const
|
|
||||||
//{
|
|
||||||
// for (const_iterator it = begin(); it != end(); ++it) {
|
|
||||||
// if ((*it)->file.fileName() == fileName)
|
|
||||||
// return *it;
|
|
||||||
// }
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//MimeBinaryProvider::~MimeBinaryProvider()
|
|
||||||
//{
|
|
||||||
// qDeleteAll(m_cacheFiles);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// 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 MimeBinaryProvider::isValid()
|
|
||||||
//{
|
|
||||||
//#if defined(QT_USE_MMAP)
|
|
||||||
// if (!qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE"))
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
// Q_ASSERT(m_cacheFiles.isEmpty()); // this method is only ever called once
|
|
||||||
// checkCache();
|
|
||||||
|
|
||||||
// if (m_cacheFiles.count() > 1)
|
|
||||||
// return true;
|
|
||||||
// if (m_cacheFiles.isEmpty())
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
// // We found exactly one file; is it the user-modified mimes, or a system file?
|
|
||||||
// const QString foundFile = m_cacheFiles.first()->file.fileName();
|
|
||||||
// const QString localCacheFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/mime/mime.cache");
|
|
||||||
|
|
||||||
// return foundFile != localCacheFile;
|
|
||||||
//#else
|
|
||||||
// return false;
|
|
||||||
//#endif
|
|
||||||
//}
|
|
||||||
|
|
||||||
//bool MimeBinaryProvider::CacheFileList::checkCacheChanged()
|
|
||||||
//{
|
|
||||||
// bool somethingChanged = false;
|
|
||||||
// QMutableListIterator<CacheFile *> it(*this);
|
|
||||||
// while (it.hasNext()) {
|
|
||||||
// CacheFile *cacheFile = it.next();
|
|
||||||
// QFileInfo fileInfo(cacheFile->file);
|
|
||||||
// if (!fileInfo.exists()) { // This can't happen by just running update-mime-database. But the user could use rm -rf :-)
|
|
||||||
// delete cacheFile;
|
|
||||||
// it.remove();
|
|
||||||
// somethingChanged = true;
|
|
||||||
// } else if (fileInfo.lastModified() > cacheFile->m_mtime) {
|
|
||||||
// if (!cacheFile->reload()) {
|
|
||||||
// delete cacheFile;
|
|
||||||
// it.remove();
|
|
||||||
// }
|
|
||||||
// somethingChanged = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return somethingChanged;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//void MimeBinaryProvider::checkCache()
|
|
||||||
//{
|
|
||||||
// if (!shouldCheck())
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// // First iterate over existing known cache files and check for uptodate
|
|
||||||
// if (m_cacheFiles.checkCacheChanged())
|
|
||||||
// m_mimetypeListLoaded = false;
|
|
||||||
|
|
||||||
// // Then check if new cache files appeared
|
|
||||||
// const QStringList cacheFileNames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/mime.cache"));
|
|
||||||
// if (cacheFileNames != m_cacheFileNames) {
|
|
||||||
// for (const QString &cacheFileName : cacheFileNames) {
|
|
||||||
// CacheFile *cacheFile = m_cacheFiles.findCacheFile(cacheFileName);
|
|
||||||
// if (!cacheFile) {
|
|
||||||
// //qDebug() << "new file:" << cacheFileName;
|
|
||||||
// cacheFile = new CacheFile(cacheFileName);
|
|
||||||
// if (cacheFile->isValid()) // verify version
|
|
||||||
// m_cacheFiles.append(cacheFile);
|
|
||||||
// else
|
|
||||||
// delete cacheFile;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// m_cacheFileNames = cacheFileNames;
|
|
||||||
// m_mimetypeListLoaded = false;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//static MimeType mimeTypeForNameUnchecked(const QString &name)
|
|
||||||
//{
|
|
||||||
// MimeTypePrivate data;
|
|
||||||
// data.name = name;
|
|
||||||
// // The rest is retrieved on demand.
|
|
||||||
// // comment and globPatterns: in loadMimeTypePrivate
|
|
||||||
// // iconName: in loadIcon
|
|
||||||
// // genericIconName: in loadGenericIcon
|
|
||||||
// return MimeType(data);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//MimeType MimeBinaryProvider::mimeTypeForName(const QString &name)
|
|
||||||
//{
|
|
||||||
// checkCache();
|
|
||||||
// if (!m_mimetypeListLoaded)
|
|
||||||
// loadMimeTypeList();
|
|
||||||
// if (!m_mimetypeNames.contains(name))
|
|
||||||
// return MimeType(); // unknown mimetype
|
|
||||||
// return mimeTypeForNameUnchecked(name);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//QStringList MimeBinaryProvider::findByFileName(const QString &fileName, QString *foundSuffix)
|
|
||||||
//{
|
|
||||||
// checkCache();
|
|
||||||
// if (fileName.isEmpty())
|
|
||||||
// return QStringList();
|
|
||||||
// const QString lowerFileName = fileName.toLower();
|
|
||||||
// MimeGlobMatchResult result;
|
|
||||||
// // TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly.
|
|
||||||
// for (CacheFile *cacheFile : std::as_const(m_cacheFiles)) {
|
|
||||||
// matchGlobList(result, cacheFile, cacheFile->getUint32(PosLiteralListOffset), fileName);
|
|
||||||
// matchGlobList(result, cacheFile, cacheFile->getUint32(PosGlobListOffset), fileName);
|
|
||||||
// const int reverseSuffixTreeOffset = cacheFile->getUint32(PosReverseSuffixTreeOffset);
|
|
||||||
// const int numRoots = cacheFile->getUint32(reverseSuffixTreeOffset);
|
|
||||||
// const int firstRootOffset = cacheFile->getUint32(reverseSuffixTreeOffset + 4);
|
|
||||||
// matchSuffixTree(result, cacheFile, numRoots, firstRootOffset, lowerFileName, fileName.length() - 1, false);
|
|
||||||
// if (result.m_matchingMimeTypes.isEmpty())
|
|
||||||
// matchSuffixTree(result, cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true);
|
|
||||||
// }
|
|
||||||
// if (foundSuffix)
|
|
||||||
// *foundSuffix = result.m_foundSuffix;
|
|
||||||
// return result.m_matchingMimeTypes;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//void MimeBinaryProvider::matchGlobList(MimeGlobMatchResult &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;
|
|
||||||
// MimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive);
|
|
||||||
|
|
||||||
// // TODO: this could be done faster for literals where a simple == would do.
|
|
||||||
// if (glob.matchFileName(fileName))
|
|
||||||
// result.addMatch(QLatin1String(mimeType), weight, pattern);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//bool MimeBinaryProvider::matchSuffixTree(MimeGlobMatchResult &result, MimeBinaryProvider::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 = 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('*') + fileName.mid(charPos+1));
|
|
||||||
// success = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return success;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//bool MimeBinaryProvider::matchMagicRule(MimeBinaryProvider::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) : NULL;
|
|
||||||
|
|
||||||
// if (!MimeMagicRule::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;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//MimeType MimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr)
|
|
||||||
//{
|
|
||||||
// checkCache();
|
|
||||||
// for (CacheFile *cacheFile : std::as_const(m_cacheFiles)) {
|
|
||||||
// const int magicListOffset = cacheFile->getUint32(PosMagicListOffset);
|
|
||||||
// const int numMatches = cacheFile->getUint32(magicListOffset);
|
|
||||||
// //const int maxExtent = cacheFile->getUint32(magicListOffset + 4);
|
|
||||||
// const int firstMatchOffset = cacheFile->getUint32(magicListOffset + 8);
|
|
||||||
|
|
||||||
// for (int i = 0; i < numMatches; ++i) {
|
|
||||||
// const int off = firstMatchOffset + i * 16;
|
|
||||||
// const int numMatchlets = cacheFile->getUint32(off + 8);
|
|
||||||
// const int firstMatchletOffset = cacheFile->getUint32(off + 12);
|
|
||||||
// if (matchMagicRule(cacheFile, numMatchlets, firstMatchletOffset, data)) {
|
|
||||||
// const int mimeTypeOffset = cacheFile->getUint32(off + 4);
|
|
||||||
// const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
|
|
||||||
// *accuracyPtr = cacheFile->getUint32(off);
|
|
||||||
// // Return the first match. We have no rules for conflicting magic data...
|
|
||||||
// // (mime.cache itself is sorted, but what about local overrides with a lower prio?)
|
|
||||||
// return mimeTypeForNameUnchecked(QLatin1String(mimeType));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return MimeType();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//QStringList MimeBinaryProvider::parents(const QString &mime)
|
|
||||||
//{
|
|
||||||
// checkCache();
|
|
||||||
// const QByteArray mimeStr = mime.toLatin1();
|
|
||||||
// QStringList result;
|
|
||||||
// for (CacheFile *cacheFile : std::as_const(m_cacheFiles)) {
|
|
||||||
// const int parentListOffset = cacheFile->getUint32(PosParentListOffset);
|
|
||||||
// const int numEntries = cacheFile->getUint32(parentListOffset);
|
|
||||||
|
|
||||||
// int begin = 0;
|
|
||||||
// int end = numEntries - 1;
|
|
||||||
// while (begin <= end) {
|
|
||||||
// const int medium = (begin + end) / 2;
|
|
||||||
// const int off = parentListOffset + 4 + 8 * medium;
|
|
||||||
// const int mimeOffset = cacheFile->getUint32(off);
|
|
||||||
// const char *aMime = cacheFile->getCharStar(mimeOffset);
|
|
||||||
// const int cmp = qstrcmp(aMime, mimeStr);
|
|
||||||
// if (cmp < 0) {
|
|
||||||
// begin = medium + 1;
|
|
||||||
// } else if (cmp > 0) {
|
|
||||||
// end = medium - 1;
|
|
||||||
// } else {
|
|
||||||
// const int parentsOffset = cacheFile->getUint32(off + 4);
|
|
||||||
// const int numParents = cacheFile->getUint32(parentsOffset);
|
|
||||||
// for (int i = 0; i < numParents; ++i) {
|
|
||||||
// const int parentOffset = cacheFile->getUint32(parentsOffset + 4 + 4 * i);
|
|
||||||
// const char *aParent = cacheFile->getCharStar(parentOffset);
|
|
||||||
// result.append(QString::fromLatin1(aParent));
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (result.isEmpty()) {
|
|
||||||
// const QString parent = fallbackParent(mime);
|
|
||||||
// if (!parent.isEmpty())
|
|
||||||
// result.append(parent);
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//QString MimeBinaryProvider::resolveAlias(const QString &name)
|
|
||||||
//{
|
|
||||||
// checkCache();
|
|
||||||
// const QByteArray input = name.toLatin1();
|
|
||||||
// for (CacheFile *cacheFile : std::as_const(m_cacheFiles)) {
|
|
||||||
// const int aliasListOffset = cacheFile->getUint32(PosAliasListOffset);
|
|
||||||
// const int numEntries = cacheFile->getUint32(aliasListOffset);
|
|
||||||
// int begin = 0;
|
|
||||||
// int end = numEntries - 1;
|
|
||||||
// while (begin <= end) {
|
|
||||||
// const int medium = (begin + end) / 2;
|
|
||||||
// const int off = aliasListOffset + 4 + 8 * medium;
|
|
||||||
// const int aliasOffset = cacheFile->getUint32(off);
|
|
||||||
// const char *alias = cacheFile->getCharStar(aliasOffset);
|
|
||||||
// const int cmp = qstrcmp(alias, input);
|
|
||||||
// if (cmp < 0) {
|
|
||||||
// begin = medium + 1;
|
|
||||||
// } else if (cmp > 0) {
|
|
||||||
// end = medium - 1;
|
|
||||||
// } else {
|
|
||||||
// const int mimeOffset = cacheFile->getUint32(off + 4);
|
|
||||||
// const char *mimeType = cacheFile->getCharStar(mimeOffset);
|
|
||||||
// return QLatin1String(mimeType);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return name;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//QStringList MimeBinaryProvider::listAliases(const QString &name)
|
|
||||||
//{
|
|
||||||
// checkCache();
|
|
||||||
// QStringList result;
|
|
||||||
// const QByteArray input = name.toLatin1();
|
|
||||||
// for (CacheFile *cacheFile : std::as_const(m_cacheFiles)) {
|
|
||||||
// const int aliasListOffset = cacheFile->getUint32(PosAliasListOffset);
|
|
||||||
// const int numEntries = cacheFile->getUint32(aliasListOffset);
|
|
||||||
// for (int pos = 0; pos < numEntries; ++pos) {
|
|
||||||
// const int off = aliasListOffset + 4 + 8 * pos;
|
|
||||||
// const int mimeOffset = cacheFile->getUint32(off + 4);
|
|
||||||
// const char *mimeType = cacheFile->getCharStar(mimeOffset);
|
|
||||||
|
|
||||||
// if (input == mimeType) {
|
|
||||||
// const int aliasOffset = cacheFile->getUint32(off);
|
|
||||||
// const char *alias = cacheFile->getCharStar(aliasOffset);
|
|
||||||
// result.append(QString::fromLatin1(alias));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//void MimeBinaryProvider::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".
|
|
||||||
// const QStringList typesFilenames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/types"));
|
|
||||||
// for (const QString &typeFilename : typesFilenames) {
|
|
||||||
// QFile file(typeFilename);
|
|
||||||
// if (file.open(QIODevice::ReadOnly)) {
|
|
||||||
// while (!file.atEnd()) {
|
|
||||||
// QByteArray line = file.readLine();
|
|
||||||
// line.chop(1);
|
|
||||||
// m_mimetypeNames.insert(QString::fromLatin1(line.constData(), line.size()));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//QList<MimeType> MimeBinaryProvider::allMimeTypes()
|
|
||||||
//{
|
|
||||||
// QList<MimeType> result;
|
|
||||||
// loadMimeTypeList();
|
|
||||||
|
|
||||||
// for (QSet<QString>::const_iterator it = m_mimetypeNames.constBegin();
|
|
||||||
// it != m_mimetypeNames.constEnd(); ++it)
|
|
||||||
// result.append(mimeTypeForNameUnchecked(*it));
|
|
||||||
|
|
||||||
// return result;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//void MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data)
|
|
||||||
//{
|
|
||||||
//#ifdef QT_NO_XMLSTREAMREADER
|
|
||||||
// qWarning() << "Cannot load mime type since QXmlStreamReader is not available.";
|
|
||||||
// return;
|
|
||||||
//#else
|
|
||||||
// if (data.loaded)
|
|
||||||
// return;
|
|
||||||
// data.loaded = true;
|
|
||||||
// // load comment and globPatterns
|
|
||||||
|
|
||||||
// const QString file = data.name + QLatin1String(".xml");
|
|
||||||
// const QStringList mimeFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QString::fromLatin1("mime/") + file);
|
|
||||||
// if (mimeFiles.isEmpty()) {
|
|
||||||
// // TODO: ask Thiago about this
|
|
||||||
// qWarning() << "No file found for" << file << ", even though the file appeared in a directory listing.";
|
|
||||||
// qWarning() << "Either it was just removed, or the directory doesn't have executable permission...";
|
|
||||||
// qWarning() << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// QString comment;
|
|
||||||
// QString mainPattern;
|
|
||||||
// const QString preferredLanguage = QLocale::system().name();
|
|
||||||
|
|
||||||
// QListIterator<QString> mimeFilesIter(mimeFiles);
|
|
||||||
// mimeFilesIter.toBack();
|
|
||||||
// while (mimeFilesIter.hasPrevious()) { // global first, then local.
|
|
||||||
// const QString fullPath = mimeFilesIter.previous();
|
|
||||||
// QFile qfile(fullPath);
|
|
||||||
// if (!qfile.open(QFile::ReadOnly))
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// QXmlStreamReader xml(&qfile);
|
|
||||||
// if (xml.readNextStartElement()) {
|
|
||||||
// if (xml.name() != QLatin1String("mime-type")) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// const QString name = xml.attributes().value(QLatin1String("type")).toString();
|
|
||||||
// if (name.isEmpty())
|
|
||||||
// continue;
|
|
||||||
// if (name != data.name) {
|
|
||||||
// qWarning() << "Got name" << name << "in file" << file << "expected" << data.name;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// while (xml.readNextStartElement()) {
|
|
||||||
// const QStringRef 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("en_US");
|
|
||||||
// }
|
|
||||||
// data.localeComments.insert(lang, text);
|
|
||||||
// continue; // we called readElementText, so we're at the EndElement already.
|
|
||||||
// } else if (tag == QLatin1String("icon")) { // as written out by shared-mime-info >= 0.40
|
|
||||||
// data.iconName = xml.attributes().value(QLatin1String("name")).toString();
|
|
||||||
// } else if (tag == QLatin1String("glob-deleteall")) { // as written out by shared-mime-info >= 0.70
|
|
||||||
// data.globPatterns.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 (!data.globPatterns.contains(pattern))
|
|
||||||
// data.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 1
|
|
||||||
// if (!mainPattern.isEmpty() && data.globPatterns.first() != mainPattern) {
|
|
||||||
// // ensure it's first in the list of patterns
|
|
||||||
// data.globPatterns.removeAll(mainPattern);
|
|
||||||
// data.globPatterns.prepend(mainPattern);
|
|
||||||
// }
|
|
||||||
//#else
|
|
||||||
// const bool globsInXml = sharedMimeInfoVersion() >= QT_VERSION_CHECK(0, 70, 0);
|
|
||||||
// if (globsInXml) {
|
|
||||||
// if (!mainPattern.isEmpty() && data.globPatterns.first() != mainPattern) {
|
|
||||||
// // ensure it's first in the list of patterns
|
|
||||||
// data.globPatterns.removeAll(mainPattern);
|
|
||||||
// data.globPatterns.prepend(mainPattern);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// // Fallback: get the patterns from the globs file
|
|
||||||
// // TODO: This would be the only way to support shared-mime-info < 0.70
|
|
||||||
// // But is this really worth the effort?
|
|
||||||
// }
|
|
||||||
//#endif
|
|
||||||
//#endif //QT_NO_XMLSTREAMREADER
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// Binary search in the icons or generic-icons list
|
|
||||||
//QString MimeBinaryProvider::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 QString();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//void MimeBinaryProvider::loadIcon(MimeTypePrivate &data)
|
|
||||||
//{
|
|
||||||
// checkCache();
|
|
||||||
// const QByteArray inputMime = data.name.toLatin1();
|
|
||||||
// for (CacheFile *cacheFile : std::as_const(m_cacheFiles)) {
|
|
||||||
// const QString icon = iconForMime(cacheFile, PosIconsListOffset, inputMime);
|
|
||||||
// if (!icon.isEmpty()) {
|
|
||||||
// data.iconName = icon;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//void MimeBinaryProvider::loadGenericIcon(MimeTypePrivate &data)
|
|
||||||
//{
|
|
||||||
// checkCache();
|
|
||||||
// const QByteArray inputMime = data.name.toLatin1();
|
|
||||||
// for (CacheFile *cacheFile : std::as_const(m_cacheFiles)) {
|
|
||||||
// const QString icon = iconForMime(cacheFile, PosGenericIconsListOffset, inputMime);
|
|
||||||
// if (!icon.isEmpty()) {
|
|
||||||
// data.genericIconName = icon;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
////
|
|
||||||
|
|
||||||
MimeXMLProvider::MimeXMLProvider(MimeDatabasePrivate *db)
|
|
||||||
: MimeProviderBase(db), m_loaded(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MimeXMLProvider::isValid()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType MimeXMLProvider::mimeTypeForName(const QString &name)
|
|
||||||
{
|
|
||||||
ensureLoaded();
|
|
||||||
|
|
||||||
return m_nameMimeTypeMap.value(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList MimeXMLProvider::findByFileName(const QString &fileName, QString *foundSuffix)
|
|
||||||
{
|
|
||||||
ensureLoaded();
|
|
||||||
|
|
||||||
const QStringList matchingMimeTypes = m_mimeTypeGlobs.matchingGlobs(fileName, foundSuffix);
|
|
||||||
return matchingMimeTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType MimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr)
|
|
||||||
{
|
|
||||||
ensureLoaded();
|
|
||||||
|
|
||||||
QString candidate;
|
|
||||||
|
|
||||||
for (const MimeMagicRuleMatcher &matcher : std::as_const(m_magicMatchers)) {
|
|
||||||
if (matcher.matches(data)) {
|
|
||||||
const int priority = matcher.priority();
|
|
||||||
if (priority > *accuracyPtr) {
|
|
||||||
*accuracyPtr = priority;
|
|
||||||
candidate = matcher.mimetype();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mimeTypeForName(candidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<int, QList<MimeMagicRule> > MimeXMLProvider::magicRulesForMimeType(const MimeType &mimeType)
|
|
||||||
{
|
|
||||||
QMap<int, QList<MimeMagicRule> > result;
|
|
||||||
for (const MimeMagicRuleMatcher &matcher : std::as_const(m_magicMatchers)) {
|
|
||||||
if (mimeType.matchesName(matcher.mimetype()))
|
|
||||||
result[matcher.priority()].append(matcher.magicRules());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns)
|
|
||||||
{
|
|
||||||
// remove all previous globs
|
|
||||||
m_mimeTypeGlobs.removeMimeType(mimeType.name());
|
|
||||||
// add new patterns as case-insensitive default-weight patterns
|
|
||||||
for (const QString &pattern : patterns)
|
|
||||||
addGlobPattern(MimeGlobPattern(pattern, mimeType.name()));
|
|
||||||
mimeType.d->globPatterns = patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::setMagicRulesForMimeType(const MimeType &mimeType, const QMap<int, QList<MimeMagicRule> > &rules)
|
|
||||||
{
|
|
||||||
// remove all previous rules
|
|
||||||
for (int i = 0; i < m_magicMatchers.size(); ++i) {
|
|
||||||
if (m_magicMatchers.at(i).mimetype() == mimeType.name())
|
|
||||||
m_magicMatchers.removeAt(i--);
|
|
||||||
}
|
|
||||||
// add new rules
|
|
||||||
for (auto it = rules.cbegin(); it != rules.cend(); ++it) {
|
|
||||||
MimeMagicRuleMatcher matcher(mimeType.name(), it.key()/*priority*/);
|
|
||||||
matcher.addRules(it.value());
|
|
||||||
addMagicMatcher(matcher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::ensureLoaded()
|
|
||||||
{
|
|
||||||
if (!m_loaded /*|| shouldCheck()*/) {
|
|
||||||
m_loaded = true;
|
|
||||||
QStringList allFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
|
|
||||||
QStringLiteral("mime/packages/freedesktop.org.xml"),
|
|
||||||
QStandardPaths::LocateFile);
|
|
||||||
|
|
||||||
if (allFiles.isEmpty()) {
|
|
||||||
// System freedsktop.org.xml file not found, use our bundled copy
|
|
||||||
const char freedesktopOrgXml[] = ":/utils/mimetypes/freedesktop.org.xml";
|
|
||||||
allFiles.prepend(QLatin1String(freedesktopOrgXml));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_nameMimeTypeMap.clear();
|
|
||||||
m_aliases.clear();
|
|
||||||
m_parents.clear();
|
|
||||||
m_mimeTypeGlobs.clear();
|
|
||||||
m_magicMatchers.clear();
|
|
||||||
|
|
||||||
//qDebug() << "Loading" << m_allFiles;
|
|
||||||
|
|
||||||
// add custom mime types first, which override any default from freedesktop.org.xml
|
|
||||||
MimeTypeParser parser(*this);
|
|
||||||
for (auto it = m_additionalData.constBegin(), end = m_additionalData.constEnd(); it != end; ++it) {
|
|
||||||
QString errorMessage;
|
|
||||||
if (!parser.parse(it.value(), it.key(), &errorMessage)) {
|
|
||||||
qWarning("MimeDatabase: Error loading %s\n%s", qPrintable(it.key()),
|
|
||||||
qPrintable(errorMessage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QString &file : std::as_const(allFiles))
|
|
||||||
load(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::load(const QString &fileName)
|
|
||||||
{
|
|
||||||
QString errorMessage;
|
|
||||||
if (!load(fileName, &errorMessage))
|
|
||||||
qWarning("MimeDatabase: Error loading %s\n%s", qPrintable(fileName), qPrintable(errorMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MimeXMLProvider::load(const QString &fileName, QString *errorMessage)
|
|
||||||
{
|
|
||||||
m_loaded = true;
|
|
||||||
|
|
||||||
QFile file(fileName);
|
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
if (errorMessage)
|
|
||||||
*errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(fileName, file.errorString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
errorMessage->clear();
|
|
||||||
|
|
||||||
const QByteArray content = file.readAll();
|
|
||||||
MimeTypeParser parser(*this);
|
|
||||||
return parser.parse(content, fileName, errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::addGlobPattern(const MimeGlobPattern &glob)
|
|
||||||
{
|
|
||||||
m_mimeTypeGlobs.addGlob(glob);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::addMimeType(const MimeType &mt)
|
|
||||||
{
|
|
||||||
m_nameMimeTypeMap.insert(mt.name(), mt);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList MimeXMLProvider::parents(const QString &mime)
|
|
||||||
{
|
|
||||||
ensureLoaded();
|
|
||||||
QStringList result = m_parents.value(mime);
|
|
||||||
if (result.isEmpty()) {
|
|
||||||
const QString parent = fallbackParent(mime);
|
|
||||||
if (!parent.isEmpty())
|
|
||||||
result.append(parent);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::addParent(const QString &child, const QString &parent)
|
|
||||||
{
|
|
||||||
m_parents[child].append(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList MimeXMLProvider::listAliases(const QString &name)
|
|
||||||
{
|
|
||||||
ensureLoaded();
|
|
||||||
// Iterate through the whole hash. This method is rarely used.
|
|
||||||
return m_aliases.keys(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MimeXMLProvider::resolveAlias(const QString &name)
|
|
||||||
{
|
|
||||||
ensureLoaded();
|
|
||||||
return m_aliases.value(name, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::addAlias(const QString &alias, const QString &name)
|
|
||||||
{
|
|
||||||
m_aliases.insert(alias, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<MimeType> MimeXMLProvider::allMimeTypes()
|
|
||||||
{
|
|
||||||
ensureLoaded();
|
|
||||||
return m_nameMimeTypeMap.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::addMagicMatcher(const MimeMagicRuleMatcher &matcher)
|
|
||||||
{
|
|
||||||
m_magicMatchers.append(matcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeXMLProvider::addData(const QString &id, const QByteArray &data)
|
|
||||||
{
|
|
||||||
if (m_additionalData.contains(id))
|
|
||||||
qWarning("Overwriting data in mime database, id '%s'", qPrintable(id));
|
|
||||||
m_additionalData.insert(id, data);
|
|
||||||
m_loaded = false; // force reload to ensure correct load order for overridden mime types
|
|
||||||
}
|
|
@@ -1,155 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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 "mimedatabase_p.h"
|
|
||||||
#include "mimemagicrule_p.h"
|
|
||||||
|
|
||||||
#include <QtCore/qdatetime.h>
|
|
||||||
#include <QtCore/qset.h>
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class MimeMagicRuleMatcher;
|
|
||||||
|
|
||||||
class MimeProviderBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MimeProviderBase(MimeDatabasePrivate *db);
|
|
||||||
virtual ~MimeProviderBase() {}
|
|
||||||
|
|
||||||
virtual bool isValid() = 0;
|
|
||||||
virtual MimeType mimeTypeForName(const QString &name) = 0;
|
|
||||||
virtual QStringList findByFileName(const QString &fileName, QString *foundSuffix) = 0;
|
|
||||||
virtual QStringList parents(const QString &mime) = 0;
|
|
||||||
virtual QString resolveAlias(const QString &name) = 0;
|
|
||||||
virtual QStringList listAliases(const QString &name) = 0;
|
|
||||||
virtual MimeType findByMagic(const QByteArray &data, int *accuracyPtr) = 0;
|
|
||||||
virtual QList<MimeType> allMimeTypes() = 0;
|
|
||||||
virtual void loadMimeTypePrivate(MimeTypePrivate &) {}
|
|
||||||
virtual void loadIcon(MimeTypePrivate &) {}
|
|
||||||
virtual void loadGenericIcon(MimeTypePrivate &) {}
|
|
||||||
|
|
||||||
// Qt Creator additions
|
|
||||||
virtual QMap<int, QList<MimeMagicRule> > magicRulesForMimeType(const MimeType &mimeType) = 0;
|
|
||||||
virtual void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns) = 0;
|
|
||||||
virtual void setMagicRulesForMimeType(const MimeType &mimeType, const QMap<int, QList<MimeMagicRule> > &rules) = 0;
|
|
||||||
|
|
||||||
MimeDatabasePrivate *m_db;
|
|
||||||
protected:
|
|
||||||
bool shouldCheck();
|
|
||||||
QDateTime m_lastCheck;
|
|
||||||
};
|
|
||||||
|
|
||||||
///*
|
|
||||||
// Parses the files 'mime.cache' and 'types' on demand
|
|
||||||
// */
|
|
||||||
//class MimeBinaryProvider : public MimeProviderBase
|
|
||||||
//{
|
|
||||||
//public:
|
|
||||||
// MimeBinaryProvider(MimeDatabasePrivate *db);
|
|
||||||
// virtual ~MimeBinaryProvider();
|
|
||||||
|
|
||||||
// virtual bool isValid();
|
|
||||||
// virtual MimeType mimeTypeForName(const QString &name);
|
|
||||||
// virtual QStringList findByFileName(const QString &fileName, QString *foundSuffix);
|
|
||||||
// virtual QStringList parents(const QString &mime);
|
|
||||||
// virtual QString resolveAlias(const QString &name);
|
|
||||||
// virtual QStringList listAliases(const QString &name);
|
|
||||||
// virtual MimeType findByMagic(const QByteArray &data, int *accuracyPtr);
|
|
||||||
// virtual QList<MimeType> allMimeTypes();
|
|
||||||
// virtual void loadMimeTypePrivate(MimeTypePrivate &);
|
|
||||||
// virtual void loadIcon(MimeTypePrivate &);
|
|
||||||
// virtual void loadGenericIcon(MimeTypePrivate &);
|
|
||||||
|
|
||||||
//private:
|
|
||||||
// struct CacheFile;
|
|
||||||
|
|
||||||
// void matchGlobList(MimeGlobMatchResult &result, CacheFile *cacheFile, int offset, const QString &fileName);
|
|
||||||
// bool matchSuffixTree(MimeGlobMatchResult &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);
|
|
||||||
// QString iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime);
|
|
||||||
// void loadMimeTypeList();
|
|
||||||
// void checkCache();
|
|
||||||
|
|
||||||
// class CacheFileList : public QList<CacheFile *>
|
|
||||||
// {
|
|
||||||
// public:
|
|
||||||
// CacheFile *findCacheFile(const QString &fileName) const;
|
|
||||||
// bool checkCacheChanged();
|
|
||||||
// };
|
|
||||||
// CacheFileList m_cacheFiles;
|
|
||||||
// QStringList m_cacheFileNames;
|
|
||||||
// QSet<QString> m_mimetypeNames;
|
|
||||||
// bool m_mimetypeListLoaded;
|
|
||||||
//};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Parses the raw XML files (slower)
|
|
||||||
*/
|
|
||||||
class MimeXMLProvider : public MimeProviderBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MimeXMLProvider(MimeDatabasePrivate *db);
|
|
||||||
|
|
||||||
bool isValid() override;
|
|
||||||
MimeType mimeTypeForName(const QString &name) override;
|
|
||||||
QStringList findByFileName(const QString &fileName, QString *foundSuffix) override;
|
|
||||||
QStringList parents(const QString &mime) override;
|
|
||||||
QString resolveAlias(const QString &name) override;
|
|
||||||
QStringList listAliases(const QString &name) override;
|
|
||||||
MimeType findByMagic(const QByteArray &data, int *accuracyPtr) override;
|
|
||||||
QList<MimeType> allMimeTypes() override;
|
|
||||||
|
|
||||||
bool load(const QString &fileName, QString *errorMessage);
|
|
||||||
|
|
||||||
// Called by the mimetype xml parser
|
|
||||||
void addMimeType(const MimeType &mt);
|
|
||||||
void addGlobPattern(const MimeGlobPattern &glob);
|
|
||||||
void addParent(const QString &child, const QString &parent);
|
|
||||||
void addAlias(const QString &alias, const QString &name);
|
|
||||||
void addMagicMatcher(const MimeMagicRuleMatcher &matcher);
|
|
||||||
|
|
||||||
// Qt Creator additions
|
|
||||||
void addData(const QString &id, const QByteArray &data);
|
|
||||||
QMap<int, QList<MimeMagicRule> > magicRulesForMimeType(const MimeType &mimeType) override;
|
|
||||||
void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns) override;
|
|
||||||
void setMagicRulesForMimeType(const MimeType &mimeType, const QMap<int, QList<MimeMagicRule> > &rules) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void ensureLoaded();
|
|
||||||
void load(const QString &fileName);
|
|
||||||
|
|
||||||
bool m_loaded;
|
|
||||||
|
|
||||||
typedef QHash<QString, MimeType> NameMimeTypeMap;
|
|
||||||
NameMimeTypeMap m_nameMimeTypeMap;
|
|
||||||
|
|
||||||
typedef QHash<QString, QString> AliasHash;
|
|
||||||
AliasHash m_aliases;
|
|
||||||
|
|
||||||
typedef QHash<QString, QStringList> ParentsHash;
|
|
||||||
ParentsHash m_parents;
|
|
||||||
MimeAllGlobPatterns m_mimeTypeGlobs;
|
|
||||||
|
|
||||||
QList<MimeMagicRuleMatcher> m_magicMatchers;
|
|
||||||
|
|
||||||
// Qt Creator additions
|
|
||||||
QHash<QString, QByteArray> m_additionalData; // id -> data
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
} // Utils
|
|
@@ -1,423 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#include "mimetype.h"
|
|
||||||
|
|
||||||
#include "mimetype_p.h"
|
|
||||||
#include "mimedatabase_p.h"
|
|
||||||
#include "mimeprovider_p.h"
|
|
||||||
|
|
||||||
#include "mimeglobpattern_p.h"
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <QtCore/QLocale>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
using namespace Utils::Internal;
|
|
||||||
|
|
||||||
static QString suffixFromPattern(const QString &pattern)
|
|
||||||
{
|
|
||||||
// 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) {
|
|
||||||
return pattern.mid(2);
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeTypePrivate::MimeTypePrivate()
|
|
||||||
: loaded(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
MimeTypePrivate::MimeTypePrivate(const MimeType &other)
|
|
||||||
: name(other.d->name),
|
|
||||||
localeComments(other.d->localeComments),
|
|
||||||
genericIconName(other.d->genericIconName),
|
|
||||||
iconName(other.d->iconName),
|
|
||||||
globPatterns(other.d->globPatterns),
|
|
||||||
loaded(other.d->loaded)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void MimeTypePrivate::clear()
|
|
||||||
{
|
|
||||||
name.clear();
|
|
||||||
localeComments.clear();
|
|
||||||
genericIconName.clear();
|
|
||||||
iconName.clear();
|
|
||||||
globPatterns.clear();
|
|
||||||
loaded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MimeTypePrivate::addGlobPattern(const QString &pattern)
|
|
||||||
{
|
|
||||||
globPatterns.append(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\class MimeType
|
|
||||||
\inmodule QtCreator
|
|
||||||
\ingroup shared
|
|
||||||
\brief The MimeType class describes types of file or data, represented by a MIME type string.
|
|
||||||
|
|
||||||
\since 5.0
|
|
||||||
|
|
||||||
For instance, a file named \c readme.txt has the MIME type \c 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 {MimeType::iconName}{icon} for the file, or even
|
|
||||||
the descriptive \l {MimeType::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 \c text/x-csrc inherits \c text/plain.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Constructs this MimeType object initialized with default property values that indicate an invalid MIME type.
|
|
||||||
*/
|
|
||||||
MimeType::MimeType() :
|
|
||||||
d(new MimeTypePrivate())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Constructs this MimeType object as a copy of \a other.
|
|
||||||
*/
|
|
||||||
MimeType::MimeType(const MimeType &other) = default;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Assigns the data of \a other to this MimeType object, and returns a reference to this object.
|
|
||||||
*/
|
|
||||||
MimeType &MimeType::operator=(const MimeType &other)
|
|
||||||
{
|
|
||||||
if (d != other.d)
|
|
||||||
d = other.d;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn MimeType::MimeType(const Internal::MimeTypePrivate &dd)
|
|
||||||
\internal
|
|
||||||
*/
|
|
||||||
MimeType::MimeType(const MimeTypePrivate &dd) :
|
|
||||||
d(new MimeTypePrivate(dd))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Destroys the MimeType object, and releases the d pointer.
|
|
||||||
*/
|
|
||||||
MimeType::~MimeType() = default;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c true if \a other equals this MimeType object, otherwise returns \c false.
|
|
||||||
The name is the unique identifier for a MIME type, so two MIME types with
|
|
||||||
the same name are equal.
|
|
||||||
*/
|
|
||||||
bool MimeType::operator==(const MimeType &other) const
|
|
||||||
{
|
|
||||||
return d == other.d || d->name == other.d->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn bool MimeType::operator!=(const MimeType &other) const;
|
|
||||||
Returns \c true if \a other does not equal this MimeType object, otherwise returns \c false.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn inline uint Utils::qHash(const MimeType &mime)
|
|
||||||
\internal
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c true if the MimeType object contains valid data, otherwise returns \c false.
|
|
||||||
A valid MIME type has a non-empty name().
|
|
||||||
The invalid MIME type is the default-constructed MimeType.
|
|
||||||
*/
|
|
||||||
bool MimeType::isValid() const
|
|
||||||
{
|
|
||||||
return !d->name.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c true if this MIME type is the default MIME type which
|
|
||||||
applies to all files: \c application/octet-stream.
|
|
||||||
*/
|
|
||||||
bool MimeType::isDefault() const
|
|
||||||
{
|
|
||||||
return d->name == MimeDatabasePrivate::instance()->defaultMimeType();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the name of the MIME type.
|
|
||||||
*/
|
|
||||||
QString MimeType::name() const
|
|
||||||
{
|
|
||||||
return d->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the description of the MIME type to be displayed on user interfaces.
|
|
||||||
|
|
||||||
The system language (QLocale::system().name()) is used to select the appropriate translation.
|
|
||||||
*/
|
|
||||||
QString MimeType::comment() const
|
|
||||||
{
|
|
||||||
MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
|
|
||||||
|
|
||||||
QStringList languageList;
|
|
||||||
languageList << QLocale::system().name();
|
|
||||||
languageList << QLocale::system().uiLanguages();
|
|
||||||
for (const QString &language : std::as_const(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns 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.
|
|
||||||
*/
|
|
||||||
QString MimeType::genericIconName() const
|
|
||||||
{
|
|
||||||
MimeDatabasePrivate::instance()->provider()->loadGenericIcon(*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).
|
|
||||||
QString group = name();
|
|
||||||
const int slashindex = group.indexOf(QLatin1Char('/'));
|
|
||||||
if (slashindex != -1)
|
|
||||||
group = group.left(slashindex);
|
|
||||||
return group + QLatin1String("-x-generic");
|
|
||||||
}
|
|
||||||
return d->genericIconName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns 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.
|
|
||||||
*/
|
|
||||||
QString MimeType::iconName() const
|
|
||||||
{
|
|
||||||
MimeDatabasePrivate::instance()->provider()->loadIcon(*d);
|
|
||||||
if (d->iconName.isEmpty()) {
|
|
||||||
// Make default icon name from the mimetype name
|
|
||||||
d->iconName = name();
|
|
||||||
const int slashindex = d->iconName.indexOf(QLatin1Char('/'));
|
|
||||||
if (slashindex != -1)
|
|
||||||
d->iconName[slashindex] = QLatin1Char('-');
|
|
||||||
}
|
|
||||||
return d->iconName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the list of glob matching patterns.
|
|
||||||
*/
|
|
||||||
QStringList MimeType::globPatterns() const
|
|
||||||
{
|
|
||||||
MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
|
|
||||||
return d->globPatterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
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 \c image/svg+xml files are
|
|
||||||
also \c text/xml, \c text/plain and \c application/octet-stream files.
|
|
||||||
|
|
||||||
Subclassing is about the format, rather than the category of the data.
|
|
||||||
For example, there is no \e {generic spreadsheet} class that all
|
|
||||||
spreadsheets inherit from.
|
|
||||||
|
|
||||||
Conversely, the parent MIME type of \c image/svg+xml is \c text/xml.
|
|
||||||
|
|
||||||
A MIME type can have multiple parents. For instance, \c application/x-perl
|
|
||||||
has two parents: \c application/x-executable and \c text/plain. This makes
|
|
||||||
it possible to both execute perl scripts, and to open them in text editors.
|
|
||||||
*/
|
|
||||||
QStringList MimeType::parentMimeTypes() const
|
|
||||||
{
|
|
||||||
return MimeDatabasePrivate::instance()->provider()->parents(d->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void collectParentMimeTypes(const QString &mime, QStringList &allParents)
|
|
||||||
{
|
|
||||||
const QStringList parents = MimeDatabasePrivate::instance()->provider()->parents(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns all the parent MIME types of this type, direct and indirect.
|
|
||||||
This includes grandparents, and so on.
|
|
||||||
|
|
||||||
For instance, for \c image/svg+xml the list would be:
|
|
||||||
\c application/xml, \c text/plain, \c application/octet-stream.
|
|
||||||
|
|
||||||
\note The \c application/octet-stream type is the ultimate parent for all types
|
|
||||||
of files (but not directories).
|
|
||||||
*/
|
|
||||||
QStringList MimeType::allAncestors() const
|
|
||||||
{
|
|
||||||
QStringList allParents;
|
|
||||||
collectParentMimeTypes(d->name, allParents);
|
|
||||||
return allParents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the list of aliases of this MIME type.
|
|
||||||
|
|
||||||
For instance, for \c text/csv, the returned list would be:
|
|
||||||
\c text/x-csv, \c text/x-comma-separated-values.
|
|
||||||
|
|
||||||
\note All MimeType instances refer to proper MIME types,
|
|
||||||
never to aliases directly.
|
|
||||||
|
|
||||||
The order of the aliases in the list is undefined.
|
|
||||||
*/
|
|
||||||
QStringList MimeType::aliases() const
|
|
||||||
{
|
|
||||||
return MimeDatabasePrivate::instance()->provider()->listAliases(d->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the known suffixes for the MIME type.
|
|
||||||
No leading dot is included, so for instance this would return
|
|
||||||
\c {"jpg", "jpeg"} for \c image/jpeg.
|
|
||||||
*/
|
|
||||||
QStringList MimeType::suffixes() const
|
|
||||||
{
|
|
||||||
MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
|
|
||||||
|
|
||||||
QStringList result;
|
|
||||||
for (const QString &pattern : std::as_const(d->globPatterns)) {
|
|
||||||
const QString suffix = suffixFromPattern(pattern);
|
|
||||||
if (!suffix.isEmpty())
|
|
||||||
result.append(suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the preferred suffix for the MIME type.
|
|
||||||
No leading dot is included, so for instance this would return \c "pdf" for
|
|
||||||
\c application/pdf. The return value can be empty, for MIME types which do
|
|
||||||
not have any suffixes associated.
|
|
||||||
*/
|
|
||||||
QString MimeType::preferredSuffix() const
|
|
||||||
{
|
|
||||||
const QStringList suffixList = suffixes();
|
|
||||||
return suffixList.isEmpty() ? QString() : suffixList.at(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns a filter string usable for a file dialog.
|
|
||||||
*/
|
|
||||||
QString MimeType::filterString() const
|
|
||||||
{
|
|
||||||
MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c true if the name or alias of the MIME type matches
|
|
||||||
\a nameOrAlias.
|
|
||||||
*/
|
|
||||||
bool MimeType::matchesName(const QString &nameOrAlias) const
|
|
||||||
{
|
|
||||||
if (d->name == nameOrAlias)
|
|
||||||
return true;
|
|
||||||
return MimeDatabasePrivate::instance()->provider()->resolveAlias(nameOrAlias) == d->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Sets the preferred filename suffix for the MIME type to \a suffix.
|
|
||||||
*/
|
|
||||||
void MimeType::setPreferredSuffix(const QString &suffix)
|
|
||||||
{
|
|
||||||
MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
|
|
||||||
|
|
||||||
auto it = std::find_if(d->globPatterns.begin(), d->globPatterns.end(),
|
|
||||||
[suffix](const QString &pattern) {
|
|
||||||
return suffixFromPattern(pattern) == suffix;
|
|
||||||
});
|
|
||||||
if (it != d->globPatterns.end())
|
|
||||||
d->globPatterns.erase(it);
|
|
||||||
d->globPatterns.prepend(QLatin1String("*.") + suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns \c true if this MIME type is \a mimeTypeName or inherits it,
|
|
||||||
or if \a mimeTypeName is an alias for this mimetype.
|
|
||||||
|
|
||||||
\sa parentMimeTypes()
|
|
||||||
*/
|
|
||||||
bool MimeType::inherits(const QString &mimeTypeName) const
|
|
||||||
{
|
|
||||||
if (d->name == mimeTypeName)
|
|
||||||
return true;
|
|
||||||
return MimeDatabasePrivate::instance()->inherits(d->name, mimeTypeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
|
||||||
QDebug operator<<(QDebug debug, const MimeType &mime)
|
|
||||||
{
|
|
||||||
QDebugStateSaver saver(debug);
|
|
||||||
if (!mime.isValid()) {
|
|
||||||
debug.nospace() << "MimeType(invalid)";
|
|
||||||
} else {
|
|
||||||
debug.nospace() << "MimeType(" << mime.name() << ")";
|
|
||||||
}
|
|
||||||
return debug;
|
|
||||||
}
|
|
||||||
#endif
|
|
@@ -1,99 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utils/utils_global.h>
|
|
||||||
|
|
||||||
#include <QtCore/qshareddata.h>
|
|
||||||
#include <QtCore/qstring.h>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QFileinfo;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
|
|
||||||
namespace Internal {
|
|
||||||
class MimeTypeParserBase;
|
|
||||||
class MimeTypeMapEntry;
|
|
||||||
class MimeDatabasePrivate;
|
|
||||||
class MimeXMLProvider;
|
|
||||||
class MimeBinaryProvider;
|
|
||||||
class MimeTypePrivate;
|
|
||||||
}
|
|
||||||
|
|
||||||
class QTCREATOR_UTILS_EXPORT MimeType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MimeType();
|
|
||||||
MimeType(const MimeType &other);
|
|
||||||
MimeType &operator=(const MimeType &other);
|
|
||||||
//#ifdef Q_COMPILER_RVALUE_REFS
|
|
||||||
// MimeType &operator=(MimeType &&other)
|
|
||||||
// {
|
|
||||||
// qSwap(d, other.d);
|
|
||||||
// return *this;
|
|
||||||
// }
|
|
||||||
//#endif
|
|
||||||
// void swap(MimeType &other)
|
|
||||||
// {
|
|
||||||
// qSwap(d, other.d);
|
|
||||||
// }
|
|
||||||
explicit MimeType(const Internal::MimeTypePrivate &dd);
|
|
||||||
~MimeType();
|
|
||||||
|
|
||||||
bool operator==(const MimeType &other) const;
|
|
||||||
|
|
||||||
inline bool operator!=(const MimeType &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;
|
|
||||||
|
|
||||||
bool inherits(const QString &mimeTypeName) const;
|
|
||||||
|
|
||||||
QString filterString() const;
|
|
||||||
|
|
||||||
// Qt Creator additions
|
|
||||||
bool matchesName(const QString &nameOrAlias) const;
|
|
||||||
void setPreferredSuffix(const QString &suffix);
|
|
||||||
|
|
||||||
friend auto qHash(const MimeType &mime) { return qHash(mime.name()); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
friend class Internal::MimeTypeParserBase;
|
|
||||||
friend class Internal::MimeTypeMapEntry;
|
|
||||||
friend class Internal::MimeDatabasePrivate;
|
|
||||||
friend class Internal::MimeXMLProvider;
|
|
||||||
friend class Internal::MimeBinaryProvider;
|
|
||||||
friend class Internal::MimeTypePrivate;
|
|
||||||
|
|
||||||
QExplicitlySharedDataPointer<Internal::MimeTypePrivate> d;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Utils
|
|
||||||
|
|
||||||
//Q_DECLARE_SHARED(Utils::MimeType)
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QDebug;
|
|
||||||
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const Utils::MimeType &mime);
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
#endif
|
|
||||||
|
|
@@ -1,84 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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 "mimetype.h"
|
|
||||||
|
|
||||||
#include <QtCore/qhash.h>
|
|
||||||
#include <QtCore/qstringlist.h>
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class MimeTypePrivate : public QSharedData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef QHash<QString, QString> LocaleHash;
|
|
||||||
|
|
||||||
MimeTypePrivate();
|
|
||||||
explicit MimeTypePrivate(const MimeType &other);
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
void addGlobPattern(const QString &pattern);
|
|
||||||
|
|
||||||
QString name;
|
|
||||||
LocaleHash localeComments;
|
|
||||||
QString genericIconName;
|
|
||||||
QString iconName;
|
|
||||||
QStringList globPatterns;
|
|
||||||
bool loaded;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
} // Utils
|
|
||||||
|
|
||||||
#define MIMETYPE_BUILDER \
|
|
||||||
QT_BEGIN_NAMESPACE \
|
|
||||||
static MimeType buildMimeType ( \
|
|
||||||
const QString &name, \
|
|
||||||
const QString &genericIconName, \
|
|
||||||
const QString &iconName, \
|
|
||||||
const QStringList &globPatterns \
|
|
||||||
) \
|
|
||||||
{ \
|
|
||||||
MimeTypePrivate qMimeTypeData; \
|
|
||||||
qMimeTypeData.name = name; \
|
|
||||||
qMimeTypeData.genericIconName = genericIconName; \
|
|
||||||
qMimeTypeData.iconName = iconName; \
|
|
||||||
qMimeTypeData.globPatterns = globPatterns; \
|
|
||||||
return MimeType(qMimeTypeData); \
|
|
||||||
} \
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
#ifdef Q_COMPILER_RVALUE_REFS
|
|
||||||
#define MIMETYPE_BUILDER_FROM_RVALUE_REFS \
|
|
||||||
QT_BEGIN_NAMESPACE \
|
|
||||||
static MimeType buildMimeType ( \
|
|
||||||
QString &&name, \
|
|
||||||
QString &&genericIconName, \
|
|
||||||
QString &&iconName, \
|
|
||||||
QStringList &&globPatterns \
|
|
||||||
) \
|
|
||||||
{ \
|
|
||||||
MimeTypePrivate qMimeTypeData; \
|
|
||||||
qMimeTypeData.name = std::move(name); \
|
|
||||||
qMimeTypeData.genericIconName = std::move(genericIconName); \
|
|
||||||
qMimeTypeData.iconName = std::move(iconName); \
|
|
||||||
qMimeTypeData.globPatterns = std::move(globPatterns); \
|
|
||||||
return MimeType(qMimeTypeData); \
|
|
||||||
} \
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
#endif
|
|
@@ -1,325 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#include "mimetypeparser_p.h"
|
|
||||||
|
|
||||||
#include "mimetype_p.h"
|
|
||||||
#include "mimemagicrulematcher_p.h"
|
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <QtCore/QDir>
|
|
||||||
#include <QtCore/QPair>
|
|
||||||
#include <QtCore/QXmlStreamReader>
|
|
||||||
#include <QtCore/QXmlStreamWriter>
|
|
||||||
#include <QtCore/QStack>
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
using namespace Utils::Internal;
|
|
||||||
|
|
||||||
// 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 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 MimeTypeParser
|
|
||||||
\inmodule QtCore
|
|
||||||
\internal
|
|
||||||
\brief The MimeTypeParser class parses MIME types, and builds a MIME database hierarchy by adding to MimeDatabasePrivate.
|
|
||||||
|
|
||||||
Populates MimeDataBase
|
|
||||||
|
|
||||||
\sa MimeDatabase, MimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
|
||||||
\sa MimeTypeParser
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\class MimeTypeParserBase
|
|
||||||
\inmodule QtCore
|
|
||||||
\internal
|
|
||||||
\brief The MimeTypeParserBase class parses for a sequence of <mime-type> in a generic way.
|
|
||||||
|
|
||||||
Calls abstract handler function process for MimeType it finds.
|
|
||||||
|
|
||||||
\sa MimeDatabase, MimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
|
||||||
\sa MimeTypeParser
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn virtual bool MimeTypeParserBase::process(const MimeType &t, QString *errorMessage) = 0;
|
|
||||||
Overwrite to process the sequence of parsed data
|
|
||||||
*/
|
|
||||||
|
|
||||||
MimeTypeParserBase::ParseState MimeTypeParserBase::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 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(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)
|
|
||||||
static bool parseNumber(const QString &n, int *target, QString *errorMessage)
|
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
*target = n.toInt(&ok);
|
|
||||||
if (!ok) {
|
|
||||||
if (errorMessage)
|
|
||||||
*errorMessage = QString::fromLatin1("Not a number '%1'.").arg(n);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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"/>
|
|
||||||
#ifndef QT_NO_XMLSTREAMREADER
|
|
||||||
static bool createMagicMatchRule(const QXmlStreamAttributes &atts,
|
|
||||||
QString *errorMessage, MimeMagicRule *&rule)
|
|
||||||
{
|
|
||||||
const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString();
|
|
||||||
MimeMagicRule::Type magicType = MimeMagicRule::type(type.toLatin1());
|
|
||||||
if (magicType == MimeMagicRule::Invalid) {
|
|
||||||
qWarning("%s: match type %s is not supported.", Q_FUNC_INFO, type.toUtf8().constData());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString();
|
|
||||||
// Parse for offset as "1" or "1:10"
|
|
||||||
int startPos, endPos;
|
|
||||||
const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString();
|
|
||||||
const int colonIndex = offsetS.indexOf(QLatin1Char(':'));
|
|
||||||
const QString startPosS = colonIndex == -1 ? offsetS : offsetS.mid(0, colonIndex);
|
|
||||||
const QString endPosS = colonIndex == -1 ? offsetS : offsetS.mid(colonIndex + 1);
|
|
||||||
if (!parseNumber(startPosS, &startPos, errorMessage) || !parseNumber(endPosS, &endPos, errorMessage))
|
|
||||||
return false;
|
|
||||||
const QString mask = atts.value(QLatin1String(matchMaskAttributeC)).toString();
|
|
||||||
|
|
||||||
MimeMagicRule *tempRule = new MimeMagicRule(magicType, value.toUtf8(), startPos, endPos,
|
|
||||||
mask.toLatin1(), errorMessage);
|
|
||||||
if (!tempRule->isValid()) {
|
|
||||||
delete tempRule;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
rule = tempRule;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool MimeTypeParserBase::parse(const QByteArray &content, const QString &fileName, QString *errorMessage)
|
|
||||||
{
|
|
||||||
#ifdef QT_NO_XMLSTREAMREADER
|
|
||||||
if (errorMessage)
|
|
||||||
*errorMessage = QString::fromLatin1("QXmlStreamReader is not available, cannot parse.");
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
MimeTypePrivate data;
|
|
||||||
int priority = 50;
|
|
||||||
QStack<MimeMagicRule *> currentRules; // stack for the nesting of rules
|
|
||||||
QList<MimeMagicRule> rules; // toplevel rules
|
|
||||||
QXmlStreamReader reader(content);
|
|
||||||
ParseState ps = ParseBeginning;
|
|
||||||
QXmlStreamAttributes atts;
|
|
||||||
bool ignoreCurrentMimeType = false;
|
|
||||||
while (!reader.atEnd()) {
|
|
||||||
switch (reader.readNext()) {
|
|
||||||
case QXmlStreamReader::StartElement:
|
|
||||||
if (ignoreCurrentMimeType)
|
|
||||||
continue;
|
|
||||||
ps = nextState(ps, reader.name());
|
|
||||||
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(QString::fromLatin1("Missing '%1'-attribute").arg(QString::fromLatin1(mimeTypeAttributeC)));
|
|
||||||
} else {
|
|
||||||
if (mimeTypeExists(name))
|
|
||||||
ignoreCurrentMimeType = true;
|
|
||||||
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)).toString().toInt();
|
|
||||||
const bool caseSensitive = atts.value(QLatin1String(caseSensitiveAttributeC)).toString() == QLatin1String("true");
|
|
||||||
|
|
||||||
if (weight == 0)
|
|
||||||
weight = MimeGlobPattern::DefaultWeight;
|
|
||||||
|
|
||||||
Q_ASSERT(!data.name.isEmpty());
|
|
||||||
const MimeGlobPattern 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 MimeType::globPatterns()
|
|
||||||
}
|
|
||||||
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. We want the default, English one
|
|
||||||
QString locale = atts.value(QLatin1String(localeAttributeC)).toString();
|
|
||||||
const QString comment = reader.readElementText();
|
|
||||||
if (locale.isEmpty())
|
|
||||||
locale = QString::fromLatin1("en_US");
|
|
||||||
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 QString priorityS = atts.value(QLatin1String(priorityAttributeC)).toString();
|
|
||||||
if (!priorityS.isEmpty()) {
|
|
||||||
if (!parseNumber(priorityS, &priority, errorMessage))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
currentRules.clear();
|
|
||||||
//qDebug() << "MAGIC start for mimetype" << data.name;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ParseMagicMatchRule: {
|
|
||||||
MimeMagicRule *rule = nullptr;
|
|
||||||
if (!createMagicMatchRule(atts, errorMessage, rule))
|
|
||||||
return false;
|
|
||||||
QList<MimeMagicRule> *ruleList;
|
|
||||||
if (currentRules.isEmpty())
|
|
||||||
ruleList = &rules;
|
|
||||||
else // nest this rule into the proper parent
|
|
||||||
ruleList = ¤tRules.top()->m_subMatches;
|
|
||||||
ruleList->append(*rule);
|
|
||||||
//qDebug() << " MATCH added. Stack size was" << currentRules.size();
|
|
||||||
currentRules.push(&ruleList->last());
|
|
||||||
delete rule;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ParseError:
|
|
||||||
reader.raiseError(QString::fromLatin1("Unexpected element <%1>").
|
|
||||||
arg(reader.name().toString()));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// continue switch QXmlStreamReader::Token...
|
|
||||||
case QXmlStreamReader::EndElement: // Finished element
|
|
||||||
{
|
|
||||||
const QStringView elementName = reader.name();
|
|
||||||
if (elementName == QLatin1String(mimeTypeTagC)) {
|
|
||||||
if (!ignoreCurrentMimeType) {
|
|
||||||
if (!process(MimeType(data), errorMessage))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ignoreCurrentMimeType = false;
|
|
||||||
data.clear();
|
|
||||||
} else if (!ignoreCurrentMimeType) {
|
|
||||||
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
|
|
||||||
MimeMagicRuleMatcher ruleMatcher(data.name, priority);
|
|
||||||
ruleMatcher.addRules(rules);
|
|
||||||
processMagicMatcher(ruleMatcher);
|
|
||||||
rules.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader.hasError()) {
|
|
||||||
if (errorMessage)
|
|
||||||
*errorMessage = QString::fromLatin1("An error has been encountered at line %1 of %2: %3:").arg(reader.lineNumber()).arg(fileName, reader.errorString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#endif //QT_NO_XMLSTREAMREADER
|
|
||||||
}
|
|
@@ -1,91 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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 "mimedatabase_p.h"
|
|
||||||
#include "mimeprovider_p.h"
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class MimeTypeParserBase
|
|
||||||
{
|
|
||||||
Q_DISABLE_COPY(MimeTypeParserBase)
|
|
||||||
|
|
||||||
public:
|
|
||||||
MimeTypeParserBase() {}
|
|
||||||
virtual ~MimeTypeParserBase() {}
|
|
||||||
|
|
||||||
bool parse(const QByteArray &content, const QString &fileName, QString *errorMessage);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool mimeTypeExists(const QString &mimeTypeName) = 0;
|
|
||||||
virtual bool process(const MimeType &t, QString *errorMessage) = 0;
|
|
||||||
virtual bool process(const MimeGlobPattern &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 MimeMagicRuleMatcher &matcher) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum ParseState {
|
|
||||||
ParseBeginning,
|
|
||||||
ParseMimeInfo,
|
|
||||||
ParseMimeType,
|
|
||||||
ParseComment,
|
|
||||||
ParseGenericIcon,
|
|
||||||
ParseIcon,
|
|
||||||
ParseGlobPattern,
|
|
||||||
ParseSubClass,
|
|
||||||
ParseAlias,
|
|
||||||
ParseMagic,
|
|
||||||
ParseMagicMatchRule,
|
|
||||||
ParseOtherMimeTypeSubTag,
|
|
||||||
ParseError
|
|
||||||
};
|
|
||||||
|
|
||||||
static ParseState nextState(ParseState currentState, QStringView startElement);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class MimeTypeParser : public MimeTypeParserBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit MimeTypeParser(MimeXMLProvider &provider) : m_provider(provider) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
inline bool mimeTypeExists(const QString &mimeTypeName) override
|
|
||||||
{ return m_provider.mimeTypeForName(mimeTypeName).isValid(); }
|
|
||||||
|
|
||||||
inline bool process(const MimeType &t, QString *) override
|
|
||||||
{ m_provider.addMimeType(t); return true; }
|
|
||||||
|
|
||||||
inline bool process(const MimeGlobPattern &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 MimeMagicRuleMatcher &matcher) override
|
|
||||||
{ m_provider.addMagicMatcher(matcher); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
MimeXMLProvider &m_provider;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
} // Utils
|
|
@@ -1,104 +0,0 @@
|
|||||||
// Copyright (C) 2022 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR LGPL-3.0
|
|
||||||
|
|
||||||
#include "mimeutils.h"
|
|
||||||
|
|
||||||
#include "mimedatabase.h"
|
|
||||||
#include "mimedatabase_p.h"
|
|
||||||
#include "mimemagicrule_p.h"
|
|
||||||
#include "mimeprovider_p.h"
|
|
||||||
|
|
||||||
#include "filepath.h"
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
using namespace Utils::Internal;
|
|
||||||
|
|
||||||
void Utils::addMimeTypes(const QString &fileName, const QByteArray &data)
|
|
||||||
{
|
|
||||||
auto d = MimeDatabasePrivate::instance();
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
|
|
||||||
if (d->m_startupPhase >= MimeDatabase::PluginsDelayedInitializing)
|
|
||||||
qWarning("Adding items from %s to MimeDatabase after initialization time",
|
|
||||||
qPrintable(fileName));
|
|
||||||
|
|
||||||
auto xmlProvider = static_cast<MimeXMLProvider *>(d->provider());
|
|
||||||
xmlProvider->addData(fileName, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<int, QList<MimeMagicRule>> Utils::magicRulesForMimeType(const MimeType &mimeType)
|
|
||||||
{
|
|
||||||
auto d = MimeDatabasePrivate::instance();
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
return d->provider()->magicRulesForMimeType(mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Utils::setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns)
|
|
||||||
{
|
|
||||||
auto d = MimeDatabasePrivate::instance();
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
d->provider()->setGlobPatternsForMimeType(mimeType, patterns);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Utils::setMagicRulesForMimeType(const MimeType &mimeType,
|
|
||||||
const QMap<int, QList<MimeMagicRule>> &rules)
|
|
||||||
{
|
|
||||||
auto d = MimeDatabasePrivate::instance();
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
d->provider()->setMagicRulesForMimeType(mimeType, rules);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Utils::setMimeStartupPhase(MimeStartupPhase phase)
|
|
||||||
{
|
|
||||||
auto d = MimeDatabasePrivate::instance();
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
if (int(phase) != d->m_startupPhase + 1)
|
|
||||||
qWarning("Unexpected jump in MimedDatabase lifetime from %d to %d",
|
|
||||||
d->m_startupPhase,
|
|
||||||
int(phase));
|
|
||||||
d->m_startupPhase = int(phase);
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType Utils::mimeTypeForName(const QString &nameOrAlias)
|
|
||||||
{
|
|
||||||
MimeDatabase mdb;
|
|
||||||
return mdb.mimeTypeForName(nameOrAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType Utils::mimeTypeForFile(const QString &fileName, MimeMatchMode mode)
|
|
||||||
{
|
|
||||||
MimeDatabase mdb;
|
|
||||||
return mdb.mimeTypeForFile(fileName, MimeDatabase::MatchMode(mode));
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType Utils::mimeTypeForFile(const QFileInfo &fileInfo, MimeMatchMode mode)
|
|
||||||
{
|
|
||||||
MimeDatabase mdb;
|
|
||||||
return mdb.mimeTypeForFile(fileInfo, MimeDatabase::MatchMode(mode));
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType Utils::mimeTypeForFile(const FilePath &filePath, MimeMatchMode mode)
|
|
||||||
{
|
|
||||||
MimeDatabase mdb;
|
|
||||||
if (filePath.needsDevice())
|
|
||||||
return mdb.mimeTypeForUrl(filePath.toUrl());
|
|
||||||
return mdb.mimeTypeForFile(filePath.toString(), MimeDatabase::MatchMode(mode));
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<MimeType> Utils::mimeTypesForFileName(const QString &fileName)
|
|
||||||
{
|
|
||||||
MimeDatabase mdb;
|
|
||||||
return mdb.mimeTypesForFileName(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeType Utils::mimeTypeForData(const QByteArray &data)
|
|
||||||
{
|
|
||||||
MimeDatabase mdb;
|
|
||||||
return mdb.mimeTypeForData(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<MimeType> Utils::allMimeTypes()
|
|
||||||
{
|
|
||||||
MimeDatabase mdb;
|
|
||||||
return mdb.allMimeTypes();
|
|
||||||
}
|
|
@@ -5,9 +5,7 @@ Project {
|
|||||||
name: "Utils"
|
name: "Utils"
|
||||||
|
|
||||||
QtcLibrary {
|
QtcLibrary {
|
||||||
property bool useNewMimeDatabase: true
|
cpp.includePaths: base.concat("mimetypes2", ".")
|
||||||
|
|
||||||
cpp.includePaths: base.concat((useNewMimeDatabase ? "mimetypes2" : "mimetypes"), ".")
|
|
||||||
cpp.defines: base.concat([
|
cpp.defines: base.concat([
|
||||||
"UTILS_LIBRARY"
|
"UTILS_LIBRARY"
|
||||||
])
|
])
|
||||||
@@ -416,7 +414,7 @@ Project {
|
|||||||
|
|
||||||
Group {
|
Group {
|
||||||
name: "MimeTypes"
|
name: "MimeTypes"
|
||||||
prefix: useNewMimeDatabase ? "mimetypes2/" : "mimetypes/"
|
prefix: "mimetypes2/"
|
||||||
files: [
|
files: [
|
||||||
"mimedatabase.cpp",
|
"mimedatabase.cpp",
|
||||||
"mimedatabase.h",
|
"mimedatabase.h",
|
||||||
@@ -461,8 +459,7 @@ Project {
|
|||||||
|
|
||||||
Export {
|
Export {
|
||||||
Depends { name: "Qt"; submodules: ["concurrent", "widgets" ] }
|
Depends { name: "Qt"; submodules: ["concurrent", "widgets" ] }
|
||||||
cpp.includePaths: base.concat(exportingProduct.useNewMimeDatabase ? "mimetypes2"
|
cpp.includePaths: base.concat("mimetypes2")
|
||||||
: "mimetypes")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,6 @@
|
|||||||
#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
|
#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/mimetypes/mimedatabase.h>
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
Reference in New Issue
Block a user