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

KDECore

kmimemagicrule.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries
00002  *  Copyright 2007 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 "kmimemagicrule_p.h"
00021 #include <QIODevice>
00022 #include <kdebug.h>
00023 #include <QByteArrayMatcher>
00024 
00025 /*
00026  * Historical note:
00027  * The notion of indents is used differently from the old file(1) magic file.
00028  * It is not enough that a top-level rule matches for the search to be over;
00029  * in file(1) subrules were used as refinement (and in KMimeMagic they were
00030  * mandatory if the toplevel rule didn't have a mimetype associated with it).
00031  * Here they are mandatory.
00032  * We need at least one continuation at every level to match, and then the match is valid:
00033 [50:application/x-kformula]
00034 >0=^B^_<8B>
00035 1>10=^GKOffice
00036 2>18=^Xapplication/x-kformula^D^F
00037 >0=^DPK^C^D
00038 1>30=^Hmimetype
00039 2>38=^Vapplication/x-kformula
00040  * Either it's an old (tar) file and the first hierarchy (0,1,2 levels) matches,
00041  * or it's a newer file (zip) file and the second hierarchy (0,1,2 levels) has to match.
00042  *
00043  */
00044 
00045 static bool testMatches(QIODevice* device, QByteArray& availableData, const QList<KMimeMagicMatch>& matches, const QString& mimeType)
00046 {
00047     for ( QList<KMimeMagicMatch>::const_iterator it = matches.begin(), end = matches.end() ;
00048           it != end ; ++it ) {
00049         const KMimeMagicMatch& match = *it;
00050         if (match.match(device, availableData, mimeType)) {
00051             // One of the hierarchies matched -> mimetype recognized.
00052             return true;
00053         }
00054     }
00055     return false;
00056 }
00057 
00058 // Taken from QByteArray::indexOf, but that one uses strncmp so it stops on '\0',
00059 // replaced with memcmp here...
00060 static int indexOf(const QByteArray& that, const QByteArray &ba)
00061 {
00062     const int l = that.size();
00063     const int ol = ba.size();
00064     if (ol > l)
00065         return -1;
00066     if (ol == 0)
00067         return 0;
00068     if (ol == 1)
00069         return that.indexOf(*ba.constData());
00070 
00071     if (l > 500 && ol > 5)
00072         return QByteArrayMatcher(ba).indexIn(that);
00073 
00074     const char *needle = ba.data();
00075     const char *haystack = that.data();
00076     const char *end = that.data() + (l - ol);
00077     const uint ol_minus_1 = ol - 1;
00078     uint hashNeedle = 0, hashHaystack = 0;
00079     int idx;
00080     for (idx = 0; idx < ol; ++idx) {
00081         hashNeedle = ((hashNeedle<<1) + needle[idx]);
00082         hashHaystack = ((hashHaystack<<1) + haystack[idx]);
00083     }
00084     hashHaystack -= *(haystack + ol_minus_1);
00085 
00086     while (haystack <= end) {
00087         hashHaystack += *(haystack + ol_minus_1);
00088         if (hashHaystack == hashNeedle  && *needle == *haystack
00089              && memcmp(needle, haystack, ol) == 0)
00090             return haystack - that.data();
00091 
00092         if (ol_minus_1 < sizeof(uint) * 8 /*CHAR_BIT*/)
00093             hashHaystack -= (*haystack) << ol_minus_1;
00094         hashHaystack <<= 1;
00095 
00096         ++haystack;
00097     }
00098     return -1;
00099 }
00100 
00101 
00102 bool KMimeMagicRule::match(QIODevice* device, QByteArray& availableData) const
00103 {
00104     return testMatches(device, availableData, m_matches, m_mimetype);
00105 }
00106 
00107 bool KMimeMagicMatch::match(QIODevice* device, QByteArray& availableData, const QString& mimeType) const
00108 {
00109     // First, check that "this" matches, then we'll dive into subMatches if any.
00110 
00111     const qint64 deviceSize = device->size();
00112     const qint64 mDataSize = m_data.size();
00113     if (m_rangeStart + mDataSize > deviceSize)
00114         return false; // file is too small
00115 
00116     // Read in one block all the data we'll need
00117     // Example: m_data="ABC", m_rangeLength=3 -> we need 3+3-1=5 bytes (ABCxx,xABCx,xxABC would match)
00118     const int dataNeeded = qMin(mDataSize + m_rangeLength - 1, deviceSize - m_rangeStart);
00119     QByteArray readData;
00120 
00121     /*kDebug() << "need " << dataNeeded << " bytes of data starting at " << m_rangeStart
00122              << "  - availableData has " << availableData.size() << " bytes,"
00123              << " device has " << deviceSize << " bytes." << endl;*/
00124 
00125     if (m_rangeStart + dataNeeded > availableData.size() && availableData.size() < deviceSize) {
00126         // Need to read from device
00127         if (!device->seek(m_rangeStart))
00128             return false;
00129         readData.resize(dataNeeded);
00130         const int nread = device->read(readData.data(), dataNeeded);
00131         //kDebug() << "readData (from device): reading" << dataNeeded << "bytes.";
00132         if (nread < mDataSize)
00133             return false; // error (or not enough data but we checked for that already)
00134         if (m_rangeStart == 0 && readData.size() > availableData.size()) {
00135             availableData = readData; // update cache
00136         }
00137         if (nread < readData.size()) {
00138             // File big enough to contain m_data, but not big enough for the full rangeLength.
00139             // Pad with zeros.
00140             memset(readData.data() + nread, 0, dataNeeded - nread);
00141         }
00142         //kDebug() << "readData (from device) at pos " << m_rangeStart << ":" << readData;
00143     } else {
00144         readData = QByteArray::fromRawData(availableData.constData() + m_rangeStart,
00145                                            dataNeeded);
00146         // Warning, readData isn't null-terminated so this kDebug
00147         // gives valgrind warnings (when printing as char* data).
00148         //kDebug() << "readData (from availableData) at pos " << m_rangeStart << ":" << readData;
00149     }
00150 
00151     // All we need to do now, is to look for m_data in readData (whose size is dataNeeded).
00152     // Either as a simple indexOf search, or applying the mask.
00153 
00154     bool found = false;
00155     if (m_mask.isEmpty()) {
00156         //kDebug() << "m_data=" << m_data;
00157         found = ::indexOf(readData, m_data) != -1;
00158         //if (found)
00159         //    kDebug() << "Matched readData=" << readData << "with m_data=" << m_data << "so this is" << mimeType;
00160     } else {
00161         const char* mask = m_mask.constData();
00162         const char* refData = m_data.constData();
00163         const char* readDataBase = readData.constData();
00164         // Example (continued from above):
00165         // deviceSize is 4, so dataNeeded was max'ed to 4.
00166         // maxStartPos = 4 - 3 + 1 = 2, and indeed
00167         // we need to check for a match a positions 0 and 1 (ABCx and xABC).
00168         const qint64 maxStartPos = dataNeeded - mDataSize + 1;
00169         for (int i = 0; i < maxStartPos; ++i) {
00170             const char* d = readDataBase + i;
00171             bool valid = true;
00172             for (int off = 0; off < mDataSize; ++off ) {
00173                 if ( ((*d++) & mask[off]) != ((refData[off] & mask[off])) ) {
00174                     valid = false;
00175                     break;
00176                 }
00177             }
00178             if (valid)
00179                 found = true;
00180         }
00181     }
00182     if (!found)
00183         return false;
00184 
00185     // No submatch? Then we are done.
00186     if (m_subMatches.isEmpty())
00187         return true;
00188 
00189     // Check that one of the submatches matches too
00190     return testMatches(device, availableData, m_subMatches, mimeType);
00191 }

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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