25 #include <kdeversion.h>
41 KMimeTypeRepository::KMimeTypeRepository()
42 : m_parentsMapLoaded(false),
43 m_magicFilesParsed(false),
44 m_aliasFilesParsed(false),
45 m_globsFilesParsed(false),
46 m_patternsMapCalculated(false),
47 m_mimeTypesChecked(false),
49 m_useFavIconsChecked(false),
50 m_sharedMimeInfoVersion(0),
51 m_mutex(QReadWriteLock::Recursive)
55 KMimeTypeRepository::~KMimeTypeRepository()
66 const QString filename = name + QLatin1String(
".xml");
72 if (name == QLatin1String(
"inode/directory"))
78 bool KMimeTypeRepository::checkMimeTypes()
82 return !globFiles.isEmpty();
87 return aliases().value(mime);
100 const int pattern_len = pattern.length();
103 const int len = filename.length();
105 const int starCount = pattern.count(QLatin1Char(
'*'));
108 if (pattern[0] == QLatin1Char(
'*') && pattern.indexOf(QLatin1Char(
'[')) == -1 && starCount == 1)
110 if ( len + 1 < pattern_len )
return false;
112 const QChar *c1 = pattern.unicode() + pattern_len - 1;
113 const QChar *c2 = filename.unicode() + len - 1;
115 while (cnt < pattern_len && *c1-- == *c2--)
117 return cnt == pattern_len;
121 if (starCount == 1 && pattern[pattern_len - 1] == QLatin1Char(
'*')) {
122 if ( len + 1 < pattern_len )
return false;
123 if (pattern[0] == QLatin1Char(
'*'))
124 return filename.indexOf(pattern.mid(1, pattern_len - 2)) != -1;
126 const QChar *c1 = pattern.unicode();
127 const QChar *c2 = filename.unicode();
129 while (cnt < pattern_len && *c1++ == *c2++)
131 return cnt == pattern_len;
135 if (pattern.indexOf(QLatin1Char(
'[')) == -1 && starCount == 0 && pattern.indexOf(QLatin1Char(
'?')))
136 return (pattern == filename);
140 rx.setPatternSyntax(QRegExp::Wildcard);
141 return rx.exactMatch(filename);
145 void KMimeTypeRepository::findFromOtherPatternList(
QStringList& matchingMimeTypes,
152 int matchingPatternLength = 0;
153 qint32 lastMatchedWeight = 0;
154 if (!highWeight && !matchingMimeTypes.isEmpty()) {
156 matchingPatternLength = foundExt.length() + 2;
157 lastMatchedWeight = 50;
163 const QString lowerCaseFileName = fileName.toLower();
165 KMimeGlobsFileParser::GlobList::const_iterator it = patternList.constBegin();
166 const KMimeGlobsFileParser::GlobList::const_iterator end = patternList.constEnd();
167 for ( ; it != end; ++it ) {
171 if (glob.
weight < lastMatchedWeight)
173 if (lastMatchedWeight > 0 && glob.
weight > lastMatchedWeight)
175 << glob.
weight <<
">" << lastMatchedWeight;
177 if (glob.
pattern.length() < matchingPatternLength) {
179 }
else if (glob.
pattern.length() > matchingPatternLength) {
181 matchingMimeTypes.clear();
183 matchingPatternLength = glob.
pattern.length();
185 matchingMimeTypes.push_back(glob.
mimeType);
186 if (glob.
pattern.startsWith(QLatin1String(
"*.")))
187 foundExt = glob.
pattern.mid(2);
194 m_mutex.lockForWrite();
198 QReadLocker lock(&m_mutex);
202 findFromOtherPatternList(matchingMimeTypes, fileName, foundExt,
true);
203 if (matchingMimeTypes.isEmpty()) {
207 const int lastDot = fileName.lastIndexOf(QLatin1Char(
'.'));
209 const int ext_len = fileName.length() - lastDot - 1;
210 const QString simpleExtension = fileName.right( ext_len ).toLower();
213 matchingMimeTypes = m_globs.
m_fastPatterns.value(simpleExtension);
214 if (!matchingMimeTypes.isEmpty()) {
215 foundExt = simpleExtension;
222 findFromOtherPatternList(matchingMimeTypes, fileName, foundExt,
false);
224 if (pMatchingExtension)
225 *pMatchingExtension = foundExt;
226 return matchingMimeTypes;
231 Q_ASSERT(device->isOpen());
232 const qint64 deviceSize = device->size();
233 if (deviceSize == 0) {
238 if (beginning.isEmpty()) {
240 const qint64 dataNeeded = qMin(deviceSize, (
qint64) 16384);
241 beginning.resize(dataNeeded);
242 if (!device->seek(0) || device->read(beginning.data(), dataNeeded) == -1) {
247 m_mutex.lockForWrite();
248 if (!m_magicFilesParsed) {
250 m_magicFilesParsed =
true;
256 QReadLocker lock(&m_mutex);
258 if (rule.
match(device, deviceSize, beginning)) {
280 const QString myGroup = mimeTypeName.left(mimeTypeName.indexOf(QLatin1Char(
'/')));
282 if (myGroup == QLatin1String(
"text") && mimeTypeName != QLatin1String(
"text/plain"))
283 return QLatin1String(
"text/plain");
285 if (myGroup != QLatin1String(
"inode") &&
287 myGroup != QLatin1String(
"all") && myGroup != QLatin1String(
"fonts") && myGroup != QLatin1String(
"print") && myGroup != QLatin1String(
"uri")
288 && mimeTypeName != QLatin1String(
"application/octet-stream")) {
289 return QLatin1String(
"application/octet-stream");
296 QWriteLocker lock(&m_mutex);
297 if (!m_parentsMapLoaded) {
298 m_parentsMapLoaded =
true;
299 Q_ASSERT(m_parents.isEmpty());
303 Q_FOREACH(
const QString& fileName, subclassFiles) {
305 QFile qfile( fileName );
307 if (qfile.open(QIODevice::ReadOnly)) {
308 QTextStream stream(&qfile);
309 stream.setCodec(
"ISO 8859-1");
310 while (!stream.atEnd()) {
311 const QString line = stream.readLine();
312 if (line.isEmpty() || line[0] == QLatin1Char(
'#'))
314 const int pos = line.indexOf(QLatin1Char(
' '));
317 const QString derivedTypeName = line.left(pos);
320 kWarning(7012) << fileName <<
" refers to unknown mimetype " << derivedTypeName;
322 const QString parentTypeName = line.mid(pos+1);
323 Q_ASSERT(!parentTypeName.isEmpty());
325 m_parents[derivedTypeName].append(parentTypeName);
333 if (parents.isEmpty()) {
335 if (!myParent.isEmpty())
336 parents.append(myParent);
342 #include <arpa/inet.h>
352 void KMimeTypeRepository::parseMagic()
356 QListIterator<QString> magicIter( magicFiles );
358 while (magicIter.hasPrevious()) {
359 const QString fileName = magicIter.previous();
360 QFile magicFile(fileName);
362 if (magicFile.open(QIODevice::ReadOnly))
363 m_magicRules += parseMagicFile(&magicFile, fileName);
371 while (file->getChar(&ch)) {
372 if (ch < '0' || ch >
'9')
374 value = 10 * value + ch -
'0';
381 #define MAKE_LITTLE_ENDIAN16(val) val = (quint16)(((quint16)(val) << 8)|((quint16)(val) >> 8))
383 #define MAKE_LITTLE_ENDIAN32(val) \
384 val = (((quint32)(val) & 0xFF000000U) >> 24) | \
385 (((quint32)(val) & 0x00FF0000U) >> 8) | \
386 (((quint32)(val) & 0x0000FF00U) << 8) | \
387 (((quint32)(val) & 0x000000FFU) << 24)
392 QByteArray
header = file->read(12);
393 if (header != QByteArray::fromRawData(
"MIME-Magic\0\n", 12)) {
403 bool chOk = file->getChar(&ch);
405 if (!chOk || ch ==
'[') {
407 if (!mimeTypeName.isEmpty()) {
410 mimeTypeName.clear();
416 const QString line = QString::fromLatin1(file->readLine());
417 const int pos = line.indexOf(QLatin1Char(
':'));
420 <<
" ':' not present in section name" << endl;
423 priority = line.left(pos).toInt();
424 mimeTypeName = line.mid(pos+1);
425 mimeTypeName = mimeTypeName.left(mimeTypeName.length()-2);
437 kWarning(
servicesDebugArea()) <<
"Invalid magic file " << fileName <<
" '>' not found, got " << ch <<
" at pos " << file->pos();
451 if (file->read(reinterpret_cast<char*>(&lengthBuffer), 2) != 2)
453 const qint16 valueLength = qFromBigEndian(lengthBuffer);
457 match.
m_data.resize(valueLength);
458 if (file->read(match.
m_data.data(), valueLength) != valueLength)
462 bool invalidLine =
false;
464 if (!file->getChar(&ch))
474 match.
m_mask.resize(valueLength);
475 if (file->read(match.
m_mask.data(), valueLength) != valueLength)
477 if (!file->getChar(&ch))
498 while (ch !=
'\n' && !file->atEnd()) {
505 if (ch ==
'\n' || invalidLine)
510 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
513 if ((wordSize != 2 && wordSize != 4) || (valueLength % wordSize != 0))
515 char* data = match.
m_data.data();
517 for (
int i = 0; i < valueLength; i += wordSize) {
520 else if (wordSize == 4)
522 if (!match.
m_mask.isEmpty()) {
525 else if (wordSize == 4)
534 matches.append(match);
538 for (
int i = 1 ; i <
indent; ++i) {
552 QWriteLocker lock(&m_mutex);
553 if (!m_aliasFilesParsed) {
554 m_aliasFilesParsed =
true;
557 Q_FOREACH(
const QString& fileName, aliasFiles) {
558 QFile qfile(fileName);
560 if (qfile.open(QIODevice::ReadOnly)) {
561 QTextStream stream(&qfile);
562 stream.setCodec(
"ISO 8859-1");
563 while (!stream.atEnd()) {
564 const QString line = stream.readLine();
565 if (line.isEmpty() || line[0] == QLatin1Char(
'#'))
567 const int pos = line.indexOf(QLatin1Char(
' '));
570 const QString aliasTypeName = line.left(pos);
571 const QString parentTypeName = line.mid(pos+1);
572 Q_ASSERT(!aliasTypeName.isEmpty());
573 Q_ASSERT(!parentTypeName.isEmpty());
580 m_aliases.insert(aliasTypeName, parentTypeName);
590 void KMimeTypeRepository::parseGlobs()
592 if (!m_globsFilesParsed) {
593 m_globsFilesParsed =
true;
601 QWriteLocker lock(&m_mutex);
602 if (!m_patternsMapCalculated) {
603 m_patternsMapCalculated =
true;
607 return m_patterns.value(mimeType);
613 "Could not find mime types:\n<resource>%2</resource>", _types.count(), _types.join(QLatin1String(
"</resource>\n<resource>")) ) );
618 QWriteLocker lock(&m_mutex);
619 if (m_mimeTypesChecked)
622 m_mimeTypesChecked =
true;
626 if (!checkMimeTypes()) {
631 "Check that shared-mime-info is installed, and that XDG_DATA_DIRS is not set, or includes /usr/share."));
638 missingMimeTypes.append(QLatin1String(
"inode/directory"));
643 missingMimeTypes.append(QLatin1String(
"inode/blockdevice"));
645 missingMimeTypes.append(QLatin1String(
"inode/chardevice"));
647 missingMimeTypes.append(QLatin1String(
"inode/socket"));
649 missingMimeTypes.append(QLatin1String(
"inode/fifo"));
652 missingMimeTypes.append(QLatin1String(
"application/x-shellscript"));
654 missingMimeTypes.append(QLatin1String(
"application/x-executable"));
656 missingMimeTypes.append(QLatin1String(
"application/x-desktop"));
658 if (!missingMimeTypes.isEmpty())
664 QWriteLocker lock(&m_mutex);
665 if (!m_defaultMimeType) {
669 m_defaultMimeType = mime;
674 m_defaultMimeType =
new KMimeType(pathDefaultMimeType, defaultMimeType, QLatin1String(
"mime"));
677 return m_defaultMimeType;
685 m_mutex.lockForWrite();
686 if (!m_useFavIconsChecked) {
687 m_useFavIconsChecked =
true;
689 m_useFavIcons = cg.
readEntry(
"EnableFavicon",
true);
692 return m_useFavIcons;
697 #if defined (Q_OS_FREEBSD)
698 paths << QLatin1String(
"/usr/local/libdata/pkgconfig");
699 #elif defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_SOLARIS)
700 paths << QLatin1String(
"/usr/local/lib/pkgconfig");
701 #elif defined (Q_OS_UNIX)
702 paths << QLatin1String(
"/usr/share/pkgconfig");
710 if (!versionFiles.isEmpty()) {
711 QFile file(versionFiles.first());
712 if (file.open(QIODevice::ReadOnly)) {
713 const QByteArray line = file.readLine().simplified();
714 QRegExp versionRe(QString::fromLatin1(
"(\\d+)\\.(\\d+)(\\.(\\d+))?"));
715 if (versionRe.indexIn(QString::fromLocal8Bit(line)) > -1) {
716 return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
726 const QByteArray pkgConfigPath = qgetenv(
"PKG_CONFIG_PATH");
727 if (!pkgConfigPath.isEmpty()) {
728 paths << QFile::decodeName(pkgConfigPath).split(QLatin1Char(
':'), QString::SkipEmptyParts);
734 Q_FOREACH(
const QString& path, paths) {
735 const QString fileName = path + QLatin1String(
"/shared-mime-info.pc");
736 if (!QFile::exists(fileName)) {
740 QFile file (fileName);
741 if (!file.open(QIODevice::ReadOnly)) {
745 while (!file.atEnd()) {
746 const QByteArray line = file.readLine().simplified();
747 if (!line.startsWith(
"Version")) {
750 QRegExp versionRe(QString::fromLatin1(
"Version: (\\d+)\\.(\\d+)(\\.(\\d+))?"));
751 if (versionRe.indexIn(QString::fromLocal8Bit(line)) > -1) {
752 return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
768 smi.start(umd,
QStringList() << QString::fromLatin1(
"-v"));
769 if (smi.waitForStarted() && smi.waitForFinished()) {
770 const QString out = QString::fromLocal8Bit(smi.readAllStandardError());
771 QRegExp versionRe(QString::fromLatin1(
"update-mime-database \\(shared-mime-info\\) (\\d+)\\.(\\d+)(\\.(\\d+))?"));
772 if (versionRe.indexIn(out) > -1) {
773 return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
785 m_mutex.lockForWrite();
786 if (m_sharedMimeInfoVersion == 0)
789 return m_sharedMimeInfoVersion;