summaryrefslogtreecommitdiffstats
path: root/src/corelib/mimetypes/qmimeglobpattern.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/mimetypes/qmimeglobpattern.cpp')
-rw-r--r--src/corelib/mimetypes/qmimeglobpattern.cpp240
1 files changed, 240 insertions, 0 deletions
diff --git a/src/corelib/mimetypes/qmimeglobpattern.cpp b/src/corelib/mimetypes/qmimeglobpattern.cpp
new file mode 100644
index 0000000000..de26dbaf15
--- /dev/null
+++ b/src/corelib/mimetypes/qmimeglobpattern.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmimeglobpattern_p.h"
+
+#include <QRegExp>
+#include <QStringList>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \class QMimeGlobMatchResult
+ \brief The QMimeGlobMatchResult class accumulates results from glob matching.
+
+ Handles glob weights, and preferring longer matches over shorter matches.
+*/
+
+void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const QString &pattern)
+{
+ // Is this a lower-weight pattern than the last match? Skip this match then.
+ if (weight < m_weight)
+ return;
+ bool replace = weight > m_weight;
+ if (!replace) {
+ // Compare the length of the match
+ if (pattern.length() < m_matchingPatternLength)
+ return; // too short, ignore
+ else if (pattern.length() > m_matchingPatternLength) {
+ // longer: clear any previous match (like *.bz2, when pattern is *.tar.bz2)
+ replace = true;
+ }
+ }
+ if (replace) {
+ m_matchingMimeTypes.clear();
+ // remember the new "longer" length
+ m_matchingPatternLength = pattern.length();
+ m_weight = weight;
+ }
+ m_matchingMimeTypes.append(mimeType);
+ if (pattern.startsWith(QLatin1String("*.")))
+ m_foundSuffix = pattern.mid(2);
+}
+
+/*!
+ \internal
+ \class QMimeGlobPattern
+ \brief The QMimeGlobPattern class contains the glob pattern for file names for MIME type matching.
+
+ \sa QMimeType, QMimeDatabase, QMimeMagicRuleMatcher, QMimeMagicRule
+*/
+
+bool QMimeGlobPattern::matchFileName(const QString &inputFilename) const
+{
+ // "Applications MUST match globs case-insensitively, except when the case-sensitive
+ // attribute is set to true."
+ // The constructor takes care of putting case-insensitive patterns in lowercase.
+ const QString filename = m_caseSensitivity == Qt::CaseInsensitive ? inputFilename.toLower() : inputFilename;
+
+ const int pattern_len = m_pattern.length();
+ if (!pattern_len)
+ return false;
+ const int len = filename.length();
+
+ const int starCount = m_pattern.count(QLatin1Char('*'));
+
+ // Patterns like "*~", "*.extension"
+ if (m_pattern[0] == QLatin1Char('*') && m_pattern.indexOf(QLatin1Char('[')) == -1 && starCount == 1)
+ {
+ if (len + 1 < pattern_len) return false;
+
+ const QChar *c1 = m_pattern.unicode() + pattern_len - 1;
+ const QChar *c2 = filename.unicode() + len - 1;
+ int cnt = 1;
+ while (cnt < pattern_len && *c1-- == *c2--)
+ ++cnt;
+ return cnt == pattern_len;
+ }
+
+ // Patterns like "README*" (well this is currently the only one like that...)
+ if (starCount == 1 && m_pattern.at(pattern_len - 1) == QLatin1Char('*')) {
+ if (len + 1 < pattern_len) return false;
+ if (m_pattern.at(0) == QLatin1Char('*'))
+ return filename.indexOf(m_pattern.mid(1, pattern_len - 2)) != -1;
+
+ const QChar *c1 = m_pattern.unicode();
+ const QChar *c2 = filename.unicode();
+ int cnt = 1;
+ while (cnt < pattern_len && *c1++ == *c2++)
+ ++cnt;
+ return cnt == pattern_len;
+ }
+
+ // Names without any wildcards like "README"
+ if (m_pattern.indexOf(QLatin1Char('[')) == -1 && starCount == 0 && m_pattern.indexOf(QLatin1Char('?')))
+ return (m_pattern == filename);
+
+ // Other (quite rare) patterns, like "*.anim[1-9j]": use slow but correct method
+ const QRegExp rx(m_pattern, Qt::CaseSensitive, QRegExp::WildcardUnix);
+ return rx.exactMatch(filename);
+}
+
+static bool isFastPattern(const QString &pattern)
+{
+ // starts with "*.", has no other '*' and no other '.'
+ return pattern.lastIndexOf(QLatin1Char('*')) == 0
+ && pattern.lastIndexOf(QLatin1Char('.')) == 1
+ // and contains no other special character
+ && !pattern.contains(QLatin1Char('?'))
+ && !pattern.contains(QLatin1Char('['))
+ ;
+}
+
+void QMimeAllGlobPatterns::addGlob(const QMimeGlobPattern &glob)
+{
+ const QString &pattern = glob.pattern();
+ Q_ASSERT(!pattern.isEmpty());
+
+ // Store each patterns into either m_fastPatternDict (*.txt, *.html etc. with default weight 50)
+ // or for the rest, like core.*, *.tar.bz2, *~, into highWeightPatternOffset (>50)
+ // or lowWeightPatternOffset (<=50)
+
+ if (glob.weight() == 50 && isFastPattern(pattern) && !glob.isCaseSensitive()) {
+ // The bulk of the patterns is *.foo with weight 50 --> those go into the fast patterns hash.
+ const QString extension = pattern.mid(2).toLower();
+ QStringList &patterns = m_fastPatterns[extension]; // find or create
+ // This would just slow things down: if (!patterns.contains(glob.mimeType()))
+ patterns.append(glob.mimeType());
+ } else {
+ if (glob.weight() > 50) {
+ // This would just slow things down: if (!m_highWeightGlobs.hasPattern(glob.mimeType(), glob.pattern()))
+ m_highWeightGlobs.append(glob);
+ } else {
+ //This would just slow things down: if (!m_lowWeightGlobs.hasPattern(glob.mimeType(), glob.pattern()))
+ m_lowWeightGlobs.append(glob);
+ }
+ }
+}
+
+void QMimeAllGlobPatterns::removeMimeType(const QString &mimeType)
+{
+ QMutableHashIterator<QString, QStringList> it(m_fastPatterns);
+ while (it.hasNext()) {
+ it.next().value().removeAll(mimeType);
+ }
+ m_highWeightGlobs.removeMimeType(mimeType);
+ m_lowWeightGlobs.removeMimeType(mimeType);
+}
+
+void QMimeGlobPatternList::match(QMimeGlobMatchResult &result,
+ const QString &fileName) const
+{
+
+ QMimeGlobPatternList::const_iterator it = this->constBegin();
+ const QMimeGlobPatternList::const_iterator endIt = this->constEnd();
+ for (; it != endIt; ++it) {
+ const QMimeGlobPattern &glob = *it;
+ if (glob.matchFileName(fileName))
+ result.addMatch(glob.mimeType(), glob.weight(), glob.pattern());
+ }
+}
+
+QStringList QMimeAllGlobPatterns::matchingGlobs(const QString &fileName, QString *foundSuffix) const
+{
+ // First try the high weight matches (>50), if any.
+ QMimeGlobMatchResult result;
+ m_highWeightGlobs.match(result, fileName);
+ if (result.m_matchingMimeTypes.isEmpty()) {
+
+ // Now use the "fast patterns" dict, for simple *.foo patterns with weight 50
+ // (which is most of them, so this optimization is definitely worth it)
+ const int lastDot = fileName.lastIndexOf(QLatin1Char('.'));
+ if (lastDot != -1) { // if no '.', skip the extension lookup
+ const int ext_len = fileName.length() - lastDot - 1;
+ const QString simpleExtension = fileName.right(ext_len).toLower();
+ // (toLower because fast patterns are always case-insensitive and saved as lowercase)
+
+ const QStringList matchingMimeTypes = m_fastPatterns.value(simpleExtension);
+ foreach (const QString &mime, matchingMimeTypes) {
+ result.addMatch(mime, 50, QLatin1String("*.") + simpleExtension);
+ }
+ // Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway,
+ // at least those with weight 50.
+ }
+
+ // Finally, try the low weight matches (<=50)
+ m_lowWeightGlobs.match(result, fileName);
+ }
+ if (foundSuffix)
+ *foundSuffix = result.m_foundSuffix;
+ return result.m_matchingMimeTypes;
+}
+
+void QMimeAllGlobPatterns::clear()
+{
+ m_fastPatterns.clear();
+ m_highWeightGlobs.clear();
+ m_lowWeightGlobs.clear();
+}
+
+QT_END_NAMESPACE