summaryrefslogtreecommitdiffstats
path: root/src/corelib/mimetypes/qmimeprovider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/mimetypes/qmimeprovider.cpp')
-rw-r--r--src/corelib/mimetypes/qmimeprovider.cpp830
1 files changed, 830 insertions, 0 deletions
diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp
new file mode 100644
index 0000000000..8ef0ee8881
--- /dev/null
+++ b/src/corelib/mimetypes/qmimeprovider.cpp
@@ -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