Import QMimeType / QMimeDatabase into QtCore.
History of the development before the import: ssh://codereview.qt-project.org/playground/mimetypes.git Mimetype definitions come from shared-mime-info where available (UNIX systems), loaded using a mmap'ed binary cache generated by update-mime-database. As a fallback if no cache is found, we parse the raw XML files otherwise. This makes the MIME type support fast and with very low memory usage on UNIX, and it makes it easy to use on Windows (no dependency on shared-mime-info, Qt even includes a freedesktop.xml file to use if none are found on the system). Change-Id: I27b05008216ff936dc463bd80d3893422bfb940e Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
parent
87fcbd82fc
commit
b164911b7f
2
.gitignore
vendored
2
.gitignore
vendored
@ -112,6 +112,8 @@ tests/auto/qmake/testdata/quotedfilenames/*.exe
|
||||
tests/auto/compilerwarnings/*.exe
|
||||
tests/auto/qmake/testdata/quotedfilenames/test.cpp
|
||||
tests/auto/qprocess/fileWriterProcess.txt
|
||||
tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-cache/mime/
|
||||
tests/auto/corelib/mimetypes/qmimedatabase/qmimedatabase-xml/mime/
|
||||
.com.apple.timemachine.supported
|
||||
tests/auto/qlibrary/libmylib.so*
|
||||
tests/auto/qresourceengine/runtime_resource.rcc
|
||||
|
47
doc/src/snippets/code/src_corelib_mimetype_qmimedatabase.cpp
Normal file
47
doc/src/snippets/code/src_corelib_mimetype_qmimedatabase.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the documentation of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
|
||||
** the names of its contributors may be used to endorse or promote
|
||||
** products derived from this software without specific prior written
|
||||
** permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//! [0]
|
||||
QMimeDatabase db;
|
||||
QMimeType mime = db.mimeTypeForFile(fileName);
|
||||
if (mime.inherits("text/plain")) {
|
||||
// The file is plain text, we can display it in a QTextEdit
|
||||
}
|
||||
//! [0]
|
@ -28,6 +28,7 @@ include(plugin/plugin.pri)
|
||||
include(kernel/kernel.pri)
|
||||
include(codecs/codecs.pri)
|
||||
include(statemachine/statemachine.pri)
|
||||
include(mimetypes/mimetypes.pri)
|
||||
include(xml/xml.pri)
|
||||
|
||||
mac|darwin {
|
||||
|
31966
src/corelib/mimetypes/mime/packages/freedesktop.org.xml
Normal file
31966
src/corelib/mimetypes/mime/packages/freedesktop.org.xml
Normal file
File diff suppressed because it is too large
Load Diff
25
src/corelib/mimetypes/mimetypes.pri
Normal file
25
src/corelib/mimetypes/mimetypes.pri
Normal file
@ -0,0 +1,25 @@
|
||||
# Qt core mimetype module
|
||||
|
||||
HEADERS += \
|
||||
mimetypes/qmimedatabase.h \
|
||||
mimetypes/qmimetype.h \
|
||||
mimetypes/qmimemagicrulematcher_p.h \
|
||||
mimetypes/qmimetype_p.h \
|
||||
mimetypes/qmimetypeparser_p.h \
|
||||
mimetypes/qmimedatabase_p.h \
|
||||
mimetypes/qmimemagicrule_p.h \
|
||||
mimetypes/qmimeglobpattern_p.h \
|
||||
mimetypes/qmimeprovider_p.h
|
||||
|
||||
SOURCES += \
|
||||
mimetypes/qmimedatabase.cpp \
|
||||
mimetypes/qmimetype.cpp \
|
||||
mimetypes/qmimemagicrulematcher.cpp \
|
||||
mimetypes/qmimetypeparser.cpp \
|
||||
mimetypes/qmimemagicrule.cpp \
|
||||
mimetypes/qmimeglobpattern.cpp \
|
||||
mimetypes/qmimeprovider.cpp
|
||||
|
||||
RESOURCES += \
|
||||
mimetypes/mimetypes.qrc
|
||||
|
5
src/corelib/mimetypes/mimetypes.qrc
Normal file
5
src/corelib/mimetypes/mimetypes.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/qt-project.org/qmime">
|
||||
<file alias="freedesktop.org.xml">mime/packages/freedesktop.org.xml</file>
|
||||
</qresource>
|
||||
</RCC>
|
609
src/corelib/mimetypes/qmimedatabase.cpp
Normal file
609
src/corelib/mimetypes/qmimedatabase.cpp
Normal file
@ -0,0 +1,609 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qplatformdefs.h> // always first
|
||||
|
||||
#include "qmimedatabase.h"
|
||||
|
||||
#include "qmimedatabase_p.h"
|
||||
|
||||
#include "qmimeprovider_p.h"
|
||||
#include "qmimetype_p.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QStack>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
bool qt_isQMimeDatabaseDebuggingActivated (false);
|
||||
|
||||
#ifndef QT_NO_DEBUG_OUTPUT
|
||||
#define DBG() if (qt_isQMimeDatabaseDebuggingActivated) qDebug() << static_cast<const void *>(this) << Q_FUNC_INFO
|
||||
#else
|
||||
#define DBG() if (0) qDebug() << static_cast<const void *>(this) << Q_FUNC_INFO
|
||||
#endif
|
||||
|
||||
Q_GLOBAL_STATIC(QMimeDatabasePrivate, staticQMimeDatabase)
|
||||
|
||||
QMimeDatabasePrivate *QMimeDatabasePrivate::instance()
|
||||
{
|
||||
return staticQMimeDatabase();
|
||||
}
|
||||
|
||||
QMimeDatabasePrivate::QMimeDatabasePrivate()
|
||||
: m_provider(0), m_defaultMimeType(QLatin1String("application/octet-stream"))
|
||||
{
|
||||
}
|
||||
|
||||
QMimeDatabasePrivate::~QMimeDatabasePrivate()
|
||||
{
|
||||
delete m_provider;
|
||||
m_provider = 0;
|
||||
}
|
||||
|
||||
QMimeProviderBase *QMimeDatabasePrivate::provider()
|
||||
{
|
||||
if (!m_provider) {
|
||||
QMimeProviderBase *binaryProvider = new QMimeBinaryProvider(this);
|
||||
if (binaryProvider->isValid()) {
|
||||
m_provider = binaryProvider;
|
||||
} else {
|
||||
delete binaryProvider;
|
||||
m_provider = new QMimeXMLProvider(this);
|
||||
}
|
||||
}
|
||||
return m_provider;
|
||||
}
|
||||
|
||||
void QMimeDatabasePrivate::setProvider(QMimeProviderBase *theProvider)
|
||||
{
|
||||
delete m_provider;
|
||||
m_provider = theProvider;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Returns a MIME type or an invalid one if none found
|
||||
*/
|
||||
QMimeType QMimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias)
|
||||
{
|
||||
return provider()->mimeTypeForName(provider()->resolveAlias(nameOrAlias));
|
||||
}
|
||||
|
||||
QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString &fileName, QString *foundSuffix)
|
||||
{
|
||||
if (fileName.endsWith(QLatin1Char('/')))
|
||||
return QStringList() << QLatin1String("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;
|
||||
}
|
||||
|
||||
QMimeType QMimeDatabasePrivate::findByData(const QByteArray &data, int *accuracyPtr)
|
||||
{
|
||||
if (data.isEmpty()) {
|
||||
*accuracyPtr = 100;
|
||||
return mimeTypeForName(QLatin1String("application/x-zerosize"));
|
||||
}
|
||||
|
||||
*accuracyPtr = 0;
|
||||
QMimeType candidate = provider()->findByMagic(data, accuracyPtr);
|
||||
|
||||
if (candidate.isValid())
|
||||
return candidate;
|
||||
|
||||
if (isTextFile(data)) {
|
||||
*accuracyPtr = 5;
|
||||
return mimeTypeForName(QLatin1String("text/plain"));
|
||||
}
|
||||
|
||||
return mimeTypeForName(defaultMimeType());
|
||||
}
|
||||
|
||||
QMimeType QMimeDatabasePrivate::mimeTypeForNameAndData(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 QMimeType 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
|
||||
if (device->isOpen()) {
|
||||
|
||||
// Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h).
|
||||
// This is much faster than seeking back and forth into QIODevice.
|
||||
const QByteArray data = device->peek(16384);
|
||||
|
||||
int magicAccuracy = 0;
|
||||
QMimeType candidateByData(findByData(data, &magicAccuracy));
|
||||
|
||||
// Disambiguate conflicting extensions (if magic matching found something)
|
||||
if (candidateByData.isValid() && magicAccuracy > 0) {
|
||||
// "for glob_match in glob_matches:"
|
||||
// "if glob_match is subclass or equal to sniffed_type, use glob_match"
|
||||
const QString sniffedMime = candidateByData.name();
|
||||
foreach (const QString &m, 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 QMimeType mime = mimeTypeForName(candidatesByName.at(0));
|
||||
if (mime.isValid())
|
||||
return mime;
|
||||
}
|
||||
|
||||
return mimeTypeForName(defaultMimeType());
|
||||
}
|
||||
|
||||
QList<QMimeType> QMimeDatabasePrivate::allMimeTypes()
|
||||
{
|
||||
return provider()->allMimeTypes();
|
||||
}
|
||||
|
||||
bool QMimeDatabasePrivate::inherits(const QString &mime, const QString &parent)
|
||||
{
|
||||
const QString resolvedParent = provider()->resolveAlias(parent);
|
||||
//Q_ASSERT(provider()->resolveAlias(mime) == mime);
|
||||
QStack<QString> toCheck;
|
||||
toCheck.push(mime);
|
||||
while (!toCheck.isEmpty()) {
|
||||
const QString current = toCheck.pop();
|
||||
if (current == resolvedParent)
|
||||
return true;
|
||||
foreach (const QString &par, provider()->parents(current))
|
||||
toCheck.push(par);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QMimeDatabase
|
||||
\brief The QMimeDatabase class maintains a database of MIME types.
|
||||
|
||||
\since 5.0
|
||||
|
||||
The MIME type database is provided by the freedesktop.org shared-mime-info
|
||||
project. If the MIME type database cannot be found on the system, as is the case
|
||||
on most Windows 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
|
||||
XDG_DATA_DIRS. For instance adding /opt/myapp/share to 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.nokia.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 doc/src/snippets/code/src_corelib_mimetype_qmimedatabase.cpp 0
|
||||
|
||||
\sa QMimeType
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QMimeDatabase::QMimeDatabase();
|
||||
Constructs this QMimeDatabase object.
|
||||
*/
|
||||
QMimeDatabase::QMimeDatabase() :
|
||||
d(staticQMimeDatabase())
|
||||
{
|
||||
DBG();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeDatabase::~QMimeDatabase();
|
||||
Destroys the QMimeDatabase object.
|
||||
*/
|
||||
QMimeDatabase::~QMimeDatabase()
|
||||
{
|
||||
DBG();
|
||||
|
||||
d = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const;
|
||||
Returns a MIME type for \a nameOrAlias or an invalid one if none found.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
return d->mimeTypeForName(nameOrAlias);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for \a fileInfo.
|
||||
|
||||
A valid MIME type is always returned.
|
||||
|
||||
The default matching algorithm looks at both the file name and the file
|
||||
contents, if necessary. The file extension has priority over the contents,
|
||||
but the contents will be used if the file extension is unknown, or
|
||||
matches multiple MIME types.
|
||||
If \a fileInfo is a Unix symbolic link, the file that it refers to
|
||||
will be used instead.
|
||||
If the file doesn't match any known pattern or data, the default MIME type
|
||||
(application/octet-stream) is returned.
|
||||
|
||||
When \a mode is set to MatchExtension, only the file name is used, not
|
||||
the file contents. The file doesn't even have to exist. If the file name
|
||||
doesn't match any known pattern, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
If multiple MIME types match this file, the first one (alphabetically) is returned.
|
||||
|
||||
When \a mode is set to MatchContent, and the file is readable, only the
|
||||
file contents are used to determine the MIME type. This is equivalent to
|
||||
calling mimeTypeForData with a QFile as input device.
|
||||
|
||||
In all cases, the \a fileName can also include an absolute or relative path.
|
||||
|
||||
\sa isDefault, mimeTypeForData
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const
|
||||
{
|
||||
DBG() << "fileInfo" << fileInfo.absoluteFilePath();
|
||||
|
||||
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:
|
||||
file.open(QIODevice::ReadOnly); // isOpen() will be tested by method below
|
||||
return d->mimeTypeForNameAndData(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
|
||||
*/
|
||||
QMimeType QMimeDatabase::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);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeType QMimeDatabase::findMimeTypesByFileName(const QString &fileName) const;
|
||||
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
|
||||
mimeTypeForNameAndData() instead.
|
||||
|
||||
\sa mimeTypeForFile
|
||||
*/
|
||||
QList<QMimeType> QMimeDatabase::mimeTypesForFileName(const QString &fileName) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
QStringList matches = d->mimeTypeForFileName(fileName);
|
||||
QList<QMimeType> mimes;
|
||||
matches.sort(); // Make it deterministic
|
||||
foreach (const QString &mime, matches)
|
||||
mimes.append(d->mimeTypeForName(mime));
|
||||
return mimes;
|
||||
}
|
||||
/*!
|
||||
Returns the suffix for the file \a fileName, as known by the MIME database.
|
||||
|
||||
This allows to pre-select "tar.bz2" for foo.tar.bz2, but still only
|
||||
"txt" for my.file.with.dots.txt.
|
||||
*/
|
||||
QString QMimeDatabase::suffixForFileName(const QString &fileName) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
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.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForData(const QByteArray &data) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
int accuracy = 0;
|
||||
return d->findByData(data, &accuracy);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for the data in \a device.
|
||||
|
||||
A valid MIME type is always returned. If the data in \a device doesn't match any
|
||||
known MIME type data, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForData(QIODevice *device) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
int accuracy = 0;
|
||||
const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
|
||||
if (device->isOpen()) {
|
||||
// Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h).
|
||||
// This is much faster than seeking back and forth into QIODevice.
|
||||
const QByteArray data = device->peek(16384);
|
||||
const QMimeType result = d->findByData(data, &accuracy);
|
||||
if (openedByUs)
|
||||
device->close();
|
||||
return result;
|
||||
}
|
||||
return d->mimeTypeForName(d->defaultMimeType());
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for \a url.
|
||||
|
||||
If the URL is a local file, this calls mimeTypeForFile.
|
||||
|
||||
Otherwise the matching is done based on the file name only,
|
||||
except for schemes where file names don't mean much, like HTTP.
|
||||
This method always returns the default mimetype for HTTP URLs,
|
||||
use QNetworkAccessManager to handle HTTP URLs properly.
|
||||
|
||||
A valid MIME type is always returned. If \a url doesn't match any
|
||||
known MIME type data, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForUrl(const QUrl &url) const
|
||||
{
|
||||
if (url.isLocalFile())
|
||||
return mimeTypeForFile(url.toLocalFile());
|
||||
|
||||
const QString scheme = url.scheme();
|
||||
if (scheme.startsWith(QLatin1String("http")))
|
||||
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.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForNameAndData(const QString &fileName, QIODevice *device) const
|
||||
{
|
||||
DBG() << "fileName" << fileName;
|
||||
|
||||
int accuracy = 0;
|
||||
const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly);
|
||||
const QMimeType result = d->mimeTypeForNameAndData(fileName, device, &accuracy);
|
||||
if (openedByUs)
|
||||
device->close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a MIME type for the given \a fileName and device \a data.
|
||||
|
||||
This overload can be useful when the file is remote, and we started to
|
||||
download some of its data. This allows to do full MIME type matching for
|
||||
remote files as well.
|
||||
|
||||
A valid MIME type is always returned. If \a data doesn't match any
|
||||
known MIME type data, the default MIME type (application/octet-stream)
|
||||
is returned.
|
||||
|
||||
This method looks at both the file name and the file contents,
|
||||
if necessary. The file extension has priority over the contents,
|
||||
but the contents will be used if the file extension is unknown, or
|
||||
matches multiple MIME types.
|
||||
*/
|
||||
QMimeType QMimeDatabase::mimeTypeForNameAndData(const QString &fileName, const QByteArray &data) const
|
||||
{
|
||||
DBG() << "fileName" << fileName;
|
||||
|
||||
QBuffer buffer(const_cast<QByteArray *>(&data));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
int accuracy = 0;
|
||||
return d->mimeTypeForNameAndData(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 mimeTypeFor* methods for performance reasons.
|
||||
*/
|
||||
QList<QMimeType> QMimeDatabase::allMimeTypes() const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
return d->allMimeTypes();
|
||||
}
|
||||
|
||||
#undef DBG
|
||||
|
||||
QT_END_NAMESPACE
|
95
src/corelib/mimetypes/qmimedatabase.h
Normal file
95
src/corelib/mimetypes/qmimedatabase.h
Normal file
@ -0,0 +1,95 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QMIMEDATABASE_H
|
||||
#define QMIMEDATABASE_H
|
||||
|
||||
#include "qmimetype.h"
|
||||
|
||||
#include <QtCore/qstringlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QByteArray;
|
||||
class QFileInfo;
|
||||
class QIODevice;
|
||||
class QUrl;
|
||||
|
||||
class QMimeDatabasePrivate;
|
||||
class Q_CORE_EXPORT QMimeDatabase
|
||||
{
|
||||
Q_DISABLE_COPY(QMimeDatabase)
|
||||
|
||||
public:
|
||||
QMimeDatabase();
|
||||
~QMimeDatabase();
|
||||
|
||||
QMimeType mimeTypeForName(const QString &nameOrAlias) const;
|
||||
|
||||
enum MatchMode {
|
||||
MatchDefault = 0x0,
|
||||
MatchExtension = 0x1,
|
||||
MatchContent = 0x2
|
||||
};
|
||||
|
||||
QMimeType mimeTypeForFile(const QString &fileName, MatchMode mode = MatchDefault) const;
|
||||
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode = MatchDefault) const;
|
||||
QList<QMimeType> mimeTypesForFileName(const QString &fileName) const;
|
||||
|
||||
QMimeType mimeTypeForData(const QByteArray &data) const;
|
||||
QMimeType mimeTypeForData(QIODevice *device) const;
|
||||
|
||||
QMimeType mimeTypeForUrl(const QUrl &url) const;
|
||||
QMimeType mimeTypeForNameAndData(const QString &fileName, QIODevice *device) const;
|
||||
QMimeType mimeTypeForNameAndData(const QString &fileName, const QByteArray &data) const;
|
||||
|
||||
QString suffixForFileName(const QString &fileName) const;
|
||||
|
||||
QList<QMimeType> allMimeTypes() const;
|
||||
|
||||
private:
|
||||
QMimeDatabasePrivate *d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEDATABASE_H
|
90
src/corelib/mimetypes/qmimedatabase_p.h
Normal file
90
src/corelib/mimetypes/qmimedatabase_p.h
Normal file
@ -0,0 +1,90 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QMIMEDATABASE_P_H
|
||||
#define QMIMEDATABASE_P_H
|
||||
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include "qmimetype.h"
|
||||
#include "qmimetype_p.h"
|
||||
#include "qmimeglobpattern_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeDatabase;
|
||||
class QMimeProviderBase;
|
||||
|
||||
class QMimeDatabasePrivate
|
||||
{
|
||||
public:
|
||||
Q_DISABLE_COPY(QMimeDatabasePrivate)
|
||||
|
||||
QMimeDatabasePrivate();
|
||||
~QMimeDatabasePrivate();
|
||||
|
||||
static QMimeDatabasePrivate *instance();
|
||||
|
||||
QMimeProviderBase *provider();
|
||||
void setProvider(QMimeProviderBase *theProvider);
|
||||
|
||||
inline QString defaultMimeType() const { return m_defaultMimeType; }
|
||||
|
||||
bool inherits(const QString &mime, const QString &parent);
|
||||
|
||||
QList<QMimeType> allMimeTypes();
|
||||
|
||||
|
||||
QMimeType mimeTypeForName(const QString &nameOrAlias);
|
||||
QMimeType mimeTypeForNameAndData(const QString &fileName, QIODevice *device, int *priorityPtr);
|
||||
QMimeType findByData(const QByteArray &data, int *priorityPtr);
|
||||
QStringList mimeTypeForFileName(const QString &fileName, QString *foundSuffix = 0);
|
||||
|
||||
mutable QMimeProviderBase *m_provider;
|
||||
const QString m_defaultMimeType;
|
||||
QMutex mutex;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEDATABASE_P_H
|
240
src/corelib/mimetypes/qmimeglobpattern.cpp
Normal file
240
src/corelib/mimetypes/qmimeglobpattern.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmimeglobpattern_p.h"
|
||||
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\class QMimeGlobMatchResult
|
||||
\brief The QMimeGlobMatchResult class accumulates results from glob matching.
|
||||
|
||||
Handles glob weights, and preferring longer matches over shorter matches.
|
||||
*/
|
||||
|
||||
void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const QString &pattern)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
m_matchingMimeTypes.append(mimeType);
|
||||
if (pattern.startsWith(QLatin1String("*.")))
|
||||
m_foundSuffix = pattern.mid(2);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\class QMimeGlobPattern
|
||||
\brief The QMimeGlobPattern class contains the glob pattern for file names for MIME type matching.
|
||||
|
||||
\sa QMimeType, QMimeDatabase, QMimeMagicRuleMatcher, QMimeMagicRule
|
||||
*/
|
||||
|
||||
bool QMimeGlobPattern::matchFileName(const QString &inputFilename) const
|
||||
{
|
||||
// "Applications MUST match globs case-insensitively, except when the case-sensitive
|
||||
// attribute is set to true."
|
||||
// The constructor takes care of putting case-insensitive patterns in lowercase.
|
||||
const QString filename = m_caseSensitivity == Qt::CaseInsensitive ? inputFilename.toLower() : inputFilename;
|
||||
|
||||
const int pattern_len = m_pattern.length();
|
||||
if (!pattern_len)
|
||||
return false;
|
||||
const int len = filename.length();
|
||||
|
||||
const int starCount = m_pattern.count(QLatin1Char('*'));
|
||||
|
||||
// Patterns like "*~", "*.extension"
|
||||
if (m_pattern[0] == QLatin1Char('*') && m_pattern.indexOf(QLatin1Char('[')) == -1 && starCount == 1)
|
||||
{
|
||||
if (len + 1 < pattern_len) return false;
|
||||
|
||||
const QChar *c1 = m_pattern.unicode() + pattern_len - 1;
|
||||
const QChar *c2 = filename.unicode() + len - 1;
|
||||
int cnt = 1;
|
||||
while (cnt < pattern_len && *c1-- == *c2--)
|
||||
++cnt;
|
||||
return cnt == pattern_len;
|
||||
}
|
||||
|
||||
// Patterns like "README*" (well this is currently the only one like that...)
|
||||
if (starCount == 1 && m_pattern.at(pattern_len - 1) == QLatin1Char('*')) {
|
||||
if (len + 1 < pattern_len) return false;
|
||||
if (m_pattern.at(0) == QLatin1Char('*'))
|
||||
return filename.indexOf(m_pattern.mid(1, pattern_len - 2)) != -1;
|
||||
|
||||
const QChar *c1 = m_pattern.unicode();
|
||||
const QChar *c2 = filename.unicode();
|
||||
int cnt = 1;
|
||||
while (cnt < pattern_len && *c1++ == *c2++)
|
||||
++cnt;
|
||||
return cnt == pattern_len;
|
||||
}
|
||||
|
||||
// Names without any wildcards like "README"
|
||||
if (m_pattern.indexOf(QLatin1Char('[')) == -1 && starCount == 0 && m_pattern.indexOf(QLatin1Char('?')))
|
||||
return (m_pattern == filename);
|
||||
|
||||
// Other (quite rare) patterns, like "*.anim[1-9j]": use slow but correct method
|
||||
const QRegExp rx(m_pattern, Qt::CaseSensitive, QRegExp::WildcardUnix);
|
||||
return rx.exactMatch(filename);
|
||||
}
|
||||
|
||||
static bool isFastPattern(const QString &pattern)
|
||||
{
|
||||
// starts with "*.", has no other '*' and no other '.'
|
||||
return pattern.lastIndexOf(QLatin1Char('*')) == 0
|
||||
&& pattern.lastIndexOf(QLatin1Char('.')) == 1
|
||||
// and contains no other special character
|
||||
&& !pattern.contains(QLatin1Char('?'))
|
||||
&& !pattern.contains(QLatin1Char('['))
|
||||
;
|
||||
}
|
||||
|
||||
void QMimeAllGlobPatterns::addGlob(const QMimeGlobPattern &glob)
|
||||
{
|
||||
const QString &pattern = glob.pattern();
|
||||
Q_ASSERT(!pattern.isEmpty());
|
||||
|
||||
// Store each patterns into either m_fastPatternDict (*.txt, *.html etc. with default weight 50)
|
||||
// or for the rest, like core.*, *.tar.bz2, *~, into highWeightPatternOffset (>50)
|
||||
// or lowWeightPatternOffset (<=50)
|
||||
|
||||
if (glob.weight() == 50 && isFastPattern(pattern) && !glob.isCaseSensitive()) {
|
||||
// The bulk of the patterns is *.foo with weight 50 --> those go into the fast patterns hash.
|
||||
const QString extension = pattern.mid(2).toLower();
|
||||
QStringList &patterns = m_fastPatterns[extension]; // find or create
|
||||
// This would just slow things down: if (!patterns.contains(glob.mimeType()))
|
||||
patterns.append(glob.mimeType());
|
||||
} else {
|
||||
if (glob.weight() > 50) {
|
||||
// This would just slow things down: if (!m_highWeightGlobs.hasPattern(glob.mimeType(), glob.pattern()))
|
||||
m_highWeightGlobs.append(glob);
|
||||
} else {
|
||||
//This would just slow things down: if (!m_lowWeightGlobs.hasPattern(glob.mimeType(), glob.pattern()))
|
||||
m_lowWeightGlobs.append(glob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeAllGlobPatterns::removeMimeType(const QString &mimeType)
|
||||
{
|
||||
QMutableHashIterator<QString, QStringList> it(m_fastPatterns);
|
||||
while (it.hasNext()) {
|
||||
it.next().value().removeAll(mimeType);
|
||||
}
|
||||
m_highWeightGlobs.removeMimeType(mimeType);
|
||||
m_lowWeightGlobs.removeMimeType(mimeType);
|
||||
}
|
||||
|
||||
void QMimeGlobPatternList::match(QMimeGlobMatchResult &result,
|
||||
const QString &fileName) const
|
||||
{
|
||||
|
||||
QMimeGlobPatternList::const_iterator it = this->constBegin();
|
||||
const QMimeGlobPatternList::const_iterator endIt = this->constEnd();
|
||||
for (; it != endIt; ++it) {
|
||||
const QMimeGlobPattern &glob = *it;
|
||||
if (glob.matchFileName(fileName))
|
||||
result.addMatch(glob.mimeType(), glob.weight(), glob.pattern());
|
||||
}
|
||||
}
|
||||
|
||||
QStringList QMimeAllGlobPatterns::matchingGlobs(const QString &fileName, QString *foundSuffix) const
|
||||
{
|
||||
// First try the high weight matches (>50), if any.
|
||||
QMimeGlobMatchResult 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);
|
||||
foreach (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 QMimeAllGlobPatterns::clear()
|
||||
{
|
||||
m_fastPatterns.clear();
|
||||
m_highWeightGlobs.clear();
|
||||
m_lowWeightGlobs.clear();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
146
src/corelib/mimetypes/qmimeglobpattern_p.h
Normal file
146
src/corelib/mimetypes/qmimeglobpattern_p.h
Normal file
@ -0,0 +1,146 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMEGLOBPATTERN_P_H
|
||||
#define QMIMEGLOBPATTERN_P_H
|
||||
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qhash.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct QMimeGlobMatchResult
|
||||
{
|
||||
QMimeGlobMatchResult()
|
||||
: m_weight(0), m_matchingPatternLength(0)
|
||||
{}
|
||||
|
||||
void addMatch(const QString &mimeType, int weight, const QString &pattern);
|
||||
|
||||
QStringList m_matchingMimeTypes;
|
||||
int m_weight;
|
||||
int m_matchingPatternLength;
|
||||
QString m_foundSuffix;
|
||||
};
|
||||
|
||||
class QMimeGlobPattern
|
||||
{
|
||||
public:
|
||||
static const unsigned MaxWeight = 100;
|
||||
static const unsigned DefaultWeight = 50;
|
||||
static const unsigned MinWeight = 1;
|
||||
|
||||
explicit QMimeGlobPattern(const QString &thePattern, const QString &theMimeType, unsigned theWeight = DefaultWeight, Qt::CaseSensitivity s = Qt::CaseInsensitive) :
|
||||
m_pattern(thePattern), m_mimeType(theMimeType), m_weight(theWeight), m_caseSensitivity(s)
|
||||
{
|
||||
if (s == Qt::CaseInsensitive) {
|
||||
m_pattern = m_pattern.toLower();
|
||||
}
|
||||
}
|
||||
~QMimeGlobPattern() {}
|
||||
|
||||
bool matchFileName(const QString &filename) 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:
|
||||
QString m_pattern;
|
||||
QString m_mimeType;
|
||||
int m_weight;
|
||||
Qt::CaseSensitivity m_caseSensitivity;
|
||||
};
|
||||
|
||||
class QMimeGlobPatternList : public QList<QMimeGlobPattern>
|
||||
{
|
||||
public:
|
||||
bool hasPattern(const QString &mimeType, const QString &pattern) const
|
||||
{
|
||||
const_iterator it = begin();
|
||||
const const_iterator myend = end();
|
||||
for (; it != myend; ++it)
|
||||
if ((*it).pattern() == pattern && (*it).mimeType() == mimeType)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
"noglobs" is very rare occurrence, so it's ok if it's slow
|
||||
*/
|
||||
void removeMimeType(const QString &mimeType)
|
||||
{
|
||||
QMutableListIterator<QMimeGlobPattern> it(*this);
|
||||
while (it.hasNext()) {
|
||||
if (it.next().mimeType() == mimeType)
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
void match(QMimeGlobMatchResult &result, const QString &fileName) const;
|
||||
};
|
||||
|
||||
/*!
|
||||
Result of the globs parsing, as data structures ready for efficient MIME type matching.
|
||||
This contains:
|
||||
1) a map of fast regular patterns (e.g. *.txt is stored as "txt" in a qhash's key)
|
||||
2) a linear list of high-weight globs
|
||||
3) a linear list of low-weight globs
|
||||
*/
|
||||
class QMimeAllGlobPatterns
|
||||
{
|
||||
public:
|
||||
typedef QHash<QString, QStringList> PatternsMap; // MIME type -> patterns
|
||||
|
||||
void addGlob(const QMimeGlobPattern &glob);
|
||||
void removeMimeType(const QString &mimeType);
|
||||
QStringList matchingGlobs(const QString &fileName, QString *foundSuffix) const;
|
||||
void clear();
|
||||
|
||||
PatternsMap m_fastPatterns; // example: "doc" -> "application/msword", "text/plain"
|
||||
QMimeGlobPatternList m_highWeightGlobs;
|
||||
QMimeGlobPatternList m_lowWeightGlobs; // <= 50, including the non-fast 50 patterns
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEGLOBPATTERN_P_H
|
387
src/corelib/mimetypes/qmimemagicrule.cpp
Normal file
387
src/corelib/mimetypes/qmimemagicrule.cpp
Normal file
@ -0,0 +1,387 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#define QT_NO_CAST_FROM_ASCII
|
||||
|
||||
#include "qmimemagicrule_p.h"
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QDebug>
|
||||
#include <qendian.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// in the same order as Type!
|
||||
static const char magicRuleTypes_string[] =
|
||||
"invalid\0"
|
||||
"string\0"
|
||||
"host16\0"
|
||||
"host32\0"
|
||||
"big16\0"
|
||||
"big32\0"
|
||||
"little16\0"
|
||||
"little32\0"
|
||||
"byte\0"
|
||||
"\0";
|
||||
|
||||
static const int magicRuleTypes_indices[] = {
|
||||
0, 8, 15, 22, 29, 35, 41, 50, 59, 65, 0
|
||||
};
|
||||
|
||||
QMimeMagicRule::Type QMimeMagicRule::type(const QByteArray &theTypeName)
|
||||
{
|
||||
for (int i = String; i <= Byte; ++i) {
|
||||
if (theTypeName == magicRuleTypes_string + magicRuleTypes_indices[i])
|
||||
return Type(i);
|
||||
}
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
QByteArray QMimeMagicRule::typeName(QMimeMagicRule::Type theType)
|
||||
{
|
||||
return magicRuleTypes_string + magicRuleTypes_indices[theType];
|
||||
}
|
||||
|
||||
class QMimeMagicRulePrivate
|
||||
{
|
||||
public:
|
||||
bool operator==(const QMimeMagicRulePrivate &other) const;
|
||||
|
||||
QMimeMagicRule::Type type;
|
||||
QByteArray value;
|
||||
int startPos;
|
||||
int endPos;
|
||||
QByteArray mask;
|
||||
|
||||
QByteArray pattern;
|
||||
quint32 number;
|
||||
quint32 numberMask;
|
||||
|
||||
typedef bool (*MatchFunction)(const QMimeMagicRulePrivate *d, const QByteArray &data);
|
||||
MatchFunction matchFunction;
|
||||
};
|
||||
|
||||
bool QMimeMagicRulePrivate::operator==(const QMimeMagicRulePrivate &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;
|
||||
}
|
||||
|
||||
// Used by both providers
|
||||
bool QMimeMagicRule::matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength,
|
||||
int valueLength, const char *valueData, const char *mask)
|
||||
{
|
||||
// Size of searched data.
|
||||
// Example: value="ABC", rangeLength=3 -> we need 3+3-1=5 bytes (ABCxx,xABCx,xxABC would match)
|
||||
const int dataNeeded = qMin(rangeLength + valueLength - 1, dataSize - rangeStart);
|
||||
|
||||
if (!mask) {
|
||||
// callgrind says QByteArray::indexOf is much slower, since our strings are typically too
|
||||
// short for be worth Boyer-Moore matching (1 to 71 bytes, 11 bytes on average).
|
||||
bool found = false;
|
||||
for (int i = rangeStart; i < rangeStart + rangeLength; ++i) {
|
||||
if (i + valueLength > dataSize)
|
||||
break;
|
||||
|
||||
if (memcmp(valueData, dataPtr + i, valueLength) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return false;
|
||||
} else {
|
||||
bool found = false;
|
||||
const char *readDataBase = dataPtr + rangeStart;
|
||||
// Example (continued from above):
|
||||
// deviceSize is 4, so dataNeeded was max'ed to 4.
|
||||
// maxStartPos = 4 - 3 + 1 = 2, and indeed
|
||||
// we need to check for a match a positions 0 and 1 (ABCx and xABC).
|
||||
const int maxStartPos = dataNeeded - valueLength + 1;
|
||||
for (int i = 0; i < maxStartPos; ++i) {
|
||||
const char *d = readDataBase + i;
|
||||
bool valid = true;
|
||||
for (int idx = 0; idx < valueLength; ++idx) {
|
||||
if (((*d++) & mask[idx]) != (valueData[idx] & mask[idx])) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
return false;
|
||||
}
|
||||
//qDebug() << "Found" << value << "in" << searchedData;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool matchString(const QMimeMagicRulePrivate *d, const QByteArray &data)
|
||||
{
|
||||
const int rangeLength = d->endPos - d->startPos + 1;
|
||||
return QMimeMagicRule::matchSubstring(data.constData(), data.size(), d->startPos, rangeLength, d->pattern.size(), d->pattern.constData(), d->mask.constData());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool matchNumber(const QMimeMagicRulePrivate *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;
|
||||
}
|
||||
|
||||
QMimeMagicRule::QMimeMagicRule(QMimeMagicRule::Type theType,
|
||||
const QByteArray &theValue,
|
||||
int theStartPos,
|
||||
int theEndPos,
|
||||
const QByteArray &theMask) :
|
||||
d(new QMimeMagicRulePrivate)
|
||||
{
|
||||
Q_ASSERT(!theValue.isEmpty());
|
||||
|
||||
d->type = theType;
|
||||
d->value = theValue;
|
||||
d->startPos = theStartPos;
|
||||
d->endPos = theEndPos;
|
||||
d->mask = theMask;
|
||||
d->matchFunction = 0;
|
||||
|
||||
if (d->type >= Host16 && d->type <= Byte) {
|
||||
bool ok;
|
||||
d->number = d->value.toUInt(&ok, 0); // autodetect
|
||||
Q_ASSERT(ok);
|
||||
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()) {
|
||||
Q_ASSERT(d->mask.size() >= 4 && d->mask.startsWith("0x"));
|
||||
d->mask = QByteArray::fromHex(QByteArray::fromRawData(d->mask.constData() + 2, d->mask.size() - 2));
|
||||
Q_ASSERT(d->mask.size() == d->pattern.size());
|
||||
} else {
|
||||
d->mask.fill(static_cast<char>(0xff), d->pattern.size());
|
||||
}
|
||||
d->mask.squeeze();
|
||||
d->matchFunction = matchString;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
QMimeMagicRule::QMimeMagicRule(const QMimeMagicRule &other) :
|
||||
d(new QMimeMagicRulePrivate(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
QMimeMagicRule::~QMimeMagicRule()
|
||||
{
|
||||
}
|
||||
|
||||
QMimeMagicRule &QMimeMagicRule::operator=(const QMimeMagicRule &other)
|
||||
{
|
||||
*d = *other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool QMimeMagicRule::operator==(const QMimeMagicRule &other) const
|
||||
{
|
||||
return d == other.d ||
|
||||
*d == *other.d;
|
||||
}
|
||||
|
||||
QMimeMagicRule::Type QMimeMagicRule::type() const
|
||||
{
|
||||
return d->type;
|
||||
}
|
||||
|
||||
QByteArray QMimeMagicRule::value() const
|
||||
{
|
||||
return d->value;
|
||||
}
|
||||
|
||||
int QMimeMagicRule::startPos() const
|
||||
{
|
||||
return d->startPos;
|
||||
}
|
||||
|
||||
int QMimeMagicRule::endPos() const
|
||||
{
|
||||
return d->endPos;
|
||||
}
|
||||
|
||||
QByteArray QMimeMagicRule::mask() const
|
||||
{
|
||||
QByteArray result = d->mask;
|
||||
if (d->type == String) {
|
||||
// restore '0x'
|
||||
result = "0x" + result.toHex();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QMimeMagicRule::isValid() const
|
||||
{
|
||||
return d->matchFunction;
|
||||
}
|
||||
|
||||
bool QMimeMagicRule::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<QMimeMagicRule>::const_iterator it = m_subMatches.begin(), end = m_subMatches.end() ;
|
||||
it != end ; ++it ) {
|
||||
if ((*it).matches(data)) {
|
||||
// One of the hierarchies matched -> mimetype recognized.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
89
src/corelib/mimetypes/qmimemagicrule_p.h
Normal file
89
src/corelib/mimetypes/qmimemagicrule_p.h
Normal file
@ -0,0 +1,89 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QMIMEMAGICRULE_P_H
|
||||
#define QMIMEMAGICRULE_P_H
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeMagicRulePrivate;
|
||||
class QMimeMagicRule
|
||||
{
|
||||
public:
|
||||
enum Type { Invalid = 0, String, Host16, Host32, Big16, Big32, Little16, Little32, Byte };
|
||||
|
||||
QMimeMagicRule(Type type, const QByteArray &value, int startPos, int endPos, const QByteArray &mask = QByteArray());
|
||||
QMimeMagicRule(const QMimeMagicRule &other);
|
||||
~QMimeMagicRule();
|
||||
|
||||
QMimeMagicRule &operator=(const QMimeMagicRule &other);
|
||||
|
||||
bool operator==(const QMimeMagicRule &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<QMimeMagicRule> m_subMatches;
|
||||
|
||||
static Type type(const QByteArray &type);
|
||||
static QByteArray typeName(Type type);
|
||||
|
||||
static bool matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, int valueLength, const char *valueData, const char *mask);
|
||||
|
||||
private:
|
||||
const QScopedPointer<QMimeMagicRulePrivate> d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEMAGICRULE_H
|
108
src/corelib/mimetypes/qmimemagicrulematcher.cpp
Normal file
108
src/corelib/mimetypes/qmimemagicrulematcher.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#define QT_NO_CAST_FROM_ASCII
|
||||
|
||||
#include "qmimemagicrulematcher_p.h"
|
||||
|
||||
#include "qmimetype_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\class QMimeMagicRuleMatcher
|
||||
|
||||
\brief The QMimeMagicRuleMatcher class checks a number of rules based on operator "or".
|
||||
|
||||
It is used for rules parsed from XML files.
|
||||
|
||||
\sa QMimeType, QMimeDatabase, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
||||
\sa QMimeTypeParserBase, MimeTypeParser
|
||||
*/
|
||||
|
||||
QMimeMagicRuleMatcher::QMimeMagicRuleMatcher(const QString &mime, unsigned thePriority) :
|
||||
m_list(),
|
||||
m_priority(thePriority),
|
||||
m_mimetype(mime)
|
||||
{
|
||||
}
|
||||
|
||||
bool QMimeMagicRuleMatcher::operator==(const QMimeMagicRuleMatcher &other)
|
||||
{
|
||||
return m_list == other.m_list &&
|
||||
m_priority == other.m_priority;
|
||||
}
|
||||
|
||||
void QMimeMagicRuleMatcher::addRule(const QMimeMagicRule &rule)
|
||||
{
|
||||
m_list.append(rule);
|
||||
}
|
||||
|
||||
void QMimeMagicRuleMatcher::addRules(const QList<QMimeMagicRule> &rules)
|
||||
{
|
||||
m_list.append(rules);
|
||||
}
|
||||
|
||||
QList<QMimeMagicRule> QMimeMagicRuleMatcher::magicRules() const
|
||||
{
|
||||
return m_list;
|
||||
}
|
||||
|
||||
// Check for a match on contents of a file
|
||||
bool QMimeMagicRuleMatcher::matches(const QByteArray &data) const
|
||||
{
|
||||
foreach (const QMimeMagicRule &magicRule, m_list) {
|
||||
if (magicRule.matches(data))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return a priority value from 1..100
|
||||
unsigned QMimeMagicRuleMatcher::priority() const
|
||||
{
|
||||
return m_priority;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
79
src/corelib/mimetypes/qmimemagicrulematcher_p.h
Normal file
79
src/corelib/mimetypes/qmimemagicrulematcher_p.h
Normal file
@ -0,0 +1,79 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QMIMEMAGICRULEMATCHER_P_H
|
||||
#define QMIMEMAGICRULEMATCHER_P_H
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qstring.h>
|
||||
|
||||
#include "qmimemagicrule_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeMagicRuleMatcher
|
||||
{
|
||||
public:
|
||||
explicit QMimeMagicRuleMatcher(const QString &mime, unsigned priority = 65535);
|
||||
|
||||
bool operator==(const QMimeMagicRuleMatcher &other);
|
||||
|
||||
void addRule(const QMimeMagicRule &rule);
|
||||
void addRules(const QList<QMimeMagicRule> &rules);
|
||||
QList<QMimeMagicRule> magicRules() const;
|
||||
|
||||
bool matches(const QByteArray &data) const;
|
||||
|
||||
unsigned priority() const;
|
||||
|
||||
QString mimetype() const { return m_mimetype; }
|
||||
|
||||
private:
|
||||
QList<QMimeMagicRule> m_list;
|
||||
unsigned m_priority;
|
||||
QString m_mimetype;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEMAGICRULEMATCHER_P_H
|
830
src/corelib/mimetypes/qmimeprovider.cpp
Normal file
830
src/corelib/mimetypes/qmimeprovider.cpp
Normal file
@ -0,0 +1,830 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmimeprovider_p.h"
|
||||
|
||||
#include "qmimetypeparser_p.h"
|
||||
#include <qstandardpaths.h>
|
||||
#include "qmimemagicrulematcher_p.h"
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QByteArrayMatcher>
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QtEndian>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db)
|
||||
: m_db(db)
|
||||
{
|
||||
}
|
||||
|
||||
Q_CORE_EXPORT int qmime_secondsBetweenChecks = 5; // exported for the unit test
|
||||
|
||||
bool QMimeProviderBase::shouldCheck()
|
||||
{
|
||||
const QDateTime now = QDateTime::currentDateTime();
|
||||
if (m_lastCheck.isValid() && m_lastCheck.secsTo(now) < qmime_secondsBetweenChecks)
|
||||
return false;
|
||||
m_lastCheck = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
QMimeBinaryProvider::QMimeBinaryProvider(QMimeDatabasePrivate *db)
|
||||
: QMimeProviderBase(db), m_mimetypeListLoaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
|
||||
#define QT_USE_MMAP
|
||||
#endif
|
||||
|
||||
struct QMimeBinaryProvider::CacheFile
|
||||
{
|
||||
CacheFile(const QString &fileName);
|
||||
~CacheFile();
|
||||
|
||||
bool isValid() const { return m_valid; }
|
||||
inline quint16 getUint16(int offset) const
|
||||
{
|
||||
return qFromBigEndian(*reinterpret_cast<quint16 *>(data + offset));
|
||||
}
|
||||
inline quint32 getUint32(int offset) const
|
||||
{
|
||||
return qFromBigEndian(*reinterpret_cast<quint32 *>(data + offset));
|
||||
}
|
||||
inline const char *getCharStar(int offset) const
|
||||
{
|
||||
return reinterpret_cast<const char *>(data + offset);
|
||||
}
|
||||
bool load();
|
||||
bool reload();
|
||||
|
||||
QFile file;
|
||||
uchar *data;
|
||||
QDateTime m_mtime;
|
||||
bool m_valid;
|
||||
};
|
||||
|
||||
QMimeBinaryProvider::CacheFile::CacheFile(const QString &fileName)
|
||||
: file(fileName), m_valid(false)
|
||||
{
|
||||
load();
|
||||
}
|
||||
|
||||
QMimeBinaryProvider::CacheFile::~CacheFile()
|
||||
{
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::CacheFile::load()
|
||||
{
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
data = file.map(0, file.size());
|
||||
if (data) {
|
||||
const int major = getUint16(0);
|
||||
const int minor = getUint16(2);
|
||||
m_valid = (major == 1 && minor >= 1 && minor <= 2);
|
||||
}
|
||||
m_mtime = QFileInfo(file).lastModified();
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::CacheFile::reload()
|
||||
{
|
||||
//qDebug() << "reload!" << file->fileName();
|
||||
m_valid = false;
|
||||
if (file.isOpen()) {
|
||||
file.close();
|
||||
}
|
||||
data = 0;
|
||||
return load();
|
||||
}
|
||||
|
||||
QMimeBinaryProvider::CacheFile *QMimeBinaryProvider::CacheFileList::findCacheFile(const QString &fileName) const
|
||||
{
|
||||
for (const_iterator it = begin(); it != end(); ++it) {
|
||||
if ((*it)->file.fileName() == fileName)
|
||||
return *it;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QMimeBinaryProvider::~QMimeBinaryProvider()
|
||||
{
|
||||
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 QMimeBinaryProvider::isValid()
|
||||
{
|
||||
#if defined(QT_USE_MMAP)
|
||||
if (!qgetenv("QT_NO_MIME_CACHE").isEmpty())
|
||||
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 QMimeBinaryProvider::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 QMimeBinaryProvider::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) {
|
||||
foreach (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 QMimeType mimeTypeForNameUnchecked(const QString &name)
|
||||
{
|
||||
QMimeTypePrivate data;
|
||||
data.name = name;
|
||||
// The rest is retrieved on demand.
|
||||
// comment and globPatterns: in loadMimeTypePrivate
|
||||
// iconName: in loadIcon
|
||||
// genericIconName: in loadGenericIcon
|
||||
return QMimeType(data);
|
||||
}
|
||||
|
||||
QMimeType QMimeBinaryProvider::mimeTypeForName(const QString &name)
|
||||
{
|
||||
checkCache();
|
||||
if (!m_mimetypeListLoaded)
|
||||
loadMimeTypeList();
|
||||
if (!m_mimetypeNames.contains(name))
|
||||
return QMimeType(); // unknown mimetype
|
||||
return mimeTypeForNameUnchecked(name);
|
||||
}
|
||||
|
||||
QStringList QMimeBinaryProvider::findByFileName(const QString &fileName, QString *foundSuffix)
|
||||
{
|
||||
checkCache();
|
||||
const QString lowerFileName = fileName.toLower();
|
||||
QMimeGlobMatchResult result;
|
||||
// TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly.
|
||||
foreach (CacheFile *cacheFile, 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 QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName)
|
||||
{
|
||||
const int numGlobs = cacheFile->getUint32(off);
|
||||
//qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset;
|
||||
for (int i = 0; i < numGlobs; ++i) {
|
||||
const int globOffset = cacheFile->getUint32(off + 4 + 12 * i);
|
||||
const int mimeTypeOffset = cacheFile->getUint32(off + 4 + 12 * i + 4);
|
||||
const int flagsAndWeight = cacheFile->getUint32(off + 4 + 12 * i + 8);
|
||||
const int weight = flagsAndWeight & 0xff;
|
||||
const bool caseSensitive = flagsAndWeight & 0x100;
|
||||
const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||
const QString pattern = QLatin1String(cacheFile->getCharStar(globOffset));
|
||||
|
||||
const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
|
||||
//qDebug() << pattern << mimeType << weight << caseSensitive;
|
||||
QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive);
|
||||
|
||||
// TODO: this could be done faster for literals where a simple == would do.
|
||||
if (glob.matchFileName(fileName))
|
||||
result.addMatch(QLatin1String(mimeType), weight, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result, QMimeBinaryProvider::CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck)
|
||||
{
|
||||
QChar fileChar = fileName[charPos];
|
||||
int min = 0;
|
||||
int max = numEntries - 1;
|
||||
while (min <= max) {
|
||||
const int mid = (min + max) / 2;
|
||||
const int off = firstOffset + 12 * mid;
|
||||
const QChar ch = 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 QMimeBinaryProvider::matchMagicRule(QMimeBinaryProvider::CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data)
|
||||
{
|
||||
const char *dataPtr = data.constData();
|
||||
const int dataSize = data.size();
|
||||
for (int matchlet = 0; matchlet < numMatchlets; ++matchlet) {
|
||||
const int off = firstOffset + matchlet * 32;
|
||||
const int rangeStart = cacheFile->getUint32(off);
|
||||
const int rangeLength = cacheFile->getUint32(off + 4);
|
||||
//const int wordSize = cacheFile->getUint32(off + 8);
|
||||
const int valueLength = cacheFile->getUint32(off + 12);
|
||||
const int valueOffset = cacheFile->getUint32(off + 16);
|
||||
const int maskOffset = cacheFile->getUint32(off + 20);
|
||||
const char *mask = maskOffset ? cacheFile->getCharStar(maskOffset) : NULL;
|
||||
|
||||
if (!QMimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile->getCharStar(valueOffset), mask))
|
||||
continue;
|
||||
|
||||
const int numChildren = cacheFile->getUint32(off + 24);
|
||||
const int firstChildOffset = cacheFile->getUint32(off + 28);
|
||||
if (numChildren == 0) // No submatch? Then we are done.
|
||||
return true;
|
||||
// Check that one of the submatches matches too
|
||||
if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QMimeType QMimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr)
|
||||
{
|
||||
checkCache();
|
||||
foreach (CacheFile *cacheFile, 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 QMimeType();
|
||||
}
|
||||
|
||||
QStringList QMimeBinaryProvider::parents(const QString &mime)
|
||||
{
|
||||
checkCache();
|
||||
const QByteArray mimeStr = mime.toLatin1();
|
||||
QStringList result;
|
||||
foreach (CacheFile *cacheFile, 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 QMimeBinaryProvider::resolveAlias(const QString &name)
|
||||
{
|
||||
checkCache();
|
||||
const QByteArray input = name.toLatin1();
|
||||
foreach (CacheFile *cacheFile, 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;
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::loadMimeTypeList()
|
||||
{
|
||||
if (!m_mimetypeListLoaded) {
|
||||
m_mimetypeListLoaded = true;
|
||||
m_mimetypeNames.clear();
|
||||
// Unfortunately mime.cache doesn't have a full list of all mimetypes.
|
||||
// So we have to parse the plain-text files called "types".
|
||||
const QStringList typesFilenames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/types"));
|
||||
foreach (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<QMimeType> QMimeBinaryProvider::allMimeTypes()
|
||||
{
|
||||
QList<QMimeType> result;
|
||||
loadMimeTypeList();
|
||||
|
||||
for (QSet<QString>::const_iterator it = m_mimetypeNames.constBegin();
|
||||
it != m_mimetypeNames.constEnd(); ++it)
|
||||
result.append(mimeTypeForNameUnchecked(*it));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate &data)
|
||||
{
|
||||
// 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
|
||||
}
|
||||
|
||||
// Binary search in the icons or generic-icons list
|
||||
QString QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime)
|
||||
{
|
||||
const int iconsListOffset = cacheFile->getUint32(posListOffset);
|
||||
const int numIcons = cacheFile->getUint32(iconsListOffset);
|
||||
int begin = 0;
|
||||
int end = numIcons - 1;
|
||||
while (begin <= end) {
|
||||
const int medium = (begin + end) / 2;
|
||||
const int off = iconsListOffset + 4 + 8 * medium;
|
||||
const int mimeOffset = cacheFile->getUint32(off);
|
||||
const char *mime = cacheFile->getCharStar(mimeOffset);
|
||||
const int cmp = qstrcmp(mime, inputMime);
|
||||
if (cmp < 0)
|
||||
begin = medium + 1;
|
||||
else if (cmp > 0)
|
||||
end = medium - 1;
|
||||
else {
|
||||
const int iconOffset = cacheFile->getUint32(off + 4);
|
||||
return QLatin1String(cacheFile->getCharStar(iconOffset));
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::loadIcon(QMimeTypePrivate &data)
|
||||
{
|
||||
checkCache();
|
||||
const QByteArray inputMime = data.name.toLatin1();
|
||||
foreach (CacheFile *cacheFile, m_cacheFiles) {
|
||||
const QString icon = iconForMime(cacheFile, PosIconsListOffset, inputMime);
|
||||
if (!icon.isEmpty()) {
|
||||
data.iconName = icon;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeBinaryProvider::loadGenericIcon(QMimeTypePrivate &data)
|
||||
{
|
||||
checkCache();
|
||||
const QByteArray inputMime = data.name.toLatin1();
|
||||
foreach (CacheFile *cacheFile, m_cacheFiles) {
|
||||
const QString icon = iconForMime(cacheFile, PosGenericIconsListOffset, inputMime);
|
||||
if (!icon.isEmpty()) {
|
||||
data.genericIconName = icon;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db)
|
||||
: QMimeProviderBase(db), m_loaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool QMimeXMLProvider::isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QMimeType QMimeXMLProvider::mimeTypeForName(const QString &name)
|
||||
{
|
||||
ensureLoaded();
|
||||
|
||||
return m_nameMimeTypeMap.value(name);
|
||||
}
|
||||
|
||||
QStringList QMimeXMLProvider::findByFileName(const QString &fileName, QString *foundSuffix)
|
||||
{
|
||||
ensureLoaded();
|
||||
|
||||
const QStringList matchingMimeTypes = m_mimeTypeGlobs.matchingGlobs(fileName, foundSuffix);
|
||||
return matchingMimeTypes;
|
||||
}
|
||||
|
||||
QMimeType QMimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr)
|
||||
{
|
||||
ensureLoaded();
|
||||
|
||||
QString candidate;
|
||||
|
||||
foreach (const QMimeMagicRuleMatcher &matcher, m_magicMatchers) {
|
||||
if (matcher.matches(data)) {
|
||||
const int priority = matcher.priority();
|
||||
if (priority > *accuracyPtr) {
|
||||
*accuracyPtr = priority;
|
||||
candidate = matcher.mimetype();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mimeTypeForName(candidate);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::ensureLoaded()
|
||||
{
|
||||
if (!m_loaded || shouldCheck()) {
|
||||
bool fdoXmlFound = false;
|
||||
QStringList allFiles;
|
||||
|
||||
const QStringList packageDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/packages"), QStandardPaths::LocateDirectory);
|
||||
//qDebug() << "packageDirs=" << packageDirs;
|
||||
foreach (const QString &packageDir, packageDirs) {
|
||||
QDir dir(packageDir);
|
||||
const QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
|
||||
//qDebug() << static_cast<const void *>(this) << Q_FUNC_INFO << packageDir << files;
|
||||
if (!fdoXmlFound)
|
||||
fdoXmlFound = files.contains(QLatin1String("freedesktop.org.xml"));
|
||||
QStringList::const_iterator endIt(files.constEnd());
|
||||
for (QStringList::const_iterator it(files.constBegin()); it != endIt; ++it) {
|
||||
allFiles.append(packageDir + QLatin1Char('/') + *it);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fdoXmlFound) {
|
||||
// We could instead install the file as part of installing Qt?
|
||||
allFiles.prepend(QLatin1String(":/qt-project.org/qmime/freedesktop.org.xml"));
|
||||
}
|
||||
|
||||
if (m_allFiles == allFiles)
|
||||
return;
|
||||
m_allFiles = allFiles;
|
||||
|
||||
m_nameMimeTypeMap.clear();
|
||||
m_aliases.clear();
|
||||
m_parents.clear();
|
||||
m_mimeTypeGlobs.clear();
|
||||
m_magicMatchers.clear();
|
||||
|
||||
//qDebug() << "Loading" << m_allFiles;
|
||||
|
||||
foreach (const QString &file, allFiles)
|
||||
load(file);
|
||||
}
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::load(const QString &fileName)
|
||||
{
|
||||
QString errorMessage;
|
||||
if (!load(fileName, &errorMessage))
|
||||
qWarning("QMimeDatabase: Error loading %s\n%s", qPrintable(fileName), qPrintable(errorMessage));
|
||||
}
|
||||
|
||||
bool QMimeXMLProvider::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();
|
||||
|
||||
QMimeTypeParser parser(*this);
|
||||
return parser.parse(&file, fileName, errorMessage);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addGlobPattern(const QMimeGlobPattern &glob)
|
||||
{
|
||||
m_mimeTypeGlobs.addGlob(glob);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addMimeType(const QMimeType &mt)
|
||||
{
|
||||
m_nameMimeTypeMap.insert(mt.name(), mt);
|
||||
}
|
||||
|
||||
QStringList QMimeXMLProvider::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 QMimeXMLProvider::addParent(const QString &child, const QString &parent)
|
||||
{
|
||||
m_parents[child].append(parent);
|
||||
}
|
||||
|
||||
QString QMimeXMLProvider::resolveAlias(const QString &name)
|
||||
{
|
||||
ensureLoaded();
|
||||
return m_aliases.value(name, name);
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addAlias(const QString &alias, const QString &name)
|
||||
{
|
||||
m_aliases.insert(alias, name);
|
||||
}
|
||||
|
||||
QList<QMimeType> QMimeXMLProvider::allMimeTypes()
|
||||
{
|
||||
ensureLoaded();
|
||||
return m_nameMimeTypeMap.values();
|
||||
}
|
||||
|
||||
void QMimeXMLProvider::addMagicMatcher(const QMimeMagicRuleMatcher &matcher)
|
||||
{
|
||||
m_magicMatchers.append(matcher);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
165
src/corelib/mimetypes/qmimeprovider_p.h
Normal file
165
src/corelib/mimetypes/qmimeprovider_p.h
Normal file
@ -0,0 +1,165 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMIMEPROVIDER_P_H
|
||||
#define QMIMEPROVIDER_P_H
|
||||
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include "qmimedatabase_p.h"
|
||||
#include <QtCore/qset.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeMagicRuleMatcher;
|
||||
|
||||
class QMimeProviderBase
|
||||
{
|
||||
public:
|
||||
QMimeProviderBase(QMimeDatabasePrivate *db);
|
||||
virtual ~QMimeProviderBase() {}
|
||||
|
||||
virtual bool isValid() = 0;
|
||||
virtual QMimeType 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 QMimeType findByMagic(const QByteArray &data, int *accuracyPtr) = 0;
|
||||
virtual QList<QMimeType> allMimeTypes() = 0;
|
||||
virtual void loadMimeTypePrivate(QMimeTypePrivate &) {}
|
||||
virtual void loadIcon(QMimeTypePrivate &) {}
|
||||
virtual void loadGenericIcon(QMimeTypePrivate &) {}
|
||||
|
||||
QMimeDatabasePrivate *m_db;
|
||||
protected:
|
||||
bool shouldCheck();
|
||||
QDateTime m_lastCheck;
|
||||
};
|
||||
|
||||
/*
|
||||
Parses the files 'mime.cache' and 'types' on demand
|
||||
*/
|
||||
class QMimeBinaryProvider : public QMimeProviderBase
|
||||
{
|
||||
public:
|
||||
QMimeBinaryProvider(QMimeDatabasePrivate *db);
|
||||
virtual ~QMimeBinaryProvider();
|
||||
|
||||
virtual bool isValid();
|
||||
virtual QMimeType 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 QMimeType findByMagic(const QByteArray &data, int *accuracyPtr);
|
||||
virtual QList<QMimeType> allMimeTypes();
|
||||
virtual void loadMimeTypePrivate(QMimeTypePrivate &);
|
||||
virtual void loadIcon(QMimeTypePrivate &);
|
||||
virtual void loadGenericIcon(QMimeTypePrivate &);
|
||||
|
||||
private:
|
||||
struct CacheFile;
|
||||
|
||||
void matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int offset, const QString &fileName);
|
||||
bool matchSuffixTree(QMimeGlobMatchResult &result, CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck);
|
||||
bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data);
|
||||
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 QMimeXMLProvider : public QMimeProviderBase
|
||||
{
|
||||
public:
|
||||
QMimeXMLProvider(QMimeDatabasePrivate *db);
|
||||
|
||||
virtual bool isValid();
|
||||
virtual QMimeType 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 QMimeType findByMagic(const QByteArray &data, int *accuracyPtr);
|
||||
virtual QList<QMimeType> allMimeTypes();
|
||||
|
||||
bool load(const QString &fileName, QString *errorMessage);
|
||||
|
||||
// Called by the mimetype xml parser
|
||||
void addMimeType(const QMimeType &mt);
|
||||
void addGlobPattern(const QMimeGlobPattern &glob);
|
||||
void addParent(const QString &child, const QString &parent);
|
||||
void addAlias(const QString &alias, const QString &name);
|
||||
void addMagicMatcher(const QMimeMagicRuleMatcher &matcher);
|
||||
|
||||
private:
|
||||
void ensureLoaded();
|
||||
void load(const QString &fileName);
|
||||
|
||||
bool m_loaded;
|
||||
|
||||
typedef QHash<QString, QMimeType> NameMimeTypeMap;
|
||||
NameMimeTypeMap m_nameMimeTypeMap;
|
||||
|
||||
typedef QHash<QString, QString> AliasHash;
|
||||
AliasHash m_aliases;
|
||||
|
||||
typedef QHash<QString, QStringList> ParentsHash;
|
||||
ParentsHash m_parents;
|
||||
QMimeAllGlobPatterns m_mimeTypeGlobs;
|
||||
|
||||
QList<QMimeMagicRuleMatcher> m_magicMatchers;
|
||||
QStringList m_allFiles;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMEPROVIDER_P_H
|
506
src/corelib/mimetypes/qmimetype.cpp
Normal file
506
src/corelib/mimetypes/qmimetype.cpp
Normal file
@ -0,0 +1,506 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include "qmimetype.h"
|
||||
|
||||
#include "qmimetype_p.h"
|
||||
#include "qmimedatabase_p.h"
|
||||
#include "qmimeprovider_p.h"
|
||||
|
||||
#include "qmimeglobpattern_p.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QLocale>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
bool qt_isQMimeTypeDebuggingActivated (false);
|
||||
|
||||
#ifndef QT_NO_DEBUG_OUTPUT
|
||||
#define DBG() if (qt_isQMimeTypeDebuggingActivated) qDebug() << static_cast<const void *>(this) << Q_FUNC_INFO
|
||||
#else
|
||||
#define DBG() if (0) qDebug() << static_cast<const void *>(this) << Q_FUNC_INFO
|
||||
#endif
|
||||
|
||||
QMimeTypePrivate::QMimeTypePrivate()
|
||||
: name()
|
||||
//, comment()
|
||||
, localeComments()
|
||||
, genericIconName()
|
||||
, iconName()
|
||||
, globPatterns()
|
||||
{}
|
||||
|
||||
QMimeTypePrivate::QMimeTypePrivate(const QMimeType &other)
|
||||
: name(other.d->name)
|
||||
//, comment(other.d->comment)
|
||||
, localeComments(other.d->localeComments)
|
||||
, genericIconName(other.d->genericIconName)
|
||||
, iconName(other.d->iconName)
|
||||
, globPatterns(other.d->globPatterns)
|
||||
{}
|
||||
|
||||
void QMimeTypePrivate::clear()
|
||||
{
|
||||
name.clear();
|
||||
//comment.clear();
|
||||
localeComments.clear();
|
||||
genericIconName.clear();
|
||||
iconName.clear();
|
||||
globPatterns.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QMimeTypePrivate::operator==(const QMimeTypePrivate &other) const;
|
||||
Returns true if \a other equals this QMimeTypePrivate object, otherwise returns false.
|
||||
*/
|
||||
bool QMimeTypePrivate::operator==(const QMimeTypePrivate &other) const
|
||||
{
|
||||
DBG();
|
||||
if (name == other.name &&
|
||||
//comment == other.comment &&
|
||||
localeComments == other.localeComments &&
|
||||
genericIconName == other.genericIconName &&
|
||||
iconName == other.iconName &&
|
||||
globPatterns == other.globPatterns) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DBG() << name << other.name << (name == other.name);
|
||||
//DBG() << comment << other.comment << (comment == other.comment);
|
||||
DBG() << localeComments << other.localeComments << (localeComments == other.localeComments);
|
||||
DBG() << genericIconName << other.genericIconName << (genericIconName == other.genericIconName);
|
||||
DBG() << iconName << other.iconName << (iconName == other.iconName);
|
||||
DBG() << globPatterns << other.globPatterns << (globPatterns == other.globPatterns);
|
||||
return false;
|
||||
}
|
||||
|
||||
void QMimeTypePrivate::addGlobPattern(const QString &pattern)
|
||||
{
|
||||
globPatterns.append(pattern);
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QMimeType
|
||||
\brief The QMimeType class describes types of file or data, represented by a MIME type string.
|
||||
|
||||
\since 5.0
|
||||
|
||||
For instance a file named "readme.txt" has the MIME type "text/plain".
|
||||
The MIME type can be determined from the file name, or from the file
|
||||
contents, or from both. MIME type determination can also be done on
|
||||
buffers of data not coming from files.
|
||||
|
||||
Determining the MIME type of a file can be useful to make sure your
|
||||
application supports it. It is also useful in file-manager-like applications
|
||||
or widgets, in order to display an appropriate icon() for the file, or even
|
||||
the descriptive comment() in detailed views.
|
||||
|
||||
To check if a file has the expected MIME type, you should use inherits()
|
||||
rather than a simple string comparison based on the name(). This is because
|
||||
MIME types can inherit from each other: for instance a C source file is
|
||||
a specific type of plain text file, so text/x-csrc inherits text/plain.
|
||||
|
||||
\sa QMimeDatabase
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QMimeType::QMimeType();
|
||||
Constructs this QMimeType object initialized with default property values that indicate an invalid MIME type.
|
||||
*/
|
||||
QMimeType::QMimeType() :
|
||||
d(new QMimeTypePrivate())
|
||||
{
|
||||
DBG() << "name():" << name();
|
||||
//DBG() << "aliases():" << aliases();
|
||||
//DBG() << "comment():" << comment();
|
||||
DBG() << "genericIconName():" << genericIconName();
|
||||
DBG() << "iconName():" << iconName();
|
||||
DBG() << "globPatterns():" << globPatterns();
|
||||
DBG() << "suffixes():" << suffixes();
|
||||
DBG() << "preferredSuffix():" << preferredSuffix();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeType::QMimeType(const QMimeType &other);
|
||||
Constructs this QMimeType object as a copy of \a other.
|
||||
*/
|
||||
QMimeType::QMimeType(const QMimeType &other) :
|
||||
d(other.d)
|
||||
{
|
||||
DBG() << "name():" << name();
|
||||
//DBG() << "aliases():" << aliases();
|
||||
//DBG() << "comment():" << comment();
|
||||
DBG() << "genericIconName():" << genericIconName();
|
||||
DBG() << "iconName():" << iconName();
|
||||
DBG() << "globPatterns():" << globPatterns();
|
||||
DBG() << "suffixes():" << suffixes();
|
||||
DBG() << "preferredSuffix():" << preferredSuffix();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QMimeType &QMimeType::operator=(const QMimeType &other);
|
||||
Assigns the data of \a other to this QMimeType object, and returns a reference to this object.
|
||||
*/
|
||||
QMimeType &QMimeType::operator=(const QMimeType &other)
|
||||
{
|
||||
if (d != other.d)
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef Q_COMPILER_RVALUE_REFS
|
||||
/*!
|
||||
\fn QMimeType::QMimeType(QMimeType &&other);
|
||||
Constructs this QMimeType object by moving the data of the rvalue reference \a other.
|
||||
*/
|
||||
QMimeType::QMimeType(QMimeType &&other) :
|
||||
d(std::move(other.d))
|
||||
{
|
||||
DBG() << "name():" << name();
|
||||
//DBG() << "aliases():" << aliases();
|
||||
//DBG() << "comment():" << comment();
|
||||
DBG() << "genericIconName():" << genericIconName();
|
||||
DBG() << "iconName():" << iconName();
|
||||
DBG() << "globPatterns():" << globPatterns();
|
||||
DBG() << "suffixes():" << suffixes();
|
||||
DBG() << "preferredSuffix():" << preferredSuffix();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\fn QMimeType::QMimeType(const QMimeTypePrivate &dd);
|
||||
Assigns the data of the QMimeTypePrivate \a dd to this QMimeType object, and returns a reference to this object.
|
||||
*/
|
||||
QMimeType::QMimeType(const QMimeTypePrivate &dd) :
|
||||
d(new QMimeTypePrivate(dd))
|
||||
{
|
||||
DBG() << "name():" << name();
|
||||
//DBG() << "aliases():" << aliases();
|
||||
//DBG() << "comment():" << comment();
|
||||
DBG() << "genericIconName():" << genericIconName();
|
||||
DBG() << "iconName():" << iconName();
|
||||
DBG() << "globPatterns():" << globPatterns();
|
||||
DBG() << "suffixes():" << suffixes();
|
||||
DBG() << "preferredSuffix():" << preferredSuffix();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn void QMimeType::swap(QMimeType &other);
|
||||
Swaps QMimeType \a other with this QMimeType object.
|
||||
|
||||
This operation is very fast and never fails.
|
||||
|
||||
The swap() method helps with the implementation of assignment
|
||||
operators in an exception-safe way. For more information consult
|
||||
\l {http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap}
|
||||
{More C++ Idioms - Copy-and-swap}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QMimeType::~QMimeType();
|
||||
Destroys the QMimeType object, and releases the d pointer.
|
||||
*/
|
||||
QMimeType::~QMimeType()
|
||||
{
|
||||
DBG() << "name():" << name();
|
||||
//DBG() << "aliases():" << aliases();
|
||||
//DBG() << "comment():" << comment();
|
||||
DBG() << "genericIconName():" << genericIconName();
|
||||
DBG() << "iconName():" << iconName();
|
||||
DBG() << "globPatterns():" << globPatterns();
|
||||
DBG() << "suffixes():" << suffixes();
|
||||
DBG() << "preferredSuffix():" << preferredSuffix();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QMimeType::operator==(const QMimeType &other) const;
|
||||
Returns true if \a other equals this QMimeType object, otherwise returns false.
|
||||
*/
|
||||
bool QMimeType::operator==(const QMimeType &other) const
|
||||
{
|
||||
return d == other.d || *d == *other.d;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QMimeType::operator!=(const QMimeType &other) const;
|
||||
Returns true if \a other does not equal this QMimeType object, otherwise returns false.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool QMimeType::isValid() const;
|
||||
Returns true if the QMimeType object contains valid data, otherwise returns false.
|
||||
A valid MIME type has a non-empty name().
|
||||
The invalid MIME type is the default-constructed QMimeType.
|
||||
*/
|
||||
bool QMimeType::isValid() const
|
||||
{
|
||||
return !d->name.isEmpty();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QMimeType::isDefault() const;
|
||||
Returns true if this MIME type is the default MIME type which
|
||||
applies to all files: application/octet-stream.
|
||||
*/
|
||||
bool QMimeType::isDefault() const
|
||||
{
|
||||
return d->name == QMimeDatabasePrivate::instance()->defaultMimeType();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QString QMimeType::name() const;
|
||||
Returns the name of the MIME type.
|
||||
*/
|
||||
QString QMimeType::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.
|
||||
Another language can be specified by setting the \a localeName argument.
|
||||
*/
|
||||
QString QMimeType::comment() const
|
||||
{
|
||||
QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
|
||||
|
||||
QStringList languageList;
|
||||
languageList << QLocale::system().name();
|
||||
languageList << QLocale::system().uiLanguages();
|
||||
Q_FOREACH (const QString &lang, languageList) {
|
||||
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;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QString QMimeType::genericIconName() const;
|
||||
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 QMimeType::genericIconName() const
|
||||
{
|
||||
QMimeDatabasePrivate::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;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QString QMimeType::iconName() const;
|
||||
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 QMimeType::iconName() const
|
||||
{
|
||||
QMimeDatabasePrivate::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;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QStringList QMimeType::globPatterns() const;
|
||||
Returns the list of glob matching patterns.
|
||||
*/
|
||||
QStringList QMimeType::globPatterns() const
|
||||
{
|
||||
QMimeDatabasePrivate::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 image/svg+xml files are also
|
||||
text/xml, text/plain and application/octet-stream files. Subclassing is about
|
||||
the format, rather than the category of the data (for example, there is no
|
||||
'generic spreadsheet' class that all spreadsheets inherit from).
|
||||
Conversely, the parent mimetype of image/svg+xml is text/xml.
|
||||
|
||||
A mimetype can have multiple parents. For instance application/x-perl
|
||||
has two parents: application/x-executable and text/plain. This makes
|
||||
it possible to both execute perl scripts, and to open them in text editors.
|
||||
*/
|
||||
QStringList QMimeType::parentMimeTypes() const
|
||||
{
|
||||
return QMimeDatabasePrivate::instance()->provider()->parents(d->name);
|
||||
}
|
||||
|
||||
static void collectParentMimeTypes(const QString &mime, QStringList &allParents)
|
||||
{
|
||||
QStringList parents = QMimeDatabasePrivate::instance()->provider()->parents(mime);
|
||||
foreach (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.
|
||||
foreach (const QString &parent, parents) {
|
||||
collectParentMimeTypes(parent, allParents);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Return all the parent mimetypes of this mimetype, direct and indirect.
|
||||
This includes the parent(s) of its parent(s), etc.
|
||||
|
||||
For instance, for image/svg+xml the list would be:
|
||||
application/xml, text/plain, application/octet-stream.
|
||||
|
||||
Note that application/octet-stream is the ultimate parent for all types
|
||||
of files (but not directories).
|
||||
*/
|
||||
QStringList QMimeType::allAncestors() const
|
||||
{
|
||||
QStringList allParents;
|
||||
collectParentMimeTypes(d->name, allParents);
|
||||
return allParents;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QStringList QMimeType::suffixes() const;
|
||||
Returns the known suffixes for the MIME type.
|
||||
*/
|
||||
QStringList QMimeType::suffixes() const
|
||||
{
|
||||
QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
|
||||
|
||||
QStringList result;
|
||||
foreach (const QString &pattern, d->globPatterns) {
|
||||
// Not a simple suffix if if looks like: README or *. or *.* or *.JP*G or *.JP?
|
||||
if (pattern.startsWith(QLatin1String("*.")) &&
|
||||
pattern.length() > 2 &&
|
||||
pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) {
|
||||
const QString suffix = pattern.mid(2);
|
||||
result.append(suffix);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QString QMimeType::preferredSuffix() const;
|
||||
Returns the preferred suffix for the MIME type.
|
||||
*/
|
||||
QString QMimeType::preferredSuffix() const
|
||||
{
|
||||
const QStringList suffixList = suffixes();
|
||||
return suffixList.isEmpty() ? QString() : suffixList.at(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QString QMimeType::filterString() const;
|
||||
Returns a filter string usable for a file dialog.
|
||||
*/
|
||||
QString QMimeType::filterString() const
|
||||
{
|
||||
QMimeDatabasePrivate::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;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QMimeType::inherits(const QString &mimeTypeName) const;
|
||||
Returns true if this mimetype is \a mimeTypeName,
|
||||
or inherits \a mimeTypeName (see parentMimeTypes()),
|
||||
or \a mimeTypeName is an alias for this mimetype.
|
||||
*/
|
||||
bool QMimeType::inherits(const QString &mimeTypeName) const
|
||||
{
|
||||
if (d->name == mimeTypeName)
|
||||
return true;
|
||||
return QMimeDatabasePrivate::instance()->inherits(d->name, mimeTypeName);
|
||||
}
|
||||
|
||||
#undef DBG
|
||||
|
||||
QT_END_NAMESPACE
|
115
src/corelib/mimetypes/qmimetype.h
Normal file
115
src/corelib/mimetypes/qmimetype.h
Normal file
@ -0,0 +1,115 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QMIMETYPE_H
|
||||
#define QMIMETYPE_H
|
||||
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtCore/qstring.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMimeTypePrivate;
|
||||
class QFileinfo;
|
||||
class QStringList;
|
||||
|
||||
class Q_CORE_EXPORT QMimeType
|
||||
{
|
||||
public:
|
||||
QMimeType();
|
||||
QMimeType(const QMimeType &other);
|
||||
QMimeType &operator=(const QMimeType &other);
|
||||
#ifdef Q_COMPILER_RVALUE_REFS
|
||||
QMimeType(QMimeType &&other);
|
||||
|
||||
QMimeType &operator=(QMimeType &&other)
|
||||
{
|
||||
qSwap(d, other.d);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
void swap(QMimeType &other)
|
||||
{
|
||||
qSwap(d, other.d);
|
||||
}
|
||||
explicit QMimeType(const QMimeTypePrivate &dd);
|
||||
~QMimeType();
|
||||
|
||||
bool operator==(const QMimeType &other) const;
|
||||
|
||||
inline bool operator!=(const QMimeType &other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
bool isDefault() const;
|
||||
|
||||
QString name() const;
|
||||
QString comment() const;
|
||||
QString genericIconName() const;
|
||||
QString iconName() const;
|
||||
QStringList globPatterns() const;
|
||||
QStringList parentMimeTypes() const;
|
||||
QStringList allAncestors() const;
|
||||
QStringList suffixes() const;
|
||||
QString preferredSuffix() const;
|
||||
|
||||
bool inherits(const QString &mimeTypeName) const;
|
||||
|
||||
QString filterString() const;
|
||||
|
||||
protected:
|
||||
friend class QMimeTypeParserBase;
|
||||
friend class MimeTypeMapEntry;
|
||||
friend class QMimeDatabasePrivate;
|
||||
friend class QMimeXMLProvider;
|
||||
friend class QMimeBinaryProvider;
|
||||
friend class QMimeTypePrivate;
|
||||
|
||||
QExplicitlySharedDataPointer<QMimeTypePrivate> d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMIMETYPE_H
|
114
src/corelib/mimetypes/qmimetype_p.h
Normal file
114
src/corelib/mimetypes/qmimetype_p.h
Normal file
@ -0,0 +1,114 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QMIMETYPE_P_H
|
||||
#define QMIMETYPE_P_H
|
||||
|
||||
#include "qmimetype.h"
|
||||
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_AUTOTEST_EXPORT QMimeTypePrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
typedef QHash<QString, QString> LocaleHash;
|
||||
|
||||
QMimeTypePrivate();
|
||||
explicit QMimeTypePrivate(const QMimeType &other);
|
||||
|
||||
void clear();
|
||||
|
||||
bool operator==(const QMimeTypePrivate &other) const;
|
||||
|
||||
void addGlobPattern(const QString &pattern);
|
||||
|
||||
QString name;
|
||||
LocaleHash localeComments;
|
||||
QString genericIconName;
|
||||
QString iconName;
|
||||
QStringList globPatterns;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#define QMIMETYPE_BUILDER \
|
||||
QT_BEGIN_NAMESPACE \
|
||||
static QMimeType buildQMimeType ( \
|
||||
const QString &name, \
|
||||
const QString &genericIconName, \
|
||||
const QString &iconName, \
|
||||
const QStringList &globPatterns \
|
||||
) \
|
||||
{ \
|
||||
QMimeTypePrivate qMimeTypeData; \
|
||||
qMimeTypeData.name = name; \
|
||||
qMimeTypeData.genericIconName = genericIconName; \
|
||||
qMimeTypeData.iconName = iconName; \
|
||||
qMimeTypeData.globPatterns = globPatterns; \
|
||||
return QMimeType(qMimeTypeData); \
|
||||
} \
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#ifdef Q_COMPILER_RVALUE_REFS
|
||||
#define QMIMETYPE_BUILDER_FROM_RVALUE_REFS \
|
||||
QT_BEGIN_NAMESPACE \
|
||||
static QMimeType buildQMimeType ( \
|
||||
QString &&name, \
|
||||
QString &&genericIconName, \
|
||||
QString &&iconName, \
|
||||
QStringList &&globPatterns \
|
||||
) \
|
||||
{ \
|
||||
QMimeTypePrivate qMimeTypeData; \
|
||||
qMimeTypeData.name = std::move(name); \
|
||||
qMimeTypeData.genericIconName = std::move(genericIconName); \
|
||||
qMimeTypeData.iconName = std::move(iconName); \
|
||||
qMimeTypeData.globPatterns = std::move(globPatterns); \
|
||||
return QMimeType(qMimeTypeData); \
|
||||
} \
|
||||
QT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
#endif // QMIMETYPE_P_H
|
342
src/corelib/mimetypes/qmimetypeparser.cpp
Normal file
342
src/corelib/mimetypes/qmimetypeparser.cpp
Normal file
@ -0,0 +1,342 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#define QT_NO_CAST_FROM_ASCII
|
||||
|
||||
#include "qmimetypeparser_p.h"
|
||||
|
||||
#include "qmimetype_p.h"
|
||||
#include "qmimemagicrulematcher_p.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QPair>
|
||||
#include <QtCore/QXmlStreamReader>
|
||||
#include <QtCore/QXmlStreamWriter>
|
||||
#include <QtCore/QStack>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// XML tags in MIME files
|
||||
const char *const mimeInfoTagC = "mime-info";
|
||||
const char *const mimeTypeTagC = "mime-type";
|
||||
const char *const mimeTypeAttributeC = "type";
|
||||
const char *const subClassTagC = "sub-class-of";
|
||||
const char *const commentTagC = "comment";
|
||||
const char *const genericIconTagC = "generic-icon";
|
||||
const char *const iconTagC = "icon";
|
||||
const char *const nameAttributeC = "name";
|
||||
const char *const globTagC = "glob";
|
||||
const char *const aliasTagC = "alias";
|
||||
const char *const patternAttributeC = "pattern";
|
||||
const char *const weightAttributeC = "weight";
|
||||
const char *const caseSensitiveAttributeC = "case-sensitive";
|
||||
const char *const localeAttributeC = "xml:lang";
|
||||
|
||||
const char *const magicTagC = "magic";
|
||||
const char *const priorityAttributeC = "priority";
|
||||
|
||||
const char *const matchTagC = "match";
|
||||
const char *const matchValueAttributeC = "value";
|
||||
const char *const matchTypeAttributeC = "type";
|
||||
const char *const matchOffsetAttributeC = "offset";
|
||||
const char *const matchMaskAttributeC = "mask";
|
||||
|
||||
/*!
|
||||
\class QMimeTypeParser
|
||||
\internal
|
||||
\brief The QMimeTypeParser class parses MIME types, and builds a MIME database hierarchy by adding to QMimeDatabasePrivate.
|
||||
|
||||
Populates QMimeDataBase
|
||||
|
||||
\sa QMimeDatabase, QMimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
||||
\sa QMimeTypeParser
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\class QMimeTypeParserBase
|
||||
\brief The QMimeTypeParserBase class parses for a sequence of <mime-type> in a generic way.
|
||||
|
||||
Calls abstract handler function process for QMimeType it finds.
|
||||
|
||||
\sa QMimeDatabase, QMimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern
|
||||
\sa QMimeTypeParser
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual bool QMimeTypeParserBase::process(const QMimeType &t, QString *errorMessage) = 0;
|
||||
Overwrite to process the sequence of parsed data
|
||||
*/
|
||||
|
||||
QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState currentState, const QStringRef &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) {
|
||||
*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"/>
|
||||
static bool createMagicMatchRule(const QXmlStreamAttributes &atts,
|
||||
QString *errorMessage, QMimeMagicRule *&rule)
|
||||
{
|
||||
const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString();
|
||||
QMimeMagicRule::Type magicType = QMimeMagicRule::type(type.toLatin1());
|
||||
if (magicType == QMimeMagicRule::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();
|
||||
if (value.isEmpty()) {
|
||||
*errorMessage = QString::fromLatin1("Empty match value detected.");
|
||||
return false;
|
||||
}
|
||||
// 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();
|
||||
|
||||
rule = new QMimeMagicRule(magicType, value.toUtf8(), startPos, endPos, mask.toLatin1());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QMimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString *errorMessage)
|
||||
{
|
||||
QMimeTypePrivate data;
|
||||
int priority = 50;
|
||||
QStack<QMimeMagicRule *> currentRules; // stack for the nesting of rules
|
||||
QList<QMimeMagicRule> rules; // toplevel rules
|
||||
QXmlStreamReader reader(dev);
|
||||
ParseState ps = ParseBeginning;
|
||||
QXmlStreamAttributes atts;
|
||||
while (!reader.atEnd()) {
|
||||
switch (reader.readNext()) {
|
||||
case QXmlStreamReader::StartElement:
|
||||
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 {
|
||||
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 = QMimeGlobPattern::DefaultWeight;
|
||||
|
||||
Q_ASSERT(!data.name.isEmpty());
|
||||
const QMimeGlobPattern glob(pattern, data.name, weight, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
||||
if (!process(glob, errorMessage)) // for actual glob matching
|
||||
return false;
|
||||
data.addGlobPattern(pattern); // just for QMimeType::globPatterns()
|
||||
}
|
||||
break;
|
||||
case 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: {
|
||||
QMimeMagicRule *rule = 0;
|
||||
if (!createMagicMatchRule(atts, errorMessage, rule))
|
||||
return false;
|
||||
QList<QMimeMagicRule> *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 QStringRef elementName = reader.name();
|
||||
if (elementName == QLatin1String(mimeTypeTagC)) {
|
||||
if (!process(QMimeType(data), errorMessage))
|
||||
return false;
|
||||
data.clear();
|
||||
} else if (elementName == QLatin1String(matchTagC)) {
|
||||
// Closing a <match> tag, pop stack
|
||||
currentRules.pop();
|
||||
//qDebug() << " MATCH closed. Stack size is now" << currentRules.size();
|
||||
} else if (elementName == QLatin1String(magicTagC)) {
|
||||
//qDebug() << "MAGIC ended, we got" << rules.count() << "rules, with prio" << priority;
|
||||
// Finished a <magic> sequence
|
||||
QMimeMagicRuleMatcher ruleMatcher(data.name, priority);
|
||||
ruleMatcher.addRules(rules);
|
||||
processMagicMatcher(ruleMatcher);
|
||||
rules.clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
142
src/corelib/mimetypes/qmimetypeparser_p.h
Normal file
142
src/corelib/mimetypes/qmimetypeparser_p.h
Normal file
@ -0,0 +1,142 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef MIMETYPEPARSER_P_H
|
||||
#define MIMETYPEPARSER_P_H
|
||||
|
||||
#include "qmimedatabase_p.h"
|
||||
#include "qmimeprovider_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QIODevice;
|
||||
|
||||
// XML tags in MIME files
|
||||
extern const char *const mimeInfoTagC;
|
||||
extern const char *const mimeTypeTagC;
|
||||
extern const char *const mimeTypeAttributeC;
|
||||
extern const char *const subClassTagC;
|
||||
extern const char *const commentTagC;
|
||||
extern const char *const genericIconTagC;
|
||||
extern const char *const nameAttributeC;
|
||||
extern const char *const globTagC;
|
||||
extern const char *const aliasTagC;
|
||||
extern const char *const patternAttributeC;
|
||||
extern const char *const weightAttributeC;
|
||||
extern const char *const caseSensitiveAttributeC;
|
||||
extern const char *const localeAttributeC;
|
||||
|
||||
extern const char *const magicTagC;
|
||||
extern const char *const priorityAttributeC;
|
||||
|
||||
extern const char *const matchTagC;
|
||||
extern const char *const matchValueAttributeC;
|
||||
extern const char *const matchTypeAttributeC;
|
||||
extern const char *const matchOffsetAttributeC;
|
||||
extern const char *const matchMaskAttributeC;
|
||||
|
||||
class QMimeTypeParserBase
|
||||
{
|
||||
Q_DISABLE_COPY(QMimeTypeParserBase)
|
||||
|
||||
public:
|
||||
QMimeTypeParserBase() {}
|
||||
virtual ~QMimeTypeParserBase() {}
|
||||
|
||||
bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage);
|
||||
|
||||
protected:
|
||||
virtual bool process(const QMimeType &t, QString *errorMessage) = 0;
|
||||
virtual bool process(const QMimeGlobPattern &t, QString *errorMessage) = 0;
|
||||
virtual void processParent(const QString &child, const QString &parent) = 0;
|
||||
virtual void processAlias(const QString &alias, const QString &name) = 0;
|
||||
virtual void processMagicMatcher(const QMimeMagicRuleMatcher &matcher) = 0;
|
||||
|
||||
private:
|
||||
enum ParseState {
|
||||
ParseBeginning,
|
||||
ParseMimeInfo,
|
||||
ParseMimeType,
|
||||
ParseComment,
|
||||
ParseGenericIcon,
|
||||
ParseIcon,
|
||||
ParseGlobPattern,
|
||||
ParseSubClass,
|
||||
ParseAlias,
|
||||
ParseMagic,
|
||||
ParseMagicMatchRule,
|
||||
ParseOtherMimeTypeSubTag,
|
||||
ParseError
|
||||
};
|
||||
|
||||
static ParseState nextState(ParseState currentState, const QStringRef &startElement);
|
||||
};
|
||||
|
||||
|
||||
class QMimeTypeParser : public QMimeTypeParserBase
|
||||
{
|
||||
public:
|
||||
explicit QMimeTypeParser(QMimeXMLProvider &provider) : m_provider(provider) {}
|
||||
|
||||
protected:
|
||||
inline bool process(const QMimeType &t, QString *)
|
||||
{ m_provider.addMimeType(t); return true; }
|
||||
|
||||
inline bool process(const QMimeGlobPattern &glob, QString *)
|
||||
{ m_provider.addGlobPattern(glob); return true; }
|
||||
|
||||
inline void processParent(const QString &child, const QString &parent)
|
||||
{ m_provider.addParent(child, parent); }
|
||||
|
||||
inline void processAlias(const QString &alias, const QString &name)
|
||||
{ m_provider.addAlias(alias, name); }
|
||||
|
||||
inline void processMagicMatcher(const QMimeMagicRuleMatcher &matcher)
|
||||
{ m_provider.addMagicMatcher(matcher); }
|
||||
|
||||
private:
|
||||
QMimeXMLProvider &m_provider;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // MIMETYPEPARSER_P_H
|
@ -7,6 +7,7 @@ SUBDIRS=\
|
||||
itemmodels \
|
||||
json \
|
||||
kernel \
|
||||
mimetypes \
|
||||
plugin \
|
||||
statemachine \
|
||||
thread \
|
||||
|
@ -403,8 +403,11 @@ void tst_QDirIterator::iterateResource()
|
||||
|
||||
QDirIterator it(dirName, nameFilters, filters, flags);
|
||||
QStringList list;
|
||||
while (it.hasNext())
|
||||
list << it.next();
|
||||
while (it.hasNext()) {
|
||||
const QString dir = it.next();
|
||||
if (!dir.startsWith(":/qt-project.org"))
|
||||
list << dir;
|
||||
}
|
||||
|
||||
list.sort();
|
||||
QStringList sortedEntries = entries;
|
||||
|
@ -92,6 +92,7 @@ void tst_QResourceEngine::checkStructure_data()
|
||||
<< QString()
|
||||
<< (QStringList() << "search_file.txt")
|
||||
<< (QStringList() << QLatin1String("aliasdir") << QLatin1String("otherdir")
|
||||
<< QLatin1String("qt-project.org")
|
||||
<< QLatin1String("runtime_resource")
|
||||
<< QLatin1String("searchpath1") << QLatin1String("searchpath2")
|
||||
<< QLatin1String("secondary_root")
|
||||
|
8
tests/auto/corelib/mimetypes/mimetypes.pro
Normal file
8
tests/auto/corelib/mimetypes/mimetypes.pro
Normal file
@ -0,0 +1,8 @@
|
||||
TEMPLATE=subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
qmimetype \
|
||||
qmimedatabase
|
||||
|
||||
!contains(QT_CONFIG, private_tests): SUBDIRS -= \
|
||||
qmimetype
|
@ -0,0 +1,12 @@
|
||||
CONFIG += testcase parallel_test
|
||||
|
||||
TARGET = tst_qmimedatabase-cache
|
||||
|
||||
QT = core testlib concurrent
|
||||
|
||||
SOURCES = tst_qmimedatabase-cache.cpp
|
||||
HEADERS = ../tst_qmimedatabase.h
|
||||
|
||||
DEFINES += SRCDIR='"\\"$$PWD/../\\""'
|
||||
|
||||
*-g++*:QMAKE_CXXFLAGS += -W -Wall -Wextra -Werror -Wshadow -Wno-long-long -Wnon-virtual-dtor
|
@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "../tst_qmimedatabase.h"
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QtTest/QtTest>
|
||||
#include <qstandardpaths.h>
|
||||
|
||||
#include "../tst_qmimedatabase.cpp"
|
||||
|
||||
tst_QMimeDatabase::tst_QMimeDatabase()
|
||||
{
|
||||
QDir here = QDir::currentPath();
|
||||
const QString tempMime = here.absolutePath() + QString::fromLatin1("/mime");
|
||||
runUpdateMimeDatabase(tempMime);
|
||||
QVERIFY(QFile::exists(tempMime + QString::fromLatin1("/mime.cache")));
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
CONFIG += testcase parallel_test
|
||||
|
||||
TARGET = tst_qmimedatabase-xml
|
||||
|
||||
QT = core testlib concurrent
|
||||
|
||||
CONFIG += depend_includepath
|
||||
|
||||
SOURCES += tst_qmimedatabase-xml.cpp
|
||||
HEADERS += ../tst_qmimedatabase.h
|
||||
|
||||
DEFINES += SRCDIR='"\\"$$PWD/../\\""'
|
||||
|
||||
*-g++*:QMAKE_CXXFLAGS += -W -Wall -Wextra -Werror -Wshadow -Wno-long-long -Wnon-virtual-dtor
|
@ -0,0 +1,51 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "../tst_qmimedatabase.h"
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
tst_QMimeDatabase::tst_QMimeDatabase()
|
||||
{
|
||||
qputenv("QT_NO_MIME_CACHE", "1");
|
||||
}
|
||||
|
||||
#include "../tst_qmimedatabase.cpp"
|
@ -0,0 +1,3 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = qmimedatabase-xml
|
||||
unix: SUBDIRS += qmimedatabase-cache
|
829
tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp
Normal file
829
tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp
Normal file
@ -0,0 +1,829 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qmimedatabase.h>
|
||||
|
||||
#include "qstandardpaths.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <QtConcurrent/QFuture>
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
void initializeLang()
|
||||
{
|
||||
qputenv("LANG", "en_US");
|
||||
}
|
||||
|
||||
// Set LANG before QCoreApplication is created
|
||||
Q_CONSTRUCTOR_FUNCTION(initializeLang)
|
||||
|
||||
void tst_QMimeDatabase::initTestCase()
|
||||
{
|
||||
// Create a "global" and a "local" XDG data dir, right here.
|
||||
// The local dir will be empty initially, while the global dir will contain a copy of freedesktop.org.xml
|
||||
|
||||
QDir here = QDir::currentPath();
|
||||
|
||||
qputenv("XDG_DATA_DIRS", QFile::encodeName(here.absolutePath()));
|
||||
QDir(here.absolutePath() + "/mime").removeRecursively();
|
||||
here.mkpath(QString::fromLatin1("mime/packages"));
|
||||
|
||||
QFile xml(QFile::decodeName(SRCDIR "../../../src/mimetypes/mime/packages/freedesktop.org.xml"));
|
||||
const QString mimeDir = here.absolutePath() + QLatin1String("/mime");
|
||||
xml.copy(mimeDir + QLatin1String("/packages/freedesktop.org.xml"));
|
||||
|
||||
m_dataHome = here.absolutePath() + QLatin1String("/../datahome");
|
||||
qputenv("XDG_DATA_HOME", QFile::encodeName(m_dataHome));
|
||||
//qDebug() << "XDG_DATA_HOME=" << m_dataHome;
|
||||
|
||||
// Make sure we start clean
|
||||
cleanupTestCase();
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::cleanupTestCase()
|
||||
{
|
||||
QDir here = QDir::currentPath();
|
||||
here.remove(QString::fromLatin1("mime/packages/yast2-metapackage-handler-mimetypes.xml"));
|
||||
|
||||
QDir(m_dataHome).removeRecursively();
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypeForName()
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QMimeType s0 = db.mimeTypeForName(QString::fromLatin1("application/x-zerosize"));
|
||||
QVERIFY(s0.isValid());
|
||||
QCOMPARE(s0.name(), QString::fromLatin1("application/x-zerosize"));
|
||||
QCOMPARE(s0.comment(), QString::fromLatin1("empty document"));
|
||||
|
||||
QMimeType s0Again = db.mimeTypeForName(QString::fromLatin1("application/x-zerosize"));
|
||||
QCOMPARE(s0Again.name(), s0.name());
|
||||
|
||||
QMimeType s1 = db.mimeTypeForName(QString::fromLatin1("text/plain"));
|
||||
QVERIFY(s1.isValid());
|
||||
QCOMPARE(s1.name(), QString::fromLatin1("text/plain"));
|
||||
//qDebug("Comment is %s", qPrintable(s1.comment()));
|
||||
|
||||
QMimeType krita = db.mimeTypeForName(QString::fromLatin1("application/x-krita"));
|
||||
QVERIFY(krita.isValid());
|
||||
|
||||
// Test <comment> parsing with application/rdf+xml which has the english comment after the other ones
|
||||
QMimeType rdf = db.mimeTypeForName(QString::fromLatin1("application/rdf+xml"));
|
||||
QVERIFY(rdf.isValid());
|
||||
QCOMPARE(rdf.comment(), QString::fromLatin1("RDF file"));
|
||||
|
||||
QMimeType bzip2 = db.mimeTypeForName(QString::fromLatin1("application/x-bzip2"));
|
||||
QVERIFY(bzip2.isValid());
|
||||
QCOMPARE(bzip2.comment(), QString::fromLatin1("Bzip archive"));
|
||||
|
||||
QMimeType defaultMime = db.mimeTypeForName(QString::fromLatin1("application/octet-stream"));
|
||||
QVERIFY(defaultMime.isValid());
|
||||
QVERIFY(defaultMime.isDefault());
|
||||
|
||||
QMimeType doesNotExist = db.mimeTypeForName(QString::fromLatin1("foobar/x-doesnot-exist"));
|
||||
QVERIFY(!doesNotExist.isValid());
|
||||
|
||||
// TODO move to findByFile
|
||||
#ifdef Q_OS_LINUX
|
||||
QString exePath = QStandardPaths::findExecutable(QLatin1String("ls"));
|
||||
if (exePath.isEmpty())
|
||||
qWarning() << "ls not found";
|
||||
else {
|
||||
const QString executableType = QString::fromLatin1("application/x-executable");
|
||||
//QTest::newRow("executable") << exePath << executableType;
|
||||
QCOMPARE(db.mimeTypeForFile(exePath).name(), executableType);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypeForFileName_data()
|
||||
{
|
||||
QTest::addColumn<QString>("fileName");
|
||||
QTest::addColumn<QString>("expectedMimeType");
|
||||
|
||||
QTest::newRow("text") << "textfile.txt" << "text/plain";
|
||||
QTest::newRow("case-insensitive search") << "textfile.TxT" << "text/plain";
|
||||
|
||||
// Needs shared-mime-info > 0.91. Earlier versions wrote .Z to the mime.cache file...
|
||||
//QTest::newRow("case-insensitive match on a non-lowercase glob") << "foo.z" << "application/x-compress";
|
||||
|
||||
QTest::newRow("case-sensitive uppercase match") << "textfile.C" << "text/x-c++src";
|
||||
QTest::newRow("case-sensitive lowercase match") << "textfile.c" << "text/x-csrc";
|
||||
QTest::newRow("case-sensitive long-extension match") << "foo.PS.gz" << "application/x-gzpostscript";
|
||||
QTest::newRow("case-sensitive-only match") << "core" << "application/x-core";
|
||||
QTest::newRow("case-sensitive-only match") << "Core" << "application/octet-stream"; // #198477
|
||||
|
||||
QTest::newRow("desktop file") << "foo.desktop" << "application/x-desktop";
|
||||
QTest::newRow("old kdelnk file is x-desktop too") << "foo.kdelnk" << "application/x-desktop";
|
||||
QTest::newRow("double-extension file") << "foo.tar.bz2" << "application/x-bzip-compressed-tar";
|
||||
QTest::newRow("single-extension file") << "foo.bz2" << "application/x-bzip";
|
||||
QTest::newRow(".doc should assume msword") << "somefile.doc" << "application/msword"; // #204139
|
||||
QTest::newRow("glob that uses [] syntax, 1") << "Makefile" << "text/x-makefile";
|
||||
QTest::newRow("glob that uses [] syntax, 2") << "makefile" << "text/x-makefile";
|
||||
QTest::newRow("glob that ends with *, no extension") << "README" << "text/x-readme";
|
||||
QTest::newRow("glob that ends with *, extension") << "README.foo" << "text/x-readme";
|
||||
QTest::newRow("glob that ends with *, also matches *.txt. Higher weight wins.") << "README.txt" << "text/plain";
|
||||
QTest::newRow("glob that ends with *, also matches *.nfo. Higher weight wins.") << "README.nfo" << "text/x-nfo";
|
||||
// fdo bug 15436, needs shared-mime-info >= 0.40 (and this tests the globs2-parsing code).
|
||||
QTest::newRow("glob that ends with *, also matches *.pdf. *.pdf has higher weight") << "README.pdf" << "application/pdf";
|
||||
QTest::newRow("directory") << "/" << "inode/directory";
|
||||
QTest::newRow("doesn't exist, no extension") << "IDontExist" << "application/octet-stream";
|
||||
QTest::newRow("doesn't exist but has known extension") << "IDontExist.txt" << "text/plain";
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypeForFileName()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
QFETCH(QString, expectedMimeType);
|
||||
QMimeDatabase db;
|
||||
QMimeType mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchExtension);
|
||||
QVERIFY(mime.isValid());
|
||||
QCOMPARE(mime.name(), expectedMimeType);
|
||||
|
||||
QList<QMimeType> mimes = db.mimeTypesForFileName(fileName);
|
||||
if (expectedMimeType == "application/octet-stream") {
|
||||
QVERIFY(mimes.isEmpty());
|
||||
} else {
|
||||
QVERIFY(!mimes.isEmpty());
|
||||
QCOMPARE(mimes.count(), 1);
|
||||
QCOMPARE(mimes.first().name(), expectedMimeType);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypesForFileName_data()
|
||||
{
|
||||
QTest::addColumn<QString>("fileName");
|
||||
QTest::addColumn<QStringList>("expectedMimeTypes");
|
||||
|
||||
QTest::newRow("txt, 1 hit") << "foo.txt" << (QStringList() << "text/plain");
|
||||
QTest::newRow("txtfoobar, 0 hit") << "foo.foobar" << QStringList();
|
||||
QTest::newRow("m, 2 hits") << "foo.m" << (QStringList() << "text/x-matlab" << "text/x-objcsrc");
|
||||
QTest::newRow("sub, 3 hits") << "foo.sub" << (QStringList() << "text/x-microdvd" << "text/x-mpsub" << "text/x-subviewer");
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypesForFileName()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
QFETCH(QStringList, expectedMimeTypes);
|
||||
QMimeDatabase db;
|
||||
QList<QMimeType> mimes = db.mimeTypesForFileName(fileName);
|
||||
QStringList mimeNames;
|
||||
foreach (const QMimeType &mime, mimes)
|
||||
mimeNames.append(mime.name());
|
||||
QCOMPARE(mimeNames, expectedMimeTypes);
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::inheritance()
|
||||
{
|
||||
QMimeDatabase db;
|
||||
|
||||
// All file-like mimetypes inherit from octet-stream
|
||||
const QMimeType wordperfect = db.mimeTypeForName(QString::fromLatin1("application/vnd.wordperfect"));
|
||||
QVERIFY(wordperfect.isValid());
|
||||
QCOMPARE(wordperfect.parentMimeTypes().join(QString::fromLatin1(",")), QString::fromLatin1("application/octet-stream"));
|
||||
QVERIFY(wordperfect.inherits(QLatin1String("application/octet-stream")));
|
||||
|
||||
QVERIFY(db.mimeTypeForName(QString::fromLatin1("image/svg+xml-compressed")).inherits(QLatin1String("application/x-gzip")));
|
||||
|
||||
// Check that msword derives from ole-storage
|
||||
const QMimeType msword = db.mimeTypeForName(QString::fromLatin1("application/msword"));
|
||||
QVERIFY(msword.isValid());
|
||||
const QMimeType olestorage = db.mimeTypeForName(QString::fromLatin1("application/x-ole-storage"));
|
||||
QVERIFY(olestorage.isValid());
|
||||
QVERIFY(msword.inherits(olestorage.name()));
|
||||
QVERIFY(msword.inherits(QLatin1String("application/octet-stream")));
|
||||
|
||||
const QMimeType directory = db.mimeTypeForName(QString::fromLatin1("inode/directory"));
|
||||
QVERIFY(directory.isValid());
|
||||
QCOMPARE(directory.parentMimeTypes().count(), 0);
|
||||
QVERIFY(!directory.inherits(QLatin1String("application/octet-stream")));
|
||||
|
||||
// Check that text/x-patch knows that it inherits from text/plain (it says so explicitly)
|
||||
const QMimeType plain = db.mimeTypeForName(QString::fromLatin1("text/plain"));
|
||||
const QMimeType derived = db.mimeTypeForName(QString::fromLatin1("text/x-patch"));
|
||||
QVERIFY(derived.isValid());
|
||||
QCOMPARE(derived.parentMimeTypes().join(QString::fromLatin1(",")), plain.name());
|
||||
QVERIFY(derived.inherits(QLatin1String("text/plain")));
|
||||
QVERIFY(derived.inherits(QLatin1String("application/octet-stream")));
|
||||
|
||||
// Check that application/x-shellscript inherits from application/x-executable
|
||||
// (Otherwise KRun cannot start shellscripts...)
|
||||
// This is a test for multiple inheritance...
|
||||
const QMimeType shellscript = db.mimeTypeForName(QString::fromLatin1("application/x-shellscript"));
|
||||
QVERIFY(shellscript.isValid());
|
||||
QVERIFY(shellscript.inherits(QLatin1String("text/plain")));
|
||||
QVERIFY(shellscript.inherits(QLatin1String("application/x-executable")));
|
||||
const QStringList shellParents = shellscript.parentMimeTypes();
|
||||
QVERIFY(shellParents.contains(QLatin1String("text/plain")));
|
||||
QVERIFY(shellParents.contains(QLatin1String("application/x-executable")));
|
||||
QCOMPARE(shellParents.count(), 2); // only the above two
|
||||
const QStringList allShellAncestors = shellscript.allAncestors();
|
||||
QVERIFY(allShellAncestors.contains(QLatin1String("text/plain")));
|
||||
QVERIFY(allShellAncestors.contains(QLatin1String("application/x-executable")));
|
||||
QVERIFY(allShellAncestors.contains(QLatin1String("application/octet-stream")));
|
||||
// Must be least-specific last, i.e. breadth first.
|
||||
QCOMPARE(allShellAncestors.last(), QString::fromLatin1("application/octet-stream"));
|
||||
|
||||
const QStringList allSvgAncestors = db.mimeTypeForName(QString::fromLatin1("image/svg+xml")).allAncestors();
|
||||
QCOMPARE(allSvgAncestors, QStringList() << QLatin1String("application/xml") << QLatin1String("text/plain") << QLatin1String("application/octet-stream"));
|
||||
|
||||
// Check that text/x-mrml knows that it inherits from text/plain (implicitly)
|
||||
const QMimeType mrml = db.mimeTypeForName(QString::fromLatin1("text/x-mrml"));
|
||||
QVERIFY(mrml.isValid());
|
||||
QVERIFY(mrml.inherits(QLatin1String("text/plain")));
|
||||
QVERIFY(mrml.inherits(QLatin1String("application/octet-stream")));
|
||||
|
||||
// Check that msword-template inherits msword
|
||||
const QMimeType mswordTemplate = db.mimeTypeForName(QString::fromLatin1("application/msword-template"));
|
||||
QVERIFY(mswordTemplate.isValid());
|
||||
QVERIFY(mswordTemplate.inherits(QLatin1String("application/msword")));
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::aliases()
|
||||
{
|
||||
QMimeDatabase db;
|
||||
|
||||
const QMimeType canonical = db.mimeTypeForName(QString::fromLatin1("application/xml"));
|
||||
QVERIFY(canonical.isValid());
|
||||
|
||||
QMimeType resolvedAlias = db.mimeTypeForName(QString::fromLatin1("text/xml"));
|
||||
QVERIFY(resolvedAlias.isValid());
|
||||
QCOMPARE(resolvedAlias.name(), QString::fromLatin1("application/xml"));
|
||||
|
||||
QVERIFY(resolvedAlias.inherits(QLatin1String("application/xml")));
|
||||
QVERIFY(canonical.inherits(QLatin1String("text/xml")));
|
||||
|
||||
// Test for kde bug 197346: does nspluginscan see that audio/mp3 already exists?
|
||||
bool mustWriteMimeType = !db.mimeTypeForName(QString::fromLatin1("audio/mp3")).isValid();
|
||||
QVERIFY(!mustWriteMimeType);
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::icons()
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QMimeType directory = db.mimeTypeForFile(QString::fromLatin1("/"));
|
||||
QCOMPARE(directory.name(), QString::fromLatin1("inode/directory"));
|
||||
QCOMPARE(directory.iconName(), QString::fromLatin1("inode-directory"));
|
||||
QCOMPARE(directory.genericIconName(), QString::fromLatin1("inode-x-generic"));
|
||||
|
||||
QMimeType pub = db.mimeTypeForFile(QString::fromLatin1("foo.epub"), QMimeDatabase::MatchExtension);
|
||||
QCOMPARE(pub.name(), QString::fromLatin1("application/epub+zip"));
|
||||
QCOMPARE(pub.iconName(), QString::fromLatin1("application-epub+zip"));
|
||||
QCOMPARE(pub.genericIconName(), QString::fromLatin1("x-office-document"));
|
||||
}
|
||||
|
||||
// In here we do the tests that need some content in a temporary file.
|
||||
// This could also be added to shared-mime-info's testsuite...
|
||||
void tst_QMimeDatabase::mimeTypeForFileWithContent()
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QMimeType mime;
|
||||
|
||||
// Test a real PDF file.
|
||||
// If we find x-matlab because it starts with '%' then we are not ordering by priority.
|
||||
QTemporaryFile tempFile;
|
||||
QVERIFY(tempFile.open());
|
||||
QString tempFileName = tempFile.fileName();
|
||||
tempFile.write("%PDF-");
|
||||
tempFile.close();
|
||||
mime = db.mimeTypeForFile(tempFileName);
|
||||
QCOMPARE(mime.name(), QString::fromLatin1("application/pdf"));
|
||||
QFile file(tempFileName);
|
||||
mime = db.mimeTypeForData(&file); // QIODevice ctor
|
||||
QCOMPARE(mime.name(), QString::fromLatin1("application/pdf"));
|
||||
// by name only, we cannot find the mimetype
|
||||
mime = db.mimeTypeForFile(tempFileName, QMimeDatabase::MatchExtension);
|
||||
QVERIFY(mime.isValid());
|
||||
QVERIFY(mime.isDefault());
|
||||
|
||||
// Test the case where the extension doesn't match the contents: extension wins
|
||||
{
|
||||
QTemporaryFile txtTempFile(QDir::tempPath() + QLatin1String("/tst_QMimeDatabase_XXXXXX.txt"));
|
||||
QVERIFY(txtTempFile.open());
|
||||
txtTempFile.write("%PDF-");
|
||||
QString txtTempFileName = txtTempFile.fileName();
|
||||
txtTempFile.close();
|
||||
mime = db.mimeTypeForFile(txtTempFileName);
|
||||
QCOMPARE(mime.name(), QString::fromLatin1("text/plain"));
|
||||
// fast mode finds the same
|
||||
mime = db.mimeTypeForFile(txtTempFileName, QMimeDatabase::MatchExtension);
|
||||
QCOMPARE(mime.name(), QString::fromLatin1("text/plain"));
|
||||
}
|
||||
|
||||
// Now the case where extension differs from contents, but contents has >80 magic rule
|
||||
// XDG spec says: contents wins. But we can't sniff all files...
|
||||
{
|
||||
QTemporaryFile txtTempFile(QDir::tempPath() + QLatin1String("/tst_QMimeDatabase_XXXXXX.txt"));
|
||||
QVERIFY(txtTempFile.open());
|
||||
txtTempFile.write("<smil");
|
||||
QString txtTempFileName = txtTempFile.fileName();
|
||||
txtTempFile.close();
|
||||
mime = db.mimeTypeForFile(txtTempFileName);
|
||||
QCOMPARE(mime.name(), QString::fromLatin1("text/plain"));
|
||||
}
|
||||
|
||||
// Test what happens with an incorrect path
|
||||
mime = db.mimeTypeForFile(QString::fromLatin1("file:///etc/passwd" /* incorrect code, use a path instead */));
|
||||
QVERIFY(mime.isDefault());
|
||||
|
||||
// findByData when the device cannot be opened (e.g. a directory)
|
||||
QFile dir("/");
|
||||
mime = db.mimeTypeForData(&dir);
|
||||
QVERIFY(mime.isDefault());
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypeForUrl()
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QVERIFY(db.mimeTypeForUrl(QUrl::fromEncoded("http://foo/bar.png")).isDefault()); // HTTP can't know before downloading
|
||||
QCOMPARE(db.mimeTypeForUrl(QUrl::fromEncoded("ftp://foo/bar.png")).name(), QString::fromLatin1("image/png"));
|
||||
QCOMPARE(db.mimeTypeForUrl(QUrl::fromEncoded("ftp://foo/bar")).name(), QString::fromLatin1("application/octet-stream")); // unknown extension
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypeForData_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("data");
|
||||
QTest::addColumn<QString>("expectedMimeTypeName");
|
||||
|
||||
QTest::newRow("tnef data, needs smi >= 0.20") << QByteArray("\x78\x9f\x3e\x22") << "application/vnd.ms-tnef";
|
||||
QTest::newRow("PDF magic") << QByteArray("%PDF-") << "application/pdf";
|
||||
QTest::newRow("PHP, High-priority rule") << QByteArray("<?php") << "application/x-php";
|
||||
QTest::newRow("unknown") << QByteArray("\001abc?}") << "application/octet-stream";
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypeForData()
|
||||
{
|
||||
QFETCH(QByteArray, data);
|
||||
QFETCH(QString, expectedMimeTypeName);
|
||||
|
||||
QMimeDatabase db;
|
||||
QCOMPARE(db.mimeTypeForData(data).name(), expectedMimeTypeName);
|
||||
QBuffer buffer(&data);
|
||||
QCOMPARE(db.mimeTypeForData(&buffer).name(), expectedMimeTypeName);
|
||||
QVERIFY(!buffer.isOpen()); // initial state was restored
|
||||
|
||||
QVERIFY(buffer.open(QIODevice::ReadOnly));
|
||||
QCOMPARE(db.mimeTypeForData(&buffer).name(), expectedMimeTypeName);
|
||||
QVERIFY(buffer.isOpen());
|
||||
QCOMPARE(buffer.pos(), qint64(0));
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypeForFileAndContent_data()
|
||||
{
|
||||
QTest::addColumn<QString>("name");
|
||||
QTest::addColumn<QByteArray>("data");
|
||||
QTest::addColumn<QString>("expectedMimeTypeName");
|
||||
|
||||
QTest::newRow("plain text, no extension") << QString::fromLatin1("textfile") << QByteArray("Hello world") << "text/plain";
|
||||
QTest::newRow("plain text, unknown extension") << QString::fromLatin1("textfile.foo") << QByteArray("Hello world") << "text/plain";
|
||||
// Needs kde/mimetypes.xml
|
||||
//QTest::newRow("plain text, doc extension") << QString::fromLatin1("textfile.doc") << QByteArray("Hello world") << "text/plain";
|
||||
|
||||
// If you get powerpoint instead, then you're hit by https://bugs.freedesktop.org/show_bug.cgi?id=435,
|
||||
// upgrade to shared-mime-info >= 0.22
|
||||
const QByteArray oleData("\320\317\021\340\241\261\032\341"); // same as \xD0\xCF\x11\xE0 \xA1\xB1\x1A\xE1
|
||||
QTest::newRow("msword file, unknown extension") << QString::fromLatin1("mswordfile") << oleData << "application/x-ole-storage";
|
||||
QTest::newRow("excel file, found by extension") << QString::fromLatin1("excelfile.xls") << oleData << "application/vnd.ms-excel";
|
||||
QTest::newRow("text.xls, found by extension, user is in control") << QString::fromLatin1("text.xls") << oleData << "application/vnd.ms-excel";
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::mimeTypeForFileAndContent()
|
||||
{
|
||||
QFETCH(QString, name);
|
||||
QFETCH(QByteArray, data);
|
||||
QFETCH(QString, expectedMimeTypeName);
|
||||
|
||||
QMimeDatabase db;
|
||||
QCOMPARE(db.mimeTypeForNameAndData(name, data).name(), expectedMimeTypeName);
|
||||
|
||||
QBuffer buffer(&data);
|
||||
QCOMPARE(db.mimeTypeForNameAndData(name, &buffer).name(), expectedMimeTypeName);
|
||||
QVERIFY(!buffer.isOpen()); // initial state was restored
|
||||
|
||||
QVERIFY(buffer.open(QIODevice::ReadOnly));
|
||||
QCOMPARE(db.mimeTypeForNameAndData(name, &buffer).name(), expectedMimeTypeName);
|
||||
QVERIFY(buffer.isOpen());
|
||||
QCOMPARE(buffer.pos(), qint64(0));
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::allMimeTypes()
|
||||
{
|
||||
QMimeDatabase db;
|
||||
const QList<QMimeType> lst = db.allMimeTypes(); // does NOT include aliases
|
||||
QVERIFY(!lst.isEmpty());
|
||||
|
||||
// Hardcoding this is the only way to check both providers find the same number of mimetypes.
|
||||
QCOMPARE(lst.count(), 661);
|
||||
|
||||
foreach (const QMimeType &mime, lst) {
|
||||
const QString name = mime.name();
|
||||
QVERIFY(!name.isEmpty());
|
||||
QCOMPARE(name.count(QLatin1Char('/')), 1);
|
||||
const QMimeType lookedupMime = db.mimeTypeForName(name);
|
||||
QVERIFY(lookedupMime.isValid());
|
||||
QCOMPARE(lookedupMime.name(), name); // if this fails, you have an alias defined as a real mimetype too!
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::inheritsPerformance()
|
||||
{
|
||||
// Check performance of inherits().
|
||||
// This benchmark (which started in 2009 in kmimetypetest.cpp) uses 40 mimetypes.
|
||||
QStringList mimeTypes; mimeTypes << QLatin1String("image/jpeg") << QLatin1String("image/png") << QLatin1String("image/tiff") << QLatin1String("text/plain") << QLatin1String("text/html");
|
||||
mimeTypes += mimeTypes;
|
||||
mimeTypes += mimeTypes;
|
||||
mimeTypes += mimeTypes;
|
||||
QCOMPARE(mimeTypes.count(), 40);
|
||||
QMimeDatabase db;
|
||||
QMimeType mime = db.mimeTypeForName(QString::fromLatin1("text/x-chdr"));
|
||||
QVERIFY(mime.isValid());
|
||||
QBENCHMARK {
|
||||
QString match;
|
||||
foreach (const QString &mt, mimeTypes) {
|
||||
if (mime.inherits(mt)) {
|
||||
match = mt;
|
||||
// of course there would normally be a "break" here, but we're testing worse-case
|
||||
// performance here
|
||||
}
|
||||
}
|
||||
QCOMPARE(match, QString::fromLatin1("text/plain"));
|
||||
}
|
||||
// Numbers from 2011, in release mode:
|
||||
// KDE 4.7 numbers: 0.21 msec / 494,000 ticks / 568,345 instr. loads per iteration
|
||||
// QMimeBinaryProvider (with Qt 5): 0.16 msec / NA / 416,049 instr. reads per iteration
|
||||
// QMimeXmlProvider (with Qt 5): 0.062 msec / NA / 172,889 instr. reads per iteration
|
||||
// (but the startup time is way higher)
|
||||
// And memory usage is flat at 200K with QMimeBinaryProvider, while it peaks at 6 MB when
|
||||
// parsing XML, and then keeps being around 4.5 MB for all the in-memory hashes.
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::suffixes_data()
|
||||
{
|
||||
QTest::addColumn<QString>("mimeType");
|
||||
QTest::addColumn<QString>("patterns");
|
||||
QTest::addColumn<QString>("preferredSuffix");
|
||||
|
||||
QTest::newRow("mimetype with a single pattern") << "application/pdf" << "*.pdf" << "pdf";
|
||||
QTest::newRow("mimetype with multiple patterns") << "application/x-kpresenter" << "*.kpr;*.kpt" << "kpr";
|
||||
//if (KMimeType::sharedMimeInfoVersion() > KDE_MAKE_VERSION(0, 60, 0)) {
|
||||
QTest::newRow("mimetype with many patterns") << "application/vnd.wordperfect" << "*.wp;*.wp4;*.wp5;*.wp6;*.wpd;*.wpp" << "wp";
|
||||
//}
|
||||
QTest::newRow("oasis text mimetype") << "application/vnd.oasis.opendocument.text" << "*.odt" << "odt";
|
||||
QTest::newRow("oasis presentation mimetype") << "application/vnd.oasis.opendocument.presentation" << "*.odp" << "odp";
|
||||
QTest::newRow("mimetype with multiple patterns") << "text/plain" << "*.asc;*.txt;*,v" << "txt";
|
||||
QTest::newRow("mimetype with uncommon pattern") << "text/x-readme" << "README*" << QString();
|
||||
QTest::newRow("mimetype with no patterns") << "application/x-ole-storage" << QString() << QString();
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::suffixes()
|
||||
{
|
||||
QFETCH(QString, mimeType);
|
||||
QFETCH(QString, patterns);
|
||||
QFETCH(QString, preferredSuffix);
|
||||
QMimeDatabase db;
|
||||
QMimeType mime = db.mimeTypeForName(mimeType);
|
||||
QVERIFY(mime.isValid());
|
||||
// Sort both lists; order is unreliable since shared-mime-info uses hashes internally.
|
||||
QStringList expectedPatterns = patterns.split(QLatin1Char(';'));
|
||||
expectedPatterns.sort();
|
||||
QStringList mimePatterns = mime.globPatterns();
|
||||
mimePatterns.sort();
|
||||
QCOMPARE(mimePatterns.join(QLatin1String(";")), expectedPatterns.join(QLatin1String(";")));
|
||||
QCOMPARE(mime.preferredSuffix(), preferredSuffix);
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::knownSuffix()
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QCOMPARE(db.suffixForFileName(QString::fromLatin1("foo.tar")), QString::fromLatin1("tar"));
|
||||
QCOMPARE(db.suffixForFileName(QString::fromLatin1("foo.bz2")), QString::fromLatin1("bz2"));
|
||||
QCOMPARE(db.suffixForFileName(QString::fromLatin1("foo.bar.bz2")), QString::fromLatin1("bz2"));
|
||||
QCOMPARE(db.suffixForFileName(QString::fromLatin1("foo.tar.bz2")), QString::fromLatin1("tar.bz2"));
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::findByFileName_data()
|
||||
{
|
||||
QTest::addColumn<QString>("filePath");
|
||||
QTest::addColumn<QString>("mimeTypeName");
|
||||
QTest::addColumn<QString>("xFail");
|
||||
|
||||
QString prefix = QLatin1String(SRCDIR "testfiles/");
|
||||
|
||||
QFile f(prefix + QLatin1String("list"));
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
const QString warning = QString::fromLatin1(
|
||||
"Please download the shared-mime-info test suite:\n"
|
||||
"cd tests/auto/corelib/mimetypes/qmimedatabase\n"
|
||||
"wget http://cgit.freedesktop.org/xdg/shared-mime-info/snapshot/Release-1-0.zip\n"
|
||||
"unzip Release-1-0.zip\n"
|
||||
"ln -s Release-1-0/tests testfiles\n"
|
||||
);
|
||||
qWarning() << warning;
|
||||
QSKIP("shared-mime-info test suite not available.");
|
||||
}
|
||||
|
||||
QByteArray line(1024, Qt::Uninitialized);
|
||||
|
||||
while (!f.atEnd()) {
|
||||
int len = f.readLine(line.data(), 1023);
|
||||
|
||||
if (len <= 2 || line.at(0) == '#')
|
||||
continue;
|
||||
|
||||
QString string = QString::fromLatin1(line.constData(), len - 1).trimmed();
|
||||
QStringList list = string.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||
QVERIFY(list.size() >= 2);
|
||||
|
||||
QString filePath = list.at(0);
|
||||
QString mimeTypeType = list.at(1);
|
||||
QString xFail;
|
||||
if (list.size() >= 3)
|
||||
xFail = list.at(2);
|
||||
|
||||
QTest::newRow(filePath.toLatin1().constData()) << QString(prefix + filePath) << mimeTypeType << xFail;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::findByFileName()
|
||||
{
|
||||
QFETCH(QString, filePath);
|
||||
QFETCH(QString, mimeTypeName);
|
||||
QFETCH(QString, xFail);
|
||||
|
||||
QMimeDatabase database;
|
||||
|
||||
//qDebug() << Q_FUNC_INFO << filePath;
|
||||
|
||||
const QMimeType resultMimeType(database.mimeTypeForFile(filePath, QMimeDatabase::MatchExtension));
|
||||
if (resultMimeType.isValid()) {
|
||||
//qDebug() << Q_FUNC_INFO << "MIME type" << resultMimeType.name() << "has generic icon name" << resultMimeType.genericIconName() << "and icon name" << resultMimeType.iconName();
|
||||
|
||||
// Loading icons depend on the icon theme, we can't enable this test
|
||||
#if 0
|
||||
QCOMPARE(resultMimeType.genericIconName(), QIcon::fromTheme(resultMimeType.genericIconName()).name());
|
||||
QVERIFY2(!QIcon::fromTheme(resultMimeType.genericIconName()).isNull(), qPrintable(resultMimeType.genericIconName()));
|
||||
QVERIFY2(QIcon::hasThemeIcon(resultMimeType.genericIconName()), qPrintable(resultMimeType.genericIconName()));
|
||||
|
||||
QCOMPARE(resultMimeType.iconName(), QIcon::fromTheme(resultMimeType.iconName()).name());
|
||||
QVERIFY2(!QIcon::fromTheme(resultMimeType.iconName()).isNull(), qPrintable(resultMimeType.iconName()));
|
||||
QVERIFY2(QIcon::hasThemeIcon(resultMimeType.iconName()), qPrintable(resultMimeType.iconName()));
|
||||
#endif
|
||||
}
|
||||
const QString resultMimeTypeName = resultMimeType.name();
|
||||
//qDebug() << Q_FUNC_INFO << "mimeTypeForFile() returned" << resultMimeTypeName;
|
||||
|
||||
const bool failed = resultMimeTypeName != mimeTypeName;
|
||||
const bool shouldFail = (xFail.length() >= 1 && xFail.at(0) == QLatin1Char('x'));
|
||||
if (shouldFail != failed) {
|
||||
// Results are ambiguous when multiple MIME types have the same glob
|
||||
// -> accept the current result if the found MIME type actually
|
||||
// matches the file's extension.
|
||||
// TODO: a better file format in testfiles/list!
|
||||
const QMimeType foundMimeType = database.mimeTypeForName(resultMimeTypeName);
|
||||
QVERIFY2(resultMimeType == foundMimeType, qPrintable(resultMimeType.name() + QString::fromLatin1(" vs. ") + foundMimeType.name()));
|
||||
if (foundMimeType.isValid()) {
|
||||
const QString extension = QFileInfo(filePath).suffix();
|
||||
//qDebug() << Q_FUNC_INFO << "globPatterns:" << foundMimeType.globPatterns() << "- extension:" << QString() + "*." + extension;
|
||||
if (foundMimeType.globPatterns().contains(QString::fromLatin1("*.") + extension))
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (shouldFail) {
|
||||
// Expected to fail
|
||||
QVERIFY2(resultMimeTypeName != mimeTypeName, qPrintable(resultMimeTypeName));
|
||||
} else {
|
||||
QCOMPARE(resultMimeTypeName, mimeTypeName);
|
||||
}
|
||||
|
||||
// Test QFileInfo overload
|
||||
const QMimeType mimeForFileInfo = database.mimeTypeForFile(QFileInfo(filePath), QMimeDatabase::MatchExtension);
|
||||
QCOMPARE(mimeForFileInfo.name(), resultMimeTypeName);
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::findByData_data()
|
||||
{
|
||||
findByFileName_data();
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::findByData()
|
||||
{
|
||||
QFETCH(QString, filePath);
|
||||
QFETCH(QString, mimeTypeName);
|
||||
QFETCH(QString, xFail);
|
||||
|
||||
QMimeDatabase database;
|
||||
QFile f(filePath);
|
||||
QVERIFY(f.open(QIODevice::ReadOnly));
|
||||
QByteArray data = f.read(16384);
|
||||
|
||||
const QString resultMimeTypeName = database.mimeTypeForData(data).name();
|
||||
if (xFail.length() >= 2 && xFail.at(1) == QLatin1Char('x')) {
|
||||
// Expected to fail
|
||||
QVERIFY2(resultMimeTypeName != mimeTypeName, qPrintable(resultMimeTypeName));
|
||||
} else {
|
||||
QCOMPARE(resultMimeTypeName, mimeTypeName);
|
||||
}
|
||||
|
||||
QFileInfo info(filePath);
|
||||
QString mimeForInfo = database.mimeTypeForFile(info, QMimeDatabase::MatchContent).name();
|
||||
QCOMPARE(mimeForInfo, resultMimeTypeName);
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::findByFile_data()
|
||||
{
|
||||
findByFileName_data();
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::findByFile()
|
||||
{
|
||||
QFETCH(QString, filePath);
|
||||
QFETCH(QString, mimeTypeName);
|
||||
QFETCH(QString, xFail);
|
||||
|
||||
QMimeDatabase database;
|
||||
const QString resultMimeTypeName = database.mimeTypeForFile(filePath).name();
|
||||
//qDebug() << Q_FUNC_INFO << filePath << "->" << resultMimeTypeName;
|
||||
if (xFail.length() >= 3 && xFail.at(2) == QLatin1Char('x')) {
|
||||
// Expected to fail
|
||||
QVERIFY2(resultMimeTypeName != mimeTypeName, qPrintable(resultMimeTypeName));
|
||||
} else {
|
||||
QCOMPARE(resultMimeTypeName, mimeTypeName);
|
||||
}
|
||||
|
||||
// Test QFileInfo overload
|
||||
const QMimeType mimeForFileInfo = database.mimeTypeForFile(QFileInfo(filePath));
|
||||
QCOMPARE(mimeForFileInfo.name(), resultMimeTypeName);
|
||||
}
|
||||
|
||||
|
||||
void tst_QMimeDatabase::fromThreads()
|
||||
{
|
||||
QThreadPool::globalInstance()->setMaxThreadCount(20);
|
||||
// Note that data-based tests cannot be used here (QTest::fetchData asserts).
|
||||
QList<QFuture<void> > futures;
|
||||
futures << QtConcurrent::run(this, &tst_QMimeDatabase::mimeTypeForName);
|
||||
futures << QtConcurrent::run(this, &tst_QMimeDatabase::aliases);
|
||||
futures << QtConcurrent::run(this, &tst_QMimeDatabase::allMimeTypes);
|
||||
futures << QtConcurrent::run(this, &tst_QMimeDatabase::icons);
|
||||
futures << QtConcurrent::run(this, &tst_QMimeDatabase::inheritance);
|
||||
futures << QtConcurrent::run(this, &tst_QMimeDatabase::knownSuffix);
|
||||
futures << QtConcurrent::run(this, &tst_QMimeDatabase::mimeTypeForFileWithContent);
|
||||
futures << QtConcurrent::run(this, &tst_QMimeDatabase::allMimeTypes); // a second time
|
||||
Q_FOREACH (QFuture<void> f, futures)
|
||||
f.waitForFinished();
|
||||
}
|
||||
|
||||
static void runUpdateMimeDatabase(const QString &path) // TODO make it a QMimeDatabase method?
|
||||
{
|
||||
const QString umd = QStandardPaths::findExecutable(QString::fromLatin1("update-mime-database"));
|
||||
if (umd.isEmpty())
|
||||
QSKIP("shared-mime-info not found, skipping mime.cache test");
|
||||
|
||||
QProcess proc;
|
||||
proc.setProcessChannelMode(QProcess::MergedChannels); // silence output
|
||||
proc.start(umd, QStringList() << path);
|
||||
proc.waitForFinished();
|
||||
//qDebug() << "runUpdateMimeDatabase" << path;
|
||||
}
|
||||
|
||||
static void waitAndRunUpdateMimeDatabase(const QString &path)
|
||||
{
|
||||
QFileInfo mimeCacheInfo(path + QString::fromLatin1("/mime.cache"));
|
||||
if (mimeCacheInfo.exists()) {
|
||||
// Wait until the begining of the next second
|
||||
while (mimeCacheInfo.lastModified().secsTo(QDateTime::currentDateTime()) == 0) {
|
||||
QTest::qSleep(200);
|
||||
}
|
||||
}
|
||||
runUpdateMimeDatabase(path);
|
||||
}
|
||||
|
||||
static void checkHasMimeType(const QString &mimeType)
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QVERIFY(db.mimeTypeForName(mimeType).isValid());
|
||||
|
||||
bool found = false;
|
||||
foreach (const QMimeType &mt, db.allMimeTypes()) {
|
||||
if (mt.name() == mimeType) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QVERIFY(found);
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
extern Q_CORE_EXPORT int qmime_secondsBetweenChecks; // see qmimeprovider.cpp
|
||||
QT_END_NAMESPACE
|
||||
|
||||
void tst_QMimeDatabase::installNewGlobalMimeType()
|
||||
{
|
||||
qmime_secondsBetweenChecks = 0;
|
||||
|
||||
QMimeDatabase db;
|
||||
QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid());
|
||||
|
||||
const QString fileName = QLatin1String("yast2-metapackage-handler-mimetypes.xml");
|
||||
const QString srcFile = QFile::decodeName(SRCDIR) + fileName;
|
||||
|
||||
QDir here = QDir::currentPath();
|
||||
const QString mimeDir = here.absolutePath() + QLatin1String("/mime");
|
||||
const QString destDir = mimeDir + QLatin1String("/packages/");
|
||||
const QString destFile = destDir + fileName;
|
||||
QFile::remove(destFile);
|
||||
//qDebug() << destFile;
|
||||
QVERIFY(QFile::copy(srcFile, destFile));
|
||||
waitAndRunUpdateMimeDatabase(mimeDir);
|
||||
|
||||
QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(),
|
||||
QString::fromLatin1("text/x-suse-ymu"));
|
||||
QVERIFY(db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid());
|
||||
checkHasMimeType("text/x-suse-ymp");
|
||||
|
||||
// Now test removing it again
|
||||
QFile::remove(destFile);
|
||||
waitAndRunUpdateMimeDatabase(mimeDir);
|
||||
QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(),
|
||||
QString::fromLatin1("application/octet-stream"));
|
||||
QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid());
|
||||
}
|
||||
|
||||
void tst_QMimeDatabase::installNewLocalMimeType()
|
||||
{
|
||||
qmime_secondsBetweenChecks = 0;
|
||||
|
||||
QMimeDatabase db;
|
||||
QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid());
|
||||
|
||||
const QString fileName = QLatin1String("yast2-metapackage-handler-mimetypes.xml");
|
||||
const QString srcFile = QFile::decodeName(SRCDIR) + fileName;
|
||||
const QString mimeDir = m_dataHome + QLatin1String("/mime");
|
||||
const QString destDir = mimeDir + QLatin1String("/packages/");
|
||||
QDir().mkpath(destDir);
|
||||
const QString destFile = destDir + fileName;
|
||||
QFile::remove(destFile);
|
||||
QVERIFY(QFile::copy(srcFile, destFile));
|
||||
runUpdateMimeDatabase(mimeDir);
|
||||
|
||||
QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(),
|
||||
QString::fromLatin1("text/x-suse-ymu"));
|
||||
QVERIFY(db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid());
|
||||
checkHasMimeType("text/x-suse-ymp");
|
||||
|
||||
// Now test removing it again (note, this leaves a mostly-empty mime.cache file)
|
||||
QFile::remove(destFile);
|
||||
waitAndRunUpdateMimeDatabase(mimeDir);
|
||||
QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(),
|
||||
QString::fromLatin1("application/octet-stream"));
|
||||
QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid());
|
||||
|
||||
// And now the user goes wild and uses rm -rf
|
||||
QFile::remove(mimeDir + QString::fromLatin1("/mime.cache"));
|
||||
QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(),
|
||||
QString::fromLatin1("application/octet-stream"));
|
||||
QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid());
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_QMimeDatabase)
|
@ -0,0 +1,99 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TST_QMIMEDATABASE_H
|
||||
#define TST_QMIMEDATABASE_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class tst_QMimeDatabase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QMimeDatabase();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void mimeTypeForName();
|
||||
void mimeTypeForFileName_data();
|
||||
void mimeTypeForFileName();
|
||||
void mimeTypesForFileName_data();
|
||||
void mimeTypesForFileName();
|
||||
void inheritance();
|
||||
void aliases();
|
||||
void icons();
|
||||
void mimeTypeForFileWithContent();
|
||||
void mimeTypeForUrl();
|
||||
void mimeTypeForData_data();
|
||||
void mimeTypeForData();
|
||||
void mimeTypeForFileAndContent_data();
|
||||
void mimeTypeForFileAndContent();
|
||||
void allMimeTypes();
|
||||
void inheritsPerformance();
|
||||
void suffixes_data();
|
||||
void suffixes();
|
||||
void knownSuffix();
|
||||
void fromThreads();
|
||||
|
||||
// shared-mime-info test suite
|
||||
|
||||
void findByFileName_data();
|
||||
void findByFileName();
|
||||
|
||||
void findByData_data();
|
||||
void findByData();
|
||||
|
||||
void findByFile_data();
|
||||
void findByFile();
|
||||
|
||||
//
|
||||
|
||||
void installNewGlobalMimeType();
|
||||
void installNewLocalMimeType();
|
||||
|
||||
private:
|
||||
QString m_dataHome;
|
||||
};
|
||||
|
||||
#endif // TST_QMIMEDATABASE_H
|
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec -->
|
||||
<!-- to to ${PREFIX-~/.local}/share/mime/packages -->
|
||||
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
|
||||
<mime-type type="text/x-suse-ymp">
|
||||
<comment>YaST Meta Package</comment>
|
||||
<glob pattern="*.ymp"/>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="text/x-suse-ymu">
|
||||
<comment>URL of a YaST Meta Package</comment>
|
||||
<glob pattern="*.ymu"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
|
7
tests/auto/corelib/mimetypes/qmimetype/qmimetype.pro
Normal file
7
tests/auto/corelib/mimetypes/qmimetype/qmimetype.pro
Normal file
@ -0,0 +1,7 @@
|
||||
CONFIG += testcase parallel_test
|
||||
TARGET = tst_qmimetype
|
||||
QT = core-private testlib
|
||||
|
||||
*-g++*:QMAKE_CXXFLAGS += -W -Wall -Wextra -Werror -Wshadow -Wno-long-long -Wnon-virtual-dtor
|
||||
|
||||
SOURCES = tst_qmimetype.cpp
|
257
tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp
Normal file
257
tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <private/qmimetype_p.h>
|
||||
|
||||
#include <qmimetype.h>
|
||||
#include <qmimedatabase.h>
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
|
||||
class tst_qmimetype : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void isValid();
|
||||
void name();
|
||||
void genericIconName();
|
||||
void iconName();
|
||||
void suffixes();
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void tst_qmimetype::initTestCase()
|
||||
{
|
||||
qputenv("XDG_DATA_DIRS", "doesnotexist");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
static QString qMimeTypeName()
|
||||
{
|
||||
static const QString result ("No name of the MIME type");
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString qMimeTypeGenericIconName()
|
||||
{
|
||||
static const QString result ("No file name of an icon image that represents the MIME type");
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString qMimeTypeIconName()
|
||||
{
|
||||
static const QString result ("No file name of an icon image that represents the MIME type");
|
||||
return result;
|
||||
}
|
||||
|
||||
static QStringList buildQMimeTypeFilenameExtensions()
|
||||
{
|
||||
QStringList result;
|
||||
result << QString::fromLatin1("*.png");
|
||||
return result;
|
||||
}
|
||||
|
||||
static QStringList qMimeTypeGlobPatterns()
|
||||
{
|
||||
static const QStringList result (buildQMimeTypeFilenameExtensions());
|
||||
return result;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
#ifndef Q_COMPILER_RVALUE_REFS
|
||||
QMIMETYPE_BUILDER
|
||||
#else
|
||||
QMIMETYPE_BUILDER_FROM_RVALUE_REFS
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void tst_qmimetype::isValid()
|
||||
{
|
||||
QMimeType instantiatedQMimeType (
|
||||
buildQMimeType (
|
||||
qMimeTypeName(),
|
||||
qMimeTypeGenericIconName(),
|
||||
qMimeTypeIconName(),
|
||||
qMimeTypeGlobPatterns()
|
||||
)
|
||||
);
|
||||
|
||||
QVERIFY(instantiatedQMimeType.isValid());
|
||||
|
||||
QMimeType otherQMimeType (instantiatedQMimeType);
|
||||
|
||||
QVERIFY(otherQMimeType.isValid());
|
||||
QCOMPARE(instantiatedQMimeType, otherQMimeType);
|
||||
|
||||
QMimeType defaultQMimeType;
|
||||
|
||||
QVERIFY(!defaultQMimeType.isValid());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void tst_qmimetype::name()
|
||||
{
|
||||
QMimeType instantiatedQMimeType (
|
||||
buildQMimeType (
|
||||
qMimeTypeName(),
|
||||
qMimeTypeGenericIconName(),
|
||||
qMimeTypeIconName(),
|
||||
qMimeTypeGlobPatterns()
|
||||
)
|
||||
);
|
||||
|
||||
QMimeType otherQMimeType (
|
||||
buildQMimeType (
|
||||
QString(),
|
||||
qMimeTypeGenericIconName(),
|
||||
qMimeTypeIconName(),
|
||||
qMimeTypeGlobPatterns()
|
||||
)
|
||||
);
|
||||
|
||||
// Verify that the Name is part of the equality test:
|
||||
QCOMPARE(instantiatedQMimeType.name(), qMimeTypeName());
|
||||
|
||||
QVERIFY(instantiatedQMimeType != otherQMimeType);
|
||||
QVERIFY(!(instantiatedQMimeType == otherQMimeType));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void tst_qmimetype::genericIconName()
|
||||
{
|
||||
QMimeType instantiatedQMimeType (
|
||||
buildQMimeType (
|
||||
qMimeTypeName(),
|
||||
qMimeTypeGenericIconName(),
|
||||
qMimeTypeIconName(),
|
||||
qMimeTypeGlobPatterns()
|
||||
)
|
||||
);
|
||||
|
||||
QMimeType otherQMimeType (
|
||||
buildQMimeType (
|
||||
qMimeTypeName(),
|
||||
QString(),
|
||||
qMimeTypeGenericIconName(),
|
||||
qMimeTypeGlobPatterns()
|
||||
)
|
||||
);
|
||||
|
||||
// Verify that the GenericIconName is part of the equality test:
|
||||
QCOMPARE(instantiatedQMimeType.genericIconName(), qMimeTypeGenericIconName());
|
||||
|
||||
QVERIFY(instantiatedQMimeType != otherQMimeType);
|
||||
QVERIFY(!(instantiatedQMimeType == otherQMimeType));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void tst_qmimetype::iconName()
|
||||
{
|
||||
QMimeType instantiatedQMimeType (
|
||||
buildQMimeType (
|
||||
qMimeTypeName(),
|
||||
qMimeTypeGenericIconName(),
|
||||
qMimeTypeIconName(),
|
||||
qMimeTypeGlobPatterns()
|
||||
)
|
||||
);
|
||||
|
||||
QMimeType otherQMimeType (
|
||||
buildQMimeType (
|
||||
qMimeTypeName(),
|
||||
qMimeTypeGenericIconName(),
|
||||
QString(),
|
||||
qMimeTypeGlobPatterns()
|
||||
)
|
||||
);
|
||||
|
||||
// Verify that the IconName is part of the equality test:
|
||||
QCOMPARE(instantiatedQMimeType.iconName(), qMimeTypeIconName());
|
||||
|
||||
QVERIFY(instantiatedQMimeType != otherQMimeType);
|
||||
QVERIFY(!(instantiatedQMimeType == otherQMimeType));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void tst_qmimetype::suffixes()
|
||||
{
|
||||
QMimeType instantiatedQMimeType (
|
||||
buildQMimeType (
|
||||
qMimeTypeName(),
|
||||
qMimeTypeGenericIconName(),
|
||||
qMimeTypeIconName(),
|
||||
qMimeTypeGlobPatterns()
|
||||
)
|
||||
);
|
||||
|
||||
QMimeType otherQMimeType (
|
||||
buildQMimeType (
|
||||
qMimeTypeName(),
|
||||
qMimeTypeGenericIconName(),
|
||||
qMimeTypeIconName(),
|
||||
QStringList()
|
||||
)
|
||||
);
|
||||
|
||||
// Verify that the Suffixes are part of the equality test:
|
||||
QCOMPARE(instantiatedQMimeType.globPatterns(), qMimeTypeGlobPatterns());
|
||||
QCOMPARE(instantiatedQMimeType.suffixes(), QStringList() << QString::fromLatin1("png"));
|
||||
|
||||
QVERIFY(instantiatedQMimeType != otherQMimeType);
|
||||
QVERIFY(!(instantiatedQMimeType == otherQMimeType));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_qmimetype)
|
||||
#include "tst_qmimetype.moc"
|
Loading…
x
Reference in New Issue
Block a user