/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qmimeprovider_p.h" #include "qmimetypeparser_p.h" #include #include "qmimemagicrulematcher_p.h" #include #include #include #include #include #include #include static void initResources() { #if QT_CONFIG(mimetype_database) Q_INIT_RESOURCE(mimetypes); #endif } QT_BEGIN_NAMESPACE QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory) : m_db(db), m_directory(directory) { } QMimeBinaryProvider::QMimeBinaryProvider(QMimeDatabasePrivate *db, const QString &directory) : QMimeProviderBase(db, directory), m_mimetypeListLoaded(false) { ensureLoaded(); } struct QMimeBinaryProvider::CacheFile { CacheFile(const QString &fileName); ~CacheFile(); bool isValid() const { return m_valid; } inline quint16 getUint16(int offset) const { return qFromBigEndian(*reinterpret_cast(data + offset)); } inline quint32 getUint32(int offset) const { return qFromBigEndian(*reinterpret_cast(data + offset)); } inline const char *getCharStar(int offset) const { return reinterpret_cast(data + offset); } bool load(); bool reload(); QFile file; uchar *data; QDateTime m_mtime; bool m_valid; }; QMimeBinaryProvider::CacheFile::CacheFile(const QString &fileName) : file(fileName), m_valid(false) { load(); } QMimeBinaryProvider::CacheFile::~CacheFile() { } bool QMimeBinaryProvider::CacheFile::load() { if (!file.open(QIODevice::ReadOnly)) return false; data = file.map(0, file.size()); if (data) { const int major = getUint16(0); const int minor = getUint16(2); m_valid = (major == 1 && minor >= 1 && minor <= 2); } m_mtime = QFileInfo(file).lastModified(); return m_valid; } bool QMimeBinaryProvider::CacheFile::reload() { m_valid = false; if (file.isOpen()) { file.close(); } data = nullptr; return load(); } QMimeBinaryProvider::~QMimeBinaryProvider() { delete m_cacheFile; } bool QMimeBinaryProvider::isValid() { return m_cacheFile != nullptr; } // Position of the "list offsets" values, at the beginning of the mime.cache file enum { PosAliasListOffset = 4, PosParentListOffset = 8, PosLiteralListOffset = 12, PosReverseSuffixTreeOffset = 16, PosGlobListOffset = 20, PosMagicListOffset = 24, // PosNamespaceListOffset = 28, PosIconsListOffset = 32, PosGenericIconsListOffset = 36 }; bool QMimeBinaryProvider::checkCacheChanged() { QFileInfo fileInfo(m_cacheFile->file); if (fileInfo.lastModified() > m_cacheFile->m_mtime) { // Deletion can't happen by just running update-mime-database. // But the user could use rm -rf :-) m_cacheFile->reload(); // will mark itself as invalid on failure return true; } return false; } void QMimeBinaryProvider::ensureLoaded() { if (!m_cacheFile) { const QString cacheFileName = m_directory + QLatin1String("/mime.cache"); m_cacheFile = new CacheFile(cacheFileName); m_mimetypeListLoaded = false; } else { if (checkCacheChanged()) m_mimetypeListLoaded = false; else return; // nothing to do } if (!m_cacheFile->isValid()) { // verify existence and version delete m_cacheFile; m_cacheFile = nullptr; } } static QMimeType mimeTypeForNameUnchecked(const QString &name) { QMimeTypePrivate data; data.name = name; data.fromCache = true; // The rest is retrieved on demand. // comment and globPatterns: in loadMimeTypePrivate // iconName: in loadIcon // genericIconName: in loadGenericIcon return QMimeType(data); } QMimeType QMimeBinaryProvider::mimeTypeForName(const QString &name) { if (!m_mimetypeListLoaded) loadMimeTypeList(); if (!m_mimetypeNames.contains(name)) return QMimeType(); // unknown mimetype return mimeTypeForNameUnchecked(name); } void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) { if (fileName.isEmpty()) return; Q_ASSERT(m_cacheFile); const QString lowerFileName = fileName.toLower(); // Check literals (e.g. "Makefile") matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosLiteralListOffset), fileName); // Check complex globs (e.g. "callgrind.out[0-9]*") matchGlobList(result, m_cacheFile, m_cacheFile->getUint32(PosGlobListOffset), fileName); // Check the very common *.txt cases with the suffix tree const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, lowerFileName, lowerFileName.length() - 1, false); if (result.m_matchingMimeTypes.isEmpty()) matchSuffixTree(result, m_cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); } 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.midRef(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) : nullptr; if (!QMimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile->getCharStar(valueOffset), mask)) continue; const int numChildren = cacheFile->getUint32(off + 24); const int firstChildOffset = cacheFile->getUint32(off + 28); if (numChildren == 0) // No submatch? Then we are done. return true; // Check that one of the submatches matches too if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data)) return true; } return false; } void QMimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) { const int magicListOffset = m_cacheFile->getUint32(PosMagicListOffset); const int numMatches = m_cacheFile->getUint32(magicListOffset); //const int maxExtent = cacheFile->getUint32(magicListOffset + 4); const int firstMatchOffset = m_cacheFile->getUint32(magicListOffset + 8); for (int i = 0; i < numMatches; ++i) { const int off = firstMatchOffset + i * 16; const int numMatchlets = m_cacheFile->getUint32(off + 8); const int firstMatchletOffset = m_cacheFile->getUint32(off + 12); if (matchMagicRule(m_cacheFile, numMatchlets, firstMatchletOffset, data)) { const int mimeTypeOffset = m_cacheFile->getUint32(off + 4); const char *mimeType = m_cacheFile->getCharStar(mimeTypeOffset); *accuracyPtr = m_cacheFile->getUint32(off); // Return the first match. We have no rules for conflicting magic data... // (mime.cache itself is sorted, but what about local overrides with a lower prio?) candidate = mimeTypeForNameUnchecked(QLatin1String(mimeType)); return; } } } void QMimeBinaryProvider::addParents(const QString &mime, QStringList &result) { const QByteArray mimeStr = mime.toLatin1(); const int parentListOffset = m_cacheFile->getUint32(PosParentListOffset); const int numEntries = m_cacheFile->getUint32(parentListOffset); int begin = 0; int end = numEntries - 1; while (begin <= end) { const int medium = (begin + end) / 2; const int off = parentListOffset + 4 + 8 * medium; const int mimeOffset = m_cacheFile->getUint32(off); const char *aMime = m_cacheFile->getCharStar(mimeOffset); const int cmp = qstrcmp(aMime, mimeStr); if (cmp < 0) { begin = medium + 1; } else if (cmp > 0) { end = medium - 1; } else { const int parentsOffset = m_cacheFile->getUint32(off + 4); const int numParents = m_cacheFile->getUint32(parentsOffset); for (int i = 0; i < numParents; ++i) { const int parentOffset = m_cacheFile->getUint32(parentsOffset + 4 + 4 * i); const char *aParent = m_cacheFile->getCharStar(parentOffset); const QString strParent = QString::fromLatin1(aParent); if (!result.contains(strParent)) result.append(strParent); } break; } } } QString QMimeBinaryProvider::resolveAlias(const QString &name) { const QByteArray input = name.toLatin1(); const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset); const int numEntries = m_cacheFile->getUint32(aliasListOffset); int begin = 0; int end = numEntries - 1; while (begin <= end) { const int medium = (begin + end) / 2; const int off = aliasListOffset + 4 + 8 * medium; const int aliasOffset = m_cacheFile->getUint32(off); const char *alias = m_cacheFile->getCharStar(aliasOffset); const int cmp = qstrcmp(alias, input); if (cmp < 0) { begin = medium + 1; } else if (cmp > 0) { end = medium - 1; } else { const int mimeOffset = m_cacheFile->getUint32(off + 4); const char *mimeType = m_cacheFile->getCharStar(mimeOffset); return QLatin1String(mimeType); } } return QString(); } void QMimeBinaryProvider::addAliases(const QString &name, QStringList &result) { const QByteArray input = name.toLatin1(); const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset); const int numEntries = m_cacheFile->getUint32(aliasListOffset); for (int pos = 0; pos < numEntries; ++pos) { const int off = aliasListOffset + 4 + 8 * pos; const int mimeOffset = m_cacheFile->getUint32(off + 4); const char *mimeType = m_cacheFile->getCharStar(mimeOffset); if (input == mimeType) { const int aliasOffset = m_cacheFile->getUint32(off); const char *alias = m_cacheFile->getCharStar(aliasOffset); const QString strAlias = QString::fromLatin1(alias); if (!result.contains(strAlias)) result.append(strAlias); } } } void QMimeBinaryProvider::loadMimeTypeList() { if (!m_mimetypeListLoaded) { m_mimetypeListLoaded = true; m_mimetypeNames.clear(); // Unfortunately mime.cache doesn't have a full list of all mimetypes. // So we have to parse the plain-text files called "types". QFile file(m_directory + QStringLiteral("/types")); if (file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); stream.setCodec("ISO 8859-1"); QString line; while (stream.readLineInto(&line)) m_mimetypeNames.insert(line); } } } void QMimeBinaryProvider::addAllMimeTypes(QList &result) { loadMimeTypeList(); if (result.isEmpty()) { result.reserve(m_mimetypeNames.count()); for (const QString &name : qAsConst(m_mimetypeNames)) result.append(mimeTypeForNameUnchecked(name)); } else { for (const QString &name : qAsConst(m_mimetypeNames)) if (std::find_if(result.constBegin(), result.constEnd(), [name](const QMimeType &mime) -> bool { return mime.name() == name; }) == result.constEnd()) result.append(mimeTypeForNameUnchecked(name)); } } void QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate &data) { #ifdef QT_NO_XMLSTREAMREADER Q_UNUSED(data); qWarning("Cannot load mime type since QXmlStreamReader is not available."); return; #else if (data.loaded) return; data.loaded = true; // load comment and globPatterns const QString file = data.name + QLatin1String(".xml"); // shared-mime-info since 1.3 lowercases the xml files QStringList mimeFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/") + file.toLower()); if (mimeFiles.isEmpty()) mimeFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/") + file); // pre-1.3 if (mimeFiles.isEmpty()) { qWarning() << "No file found for" << file << ", even though update-mime-info said it would exist.\n" "Either it was just removed, or the directory doesn't have executable permission..." << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory); return; } QString mainPattern; for (QStringList::const_reverse_iterator it = mimeFiles.crbegin(), end = mimeFiles.crend(); it != end; ++it) { // global first, then local. QFile qfile(*it); if (!qfile.open(QFile::ReadOnly)) continue; QXmlStreamReader xml(&qfile); if (xml.readNextStartElement()) { if (xml.name() != QLatin1String("mime-type")) { continue; } const QStringRef name = xml.attributes().value(QLatin1String("type")); if (name.isEmpty()) continue; if (name.compare(data.name, Qt::CaseInsensitive)) 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("default"); // no locale attribute provided, treat it as default. } 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.isEmpty() || data.globPatterns.constFirst() != 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.constFirst() != mainPattern) { // ensure it's first in the list of patterns data.globPatterns.removeAll(mainPattern); data.globPatterns.prepend(mainPattern); } } else { // Fallback: get the patterns from the globs file // TODO: This would be the only way to support shared-mime-info < 0.70 // But is this really worth the effort? } #endif #endif //QT_NO_XMLSTREAMREADER } // Binary search in the icons or generic-icons list QLatin1String QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime) { const int iconsListOffset = cacheFile->getUint32(posListOffset); const int numIcons = cacheFile->getUint32(iconsListOffset); int begin = 0; int end = numIcons - 1; while (begin <= end) { const int medium = (begin + end) / 2; const int off = iconsListOffset + 4 + 8 * medium; const int mimeOffset = cacheFile->getUint32(off); const char *mime = cacheFile->getCharStar(mimeOffset); const int cmp = qstrcmp(mime, inputMime); if (cmp < 0) begin = medium + 1; else if (cmp > 0) end = medium - 1; else { const int iconOffset = cacheFile->getUint32(off + 4); return QLatin1String(cacheFile->getCharStar(iconOffset)); } } return QLatin1String(); } void QMimeBinaryProvider::loadIcon(QMimeTypePrivate &data) { const QByteArray inputMime = data.name.toLatin1(); const QLatin1String icon = iconForMime(m_cacheFile, PosIconsListOffset, inputMime); if (!icon.isEmpty()) { data.iconName = icon; } } void QMimeBinaryProvider::loadGenericIcon(QMimeTypePrivate &data) { const QByteArray inputMime = data.name.toLatin1(); const QLatin1String icon = iconForMime(m_cacheFile, PosGenericIconsListOffset, inputMime); if (!icon.isEmpty()) { data.genericIconName = icon; } } //// QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db, const QString &directory) : QMimeProviderBase(db, directory) { initResources(); ensureLoaded(); } QMimeXMLProvider::~QMimeXMLProvider() { } bool QMimeXMLProvider::isValid() { // If you change this method, adjust the logic in QMimeDatabasePrivate::loadProviders, // which assumes isValid==false is only possible in QMimeBinaryProvider. return true; } QMimeType QMimeXMLProvider::mimeTypeForName(const QString &name) { return m_nameMimeTypeMap.value(name); } void QMimeXMLProvider::addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) { m_mimeTypeGlobs.matchingGlobs(fileName, result); } void QMimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) { QString candidateName; bool foundOne = false; for (const QMimeMagicRuleMatcher &matcher : qAsConst(m_magicMatchers)) { if (matcher.matches(data)) { const int priority = matcher.priority(); if (priority > *accuracyPtr) { *accuracyPtr = priority; candidateName = matcher.mimetype(); foundOne = true; } } } if (foundOne) candidate = mimeTypeForName(candidateName); } void QMimeXMLProvider::ensureLoaded() { QStringList allFiles; const QString packageDir = m_directory + QStringLiteral("/packages"); QDir dir(packageDir); const QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); allFiles.reserve(files.count()); for (const QString &xmlFile : files) allFiles.append(packageDir + QLatin1Char('/') + xmlFile); if (m_allFiles == allFiles) return; m_allFiles = allFiles; m_nameMimeTypeMap.clear(); m_aliases.clear(); m_parents.clear(); m_mimeTypeGlobs.clear(); m_magicMatchers.clear(); //qDebug() << "Loading" << m_allFiles; for (const QString &file : qAsConst(allFiles)) load(file); } void QMimeXMLProvider::load(const QString &fileName) { QString errorMessage; if (!load(fileName, &errorMessage)) qWarning("QMimeDatabase: Error loading %ls\n%ls", qUtf16Printable(fileName), qUtf16Printable(errorMessage)); } bool QMimeXMLProvider::load(const QString &fileName, QString *errorMessage) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (errorMessage) *errorMessage = QLatin1String("Cannot open ") + fileName + QLatin1String(": ") + file.errorString(); return false; } if (errorMessage) errorMessage->clear(); QMimeTypeParser parser(*this); return parser.parse(&file, fileName, errorMessage); } void QMimeXMLProvider::addGlobPattern(const QMimeGlobPattern &glob) { m_mimeTypeGlobs.addGlob(glob); } void QMimeXMLProvider::addMimeType(const QMimeType &mt) { Q_ASSERT(!mt.d.data()->fromCache); m_nameMimeTypeMap.insert(mt.name(), mt); } void QMimeXMLProvider::addParents(const QString &mime, QStringList &result) { for (const QString &parent : m_parents.value(mime)) { if (!result.contains(parent)) result.append(parent); } } void QMimeXMLProvider::addParent(const QString &child, const QString &parent) { m_parents[child].append(parent); } void QMimeXMLProvider::addAliases(const QString &name, QStringList &result) { // Iterate through the whole hash. This method is rarely used. for (auto it = m_aliases.constBegin(), end = m_aliases.constEnd() ; it != end ; ++it) { if (it.value() == name) { if (!result.contains(it.key())) result.append(it.key()); } } } QString QMimeXMLProvider::resolveAlias(const QString &name) { return m_aliases.value(name); } void QMimeXMLProvider::addAlias(const QString &alias, const QString &name) { m_aliases.insert(alias, name); } void QMimeXMLProvider::addAllMimeTypes(QList &result) { if (result.isEmpty()) { // fast path result = m_nameMimeTypeMap.values(); } else { for (auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd() ; it != end ; ++it) { const QString newMime = it.key(); if (std::find_if(result.constBegin(), result.constEnd(), [newMime](const QMimeType &mime) -> bool { return mime.name() == newMime; }) == result.constEnd()) result.append(it.value()); } } } void QMimeXMLProvider::addMagicMatcher(const QMimeMagicRuleMatcher &matcher) { m_magicMatchers.append(matcher); } QT_END_NAMESPACE