Qt 6.x
The Qt SDK
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{
161 return CreateMagicMatchRuleResult(type, value, offsets, mask);
162}
163#endif // feature xmlstreamreader
164
166{
167#if QT_CONFIG(xmlstreamreader)
169 data.loaded = true;
170 int priority = 50;
171 QStack<QMimeMagicRule *> currentRules; // stack for the nesting of rules
172 QList<QMimeMagicRule> rules; // toplevel rules
173 QXmlStreamReader reader(dev);
174 ParseState ps = ParseBeginning;
175 while (!reader.atEnd()) {
176 switch (reader.readNext()) {
177 case QXmlStreamReader::StartElement: {
178 ps = nextState(ps, reader.name());
179 const QXmlStreamAttributes atts = reader.attributes();
180 switch (ps) {
181 case ParseMimeType: { // start parsing a MIME type name
183 if (name.isEmpty()) {
184 reader.raiseError(QStringLiteral("Missing 'type'-attribute"));
185 } else {
186 data.name = name;
187 }
188 }
189 break;
190 case ParseGenericIcon:
191 data.genericIconName = atts.value(QLatin1StringView(nameAttributeC)).toString();
192 break;
193 case ParseIcon:
195 break;
196 case ParseGlobPattern: {
199 const bool caseSensitive = atts.value(QLatin1StringView(caseSensitiveAttributeC)) == "true"_L1;
200
201 if (weight == 0)
203
204 Q_ASSERT(!data.name.isEmpty());
205 const QMimeGlobPattern glob(pattern, data.name, weight, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
206 if (!process(glob, errorMessage)) // for actual glob matching
207 return false;
208 data.addGlobPattern(pattern); // just for QMimeType::globPatterns()
209 }
210 break;
211 case ParseGlobDeleteAll:
212 data.globPatterns.clear();
213 data.hasGlobDeleteAll = true;
214 break;
215 case ParseSubClass: {
216 const QString inheritsFrom = atts.value(QLatin1StringView(mimeTypeAttributeC)).toString();
217 if (!inheritsFrom.isEmpty())
218 processParent(data.name, inheritsFrom);
219 }
220 break;
221 case ParseComment: {
222 // comments have locale attributes.
224 const QString comment = reader.readElementText();
225 if (locale.isEmpty())
226 locale = QString::fromLatin1("default");
227 data.localeComments.insert(locale, comment);
228 }
229 break;
230 case ParseAlias: {
232 if (!alias.isEmpty())
233 processAlias(alias, data.name);
234 }
235 break;
236 case ParseMagic: {
237 priority = 50;
238 const auto priorityS = atts.value(QLatin1StringView(priorityAttributeC));
239 if (!priorityS.isEmpty()) {
240 if (!parseNumber(priorityS, &priority, errorMessage))
241 return false;
242
243 }
244 currentRules.clear();
245 //qDebug() << "MAGIC start for mimetype" << data.name;
246 }
247 break;
248 case ParseMagicMatchRule: {
249 auto result = createMagicMatchRule(atts);
250 if (Q_UNLIKELY(!result.rule.isValid()))
251 qWarning("QMimeDatabase: Error parsing %ls\n%ls",
253 QList<QMimeMagicRule> *ruleList;
254 if (currentRules.isEmpty())
255 ruleList = &rules;
256 else // nest this rule into the proper parent
257 ruleList = &currentRules.top()->m_subMatches;
258 ruleList->append(std::move(result.rule));
259 //qDebug() << " MATCH added. Stack size was" << currentRules.size();
260 currentRules.push(&ruleList->last());
261 break;
262 }
263 case ParseError:
264 reader.raiseError("Unexpected element <"_L1 + reader.name() + u'>');
265 break;
266 default:
267 break;
268 }
269 }
270 break;
271 // continue switch QXmlStreamReader::Token...
272 case QXmlStreamReader::EndElement: // Finished element
273 {
274 const auto elementName = reader.name();
275 if (elementName == QLatin1StringView(mimeTypeTagC)) {
277 return false;
278 data.clear();
279 } else if (elementName == QLatin1StringView(matchTagC)) {
280 // Closing a <match> tag, pop stack
281 currentRules.pop();
282 //qDebug() << " MATCH closed. Stack size is now" << currentRules.size();
283 } else if (elementName == QLatin1StringView(magicTagC)) {
284 //qDebug() << "MAGIC ended, we got" << rules.count() << "rules, with prio" << priority;
285 // Finished a <magic> sequence
286 QMimeMagicRuleMatcher ruleMatcher(data.name, priority);
287 ruleMatcher.addRules(rules);
288 processMagicMatcher(ruleMatcher);
289 rules.clear();
290 }
291 break;
292 }
293 default:
294 break;
295 }
296 }
297
298 if (Q_UNLIKELY(reader.hasError())) {
299 if (errorMessage) {
300 *errorMessage = QString::asprintf("An error has been encountered at line %lld of %ls: %ls:",
301 reader.lineNumber(),
303 qUtf16Printable(reader.errorString()));
304 }
305 return false;
306 }
307
308 return true;
309#else
310 Q_UNUSED(dev);
311 if (errorMessage)
312 *errorMessage = "QXmlStreamReader is not available, cannot parse '%1'."_L1.arg(fileName);
313 return false;
314#endif // feature xmlstreamreader
315}
316
\inmodule QtCore \reentrant
Definition qiodevice.h:34
QString errorString() const
Returns a human readable description of the last error that occurred.
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
T & last()
Definition qlist.h:631
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
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".
void addRules(const QList< QMimeMagicRule > &rules)
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 QMimeType &t, QString *errorMessage)=0
Overwrite to process the sequence of parsed data.
virtual void processParent(const QString &child, const QString &parent)=0
virtual void processAlias(const QString &alias, const QString &name)=0
\inmodule QtCore
Definition qmimetype.h:25
\inmodule QtCore
Definition qstack.h:13
T & top()
Returns a reference to the stack's top item.
Definition qstack.h:19
T pop()
Removes the top item from the stack and returns it.
Definition qstack.h:18
void push(const T &t)
Adds element t to the top of the stack.
Definition qstack.h:17
\inmodule QtCore
Definition qstringview.h:76
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1014
int toInt(bool *ok=nullptr, int base=10) const
Returns the string view converted to an int using base base, which is 10 by default and must be betwe...
Definition qstring.h:1025
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3110
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
Q_CORE_EXPORT QStringView value(QAnyStringView namespaceUri, QAnyStringView name) const noexcept
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:162
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[]
GLuint GLuint GLfloat weight
GLenum type
GLenum target
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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:1403
#define QStringLiteral(str)
#define Q_UNUSED(x)
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3503
char * toString(const MyType &t)
[31]