Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qmimeprovider.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// Copyright (C) 2018 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com>
3// Copyright (C) 2019 Intel Corporation.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qmimeprovider_p.h"
7
8#include "qmimetypeparser_p.h"
9#include <qstandardpaths.h>
11
12#include <QMap>
13#include <QXmlStreamReader>
14#include <QBuffer>
15#include <QDir>
16#include <QFile>
17#include <QByteArrayMatcher>
18#include <QDebug>
19#include <QDateTime>
20#include <QtEndian>
21
22#if QT_CONFIG(mimetype_database)
23# if defined(Q_CC_MSVC_ONLY)
24# pragma section(".qtmimedatabase", read, shared)
25__declspec(allocate(".qtmimedatabase")) __declspec(align(4096))
26# elif defined(Q_OS_DARWIN)
27__attribute__((section("__TEXT,.qtmimedatabase"), aligned(4096)))
28# elif (defined(Q_OF_ELF) || defined(Q_OS_WIN)) && defined(Q_CC_GNU)
29__attribute__((section(".qtmimedatabase"), aligned(4096)))
30# endif
31
32# include "qmimeprovider_database.cpp"
33
34# ifdef MIME_DATABASE_IS_ZSTD
35# if !QT_CONFIG(zstd)
36# error "MIME database is zstd but no support compiled in!"
37# endif
38# include <zstd.h>
39# endif
40# ifdef MIME_DATABASE_IS_GZIP
41# ifdef QT_NO_COMPRESS
42# error "MIME database is zlib but no support compiled in!"
43# endif
44# define ZLIB_CONST
45# include <zconf.h>
46# include <zlib.h>
47# endif
48#endif
49
51
52using namespace Qt::StringLiterals;
53
54static inline void appendIfNew(QStringList &list, const QString &str)
55{
56 if (!list.contains(str))
58}
59
61 : m_db(db), m_directory(directory)
62{
63}
64
65
67 : QMimeProviderBase(db, directory), m_mimetypeListLoaded(false)
68{
70}
71
73{
75 ~CacheFile();
76
77 bool isValid() const { return m_valid; }
78 inline quint16 getUint16(int offset) const
79 {
80 return qFromBigEndian(*reinterpret_cast<quint16 *>(data + offset));
81 }
82 inline quint32 getUint32(int offset) const
83 {
84 return qFromBigEndian(*reinterpret_cast<quint32 *>(data + offset));
85 }
86 inline const char *getCharStar(int offset) const
87 {
88 return reinterpret_cast<const char *>(data + offset);
89 }
90 bool load();
91 bool reload();
92
96 bool m_valid;
97};
98
100 : file(fileName), m_valid(false)
101{
102 load();
103}
104
106{
107}
108
110{
112 return false;
113 data = file.map(0, file.size());
114 if (data) {
115 const int major = getUint16(0);
116 const int minor = getUint16(2);
117 m_valid = (major == 1 && minor >= 1 && minor <= 2);
118 }
120 return m_valid;
121}
122
124{
125 m_valid = false;
126 if (file.isOpen()) {
127 file.close();
128 }
129 data = nullptr;
130 return load();
131}
132
134
136{
137 return m_cacheFile != nullptr;
138}
139
141{
142 return false;
143}
144
145// Position of the "list offsets" values, at the beginning of the mime.cache file
146enum {
153 // PosNamespaceListOffset = 28,
157
158bool QMimeBinaryProvider::checkCacheChanged()
159{
160 QFileInfo fileInfo(m_cacheFile->file);
161 if (fileInfo.lastModified(QTimeZone::UTC) > m_cacheFile->m_mtime) {
162 // Deletion can't happen by just running update-mime-database.
163 // But the user could use rm -rf :-)
164 m_cacheFile->reload(); // will mark itself as invalid on failure
165 return true;
166 }
167 return false;
168}
169
171{
172 if (!m_cacheFile) {
173 const QString cacheFileName = m_directory + "/mime.cache"_L1;
174 m_cacheFile = std::make_unique<CacheFile>(cacheFileName);
175 m_mimetypeListLoaded = false;
176 m_mimetypeExtra.clear();
177 } else {
178 if (checkCacheChanged()) {
179 m_mimetypeListLoaded = false;
180 m_mimetypeExtra.clear();
181 } else {
182 return; // nothing to do
183 }
184 }
185 if (!m_cacheFile->isValid()) // verify existence and version
186 m_cacheFile.reset();
187}
188
190{
192 data.name = name;
193 data.fromCache = true;
194 // The rest is retrieved on demand.
195 // comment and globPatterns: in loadMimeTypePrivate
196 // iconName: in loadIcon
197 // genericIconName: in loadGenericIcon
198 return QMimeType(data);
199}
200
202{
203 if (!m_mimetypeListLoaded)
204 loadMimeTypeList();
205 if (!m_mimetypeNames.contains(name))
206 return QMimeType(); // unknown mimetype
208}
209
211{
212 if (fileName.isEmpty())
213 return;
214 Q_ASSERT(m_cacheFile);
215 const QString lowerFileName = fileName.toLower();
216 // Check literals (e.g. "Makefile")
217 matchGlobList(result, m_cacheFile.get(), m_cacheFile->getUint32(PosLiteralListOffset),
218 fileName);
219 // Check the very common *.txt cases with the suffix tree
220 if (result.m_matchingMimeTypes.isEmpty()) {
221 const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset);
222 const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset);
223 const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4);
224 matchSuffixTree(result, m_cacheFile.get(), numRoots, firstRootOffset, lowerFileName,
225 lowerFileName.size() - 1, false);
226 if (result.m_matchingMimeTypes.isEmpty())
227 matchSuffixTree(result, m_cacheFile.get(), numRoots, firstRootOffset, fileName,
228 fileName.size() - 1, true);
229 }
230 // Check complex globs (e.g. "callgrind.out[0-9]*" or "README*")
231 if (result.m_matchingMimeTypes.isEmpty())
232 matchGlobList(result, m_cacheFile.get(), m_cacheFile->getUint32(PosGlobListOffset),
233 fileName);
234}
235
236bool QMimeBinaryProvider::isMimeTypeGlobsExcluded(const char *mimeTypeName)
237{
238 return m_mimeTypesWithExcludedGlobs.contains(QLatin1StringView(mimeTypeName));
239}
240
242{
243 for (const auto &mt : toExclude)
245}
246
247void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int off, const QString &fileName)
248{
249 const int numGlobs = cacheFile->getUint32(off);
250 //qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset;
251 for (int i = 0; i < numGlobs; ++i) {
252 const int globOffset = cacheFile->getUint32(off + 4 + 12 * i);
253 const int mimeTypeOffset = cacheFile->getUint32(off + 4 + 12 * i + 4);
254 const int flagsAndWeight = cacheFile->getUint32(off + 4 + 12 * i + 8);
255 const int weight = flagsAndWeight & 0xff;
256 const bool caseSensitive = flagsAndWeight & 0x100;
257 const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
258 const QString pattern = QLatin1StringView(cacheFile->getCharStar(globOffset));
259
260 const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
261 //qDebug() << pattern << mimeType << weight << caseSensitive;
262 if (isMimeTypeGlobsExcluded(mimeType))
263 continue;
264
265 QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive);
266 if (glob.matchFileName(fileName))
268 }
269}
270
271bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult &result,
272 QMimeBinaryProvider::CacheFile *cacheFile, int numEntries,
273 int firstOffset, const QString &fileName,
274 qsizetype charPos, bool caseSensitiveCheck)
275{
276 QChar fileChar = fileName[charPos];
277 int min = 0;
278 int max = numEntries - 1;
279 while (min <= max) {
280 const int mid = (min + max) / 2;
281 const int off = firstOffset + 12 * mid;
282 const QChar ch = char16_t(cacheFile->getUint32(off));
283 if (ch < fileChar)
284 min = mid + 1;
285 else if (ch > fileChar)
286 max = mid - 1;
287 else {
288 --charPos;
289 int numChildren = cacheFile->getUint32(off + 4);
290 int childrenOffset = cacheFile->getUint32(off + 8);
291 bool success = false;
292 if (charPos > 0)
293 success = matchSuffixTree(result, cacheFile, numChildren, childrenOffset, fileName, charPos, caseSensitiveCheck);
294 if (!success) {
295 for (int i = 0; i < numChildren; ++i) {
296 const int childOff = childrenOffset + 12 * i;
297 const int mch = cacheFile->getUint32(childOff);
298 if (mch != 0)
299 break;
300 const int mimeTypeOffset = cacheFile->getUint32(childOff + 4);
301 const char *mimeType = cacheFile->getCharStar(mimeTypeOffset);
302 if (isMimeTypeGlobsExcluded(mimeType))
303 continue;
304 const int flagsAndWeight = cacheFile->getUint32(childOff + 8);
305 const int weight = flagsAndWeight & 0xff;
306 const bool caseSensitive = flagsAndWeight & 0x100;
307 if (caseSensitiveCheck || !caseSensitive) {
309 u'*' + QStringView{fileName}.mid(charPos + 1),
310 fileName.size() - charPos - 2);
311 success = true;
312 }
313 }
314 }
315 return success;
316 }
317 }
318 return false;
319}
320
321bool QMimeBinaryProvider::matchMagicRule(QMimeBinaryProvider::CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data)
322{
323 const char *dataPtr = data.constData();
324 const qsizetype dataSize = data.size();
325 for (int matchlet = 0; matchlet < numMatchlets; ++matchlet) {
326 const int off = firstOffset + matchlet * 32;
327 const int rangeStart = cacheFile->getUint32(off);
328 const int rangeLength = cacheFile->getUint32(off + 4);
329 //const int wordSize = cacheFile->getUint32(off + 8);
330 const int valueLength = cacheFile->getUint32(off + 12);
331 const int valueOffset = cacheFile->getUint32(off + 16);
332 const int maskOffset = cacheFile->getUint32(off + 20);
333 const char *mask = maskOffset ? cacheFile->getCharStar(maskOffset) : nullptr;
334
335 if (!QMimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile->getCharStar(valueOffset), mask))
336 continue;
337
338 const int numChildren = cacheFile->getUint32(off + 24);
339 const int firstChildOffset = cacheFile->getUint32(off + 28);
340 if (numChildren == 0) // No submatch? Then we are done.
341 return true;
342 // Check that one of the submatches matches too
343 if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data))
344 return true;
345 }
346 return false;
347}
348
349void QMimeBinaryProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate)
350{
351 const int magicListOffset = m_cacheFile->getUint32(PosMagicListOffset);
352 const int numMatches = m_cacheFile->getUint32(magicListOffset);
353 //const int maxExtent = cacheFile->getUint32(magicListOffset + 4);
354 const int firstMatchOffset = m_cacheFile->getUint32(magicListOffset + 8);
355
356 for (int i = 0; i < numMatches; ++i) {
357 const int off = firstMatchOffset + i * 16;
358 const int numMatchlets = m_cacheFile->getUint32(off + 8);
359 const int firstMatchletOffset = m_cacheFile->getUint32(off + 12);
360 if (matchMagicRule(m_cacheFile.get(), numMatchlets, firstMatchletOffset, data)) {
361 const int mimeTypeOffset = m_cacheFile->getUint32(off + 4);
362 const char *mimeType = m_cacheFile->getCharStar(mimeTypeOffset);
363 *accuracyPtr = m_cacheFile->getUint32(off);
364 // Return the first match. We have no rules for conflicting magic data...
365 // (mime.cache itself is sorted, but what about local overrides with a lower prio?)
367 return;
368 }
369 }
370}
371
373{
374 const QByteArray mimeStr = mime.toLatin1();
375 const int parentListOffset = m_cacheFile->getUint32(PosParentListOffset);
376 const int numEntries = m_cacheFile->getUint32(parentListOffset);
377
378 int begin = 0;
379 int end = numEntries - 1;
380 while (begin <= end) {
381 const int medium = (begin + end) / 2;
382 const int off = parentListOffset + 4 + 8 * medium;
383 const int mimeOffset = m_cacheFile->getUint32(off);
384 const char *aMime = m_cacheFile->getCharStar(mimeOffset);
385 const int cmp = qstrcmp(aMime, mimeStr);
386 if (cmp < 0) {
387 begin = medium + 1;
388 } else if (cmp > 0) {
389 end = medium - 1;
390 } else {
391 const int parentsOffset = m_cacheFile->getUint32(off + 4);
392 const int numParents = m_cacheFile->getUint32(parentsOffset);
393 for (int i = 0; i < numParents; ++i) {
394 const int parentOffset = m_cacheFile->getUint32(parentsOffset + 4 + 4 * i);
395 const char *aParent = m_cacheFile->getCharStar(parentOffset);
396 const QString strParent = QString::fromLatin1(aParent);
397 appendIfNew(result, strParent);
398 }
399 break;
400 }
401 }
402}
403
405{
406 const QByteArray input = name.toLatin1();
407 const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset);
408 const int numEntries = m_cacheFile->getUint32(aliasListOffset);
409 int begin = 0;
410 int end = numEntries - 1;
411 while (begin <= end) {
412 const int medium = (begin + end) / 2;
413 const int off = aliasListOffset + 4 + 8 * medium;
414 const int aliasOffset = m_cacheFile->getUint32(off);
415 const char *alias = m_cacheFile->getCharStar(aliasOffset);
416 const int cmp = qstrcmp(alias, input);
417 if (cmp < 0) {
418 begin = medium + 1;
419 } else if (cmp > 0) {
420 end = medium - 1;
421 } else {
422 const int mimeOffset = m_cacheFile->getUint32(off + 4);
423 const char *mimeType = m_cacheFile->getCharStar(mimeOffset);
425 }
426 }
427 return QString();
428}
429
431{
432 const QByteArray input = name.toLatin1();
433 const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset);
434 const int numEntries = m_cacheFile->getUint32(aliasListOffset);
435 for (int pos = 0; pos < numEntries; ++pos) {
436 const int off = aliasListOffset + 4 + 8 * pos;
437 const int mimeOffset = m_cacheFile->getUint32(off + 4);
438 const char *mimeType = m_cacheFile->getCharStar(mimeOffset);
439
440 if (input == mimeType) {
441 const int aliasOffset = m_cacheFile->getUint32(off);
442 const char *alias = m_cacheFile->getCharStar(aliasOffset);
443 const QString strAlias = QString::fromLatin1(alias);
444 appendIfNew(result, strAlias);
445 }
446 }
447}
448
449void QMimeBinaryProvider::loadMimeTypeList()
450{
451 if (!m_mimetypeListLoaded) {
452 m_mimetypeListLoaded = true;
453 m_mimetypeNames.clear();
454 // Unfortunately mime.cache doesn't have a full list of all mimetypes.
455 // So we have to parse the plain-text files called "types".
458 while (!file.atEnd()) {
460 if (line.endsWith('\n'))
461 line.chop(1);
462 m_mimetypeNames.insert(QString::fromLatin1(line));
463 }
464 }
465 }
466}
467
469{
470 loadMimeTypeList();
471 if (result.isEmpty()) {
472 result.reserve(m_mimetypeNames.size());
473 for (const QString &name : std::as_const(m_mimetypeNames))
475 } else {
476 for (const QString &name : std::as_const(m_mimetypeNames))
477 if (std::find_if(result.constBegin(), result.constEnd(), [name](const QMimeType &mime) -> bool { return mime.name() == name; })
478 == result.constEnd())
480 }
481}
482
484{
485#if QT_CONFIG(xmlstreamreader)
486 if (data.loaded)
487 return true;
488
489 auto it = m_mimetypeExtra.constFind(data.name);
490 if (it == m_mimetypeExtra.constEnd()) {
491 // load comment and globPatterns
492
493 // shared-mime-info since 1.3 lowercases the xml files
494 QString mimeFile = m_directory + u'/' + data.name.toLower() + ".xml"_L1;
495 if (!QFile::exists(mimeFile))
496 mimeFile = m_directory + u'/' + data.name + ".xml"_L1; // pre-1.3
497
498 QFile qfile(mimeFile);
499 if (!qfile.open(QFile::ReadOnly))
500 return false;
501
502 auto insertIt = m_mimetypeExtra.insert(data.name, MimeTypeExtra{});
503 it = insertIt;
504 MimeTypeExtra &extra = insertIt.value();
505 QString mainPattern;
506
507 QXmlStreamReader xml(&qfile);
508 if (xml.readNextStartElement()) {
509 if (xml.name() != "mime-type"_L1) {
510 return false;
511 }
512 const auto name = xml.attributes().value("type"_L1);
513 if (name.isEmpty())
514 return false;
515 if (name.compare(data.name, Qt::CaseInsensitive))
516 qWarning() << "Got name" << name << "in file" << mimeFile << "expected" << data.name;
517
518 while (xml.readNextStartElement()) {
519 const auto tag = xml.name();
520 if (tag == "comment"_L1) {
521 QString lang = xml.attributes().value("xml:lang"_L1).toString();
522 const QString text = xml.readElementText();
523 if (lang.isEmpty()) {
524 lang = "default"_L1; // no locale attribute provided, treat it as default.
525 }
526 extra.localeComments.insert(lang, text);
527 continue; // we called readElementText, so we're at the EndElement already.
528 } else if (tag == "glob-deleteall"_L1) { // as written out by shared-mime-info >= 0.70
529 extra.globPatterns.clear();
530 mainPattern.clear();
531 } else if (tag == "glob"_L1) { // as written out by shared-mime-info >= 0.70
532 const QString pattern = xml.attributes().value("pattern"_L1).toString();
533 if (mainPattern.isEmpty() && pattern.startsWith(u'*')) {
534 mainPattern = pattern;
535 }
536 appendIfNew(extra.globPatterns, pattern);
537 }
538 xml.skipCurrentElement();
539 }
540 Q_ASSERT(xml.name() == "mime-type"_L1);
541 }
542
543 // Let's assume that shared-mime-info is at least version 0.70
544 // Otherwise we would need 1) a version check, and 2) code for parsing patterns from the globs file.
545 if (!mainPattern.isEmpty() &&
546 (extra.globPatterns.isEmpty() || extra.globPatterns.constFirst() != mainPattern)) {
547 // ensure it's first in the list of patterns
548 extra.globPatterns.removeAll(mainPattern);
549 extra.globPatterns.prepend(mainPattern);
550 }
551 }
552 const MimeTypeExtra &e = it.value();
553 data.localeComments = e.localeComments;
554 data.globPatterns = e.globPatterns;
555 return true;
556#else
557 Q_UNUSED(data);
558 qWarning("Cannot load mime type since QXmlStreamReader is not available.");
559 return false;
560#endif // feature xmlstreamreader
561}
562
563// Binary search in the icons or generic-icons list
564QLatin1StringView QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset,
565 const QByteArray &inputMime)
566{
567 const int iconsListOffset = cacheFile->getUint32(posListOffset);
568 const int numIcons = cacheFile->getUint32(iconsListOffset);
569 int begin = 0;
570 int end = numIcons - 1;
571 while (begin <= end) {
572 const int medium = (begin + end) / 2;
573 const int off = iconsListOffset + 4 + 8 * medium;
574 const int mimeOffset = cacheFile->getUint32(off);
575 const char *mime = cacheFile->getCharStar(mimeOffset);
576 const int cmp = qstrcmp(mime, inputMime);
577 if (cmp < 0)
578 begin = medium + 1;
579 else if (cmp > 0)
580 end = medium - 1;
581 else {
582 const int iconOffset = cacheFile->getUint32(off + 4);
583 return QLatin1StringView(cacheFile->getCharStar(iconOffset));
584 }
585 }
586 return QLatin1StringView();
587}
588
590{
591 const QByteArray inputMime = data.name.toLatin1();
592 const QLatin1StringView icon = iconForMime(m_cacheFile.get(), PosIconsListOffset, inputMime);
593 if (!icon.isEmpty()) {
594 data.iconName = icon;
595 }
596}
597
599{
600 const QByteArray inputMime = data.name.toLatin1();
601 const QLatin1StringView icon = iconForMime(m_cacheFile.get(), PosGenericIconsListOffset, inputMime);
602 if (!icon.isEmpty()) {
603 data.genericIconName = icon;
604 }
605}
606
608
609#if QT_CONFIG(mimetype_database)
610static QString internalMimeFileName()
611{
612 return QStringLiteral("<internal MIME data>");
613}
614
616 : QMimeProviderBase(db, internalMimeFileName())
617{
618 static_assert(sizeof(mimetype_database), "Bundled MIME database is empty");
619 static_assert(sizeof(mimetype_database) <= MimeTypeDatabaseOriginalSize,
620 "Compressed MIME database is larger than the original size");
621 static_assert(MimeTypeDatabaseOriginalSize <= 16*1024*1024,
622 "Bundled MIME database is too big");
623 const char *data = reinterpret_cast<const char *>(mimetype_database);
624 qsizetype size = MimeTypeDatabaseOriginalSize;
625
626#ifdef MIME_DATABASE_IS_ZSTD
627 // uncompress with libzstd
628 std::unique_ptr<char []> uncompressed(new char[size]);
629 size = ZSTD_decompress(uncompressed.get(), size, mimetype_database, sizeof(mimetype_database));
630 Q_ASSERT(!ZSTD_isError(size));
631 data = uncompressed.get();
632#elif defined(MIME_DATABASE_IS_GZIP)
633 std::unique_ptr<char []> uncompressed(new char[size]);
634 z_stream zs = {};
635 zs.next_in = const_cast<Bytef *>(mimetype_database);
636 zs.avail_in = sizeof(mimetype_database);
637 zs.next_out = reinterpret_cast<Bytef *>(uncompressed.get());
638 zs.avail_out = size;
639
640 int res = inflateInit2(&zs, MAX_WBITS | 32);
641 Q_ASSERT(res == Z_OK);
642 res = inflate(&zs, Z_FINISH);
643 Q_ASSERT(res == Z_STREAM_END);
644 res = inflateEnd(&zs);
645 Q_ASSERT(res == Z_OK);
646
647 data = uncompressed.get();
648 size = zs.total_out;
649#endif
650
651 load(data, size);
652}
653#else // !QT_CONFIG(mimetype_database)
654// never called in release mode, but some debug builds may need
655// this to be defined.
658{
659 Q_UNREACHABLE();
660}
661#endif // QT_CONFIG(mimetype_database)
662
665{
666 ensureLoaded();
667}
668
670{
671}
672
674{
675 // If you change this method, adjust the logic in QMimeDatabasePrivate::loadProviders,
676 // which assumes isValid==false is only possible in QMimeBinaryProvider.
677 return true;
678}
679
681{
682#if QT_CONFIG(mimetype_database)
683 return m_directory == internalMimeFileName();
684#else
685 return false;
686#endif
687}
688
690{
691 return m_nameMimeTypeMap.value(name);
692}
693
695{
696 m_mimeTypeGlobs.matchingGlobs(fileName, result);
697}
698
699void QMimeXMLProvider::findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate)
700{
701 QString candidateName;
702 bool foundOne = false;
703 for (const QMimeMagicRuleMatcher &matcher : std::as_const(m_magicMatchers)) {
704 if (matcher.matches(data)) {
705 const int priority = matcher.priority();
706 if (priority > *accuracyPtr) {
707 *accuracyPtr = priority;
708 candidateName = matcher.mimetype();
709 foundOne = true;
710 }
711 }
712 }
713 if (foundOne)
714 candidate = mimeTypeForName(candidateName);
715}
716
718{
719 QStringList allFiles;
720 const QString packageDir = m_directory + QStringLiteral("/packages");
721 QDir dir(packageDir);
723 allFiles.reserve(files.size());
724 for (const QString &xmlFile : files)
725 allFiles.append(packageDir + u'/' + xmlFile);
726
727 if (m_allFiles == allFiles)
728 return;
729 m_allFiles = allFiles;
730
731 m_nameMimeTypeMap.clear();
732 m_aliases.clear();
733 m_parents.clear();
734 m_mimeTypeGlobs.clear();
735 m_magicMatchers.clear();
737
738 //qDebug() << "Loading" << m_allFiles;
739
740 for (const QString &file : std::as_const(allFiles))
741 load(file);
742}
743
745{
747 if (!load(fileName, &errorMessage))
748 qWarning("QMimeDatabase: Error loading %ls\n%ls", qUtf16Printable(fileName), qUtf16Printable(errorMessage));
749}
750
752{
755 if (errorMessage)
756 *errorMessage = "Cannot open "_L1 + fileName + ": "_L1 + file.errorString();
757 return false;
758 }
759
760 if (errorMessage)
762
763 QMimeTypeParser parser(*this);
764 return parser.parse(&file, fileName, errorMessage);
765}
766
767#if QT_CONFIG(mimetype_database)
768void QMimeXMLProvider::load(const char *data, qsizetype len)
769{
774 QMimeTypeParser parser(*this);
775 if (!parser.parse(&buffer, internalMimeFileName(), &errorMessage))
776 qWarning("QMimeDatabase: Error loading internal MIME data\n%s", qPrintable(errorMessage));
777}
778#endif
779
781{
782 m_mimeTypeGlobs.addGlob(glob);
783}
784
786{
787 Q_ASSERT(!mt.d.data()->fromCache);
788
789 QString name = mt.name();
790 if (mt.d->hasGlobDeleteAll)
792 m_nameMimeTypeMap.insert(mt.name(), mt);
793}
794
795/*
796 \a toExclude is a list of mime type names that should have the the glob patterns
797 associated with them cleared (because there are mime types with the same names
798 in a higher precedence Provider that have glob-deleteall tags).
799
800 This method is called from QMimeDatabasePrivate::loadProviders() to exclude mime
801 type glob patterns in lower precedence Providers.
802*/
804{
805 for (const auto &mt : toExclude) {
806 auto it = m_nameMimeTypeMap.find(mt);
807 if (it != m_nameMimeTypeMap.end())
808 it->d->globPatterns.clear();
809 m_mimeTypeGlobs.removeMimeType(mt);
810 }
811}
812
814{
815 for (const QString &parent : m_parents.value(mime)) {
816 if (!result.contains(parent))
817 result.append(parent);
818 }
819}
820
822{
823 m_parents[child].append(parent);
824}
825
827{
828 // Iterate through the whole hash. This method is rarely used.
829 for (const auto &[alias, mimeName] : std::as_const(m_aliases).asKeyValueRange()) {
830 if (mimeName == name)
831 appendIfNew(result, alias);
832 }
833}
834
836{
837 return m_aliases.value(name);
838}
839
841{
842 m_aliases.insert(alias, name);
843}
844
846{
847 if (result.isEmpty()) { // fast path
848 result = m_nameMimeTypeMap.values();
849 } else {
850 for (auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd() ; it != end ; ++it) {
851 const QString newMime = it.key();
852 if (std::find_if(result.constBegin(), result.constEnd(), [newMime](const QMimeType &mime) -> bool { return mime.name() == newMime; })
853 == result.constEnd())
854 result.append(it.value());
855 }
856 }
857}
858
860{
861 m_magicMatchers.append(matcher);
862}
863
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:394
\inmodule QtCore
Definition qchar.h:48
\inmodule QtCore\reentrant
Definition qdatetime.h:257
\inmodule QtCore
Definition qdir.h:19
@ Files
Definition qdir.h:22
@ NoDotAndDotDot
Definition qdir.h:43
T * data() const noexcept
Returns a pointer to the shared data object.
uchar * map(qint64 offset, qint64 size, MemoryMapFlags flags=NoOptions)
Maps size bytes of the file into memory starting at offset.
bool atEnd() const override
Returns true if the end of the file has been reached; otherwise returns false.
void close() override
Calls QFileDevice::flush() and closes the file.
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QDateTime lastModified() const
Returns the date and time when the file was last modified.
Definition qfileinfo.h:156
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
qint64 size() const override
\reimp
Definition qfile.cpp:1156
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1209
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition qhash.h:1088
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1205
T value(const Key &key) const noexcept
Definition qhash.h:1044
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
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:949
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
qint64 readLine(char *data, qint64 maxlen)
This function reads a line of ASCII characters from the device, up to a maximum of maxSize - 1 bytes,...
bool isOpen() const
Returns true if the device is open; otherwise returns false.
QString errorString() const
Returns a human-readable description of the last device error that occurred.
Definition qlist.h:74
void push_back(parameter_type t)
Definition qlist.h:672
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
const_iterator constFind(const Key &key) const
Definition qmap.h:654
void clear()
Definition qmap.h:288
const_iterator constEnd() const
Definition qmap.h:603
void addGlob(const QMimeGlobPattern &glob)
void removeMimeType(const QString &mimeType)
void matchingGlobs(const QString &fileName, QMimeGlobMatchResult &result) const
void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) override
QString resolveAlias(const QString &name) override
void findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) override
void ensureLoaded() override
bool isValid() override
void loadIcon(QMimeTypePrivate &) override
void addAliases(const QString &name, QStringList &result) override
virtual ~QMimeBinaryProvider()
void addAllMimeTypes(QList< QMimeType > &result) override
void addParents(const QString &mime, QStringList &result) override
void loadGenericIcon(QMimeTypePrivate &) override
QMimeBinaryProvider(QMimeDatabasePrivate *db, const QString &directory)
void excludeMimeTypeGlobs(const QStringList &toExclude) override
QMimeType mimeTypeForName(const QString &name) override
bool isInternalDatabase() const override
bool loadMimeTypePrivate(QMimeTypePrivate &) override
The QMimeGlobPattern class contains the glob pattern for file names for MIME type matching.
The QMimeMagicRuleMatcher class checks a number of rules based on operator "or".
static bool matchSubstring(const char *dataPtr, qsizetype dataSize, int rangeStart, int rangeLength, qsizetype valueLength, const char *valueData, const char *mask)
QStringList m_mimeTypesWithExcludedGlobs
QStringList m_mimeTypesWithDeletedGlobs
QMimeProviderBase(QMimeDatabasePrivate *db, const QString &directory)
bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage)
\inmodule QtCore
\inmodule QtCore
Definition qmimetype.h:25
QExplicitlySharedDataPointer< QMimeTypePrivate > d
Definition qmimetype.h:88
QString name
the name of the MIME type
Definition qmimetype.h:29
void addFileNameMatches(const QString &fileName, QMimeGlobMatchResult &result) override
void addGlobPattern(const QMimeGlobPattern &glob)
void addAliases(const QString &name, QStringList &result) override
void addAlias(const QString &alias, const QString &name)
void excludeMimeTypeGlobs(const QStringList &toExclude) override
bool load(const QString &fileName, QString *errorMessage)
void findByMagic(const QByteArray &data, int *accuracyPtr, QMimeType &candidate) override
void ensureLoaded() override
bool isValid() override
bool isInternalDatabase() const override
QString resolveAlias(const QString &name) override
void addParents(const QString &mime, QStringList &result) override
void addAllMimeTypes(QList< QMimeType > &result) override
QMimeType mimeTypeForName(const QString &name) override
QMimeXMLProvider(QMimeDatabasePrivate *db, InternalDatabaseEnum)
void addMagicMatcher(const QMimeMagicRuleMatcher &matcher)
void addMimeType(const QMimeType &mt)
void addParent(const QString &child, const QString &parent)
qsizetype size() const
Definition qset.h:50
void clear()
Definition qset.h:61
bool contains(const T &value) const
Definition qset.h:71
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
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
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
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
QString toLower() const &
Definition qstring.h:368
QString str
[2]
p1 load("image.bmp")
QString text
double e
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr const T & min(const T &a, const T &b)
Definition qnumeric.h:366
CaseSensitivity
@ CaseInsensitive
@ CaseSensitive
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
AudioChannelLayoutTag tag
constexpr T qFromBigEndian(T source)
Definition qendian.h:174
const char * mimeType
#define qWarning
Definition qlogging.h:162
static QMimeType mimeTypeForNameUnchecked(const QString &name)
@ PosReverseSuffixTreeOffset
@ PosGlobListOffset
@ PosIconsListOffset
@ PosLiteralListOffset
@ PosAliasListOffset
@ PosMagicListOffset
@ PosParentListOffset
@ PosGenericIconsListOffset
static void appendIfNew(QStringList &list, const QString &str)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLsizei dataSize
GLuint GLuint GLfloat weight
GLenum GLuint buffer
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLuint name
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLuint res
GLenum GLsizei len
GLuint64EXT * result
[6]
GLubyte * pattern
GLenum GLenum GLenum input
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
Int aligned(Int v, Int byteAlign)
#define qPrintable(string)
Definition qstring.h:1391
#define qUtf16Printable(string)
Definition qstring.h:1403
#define QStringLiteral(str)
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
unsigned short quint16
Definition qtypes.h:43
ptrdiff_t qsizetype
Definition qtypes.h:70
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3503
static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
Definition qzip.cpp:92
QList< int > list
[14]
QFile file
[0]
application x qt windows mime
[2]
QMimeDatabase db
[0]
static const auto matcher
[0]
QXmlStreamReader xml
[0]
QString dir
[11]
QStringList files
[8]
QLayoutItem * child
[0]
bool contains(const AT &t) const noexcept
Definition qlist.h:44
const char * getCharStar(int offset) const
quint16 getUint16(int offset) const
CacheFile(const QString &fileName)
quint32 getUint32(int offset) const
The QMimeGlobMatchResult class accumulates results from glob matching.
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent