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
|
||||
)
|
||||
|
||||
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
|
||||
SOURCES_PREFIX ${mime_prefix}
|
||||
PUBLIC_INCLUDES ${mime_prefix}
|
||||
SOURCES_PREFIX mimetypes2
|
||||
PUBLIC_INCLUDES mimetypes2
|
||||
SOURCES
|
||||
mimedatabase.cpp
|
||||
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"
|
||||
|
||||
QtcLibrary {
|
||||
property bool useNewMimeDatabase: true
|
||||
|
||||
cpp.includePaths: base.concat((useNewMimeDatabase ? "mimetypes2" : "mimetypes"), ".")
|
||||
cpp.includePaths: base.concat("mimetypes2", ".")
|
||||
cpp.defines: base.concat([
|
||||
"UTILS_LIBRARY"
|
||||
])
|
||||
@@ -416,7 +414,7 @@ Project {
|
||||
|
||||
Group {
|
||||
name: "MimeTypes"
|
||||
prefix: useNewMimeDatabase ? "mimetypes2/" : "mimetypes/"
|
||||
prefix: "mimetypes2/"
|
||||
files: [
|
||||
"mimedatabase.cpp",
|
||||
"mimedatabase.h",
|
||||
@@ -461,8 +459,7 @@ Project {
|
||||
|
||||
Export {
|
||||
Depends { name: "Qt"; submodules: ["concurrent", "widgets" ] }
|
||||
cpp.includePaths: base.concat(exportingProduct.useNewMimeDatabase ? "mimetypes2"
|
||||
: "mimetypes")
|
||||
cpp.includePaths: base.concat("mimetypes2")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QMenu>
|
||||
|
Reference in New Issue
Block a user