diff options
author | Eike Ziller <eike.ziller@theqtcompany.com> | 2015-01-27 14:36:05 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@theqtcompany.com> | 2015-02-17 12:40:49 +0000 |
commit | 306b28dd8d05a3173dd984ec93b11604cbd47c09 (patch) | |
tree | c354c47e27897a8a6aec60e3ce1d167290be3337 /src/libs/utils/mimetypes/mimeprovider.cpp | |
parent | 15d748b4eb0a7d926665fada088d4ad3b5f6ec39 (diff) |
MimeDatabase: Restrict to using XML mime provider
Change-Id: I335a675d24cd319a5d1d47bb8e8e1ab154d10cef
Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
Diffstat (limited to 'src/libs/utils/mimetypes/mimeprovider.cpp')
-rw-r--r-- | src/libs/utils/mimetypes/mimeprovider.cpp | 1222 |
1 files changed, 611 insertions, 611 deletions
diff --git a/src/libs/utils/mimetypes/mimeprovider.cpp b/src/libs/utils/mimetypes/mimeprovider.cpp index 172ae351da..983e5d8cb5 100644 --- a/src/libs/utils/mimetypes/mimeprovider.cpp +++ b/src/libs/utils/mimetypes/mimeprovider.cpp @@ -80,617 +80,617 @@ bool MimeProviderBase::shouldCheck() return true; } -MimeBinaryProvider::MimeBinaryProvider(MimeDatabasePrivate *db) - : MimeProviderBase(db), m_mimetypeListLoaded(false) -{ -} - -#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) -#define QT_USE_MMAP -#endif - -struct MimeBinaryProvider::CacheFile -{ - CacheFile(const QString &fileName); - ~CacheFile(); - - bool isValid() const { return m_valid; } - inline quint16 getUint16(int offset) const - { - return qFromBigEndian(*reinterpret_cast<quint16 *>(data + offset)); - } - inline quint32 getUint32(int offset) const - { - return qFromBigEndian(*reinterpret_cast<quint32 *>(data + offset)); - } - inline const char *getCharStar(int offset) const - { - return reinterpret_cast<const char *>(data + offset); - } - bool load(); - bool reload(); - - QFile file; - uchar *data; - QDateTime m_mtime; - bool m_valid; -}; - -MimeBinaryProvider::CacheFile::CacheFile(const QString &fileName) - : file(fileName), m_valid(false) -{ - load(); -} - -MimeBinaryProvider::CacheFile::~CacheFile() -{ -} - -bool MimeBinaryProvider::CacheFile::load() -{ - if (!file.open(QIODevice::ReadOnly)) - return false; - data = file.map(0, file.size()); - if (data) { - const int major = getUint16(0); - const int minor = getUint16(2); - m_valid = (major == 1 && minor >= 1 && minor <= 2); - } - m_mtime = QFileInfo(file).lastModified(); - return m_valid; -} - -bool MimeBinaryProvider::CacheFile::reload() -{ - //qDebug() << "reload!" << file->fileName(); - m_valid = false; - if (file.isOpen()) { - file.close(); - } - data = 0; - return load(); -} - -MimeBinaryProvider::CacheFile *MimeBinaryProvider::CacheFileList::findCacheFile(const QString &fileName) const -{ - for (const_iterator it = begin(); it != end(); ++it) { - if ((*it)->file.fileName() == fileName) - return *it; - } - return 0; -} - -MimeBinaryProvider::~MimeBinaryProvider() -{ - qDeleteAll(m_cacheFiles); -} - -// Position of the "list offsets" values, at the beginning of the mime.cache file -enum { - PosAliasListOffset = 4, - PosParentListOffset = 8, - PosLiteralListOffset = 12, - PosReverseSuffixTreeOffset = 16, - PosGlobListOffset = 20, - PosMagicListOffset = 24, - // PosNamespaceListOffset = 28, - PosIconsListOffset = 32, - PosGenericIconsListOffset = 36 -}; - -bool MimeBinaryProvider::isValid() -{ -#if defined(QT_USE_MMAP) - if (!qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE")) - return false; - - Q_ASSERT(m_cacheFiles.isEmpty()); // this method is only ever called once - checkCache(); - - if (m_cacheFiles.count() > 1) - return true; - if (m_cacheFiles.isEmpty()) - return false; - - // We found exactly one file; is it the user-modified mimes, or a system file? - const QString foundFile = m_cacheFiles.first()->file.fileName(); - const QString localCacheFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/mime/mime.cache"); - - return foundFile != localCacheFile; -#else - return false; -#endif -} - -bool MimeBinaryProvider::CacheFileList::checkCacheChanged() -{ - bool somethingChanged = false; - QMutableListIterator<CacheFile *> it(*this); - while (it.hasNext()) { - CacheFile *cacheFile = it.next(); - QFileInfo fileInfo(cacheFile->file); - if (!fileInfo.exists()) { // This can't happen by just running update-mime-database. But the user could use rm -rf :-) - delete cacheFile; - it.remove(); - somethingChanged = true; - } else if (fileInfo.lastModified() > cacheFile->m_mtime) { - if (!cacheFile->reload()) { - delete cacheFile; - it.remove(); - } - somethingChanged = true; - } - } - return somethingChanged; -} - -void MimeBinaryProvider::checkCache() -{ - if (!shouldCheck()) - return; - - // First iterate over existing known cache files and check for uptodate - if (m_cacheFiles.checkCacheChanged()) - m_mimetypeListLoaded = false; - - // Then check if new cache files appeared - const QStringList cacheFileNames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/mime.cache")); - if (cacheFileNames != m_cacheFileNames) { - 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 MimeType mimeTypeForNameUnchecked(const QString &name) -{ - MimeTypePrivate data; - data.name = name; - // The rest is retrieved on demand. - // comment and globPatterns: in loadMimeTypePrivate - // iconName: in loadIcon - // genericIconName: in loadGenericIcon - return MimeType(data); -} - -MimeType MimeBinaryProvider::mimeTypeForName(const QString &name) -{ - checkCache(); - if (!m_mimetypeListLoaded) - loadMimeTypeList(); - if (!m_mimetypeNames.contains(name)) - return MimeType(); // unknown mimetype - return mimeTypeForNameUnchecked(name); -} - -QStringList MimeBinaryProvider::findByFileName(const QString &fileName, QString *foundSuffix) -{ - checkCache(); - if (fileName.isEmpty()) - return QStringList(); - const QString lowerFileName = fileName.toLower(); - MimeGlobMatchResult result; - // TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly. - 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 MimeBinaryProvider::matchGlobList(MimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) -{ - const int numGlobs = cacheFile->getUint32(off); - //qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset; - for (int i = 0; i < numGlobs; ++i) { - const int globOffset = cacheFile->getUint32(off + 4 + 12 * i); - const int mimeTypeOffset = cacheFile->getUint32(off + 4 + 12 * i + 4); - const int flagsAndWeight = cacheFile->getUint32(off + 4 + 12 * i + 8); - const int weight = flagsAndWeight & 0xff; - const bool caseSensitive = flagsAndWeight & 0x100; - const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; - const QString pattern = QLatin1String(cacheFile->getCharStar(globOffset)); - - const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); - //qDebug() << pattern << mimeType << weight << caseSensitive; - MimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive); - - // TODO: this could be done faster for literals where a simple == would do. - if (glob.matchFileName(fileName)) - result.addMatch(QLatin1String(mimeType), weight, pattern); - } -} - -bool MimeBinaryProvider::matchSuffixTree(MimeGlobMatchResult &result, MimeBinaryProvider::CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck) -{ - QChar fileChar = fileName[charPos]; - int min = 0; - int max = numEntries - 1; - while (min <= max) { - const int mid = (min + max) / 2; - const int off = firstOffset + 12 * mid; - const QChar ch = cacheFile->getUint32(off); - if (ch < fileChar) - min = mid + 1; - else if (ch > fileChar) - max = mid - 1; - else { - --charPos; - int numChildren = cacheFile->getUint32(off + 4); - int childrenOffset = cacheFile->getUint32(off + 8); - bool success = false; - if (charPos > 0) - success = matchSuffixTree(result, cacheFile, numChildren, childrenOffset, fileName, charPos, caseSensitiveCheck); - if (!success) { - for (int i = 0; i < numChildren; ++i) { - const int childOff = childrenOffset + 12 * i; - const int mch = cacheFile->getUint32(childOff); - if (mch != 0) - break; - const int mimeTypeOffset = cacheFile->getUint32(childOff + 4); - const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); - const int flagsAndWeight = cacheFile->getUint32(childOff + 8); - const int weight = flagsAndWeight & 0xff; - const bool caseSensitive = flagsAndWeight & 0x100; - if (caseSensitiveCheck || !caseSensitive) { - result.addMatch(QLatin1String(mimeType), weight, QLatin1Char('*') + fileName.mid(charPos+1)); - success = true; - } - } - } - return success; - } - } - return false; -} - -bool MimeBinaryProvider::matchMagicRule(MimeBinaryProvider::CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data) -{ - const char *dataPtr = data.constData(); - const int dataSize = data.size(); - for (int matchlet = 0; matchlet < numMatchlets; ++matchlet) { - const int off = firstOffset + matchlet * 32; - const int rangeStart = cacheFile->getUint32(off); - const int rangeLength = cacheFile->getUint32(off + 4); - //const int wordSize = cacheFile->getUint32(off + 8); - const int valueLength = cacheFile->getUint32(off + 12); - const int valueOffset = cacheFile->getUint32(off + 16); - const int maskOffset = cacheFile->getUint32(off + 20); - const char *mask = maskOffset ? cacheFile->getCharStar(maskOffset) : NULL; - - if (!MimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile->getCharStar(valueOffset), mask)) - continue; - - const int numChildren = cacheFile->getUint32(off + 24); - const int firstChildOffset = cacheFile->getUint32(off + 28); - if (numChildren == 0) // No submatch? Then we are done. - return true; - // Check that one of the submatches matches too - if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data)) - return true; - } - return false; -} - -MimeType MimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr) -{ - checkCache(); - 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 MimeType(); -} - -QStringList MimeBinaryProvider::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 MimeBinaryProvider::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; -} - -QStringList MimeBinaryProvider::listAliases(const QString &name) -{ - checkCache(); - QStringList result; - const QByteArray input = name.toLatin1(); - foreach (CacheFile *cacheFile, m_cacheFiles) { - const int aliasListOffset = cacheFile->getUint32(PosAliasListOffset); - const int numEntries = cacheFile->getUint32(aliasListOffset); - for (int pos = 0; pos < numEntries; ++pos) { - const int off = aliasListOffset + 4 + 8 * pos; - const int mimeOffset = cacheFile->getUint32(off + 4); - const char *mimeType = cacheFile->getCharStar(mimeOffset); - - if (input == mimeType) { - const int aliasOffset = cacheFile->getUint32(off); - const char *alias = cacheFile->getCharStar(aliasOffset); - result.append(QString::fromLatin1(alias)); - } - } - } - return result; -} - -void MimeBinaryProvider::loadMimeTypeList() -{ - if (!m_mimetypeListLoaded) { - m_mimetypeListLoaded = true; - m_mimetypeNames.clear(); - // Unfortunately mime.cache doesn't have a full list of all mimetypes. - // So we have to parse the plain-text files called "types". - const QStringList typesFilenames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/types")); - 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<MimeType> MimeBinaryProvider::allMimeTypes() -{ - QList<MimeType> result; - loadMimeTypeList(); - - for (QSet<QString>::const_iterator it = m_mimetypeNames.constBegin(); - it != m_mimetypeNames.constEnd(); ++it) - result.append(mimeTypeForNameUnchecked(*it)); - - return result; -} - -void MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data) -{ -#ifdef QT_NO_XMLSTREAMREADER - qWarning() << "Cannot load mime type since QXmlStreamReader is not available."; - return; -#else - if (data.loaded) - return; - data.loaded = true; - // load comment and globPatterns - - const QString file = data.name + QLatin1String(".xml"); - const QStringList mimeFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QString::fromLatin1("mime/") + file); - if (mimeFiles.isEmpty()) { - // TODO: ask Thiago about this - qWarning() << "No file found for" << file << ", even though the file appeared in a directory listing."; - qWarning() << "Either it was just removed, or the directory doesn't have executable permission..."; - qWarning() << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory); - return; - } - - QString comment; - QString mainPattern; - const QString preferredLanguage = QLocale::system().name(); - - QListIterator<QString> mimeFilesIter(mimeFiles); - mimeFilesIter.toBack(); - while (mimeFilesIter.hasPrevious()) { // global first, then local. - const QString fullPath = mimeFilesIter.previous(); - QFile qfile(fullPath); - if (!qfile.open(QFile::ReadOnly)) - continue; - - QXmlStreamReader xml(&qfile); - if (xml.readNextStartElement()) { - if (xml.name() != QLatin1String("mime-type")) { - continue; - } - const QString name = xml.attributes().value(QLatin1String("type")).toString(); - if (name.isEmpty()) - continue; - if (name != data.name) { - qWarning() << "Got name" << name << "in file" << file << "expected" << data.name; - } - - while (xml.readNextStartElement()) { - const QStringRef tag = xml.name(); - if (tag == QLatin1String("comment")) { - QString lang = xml.attributes().value(QLatin1String("xml:lang")).toString(); - const QString text = xml.readElementText(); - if (lang.isEmpty()) { - lang = QLatin1String("en_US"); - } - data.localeComments.insert(lang, text); - continue; // we called readElementText, so we're at the EndElement already. - } else if (tag == QLatin1String("icon")) { // as written out by shared-mime-info >= 0.40 - data.iconName = xml.attributes().value(QLatin1String("name")).toString(); - } else if (tag == QLatin1String("glob-deleteall")) { // as written out by shared-mime-info >= 0.70 - data.globPatterns.clear(); - } else if (tag == QLatin1String("glob")) { // as written out by shared-mime-info >= 0.70 - const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString(); - if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) { - mainPattern = pattern; - } - if (!data.globPatterns.contains(pattern)) - data.globPatterns.append(pattern); - } - xml.skipCurrentElement(); - } - Q_ASSERT(xml.name() == QLatin1String("mime-type")); - } - } - - // Let's assume that shared-mime-info is at least version 0.70 - // Otherwise we would need 1) a version check, and 2) code for parsing patterns from the globs file. -#if 1 - if (!mainPattern.isEmpty() && data.globPatterns.first() != mainPattern) { - // ensure it's first in the list of patterns - data.globPatterns.removeAll(mainPattern); - data.globPatterns.prepend(mainPattern); - } -#else - const bool globsInXml = sharedMimeInfoVersion() >= QT_VERSION_CHECK(0, 70, 0); - if (globsInXml) { - if (!mainPattern.isEmpty() && data.globPatterns.first() != mainPattern) { - // ensure it's first in the list of patterns - data.globPatterns.removeAll(mainPattern); - data.globPatterns.prepend(mainPattern); - } - } else { - // Fallback: get the patterns from the globs file - // TODO: This would be the only way to support shared-mime-info < 0.70 - // But is this really worth the effort? - } -#endif -#endif //QT_NO_XMLSTREAMREADER -} - -// Binary search in the icons or generic-icons list -QString MimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime) -{ - const int iconsListOffset = cacheFile->getUint32(posListOffset); - const int numIcons = cacheFile->getUint32(iconsListOffset); - int begin = 0; - int end = numIcons - 1; - while (begin <= end) { - const int medium = (begin + end) / 2; - const int off = iconsListOffset + 4 + 8 * medium; - const int mimeOffset = cacheFile->getUint32(off); - const char *mime = cacheFile->getCharStar(mimeOffset); - const int cmp = qstrcmp(mime, inputMime); - if (cmp < 0) - begin = medium + 1; - else if (cmp > 0) - end = medium - 1; - else { - const int iconOffset = cacheFile->getUint32(off + 4); - return QLatin1String(cacheFile->getCharStar(iconOffset)); - } - } - return QString(); -} - -void MimeBinaryProvider::loadIcon(MimeTypePrivate &data) -{ - checkCache(); - const QByteArray inputMime = data.name.toLatin1(); - foreach (CacheFile *cacheFile, m_cacheFiles) { - const QString icon = iconForMime(cacheFile, PosIconsListOffset, inputMime); - if (!icon.isEmpty()) { - data.iconName = icon; - return; - } - } -} - -void MimeBinaryProvider::loadGenericIcon(MimeTypePrivate &data) -{ - checkCache(); - const QByteArray inputMime = data.name.toLatin1(); - foreach (CacheFile *cacheFile, m_cacheFiles) { - const QString icon = iconForMime(cacheFile, PosGenericIconsListOffset, inputMime); - if (!icon.isEmpty()) { - data.genericIconName = icon; - return; - } - } -} +//MimeBinaryProvider::MimeBinaryProvider(MimeDatabasePrivate *db) +// : MimeProviderBase(db), m_mimetypeListLoaded(false) +//{ +//} + +//#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) +//#define QT_USE_MMAP +//#endif + +//struct MimeBinaryProvider::CacheFile +//{ +// CacheFile(const QString &fileName); +// ~CacheFile(); + +// bool isValid() const { return m_valid; } +// inline quint16 getUint16(int offset) const +// { +// return qFromBigEndian(*reinterpret_cast<quint16 *>(data + offset)); +// } +// inline quint32 getUint32(int offset) const +// { +// return qFromBigEndian(*reinterpret_cast<quint32 *>(data + offset)); +// } +// inline const char *getCharStar(int offset) const +// { +// return reinterpret_cast<const char *>(data + offset); +// } +// bool load(); +// bool reload(); + +// QFile file; +// uchar *data; +// QDateTime m_mtime; +// bool m_valid; +//}; + +//MimeBinaryProvider::CacheFile::CacheFile(const QString &fileName) +// : file(fileName), m_valid(false) +//{ +// load(); +//} + +//MimeBinaryProvider::CacheFile::~CacheFile() +//{ +//} + +//bool MimeBinaryProvider::CacheFile::load() +//{ +// if (!file.open(QIODevice::ReadOnly)) +// return false; +// data = file.map(0, file.size()); +// if (data) { +// const int major = getUint16(0); +// const int minor = getUint16(2); +// m_valid = (major == 1 && minor >= 1 && minor <= 2); +// } +// m_mtime = QFileInfo(file).lastModified(); +// return m_valid; +//} + +//bool MimeBinaryProvider::CacheFile::reload() +//{ +// //qDebug() << "reload!" << file->fileName(); +// m_valid = false; +// if (file.isOpen()) { +// file.close(); +// } +// data = 0; +// return load(); +//} + +//MimeBinaryProvider::CacheFile *MimeBinaryProvider::CacheFileList::findCacheFile(const QString &fileName) const +//{ +// for (const_iterator it = begin(); it != end(); ++it) { +// if ((*it)->file.fileName() == fileName) +// return *it; +// } +// return 0; +//} + +//MimeBinaryProvider::~MimeBinaryProvider() +//{ +// qDeleteAll(m_cacheFiles); +//} + +//// Position of the "list offsets" values, at the beginning of the mime.cache file +//enum { +// PosAliasListOffset = 4, +// PosParentListOffset = 8, +// PosLiteralListOffset = 12, +// PosReverseSuffixTreeOffset = 16, +// PosGlobListOffset = 20, +// PosMagicListOffset = 24, +// // PosNamespaceListOffset = 28, +// PosIconsListOffset = 32, +// PosGenericIconsListOffset = 36 +//}; + +//bool MimeBinaryProvider::isValid() +//{ +//#if defined(QT_USE_MMAP) +// if (!qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE")) +// return false; + +// Q_ASSERT(m_cacheFiles.isEmpty()); // this method is only ever called once +// checkCache(); + +// if (m_cacheFiles.count() > 1) +// return true; +// if (m_cacheFiles.isEmpty()) +// return false; + +// // We found exactly one file; is it the user-modified mimes, or a system file? +// const QString foundFile = m_cacheFiles.first()->file.fileName(); +// const QString localCacheFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/mime/mime.cache"); + +// return foundFile != localCacheFile; +//#else +// return false; +//#endif +//} + +//bool MimeBinaryProvider::CacheFileList::checkCacheChanged() +//{ +// bool somethingChanged = false; +// QMutableListIterator<CacheFile *> it(*this); +// while (it.hasNext()) { +// CacheFile *cacheFile = it.next(); +// QFileInfo fileInfo(cacheFile->file); +// if (!fileInfo.exists()) { // This can't happen by just running update-mime-database. But the user could use rm -rf :-) +// delete cacheFile; +// it.remove(); +// somethingChanged = true; +// } else if (fileInfo.lastModified() > cacheFile->m_mtime) { +// if (!cacheFile->reload()) { +// delete cacheFile; +// it.remove(); +// } +// somethingChanged = true; +// } +// } +// return somethingChanged; +//} + +//void MimeBinaryProvider::checkCache() +//{ +// if (!shouldCheck()) +// return; + +// // First iterate over existing known cache files and check for uptodate +// if (m_cacheFiles.checkCacheChanged()) +// m_mimetypeListLoaded = false; + +// // Then check if new cache files appeared +// const QStringList cacheFileNames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/mime.cache")); +// if (cacheFileNames != m_cacheFileNames) { +// 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 MimeType mimeTypeForNameUnchecked(const QString &name) +//{ +// MimeTypePrivate data; +// data.name = name; +// // The rest is retrieved on demand. +// // comment and globPatterns: in loadMimeTypePrivate +// // iconName: in loadIcon +// // genericIconName: in loadGenericIcon +// return MimeType(data); +//} + +//MimeType MimeBinaryProvider::mimeTypeForName(const QString &name) +//{ +// checkCache(); +// if (!m_mimetypeListLoaded) +// loadMimeTypeList(); +// if (!m_mimetypeNames.contains(name)) +// return MimeType(); // unknown mimetype +// return mimeTypeForNameUnchecked(name); +//} + +//QStringList MimeBinaryProvider::findByFileName(const QString &fileName, QString *foundSuffix) +//{ +// checkCache(); +// if (fileName.isEmpty()) +// return QStringList(); +// const QString lowerFileName = fileName.toLower(); +// MimeGlobMatchResult result; +// // TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly. +// 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 MimeBinaryProvider::matchGlobList(MimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName) +//{ +// const int numGlobs = cacheFile->getUint32(off); +// //qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset; +// for (int i = 0; i < numGlobs; ++i) { +// const int globOffset = cacheFile->getUint32(off + 4 + 12 * i); +// const int mimeTypeOffset = cacheFile->getUint32(off + 4 + 12 * i + 4); +// const int flagsAndWeight = cacheFile->getUint32(off + 4 + 12 * i + 8); +// const int weight = flagsAndWeight & 0xff; +// const bool caseSensitive = flagsAndWeight & 0x100; +// const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; +// const QString pattern = QLatin1String(cacheFile->getCharStar(globOffset)); + +// const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); +// //qDebug() << pattern << mimeType << weight << caseSensitive; +// MimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive); + +// // TODO: this could be done faster for literals where a simple == would do. +// if (glob.matchFileName(fileName)) +// result.addMatch(QLatin1String(mimeType), weight, pattern); +// } +//} + +//bool MimeBinaryProvider::matchSuffixTree(MimeGlobMatchResult &result, MimeBinaryProvider::CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck) +//{ +// QChar fileChar = fileName[charPos]; +// int min = 0; +// int max = numEntries - 1; +// while (min <= max) { +// const int mid = (min + max) / 2; +// const int off = firstOffset + 12 * mid; +// const QChar ch = cacheFile->getUint32(off); +// if (ch < fileChar) +// min = mid + 1; +// else if (ch > fileChar) +// max = mid - 1; +// else { +// --charPos; +// int numChildren = cacheFile->getUint32(off + 4); +// int childrenOffset = cacheFile->getUint32(off + 8); +// bool success = false; +// if (charPos > 0) +// success = matchSuffixTree(result, cacheFile, numChildren, childrenOffset, fileName, charPos, caseSensitiveCheck); +// if (!success) { +// for (int i = 0; i < numChildren; ++i) { +// const int childOff = childrenOffset + 12 * i; +// const int mch = cacheFile->getUint32(childOff); +// if (mch != 0) +// break; +// const int mimeTypeOffset = cacheFile->getUint32(childOff + 4); +// const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); +// const int flagsAndWeight = cacheFile->getUint32(childOff + 8); +// const int weight = flagsAndWeight & 0xff; +// const bool caseSensitive = flagsAndWeight & 0x100; +// if (caseSensitiveCheck || !caseSensitive) { +// result.addMatch(QLatin1String(mimeType), weight, QLatin1Char('*') + fileName.mid(charPos+1)); +// success = true; +// } +// } +// } +// return success; +// } +// } +// return false; +//} + +//bool MimeBinaryProvider::matchMagicRule(MimeBinaryProvider::CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data) +//{ +// const char *dataPtr = data.constData(); +// const int dataSize = data.size(); +// for (int matchlet = 0; matchlet < numMatchlets; ++matchlet) { +// const int off = firstOffset + matchlet * 32; +// const int rangeStart = cacheFile->getUint32(off); +// const int rangeLength = cacheFile->getUint32(off + 4); +// //const int wordSize = cacheFile->getUint32(off + 8); +// const int valueLength = cacheFile->getUint32(off + 12); +// const int valueOffset = cacheFile->getUint32(off + 16); +// const int maskOffset = cacheFile->getUint32(off + 20); +// const char *mask = maskOffset ? cacheFile->getCharStar(maskOffset) : NULL; + +// if (!MimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile->getCharStar(valueOffset), mask)) +// continue; + +// const int numChildren = cacheFile->getUint32(off + 24); +// const int firstChildOffset = cacheFile->getUint32(off + 28); +// if (numChildren == 0) // No submatch? Then we are done. +// return true; +// // Check that one of the submatches matches too +// if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data)) +// return true; +// } +// return false; +//} + +//MimeType MimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr) +//{ +// checkCache(); +// 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 MimeType(); +//} + +//QStringList MimeBinaryProvider::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 MimeBinaryProvider::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; +//} + +//QStringList MimeBinaryProvider::listAliases(const QString &name) +//{ +// checkCache(); +// QStringList result; +// const QByteArray input = name.toLatin1(); +// foreach (CacheFile *cacheFile, m_cacheFiles) { +// const int aliasListOffset = cacheFile->getUint32(PosAliasListOffset); +// const int numEntries = cacheFile->getUint32(aliasListOffset); +// for (int pos = 0; pos < numEntries; ++pos) { +// const int off = aliasListOffset + 4 + 8 * pos; +// const int mimeOffset = cacheFile->getUint32(off + 4); +// const char *mimeType = cacheFile->getCharStar(mimeOffset); + +// if (input == mimeType) { +// const int aliasOffset = cacheFile->getUint32(off); +// const char *alias = cacheFile->getCharStar(aliasOffset); +// result.append(QString::fromLatin1(alias)); +// } +// } +// } +// return result; +//} + +//void MimeBinaryProvider::loadMimeTypeList() +//{ +// if (!m_mimetypeListLoaded) { +// m_mimetypeListLoaded = true; +// m_mimetypeNames.clear(); +// // Unfortunately mime.cache doesn't have a full list of all mimetypes. +// // So we have to parse the plain-text files called "types". +// const QStringList typesFilenames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/types")); +// 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<MimeType> MimeBinaryProvider::allMimeTypes() +//{ +// QList<MimeType> result; +// loadMimeTypeList(); + +// for (QSet<QString>::const_iterator it = m_mimetypeNames.constBegin(); +// it != m_mimetypeNames.constEnd(); ++it) +// result.append(mimeTypeForNameUnchecked(*it)); + +// return result; +//} + +//void MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data) +//{ +//#ifdef QT_NO_XMLSTREAMREADER +// qWarning() << "Cannot load mime type since QXmlStreamReader is not available."; +// return; +//#else +// if (data.loaded) +// return; +// data.loaded = true; +// // load comment and globPatterns + +// const QString file = data.name + QLatin1String(".xml"); +// const QStringList mimeFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QString::fromLatin1("mime/") + file); +// if (mimeFiles.isEmpty()) { +// // TODO: ask Thiago about this +// qWarning() << "No file found for" << file << ", even though the file appeared in a directory listing."; +// qWarning() << "Either it was just removed, or the directory doesn't have executable permission..."; +// qWarning() << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory); +// return; +// } + +// QString comment; +// QString mainPattern; +// const QString preferredLanguage = QLocale::system().name(); + +// QListIterator<QString> mimeFilesIter(mimeFiles); +// mimeFilesIter.toBack(); +// while (mimeFilesIter.hasPrevious()) { // global first, then local. +// const QString fullPath = mimeFilesIter.previous(); +// QFile qfile(fullPath); +// if (!qfile.open(QFile::ReadOnly)) +// continue; + +// QXmlStreamReader xml(&qfile); +// if (xml.readNextStartElement()) { +// if (xml.name() != QLatin1String("mime-type")) { +// continue; +// } +// const QString name = xml.attributes().value(QLatin1String("type")).toString(); +// if (name.isEmpty()) +// continue; +// if (name != data.name) { +// qWarning() << "Got name" << name << "in file" << file << "expected" << data.name; +// } + +// while (xml.readNextStartElement()) { +// const QStringRef tag = xml.name(); +// if (tag == QLatin1String("comment")) { +// QString lang = xml.attributes().value(QLatin1String("xml:lang")).toString(); +// const QString text = xml.readElementText(); +// if (lang.isEmpty()) { +// lang = QLatin1String("en_US"); +// } +// data.localeComments.insert(lang, text); +// continue; // we called readElementText, so we're at the EndElement already. +// } else if (tag == QLatin1String("icon")) { // as written out by shared-mime-info >= 0.40 +// data.iconName = xml.attributes().value(QLatin1String("name")).toString(); +// } else if (tag == QLatin1String("glob-deleteall")) { // as written out by shared-mime-info >= 0.70 +// data.globPatterns.clear(); +// } else if (tag == QLatin1String("glob")) { // as written out by shared-mime-info >= 0.70 +// const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString(); +// if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) { +// mainPattern = pattern; +// } +// if (!data.globPatterns.contains(pattern)) +// data.globPatterns.append(pattern); +// } +// xml.skipCurrentElement(); +// } +// Q_ASSERT(xml.name() == QLatin1String("mime-type")); +// } +// } + +// // Let's assume that shared-mime-info is at least version 0.70 +// // Otherwise we would need 1) a version check, and 2) code for parsing patterns from the globs file. +//#if 1 +// if (!mainPattern.isEmpty() && data.globPatterns.first() != mainPattern) { +// // ensure it's first in the list of patterns +// data.globPatterns.removeAll(mainPattern); +// data.globPatterns.prepend(mainPattern); +// } +//#else +// const bool globsInXml = sharedMimeInfoVersion() >= QT_VERSION_CHECK(0, 70, 0); +// if (globsInXml) { +// if (!mainPattern.isEmpty() && data.globPatterns.first() != mainPattern) { +// // ensure it's first in the list of patterns +// data.globPatterns.removeAll(mainPattern); +// data.globPatterns.prepend(mainPattern); +// } +// } else { +// // Fallback: get the patterns from the globs file +// // TODO: This would be the only way to support shared-mime-info < 0.70 +// // But is this really worth the effort? +// } +//#endif +//#endif //QT_NO_XMLSTREAMREADER +//} + +//// Binary search in the icons or generic-icons list +//QString MimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime) +//{ +// const int iconsListOffset = cacheFile->getUint32(posListOffset); +// const int numIcons = cacheFile->getUint32(iconsListOffset); +// int begin = 0; +// int end = numIcons - 1; +// while (begin <= end) { +// const int medium = (begin + end) / 2; +// const int off = iconsListOffset + 4 + 8 * medium; +// const int mimeOffset = cacheFile->getUint32(off); +// const char *mime = cacheFile->getCharStar(mimeOffset); +// const int cmp = qstrcmp(mime, inputMime); +// if (cmp < 0) +// begin = medium + 1; +// else if (cmp > 0) +// end = medium - 1; +// else { +// const int iconOffset = cacheFile->getUint32(off + 4); +// return QLatin1String(cacheFile->getCharStar(iconOffset)); +// } +// } +// return QString(); +//} + +//void MimeBinaryProvider::loadIcon(MimeTypePrivate &data) +//{ +// checkCache(); +// const QByteArray inputMime = data.name.toLatin1(); +// foreach (CacheFile *cacheFile, m_cacheFiles) { +// const QString icon = iconForMime(cacheFile, PosIconsListOffset, inputMime); +// if (!icon.isEmpty()) { +// data.iconName = icon; +// return; +// } +// } +//} + +//void MimeBinaryProvider::loadGenericIcon(MimeTypePrivate &data) +//{ +// checkCache(); +// const QByteArray inputMime = data.name.toLatin1(); +// foreach (CacheFile *cacheFile, m_cacheFiles) { +// const QString icon = iconForMime(cacheFile, PosGenericIconsListOffset, inputMime); +// if (!icon.isEmpty()) { +// data.genericIconName = icon; +// return; +// } +// } +//} //// |