diff options
Diffstat (limited to 'src/corelib/mimetypes/qmimeprovider.cpp')
-rw-r--r-- | src/corelib/mimetypes/qmimeprovider.cpp | 321 |
1 files changed, 168 insertions, 153 deletions
diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp index 177895ff6c..458cd46385 100644 --- a/src/corelib/mimetypes/qmimeprovider.cpp +++ b/src/corelib/mimetypes/qmimeprovider.cpp @@ -9,7 +9,6 @@ #include <qstandardpaths.h> #include "qmimemagicrulematcher_p.h" -#include <QMap> #include <QXmlStreamReader> #include <QBuffer> #include <QDir> @@ -51,24 +50,6 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -static inline void appendIfNew(QStringList &list, const QString &str) -{ - if (!list.contains(str)) - list.push_back(str); -} - -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); @@ -96,6 +77,43 @@ struct QMimeBinaryProvider::CacheFile bool m_valid; }; +static inline void appendIfNew(QStringList &list, const QString &str) +{ + if (!list.contains(str)) + list.push_back(str); +} + +QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory) + : m_db(db), m_directory(directory) +{ +} + +QMimeProviderBase *QMimeProviderBase::overrideProvider() const +{ + return m_overrideProvider; +} + +void QMimeProviderBase::setOverrideProvider(QMimeProviderBase *provider) +{ + m_overrideProvider = provider; +} + +bool QMimeProviderBase::isMimeTypeGlobsExcluded(const QString &name) const +{ + if (m_overrideProvider) { + if (m_overrideProvider->hasGlobDeleteAll(name)) + return true; + return m_overrideProvider->isMimeTypeGlobsExcluded(name); + } + return false; +} + +QMimeBinaryProvider::QMimeBinaryProvider(QMimeDatabasePrivate *db, const QString &directory) + : QMimeProviderBase(db, directory), m_mimetypeListLoaded(false) +{ + ensureLoaded(); +} + QMimeBinaryProvider::CacheFile::CacheFile(const QString &fileName) : file(fileName), m_valid(false) { @@ -186,25 +204,11 @@ void QMimeBinaryProvider::ensureLoaded() m_cacheFile.reset(); } -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) +bool QMimeBinaryProvider::knowsMimeType(const QString &name) { if (!m_mimetypeListLoaded) loadMimeTypeList(); - if (!m_mimetypeNames.contains(name)) - return QMimeType(); // unknown mimetype - return mimeTypeForNameUnchecked(name); + return m_mimetypeNames.contains(name); } void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) @@ -212,40 +216,34 @@ void QMimeBinaryProvider::addFileNameMatches(const QString &fileName, QMimeGlobM if (fileName.isEmpty()) return; Q_ASSERT(m_cacheFile); - const QString lowerFileName = fileName.toLower(); + int numMatches = 0; // Check literals (e.g. "Makefile") - matchGlobList(result, m_cacheFile.get(), m_cacheFile->getUint32(PosLiteralListOffset), - fileName); + numMatches = matchGlobList(result, m_cacheFile.get(), + m_cacheFile->getUint32(PosLiteralListOffset), fileName); // Check the very common *.txt cases with the suffix tree - if (result.m_matchingMimeTypes.isEmpty()) { + if (numMatches == 0) { + const QString lowerFileName = fileName.toLower(); 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.get(), numRoots, firstRootOffset, lowerFileName, - lowerFileName.size() - 1, false); - if (result.m_matchingMimeTypes.isEmpty()) - matchSuffixTree(result, m_cacheFile.get(), numRoots, firstRootOffset, fileName, - fileName.size() - 1, true); + if (matchSuffixTree(result, m_cacheFile.get(), numRoots, firstRootOffset, lowerFileName, + lowerFileName.size() - 1, false)) { + ++numMatches; + } else if (matchSuffixTree(result, m_cacheFile.get(), numRoots, firstRootOffset, fileName, + fileName.size() - 1, true)) { + ++numMatches; + } } // Check complex globs (e.g. "callgrind.out[0-9]*" or "README*") - if (result.m_matchingMimeTypes.isEmpty()) + if (numMatches == 0) matchGlobList(result, m_cacheFile.get(), m_cacheFile->getUint32(PosGlobListOffset), fileName); } -bool QMimeBinaryProvider::isMimeTypeGlobsExcluded(const char *mimeTypeName) -{ - return m_mimeTypesWithExcludedGlobs.contains(QLatin1StringView(mimeTypeName)); -} - -void QMimeBinaryProvider::excludeMimeTypeGlobs(const QStringList &toExclude) -{ - for (const auto &mt : toExclude) - appendIfNew(m_mimeTypesWithExcludedGlobs, mt); -} - -void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) +int QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, + const QString &fileName) { + int numMatches = 0; 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) { @@ -257,15 +255,18 @@ void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; const QString pattern = QLatin1StringView(cacheFile->getCharStar(globOffset)); - const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); + const QLatin1StringView mimeType(cacheFile->getCharStar(mimeTypeOffset)); //qDebug() << pattern << mimeType << weight << caseSensitive; if (isMimeTypeGlobsExcluded(mimeType)) continue; QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive); - if (glob.matchFileName(fileName)) - result.addMatch(QLatin1StringView(mimeType), weight, pattern); + if (glob.matchFileName(fileName)) { + result.addMatch(mimeType, weight, pattern); + ++numMatches; + } } + return numMatches; } bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result, @@ -298,15 +299,15 @@ bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result, if (mch != 0) break; const int mimeTypeOffset = cacheFile->getUint32(childOff + 4); - const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); + const QLatin1StringView mimeType(cacheFile->getCharStar(mimeTypeOffset)); if (isMimeTypeGlobsExcluded(mimeType)) continue; const int flagsAndWeight = cacheFile->getUint32(childOff + 8); const int weight = flagsAndWeight & 0xff; const bool caseSensitive = flagsAndWeight & 0x100; if (caseSensitiveCheck || !caseSensitive) { - result.addMatch(QLatin1StringView(mimeType), weight, - u'*' + QStringView{fileName}.mid(charPos + 1), + result.addMatch(mimeType, weight, + u'*' + QStringView{ fileName }.mid(charPos + 1), fileName.size() - charPos - 2); success = true; } @@ -346,7 +347,7 @@ bool QMimeBinaryProvider::matchMagicRule(QMimeBinaryProvider::CacheFile *cacheFi return false; } -void QMimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) +void QMimeBinaryProvider::findByMagic(const QByteArray &data, QMimeMagicResult &result) { const int magicListOffset = m_cacheFile->getUint32(PosMagicListOffset); const int numMatches = m_cacheFile->getUint32(magicListOffset); @@ -360,11 +361,13 @@ void QMimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr, if (matchMagicRule(m_cacheFile.get(), 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(QLatin1StringView(mimeType)); - return; + const int accuracy = static_cast<int>(m_cacheFile->getUint32(off)); + if (accuracy > result.accuracy) { + result.accuracy = accuracy; + result.candidate = QString::fromLatin1(mimeType); + // Return the first match, mime.cache is sorted + return; + } } } } @@ -453,13 +456,14 @@ void QMimeBinaryProvider::loadMimeTypeList() 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")); + QFile file(m_directory + QStringView(u"/types")); if (file.open(QIODevice::ReadOnly)) { while (!file.atEnd()) { - QByteArray line = file.readLine(); - if (line.endsWith('\n')) - line.chop(1); - m_mimetypeNames.insert(QString::fromLatin1(line)); + const QByteArray line = file.readLine(); + auto lineView = QByteArrayView(line); + if (lineView.endsWith('\n')) + lineView.chop(1); + m_mimetypeNames.insert(QString::fromLatin1(lineView)); } } } @@ -471,49 +475,70 @@ void QMimeBinaryProvider::addAllMimeTypes(QList<QMimeType> &result) if (result.isEmpty()) { result.reserve(m_mimetypeNames.size()); for (const QString &name : std::as_const(m_mimetypeNames)) - result.append(mimeTypeForNameUnchecked(name)); + result.append(QMimeType(QMimeTypePrivate(name))); } else { for (const QString &name : std::as_const(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)); + result.append(QMimeType(QMimeTypePrivate(name))); } } -bool QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate &data) +QMimeTypePrivate::LocaleHash QMimeBinaryProvider::localeComments(const QString &name) { -#if QT_CONFIG(xmlstreamreader) - if (data.loaded) - return true; + MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name); + if (it != m_mimetypeExtra.cend()) + return it->second.localeComments; + return {}; +} + +bool QMimeBinaryProvider::hasGlobDeleteAll(const QString &name) +{ + MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name); + if (it != m_mimetypeExtra.cend()) + return it->second.hasGlobDeleteAll; + return {}; +} + +QStringList QMimeBinaryProvider::globPatterns(const QString &name) +{ + MimeTypeExtraMap::const_iterator it = loadMimeTypeExtra(name); + if (it != m_mimetypeExtra.cend()) + return it->second.globPatterns; + return {}; +} - auto it = m_mimetypeExtra.constFind(data.name); - if (it == m_mimetypeExtra.constEnd()) { +QMimeBinaryProvider::MimeTypeExtraMap::const_iterator +QMimeBinaryProvider::loadMimeTypeExtra(const QString &mimeName) +{ +#if QT_CONFIG(xmlstreamreader) + auto it = m_mimetypeExtra.find(mimeName); + if (it == m_mimetypeExtra.cend()) { // load comment and globPatterns // shared-mime-info since 1.3 lowercases the xml files - QString mimeFile = m_directory + u'/' + data.name.toLower() + ".xml"_L1; + QString mimeFile = m_directory + u'/' + mimeName.toLower() + ".xml"_L1; if (!QFile::exists(mimeFile)) - mimeFile = m_directory + u'/' + data.name + ".xml"_L1; // pre-1.3 + mimeFile = m_directory + u'/' + mimeName + ".xml"_L1; // pre-1.3 QFile qfile(mimeFile); if (!qfile.open(QFile::ReadOnly)) - return false; + return m_mimetypeExtra.cend(); - auto insertIt = m_mimetypeExtra.insert(data.name, MimeTypeExtra{}); - it = insertIt; - MimeTypeExtra &extra = insertIt.value(); + it = m_mimetypeExtra.try_emplace(mimeName).first; + MimeTypeExtra &extra = it->second; QString mainPattern; QXmlStreamReader xml(&qfile); if (xml.readNextStartElement()) { if (xml.name() != "mime-type"_L1) { - return false; + return m_mimetypeExtra.cend(); } const auto name = xml.attributes().value("type"_L1); if (name.isEmpty()) - return false; - if (name.compare(data.name, Qt::CaseInsensitive)) - qWarning() << "Got name" << name << "in file" << mimeFile << "expected" << data.name; + return m_mimetypeExtra.cend(); + if (name.compare(mimeName, Qt::CaseInsensitive)) + qWarning() << "Got name" << name << "in file" << mimeFile << "expected" << mimeName; while (xml.readNextStartElement()) { const auto tag = xml.name(); @@ -526,8 +551,7 @@ bool QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate &data) extra.localeComments.insert(lang, text); continue; // we called readElementText, so we're at the EndElement already. } else if (tag == "glob-deleteall"_L1) { // as written out by shared-mime-info >= 0.70 - extra.globPatterns.clear(); - mainPattern.clear(); + extra.hasGlobDeleteAll = true; } else if (tag == "glob"_L1) { // as written out by shared-mime-info >= 0.70 const QString pattern = xml.attributes().value("pattern"_L1).toString(); if (mainPattern.isEmpty() && pattern.startsWith(u'*')) { @@ -549,14 +573,11 @@ bool QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate &data) extra.globPatterns.prepend(mainPattern); } } - const MimeTypeExtra &e = it.value(); - data.localeComments = e.localeComments; - data.globPatterns = e.globPatterns; - return true; + return it; #else - Q_UNUSED(data); + Q_UNUSED(mimeName); qWarning("Cannot load mime type since QXmlStreamReader is not available."); - return false; + return m_mimetypeExtra.cend(); #endif // feature xmlstreamreader } @@ -586,22 +607,16 @@ QLatin1StringView QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int pos return QLatin1StringView(); } -void QMimeBinaryProvider::loadIcon(QMimeTypePrivate &data) +QString QMimeBinaryProvider::icon(const QString &name) { - const QByteArray inputMime = data.name.toLatin1(); - const QLatin1StringView icon = iconForMime(m_cacheFile.get(), PosIconsListOffset, inputMime); - if (!icon.isEmpty()) { - data.iconName = icon; - } + const QByteArray inputMime = name.toLatin1(); + return iconForMime(m_cacheFile.get(), PosIconsListOffset, inputMime); } -void QMimeBinaryProvider::loadGenericIcon(QMimeTypePrivate &data) +QString QMimeBinaryProvider::genericIcon(const QString &name) { - const QByteArray inputMime = data.name.toLatin1(); - const QLatin1StringView icon = iconForMime(m_cacheFile.get(), PosGenericIconsListOffset, inputMime); - if (!icon.isEmpty()) { - data.genericIconName = icon; - } + const QByteArray inputMime = name.toLatin1(); + return iconForMime(m_cacheFile.get(), PosGenericIconsListOffset, inputMime); } //// @@ -686,38 +701,34 @@ bool QMimeXMLProvider::isInternalDatabase() const #endif } -QMimeType QMimeXMLProvider::mimeTypeForName(const QString &name) +bool QMimeXMLProvider::knowsMimeType(const QString &name) { - return m_nameMimeTypeMap.value(name); + return m_nameMimeTypeMap.contains(name); } void QMimeXMLProvider::addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) { - m_mimeTypeGlobs.matchingGlobs(fileName, result); + auto filterFunc = [this](const QString &name) { return !isMimeTypeGlobsExcluded(name); }; + m_mimeTypeGlobs.matchingGlobs(fileName, result, filterFunc); } -void QMimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) +void QMimeXMLProvider::findByMagic(const QByteArray &data, QMimeMagicResult &result) { - QString candidateName; - bool foundOne = false; for (const QMimeMagicRuleMatcher &matcher : std::as_const(m_magicMatchers)) { if (matcher.matches(data)) { const int priority = matcher.priority(); - if (priority > *accuracyPtr) { - *accuracyPtr = priority; - candidateName = matcher.mimetype(); - foundOne = true; + if (priority > result.accuracy) { + result.accuracy = priority; + result.candidate = matcher.mimetype(); } } } - if (foundOne) - candidate = mimeTypeForName(candidateName); } void QMimeXMLProvider::ensureLoaded() { QStringList allFiles; - const QString packageDir = m_directory + QStringLiteral("/packages"); + const QString packageDir = m_directory + QStringView(u"/packages"); QDir dir(packageDir); const QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); allFiles.reserve(files.size()); @@ -733,7 +744,6 @@ void QMimeXMLProvider::ensureLoaded() m_parents.clear(); m_mimeTypeGlobs.clear(); m_magicMatchers.clear(); - m_mimeTypesWithDeletedGlobs.clear(); //qDebug() << "Loading" << m_allFiles; @@ -741,6 +751,31 @@ void QMimeXMLProvider::ensureLoaded() load(file); } +QMimeTypePrivate::LocaleHash QMimeXMLProvider::localeComments(const QString &name) +{ + return m_nameMimeTypeMap.value(name).localeComments; +} + +bool QMimeXMLProvider::hasGlobDeleteAll(const QString &name) +{ + return m_nameMimeTypeMap.value(name).hasGlobDeleteAll; +} + +QStringList QMimeXMLProvider::globPatterns(const QString &name) +{ + return m_nameMimeTypeMap.value(name).globPatterns; +} + +QString QMimeXMLProvider::icon(const QString &name) +{ + return m_nameMimeTypeMap.value(name).iconName; +} + +QString QMimeXMLProvider::genericIcon(const QString &name) +{ + return m_nameMimeTypeMap.value(name).genericIconName; +} + void QMimeXMLProvider::load(const QString &fileName) { QString errorMessage; @@ -782,32 +817,9 @@ void QMimeXMLProvider::addGlobPattern(const QMimeGlobPattern &glob) m_mimeTypeGlobs.addGlob(glob); } -void QMimeXMLProvider::addMimeType(const QMimeType &mt) -{ - Q_ASSERT(!mt.d.data()->fromCache); - - QString name = mt.name(); - if (mt.d->hasGlobDeleteAll) - appendIfNew(m_mimeTypesWithDeletedGlobs, name); - m_nameMimeTypeMap.insert(mt.name(), mt); -} - -/* - \a toExclude is a list of mime type names that should have the the glob patterns - associated with them cleared (because there are mime types with the same names - in a higher precedence Provider that have glob-deleteall tags). - - This method is called from QMimeDatabasePrivate::loadProviders() to exclude mime - type glob patterns in lower precedence Providers. -*/ -void QMimeXMLProvider::excludeMimeTypeGlobs(const QStringList &toExclude) +void QMimeXMLProvider::addMimeType(const QMimeTypeXMLData &mt) { - for (const auto &mt : toExclude) { - auto it = m_nameMimeTypeMap.find(mt); - if (it != m_nameMimeTypeMap.end()) - it->d->globPatterns.clear(); - m_mimeTypeGlobs.removeMimeType(mt); - } + m_nameMimeTypeMap.insert(mt.name, mt); } void QMimeXMLProvider::addParents(const QString &mime, QStringList &result) @@ -845,13 +857,16 @@ void QMimeXMLProvider::addAlias(const QString &alias, const QString &name) void QMimeXMLProvider::addAllMimeTypes(QList<QMimeType> &result) { if (result.isEmpty()) { // fast path - result = m_nameMimeTypeMap.values(); + for (auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd(); + it != end; ++it) { + result.append(QMimeType(QMimeTypePrivate(it.value().name))); + } } 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()); + result.append(QMimeType(QMimeTypePrivate(it.value().name))); } } } |