mimetypes/qmimetypeparser.cpp

Source codeSwitch to Preprocessed file
LineSource CodeCoverage
1/**************************************************************************** -
2** -
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -
4** Contact: http://www.qt-project.org/legal -
5** -
6** This file is part of the QtCore module of the Qt Toolkit. -
7** -
8** $QT_BEGIN_LICENSE:LGPL$ -
9** Commercial License Usage -
10** Licensees holding valid commercial Qt licenses may use this file in -
11** accordance with the commercial license agreement provided with the -
12** Software or, alternatively, in accordance with the terms contained in -
13** a written agreement between you and Digia. For licensing terms and -
14** conditions see http://qt.digia.com/licensing. For further information -
15** use the contact form at http://qt.digia.com/contact-us. -
16** -
17** GNU Lesser General Public License Usage -
18** Alternatively, this file may be used under the terms of the GNU Lesser -
19** General Public License version 2.1 as published by the Free Software -
20** Foundation and appearing in the file LICENSE.LGPL included in the -
21** packaging of this file. Please review the following information to -
22** ensure the GNU Lesser General Public License version 2.1 requirements -
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -
24** -
25** In addition, as a special exception, Digia gives you certain additional -
26** rights. These rights are described in the Digia Qt LGPL Exception -
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -
28** -
29** GNU General Public License Usage -
30** Alternatively, this file may be used under the terms of the GNU -
31** General Public License version 3.0 as published by the Free Software -
32** Foundation and appearing in the file LICENSE.GPL included in the -
33** packaging of this file. Please review the following information to -
34** ensure the GNU General Public License version 3.0 requirements will be -
35** met: http://www.gnu.org/copyleft/gpl.html. -
36** -
37** -
38** $QT_END_LICENSE$ -
39** -
40****************************************************************************/ -
41 -
42#define QT_NO_CAST_FROM_ASCII -
43 -
44#include "qmimetypeparser_p.h" -
45 -
46#include "qmimetype_p.h" -
47#include "qmimemagicrulematcher_p.h" -
48 -
49#include <QtCore/QCoreApplication> -
50#include <QtCore/QDebug> -
51#include <QtCore/QDir> -
52#include <QtCore/QPair> -
53#include <QtCore/QXmlStreamReader> -
54#include <QtCore/QXmlStreamWriter> -
55#include <QtCore/QStack> -
56 -
57QT_BEGIN_NAMESPACE -
58 -
59// XML tags in MIME files -
60static const char mimeInfoTagC[] = "mime-info"; -
61static const char mimeTypeTagC[] = "mime-type"; -
62static const char mimeTypeAttributeC[] = "type"; -
63static const char subClassTagC[] = "sub-class-of"; -
64static const char commentTagC[] = "comment"; -
65static const char genericIconTagC[] = "generic-icon"; -
66static const char iconTagC[] = "icon"; -
67static const char nameAttributeC[] = "name"; -
68static const char globTagC[] = "glob"; -
69static const char aliasTagC[] = "alias"; -
70static const char patternAttributeC[] = "pattern"; -
71static const char weightAttributeC[] = "weight"; -
72static const char caseSensitiveAttributeC[] = "case-sensitive"; -
73static const char localeAttributeC[] = "xml:lang"; -
74 -
75static const char magicTagC[] = "magic"; -
76static const char priorityAttributeC[] = "priority"; -
77 -
78static const char matchTagC[] = "match"; -
79static const char matchValueAttributeC[] = "value"; -
80static const char matchTypeAttributeC[] = "type"; -
81static const char matchOffsetAttributeC[] = "offset"; -
82static const char matchMaskAttributeC[] = "mask"; -
83 -
84/*! -
85 \class QMimeTypeParser -
86 \inmodule QtCore -
87 \internal -
88 \brief The QMimeTypeParser class parses MIME types, and builds a MIME database hierarchy by adding to QMimeDatabasePrivate. -
89 -
90 Populates QMimeDataBase -
91 -
92 \sa QMimeDatabase, QMimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern -
93 \sa QMimeTypeParser -
94*/ -
95 -
96/*! -
97 \class QMimeTypeParserBase -
98 \inmodule QtCore -
99 \internal -
100 \brief The QMimeTypeParserBase class parses for a sequence of <mime-type> in a generic way. -
101 -
102 Calls abstract handler function process for QMimeType it finds. -
103 -
104 \sa QMimeDatabase, QMimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern -
105 \sa QMimeTypeParser -
106*/ -
107 -
108/*! -
109 \fn virtual bool QMimeTypeParserBase::process(const QMimeType &t, QString *errorMessage) = 0; -
110 Overwrite to process the sequence of parsed data -
111*/ -
112 -
113QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState currentState, const QStringRef &startElement) -
114{ -
115 switch (currentState) { -
116 case ParseBeginning: -
117 if (startElement == QLatin1String(mimeInfoTagC))
never evaluated: startElement == QLatin1String(mimeInfoTagC)
0
118 return ParseMimeInfo;
never executed: return ParseMimeInfo;
0
119 if (startElement == QLatin1String(mimeTypeTagC))
never evaluated: startElement == QLatin1String(mimeTypeTagC)
0
120 return ParseMimeType;
never executed: return ParseMimeType;
0
121 return ParseError;
never executed: return ParseError;
0
122 case ParseMimeInfo: -
123 return startElement == QLatin1String(mimeTypeTagC) ? ParseMimeType : ParseError;
never executed: return startElement == QLatin1String(mimeTypeTagC) ? ParseMimeType : ParseError;
0
124 case ParseMimeType: -
125 case ParseComment: -
126 case ParseGenericIcon: -
127 case ParseIcon: -
128 case ParseGlobPattern: -
129 case ParseSubClass: -
130 case ParseAlias: -
131 case ParseOtherMimeTypeSubTag: -
132 case ParseMagicMatchRule: -
133 if (startElement == QLatin1String(mimeTypeTagC)) // Sequence of <mime-type>
never evaluated: startElement == QLatin1String(mimeTypeTagC)
0
134 return ParseMimeType;
never executed: return ParseMimeType;
0
135 if (startElement == QLatin1String(commentTagC ))
never evaluated: startElement == QLatin1String(commentTagC )
0
136 return ParseComment;
never executed: return ParseComment;
0
137 if (startElement == QLatin1String(genericIconTagC))
never evaluated: startElement == QLatin1String(genericIconTagC)
0
138 return ParseGenericIcon;
never executed: return ParseGenericIcon;
0
139 if (startElement == QLatin1String(iconTagC))
never evaluated: startElement == QLatin1String(iconTagC)
0
140 return ParseIcon;
never executed: return ParseIcon;
0
141 if (startElement == QLatin1String(globTagC))
never evaluated: startElement == QLatin1String(globTagC)
0
142 return ParseGlobPattern;
never executed: return ParseGlobPattern;
0
143 if (startElement == QLatin1String(subClassTagC))
never evaluated: startElement == QLatin1String(subClassTagC)
0
144 return ParseSubClass;
never executed: return ParseSubClass;
0
145 if (startElement == QLatin1String(aliasTagC))
never evaluated: startElement == QLatin1String(aliasTagC)
0
146 return ParseAlias;
never executed: return ParseAlias;
0
147 if (startElement == QLatin1String(magicTagC))
never evaluated: startElement == QLatin1String(magicTagC)
0
148 return ParseMagic;
never executed: return ParseMagic;
0
149 if (startElement == QLatin1String(matchTagC))
never evaluated: startElement == QLatin1String(matchTagC)
0
150 return ParseMagicMatchRule;
never executed: return ParseMagicMatchRule;
0
151 return ParseOtherMimeTypeSubTag;
never executed: return ParseOtherMimeTypeSubTag;
0
152 case ParseMagic: -
153 if (startElement == QLatin1String(matchTagC))
never evaluated: startElement == QLatin1String(matchTagC)
0
154 return ParseMagicMatchRule;
never executed: return ParseMagicMatchRule;
0
155 break;
never executed: break;
0
156 case ParseError: -
157 break;
never executed: break;
0
158 } -
159 return ParseError;
never executed: return ParseError;
0
160} -
161 -
162// Parse int number from an (attribute) string) -
163static bool parseNumber(const QString &n, int *target, QString *errorMessage) -
164{ -
165 bool ok;
never executed (the execution status of this line is deduced): bool ok;
-
166 *target = n.toInt(&ok);
never executed (the execution status of this line is deduced): *target = n.toInt(&ok);
-
167 if (!ok) {
never evaluated: !ok
0
168 *errorMessage = QString::fromLatin1("Not a number '%1'.").arg(n);
never executed (the execution status of this line is deduced): *errorMessage = QString::fromLatin1("Not a number '%1'.").arg(n);
-
169 return false;
never executed: return false;
0
170 } -
171 return true;
never executed: return true;
0
172} -
173 -
174// Evaluate a magic match rule like -
175// <match value="must be converted with BinHex" type="string" offset="11"/> -
176// <match value="0x9501" type="big16" offset="0:64"/> -
177#ifndef QT_NO_XMLSTREAMREADER -
178static bool createMagicMatchRule(const QXmlStreamAttributes &atts, -
179 QString *errorMessage, QMimeMagicRule *&rule) -
180{ -
181 const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString();
-
182 QMimeMagicRule::Type magicType = QMimeMagicRule::type(type.toLatin1());
never executed (the execution status of this line is deduced): QMimeMagicRule::Type magicType = QMimeMagicRule::type(type.toLatin1());
-
183 if (magicType == QMimeMagicRule::Invalid) {
never evaluated: magicType == QMimeMagicRule::Invalid
0
184 qWarning("%s: match type %s is not supported.", Q_FUNC_INFO, type.toUtf8().constData());
never executed (the execution status of this line is deduced): QMessageLogger("mimetypes/qmimetypeparser.cpp", 184, __PRETTY_FUNCTION__).warning("%s: match type %s is not supported.", __PRETTY_FUNCTION__, type.toUtf8().constData());
-
185 return true;
never executed: return true;
0
186 } -
187 const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString();
-
188 if (value.isEmpty()) {
never evaluated: value.isEmpty()
0
189 *errorMessage = QString::fromLatin1("Empty match value detected.");
never executed (the execution status of this line is deduced): *errorMessage = QString::fromLatin1("Empty match value detected.");
-
190 return false;
never executed: return false;
0
191 } -
192 // Parse for offset as "1" or "1:10" -
193 int startPos, endPos;
never executed (the execution status of this line is deduced): int startPos, endPos;
-
194 const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString();
-
195 const int colonIndex = offsetS.indexOf(QLatin1Char(':'));
never executed (the execution status of this line is deduced): const int colonIndex = offsetS.indexOf(QLatin1Char(':'));
-
196 const QString startPosS = colonIndex == -1 ? offsetS : offsetS.mid(0, colonIndex);
never evaluated: colonIndex == -1
0
197 const QString endPosS = colonIndex == -1 ? offsetS : offsetS.mid(colonIndex + 1);
never evaluated: colonIndex == -1
0
198 if (!parseNumber(startPosS, &startPos, errorMessage) || !parseNumber(endPosS, &endPos, errorMessage))
never evaluated: !parseNumber(startPosS, &startPos, errorMessage)
never evaluated: !parseNumber(endPosS, &endPos, errorMessage)
0
199 return false;
never executed: return false;
0
200 const QString mask = atts.value(QLatin1String(matchMaskAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString mask = atts.value(QLatin1String(matchMaskAttributeC)).toString();
-
201 -
202 rule = new QMimeMagicRule(magicType, value.toUtf8(), startPos, endPos, mask.toLatin1());
never executed (the execution status of this line is deduced): rule = new QMimeMagicRule(magicType, value.toUtf8(), startPos, endPos, mask.toLatin1());
-
203 -
204 return true;
never executed: return true;
0
205} -
206#endif -
207 -
208bool QMimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString *errorMessage) -
209{ -
210#ifdef QT_NO_XMLSTREAMREADER -
211 if (errorMessage) -
212 *errorMessage = QString::fromLatin1("QXmlStreamReader is not available, cannot parse."); -
213 return false; -
214#else -
215 QMimeTypePrivate data;
never executed (the execution status of this line is deduced): QMimeTypePrivate data;
-
216 int priority = 50;
never executed (the execution status of this line is deduced): int priority = 50;
-
217 QStack<QMimeMagicRule *> currentRules; // stack for the nesting of rules
never executed (the execution status of this line is deduced): QStack<QMimeMagicRule *> currentRules;
-
218 QList<QMimeMagicRule> rules; // toplevel rules
never executed (the execution status of this line is deduced): QList<QMimeMagicRule> rules;
-
219 QXmlStreamReader reader(dev);
never executed (the execution status of this line is deduced): QXmlStreamReader reader(dev);
-
220 ParseState ps = ParseBeginning;
never executed (the execution status of this line is deduced): ParseState ps = ParseBeginning;
-
221 QXmlStreamAttributes atts;
never executed (the execution status of this line is deduced): QXmlStreamAttributes atts;
-
222 while (!reader.atEnd()) {
never evaluated: !reader.atEnd()
0
223 switch (reader.readNext()) { -
224 case QXmlStreamReader::StartElement: -
225 ps = nextState(ps, reader.name());
never executed (the execution status of this line is deduced): ps = nextState(ps, reader.name());
-
226 atts = reader.attributes();
never executed (the execution status of this line is deduced): atts = reader.attributes();
-
227 switch (ps) { -
228 case ParseMimeType: { // start parsing a MIME type name -
229 const QString name = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString name = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
-
230 if (name.isEmpty()) {
never evaluated: name.isEmpty()
0
231 reader.raiseError(QString::fromLatin1("Missing '%1'-attribute").arg(QString::fromLatin1(mimeTypeAttributeC)));
never executed (the execution status of this line is deduced): reader.raiseError(QString::fromLatin1("Missing '%1'-attribute").arg(QString::fromLatin1(mimeTypeAttributeC)));
-
232 } else {
never executed: }
0
233 data.name = name;
never executed (the execution status of this line is deduced): data.name = name;
-
234 }
never executed: }
0
235 } -
236 break;
never executed: break;
0
237 case ParseGenericIcon: -
238 data.genericIconName = atts.value(QLatin1String(nameAttributeC)).toString();
never executed (the execution status of this line is deduced): data.genericIconName = atts.value(QLatin1String(nameAttributeC)).toString();
-
239 break;
never executed: break;
0
240 case ParseIcon: -
241 data.iconName = atts.value(QLatin1String(nameAttributeC)).toString();
never executed (the execution status of this line is deduced): data.iconName = atts.value(QLatin1String(nameAttributeC)).toString();
-
242 break;
never executed: break;
0
243 case ParseGlobPattern: { -
244 const QString pattern = atts.value(QLatin1String(patternAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString pattern = atts.value(QLatin1String(patternAttributeC)).toString();
-
245 unsigned weight = atts.value(QLatin1String(weightAttributeC)).toString().toInt();
never executed (the execution status of this line is deduced): unsigned weight = atts.value(QLatin1String(weightAttributeC)).toString().toInt();
-
246 const bool caseSensitive = atts.value(QLatin1String(caseSensitiveAttributeC)).toString() == QLatin1String("true");
never executed (the execution status of this line is deduced): const bool caseSensitive = atts.value(QLatin1String(caseSensitiveAttributeC)).toString() == QLatin1String("true");
-
247 -
248 if (weight == 0)
never evaluated: weight == 0
0
249 weight = QMimeGlobPattern::DefaultWeight;
never executed: weight = QMimeGlobPattern::DefaultWeight;
0
250 -
251 Q_ASSERT(!data.name.isEmpty());
never executed (the execution status of this line is deduced): qt_noop();
-
252 const QMimeGlobPattern glob(pattern, data.name, weight, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
never executed (the execution status of this line is deduced): const QMimeGlobPattern glob(pattern, data.name, weight, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
-
253 if (!process(glob, errorMessage)) // for actual glob matching
never evaluated: !process(glob, errorMessage)
0
254 return false;
never executed: return false;
0
255 data.addGlobPattern(pattern); // just for QMimeType::globPatterns()
never executed (the execution status of this line is deduced): data.addGlobPattern(pattern);
-
256 } -
257 break;
never executed: break;
0
258 case ParseSubClass: { -
259 const QString inheritsFrom = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString inheritsFrom = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
-
260 if (!inheritsFrom.isEmpty())
never evaluated: !inheritsFrom.isEmpty()
0
261 processParent(data.name, inheritsFrom);
never executed: processParent(data.name, inheritsFrom);
0
262 } -
263 break;
never executed: break;
0
264 case ParseComment: { -
265 // comments have locale attributes. We want the default, English one -
266 QString locale = atts.value(QLatin1String(localeAttributeC)).toString();
never executed (the execution status of this line is deduced): QString locale = atts.value(QLatin1String(localeAttributeC)).toString();
-
267 const QString comment = reader.readElementText();
never executed (the execution status of this line is deduced): const QString comment = reader.readElementText();
-
268 if (locale.isEmpty())
never evaluated: locale.isEmpty()
0
269 locale = QString::fromLatin1("en_US");
never executed: locale = QString::fromLatin1("en_US");
0
270 data.localeComments.insert(locale, comment);
never executed (the execution status of this line is deduced): data.localeComments.insert(locale, comment);
-
271 } -
272 break;
never executed: break;
0
273 case ParseAlias: { -
274 const QString alias = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString alias = atts.value(QLatin1String(mimeTypeAttributeC)).toString();
-
275 if (!alias.isEmpty())
never evaluated: !alias.isEmpty()
0
276 processAlias(alias, data.name);
never executed: processAlias(alias, data.name);
0
277 } -
278 break;
never executed: break;
0
279 case ParseMagic: { -
280 priority = 50;
never executed (the execution status of this line is deduced): priority = 50;
-
281 const QString priorityS = atts.value(QLatin1String(priorityAttributeC)).toString();
never executed (the execution status of this line is deduced): const QString priorityS = atts.value(QLatin1String(priorityAttributeC)).toString();
-
282 if (!priorityS.isEmpty()) {
never evaluated: !priorityS.isEmpty()
0
283 if (!parseNumber(priorityS, &priority, errorMessage))
never evaluated: !parseNumber(priorityS, &priority, errorMessage)
0
284 return false;
never executed: return false;
0
285 -
286 }
never executed: }
0
287 currentRules.clear();
never executed (the execution status of this line is deduced): currentRules.clear();
-
288 //qDebug() << "MAGIC start for mimetype" << data.name; -
289 } -
290 break;
never executed: break;
0
291 case ParseMagicMatchRule: { -
292 QMimeMagicRule *rule = 0;
never executed (the execution status of this line is deduced): QMimeMagicRule *rule = 0;
-
293 if (!createMagicMatchRule(atts, errorMessage, rule))
never evaluated: !createMagicMatchRule(atts, errorMessage, rule)
0
294 return false;
never executed: return false;
0
295 QList<QMimeMagicRule> *ruleList;
never executed (the execution status of this line is deduced): QList<QMimeMagicRule> *ruleList;
-
296 if (currentRules.isEmpty())
never evaluated: currentRules.isEmpty()
0
297 ruleList = &rules;
never executed: ruleList = &rules;
0
298 else // nest this rule into the proper parent -
299 ruleList = &currentRules.top()->m_subMatches;
never executed: ruleList = &currentRules.top()->m_subMatches;
0
300 ruleList->append(*rule);
never executed (the execution status of this line is deduced): ruleList->append(*rule);
-
301 //qDebug() << " MATCH added. Stack size was" << currentRules.size(); -
302 currentRules.push(&ruleList->last());
never executed (the execution status of this line is deduced): currentRules.push(&ruleList->last());
-
303 delete rule;
never executed (the execution status of this line is deduced): delete rule;
-
304 break;
never executed: break;
0
305 } -
306 case ParseError: -
307 reader.raiseError(QString::fromLatin1("Unexpected element <%1>").
never executed (the execution status of this line is deduced): reader.raiseError(QString::fromLatin1("Unexpected element <%1>").
-
308 arg(reader.name().toString()));
never executed (the execution status of this line is deduced): arg(reader.name().toString()));
-
309 break;
never executed: break;
0
310 default: -
311 break;
never executed: break;
0
312 } -
313 break;
never executed: break;
0
314 // continue switch QXmlStreamReader::Token... -
315 case QXmlStreamReader::EndElement: // Finished element -
316 { -
317 const QStringRef elementName = reader.name();
never executed (the execution status of this line is deduced): const QStringRef elementName = reader.name();
-
318 if (elementName == QLatin1String(mimeTypeTagC)) {
never evaluated: elementName == QLatin1String(mimeTypeTagC)
0
319 if (!process(QMimeType(data), errorMessage))
never evaluated: !process(QMimeType(data), errorMessage)
0
320 return false;
never executed: return false;
0
321 data.clear();
never executed (the execution status of this line is deduced): data.clear();
-
322 } else if (elementName == QLatin1String(matchTagC)) {
never executed: }
never evaluated: elementName == QLatin1String(matchTagC)
0
323 // Closing a <match> tag, pop stack -
324 currentRules.pop();
never executed (the execution status of this line is deduced): currentRules.pop();
-
325 //qDebug() << " MATCH closed. Stack size is now" << currentRules.size(); -
326 } else if (elementName == QLatin1String(magicTagC)) {
never executed: }
never evaluated: elementName == QLatin1String(magicTagC)
0
327 //qDebug() << "MAGIC ended, we got" << rules.count() << "rules, with prio" << priority; -
328 // Finished a <magic> sequence -
329 QMimeMagicRuleMatcher ruleMatcher(data.name, priority);
never executed (the execution status of this line is deduced): QMimeMagicRuleMatcher ruleMatcher(data.name, priority);
-
330 ruleMatcher.addRules(rules);
never executed (the execution status of this line is deduced): ruleMatcher.addRules(rules);
-
331 processMagicMatcher(ruleMatcher);
never executed (the execution status of this line is deduced): processMagicMatcher(ruleMatcher);
-
332 rules.clear();
never executed (the execution status of this line is deduced): rules.clear();
-
333 }
never executed: }
0
334 break;
never executed: break;
0
335 } -
336 default: -
337 break;
never executed: break;
0
338 } -
339 }
never executed: }
0
340 -
341 if (reader.hasError()) {
never evaluated: reader.hasError()
0
342 if (errorMessage)
never evaluated: errorMessage
0
343 *errorMessage = QString::fromLatin1("An error has been encountered at line %1 of %2: %3:").arg(reader.lineNumber()).arg(fileName, reader.errorString());
never executed: *errorMessage = QString::fromLatin1("An error has been encountered at line %1 of %2: %3:").arg(reader.lineNumber()).arg(fileName, reader.errorString());
0
344 return false;
never executed: return false;
0
345 } -
346 -
347 return true;
never executed: return true;
0
348#endif //QT_NO_XMLSTREAMREADER -
349} -
350 -
351QT_END_NAMESPACE -
352 -
Source codeSwitch to Preprocessed file

Generated by Squish Coco Non-Commercial