From 5a2c8cb4613fd38839cf97ca987987f9e91379a7 Mon Sep 17 00:00:00 2001 From: hama Date: Tue, 14 Apr 2015 00:29:19 +0200 Subject: [PATCH] refind: TextFinder --- appl/refind/filefinder.cpp | 51 +++++----- appl/refind/mainwindow.cpp | 84 +++++++++-------- appl/refind/mainwindow.hpp | 7 +- appl/refind/refind.pro | 62 ++++++------ appl/refind/textfinder.cpp | 187 +++++++++++++++++++++++++++++++++++++ appl/refind/textfinder.hpp | 31 ++++++ 6 files changed, 322 insertions(+), 100 deletions(-) create mode 100644 appl/refind/textfinder.cpp create mode 100644 appl/refind/textfinder.hpp diff --git a/appl/refind/filefinder.cpp b/appl/refind/filefinder.cpp index c426221..b934212 100644 --- a/appl/refind/filefinder.cpp +++ b/appl/refind/filefinder.cpp @@ -11,6 +11,9 @@ #include "mainwindow.hpp" #include "filefinder.hpp" +/** + * Constructor. + */ FileFinder::FileFinder() : m_lines(0), m_patterns(), @@ -26,8 +29,7 @@ FileFinder::FileFinder() : m_countFiles(0), m_countDirs(0), m_bytes(0), - m_excludedDirs() -{ + m_excludedDirs(){ m_youngerThan.setMSecsSinceEpoch(0); m_olderThan.setMSecsSinceEpoch(0); } @@ -96,11 +98,11 @@ QString fileSize(int64_t size){ QString typeOf(QFileInfo& info){ QString rc; if (info.isSymLink()){ - if (info.isDir()) - rc = QObject::tr("link (dir)"); - else - rc = QObject::tr("link (file)"); - } else if (info.isDir()) + if (info.isDir()) + rc = QObject::tr("link (dir)"); + else + rc = QObject::tr("link (file)"); + }else if (info.isDir()) rc = QObject::tr("dir"); else rc = QObject::tr("file"); @@ -119,9 +121,9 @@ void FileFinder::fillTable(const QString& path, int depth, QTableWidget* table){ QDir dir(path); QDir::Filters filters = m_fileTypes | QDir::NoDotAndDotDot; if (m_patterns.count() == 0) - entries = dir.entryInfoList(filters, QDir::NoSort); - else - entries = dir.entryInfoList(m_patterns, filters, QDir::NoSort); + entries = dir.entryInfoList(filters, QDir::NoSort); + else + entries = dir.entryInfoList(m_patterns, filters, QDir::NoSort); QList ::iterator it; QString relativePath = path.mid(1 + m_baseDir.length()); QString node, ext; @@ -162,8 +164,8 @@ void FileFinder::fillTable(const QString& path, int depth, QTableWidget* table){ bool filtered = m_excludedDirs.length() > 0; for (it = entries.begin(); it != entries.end(); ++it){ QString node = it->fileName(); - if (! filtered || !isExcludedDir(node)) - fillTable(path + QDir::separator() + node, depth + 1, table); + if (!filtered || !isExcludedDir(node)) + fillTable(path + QDir::separator() + node, depth + 1, table); } } table->setRowCount(m_lines); @@ -176,15 +178,15 @@ void FileFinder::fillTable(const QString& path, int depth, QTableWidget* table){ * @return true: the node is part of the excluded dirs */ bool FileFinder::isExcludedDir(const QString& node){ - bool rc = false; - QList ::iterator it; - for (it = m_excludedDirs.begin(); it != m_excludedDirs.end(); ++it){ - if (QString::compare(node, *it, Qt::CaseInsensitive) == 0){ - rc = true; - break; - } - } - return rc; + bool rc = false; + QList ::iterator it; + for (it = m_excludedDirs.begin(); it != m_excludedDirs.end(); ++it){ + if (QString::compare(node, *it, Qt::CaseInsensitive) == 0){ + rc = true; + break; + } + } + return rc; } /** @@ -214,16 +216,15 @@ bool FileFinder::isValid(const QFileInfo& file){ * @param baseDir the directory where the search starts */ void FileFinder::setBaseDir(const QString& baseDir){ - m_baseDir = baseDir; + m_baseDir = baseDir; } /** * Sets the list of excluded directories. * @param excludedDirs each entry of this list will not be entered for search */ -void FileFinder::setExcludedDirs(const QStringList& excludedDirs) -{ - m_excludedDirs = excludedDirs; +void FileFinder::setExcludedDirs(const QStringList& excludedDirs){ + m_excludedDirs = excludedDirs; } /** diff --git a/appl/refind/mainwindow.cpp b/appl/refind/mainwindow.cpp index e2f4529..6ee4f2b 100644 --- a/appl/refind/mainwindow.cpp +++ b/appl/refind/mainwindow.cpp @@ -29,12 +29,14 @@ MainWindow::MainWindow(const QString& startDir, QWidget *parent) : m_errors(0){ ui->setupUi(this); m_statusMessage = new QLabel(tr("Willkommen bei refind")); - ui->comboBoxDirectory->setCurrentText(startDir.isEmpty() ? QDir::currentPath() : startDir); + ui->comboBoxDirectory->setCurrentText( + startDir.isEmpty() ? QDir::currentPath() : startDir); statusBar()->addWidget(m_statusMessage); connect(ui->pushButtonSearch, SIGNAL(clicked()), this, SLOT(search())); connect(ui->pushButtonSearch2, SIGNAL(clicked()), this, SLOT(search())); connect(ui->pushButtonUp, SIGNAL(clicked()), this, SLOT(up())); - connect(ui->pushButtonDirectory, SIGNAL(clicked()), this, SLOT(selectDirectory())); + connect(ui->pushButtonDirectory, SIGNAL(clicked()), this, + SLOT(selectDirectory())); ui->tableWidget->setColumnWidth(TC_NODE, 200); ui->tableWidget->setColumnWidth(TC_EXT, 40); ui->tableWidget->setColumnWidth(TC_SIZE, 125); @@ -143,28 +145,28 @@ QString MainWindow::comboText(QComboBox* combo){ * @return the filetypes selected by the checkboxes */ QDir::Filters MainWindow::buildFileTypes(){ - QDir::Filters rc = 0; - if (ui->checkBoxDirs->isChecked()) - rc |= QDir::Dirs; - if (ui->checkBoxFiles->isChecked()) - rc |= QDir::Files; - if (rc == 0) - rc |= QDir::Dirs | QDir::Files; - if (! ui->checkBoxLinks->isChecked()) - rc |= QDir::NoSymLinks; - if (ui->checkBoxHidden) - rc |= QDir::Hidden | QDir::System; - QDir::Filters mask = 0; - if (ui->checkBoxWritable->isChecked()) - mask |= QDir::Writable; - if (ui->checkBoxReadable->isChecked()) - mask |= QDir::Readable; - if (ui->checkBoxExecutable->isChecked()) - mask |= QDir::Executable; - if (mask == 0) - mask |= QDir::PermissionMask; - rc |= mask; - return rc; + QDir::Filters rc = 0; + if (ui->checkBoxDirs->isChecked()) + rc |= QDir::Dirs; + if (ui->checkBoxFiles->isChecked()) + rc |= QDir::Files; + if (rc == 0) + rc |= QDir::Dirs | QDir::Files; + if (!ui->checkBoxLinks->isChecked()) + rc |= QDir::NoSymLinks; + if (ui->checkBoxHidden) + rc |= QDir::Hidden | QDir::System; + QDir::Filters mask = 0; + if (ui->checkBoxWritable->isChecked()) + mask |= QDir::Writable; + if (ui->checkBoxReadable->isChecked()) + mask |= QDir::Readable; + if (ui->checkBoxExecutable->isChecked()) + mask |= QDir::Executable; + if (mask == 0) + mask |= QDir::PermissionMask; + rc |= mask; + return rc; } /** @@ -188,12 +190,13 @@ void MainWindow::search(){ patterns = value.split(","); finder.setPatterns(patterns); value = ui->comboBoxExcludedDirs->currentText(); - if (value.indexOf('/')>= 0 || value.indexOf('\\') >= 0) - guiError(ui->comboBoxExcludedDirs, tr("no path delimiter allowed")); - else if (value.indexOf('*')>= 0) - guiError(ui->comboBoxExcludedDirs, tr("no patterns allowed. Do not use '*")); + if (value.indexOf('/') >= 0 || value.indexOf('\\') >= 0) + guiError(ui->comboBoxExcludedDirs, tr("no path delimiter allowed")); + else if (value.indexOf('*') >= 0) + guiError(ui->comboBoxExcludedDirs, + tr("no patterns allowed. Do not use '*")); else if (!value.isEmpty()) - patterns = value.split(","); + patterns = value.split(","); finder.setExcludedDirs(patterns); if (m_errors == 0){ clock_t start = clock(); @@ -211,11 +214,10 @@ void MainWindow::search(){ * Handles the push of the button "select directory". */ void MainWindow::selectDirectory(){ - QString dir = QFileDialog::getExistingDirectory(this, - tr("Select Directory"), ui->comboBoxDirectory->currentText(), - QFileDialog::ShowDirsOnly); - if (! dir.isEmpty()) - ui->comboBoxDirectory->setCurrentText(dir); + QString dir = QFileDialog::getExistingDirectory(this, tr("Select Directory"), + ui->comboBoxDirectory->currentText(), QFileDialog::ShowDirsOnly); + if (!dir.isEmpty()) + ui->comboBoxDirectory->setCurrentText(dir); } /** @@ -265,14 +267,14 @@ void MainWindow::setStatusMessage(bool error, const QString& message){ * @brief Handles the "up" button: go to the parent directory. */ void MainWindow::up(){ - QString path = ui->comboBoxDirectory->currentText(); + QString path = ui->comboBoxDirectory->currentText(); QDir dir(path); if (dir.exists()){ - dir.cdUp(); - if (dir.exists()){ - path = dir.absolutePath(); - ui->comboBoxDirectory->setEditText(path); - setInHistory(ui->comboBoxDirectory, path); - } + dir.cdUp(); + if (dir.exists()){ + path = dir.absolutePath(); + ui->comboBoxDirectory->setEditText(path); + setInHistory(ui->comboBoxDirectory, path); + } } } diff --git a/appl/refind/mainwindow.hpp b/appl/refind/mainwindow.hpp index 04b0c7b..373ed7b 100644 --- a/appl/refind/mainwindow.hpp +++ b/appl/refind/mainwindow.hpp @@ -34,11 +34,10 @@ public: explicit MainWindow(const QString& startDir, QWidget *parent = 0); ~MainWindow(); - private slots: - void search(); - void up(); - void selectDirectory(); + void search(); + void up(); + void selectDirectory(); private: QDir::Filters buildFileTypes(); diff --git a/appl/refind/refind.pro b/appl/refind/refind.pro index e4ca520..f0d0ab5 100644 --- a/appl/refind/refind.pro +++ b/appl/refind/refind.pro @@ -1,30 +1,32 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2015-04-02T23:48:19 -# -#------------------------------------------------- - -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = refind -TEMPLATE = app - -INCLUDEPATH = ../.. /usr/include/c++/4.9 - -SOURCES += main.cpp\ - mainwindow.cpp \ - ../../base/ReException.cpp \ - ../../base/ReQStringUtil.cpp \ - ../../base/ReLogger.cpp \ - filefinder.cpp - - -HEADERS += mainwindow.hpp \ - ../../base/rebase.hpp \ - filefinder.hpp \ - ../../base/ReQStringUtil.hpp - - -FORMS += mainwindow.ui +#------------------------------------------------- +# +# Project created by QtCreator 2015-04-02T23:48:19 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = refind +TEMPLATE = app + +INCLUDEPATH = ../.. /usr/include/c++/4.9 + +SOURCES += main.cpp\ + mainwindow.cpp \ + ../../base/ReException.cpp \ + ../../base/ReQStringUtil.cpp \ + ../../base/ReLogger.cpp \ + filefinder.cpp \ + textfinder.cpp + + +HEADERS += mainwindow.hpp \ + ../../base/rebase.hpp \ + filefinder.hpp \ + ../../base/ReQStringUtil.hpp \ + textfinder.hpp + + +FORMS += mainwindow.ui diff --git a/appl/refind/textfinder.cpp b/appl/refind/textfinder.cpp new file mode 100644 index 0000000..abaf7da --- /dev/null +++ b/appl/refind/textfinder.cpp @@ -0,0 +1,187 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. + */ + +#include "base/rebase.hpp" +#include "textfinder.hpp" + +/** + * Constructor. + * + * @param fullName path and name of the file + * @param length length of the file + * @param ignoreBinary true: binary files will be ignored + */ +TextFinder::TextFinder(const QString& fullName, int64_t length, + bool ignoreBinary) : + m_ignoreBinary(ignoreBinary), + m_filename(fullName), + m_length(length), + m_file(fullName), + m_valid(false){ + m_valid = m_file.open(QIODevice::ReadOnly); +} + +/** + * Destructor. + */ +TextFinder::~TextFinder(){ +} + +/** + * Search a text pattern in the given file. + * + * @param pattern a text pattern to search + * @param ignoreCase true: the search must be case insensitive + * @param isRegular true: the pattern is a regular expression + * @return true: the patter was found + */ +bool TextFinder::contains(const QString& pattern, bool ignoreCase, + bool isRegular){ + bool rc = false; + if (!m_ignoreBinary || !isBinary()){ + m_file.seek(0); + QTextStream stream(&m_file); + QString line; + if (isRegular){ + QRegularExpression::PatternOption option = + ignoreCase ? + QRegularExpression::CaseInsensitiveOption : + QRegularExpression::NoPatternOption; + QRegularExpression expr(pattern, option); + QRegularExpressionMatch match; + while (!stream.atEnd()){ + line = stream.readLine(); + match = expr.match(line); + if (match.hasMatch()){ + rc = true; + break; + } + } + }else{ + Qt::CaseSensitivity mode = + ignoreCase ? Qt::CaseInsensitive : Qt::CaseSensitive; + while (!stream.atEnd()){ + line = stream.readLine(); + if (line.indexOf(pattern, 0, mode) >= 0){ + rc = true; + break; + } + } + } + } + return rc; +} + +/** + * Tests whether the file is a binary file. + * + * @return true: the file is a binary file + */ +bool TextFinder::isBinary(){ + QByteArray data = m_file.read(64 * 1024); + int length = data.length(); + const char* ptr = reinterpret_cast (memchr(data.constData(), + '\0', length)); + bool rc = int(ptr - data.constData()) != length; + + if (!rc) + rc = !isText(data); + + return rc; +} + +/** + * Tests whether a string contains only text characters. + * + * @param data data to inspect + * @param trueAscii OUT: true: only 7-bit ASCII character are present + * @return true: the file is a text file + */ +bool TextFinder::isText(const QByteArray& data, bool* trueAscii){ + const uint8_t* ptr = reinterpret_cast (data.constData()); + bool isAscii = true; + uint8_t byte1; + bool rc = true; + while ((byte1 = *ptr++) != '\0'){ + // ASCII with control chars: + if (byte1 < ' ' && byte1 != '\t' && byte1 != '\n' && byte1 != '\r'){ + rc = false; + break; + } + if (rc > 0x7f) + isAscii = false; + } + if (trueAscii != NULL) + *trueAscii = rc && isAscii; + return rc; +} + +/** + * Tests whether the file is a binary file. + * + * @param data data to inspect + * @param trueAscii OUT: true: only 7-bit ASCII character are present + * @return true: the file is an UTF-8 file + */ +bool TextFinder::isUTF8(const QByteArray& data, bool* trueAscii) const{ + const uint8_t* ptr = reinterpret_cast (data.constData()); + bool isAscii = true; + uint8_t byte1; + bool rc = true; + while ((byte1 = *ptr++) != '\0'){ + // ASCII with control chars: + if ((0x20 <= byte1 && byte1 <= 0x7E) || byte1 == '\t' || byte1 == '\n' + || byte1 == '\r'){ + continue; + } + isAscii = false; + uint8_t byte2 = *ptr++; + // non-overlong 2-byte + if (0xC2 <= byte1 && byte1 <= 0xDF && 0x80 <= byte2 && byte2 <= 0xBF){ + continue; + } + uint8_t byte3 = *ptr++; + // excluding overlongs + if ((byte1 == 0xE0 && 0xA0 <= byte2 && byte2 <= 0xBF && 0x80 <= byte3 + && byte3 <= 0xBF) + // straight 3-byte + || (((0xE1 <= byte1 && byte1 <= 0xEC) || byte1 == 0xEE || byte1 == 0xEF) + && 0x80 <= byte2 && byte2 <= 0xBF && 0x80 <= byte3 && byte3 <= 0xBF) + // excluding surrogates + || (byte1 == 0xED && (0x80 <= byte2 && byte2 <= 0x9F) + && (0x80 <= byte3 && byte3 <= 0xBF))){ + continue; + } + uint8_t byte4 = *ptr++; + if ( // planes 1-3 + (byte1 == 0xF0 && 0x90 <= byte2 && byte2 <= 0xBF && 0x80 <= byte3 + && byte3 <= 0xBF && 0x80 <= byte4 && byte4 <= 0xBF) + // planes 4-15 + || (0xF1 <= byte1 && byte1 <= 0xF3 && 0x80 <= byte2 && byte2 <= 0xBF + && 0x80 <= byte3 && byte3 <= 0xBF && 0x80 <= byte4 && byte4 <= 0xBF) + // plane 16 + || (byte1 == 0xF4 && 0x80 <= byte2 && byte2 <= 0x8F && 0x80 <= byte3 + && byte3 <= 0xBF && 0x80 <= byte4 && byte4 <= 0xBF)){ + continue; + } + + rc = false; + break; + } + if (trueAscii != NULL) + *trueAscii = rc && isAscii; + return rc; +} +/** + * Tests wether the file was opened correctly + * @return true: the file can be read + */ +bool TextFinder::isValid() const{ + return m_valid; +} + diff --git a/appl/refind/textfinder.hpp b/appl/refind/textfinder.hpp new file mode 100644 index 0000000..42748de --- /dev/null +++ b/appl/refind/textfinder.hpp @@ -0,0 +1,31 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. + */ + +#ifndef TEXTFINDER_HPP +#define TEXTFINDER_HPP + +class TextFinder { +public: + TextFinder(const QString& fullName, int64_t length, bool ignoreBinary); + ~TextFinder(); +public: + bool contains(const QString& pattern, bool ignoreCase, bool isRegular); + bool isBinary(); + bool isText(const QByteArray& data, bool* trueAscii = NULL); + bool isUTF8(const QByteArray& data, bool* trueAscii) const; + bool isValid() const; + +private: + bool m_ignoreBinary; + QString m_filename; + int64_t m_length; + QFile m_file; + bool m_valid; +}; + +#endif // TEXTFINDER_HPP -- 2.39.5