Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qmimeglobpattern.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#if QT_CONFIG(regularexpression)
7#include <QRegularExpression>
8#endif
9#include <QStringList>
10#include <QDebug>
11
13
14using namespace Qt::StringLiterals;
15
26 qsizetype knownSuffixLength)
27{
28 if (m_allMatchingMimeTypes.contains(mimeType))
29 return;
30 // Is this a lower-weight pattern than the last match? Skip this match then.
31 if (weight < m_weight) {
33 return;
34 }
35 bool replace = weight > m_weight;
36 if (!replace) {
37 // Compare the length of the match
39 return; // too short, ignore
40 else if (pattern.size() > m_matchingPatternLength) {
41 // longer: clear any previous match (like *.bz2, when pattern is *.tar.bz2)
42 replace = true;
43 }
44 }
45 if (replace) {
46 m_matchingMimeTypes.clear();
47 // remember the new "longer" length
50 }
51 if (!m_matchingMimeTypes.contains(mimeType)) {
53 if (replace)
54 m_allMatchingMimeTypes.prepend(mimeType); // highest-weight first
55 else
57 m_knownSuffixLength = knownSuffixLength;
58 }
59}
60
61QMimeGlobPattern::PatternType QMimeGlobPattern::detectPatternType(QStringView pattern) const
62{
63 const qsizetype patternLength = pattern.size();
64 if (!patternLength)
65 return OtherPattern;
66
67 const qsizetype starCount = pattern.count(u'*');
68 const bool hasSquareBracket = pattern.indexOf(u'[') != -1;
69 const bool hasQuestionMark = pattern.indexOf(u'?') != -1;
70
71 if (!hasSquareBracket && !hasQuestionMark) {
72 if (starCount == 1) {
73 // Patterns like "*~", "*.extension"
74 if (pattern.at(0) == u'*')
75 return SuffixPattern;
76 // Patterns like "README*" (well this is currently the only one like that...)
77 if (pattern.at(patternLength - 1) == u'*')
78 return PrefixPattern;
79 } else if (starCount == 0) {
80 // Names without any wildcards like "README"
81 return LiteralPattern;
82 }
83 }
84
85 if (pattern == "[0-9][0-9][0-9].vdr"_L1)
86 return VdrPattern;
87
88 if (pattern == "*.anim[1-9j]"_L1)
89 return AnimPattern;
90
91 return OtherPattern;
92}
93
94
104bool QMimeGlobPattern::matchFileName(const QString &inputFileName) const
105{
106 // "Applications MUST match globs case-insensitively, except when the case-sensitive
107 // attribute is set to true."
108 // The constructor takes care of putting case-insensitive patterns in lowercase.
109 const QString fileName = m_caseSensitivity == Qt::CaseInsensitive
110 ? inputFileName.toLower() : inputFileName;
111
112 const qsizetype patternLength = m_pattern.size();
113 if (!patternLength)
114 return false;
115 const qsizetype fileNameLength = fileName.size();
116
117 switch (m_patternType) {
118 case SuffixPattern: {
119 if (fileNameLength + 1 < patternLength)
120 return false;
121
122 const QChar *c1 = m_pattern.unicode() + patternLength - 1;
123 const QChar *c2 = fileName.unicode() + fileNameLength - 1;
124 int cnt = 1;
125 while (cnt < patternLength && *c1-- == *c2--)
126 ++cnt;
127 return cnt == patternLength;
128 }
129 case PrefixPattern: {
130 if (fileNameLength + 1 < patternLength)
131 return false;
132
133 const QChar *c1 = m_pattern.unicode();
134 const QChar *c2 = fileName.unicode();
135 int cnt = 1;
136 while (cnt < patternLength && *c1++ == *c2++)
137 ++cnt;
138 return cnt == patternLength;
139 }
140 case LiteralPattern:
141 return (m_pattern == fileName);
142 case VdrPattern: // "[0-9][0-9][0-9].vdr" case
143 return fileNameLength == 7
144 && fileName.at(0).isDigit() && fileName.at(1).isDigit() && fileName.at(2).isDigit()
145 && QStringView{fileName}.mid(3, 4) == ".vdr"_L1;
146 case AnimPattern: { // "*.anim[1-9j]" case
147 if (fileNameLength < 6)
148 return false;
149 const QChar lastChar = fileName.at(fileNameLength - 1);
150 const bool lastCharOK = (lastChar.isDigit() && lastChar != u'0')
151 || lastChar == u'j';
152 return lastCharOK && QStringView{fileName}.mid(fileNameLength - 6, 5) == ".anim"_L1;
153 }
154 case OtherPattern:
155 // Other fallback patterns: slow but correct method
156#if QT_CONFIG(regularexpression)
157 auto rx = QRegularExpression::fromWildcard(m_pattern);
158 return rx.match(fileName).hasMatch();
159#else
160 return false;
161#endif
162 }
163 return false;
164}
165
167{
168 // starts with "*.", has no other '*'
169 return pattern.lastIndexOf(u'*') == 0
170 && pattern.size() > 1
171 && pattern.at(1) == u'.' // (other dots are OK, like *.tar.bz2)
172 // and contains no other special character
173 && !pattern.contains(u'?')
174 && !pattern.contains(u'[')
175 ;
176}
177
179{
180 // starts with "*.", has no other '*' and no other '.'
181 return pattern.lastIndexOf(u'*') == 0
182 && pattern.lastIndexOf(u'.') == 1
183 // and contains no other special character
184 && !pattern.contains(u'?')
185 && !pattern.contains(u'[')
186 ;
187}
188
190{
191 const QString &pattern = glob.pattern();
192 Q_ASSERT(!pattern.isEmpty());
193
194 // Store each patterns into either m_fastPatternDict (*.txt, *.html etc. with default weight 50)
195 // or for the rest, like core.*, *.tar.bz2, *~, into highWeightPatternOffset (>50)
196 // or lowWeightPatternOffset (<=50)
197
198 if (glob.weight() == 50 && isFastPattern(pattern) && !glob.isCaseSensitive()) {
199 // The bulk of the patterns is *.foo with weight 50 --> those go into the fast patterns hash.
200 const QString extension = pattern.mid(2).toLower();
201 QStringList &patterns = m_fastPatterns[extension]; // find or create
202 if (!patterns.contains(glob.mimeType()))
203 patterns.append(glob.mimeType());
204 } else {
205 if (glob.weight() > 50) {
206 if (!m_highWeightGlobs.hasPattern(glob.mimeType(), glob.pattern()))
208 } else {
209 if (!m_lowWeightGlobs.hasPattern(glob.mimeType(), glob.pattern()))
211 }
212 }
213}
214
222
224 const AddMatchFilterFunc &filterFunc) const
225{
226 for (const QMimeGlobPattern &glob : *this) {
227 if (glob.matchFileName(fileName) && filterFunc(glob.mimeType())) {
228 const QString pattern = glob.pattern();
229 const qsizetype suffixLen = isSimplePattern(pattern) ? pattern.size() - strlen("*.") : 0;
230 result.addMatch(glob.mimeType(), glob.weight(), pattern, suffixLen);
231 }
232 }
233}
234
236 const AddMatchFilterFunc &filterFunc) const
237{
238 // First try the high weight matches (>50), if any.
240
241 // Now use the "fast patterns" dict, for simple *.foo patterns with weight 50
242 // (which is most of them, so this optimization is definitely worth it)
243 const qsizetype lastDot = fileName.lastIndexOf(u'.');
244 if (lastDot != -1) { // if no '.', skip the extension lookup
245 const qsizetype ext_len = fileName.size() - lastDot - 1;
246 const QString simpleExtension = fileName.right(ext_len).toLower();
247 // (toLower because fast patterns are always case-insensitive and saved as lowercase)
248
249 const QStringList matchingMimeTypes = m_fastPatterns.value(simpleExtension);
250 const QString simplePattern = "*."_L1 + simpleExtension;
251 for (const QString &mime : matchingMimeTypes) {
252 if (filterFunc(mime)) {
253 result.addMatch(mime, 50, simplePattern, simpleExtension.size());
254 }
255 }
256 // Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway,
257 // at least those with weight 50.
258 }
259
260 // Finally, try the low weight matches (<=50)
262}
263
270
\inmodule QtCore
T value(const Key &key) const noexcept
Definition qhash.h:1054
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
void addGlob(const QMimeGlobPattern &glob)
void matchingGlobs(const QString &fileName, QMimeGlobMatchResult &result, const AddMatchFilterFunc &filterFunc) const
void removeMimeType(const QString &mimeType)
QMimeGlobPatternList m_highWeightGlobs
QMimeGlobPatternList m_lowWeightGlobs
bool hasPattern(QStringView mimeType, QStringView pattern) const
void removeMimeType(QStringView mimeType)
"noglobs" is very rare occurrence, so it's ok if it's slow
void match(QMimeGlobMatchResult &result, const QString &fileName, const AddMatchFilterFunc &filterFunc) const
The QMimeGlobPattern class contains the glob pattern for file names for MIME type matching.
bool matchFileName(const QString &inputFileName) const
static QRegularExpression fromWildcard(QStringView pattern, Qt::CaseSensitivity cs=Qt::CaseInsensitive, WildcardConversionOptions options=DefaultWildcardConversion)
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QString right(qsizetype n) const &
Definition qstring.h:375
QString toLower() const &
Definition qstring.h:435
const QChar * unicode() const
Returns a Unicode representation of the string.
Definition qstring.h:1230
void extension()
[6]
Definition dialogs.cpp:230
Combined button and popup list for selecting options.
@ CaseInsensitive
const char * mimeType
static bool isSimplePattern(QStringView pattern)
static bool isFastPattern(QStringView pattern)
std::function< bool(const QString &)> AddMatchFilterFunc
GLint GLint GLint GLint GLint x
[0]
GLuint GLuint GLfloat weight
GLuint64EXT * result
[6]
GLubyte * pattern
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
ptrdiff_t qsizetype
Definition qtypes.h:165
application x qt windows mime
[2]
MyCustomStruct c2
p rx()++
The QMimeGlobMatchResult class accumulates results from glob matching.
void addMatch(const QString &mimeType, int weight, const QString &pattern, qsizetype knownSuffixLength=0)
QStringList m_allMatchingMimeTypes