File indexing completed on 2024-05-05 17:56:56

0001 /*
0002     SPDX-FileCopyrightText: 2001 Shie Erlich <krusader@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2001 Rafi Yanai <krusader@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "krquery.h"
0010 
0011 // QtCore
0012 #include <QFile>
0013 #include <QMetaMethod>
0014 #include <QRegExp>
0015 #include <QTextCodec>
0016 
0017 #include <KCoreAddons/KFormat>
0018 #include <KI18n/KLocalizedString>
0019 #include <KIO/Job>
0020 #include <KIOCore/KFileItem>
0021 #include <KIOWidgets/KUrlCompletion>
0022 #include <utility>
0023 
0024 #include "../Archive/krarchandler.h"
0025 #include "fileitem.h"
0026 #include "filesystem.h"
0027 #include "krpermhandler.h"
0028 
0029 #define STATUS_SEND_DELAY 250
0030 #define MAX_LINE_LEN 1000
0031 
0032 // set the defaults
0033 KrQuery::KrQuery()
0034     : matchesCaseSensitive(true)
0035     , bNull(true)
0036     , contain(QString())
0037     , containCaseSensetive(true)
0038     , containWholeWord(false)
0039     , containRegExp(false)
0040     , minSize(0)
0041     , maxSize(0)
0042     , newerThen(0)
0043     , olderThen(0)
0044     , owner(QString())
0045     , group(QString())
0046     , perm(QString())
0047     , type(QString())
0048     , inArchive(false)
0049     , recurse(true)
0050     , followLinksP(true)
0051     , receivedBuffer(nullptr)
0052     , receivedBufferLen(0)
0053     , processEventsConnected(0)
0054     , codec(QTextCodec::codecForLocale())
0055 {
0056     QChar ch = '\n';
0057     QTextCodec::ConverterState state(QTextCodec::IgnoreHeader);
0058     encodedEnterArray = codec->fromUnicode(&ch, 1, &state);
0059     encodedEnter = encodedEnterArray.data();
0060     encodedEnterLen = encodedEnterArray.size();
0061 }
0062 
0063 // set the defaults
0064 KrQuery::KrQuery(const QString &name, bool matchCase)
0065     : bNull(true)
0066     , contain(QString())
0067     , containCaseSensetive(true)
0068     , containWholeWord(false)
0069     , containRegExp(false)
0070     , minSize(0)
0071     , maxSize(0)
0072     , newerThen(0)
0073     , olderThen(0)
0074     , owner(QString())
0075     , group(QString())
0076     , perm(QString())
0077     , type(QString())
0078     , inArchive(false)
0079     , recurse(true)
0080     , followLinksP(true)
0081     , receivedBuffer(nullptr)
0082     , receivedBufferLen(0)
0083     , processEventsConnected(0)
0084     , codec(QTextCodec::codecForLocale())
0085 {
0086     QChar ch = '\n';
0087     QTextCodec::ConverterState state(QTextCodec::IgnoreHeader);
0088     encodedEnterArray = codec->fromUnicode(&ch, 1, &state);
0089     encodedEnter = encodedEnterArray.data();
0090     encodedEnterLen = encodedEnterArray.size();
0091 
0092     setNameFilter(name, matchCase);
0093 }
0094 
0095 KrQuery::KrQuery(const KrQuery &that)
0096     : QObject()
0097     , receivedBuffer(nullptr)
0098     , receivedBufferLen(0)
0099     , processEventsConnected(0)
0100 {
0101     *this = that;
0102 }
0103 
0104 KrQuery::~KrQuery()
0105 {
0106     if (receivedBuffer)
0107         delete[] receivedBuffer;
0108     receivedBuffer = nullptr;
0109 }
0110 
0111 KrQuery &KrQuery::operator=(const KrQuery &old)
0112 {
0113     matches = old.matches;
0114     excludes = old.excludes;
0115     includedDirs = old.includedDirs;
0116     excludedDirs = old.excludedDirs;
0117     matchesCaseSensitive = old.matchesCaseSensitive;
0118     bNull = old.bNull;
0119     contain = old.contain;
0120     containCaseSensetive = old.containCaseSensetive;
0121     containWholeWord = old.containWholeWord;
0122     containRegExp = old.containRegExp;
0123     minSize = old.minSize;
0124     maxSize = old.maxSize;
0125     newerThen = old.newerThen;
0126     olderThen = old.olderThen;
0127     owner = old.owner;
0128     group = old.group;
0129     perm = old.perm;
0130     type = old.type;
0131     customType = old.customType;
0132     inArchive = old.inArchive;
0133     recurse = old.recurse;
0134     followLinksP = old.followLinksP;
0135     whereToSearch = old.whereToSearch;
0136     excludedFolderNames = old.excludedFolderNames;
0137     whereNotToSearch = old.whereNotToSearch;
0138     origFilter = old.origFilter;
0139 
0140     codec = old.codec;
0141 
0142     encodedEnterArray = old.encodedEnterArray;
0143     encodedEnter = encodedEnterArray.data();
0144     encodedEnterLen = encodedEnterArray.size();
0145 
0146     return *this;
0147 }
0148 
0149 void KrQuery::load(const KConfigGroup &cfg)
0150 {
0151     *this = KrQuery(); // reset parameters first
0152 
0153     if (cfg.readEntry("IsNull", true))
0154         return;
0155 
0156 #define LOAD(key, var) (var = cfg.readEntry(key, var))
0157     LOAD("Matches", matches);
0158     LOAD("Excludes", excludes);
0159     LOAD("IncludedDirs", includedDirs);
0160     LOAD("ExcludedDirs", excludedDirs);
0161     LOAD("MatchesCaseSensitive", matchesCaseSensitive);
0162     LOAD("Contain", contain);
0163     LOAD("ContainCaseSensetive", containCaseSensetive);
0164     LOAD("ContainWholeWord", containWholeWord);
0165     LOAD("ContainRegExp", containRegExp);
0166     LOAD("MinSize", minSize);
0167     LOAD("MaxSize", maxSize);
0168     newerThen = QDateTime::fromString(cfg.readEntry("NewerThan", QDateTime::fromTime_t(static_cast<uint>(newerThen)).toString())).toTime_t();
0169     olderThen = QDateTime::fromString(cfg.readEntry("OlderThan", QDateTime::fromTime_t(static_cast<uint>(olderThen)).toString())).toTime_t();
0170     LOAD("Owner", owner);
0171     LOAD("Group", group);
0172     LOAD("Perm", perm);
0173     LOAD("Type", type);
0174     LOAD("CustomType", customType);
0175     LOAD("InArchive", inArchive);
0176     LOAD("Recurse", recurse);
0177     LOAD("FollowLinks", followLinksP);
0178     // KF5 TODO?
0179     // LOAD("WhereToSearch", whereToSearch);
0180     // LOAD("WhereNotToSearch", whereNotToSearch);
0181     LOAD("OrigFilter", origFilter);
0182 
0183     codec = QTextCodec::codecForName(cfg.readEntry("Codec", codec->name()));
0184     if (!codec)
0185         codec = QTextCodec::codecForLocale();
0186 
0187     LOAD("EncodedEnterArray", encodedEnterArray);
0188     encodedEnter = encodedEnterArray.data();
0189     encodedEnterLen = encodedEnterArray.size();
0190 #undef LOAD
0191 
0192     bNull = false;
0193 }
0194 
0195 void KrQuery::save(KConfigGroup cfg)
0196 {
0197     cfg.writeEntry("IsNull", bNull);
0198 
0199     if (bNull)
0200         return;
0201 
0202     cfg.writeEntry("Matches", matches);
0203     cfg.writeEntry("Excludes", excludes);
0204     cfg.writeEntry("IncludedDirs", includedDirs);
0205     cfg.writeEntry("ExcludedDirs", excludedDirs);
0206     cfg.writeEntry("MatchesCaseSensitive", matchesCaseSensitive);
0207     cfg.writeEntry("Contain", contain);
0208     cfg.writeEntry("ContainCaseSensetive", containCaseSensetive);
0209     cfg.writeEntry("ContainWholeWord", containWholeWord);
0210     cfg.writeEntry("ContainRegExp", containRegExp);
0211     cfg.writeEntry("MinSize", minSize);
0212     cfg.writeEntry("MaxSize", maxSize);
0213     cfg.writeEntry("NewerThan", QDateTime::fromTime_t(static_cast<uint>(newerThen)).toString());
0214     cfg.writeEntry("OlderThan", QDateTime::fromTime_t(static_cast<uint>(olderThen)).toString());
0215     cfg.writeEntry("Owner", owner);
0216     cfg.writeEntry("Group", group);
0217     cfg.writeEntry("Perm", perm);
0218     cfg.writeEntry("Type", type);
0219     cfg.writeEntry("CustomType", customType);
0220     cfg.writeEntry("InArchive", inArchive);
0221     cfg.writeEntry("Recurse", recurse);
0222     cfg.writeEntry("FollowLinks", followLinksP);
0223     // KF5 TODO?
0224     // cfg.writeEntry("WhereToSearch", whereToSearch);
0225     // cfg.writeEntry("WhereNotToSearch", whereNotToSearch);
0226     cfg.writeEntry("OrigFilter", origFilter);
0227 
0228     cfg.writeEntry("Codec", codec->name());
0229     cfg.writeEntry("EncodedEnterArray", encodedEnterArray);
0230     cfg.writeEntry("EncodedEnter", encodedEnter);
0231     cfg.writeEntry("EncodedEnterLen", encodedEnterLen);
0232 }
0233 
0234 void KrQuery::connectNotify(const QMetaMethod &signal)
0235 {
0236     if (signal == QMetaMethod::fromSignal(&KrQuery::processEvents))
0237         processEventsConnected++;
0238 }
0239 
0240 void KrQuery::disconnectNotify(const QMetaMethod &signal)
0241 {
0242     if (signal == QMetaMethod::fromSignal(&KrQuery::processEvents))
0243         processEventsConnected--;
0244 }
0245 
0246 bool KrQuery::checkPerm(QString filePerm) const
0247 {
0248     for (int i = 0; i < 9; ++i)
0249         if (perm[i] != '?' && perm[i] != filePerm[i + 1])
0250             return false;
0251     return true;
0252 }
0253 
0254 bool KrQuery::checkType(const QString &mime) const
0255 {
0256     if (type == mime)
0257         return true;
0258     if (type == i18n("Archives"))
0259         return KrArcHandler::arcSupported(mime);
0260     if (type == i18n("Folders"))
0261         return mime.contains("directory");
0262     if (type == i18n("Image Files"))
0263         return mime.contains("image/");
0264     if (type == i18n("Text Files"))
0265         return mime.contains("text/");
0266     if (type == i18n("Video Files"))
0267         return mime.contains("video/");
0268     if (type == i18n("Audio Files"))
0269         return mime.contains("audio/");
0270     if (type == i18n("Custom"))
0271         return customType.contains(mime);
0272     return false;
0273 }
0274 
0275 bool KrQuery::match(const QString &name) const
0276 {
0277     return matchCommon(name, matches, excludes);
0278 }
0279 
0280 bool KrQuery::matchDirName(const QString &name) const
0281 {
0282     return matchCommon(name, includedDirs, excludedDirs);
0283 }
0284 
0285 bool KrQuery::matchCommon(const QString &nameIn, const QStringList &matchList, const QStringList &excludeList) const
0286 {
0287     if (excludeList.count() == 0 && matchList.count() == 0) /* true if there's no match condition */
0288         return true;
0289 
0290     QString name(nameIn);
0291     int ndx = nameIn.lastIndexOf('/'); // virtual filenames may contain '/'
0292     if (ndx != -1) // but the end of the filename is OK
0293         name = nameIn.mid(ndx + 1);
0294 
0295     for (int i = 0; i < excludeList.count(); ++i) {
0296         if (QRegExp(excludeList[i], matchesCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard).exactMatch(name))
0297             return false;
0298     }
0299 
0300     if (matchList.count() == 0)
0301         return true;
0302 
0303     for (int i = 0; i < matchList.count(); ++i) {
0304         if (QRegExp(matchList[i], matchesCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard).exactMatch(name))
0305             return true;
0306     }
0307     return false;
0308 }
0309 
0310 bool KrQuery::match(FileItem *item) const
0311 {
0312     if (item->isDir() && !matchDirName(item->getName()))
0313         return false;
0314     // see if the name matches
0315     if (!match(item->getName()))
0316         return false;
0317     // checking the mime
0318     if (!type.isEmpty() && !checkType(item->getMime()))
0319         return false;
0320     // check that the size fit
0321     KIO::filesize_t size = item->getSize();
0322     if (minSize && size < minSize)
0323         return false;
0324     if (maxSize && size > maxSize)
0325         return false;
0326     // check the time frame
0327     time_t mtime = item->getModificationTime();
0328     if (olderThen && mtime > olderThen)
0329         return false;
0330     if (newerThen && mtime < newerThen)
0331         return false;
0332     // check owner name
0333     if (!owner.isEmpty() && item->getOwner() != owner)
0334         return false;
0335     // check group name
0336     if (!group.isEmpty() && item->getGroup() != group)
0337         return false;
0338     // check permission
0339     if (!perm.isEmpty() && !checkPerm(item->getPerm()))
0340         return false;
0341 
0342     if (!contain.isEmpty()) {
0343         totalBytes = item->getSize();
0344         receivedBytes = 0;
0345         if (receivedBuffer)
0346             delete receivedBuffer;
0347         receivedBuffer = nullptr;
0348         receivedBufferLen = 0;
0349         fileName = item->getName();
0350         timer.start();
0351 
0352         // search locally
0353         if (item->getUrl().isLocalFile()) {
0354             return containsContent(item->getUrl().path());
0355         }
0356 
0357         // search remotely
0358         if (processEventsConnected == 0) {
0359             return false;
0360         }
0361         return containsContent(item->getUrl());
0362     }
0363 
0364     return true;
0365 }
0366 
0367 // takes the string and adds BOLD to it, so that when it is displayed,
0368 // the grepped text will be bold
0369 void fixFoundTextForDisplay(QString &haystack, int start, int length)
0370 {
0371     QString before = haystack.left(start);
0372     QString text = haystack.mid(start, length);
0373     QString after = haystack.mid(start + length);
0374 
0375     before.replace('&', "&amp;");
0376     before.replace('<', "&lt;");
0377     before.replace('>', "&gt;");
0378 
0379     text.replace('&', "&amp;");
0380     text.replace('<', "&lt;");
0381     text.replace('>', "&gt;");
0382 
0383     after.replace('&', "&amp;");
0384     after.replace('<', "&lt;");
0385     after.replace('>', "&gt;");
0386 
0387     haystack = ("<qt>" + before + "<b>" + text + "</b>" + after + "</qt>");
0388 }
0389 
0390 bool KrQuery::checkBuffer(const char *data, int len) const
0391 {
0392     bool result = false;
0393 
0394     auto *mergedBuffer = new char[len + receivedBufferLen];
0395     if (receivedBufferLen)
0396         memcpy(mergedBuffer, receivedBuffer, receivedBufferLen);
0397     if (len)
0398         memcpy(mergedBuffer + receivedBufferLen, data, len);
0399 
0400     int maxLen = len + receivedBufferLen;
0401     int maxBuffer = maxLen - encodedEnterLen;
0402     int lastLinePosition = 0;
0403 
0404     for (int enterIndex = 0; enterIndex < maxBuffer; enterIndex++) {
0405         if (memcmp(mergedBuffer + enterIndex, encodedEnter, encodedEnterLen) == 0) {
0406             QString str = codec->toUnicode(mergedBuffer + lastLinePosition, enterIndex + encodedEnterLen - lastLinePosition);
0407             if (str.endsWith('\n')) {
0408                 str.chop(1);
0409                 result = result || checkLine(str);
0410                 lastLinePosition = enterIndex + encodedEnterLen;
0411                 enterIndex = lastLinePosition;
0412                 continue;
0413             }
0414         }
0415     }
0416 
0417     if (maxLen - lastLinePosition > MAX_LINE_LEN || len == 0) {
0418         QString str = codec->toUnicode(mergedBuffer + lastLinePosition, maxLen - lastLinePosition);
0419         result = result || checkLine(str);
0420         lastLinePosition = maxLen;
0421     }
0422 
0423     delete[] receivedBuffer;
0424     receivedBuffer = nullptr;
0425     receivedBufferLen = maxLen - lastLinePosition;
0426 
0427     if (receivedBufferLen) {
0428         receivedBuffer = new char[receivedBufferLen];
0429         memcpy(receivedBuffer, mergedBuffer + lastLinePosition, receivedBufferLen);
0430     }
0431 
0432     delete[] mergedBuffer;
0433     return result;
0434 }
0435 
0436 bool KrQuery::checkLine(const QString &line, bool backwards) const
0437 {
0438     if (containRegExp) {
0439         QRegExp rexp(contain, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::RegExp);
0440         int ndx = backwards ? rexp.lastIndexIn(line) : rexp.indexIn(line);
0441         bool result = ndx >= 0;
0442         if (result)
0443             fixFoundTextForDisplay(lastSuccessfulGrep = line, lastSuccessfulGrepMatchIndex = ndx, lastSuccessfulGrepMatchLength = rexp.matchedLength());
0444         return result;
0445     }
0446 
0447     int ndx = backwards ? -1 : 0;
0448 
0449     if (line.isNull())
0450         return false;
0451     if (containWholeWord) {
0452         while ((ndx = (backwards) ? line.lastIndexOf(contain, ndx, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive)
0453                                   : line.indexOf(contain, ndx, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive))
0454                != -1) {
0455             QChar before = '\n';
0456             QChar after = '\n';
0457 
0458             if (ndx > 0)
0459                 before = line.at(ndx - 1);
0460             if (ndx + contain.length() < line.length())
0461                 after = line.at(ndx + contain.length());
0462 
0463             if (!before.isLetterOrNumber() && !after.isLetterOrNumber() && after != '_' && before != '_') {
0464                 lastSuccessfulGrep = line;
0465                 fixFoundTextForDisplay(lastSuccessfulGrep, lastSuccessfulGrepMatchIndex = ndx, lastSuccessfulGrepMatchLength = contain.length());
0466                 return true;
0467             }
0468 
0469             if (backwards)
0470                 ndx -= line.length() + 1;
0471             else
0472                 ndx++;
0473         }
0474     } else if ((ndx = (backwards) ? line.lastIndexOf(contain, -1, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive)
0475                                   : line.indexOf(contain, 0, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive))
0476                != -1) {
0477         lastSuccessfulGrep = line;
0478         fixFoundTextForDisplay(lastSuccessfulGrep, lastSuccessfulGrepMatchIndex = ndx, lastSuccessfulGrepMatchLength = contain.length());
0479         return true;
0480     }
0481     return false;
0482 }
0483 
0484 bool KrQuery::containsContent(const QString &file) const
0485 {
0486     QFile qf(file);
0487     if (!qf.open(QIODevice::ReadOnly))
0488         return false;
0489 
0490     char buffer[1440]; // 2k buffer
0491 
0492     while (!qf.atEnd()) {
0493         // Note: As it's stated in the documentation, "`qint64 QIODevice::read(char *data,
0494         // qint64 maxSize)` Reads at most `maxSize` bytes"
0495         int bytes = static_cast<int>(qf.read(buffer, sizeof(buffer)));
0496 
0497         if (bytes <= 0)
0498             break;
0499 
0500         receivedBytes += bytes;
0501 
0502         if (checkBuffer(buffer, bytes))
0503             return true;
0504 
0505         if (checkTimer()) {
0506             bool stopped = false;
0507             emit(const_cast<KrQuery *>(this))->processEvents(stopped);
0508             if (stopped)
0509                 return false;
0510         }
0511     }
0512     if (checkBuffer(buffer, 0))
0513         return true;
0514 
0515     lastSuccessfulGrep.clear(); // nothing was found
0516     return false;
0517 }
0518 
0519 bool KrQuery::containsContent(const QUrl &url) const
0520 {
0521     KIO::TransferJob *contentReader = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
0522     connect(contentReader, &KIO::TransferJob::data, this, &KrQuery::containsContentData);
0523     connect(contentReader, &KIO::Job::result, this, &KrQuery::containsContentFinished);
0524 
0525     busy = true;
0526     containsContentResult = false;
0527     bool stopped = false;
0528 
0529     while (busy && !stopped) {
0530         checkTimer();
0531         emit(const_cast<KrQuery *>(this))->processEvents(stopped);
0532     }
0533 
0534     if (busy) {
0535         contentReader->kill(KJob::EmitResult);
0536         busy = false;
0537     }
0538 
0539     return containsContentResult;
0540 }
0541 
0542 void KrQuery::containsContentData(KIO::Job *job, const QByteArray &array)
0543 {
0544     receivedBytes += array.size();
0545     if (checkBuffer(array.data(), array.size())) {
0546         containsContentResult = true;
0547         containsContentFinished(job);
0548         job->kill(KJob::EmitResult);
0549         return;
0550     }
0551     checkTimer();
0552 }
0553 
0554 void KrQuery::containsContentFinished(KJob *)
0555 {
0556     busy = false;
0557 }
0558 
0559 bool KrQuery::checkTimer() const
0560 {
0561     if (timer.elapsed() >= STATUS_SEND_DELAY) {
0562         static KFormat format;
0563         // clang-format off
0564         QString message =
0565             i18nc("%1=filename, %2=percentage", "Searching content of '%1' (%2)", fileName,
0566                       (totalBytes > 0 && totalBytes >= receivedBytes) ?
0567                         // The normal case: A percentage is shown.
0568                         // Note: Instead of rounding, a truncation is performed because each percentage is seen
0569                         // only briefly —therefore, here speed is more important than precision
0570                         i18nc("%1=percentage", "%1%", (receivedBytes*100)/totalBytes) :
0571                         // An unusual case: There are problems when calculating that percentage. Sometimes it's
0572                         // because the contents of a big symlinked file are read, then the variable `totalBytes`
0573                         // is very small (it's the size of a symlink) but much more bytes are read
0574                         format.formatByteSize(static_cast<double>(receivedBytes), 0, KFormat::IECBinaryDialect, KFormat::UnitMegaByte)
0575                 );
0576         // clang-format on
0577 
0578         timer.start();
0579         emit(const_cast<KrQuery *>(this))->status(message);
0580         return true;
0581     }
0582     return false;
0583 }
0584 
0585 QStringList KrQuery::split(QString str)
0586 {
0587     QStringList list;
0588     int splitNdx = 0;
0589     int startNdx = 0;
0590     bool quotation = false;
0591 
0592     while (splitNdx < str.length()) {
0593         if (str[splitNdx] == '"')
0594             quotation = !quotation;
0595 
0596         if (!quotation && str[splitNdx] == ' ') {
0597             QString section = str.mid(startNdx, splitNdx - startNdx);
0598             startNdx = splitNdx + 1;
0599             if (section.startsWith('\"') && section.endsWith('\"') && section.length() >= 2)
0600                 section = section.mid(1, section.length() - 2);
0601             if (!section.isEmpty())
0602                 list.append(section);
0603         }
0604         splitNdx++;
0605     }
0606 
0607     if (startNdx < splitNdx) {
0608         QString section = str.mid(startNdx, splitNdx - startNdx);
0609         if (section.startsWith('\"') && section.endsWith('\"') && section.length() >= 2)
0610             section = section.mid(1, section.length() - 2);
0611         if (!section.isEmpty())
0612             list.append(section);
0613     }
0614 
0615     return list;
0616 }
0617 
0618 void KrQuery::setNameFilter(const QString &text, bool cs)
0619 {
0620     bNull = false;
0621     matchesCaseSensitive = cs;
0622     origFilter = text;
0623 
0624     QString matchText = text;
0625     QString excludeText;
0626 
0627     int excludeNdx = 0;
0628     bool quotationMark = 0;
0629     while (excludeNdx < matchText.length()) {
0630         if (matchText[excludeNdx] == '"')
0631             quotationMark = !quotationMark;
0632         if (!quotationMark) {
0633             if (matchText[excludeNdx] == '|')
0634                 break;
0635         }
0636         excludeNdx++;
0637     }
0638 
0639     if (excludeNdx < matchText.length()) {
0640         excludeText = matchText.mid(excludeNdx + 1).trimmed();
0641         matchText.truncate(excludeNdx);
0642         matchText = matchText.trimmed();
0643         if (matchText.isEmpty())
0644             matchText = '*';
0645     }
0646 
0647     int i;
0648 
0649     matches = split(matchText);
0650     includedDirs.clear();
0651 
0652     for (i = 0; i < matches.count();) {
0653         if (matches[i].endsWith('/')) {
0654             includedDirs.push_back(matches[i].left(matches[i].length() - 1));
0655             matches.removeAll(matches.at(i));
0656             continue;
0657         }
0658 
0659         if (!matches[i].contains("*") && !matches[i].contains("?"))
0660             matches[i] = '*' + matches[i] + '*';
0661 
0662         i++;
0663     }
0664 
0665     excludes = split(excludeText);
0666     excludedDirs.clear();
0667 
0668     for (i = 0; i < excludes.count();) {
0669         if (excludes[i].endsWith('/')) {
0670             excludedDirs.push_back(excludes[i].left(excludes[i].length() - 1));
0671             excludes.removeAll(excludes.at(i));
0672             continue;
0673         }
0674 
0675         if (!excludes[i].contains("*") && !excludes[i].contains("?"))
0676             excludes[i] = '*' + excludes[i] + '*';
0677 
0678         i++;
0679     }
0680 }
0681 
0682 void KrQuery::setContent(const QString &content, bool cs, bool wholeWord, const QString &encoding, bool regExp)
0683 {
0684     bNull = false;
0685     contain = content;
0686     containCaseSensetive = cs;
0687     containWholeWord = wholeWord;
0688     containRegExp = regExp;
0689 
0690     if (encoding.isEmpty())
0691         codec = QTextCodec::codecForLocale();
0692     else {
0693         codec = QTextCodec::codecForName(encoding.toLatin1());
0694         if (codec == nullptr)
0695             codec = QTextCodec::codecForLocale();
0696     }
0697 
0698     QChar ch = '\n';
0699     QTextCodec::ConverterState state(QTextCodec::IgnoreHeader);
0700     encodedEnterArray = codec->fromUnicode(&ch, 1, &state);
0701     encodedEnter = encodedEnterArray.data();
0702     encodedEnterLen = encodedEnterArray.size();
0703 }
0704 
0705 void KrQuery::setMinimumFileSize(KIO::filesize_t minimumSize)
0706 {
0707     bNull = false;
0708     minSize = minimumSize;
0709 }
0710 
0711 void KrQuery::setMaximumFileSize(KIO::filesize_t maximumSize)
0712 {
0713     bNull = false;
0714     maxSize = maximumSize;
0715 }
0716 
0717 void KrQuery::setNewerThan(time_t time)
0718 {
0719     bNull = false;
0720     newerThen = time;
0721 }
0722 
0723 void KrQuery::setOlderThan(time_t time)
0724 {
0725     bNull = false;
0726     olderThen = time;
0727 }
0728 
0729 void KrQuery::setOwner(const QString &ownerIn)
0730 {
0731     bNull = false;
0732     owner = ownerIn;
0733 }
0734 
0735 void KrQuery::setGroup(const QString &groupIn)
0736 {
0737     bNull = false;
0738     group = groupIn;
0739 }
0740 
0741 void KrQuery::setPermissions(const QString &permIn)
0742 {
0743     bNull = false;
0744     perm = permIn;
0745 }
0746 
0747 void KrQuery::setMimeType(const QString &typeIn, QStringList customList)
0748 {
0749     bNull = false;
0750     type = typeIn;
0751     customType = std::move(customList);
0752 }
0753 
0754 bool KrQuery::isExcluded(const QUrl &url)
0755 {
0756     for (QUrl &item : whereNotToSearch)
0757         if (item.isParentOf(url) || url.matches(item, QUrl::StripTrailingSlash))
0758             return true;
0759 
0760     // Exclude folder names that are configured in settings
0761     QString filename = url.fileName();
0762     for (QString &item : excludedFolderNames)
0763         if (filename == item)
0764             return true;
0765 
0766     if (!matchDirName(filename))
0767         return true;
0768 
0769     return false;
0770 }
0771 
0772 void KrQuery::setSearchInDirs(const QList<QUrl> &urls)
0773 {
0774     whereToSearch.clear();
0775     for (int i = 0; i < urls.count(); ++i) {
0776         QString url = urls[i].url();
0777         QUrl completed = QUrl::fromUserInput(KUrlCompletion::replacedPath(url, true, true), QString(), QUrl::AssumeLocalFile);
0778         whereToSearch.append(completed);
0779     }
0780 }
0781 
0782 void KrQuery::setDontSearchInDirs(const QList<QUrl> &urls)
0783 {
0784     whereNotToSearch.clear();
0785     for (int i = 0; i < urls.count(); ++i) {
0786         QString url = urls[i].url();
0787         QUrl completed = QUrl::fromUserInput(KUrlCompletion::replacedPath(url, true, true), QString(), QUrl::AssumeLocalFile);
0788         whereNotToSearch.append(completed);
0789     }
0790 }
0791 
0792 void KrQuery::setExcludeFolderNames(const QStringList &paths)
0793 {
0794     excludedFolderNames.clear();
0795     excludedFolderNames.append(paths);
0796 }