KDECore
kmimeglobsfileparser.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 * Copyright 2007, 2010 David Faure <faure@kde.org> 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Library General Public 00006 * License as published by the Free Software Foundation; either 00007 * version 2 of the License, or (at your option) any later version. 00008 * 00009 * This library is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 * Library General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU Library General Public License 00015 * along with this library; see the file COPYING.LIB. If not, write to 00016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 * Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kmimeglobsfileparser_p.h" 00021 #include <kglobal.h> 00022 #include <kdeversion.h> 00023 #include <kmimetype.h> 00024 #include <kstandarddirs.h> 00025 #include "kmimetyperepository_p.h" 00026 #include <kdebug.h> 00027 #include <QtCore/QTextStream> 00028 #include <QtCore/QFile> 00029 00030 KMimeGlobsFileParser::KMimeGlobsFileParser() 00031 { 00032 } 00033 00034 KMimeGlobsFileParser::AllGlobs KMimeGlobsFileParser::parseGlobs() 00035 { 00036 const QStringList globFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", QString::fromLatin1("globs")); 00037 //kDebug() << globFiles; 00038 return parseGlobs(globFiles); 00039 } 00040 00041 KMimeGlobsFileParser::AllGlobs KMimeGlobsFileParser::parseGlobs(const QStringList& globFiles) 00042 { 00043 QStringList parsedFiles; 00044 return parseGlobFiles(globFiles, parsedFiles); 00045 } 00046 00047 KMimeGlobsFileParser::AllGlobs KMimeGlobsFileParser::parseGlobFiles(const QStringList& globFiles, QStringList& parsedFiles) 00048 { 00049 KMimeGlobsFileParser::AllGlobs allGlobs; 00050 QListIterator<QString> globIter(globFiles); 00051 globIter.toBack(); 00052 // At each level, we must be able to override (not just add to) the information that we read at higher levels 00053 // (if glob-deleteall is used). 00054 while (globIter.hasPrevious()) { // global first, then local 00055 Format format = OldGlobs; 00056 QString fileName = globIter.previous(); 00057 QString fileNamev2 = fileName + QLatin1Char('2'); // NOTE: this relies on u-m-d always generating the old globs file 00058 if (QFile::exists(fileNamev2)) { 00059 fileName = fileNamev2; 00060 format = Globs2WithWeight; 00061 } 00062 parsedFiles << fileName; 00063 QFile globFile(fileName); 00064 //kDebug() << "Now parsing" << fileName; 00065 parseGlobFile(&globFile, format, allGlobs); 00066 } 00067 return allGlobs; 00068 } 00069 00070 // uses a QIODevice to make unit tests possible 00071 bool KMimeGlobsFileParser::parseGlobFile(QIODevice* file, Format format, AllGlobs& globs) 00072 { 00073 if (!file->open(QIODevice::ReadOnly)) 00074 return false; 00075 00076 // If we're not going to get the "cs" flag because smi is too old, then we need to emulate it for *.C at least. 00077 const bool caseSensitiveHackNeeded = (KMimeType::sharedMimeInfoVersion() <= KDE_MAKE_VERSION(0, 60, 0)); 00078 00079 QTextStream stream(file); 00080 //stream.setCodec("UTF-8"); // should be all latin1 00081 QString lastMime, lastPattern; 00082 QString line; 00083 while (!stream.atEnd()) { 00084 line = stream.readLine(); 00085 if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) 00086 continue; 00087 00088 const QStringList fields = line.split(QLatin1Char(':'), QString::KeepEmptyParts); 00089 if (fields.count() < 2) // syntax error 00090 continue; 00091 00092 //kDebug() << "line=" << line; 00093 00094 QString mimeTypeName, pattern; 00095 QStringList flagList; 00096 int weight = 50; 00097 if (format == Globs2WithWeight) { 00098 if (fields.count() < 3) // syntax error 00099 continue; 00100 weight = fields[0].toInt(); 00101 mimeTypeName = fields[1]; 00102 pattern = fields[2]; 00103 const QString flagsStr = fields.value(3); // could be empty 00104 flagList = flagsStr.split(QLatin1Char(','), QString::SkipEmptyParts); 00105 } else { 00106 mimeTypeName = fields[0]; 00107 pattern = fields[1]; 00108 } 00109 Q_ASSERT(!pattern.isEmpty()); 00110 Q_ASSERT(!pattern.contains(QLatin1Char(':'))); 00111 00112 //kDebug() << " got:" << mimeTypeName << pattern; 00113 00114 if (lastMime == mimeTypeName && lastPattern == pattern) { 00115 // Ignore duplicates, especially important for those with no flags after a line with flags: 00116 // 50:text/x-csrc:*.c:cs 00117 // 50:text/x-csrc:*.c 00118 continue; 00119 } 00120 00121 bool caseSensitive = flagList.contains(QLatin1String("cs")); 00122 00123 if (caseSensitiveHackNeeded && (pattern == QLatin1String("*.C") || pattern == QLatin1String("*.c") || pattern == QLatin1String("core"))) 00124 caseSensitive = true; 00125 00126 if (pattern == QLatin1String("__NOGLOBS__")) { 00127 //kDebug() << "removing" << mimeTypeName; 00128 globs.removeMime(mimeTypeName); 00129 lastMime.clear(); 00130 } else { 00131 int flags = 0; 00132 if (caseSensitive) 00133 flags = KMimeTypeRepository::CaseSensitive; 00134 00135 //if (mimeTypeName == "text/plain") 00136 // kDebug() << "Adding pattern" << pattern << "to mimetype" << mimeTypeName << "from globs file, with weight" << weight; 00137 //if (pattern.toLower() == "*.c") 00138 // kDebug() << " Adding pattern" << pattern << "to mimetype" << mimeTypeName << "from globs file, with weight" << weight << "flags" << flags; 00139 globs.addGlob(Glob(mimeTypeName, weight, pattern, flags)); 00140 lastMime = mimeTypeName; 00141 lastPattern = pattern; 00142 } 00143 } 00144 return true; 00145 } 00146 00147 static bool isFastPattern(const QString& pattern) 00148 { 00149 // starts with "*.", has no other '*' and no other '.' 00150 return pattern.lastIndexOf(QLatin1Char('*')) == 0 00151 && pattern.lastIndexOf(QLatin1Char('.')) == 1 00152 // and contains no other special character 00153 && !pattern.contains(QLatin1Char('?')) 00154 && !pattern.contains(QLatin1Char('[')) 00155 ; 00156 } 00157 00158 void KMimeGlobsFileParser::AllGlobs::addGlob(const Glob& glob) 00159 { 00160 // Note that in each case, we check for duplicates to avoid inserting duplicated patterns. 00161 // This can happen when installing kde.xml and freedesktop.org.xml 00162 // in the same prefix, and they both have text/plain:*.txt 00163 // TODO: skipping for now; evaluate if performance problem 00164 00165 const QString &pattern = glob.pattern; 00166 Q_ASSERT(!pattern.isEmpty()); 00167 00168 //kDebug() << "pattern" << pattern << "glob.weight=" << glob.weight << "isFast=" << isFastPattern(pattern) << glob.flags; 00169 00170 // Store each patterns into either m_fastPatternDict (*.txt, *.html etc. with default weight 50) 00171 // or for the rest, like core.*, *.tar.bz2, *~, into highWeightPatternOffset (>50) 00172 // or lowWeightPatternOffset (<=50) 00173 00174 if (glob.weight == 50 && isFastPattern(pattern) && ((glob.flags & KMimeTypeRepository::CaseSensitive) == 0)) { 00175 // The bulk of the patterns is *.foo with weight 50 --> those go into the fast patterns hash. 00176 const QString extension = pattern.mid(2).toLower(); 00177 QStringList& patterns = m_fastPatterns[extension]; // find or create 00178 //if (!patterns.contains(glob.mimeType)) 00179 patterns.append(glob.mimeType); 00180 } else { 00181 Glob adjustedGlob(glob); 00182 if ((adjustedGlob.flags & KMimeTypeRepository::CaseSensitive) == 0) 00183 adjustedGlob.pattern = adjustedGlob.pattern.toLower(); 00184 if (adjustedGlob.weight > 50) { 00185 //if (!m_highWeightGlobs.hasPattern(adjustedGlob.mimeType, adjustedGlob.pattern)) 00186 m_highWeightGlobs.append(adjustedGlob); 00187 } else { 00188 //if (!m_lowWeightGlobs.hasPattern(adjustedGlob.mimeType, adjustedGlob.pattern)) 00189 m_lowWeightGlobs.append(adjustedGlob); 00190 } 00191 } 00192 } 00193 00194 KMimeGlobsFileParser::PatternsMap KMimeGlobsFileParser::AllGlobs::patternsMap() const 00195 { 00196 PatternsMap patMap; 00197 00198 // This is just to fill in KMimeType::patterns. This has no real effect 00199 // on the actual mimetype matching. 00200 00201 QHash<QString, QStringList>::const_iterator it = m_fastPatterns.begin(); 00202 const QHash<QString, QStringList>::const_iterator end = m_fastPatterns.end(); 00203 for (; it != end; ++it) { 00204 Q_FOREACH(const QString& mime, it.value()) 00205 patMap[mime].append(QString::fromLatin1("*.") + it.key()); 00206 } 00207 00208 Q_FOREACH(const Glob& glob, m_highWeightGlobs) 00209 patMap[glob.mimeType].append(glob.pattern); 00210 00211 Q_FOREACH(const Glob& glob, m_lowWeightGlobs) 00212 patMap[glob.mimeType].append(glob.pattern); 00213 00214 return patMap; 00215 } 00216 00217 void KMimeGlobsFileParser::AllGlobs::removeMime(const QString& mime) 00218 { 00219 QMutableHashIterator<QString, QStringList> it(m_fastPatterns); 00220 while (it.hasNext()) { 00221 it.next().value().removeAll(mime); 00222 } 00223 m_highWeightGlobs.removeMime(mime); 00224 m_lowWeightGlobs.removeMime(mime); 00225 }
KDE 4.6 API Reference