• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

kmimetypefactory.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
00003  *  Copyright (C) 2006 David Faure <faure@kde.org>
00004  *
00005  *  This library is free software; you can redistribute it and/or
00006  *  modify it under the terms of the GNU Library General Public
00007  *  License version 2 as published by the Free Software Foundation;
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 "kmimetypefactory.h"
00021 #include "kmimetype.h"
00022 #include "kfoldermimetype.h"
00023 #include <ksycoca.h>
00024 #include <ksycocadict.h>
00025 #include <kshell.h>
00026 #include <kdebug.h>
00027 
00029 
00030 KMimeTypeFactory::KMimeTypeFactory()
00031     : KSycocaFactory( KST_KMimeTypeFactory ),
00032       m_highWeightPatternsLoaded(false),
00033       m_lowWeightPatternsLoaded(false),
00034       m_magicFilesParsed(false)
00035 {
00036     _self = this;
00037     m_fastPatternOffset = 0;
00038     m_highWeightPatternOffset = 0;
00039     m_lowWeightPatternOffset = 0;
00040     if (m_str) {
00041         // Read Header
00042         qint32 i;
00043         (*m_str) >> i;
00044         m_fastPatternOffset = i;
00045         (*m_str) >> i;
00046         // that's the old m_otherPatternOffset, kept for compat but unused
00047 
00048         // alias map
00049         qint32 n;
00050         (*m_str) >> n;
00051         QString str1, str2;
00052         for(;n;n--) {
00053             KSycocaEntry::read(*m_str, str1);
00054             KSycocaEntry::read(*m_str, str2);
00055             m_aliases.insert(str1, str2);
00056         }
00057 
00058         (*m_str) >> i;
00059         m_highWeightPatternOffset = i;
00060         (*m_str) >> i;
00061         m_lowWeightPatternOffset = i;
00062 
00063         const int saveOffset = m_str->device()->pos();
00064         // Init index tables
00065         m_fastPatternDict = new KSycocaDict(m_str, m_fastPatternOffset);
00066         m_str->device()->seek(saveOffset);
00067 
00068     } else {
00069         // Build new database
00070         m_fastPatternDict = new KSycocaDict();
00071     }
00072 }
00073 
00074 KMimeTypeFactory::~KMimeTypeFactory()
00075 {
00076     _self = 0;
00077     delete m_fastPatternDict;
00078 }
00079 
00080 KMimeTypeFactory * KMimeTypeFactory::self()
00081 {
00082     if (!_self)
00083         _self = new KMimeTypeFactory();
00084     return _self;
00085 }
00086 
00087 KMimeType::Ptr KMimeTypeFactory::findMimeTypeByName(const QString &_name, KMimeType::FindByNameOption options)
00088 {
00089     if (!sycocaDict()) return KMimeType::Ptr(); // Error!
00090     assert (!KSycoca::self()->isBuilding());
00091 
00092     QString name = _name;
00093     if (options & KMimeType::ResolveAliases) {
00094         QMap<QString, QString>::const_iterator it = m_aliases.constFind(_name);
00095         if (it != m_aliases.constEnd())
00096             name = *it;
00097     }
00098 
00099     int offset = sycocaDict()->find_string( name );
00100     if (!offset) return KMimeType::Ptr(); // Not found
00101     KMimeType::Ptr newMimeType(createEntry(offset));
00102 
00103     // Check whether the dictionary was right.
00104     if (newMimeType && (newMimeType->name() != name))
00105     {
00106         // No it wasn't...
00107         newMimeType = 0; // Not found
00108     }
00109     return newMimeType;
00110 }
00111 
00112 bool KMimeTypeFactory::checkMimeTypes()
00113 {
00114    QDataStream *str = KSycoca::self()->findFactory( factoryId() );
00115    if (!str) return false;
00116 
00117    // check if there are mimetypes
00118    return !isEmpty();
00119 }
00120 
00121 KMimeType * KMimeTypeFactory::createEntry(int offset) const
00122 {
00123    KMimeType *newEntry = 0;
00124    KSycocaType type;
00125    QDataStream *str = KSycoca::self()->findEntry(offset, type);
00126    if (!str) return 0;
00127 
00128    switch(type)
00129    {
00130      case KST_KMimeType:
00131      case KST_KDEDesktopMimeType: // old, compat only
00132         newEntry = new KMimeType(*str, offset);
00133         break;
00134      case KST_KFolderMimeType:
00135         newEntry = new KFolderMimeType(*str, offset);
00136         break;
00137 
00138      default:
00139         kError(7011) << QString("KMimeTypeFactory: unexpected object entry in KSycoca database (type = %1)").arg((int)type) << endl;
00140         break;
00141    }
00142    if (newEntry && !newEntry->isValid())
00143    {
00144       kError(7011) << "KMimeTypeFactory: corrupt object in KSycoca database!\n" << endl;
00145       delete newEntry;
00146       newEntry = 0;
00147    }
00148    return newEntry;
00149 }
00150 
00151 
00152 QString KMimeTypeFactory::resolveAlias(const QString& mime) const
00153 {
00154     return m_aliases.value(mime);
00155 }
00156 
00157 QList<KMimeType::Ptr> KMimeTypeFactory::findFromFileName( const QString &filename, QString *matchingExtension )
00158 {
00159     // Assume we're NOT building a database
00160     if (!m_str) return QList<KMimeType::Ptr>();
00161 
00162     // "Applications MUST first try a case-sensitive match, then try again with
00163     // the filename converted to lower-case if that fails. This is so that
00164     // main.C will be seen as a C++ file, but IMAGE.GIF will still use the
00165     // *.gif pattern."
00166     QList<KMimeType::Ptr> mimeList = findFromFileNameHelper(filename, matchingExtension);
00167     if (mimeList.isEmpty()) {
00168         const QString lowerCase = filename.toLower();
00169         if (lowerCase != filename)
00170             mimeList = findFromFileNameHelper(lowerCase, matchingExtension);
00171     }
00172     return mimeList;
00173 }
00174 
00175 QList<KMimeType::Ptr> KMimeTypeFactory::findFromFastPatternDict(const QString &extension)
00176 {
00177     QList<KMimeType::Ptr> mimeList;
00178     if (!m_fastPatternDict) return mimeList; // Error!
00179 
00180     // Warning : this assumes we're NOT building a database
00181     const QList<int> offsetList = m_fastPatternDict->findMultiString(extension);
00182     if (offsetList.isEmpty()) return mimeList;
00183     const QString expectedPattern = "*."+extension;
00184     foreach(int offset, offsetList) {
00185         KMimeType::Ptr newMimeType(createEntry(offset));
00186         // Check whether the dictionary was right.
00187         if (newMimeType && newMimeType->patterns().contains(expectedPattern)) {
00188             mimeList.append(newMimeType);
00189         }
00190     }
00191     return mimeList;
00192 }
00193 
00194 bool KMimeTypeFactory::matchFileName( const QString &filename, const QString &pattern )
00195 {
00196     const int pattern_len = pattern.length();
00197     if (!pattern_len)
00198         return false;
00199     const int len = filename.length();
00200 
00201     const int starCount = pattern.count('*');
00202 
00203     // Patterns like "*~", "*.extension"
00204     if (pattern[0] == '*'  && pattern.indexOf('[') == -1 && starCount == 1)
00205     {
00206         if ( len + 1 < pattern_len ) return false;
00207 
00208         const QChar *c1 = pattern.unicode() + pattern_len - 1;
00209         const QChar *c2 = filename.unicode() + len - 1;
00210         int cnt = 1;
00211         while (cnt < pattern_len && *c1-- == *c2--)
00212             ++cnt;
00213         return cnt == pattern_len;
00214     }
00215 
00216     // Patterns like "README*" (well this is currently the only one like that...)
00217     if (starCount == 1 && pattern[pattern_len - 1] == '*') {
00218         if ( len + 1 < pattern_len ) return false;
00219         if (pattern[0] == '*')
00220             return filename.indexOf(pattern.mid(1, pattern_len - 2)) != -1;
00221 
00222         const QChar *c1 = pattern.unicode();
00223         const QChar *c2 = filename.unicode();
00224         int cnt = 1;
00225         while (cnt < pattern_len && *c1++ == *c2++)
00226            ++cnt;
00227         return cnt == pattern_len;
00228     }
00229 
00230     // Names without any wildcards like "README"
00231     if (pattern.indexOf('[') == -1 && starCount == 0 && pattern.indexOf('?'))
00232         return (pattern == filename);
00233 
00234     // Other (quite rare) patterns, like "*.anim[1-9j]": use slow but correct method
00235     QRegExp rx(pattern);
00236     rx.setPatternSyntax(QRegExp::Wildcard);
00237     return rx.exactMatch(filename);
00238 }
00239 
00240 void KMimeTypeFactory::findFromOtherPatternList(QList<KMimeType::Ptr>& matchingMimeTypes,
00241                                                 const QString &fileName,
00242                                                 QString& foundExt,
00243                                                 bool highWeight)
00244 {
00245     OtherPatternList& patternList = highWeight ? m_highWeightPatterns : m_lowWeightPatterns;
00246     bool& loaded = highWeight ? m_highWeightPatternsLoaded : m_lowWeightPatternsLoaded;
00247     if ( !loaded ) {
00248         loaded = true;
00249         // Load it only once
00250         QDataStream *str = m_str;
00251         str->device()->seek( highWeight ? m_highWeightPatternOffset : m_lowWeightPatternOffset );
00252 
00253         QString pattern;
00254         qint32 mimetypeOffset;
00255         qint32 weight;
00256         Q_FOREVER {
00257             KSycocaEntry::read(*str, pattern);
00258             if (pattern.isEmpty()) // end of list
00259                 break;
00260             (*str) >> mimetypeOffset;
00261             (*str) >> weight;
00262             patternList.push_back(OtherPattern(pattern, mimetypeOffset, weight));
00263         }
00264     }
00265 
00266     int matchingPatternLength = 0;
00267     qint32 lastMatchedWeight = 0;
00268     if (!highWeight && !matchingMimeTypes.isEmpty()) {
00269         // We found matches in the fast pattern dict already:
00270         matchingPatternLength = foundExt.length() + 2; // *.foo -> length=5
00271         lastMatchedWeight = 50;
00272     }
00273     OtherPatternList::const_iterator it = patternList.constBegin();
00274     const OtherPatternList::const_iterator end = patternList.constEnd();
00275     for ( ; it != end; ++it ) {
00276         const OtherPattern op = *it;
00277         if ( matchFileName( fileName, op.pattern ) ) {
00278             // Is this a lower-weight pattern than the last match? Stop here then.
00279             if (op.weight < lastMatchedWeight)
00280                 break;
00281             if (lastMatchedWeight > 0 && op.weight > lastMatchedWeight) // can't happen
00282                 kWarning(7009) << "Assumption failed; globs2 weights not sorted correctly"
00283                                << op.weight << ">" << lastMatchedWeight;
00284             // Is this a shorter or a longer match than an existing one, or same length?
00285             if (op.pattern.length() < matchingPatternLength) {
00286                 continue; // too short, ignore
00287             } else if (op.pattern.length() > matchingPatternLength) {
00288                 // longer: clear any previous match (like *.bz2, when pattern is *.tar.bz2)
00289                 matchingMimeTypes.clear();
00290                 // remember the new "longer" length
00291                 matchingPatternLength = op.pattern.length();
00292             }
00293             KMimeType *newMimeType = createEntry( op.offset );
00294             assert (newMimeType && newMimeType->isType( KST_KMimeType ));
00295             matchingMimeTypes.push_back( KMimeType::Ptr( newMimeType ) );
00296             if (op.pattern.startsWith("*."))
00297                 foundExt = op.pattern.mid(2);
00298         }
00299     }
00300 }
00301 
00302 QList<KMimeType::Ptr> KMimeTypeFactory::findFromFileNameHelper(const QString &fileName, QString *pMatchingExtension)
00303 {
00304     // First try the high weight matches (>50), if any.
00305     QList<KMimeType::Ptr> matchingMimeTypes;
00306     QString foundExt;
00307     findFromOtherPatternList(matchingMimeTypes, fileName, foundExt, true);
00308     if (matchingMimeTypes.isEmpty()) {
00309 
00310         // Now use the "fast patterns" dict, for simple *.foo patterns with weight 50
00311         // (which is most of them, so this optimization is definitely worth it)
00312         const int lastDot = fileName.lastIndexOf('.');
00313         if (lastDot != -1) { // if no '.', skip the extension lookup
00314             const int ext_len = fileName.length() - lastDot - 1;
00315             const QString simpleExtension = fileName.right( ext_len );
00316 
00317             matchingMimeTypes = findFromFastPatternDict(simpleExtension);
00318             if (!matchingMimeTypes.isEmpty()) {
00319                 foundExt = simpleExtension;
00320                 // Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway,
00321                 // at least those with weight 50.
00322             }
00323         }
00324 
00325         // Finally, try the low weight matches (<=50)
00326         findFromOtherPatternList(matchingMimeTypes, fileName, foundExt, false);
00327     }
00328     if (pMatchingExtension)
00329         *pMatchingExtension = foundExt;
00330     return matchingMimeTypes;
00331 }
00332 
00333 // TODO: remove unused whichPriority argument
00334 KMimeType::Ptr KMimeTypeFactory::findFromContent(QIODevice* device, WhichPriority whichPriority, int* accuracy, QByteArray& beginning)
00335 {
00336     Q_ASSERT(device->isOpen());
00337     if (device->size() == 0) {
00338         if (accuracy)
00339             *accuracy = 100;
00340         return findMimeTypeByName("application/x-zerosize");
00341     }
00342 
00343     if (!m_magicFilesParsed) {
00344         parseMagic();
00345         m_magicFilesParsed = true;
00346     }
00347 
00348     Q_FOREACH ( const KMimeMagicRule& rule, m_magicRules ) {
00349         // HighPriorityRules: select rules with priority >= 80
00350         // LowPriorityRules: select rules with priority < 80
00351         if ( ( whichPriority == AllRules ) ||
00352              ( (rule.priority() >= 80) == (whichPriority == HighPriorityRules) ) ) {
00353             if (rule.match(device, beginning)) {
00354                 if (accuracy)
00355                     *accuracy = rule.priority();
00356                 return findMimeTypeByName(rule.mimetype());
00357             }
00358         }
00359         // Rules are sorted by decreasing priority, so we can abort when we're past high-prio rules
00360         if (whichPriority == HighPriorityRules && rule.priority() < 80)
00361             break;
00362     }
00363 
00364     // Do fallback code so that we never return 0 - unless we were only looking for HighPriorityRules
00365     if (whichPriority != HighPriorityRules) {
00366         // Nothing worked, check if the file contents looks like binary or text
00367         if (!KMimeType::isBufferBinaryData(beginning)) {
00368             if (accuracy)
00369                 *accuracy = 5;
00370             return findMimeTypeByName("text/plain");
00371         }
00372         if (accuracy)
00373             *accuracy = 0;
00374         return KMimeType::defaultMimeTypePtr();
00375     }
00376 
00377     return KMimeType::Ptr();
00378 }
00379 
00380 KMimeType::List KMimeTypeFactory::allMimeTypes()
00381 {
00382     KMimeType::List result;
00383     const KSycocaEntry::List list = allEntries();
00384     for( KSycocaEntry::List::ConstIterator it = list.begin();
00385          it != list.end();
00386          ++it)
00387     {
00388         Q_ASSERT( (*it)->isType( KST_KMimeType ) );
00389         result.append( KMimeType::Ptr::staticCast( *it ) );
00390     }
00391     return result;
00392 }
00393 
00394 QMap<QString, QString>& KMimeTypeFactory::aliases()
00395 {
00396     return m_aliases;
00397 }
00398 
00399 KMimeTypeFactory *KMimeTypeFactory::_self = 0;
00400 
00401 #include <arpa/inet.h> // for ntohs
00402 #include <kstandarddirs.h>
00403 #include <QFile>
00404 
00405 // Sort them in descending order of priority
00406 static bool mimeMagicRuleCompare(const KMimeMagicRule& lhs, const KMimeMagicRule& rhs) {
00407     return lhs.priority() > rhs.priority();
00408 }
00409 
00410 
00411 void KMimeTypeFactory::parseMagic()
00412 {
00413     const QStringList magicFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", "magic");
00414     //kDebug() << magicFiles;
00415     QListIterator<QString> magicIter( magicFiles );
00416     magicIter.toBack();
00417     while (magicIter.hasPrevious()) { // global first, then local. Turns out it doesn't matter though.
00418         const QString fileName = magicIter.previous();
00419         QFile magicFile(fileName);
00420         kDebug(7009) << "Now parsing " << fileName;
00421         if (magicFile.open(QIODevice::ReadOnly))
00422             m_magicRules += parseMagicFile(&magicFile, fileName);
00423     }
00424     qSort(m_magicRules.begin(), m_magicRules.end(), mimeMagicRuleCompare);
00425 }
00426 
00427 static char readNumber(qint64& value, QIODevice* file)
00428 {
00429     char ch;
00430     while (file->getChar(&ch)) {
00431         if (ch < '0' || ch > '9')
00432             return ch;
00433         value = 10 * value + ch - '0';
00434     }
00435     // eof
00436     return '\0';
00437 }
00438 
00439 
00440 #define MAKE_LITTLE_ENDIAN16(val) val = (quint16)(((quint16)(val) << 8)|((quint16)(val) >> 8))
00441 
00442 #define MAKE_LITTLE_ENDIAN32(val) \
00443    val = (((quint32)(val) & 0xFF000000U) >> 24) | \
00444          (((quint32)(val) & 0x00FF0000U) >> 8) | \
00445          (((quint32)(val) & 0x0000FF00U) << 8) | \
00446          (((quint32)(val) & 0x000000FFU) << 24)
00447 
00448 QList<KMimeMagicRule> KMimeTypeFactory::parseMagicFile(QIODevice* file, const QString& fileName) const
00449 {
00450     QList<KMimeMagicRule> rules;
00451     QByteArray header = file->read(12);
00452     if (header != QByteArray::fromRawData("MIME-Magic\0\n", 12)) {
00453         kWarning(7009) << "Invalid magic file " << fileName << " starts with " << header;
00454         return rules;
00455     }
00456     QList<KMimeMagicMatch> matches; // toplevel matches (indent==0)
00457     int priority = 0; // to avoid warning
00458     QString mimeTypeName;
00459 
00460     Q_FOREVER {
00461         char ch = '\0';
00462         bool chOk = file->getChar(&ch);
00463 
00464         if (!chOk || ch == '[') {
00465             // Finish previous section
00466             if (!mimeTypeName.isEmpty()) {
00467                 rules.append(KMimeMagicRule(mimeTypeName, priority, matches));
00468                 matches.clear();
00469                 mimeTypeName.clear();
00470             }
00471             if (file->atEnd())
00472                 break; // done
00473 
00474             // Parse new section
00475             const QString line = file->readLine();
00476             const int pos = line.indexOf(':');
00477             if (pos == -1) { // syntax error
00478                 kWarning(7009) << "Syntax error in " << mimeTypeName
00479                                << " ':' not present in section name" << endl;
00480                 break;
00481             }
00482             priority = line.left(pos).toInt();
00483             mimeTypeName = line.mid(pos+1);
00484             mimeTypeName = mimeTypeName.left(mimeTypeName.length()-2); // remove ']\n'
00485             //kDebug(7009) << "New rule for " << mimeTypeName
00486             //             << " with priority " << priority << endl;
00487         } else {
00488             // Parse line in the section
00489             // [ indent ] ">" start-offset "=" value
00490             //   [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
00491             qint64 indent = 0;
00492             if (ch != '>') {
00493                 indent = ch - '0';
00494                 ch = readNumber(indent, file);
00495                 if (ch != '>') {
00496                     kWarning(7009) << "Invalid magic file " << fileName << " '>' not found, got " << ch << " at pos " << file->pos();
00497                     break;
00498                 }
00499             }
00500 
00501             KMimeMagicMatch match;
00502             match.m_rangeStart = 0;
00503             ch = readNumber(match.m_rangeStart, file);
00504             if (ch != '=') {
00505                 kWarning(7009) << "Invalid magic file " << fileName << " '=' not found";
00506                 break;
00507             }
00508 
00509             char lengthBuffer[2];
00510             if (file->read(lengthBuffer, 2) != 2)
00511                 break;
00512             const short valueLength = ntohs(*(short*)lengthBuffer);
00513             //kDebug() << "indent=" << indent << " rangeStart=" << match.m_rangeStart
00514             //         << " valueLength=" << valueLength << endl;
00515 
00516             match.m_data.resize(valueLength);
00517             if (file->read(match.m_data.data(), valueLength) != valueLength)
00518                 break;
00519 
00520             match.m_rangeLength = 1;
00521             bool invalidLine = false;
00522 
00523             if (!file->getChar(&ch))
00524                 break;
00525             qint64 wordSize = 1;
00526 
00527             Q_FOREVER {
00528                 // We get 'ch' before coming here, or as part of the parsing in each case below.
00529                 switch (ch) {
00530                 case '\n':
00531                     break;
00532                 case '&':
00533                     match.m_mask.resize(valueLength);
00534                     if (file->read(match.m_mask.data(), valueLength) != valueLength)
00535                         invalidLine = true;
00536                     if (!file->getChar(&ch))
00537                         invalidLine = true;
00538                     break;
00539                 case '~': {
00540                     wordSize = 0;
00541                     ch = readNumber(wordSize, file);
00542                     //kDebug() << "wordSize=" << wordSize;
00543                     break;
00544                 }
00545                 case '+':
00546                     // Parse range length
00547                     match.m_rangeLength = 0;
00548                     ch = readNumber(match.m_rangeLength, file);
00549                     if (ch == '\n')
00550                         break;
00551                     // fall-through intended
00552                 default:
00553                     // "If an unknown character is found where a newline is expected
00554                     // then the whole line should be ignored (there will be no binary
00555                     // data after the new character, so the next line starts after the
00556                     // next "\n" character). This is for future extensions.", says spec
00557                     while (ch != '\n' && !file->atEnd()) {
00558                         file->getChar(&ch);
00559                     }
00560                     invalidLine = true;
00561                     kDebug(7009) << "invalid line - garbage found - ch=" << ch;
00562                     break;
00563                 }
00564                 if (ch == '\n' || invalidLine)
00565                     break;
00566             }
00567             if (!invalidLine) {
00568                 // Finish match, doing byte-swapping on little endian hosts
00569 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
00570                 if (wordSize > 1) {
00571                     //kDebug() << "data before swapping: " << match.m_data;;
00572                     if ((wordSize != 2 && wordSize != 4) || (valueLength % wordSize != 0))
00573                         continue; // invalid word size
00574                     char* data = match.m_data.data();
00575                     char* mask = match.m_mask.data();
00576                     for (int i = 0; i < valueLength; i += wordSize) {
00577                         if (wordSize == 2)
00578                             MAKE_LITTLE_ENDIAN16( *((quint16 *) data + i) );
00579                         else if (wordSize == 4)
00580                             MAKE_LITTLE_ENDIAN32( *((quint32 *) data + i) );
00581                         if (!match.m_mask.isEmpty()) {
00582                             if (wordSize == 2)
00583                                 MAKE_LITTLE_ENDIAN16( *((quint16 *) mask + i) );
00584                             else if (wordSize == 4)
00585                                 MAKE_LITTLE_ENDIAN32( *((quint32 *) mask + i) );
00586                         }
00587                     }
00588                     //kDebug() << "data after swapping: " << match.m_data;
00589                 }
00590 #endif
00591                 // Append match at the right place depending on indent:
00592                 if (indent == 0) {
00593                     matches.append(match);
00594                 } else {
00595                     KMimeMagicMatch* m = &matches.last();
00596                     Q_ASSERT(m);
00597                     for (int i = 1 /* nothing to do for indent==1 */; i < indent; ++i) {
00598                         m = &m->m_subMatches.last();
00599                         Q_ASSERT(m);
00600                     }
00601                     m->m_subMatches.append(match);
00602                 }
00603             }
00604         }
00605     }
00606     return rules;
00607 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal