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
qmimetypeparser.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
4#define QT_NO_CAST_FROM_ASCII
5
6#include "qmimetypeparser_p.h"
7
8#include "qmimetype_p.h"
10
11#include <QtCore/QCoreApplication>
12#include <QtCore/QDebug>
13#include <QtCore/QDir>
14#include <QtCore/QXmlStreamReader>
15#include <QtCore/QXmlStreamWriter>
16#include <QtCore/QStack>
17
19
20using namespace Qt::StringLiterals;
21
22// XML tags in MIME files
23static const char mimeInfoTagC[] = "mime-info";
24static const char mimeTypeTagC[] = "mime-type";
25static const char mimeTypeAttributeC[] = "type";
26static const char subClassTagC[] = "sub-class-of";
27static const char commentTagC[] = "comment";
28static const char genericIconTagC[] = "generic-icon";
29static const char iconTagC[] = "icon";
30static const char nameAttributeC[] = "name";
31static const char globTagC[] = "glob";
32static const char globDeleteAllTagC[] = "glob-deleteall";
33static const char aliasTagC[] = "alias";
34static const char patternAttributeC[] = "pattern";
35static const char weightAttributeC[] = "weight";
36static const char caseSensitiveAttributeC[] = "case-sensitive";
37static const char localeAttributeC[] = "xml:lang";
38
39static const char magicTagC[] = "magic";
40static const char priorityAttributeC[] = "priority";
41
42static const char matchTagC[] = "match";
43static const char matchValueAttributeC[] = "value";
44static const char matchTypeAttributeC[] = "type";
45static const char matchOffsetAttributeC[] = "offset";
46static const char matchMaskAttributeC[] = "mask";
47
77QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState currentState, QStringView startElement)
78{
79 switch (currentState) {
80 case ParseBeginning:
81 if (startElement == QLatin1StringView(mimeInfoTagC))
82 return ParseMimeInfo;
83 if (startElement == QLatin1StringView(mimeTypeTagC))
84 return ParseMimeType;
85 return ParseError;
86 case ParseMimeInfo:
87 return startElement == QLatin1StringView(mimeTypeTagC) ? ParseMimeType : ParseError;
88 case ParseMimeType:
89 case ParseComment:
90 case ParseGenericIcon:
91 case ParseIcon:
92 case ParseGlobPattern:
93 case ParseGlobDeleteAll:
94 case ParseSubClass:
95 case ParseAlias:
96 case ParseOtherMimeTypeSubTag:
97 case ParseMagicMatchRule:
98 if (startElement == QLatin1StringView(mimeTypeTagC)) // Sequence of <mime-type>
99 return ParseMimeType;
100 if (startElement == QLatin1StringView(commentTagC))
101 return ParseComment;
102 if (startElement == QLatin1StringView(genericIconTagC))
103 return ParseGenericIcon;
104 if (startElement == QLatin1StringView(iconTagC))
105 return ParseIcon;
106 if (startElement == QLatin1StringView(globTagC))
107 return ParseGlobPattern;
108 if (startElement == QLatin1StringView(globDeleteAllTagC))
109 return ParseGlobDeleteAll;
110 if (startElement == QLatin1StringView(subClassTagC))
111 return ParseSubClass;
112 if (startElement == QLatin1StringView(aliasTagC))
113 return ParseAlias;
114 if (startElement == QLatin1StringView(magicTagC))
115 return ParseMagic;
116 if (startElement == QLatin1StringView(matchTagC))
117 return ParseMagicMatchRule;
118 return ParseOtherMimeTypeSubTag;
119 case ParseMagic:
120 if (startElement == QLatin1StringView(matchTagC))
121 return ParseMagicMatchRule;
122 break;
123 case ParseError:
124 break;
125 }
126 return ParseError;
127}
128
129// Parse int number from an (attribute) string
131{
132 bool ok;
133 *target = n.toInt(&ok);
134 if (Q_UNLIKELY(!ok)) {
135 if (errorMessage)
136 *errorMessage = "Not a number '"_L1 + n + "'."_L1;
137 return false;
138 }
139 return true;
140}
141
142#if QT_CONFIG(xmlstreamreader)
143struct CreateMagicMatchRuleResult
144{
145 QString errorMessage; // must be first
147
148 CreateMagicMatchRuleResult(QStringView type, QStringView value, QStringView offsets, QStringView mask)
149 : errorMessage(), rule(type.toString(), value.toUtf8(), offsets.toString(), mask.toLatin1(), &errorMessage)
150 {
151
152 }
153};
154
155static CreateMagicMatchRuleResult createMagicMatchRule(const QXmlStreamAttributes &atts)
156{
157 const auto type = atts.value(QLatin1StringView(matchTypeAttributeC));
158 const auto value = atts.value(QLatin1StringView(matchValueAttributeC));
159 const auto offsets = atts.value(QLatin1StringView(matchOffsetAttributeC));
160 const auto mask = atts.value(QLatin1StringView(matchMaskAttributeC));
161 return CreateMagicMatchRuleResult(type, value, offsets, mask);
162}
163#endif // feature xmlstreamreader
164
166{
167#if QT_CONFIG(xmlstreamreader)
169 int priority = 50;
170 QStack<QMimeMagicRule *> currentRules; // stack for the nesting of rules
171 QList<QMimeMagicRule> rules; // toplevel rules
172 QXmlStreamReader reader(dev);
173 ParseState ps = ParseBeginning;
174 while (!reader.atEnd()) {
175 switch (reader.readNext()) {
176 case QXmlStreamReader::StartElement: {
177 ps = nextState(ps, reader.name());
178 const QXmlStreamAttributes atts = reader.attributes();
179 switch (ps) {
180 case ParseMimeType: { // start parsing a MIME type name
181 const QString name = atts.value(QLatin1StringView(mimeTypeAttributeC)).toString();
182 if (name.isEmpty()) {
183 reader.raiseError(QStringLiteral("Missing 'type'-attribute"));
184 } else {
185 data.name = name;
186 }
187 }
188 break;
189 case ParseGenericIcon:
190 data.genericIconName = atts.value(QLatin1StringView(nameAttributeC)).toString();
191 break;
192 case ParseIcon:
193 data.iconName = atts.value(QLatin1StringView(nameAttributeC)).toString();
194 break;
195 case ParseGlobPattern: {
196 const QString pattern = atts.value(QLatin1StringView(patternAttributeC)).toString();
197 unsigned weight = atts.value(QLatin1StringView(weightAttributeC)).toInt();
198 const bool caseSensitive = atts.value(QLatin1StringView(caseSensitiveAttributeC)) == "true"_L1;
199
200 if (weight == 0)
202
203 Q_ASSERT(!data.name.isEmpty());
204 const QMimeGlobPattern glob(pattern, data.name, weight, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
205 if (!process(glob, errorMessage)) // for actual glob matching
206 return false;
207 data.addGlobPattern(pattern); // just for QMimeType::globPatterns()
208 }
209 break;
210 case ParseGlobDeleteAll:
211 data.globPatterns.clear();
212 data.hasGlobDeleteAll = true;
213 break;
214 case ParseSubClass: {
215 const QString inheritsFrom = atts.value(QLatin1StringView(mimeTypeAttributeC)).toString();
216 if (!inheritsFrom.isEmpty())
217 processParent(data.name, inheritsFrom);
218 }
219 break;
220 case ParseComment: {
221 // comments have locale attributes.
222 QString locale = atts.value(QLatin1StringView(localeAttributeC)).toString();
223 const QString comment = reader.readElementText();
224 if (locale.isEmpty())
225 locale = QString::fromLatin1("default");
226 data.localeComments.insert(locale, comment);
227 }
228 break;
229 case ParseAlias: {
230 const QString alias = atts.value(QLatin1StringView(mimeTypeAttributeC)).toString();
231 if (!alias.isEmpty())
232 processAlias(alias, data.name);
233 }
234 break;
235 case ParseMagic: {
236 priority = 50;
237 const auto priorityS = atts.value(QLatin1StringView(priorityAttributeC));
238 if (!priorityS.isEmpty()) {
239 if (!parseNumber(priorityS, &priority, errorMessage))
240 return false;
241
242 }
243 currentRules.clear();
244 //qDebug() << "MAGIC start for mimetype" << data.name;
245 }
246 break;
247 case ParseMagicMatchRule: {
248 auto result = createMagicMatchRule(atts);
249 if (Q_UNLIKELY(!result.rule.isValid()))
250 qWarning("QMimeDatabase: Error parsing %ls\n%ls",
252 QList<QMimeMagicRule> *ruleList;
253 if (currentRules.isEmpty())
254 ruleList = &rules;
255 else // nest this rule into the proper parent
256 ruleList = &currentRules.top()->m_subMatches;
257 ruleList->append(std::move(result.rule));
258 //qDebug() << " MATCH added. Stack size was" << currentRules.size();
259 currentRules.push(&ruleList->last());
260 break;
261 }
262 case ParseError:
263 reader.raiseError("Unexpected element <"_L1 + reader.name() + u'>');
264 break;
265 default:
266 break;
267 }
268 }
269 break;
270 // continue switch QXmlStreamReader::Token...
271 case QXmlStreamReader::EndElement: // Finished element
272 {
273 const auto elementName = reader.name();
274 if (elementName == QLatin1StringView(mimeTypeTagC)) {
276 return false;
277 data.clear();
278 } else if (elementName == QLatin1StringView(matchTagC)) {
279 // Closing a <match> tag, pop stack
280 currentRules.pop();
281 //qDebug() << " MATCH closed. Stack size is now" << currentRules.size();
282 } else if (elementName == QLatin1StringView(magicTagC)) {
283 //qDebug() << "MAGIC ended, we got" << rules.count() << "rules, with prio" << priority;
284 // Finished a <magic> sequence
285 QMimeMagicRuleMatcher ruleMatcher(data.name, priority);
286 ruleMatcher.addRules(rules);
287 processMagicMatcher(ruleMatcher);
288 rules.clear();
289 }
290 break;
291 }
292 default:
293 break;
294 }
295 }
296
297 if (Q_UNLIKELY(reader.hasError())) {
298 if (errorMessage) {
299 *errorMessage = QString::asprintf("An error has been encountered at line %lld of %ls: %ls:",
300 reader.lineNumber(),
302 qUtf16Printable(reader.errorString()));
303 }
304 return false;
305 }
306
307 return true;
308#else
309 Q_UNUSED(dev);
310 if (errorMessage)
311 *errorMessage = "QXmlStreamReader is not available, cannot parse '%1'."_L1.arg(fileName);
312 return false;
313#endif // feature xmlstreamreader
314}
315
317{
318 hasGlobDeleteAll = false;
319 name.clear();
322 iconName.clear();
323 globPatterns.clear();
324}
325
330
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
\inmodule QtCore \reentrant
Definition qiodevice.h:34
QString errorString() const
Returns a human readable description of the last error that occurred.
The QMimeGlobPattern class contains the glob pattern for file names for MIME type matching.
static const unsigned DefaultWeight
The QMimeMagicRuleMatcher class checks a number of rules based on operator "or".
virtual void processMagicMatcher(const QMimeMagicRuleMatcher &matcher)=0
static bool parseNumber(QStringView n, int *target, QString *errorMessage)
bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage)
virtual bool process(const QMimeTypeXMLData &t, QString *errorMessage)=0
virtual void processParent(const QString &child, const QString &parent)=0
virtual void processAlias(const QString &alias, const QString &name)=0
QStringList globPatterns
QMimeTypePrivate::LocaleHash localeComments
void addGlobPattern(const QString &pattern)
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:731
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
Combined button and popup list for selecting options.
@ CaseInsensitive
@ CaseSensitive
#define Q_UNLIKELY(x)
DBusConnection const char * rule
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
static const char nameAttributeC[]
static const char localeAttributeC[]
static const char mimeTypeAttributeC[]
static const char mimeTypeTagC[]
static const char globDeleteAllTagC[]
static const char patternAttributeC[]
static const char matchTagC[]
static const char matchTypeAttributeC[]
static const char matchValueAttributeC[]
static const char magicTagC[]
static const char matchOffsetAttributeC[]
static const char caseSensitiveAttributeC[]
static const char commentTagC[]
static const char globTagC[]
static const char iconTagC[]
static const char genericIconTagC[]
static const char aliasTagC[]
static const char weightAttributeC[]
static const char subClassTagC[]
static const char matchMaskAttributeC[]
static const char mimeInfoTagC[]
static const char priorityAttributeC[]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLuint GLfloat weight
GLenum type
GLenum target
GLuint name
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLfloat n
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint64EXT * result
[6]
GLubyte * pattern
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qUtf16Printable(string)
Definition qstring.h:1543
#define QStringLiteral(str)
#define Q_UNUSED(x)
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3517
char * toString(const MyType &t)
[31]