25 #include <kdeversion.h>
40 KMimeTypeRepository::KMimeTypeRepository()
41 : m_parentsMapLoaded(false),
42 m_magicFilesParsed(false),
43 m_aliasFilesParsed(false),
44 m_globsFilesParsed(false),
45 m_patternsMapCalculated(false),
46 m_mimeTypesChecked(false),
48 m_useFavIconsChecked(false),
49 m_sharedMimeInfoVersion(0),
50 m_mutex(QReadWriteLock::Recursive)
54 KMimeTypeRepository::~KMimeTypeRepository()
65 const QString filename = name + QLatin1String(
".xml");
71 if (name == QLatin1String(
"inode/directory"))
77 bool KMimeTypeRepository::checkMimeTypes()
81 return !globFiles.isEmpty();
86 return aliases().value(mime);
99 const int pattern_len = pattern.length();
102 const int len = filename.length();
104 const int starCount = pattern.count(QLatin1Char(
'*'));
107 if (pattern[0] == QLatin1Char(
'*') && pattern.indexOf(QLatin1Char(
'[')) == -1 && starCount == 1)
109 if ( len + 1 < pattern_len )
return false;
111 const QChar *c1 = pattern.unicode() + pattern_len - 1;
112 const QChar *c2 = filename.unicode() + len - 1;
114 while (cnt < pattern_len && *c1-- == *c2--)
116 return cnt == pattern_len;
120 if (starCount == 1 && pattern[pattern_len - 1] == QLatin1Char(
'*')) {
121 if ( len + 1 < pattern_len )
return false;
122 if (pattern[0] == QLatin1Char(
'*'))
123 return filename.indexOf(pattern.mid(1, pattern_len - 2)) != -1;
125 const QChar *c1 = pattern.unicode();
126 const QChar *c2 = filename.unicode();
128 while (cnt < pattern_len && *c1++ == *c2++)
130 return cnt == pattern_len;
134 if (pattern.indexOf(QLatin1Char(
'[')) == -1 && starCount == 0 && pattern.indexOf(QLatin1Char(
'?')))
135 return (pattern == filename);
139 rx.setPatternSyntax(QRegExp::Wildcard);
140 return rx.exactMatch(filename);
144 void KMimeTypeRepository::findFromOtherPatternList(
QStringList& matchingMimeTypes,
151 int matchingPatternLength = 0;
152 qint32 lastMatchedWeight = 0;
153 if (!highWeight && !matchingMimeTypes.isEmpty()) {
155 matchingPatternLength = foundExt.length() + 2;
156 lastMatchedWeight = 50;
162 const QString lowerCaseFileName = fileName.toLower();
164 KMimeGlobsFileParser::GlobList::const_iterator it = patternList.constBegin();
165 const KMimeGlobsFileParser::GlobList::const_iterator end = patternList.constEnd();
166 for ( ; it != end; ++it ) {
170 if (glob.
weight < lastMatchedWeight)
172 if (lastMatchedWeight > 0 && glob.
weight > lastMatchedWeight)
174 << glob.
weight <<
">" << lastMatchedWeight;
176 if (glob.
pattern.length() < matchingPatternLength) {
178 }
else if (glob.
pattern.length() > matchingPatternLength) {
180 matchingMimeTypes.clear();
182 matchingPatternLength = glob.
pattern.length();
184 matchingMimeTypes.push_back(glob.
mimeType);
185 if (glob.
pattern.startsWith(QLatin1String(
"*.")))
186 foundExt = glob.
pattern.mid(2);
193 m_mutex.lockForWrite();
197 QReadLocker lock(&m_mutex);
201 findFromOtherPatternList(matchingMimeTypes, fileName, foundExt,
true);
202 if (matchingMimeTypes.isEmpty()) {
206 const int lastDot = fileName.lastIndexOf(QLatin1Char(
'.'));
208 const int ext_len = fileName.length() - lastDot - 1;
209 const QString simpleExtension = fileName.right( ext_len ).toLower();
212 matchingMimeTypes = m_globs.
m_fastPatterns.value(simpleExtension);
213 if (!matchingMimeTypes.isEmpty()) {
214 foundExt = simpleExtension;
221 findFromOtherPatternList(matchingMimeTypes, fileName, foundExt,
false);
223 if (pMatchingExtension)
224 *pMatchingExtension = foundExt;
225 return matchingMimeTypes;
230 Q_ASSERT(device->isOpen());
231 const qint64 deviceSize = device->size();
232 if (deviceSize == 0) {
237 if (beginning.isEmpty()) {
239 const qint64 dataNeeded = qMin(deviceSize, (
qint64) 16384);
240 beginning.resize(dataNeeded);
241 if (!device->seek(0) || device->read(beginning.data(), dataNeeded) == -1) {
246 m_mutex.lockForWrite();
247 if (!m_magicFilesParsed) {
249 m_magicFilesParsed =
true;
255 QReadLocker lock(&m_mutex);
257 if (rule.
match(device, deviceSize, beginning)) {
279 const QString myGroup = mimeTypeName.left(mimeTypeName.indexOf(QLatin1Char(
'/')));
281 if (myGroup == QLatin1String(
"text") && mimeTypeName != QLatin1String(
"text/plain"))
282 return QLatin1String(
"text/plain");
284 if (myGroup != QLatin1String(
"inode") &&
286 myGroup != QLatin1String(
"all") && myGroup != QLatin1String(
"fonts") && myGroup != QLatin1String(
"print") && myGroup != QLatin1String(
"uri")
287 && mimeTypeName != QLatin1String(
"application/octet-stream")) {
288 return QLatin1String(
"application/octet-stream");
295 QWriteLocker lock(&m_mutex);
296 if (!m_parentsMapLoaded) {
297 m_parentsMapLoaded =
true;
298 Q_ASSERT(m_parents.isEmpty());
302 Q_FOREACH(
const QString& fileName, subclassFiles) {
304 QFile qfile( fileName );
306 if (qfile.open(QIODevice::ReadOnly)) {
307 QTextStream stream(&qfile);
308 stream.setCodec(
"ISO 8859-1");
309 while (!stream.atEnd()) {
310 const QString line = stream.readLine();
311 if (line.isEmpty() || line[0] == QLatin1Char(
'#'))
313 const int pos = line.indexOf(QLatin1Char(
' '));
316 const QString derivedTypeName = line.left(pos);
319 kWarning(7012) << fileName <<
" refers to unknown mimetype " << derivedTypeName;
321 const QString parentTypeName = line.mid(pos+1);
322 Q_ASSERT(!parentTypeName.isEmpty());
324 m_parents[derivedTypeName].append(parentTypeName);
332 if (parents.isEmpty()) {
334 if (!myParent.isEmpty())
335 parents.append(myParent);
341 #include <arpa/inet.h>
351 void KMimeTypeRepository::parseMagic()
355 QListIterator<QString> magicIter( magicFiles );
357 while (magicIter.hasPrevious()) {
358 const QString fileName = magicIter.previous();
359 QFile magicFile(fileName);
361 if (magicFile.open(QIODevice::ReadOnly))
362 m_magicRules += parseMagicFile(&magicFile, fileName);
370 while (file->getChar(&ch)) {
371 if (ch < '0' || ch >
'9')
373 value = 10 * value + ch -
'0';
380 #define MAKE_LITTLE_ENDIAN16(val) val = (quint16)(((quint16)(val) << 8)|((quint16)(val) >> 8))
382 #define MAKE_LITTLE_ENDIAN32(val) \
383 val = (((quint32)(val) & 0xFF000000U) >> 24) | \
384 (((quint32)(val) & 0x00FF0000U) >> 8) | \
385 (((quint32)(val) & 0x0000FF00U) << 8) | \
386 (((quint32)(val) & 0x000000FFU) << 24)
391 QByteArray
header = file->read(12);
392 if (header != QByteArray::fromRawData(
"MIME-Magic\0\n", 12)) {
402 bool chOk = file->getChar(&ch);
404 if (!chOk || ch ==
'[') {
406 if (!mimeTypeName.isEmpty()) {
409 mimeTypeName.clear();
415 const QString line = QString::fromLatin1(file->readLine());
416 const int pos = line.indexOf(QLatin1Char(
':'));
419 <<
" ':' not present in section name" << endl;
422 priority = line.left(pos).toInt();
423 mimeTypeName = line.mid(pos+1);
424 mimeTypeName = mimeTypeName.left(mimeTypeName.length()-2);
436 kWarning(
servicesDebugArea()) <<
"Invalid magic file " << fileName <<
" '>' not found, got " << ch <<
" at pos " << file->pos();
449 char lengthBuffer[2];
450 if (file->read(lengthBuffer, 2) != 2)
452 const short valueLength = ntohs(*(
short*)lengthBuffer);
456 match.
m_data.resize(valueLength);
457 if (file->read(match.
m_data.data(), valueLength) != valueLength)
461 bool invalidLine =
false;
463 if (!file->getChar(&ch))
473 match.
m_mask.resize(valueLength);
474 if (file->read(match.
m_mask.data(), valueLength) != valueLength)
476 if (!file->getChar(&ch))
497 while (ch !=
'\n' && !file->atEnd()) {
504 if (ch ==
'\n' || invalidLine)
509 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
512 if ((wordSize != 2 && wordSize != 4) || (valueLength % wordSize != 0))
514 char* data = match.
m_data.data();
516 for (
int i = 0; i < valueLength; i += wordSize) {
519 else if (wordSize == 4)
521 if (!match.
m_mask.isEmpty()) {
524 else if (wordSize == 4)
533 matches.append(match);
537 for (
int i = 1 ; i <
indent; ++i) {
551 QWriteLocker lock(&m_mutex);
552 if (!m_aliasFilesParsed) {
553 m_aliasFilesParsed =
true;
556 Q_FOREACH(
const QString& fileName, aliasFiles) {
557 QFile qfile(fileName);
559 if (qfile.open(QIODevice::ReadOnly)) {
560 QTextStream stream(&qfile);
561 stream.setCodec(
"ISO 8859-1");
562 while (!stream.atEnd()) {
563 const QString line = stream.readLine();
564 if (line.isEmpty() || line[0] == QLatin1Char(
'#'))
566 const int pos = line.indexOf(QLatin1Char(
' '));
569 const QString aliasTypeName = line.left(pos);
570 const QString parentTypeName = line.mid(pos+1);
571 Q_ASSERT(!aliasTypeName.isEmpty());
572 Q_ASSERT(!parentTypeName.isEmpty());
579 m_aliases.insert(aliasTypeName, parentTypeName);
589 void KMimeTypeRepository::parseGlobs()
591 if (!m_globsFilesParsed) {
592 m_globsFilesParsed =
true;
600 QWriteLocker lock(&m_mutex);
601 if (!m_patternsMapCalculated) {
602 m_patternsMapCalculated =
true;
606 return m_patterns.value(mimeType);
612 "Could not find mime types:\n<resource>%2</resource>", _types.count(), _types.join(QLatin1String(
"</resource>\n<resource>")) ) );
617 QWriteLocker lock(&m_mutex);
618 if (m_mimeTypesChecked)
621 m_mimeTypesChecked =
true;
625 if (!checkMimeTypes()) {
630 "Check that shared-mime-info is installed, and that XDG_DATA_DIRS is not set, or includes /usr/share."));
637 missingMimeTypes.append(QLatin1String(
"inode/directory"));
642 missingMimeTypes.append(QLatin1String(
"inode/blockdevice"));
644 missingMimeTypes.append(QLatin1String(
"inode/chardevice"));
646 missingMimeTypes.append(QLatin1String(
"inode/socket"));
648 missingMimeTypes.append(QLatin1String(
"inode/fifo"));
651 missingMimeTypes.append(QLatin1String(
"application/x-shellscript"));
653 missingMimeTypes.append(QLatin1String(
"application/x-executable"));
655 missingMimeTypes.append(QLatin1String(
"application/x-desktop"));
657 if (!missingMimeTypes.isEmpty())
663 QWriteLocker lock(&m_mutex);
664 if (!m_defaultMimeType) {
668 m_defaultMimeType = mime;
673 m_defaultMimeType =
new KMimeType(pathDefaultMimeType, defaultMimeType, QLatin1String(
"mime"));
676 return m_defaultMimeType;
684 m_mutex.lockForWrite();
685 if (!m_useFavIconsChecked) {
686 m_useFavIconsChecked =
true;
688 m_useFavIcons = cg.
readEntry(
"EnableFavicon",
true);
691 return m_useFavIcons;
696 #if defined (Q_OS_FREEBSD)
697 paths << QLatin1String(
"/usr/local/libdata/pkgconfig");
698 #elif defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_SOLARIS)
699 paths << QLatin1String(
"/usr/local/lib/pkgconfig");
700 #elif defined (Q_OS_UNIX)
701 paths << QLatin1String(
"/usr/share/pkgconfig");
709 if (!versionFiles.isEmpty()) {
710 QFile file(versionFiles.first());
711 if (file.open(QIODevice::ReadOnly)) {
712 const QByteArray line = file.readLine().simplified();
713 QRegExp versionRe(QString::fromLatin1(
"(\\d+)\\.(\\d+)(\\.(\\d+))?"));
714 if (versionRe.indexIn(QString::fromLocal8Bit(line)) > -1) {
715 return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
725 const QByteArray pkgConfigPath = qgetenv(
"PKG_CONFIG_PATH");
726 if (!pkgConfigPath.isEmpty()) {
727 paths << QFile::decodeName(pkgConfigPath).split(QLatin1Char(
':'), QString::SkipEmptyParts);
733 Q_FOREACH(
const QString& path, paths) {
734 const QString fileName = path + QLatin1String(
"/shared-mime-info.pc");
735 if (!QFile::exists(fileName)) {
739 QFile file (fileName);
740 if (!file.open(QIODevice::ReadOnly)) {
744 while (!file.atEnd()) {
745 const QByteArray line = file.readLine().simplified();
746 if (!line.startsWith(
"Version")) {
749 QRegExp versionRe(QString::fromLatin1(
"Version: (\\d+)\\.(\\d+)(\\.(\\d+))?"));
750 if (versionRe.indexIn(QString::fromLocal8Bit(line)) > -1) {
751 return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
767 smi.start(umd,
QStringList() << QString::fromLatin1(
"-v"));
768 if (smi.waitForStarted() && smi.waitForFinished()) {
769 const QString out = QString::fromLocal8Bit(smi.readAllStandardError());
770 QRegExp versionRe(QString::fromLatin1(
"update-mime-database \\(shared-mime-info\\) (\\d+)\\.(\\d+)(\\.(\\d+))?"));
771 if (versionRe.indexIn(out) > -1) {
772 return KDE_MAKE_VERSION(versionRe.cap(1).toInt(), versionRe.cap(2).toInt(), versionRe.cap(4).toInt());
784 m_mutex.lockForWrite();
785 if (m_sharedMimeInfoVersion == 0)
788 return m_sharedMimeInfoVersion;