diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e385ed7..0a8f0a94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,155 +1,156 @@ # set minimum version requirements cmake_minimum_required(VERSION 3.5) set(REQUIRED_QT_VERSION 5.10.0) set(KF5_VERSION "5.57.0") # handled by release scripts set(KF5_DEP_VERSION "5.56.0") # handled by release scripts # set up project project(Baloo VERSION ${KF5_VERSION}) # set up extra-cmake-modules before trying anything else include(FeatureSummary) find_package(ECM ${KF5_DEP_VERSION} NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules" URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules" ) feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) # we found extra-cmake-modules, so use it now set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ) include(ECMSetupVersion) # set up baloo versions ecm_setup_version(PROJECT VARIABLE_PREFIX BALOO VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/baloo_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5BalooConfigVersion.cmake" SOVERSION 5 ) # include optional and extra cmake stuff include(GenerateExportHeader) include(ECMGenerateHeaders) include(CMakePackageConfigHelpers) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMAddTests) include(ECMAddQch) include(ECMQtDeclareLoggingCategory) include(ECMMarkNonGuiExecutable) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") option(BUILD_EXPERIMENTAL "Build experimental features" OFF) add_feature_info(EXP ${BUILD_EXPERIMENTAL} "Build experimental features") # set up build dependencies find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Core DBus Widgets Qml Quick Test) find_package(KF5 ${KF5_DEP_VERSION} REQUIRED COMPONENTS CoreAddons Config DBusAddons I18n IdleTime Solid FileMetaData Crash KIO) find_package(LMDB) set_package_properties(LMDB PROPERTIES DESCRIPTION "Lightning Memory-Mapped Database (LMDB)" URL "http://symas.com/mdb" TYPE REQUIRED ) if(${LMDB_FOUND}) include_directories(${LMDB_INCLUDE_DIRS}) endif() # compiler flags and build system remove_definitions(-DQT_NO_CAST_FROM_ASCII) +add_definitions(-DQT_NO_FOREACH) find_package(Inotify) set_package_properties(Inotify PROPERTIES PURPOSE "Filesystem alteration notifications using inotify") set(BUILD_KINOTIFY ${Inotify_FOUND}) include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR} # we'll configure the rest here so that we don't have to # specify them in the source and test directories separately ${CMAKE_SOURCE_DIR}/src/lib ${CMAKE_BINARY_DIR}/src/lib ${CMAKE_BINARY_DIR}/src/codecs ${CMAKE_SOURCE_DIR}/src/codecs ${CMAKE_BINARY_DIR}/src/engine ${CMAKE_SOURCE_DIR}/src/engine ${CMAKE_BINARY_DIR}/src/file ${CMAKE_SOURCE_DIR}/src/file ${CMAKE_BINARY_DIR}/src/dbus ${CMAKE_SOURCE_DIR}/src/dbus ) # targets add_subdirectory(src) add_subdirectory(icons) if (BUILD_TESTING AND BUILD_KINOTIFY) add_subdirectory(tests) add_subdirectory(autotests) endif() # i18n if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) endif() # config files set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KF5Baloo") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Baloo_QCH FILE KF5BalooQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5BalooQchTargets.cmake\")") endif() configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5BalooConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5BalooConfig.cmake" PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) # install targets install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5BalooConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5BalooConfigVersion.cmake" DESTINATION ${CMAKECONFIG_INSTALL_DIR} COMPONENT devel ) install(EXPORT KF5BalooTargets NAMESPACE KF5:: DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5BalooTargets.cmake) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/baloo_version.h" DESTINATION ${KF5_INCLUDE_INSTALL_DIR} COMPONENT Devel ) install(FILES baloo.categories DESTINATION ${KDE_INSTALL_CONFDIR}) # and we're done feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/autotests/unit/file/basicindexingqueuetest.cpp b/autotests/unit/file/basicindexingqueuetest.cpp index dc950cf9..7c5e2d9e 100644 --- a/autotests/unit/file/basicindexingqueuetest.cpp +++ b/autotests/unit/file/basicindexingqueuetest.cpp @@ -1,447 +1,447 @@ /* * Copyright (C) 2014 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "fileindexerconfigutils.h" #include "../fileindexerconfig.h" #include "../basicindexingqueue.h" #include "../database.h" #include "../../lib/autotests/xattrdetector.h" #include "baloo_xattr_p.h" #include #include #include using namespace Baloo; class BasicIndexingQueueTest : public QObject { Q_OBJECT private Q_SLOTS: void testSimpleDirectoryStructure(); void textExtendedAttributeIndexing(); void textNormalAndThenExtendedAttributeIndexing(); void testExtendedAttributeIndexingWhenEmpty(); void testFileModifications(); }; Q_DECLARE_METATYPE(Xapian::Document); void BasicIndexingQueueTest::testSimpleDirectoryStructure() { qRegisterMetaType("Xapian::Document"); QStringList dirs; dirs << QLatin1String("home/"); dirs << QLatin1String("home/1"); dirs << QLatin1String("home/2"); dirs << QLatin1String("home/kde/"); dirs << QLatin1String("home/kde/1"); dirs << QLatin1String("home/docs/"); dirs << QLatin1String("home/docs/1"); QScopedPointer dir(Test::createTmpFilesAndFolders(dirs)); QStringList includeFolders; includeFolders << dir->path() + QLatin1String("/home"); QStringList excludeFolders; excludeFolders << dir->path() + QLatin1String("/home/kde"); Test::writeIndexerConfig(includeFolders, excludeFolders); QTemporaryDir dbDir; Database db; db.setPath(dbDir.path()); db.init(); FileIndexerConfig config; BasicIndexingQueue queue(&db, &config); QCOMPARE(queue.isSuspended(), false); QSignalSpy spy(&queue, SIGNAL(newDocument(quint64,Xapian::Document))); QSignalSpy spyStarted(&queue, SIGNAL(startedIndexing())); QSignalSpy spyFinished(&queue, SIGNAL(finishedIndexing())); queue.enqueue(FileMapping(dir->path() + QLatin1String("/home"))); QEventLoop loop; connect(&queue, &BasicIndexingQueue::finishedIndexing, &loop, &QEventLoop::quit); loop.exec(); // kde and kde/1 are not indexed QCOMPARE(spy.count(), 5); QCOMPARE(spyStarted.count(), 1); QCOMPARE(spyFinished.count(), 1); QStringList urls; for (int i = 0; i < spy.count(); i++) { QVariantList args = spy.at(i); QCOMPARE(args.size(), 2); Xapian::Document doc = args[1].value(); urls << QString::fromUtf8(doc.get_value(3).c_str()); } QString home = dir->path() + QLatin1String("/home"); QStringList expectedUrls; expectedUrls << home << home + QLatin1String("/1") << home + QLatin1String("/2") << home + QLatin1String("/docs") << home + QLatin1String("/docs/1"); // Based on the locale the default sorting order could be different. // Plus, we don't care about the order. We just want the same files // to be indexed QCOMPARE(expectedUrls.size(), urls.size()); - Q_FOREACH (const QString& url, expectedUrls) { + for (const QString& url : qAsConst(expectedUrls)) { QVERIFY(urls.count(url) == 1); } } void BasicIndexingQueueTest::textExtendedAttributeIndexing() { qRegisterMetaType("Xapian::Document"); QStringList dirs; dirs << QLatin1String("home/"); dirs << QLatin1String("home/1"); QScopedPointer dir(Test::createTmpFilesAndFolders(dirs)); QStringList includeFolders; includeFolders << dir->path() + QLatin1String("/home"); QStringList excludeFolders; Test::writeIndexerConfig(includeFolders, excludeFolders); QTemporaryDir dbDir; Database db; db.setPath(dbDir.path()); db.init(); FileIndexerConfig config; BasicIndexingQueue queue(&db, &config); // Index the file once // Write xattr stuff XattrDetector detector; if (!detector.isSupported(dbDir.path())) { qWarning() << "Xattr not supported on this filesystem"; return; } const QString fileName = dir->path() + QStringLiteral("/home/1"); QString rat = QString::number(4); QVERIFY(baloo_setxattr(fileName, QLatin1String("user.baloo.rating"), rat) != -1); QStringList tags; tags << QLatin1String("TagA") << QLatin1String("TagB") << QLatin1String("Tag C") << QLatin1String("Tag/D"); QString tagStr = tags.join(QLatin1String(",")); QVERIFY(baloo_setxattr(fileName, QLatin1String("user.xdg.tags"), tagStr) != -1); const QString userComment(QLatin1String("UserComment")); QVERIFY(baloo_setxattr(fileName, QLatin1String("user.xdg.comment"), userComment) != -1); QSignalSpy spy(&queue, SIGNAL(newDocument(quint64,Xapian::Document))); queue.enqueue(FileMapping(fileName), Baloo::ExtendedAttributesOnly); QEventLoop loop; connect(&queue, &BasicIndexingQueue::finishedIndexing, &loop, &QEventLoop::quit); loop.exec(); QCOMPARE(spy.size(), 1); int id = spy.first().at(0).toInt(); Xapian::Document doc = spy.first().at(1).value(); spy.clear(); Xapian::TermIterator iter = doc.termlist_begin(); QCOMPARE(*iter, std::string("Cusercomment")); ++iter; QCOMPARE(*iter, std::string("R4")); ++iter; QCOMPARE(*iter, std::string("TAG-TagA")); ++iter; QCOMPARE(*iter, std::string("TAG-TagB")); ++iter; QCOMPARE(*iter, std::string("TAG-Tag C")); ++iter; QCOMPARE(*iter, std::string("TAG-Tag/D")); ++iter; QCOMPARE(*iter, std::string("TAtaga")); ++iter; QCOMPARE(*iter, std::string("TAtagb")); ++iter; QCOMPARE(*iter, std::string("TAtag")); ++iter; QCOMPARE(*iter, std::string("TAc")); ++iter; QCOMPARE(*iter, std::string("TAd")); ++iter; QCOMPARE(iter, doc.termlist_end()); db.xapianDatabase()->replaceDocument(id, doc); db.xapianDatabase()->commit(); QVERIFY(baloo_setxattr(fileName, QLatin1String("user.xdg.comment"), QStringLiteral("noob")) != -1); queue.enqueue(FileMapping(fileName), Baloo::ExtendedAttributesOnly); loop.exec(); { QCOMPARE(spy.size(), 1); Xapian::Document doc = spy.first().at(1).value(); spy.clear(); iter = doc.termlist_begin(); QCOMPARE(*iter, std::string("Cnoob")); ++iter; QCOMPARE(*iter, std::string("R4")); ++iter; QCOMPARE(*iter, std::string("TAG-TagA")); ++iter; QCOMPARE(*iter, std::string("TAG-TagB")); ++iter; QCOMPARE(*iter, std::string("TAG-Tag C")); ++iter; QCOMPARE(*iter, std::string("TAG-Tag/D")); ++iter; QCOMPARE(*iter, std::string("TAtaga")); ++iter; QCOMPARE(*iter, std::string("TAtagb")); ++iter; QCOMPARE(*iter, std::string("TAtag")); ++iter; QCOMPARE(*iter, std::string("TAc")); ++iter; QCOMPARE(*iter, std::string("TAd")); ++iter; QCOMPARE(iter, doc.termlist_end()); } } void BasicIndexingQueueTest::textNormalAndThenExtendedAttributeIndexing() { qRegisterMetaType("Xapian::Document"); QStringList dirs; dirs << QLatin1String("home/"); dirs << QLatin1String("home/1"); QScopedPointer dir(Test::createTmpFilesAndFolders(dirs)); QStringList includeFolders; includeFolders << dir->path() + QLatin1String("/home"); QStringList excludeFolders; Test::writeIndexerConfig(includeFolders, excludeFolders); QTemporaryDir dbDir; Database db; db.setPath(dbDir.path()); db.init(); FileIndexerConfig config; BasicIndexingQueue queue(&db, &config); // Index the file once // Write xattr stuff XattrDetector detector; if (!detector.isSupported(dbDir.path())) { qWarning() << "Xattr not supported on this filesystem"; return; } const QString fileName = dir->path() + QStringLiteral("/home/1"); QString rat = QString::number(4); QVERIFY(baloo_setxattr(fileName, QLatin1String("user.baloo.rating"), rat) != -1); QStringList tags; tags << QLatin1String("TagA") << QLatin1String("TagB") << QLatin1String("Tag C") << QLatin1String("Tag/D"); QString tagStr = tags.join(QLatin1String(",")); QVERIFY(baloo_setxattr(fileName, QLatin1String("user.xdg.tags"), tagStr) != -1); const QString userComment(QLatin1String("UserComment")); QVERIFY(baloo_setxattr(fileName, QLatin1String("user.xdg.comment"), userComment) != -1); QSignalSpy spy(&queue, SIGNAL(newDocument(quint64,Xapian::Document))); queue.enqueue(FileMapping(fileName)); QEventLoop loop; connect(&queue, &BasicIndexingQueue::finishedIndexing, &loop, &QEventLoop::quit); loop.exec(); QCOMPARE(spy.size(), 1); int id = spy.first().at(0).toInt(); Xapian::Document doc = spy.first().at(1).value(); spy.clear(); Xapian::TermIterator iter = doc.termlist_begin(); iter.skip_to("C"); QCOMPARE(*iter, std::string("Cusercomment")); iter.skip_to("R"); QCOMPARE(*iter, std::string("R4")); ++iter; QCOMPARE(*iter, std::string("TAG-TagA")); ++iter; QCOMPARE(*iter, std::string("TAG-TagB")); ++iter; QCOMPARE(*iter, std::string("TAG-Tag C")); ++iter; QCOMPARE(*iter, std::string("TAG-Tag/D")); ++iter; QCOMPARE(*iter, std::string("TAtaga")); ++iter; QCOMPARE(*iter, std::string("TAtagb")); ++iter; QCOMPARE(*iter, std::string("TAtag")); ++iter; QCOMPARE(*iter, std::string("TAc")); ++iter; QCOMPARE(*iter, std::string("TAd")); db.xapianDatabase()->replaceDocument(id, doc); db.xapianDatabase()->commit(); QVERIFY(baloo_setxattr(fileName, QLatin1String("user.xdg.comment"), QStringLiteral("noob")) != -1); queue.enqueue(FileMapping(fileName), Baloo::ExtendedAttributesOnly); loop.exec(); { QCOMPARE(spy.size(), 1); Xapian::Document doc = spy.first().at(1).value(); spy.clear(); iter = doc.termlist_begin(); QCOMPARE(*iter, std::string("Cnoob")); ++iter; QCOMPARE(*iter, std::string("R4")); ++iter; QCOMPARE(*iter, std::string("TAG-TagA")); ++iter; QCOMPARE(*iter, std::string("TAG-TagB")); ++iter; QCOMPARE(*iter, std::string("TAG-Tag C")); ++iter; QCOMPARE(*iter, std::string("TAG-Tag/D")); ++iter; QCOMPARE(*iter, std::string("TAtaga")); ++iter; QCOMPARE(*iter, std::string("TAtagb")); ++iter; QCOMPARE(*iter, std::string("TAtag")); ++iter; QCOMPARE(*iter, std::string("TAc")); ++iter; QCOMPARE(*iter, std::string("TAd")); ++iter; QCOMPARE(iter, doc.termlist_end()); } } void BasicIndexingQueueTest::testExtendedAttributeIndexingWhenEmpty() { qRegisterMetaType("Xapian::Document"); QStringList dirs; dirs << QLatin1String("home/"); dirs << QLatin1String("home/1"); QScopedPointer dir(Test::createTmpFilesAndFolders(dirs)); QStringList includeFolders; includeFolders << dir->path() + QLatin1String("/home"); QStringList excludeFolders; Test::writeIndexerConfig(includeFolders, excludeFolders); QTemporaryDir dbDir; Database db; db.setPath(dbDir.path()); db.init(); FileIndexerConfig config; BasicIndexingQueue queue(&db, &config); const QString fileName = dir->path() + QStringLiteral("/home/1"); QSignalSpy spy(&queue, SIGNAL(newDocument(quint64,Xapian::Document))); queue.enqueue(FileMapping(fileName), Baloo::ExtendedAttributesOnly); QEventLoop loop; connect(&queue, &BasicIndexingQueue::finishedIndexing, &loop, &QEventLoop::quit); loop.exec(); QCOMPARE(spy.size(), 0); } void BasicIndexingQueueTest::testFileModifications() { qRegisterMetaType("Xapian::Document"); QStringList dirs; dirs << QLatin1String("home/"); dirs << QLatin1String("home/1"); QScopedPointer dir(Test::createTmpFilesAndFolders(dirs)); QStringList includeFolders; includeFolders << dir->path() + QLatin1String("/home"); QStringList excludeFolders; Test::writeIndexerConfig(includeFolders, excludeFolders); QTemporaryDir dbDir; Database db; db.setPath(dbDir.path()); db.init(); FileIndexerConfig config; BasicIndexingQueue queue(&db, &config); const QString fileName = dir->path() + QStringLiteral("/home/1"); QSignalSpy spy(&queue, SIGNAL(newDocument(quint64,Xapian::Document))); queue.enqueue(FileMapping(fileName), Baloo::AutoUpdateFolder); QEventLoop loop; connect(&queue, &BasicIndexingQueue::finishedIndexing, &loop, &QEventLoop::quit); loop.exec(); QCOMPARE(spy.size(), 1); spy.takeFirst(); // Again queue.enqueue(FileMapping(fileName), Baloo::AutoUpdateFolder); loop.exec(); QCOMPARE(spy.size(), 1); } QTEST_MAIN(BasicIndexingQueueTest) #include "basicindexingqueuetest.moc" diff --git a/autotests/unit/file/fileindexerconfigutils.h b/autotests/unit/file/fileindexerconfigutils.h index 69438d99..39a8239a 100644 --- a/autotests/unit/file/fileindexerconfigutils.h +++ b/autotests/unit/file/fileindexerconfigutils.h @@ -1,156 +1,158 @@ /* This file is part of the Nepomuk KDE project. Copyright (C) 2011 Sebastian Trueg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef FILEINDEXERCONFIGUTILS_H #define FILEINDEXERCONFIGUTILS_H #include #include #include #include #include #include #ifdef Q_OS_WIN #include #endif namespace Baloo { namespace Test { void writeIndexerConfig(const QStringList& includeFolders, const QStringList& excludeFolders, const QStringList& excludeFilters = QStringList(), bool indexHidden = false) { QStandardPaths::setTestModeEnabled(true); KConfig fileIndexerConfig(QStringLiteral("baloofilerc")); fileIndexerConfig.group("General").writePathEntry("folders", includeFolders); fileIndexerConfig.group("General").writePathEntry("exclude folders", excludeFolders); fileIndexerConfig.group("General").writeEntry("exclude filters", excludeFilters); fileIndexerConfig.group("General").writeEntry("index hidden folders", indexHidden); fileIndexerConfig.sync(); } QTemporaryDir* createTmpFolders(const QStringList& folders) { QTemporaryDir* tmpDir = new QTemporaryDir(); // If the temporary directory is in a hidden folder, then the tests will fail, // so we use /tmp/ instead. // TODO: Find a better solution if (QFileInfo(tmpDir->path()).isHidden()) { delete tmpDir; tmpDir = new QTemporaryDir(QStringLiteral("/tmp/")); } - Q_FOREACH (const QString & f, folders) { + for (const QString & f : folders) { QDir dir(tmpDir->path()); - Q_FOREACH (const QString & sf, f.split(QLatin1Char('/'), QString::SkipEmptyParts)) { + const auto lst = f.split(QLatin1Char('/'), QString::SkipEmptyParts); + for (const QString & sf : lst) { if (!dir.exists(sf)) { dir.mkdir(sf); } #ifdef Q_OS_WIN if(sf.startsWith(QLatin1String("."))) { if(!SetFileAttributesW(reinterpret_cast((dir.path() + "/" + sf).utf16()), FILE_ATTRIBUTE_HIDDEN)) { qWarning("failed to set 'hidden' attribute!"); } } #endif dir.cd(sf); } } return tmpDir; } QTemporaryDir* createTmpFilesAndFolders(const QStringList& list) { QTemporaryDir* tmpDir = new QTemporaryDir(); // If the temporary directory is in a hidden folder, then the tests will fail, // so we use /tmp/ instead. // TODO: Find a better solution if (QFileInfo(tmpDir->path()).isHidden()) { delete tmpDir; tmpDir = new QTemporaryDir(QStringLiteral("/tmp/")); } - Q_FOREACH (const QString& f, list) { + for (const QString& f : list) { if (f.endsWith(QLatin1Char('/'))) { QDir dir(tmpDir->path()); - Q_FOREACH (const QString & sf, f.split(QLatin1Char('/'), QString::SkipEmptyParts)) { + const auto lst = f.split(QLatin1Char('/'), QString::SkipEmptyParts); + for (const QString & sf : lst) { if (!dir.exists(sf)) { dir.mkdir(sf); } #ifdef Q_OS_WIN if(sf.startsWith(QLatin1String("."))) { if(!SetFileAttributesW(reinterpret_cast((dir.path() + "/" + sf).utf16()), FILE_ATTRIBUTE_HIDDEN)) { qWarning("failed to set 'hidden' attribute!"); } } #endif dir.cd(sf); } } else { QFile file(tmpDir->path() + QLatin1Char('/') + f); file.open(QIODevice::WriteOnly); QTextStream stream(&file); stream << "test"; } } return tmpDir; } // // Trying to put all cases into one folder tree: // |- indexedRootDir // |- indexedSubDir // |- indexedSubSubDir // |- excludedSubSubDir // |- .hiddenSubSubDir // |- ignoredSubFolderToIndexedHidden // |- indexedSubFolderToIndexedHidden // |- excludedSubDir // |- indexedSubDirToExcluded // |- .indexedHiddenSubDirToExcluded // |- .hiddenSubDir // |- .indexedHiddenSubDir // |- ignoredRootDir // |- excludedRootDir // const QString indexedRootDir = QStringLiteral("d1"); const QString indexedSubDir = QStringLiteral("d1/sd1"); const QString indexedSubSubDir = QStringLiteral("d1/sd1/ssd1"); const QString excludedSubSubDir = QStringLiteral("d1/sd1/ssd2"); const QString hiddenSubSubDir = QStringLiteral("d1/sd1/.ssd3"); const QString ignoredSubFolderToIndexedHidden = QStringLiteral("d1/sd1/.ssd3/isfh1"); const QString indexedSubFolderToIndexedHidden = QStringLiteral("d1/sd1/.ssd3/isfh2"); const QString excludedSubDir = QStringLiteral("d1/sd2"); const QString indexedSubDirToExcluded = QStringLiteral("d1/sd2/isde1"); const QString indexedHiddenSubDirToExcluded = QStringLiteral("d1/sd2/.isde2"); const QString hiddenSubDir = QStringLiteral("d1/.sd3"); const QString indexedHiddenSubDir = QStringLiteral("d1/.sd4"); const QString ignoredRootDir = QStringLiteral("d2"); const QString excludedRootDir = QStringLiteral("d3"); } } #endif // FILEINDEXERCONFIGUTILS_H diff --git a/autotests/unit/lib/xattrdetector.cpp b/autotests/unit/lib/xattrdetector.cpp index 95919e7a..9f7b9528 100644 --- a/autotests/unit/lib/xattrdetector.cpp +++ b/autotests/unit/lib/xattrdetector.cpp @@ -1,121 +1,121 @@ /* * This file is part of the KDE Baloo Project * Copyright (C) 2014 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "xattrdetector.h" #include "baloo_xattr_p.h" #include #include #include #include #include #include using namespace Baloo; class XattrDetector::Private { public: QStringList m_unSupportedPaths; QStringList m_supportedPaths; void init(); bool m_initialized; }; XattrDetector::XattrDetector(QObject* parent) : QObject(parent) , d(new Private) { d->m_initialized = false; } void XattrDetector::Private::init() { - QList devices + const QList devices = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); QStringList mountPaths; - Q_FOREACH (const Solid::Device& dev, devices) { + for (const Solid::Device& dev : devices) { const Solid::StorageAccess* sa = dev.as(); if (!sa->isAccessible()) continue; mountPaths << sa->filePath(); } mountPaths << QDir::homePath(); - Q_FOREACH (const QString& mountPath, mountPaths) { + for (const QString& mountPath : qAsConst(mountPaths)) { while (1) { QString randFile = QLatin1String("baloo-xattr-check-") + QUuid::createUuid().toString(); const QString url = mountPath + QDir::separator() + randFile; if (QFile::exists(url)) continue; QFile file(url); if (!file.open(QIODevice::WriteOnly)) { m_unSupportedPaths << mountPath; break; } file.close(); int ret = baloo_setxattr(url, QStringLiteral("test"), QStringLiteral("0")); if (ret != -1) { // Check the actual error? m_unSupportedPaths << mountPath; } else { m_supportedPaths << mountPath; } QFile::remove(url); break; } } m_unSupportedPaths << QStringLiteral("/tmp") << QStringLiteral("/proc"); qDebug() << "supportedPaths:" << m_supportedPaths; qDebug() << "UnsupportedPaths:" << m_unSupportedPaths; m_initialized = true; } XattrDetector::~XattrDetector() { delete d; } bool XattrDetector::isSupported(const QString& path) { #ifdef Q_OS_WIN return false; #endif if (!d->m_initialized) d->init(); - Q_FOREACH (const QString& p, d->m_supportedPaths) { + for (const QString& p : qAsConst(d->m_supportedPaths)) { if (path.startsWith(p)) return true; } - Q_FOREACH (const QString& p, d->m_unSupportedPaths) { + for (const QString& p : qAsConst(d->m_unSupportedPaths)) { if (path.startsWith(p)) return false; } return true; } diff --git a/src/engine/queryparser.cpp b/src/engine/queryparser.cpp index 56716e67..3b16f6bb 100644 --- a/src/engine/queryparser.cpp +++ b/src/engine/queryparser.cpp @@ -1,171 +1,171 @@ /* * This file is part of the KDE Baloo Project * Copyright (C) 2014-2015 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "queryparser.h" #include "enginequery.h" #include #include #include using namespace Baloo; QueryParser::QueryParser() : m_autoExpandSize(3) { } namespace { bool containsSpace(const QString& string) { - Q_FOREACH (const QChar& ch, string) { + for (const QChar& ch : string) { if (ch.isSpace()) return true; } return false; } } EngineQuery QueryParser::parseQuery(const QString& text_, const QString& prefix) { Q_ASSERT(!text_.isEmpty()); QString text(text_); text.replace(QLatin1Char('_'), QLatin1Char(' ')); QVector queries; QVector phraseQueries; int start = 0; int end = 0; int position = 0; bool inDoubleQuotes = false; bool inSingleQuotes = false; bool inPhrase = false; QTextBoundaryFinder bf(QTextBoundaryFinder::Word, text); for (; bf.position() != -1; bf.toNextBoundary()) { if (bf.boundaryReasons() & QTextBoundaryFinder::StartOfItem) { // // Check the previous delimiter int pos = bf.position(); if (pos != end) { QString delim = text_.mid(end, pos-end); if (delim.contains(QLatin1Char('"'))) { if (inDoubleQuotes) { queries << EngineQuery(phraseQueries, EngineQuery::Phrase); phraseQueries.clear(); inDoubleQuotes = false; } else { inDoubleQuotes = true; } } else if (delim.contains(QLatin1Char('\''))) { if (inSingleQuotes) { queries << EngineQuery(phraseQueries, EngineQuery::Phrase); phraseQueries.clear(); inSingleQuotes = false; } else { inSingleQuotes = true; } } else if (!containsSpace(delim)) { if (!inPhrase && !queries.isEmpty()) { EngineQuery q = queries.takeLast(); q.setOp(EngineQuery::Equal); phraseQueries << q; inPhrase = true; } } else if (inPhrase && !phraseQueries.isEmpty()) { queries << EngineQuery(phraseQueries, EngineQuery::Phrase); phraseQueries.clear(); inPhrase = false; } } start = bf.position(); continue; } else if (bf.boundaryReasons() & QTextBoundaryFinder::EndOfItem) { end = bf.position(); QString str = text.mid(start, end - start); // Remove all accents and lower it const QString denormalized = str.normalized(QString::NormalizationForm_KD).toLower(); QString cleanString; - Q_FOREACH (const QChar& ch, denormalized) { + for (const QChar& ch : denormalized) { auto cat = ch.category(); if (cat != QChar::Mark_NonSpacing && cat != QChar::Mark_SpacingCombining && cat != QChar::Mark_Enclosing) { cleanString.append(ch); } } str = cleanString.normalized(QString::NormalizationForm_KC); const QString term = prefix + str; const QByteArray arr = term.toUtf8(); position++; if (inDoubleQuotes || inSingleQuotes || inPhrase) { phraseQueries << EngineQuery(arr, position); } else { if (m_autoExpandSize && arr.size() >= m_autoExpandSize) { queries << EngineQuery(arr, EngineQuery::StartsWith, position); } else { queries << EngineQuery(arr, position); } } } } if (inPhrase) { queries << EngineQuery(phraseQueries, EngineQuery::Phrase); phraseQueries.clear(); inPhrase = false; } if (!phraseQueries.isEmpty()) { for (EngineQuery& q : phraseQueries) { if (m_autoExpandSize && q.term().size() >= m_autoExpandSize) { q.setOp(EngineQuery::StartsWith); } else { q.setOp(EngineQuery::Equal); } } queries << phraseQueries; phraseQueries.clear(); } if (queries.size() == 1) { return queries.first(); } return EngineQuery(queries, EngineQuery::And); } void QueryParser::setAutoExapandSize(int size) { m_autoExpandSize = size; } diff --git a/src/engine/termgenerator.cpp b/src/engine/termgenerator.cpp index d07c7d15..f3f28513 100644 --- a/src/engine/termgenerator.cpp +++ b/src/engine/termgenerator.cpp @@ -1,140 +1,140 @@ /* * This file is part of the KDE Baloo project. * Copyright (C) 2014-2015 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "termgenerator.h" #include "document.h" #include #include using namespace Baloo; TermGenerator::TermGenerator(Document& doc) : m_doc(doc) , m_position(1) { } void TermGenerator::indexText(const QString& text) { indexText(text, QByteArray()); } QStringList TermGenerator::termList(const QString& text_) { QString text(text_); text.replace(QLatin1Char('_'), QLatin1Char(' ')); int start = 0; int end = 0; QStringList list; QTextBoundaryFinder bf(QTextBoundaryFinder::Word, text); for (; bf.position() != -1; bf.toNextBoundary()) { if (bf.boundaryReasons() & QTextBoundaryFinder::StartOfItem) { start = bf.position(); continue; } else if (bf.boundaryReasons() & QTextBoundaryFinder::EndOfItem) { end = bf.position(); QString str = text.mid(start, end - start); // Remove all accents. It is important to call toLower after normalization, // since some exotic unicode symbols can remain uppercase const QString denormalized = str.normalized(QString::NormalizationForm_KD).toLower(); QString cleanString; cleanString.reserve(denormalized.size()); - Q_FOREACH (const QChar& ch, denormalized) { + for (const QChar& ch : denormalized) { auto cat = ch.category(); if (cat != QChar::Mark_NonSpacing && cat != QChar::Mark_SpacingCombining && cat != QChar::Mark_Enclosing) { cleanString.append(ch); } } str = cleanString.normalized(QString::NormalizationForm_KC); if (!str.isEmpty()) { list << str; } } } return list; } void TermGenerator::indexText(const QString& text, const QByteArray& prefix) { QStringList terms = termList(text); for (const QString& term : terms) { QByteArray arr = term.toUtf8(); QByteArray finalArr = prefix + arr; finalArr = finalArr.mid(0, maxTermSize); m_doc.addPositionTerm(finalArr, m_position); m_position++; } } void TermGenerator::indexFileNameText(const QString& text, const QByteArray& prefix) { QStringList terms = termList(text); for (const QString& term : terms) { QByteArray arr = term.toUtf8(); QByteArray finalArr = prefix + arr; finalArr = finalArr.mid(0, maxTermSize); m_doc.addFileNamePositionTerm(finalArr, m_position); m_position++; } } void TermGenerator::indexFileNameText(const QString& text) { indexFileNameText(text, QByteArray()); } void TermGenerator::indexXattrText(const QString& text, const QByteArray& prefix) { QStringList terms = termList(text); for (const QString& term : terms) { QByteArray arr = term.toUtf8(); QByteArray finalArr = prefix + arr; finalArr = finalArr.mid(0, maxTermSize); m_doc.addXattrPositionTerm(finalArr, m_position); m_position++; } } int TermGenerator::position() const { return m_position; } void TermGenerator::setPosition(int position) { m_position = position; } diff --git a/src/engine/transaction.cpp b/src/engine/transaction.cpp index b1c1659e..27cffda6 100644 --- a/src/engine/transaction.cpp +++ b/src/engine/transaction.cpp @@ -1,640 +1,640 @@ /* * This file is part of the KDE Baloo project. * Copyright (C) 2015 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "transaction.h" #include "postingdb.h" #include "documentdb.h" #include "documenturldb.h" #include "documentiddb.h" #include "positiondb.h" #include "documentdatadb.h" #include "mtimedb.h" #include "document.h" #include "enginequery.h" #include "andpostingiterator.h" #include "orpostingiterator.h" #include "phraseanditerator.h" #include "writetransaction.h" #include "idutils.h" #include "database.h" #include "databasesize.h" #include "enginedebug.h" #include #include using namespace Baloo; Transaction::Transaction(const Database& db, Transaction::TransactionType type) : m_dbis(db.m_dbis) , m_env(db.m_env) , m_writeTrans(nullptr) { uint flags = type == ReadOnly ? MDB_RDONLY : 0; int rc = mdb_txn_begin(db.m_env, nullptr, flags, &m_txn); if (rc) { qCDebug(ENGINE) << "Transaction" << mdb_strerror(rc); return; } if (type == ReadWrite) { m_writeTrans = new WriteTransaction(m_dbis, m_txn); } } Transaction::Transaction(Database* db, Transaction::TransactionType type) : Transaction(*db, type) { } Transaction::~Transaction() { if (m_writeTrans) { qWarning(ENGINE) << "Closing an active WriteTransaction without calling abort/commit"; } if (m_txn) { abort(); } } bool Transaction::hasDocument(quint64 id) const { Q_ASSERT(id > 0); IdFilenameDB idFilenameDb(m_dbis.idFilenameDbi, m_txn); return idFilenameDb.contains(id); } bool Transaction::inPhaseOne(quint64 id) const { Q_ASSERT(id > 0); DocumentIdDB contentIndexingDb(m_dbis.contentIndexingDbi, m_txn); return contentIndexingDb.contains(id); } bool Transaction::hasFailed(quint64 id) const { Q_ASSERT(id > 0); DocumentIdDB failedIdDb(m_dbis.failedIdDbi, m_txn); return failedIdDb.contains(id); } QByteArray Transaction::documentUrl(quint64 id) const { Q_ASSERT(m_txn); Q_ASSERT(id > 0); DocumentUrlDB docUrlDb(m_dbis.idTreeDbi, m_dbis.idFilenameDbi, m_txn); return docUrlDb.get(id); } quint64 Transaction::documentId(const QByteArray& path) const { Q_ASSERT(m_txn); Q_ASSERT(!path.isEmpty()); DocumentUrlDB docUrlDb(m_dbis.idTreeDbi, m_dbis.idFilenameDbi, m_txn); QList li = path.split('/'); quint64 parentId = 0; for (const QByteArray& fileName : li) { if (fileName.isEmpty()) { continue; } parentId = docUrlDb.getId(parentId, fileName); if (!parentId) { return 0; } } return parentId; } QVector Transaction::childrenDocumentId(quint64 parentId) const { DocumentUrlDB docUrlDB(m_dbis.idTreeDbi, m_dbis.idFilenameDbi, m_txn); return docUrlDB.getChildren(parentId); } DocumentTimeDB::TimeInfo Transaction::documentTimeInfo(quint64 id) const { Q_ASSERT(m_txn); DocumentTimeDB docTimeDb(m_dbis.docTimeDbi, m_txn); return docTimeDb.get(id); } QByteArray Transaction::documentData(quint64 id) const { Q_ASSERT(m_txn); Q_ASSERT(id > 0); DocumentDataDB docDataDb(m_dbis.docDataDbi, m_txn); return docDataDb.get(id); } bool Transaction::hasChanges() const { Q_ASSERT(m_txn); if (!m_writeTrans) { qCWarning(ENGINE) << "m_writeTrans is null"; return false; } return m_writeTrans->hasChanges(); } QVector Transaction::fetchPhaseOneIds(int size) const { Q_ASSERT(m_txn); Q_ASSERT(size > 0); DocumentIdDB contentIndexingDb(m_dbis.contentIndexingDbi, m_txn); return contentIndexingDb.fetchItems(size); } QVector Transaction::fetchTermsStartingWith(const QByteArray& term) const { Q_ASSERT(term.size() > 0); PostingDB postingDb(m_dbis.postingDbi, m_txn); return postingDb.fetchTermsStartingWith(term); } uint Transaction::phaseOneSize() const { Q_ASSERT(m_txn); DocumentIdDB contentIndexingDb(m_dbis.contentIndexingDbi, m_txn); return contentIndexingDb.size(); } uint Transaction::size() const { Q_ASSERT(m_txn); DocumentDB docTermsDb(m_dbis.docTermsDbi, m_txn); return docTermsDb.size(); } // // Write Operations // void Transaction::setPhaseOne(quint64 id) { Q_ASSERT(m_txn); Q_ASSERT(id > 0); Q_ASSERT(m_writeTrans); DocumentIdDB contentIndexingDb(m_dbis.contentIndexingDbi, m_txn); contentIndexingDb.put(id); } void Transaction::removePhaseOne(quint64 id) { Q_ASSERT(m_txn); Q_ASSERT(id > 0); Q_ASSERT(m_writeTrans); DocumentIdDB contentIndexingDb(m_dbis.contentIndexingDbi, m_txn); contentIndexingDb.del(id); } void Transaction::addFailed(quint64 id) { Q_ASSERT(m_txn); Q_ASSERT(id > 0); Q_ASSERT(m_writeTrans); DocumentIdDB failedIdDb(m_dbis.failedIdDbi, m_txn); failedIdDb.put(id); } void Transaction::addDocument(const Document& doc) { Q_ASSERT(m_txn); Q_ASSERT(doc.id() > 0); if (!m_writeTrans) { qCWarning(ENGINE) << "m_writeTrans is null"; return; } m_writeTrans->addDocument(doc); } void Transaction::removeDocument(quint64 id) { Q_ASSERT(m_txn); Q_ASSERT(id > 0); if (!m_writeTrans) { qCWarning(ENGINE) << "m_writeTrans is null"; return; } m_writeTrans->removeDocument(id); } void Transaction::removeRecursively(quint64 id) { Q_ASSERT(m_txn); Q_ASSERT(id > 0); if (!m_writeTrans) { qCWarning(ENGINE) << "m_writeTrans is null"; return; } m_writeTrans->removeRecursively(id); } void Transaction::replaceDocument(const Document& doc, DocumentOperations operations) { Q_ASSERT(m_txn); Q_ASSERT(doc.id() > 0); Q_ASSERT(m_writeTrans); if (!hasDocument(doc.id())) { qCDebug(ENGINE) << "Transaction::replaceDocument" << "Document does not exist"; } if (!m_writeTrans) { qCWarning(ENGINE) << "m_writeTrans is null"; return; } m_writeTrans->replaceDocument(doc, operations); } void Transaction::commit() { Q_ASSERT(m_txn); if (!m_writeTrans) { qCWarning(ENGINE) << "m_writeTrans is null"; return; } m_writeTrans->commit(); delete m_writeTrans; m_writeTrans = nullptr; int rc = mdb_txn_commit(m_txn); if (rc) { qCWarning(ENGINE) << "Transaction::commit" << mdb_strerror(rc); } m_txn = nullptr; } void Transaction::abort() { Q_ASSERT(m_txn); mdb_txn_abort(m_txn); m_txn = nullptr; delete m_writeTrans; m_writeTrans = nullptr; } // // Queries // PostingIterator* Transaction::postingIterator(const EngineQuery& query) const { PostingDB postingDb(m_dbis.postingDbi, m_txn); PositionDB positionDb(m_dbis.positionDBi, m_txn); if (query.leaf()) { if (query.op() == EngineQuery::Equal) { return postingDb.iter(query.term()); } else if (query.op() == EngineQuery::StartsWith) { return postingDb.prefixIter(query.term()); } else { Q_ASSERT(0); } } if (query.subQueries().isEmpty()) { return nullptr; } QVector vec; vec.reserve(query.subQueries().size()); if (query.op() == EngineQuery::Phrase) { const auto subQueries = query.subQueries(); for (const EngineQuery& q : subQueries) { if (!q.leaf()) { qCDebug(ENGINE) << "Transaction::toPostingIterator" << "Phrase queries must contain leaf queries"; continue; } vec << positionDb.iter(q.term()); } return new PhraseAndIterator(vec); } const auto subQueries = query.subQueries(); for (const EngineQuery& q : subQueries) { auto iterator = postingIterator(q); if (iterator) { vec << iterator; } else if (query.op() == EngineQuery::And) { return nullptr; } } if (vec.empty()) { return nullptr; } else if (vec.size() == 1) { return vec.takeFirst(); } if (query.op() == EngineQuery::And) { return new AndPostingIterator(vec); } else if (query.op() == EngineQuery::Or) { return new OrPostingIterator(vec); } Q_ASSERT(0); return nullptr; } PostingIterator* Transaction::postingCompIterator(const QByteArray& prefix, qlonglong value, PostingDB::Comparator com) const { PostingDB postingDb(m_dbis.postingDbi, m_txn); return postingDb.compIter(prefix, value, com); } PostingIterator* Transaction::mTimeIter(quint32 mtime, MTimeDB::Comparator com) const { MTimeDB mTimeDb(m_dbis.mtimeDbi, m_txn); return mTimeDb.iter(mtime, com); } PostingIterator* Transaction::mTimeRangeIter(quint32 beginTime, quint32 endTime) const { MTimeDB mTimeDb(m_dbis.mtimeDbi, m_txn); return mTimeDb.iterRange(beginTime, endTime); } PostingIterator* Transaction::docUrlIter(quint64 id) const { DocumentUrlDB docUrlDb(m_dbis.idTreeDbi, m_dbis.idFilenameDbi, m_txn); return docUrlDb.iter(id); } QVector Transaction::exec(const EngineQuery& query, int limit) const { Q_ASSERT(m_txn); QVector results; PostingIterator* it = postingIterator(query); if (!it) { return results; } while (it->next() && limit) { results << it->docId(); limit--; } return results; } // // Introspection // QVector Transaction::documentTerms(quint64 docId) const { Q_ASSERT(docId); DocumentDB documentTermsDB(m_dbis.docTermsDbi, m_txn); return documentTermsDB.get(docId); } QVector Transaction::documentFileNameTerms(quint64 docId) const { Q_ASSERT(docId); DocumentDB documentFileNameTermsDB(m_dbis.docFilenameTermsDbi, m_txn); return documentFileNameTermsDB.get(docId); } QVector Transaction::documentXattrTerms(quint64 docId) const { Q_ASSERT(docId); DocumentDB documentXattrTermsDB(m_dbis.docXattrTermsDbi, m_txn); return documentXattrTermsDB.get(docId); } // // File Size // static size_t dbiSize(MDB_txn* txn, MDB_dbi dbi) { MDB_stat stat; mdb_stat(txn, dbi, &stat); return (stat.ms_branch_pages + stat.ms_leaf_pages + stat.ms_overflow_pages) * stat.ms_psize; } DatabaseSize Transaction::dbSize() { DatabaseSize dbSize; dbSize.postingDb = dbiSize(m_txn, m_dbis.postingDbi); dbSize.positionDb = dbiSize(m_txn, m_dbis.positionDBi); dbSize.docTerms = dbiSize(m_txn, m_dbis.docTermsDbi); dbSize.docFilenameTerms = dbiSize(m_txn, m_dbis.docFilenameTermsDbi); dbSize.docXattrTerms = dbiSize(m_txn, m_dbis.docXattrTermsDbi); dbSize.idTree = dbiSize(m_txn, m_dbis.idTreeDbi); dbSize.idFilename = dbiSize(m_txn, m_dbis.idFilenameDbi); dbSize.docTime = dbiSize(m_txn, m_dbis.docTimeDbi); dbSize.docData = dbiSize(m_txn, m_dbis.docDataDbi); dbSize.contentIndexingIds = dbiSize(m_txn, m_dbis.contentIndexingDbi); dbSize.failedIds = dbiSize(m_txn, m_dbis.failedIdDbi); dbSize.mtimeDb = dbiSize(m_txn, m_dbis.mtimeDbi); dbSize.expectedSize = dbSize.positionDb + dbSize.positionDb + dbSize.docTerms + dbSize.docFilenameTerms + dbSize.docXattrTerms + dbSize.idTree + dbSize.idFilename + dbSize.docTime + dbSize.docData + dbSize.contentIndexingIds + dbSize.failedIds + dbSize.mtimeDb; MDB_envinfo info; mdb_env_info(m_env, &info); dbSize.actualSize = info.me_last_pgno * 4096; // TODO: separate page size return dbSize; } // // Debugging // void Transaction::checkFsTree() { DocumentDB documentTermsDB(m_dbis.docTermsDbi, m_txn); DocumentDB documentXattrTermsDB(m_dbis.docXattrTermsDbi, m_txn); DocumentDB documentFileNameTermsDB(m_dbis.docFilenameTermsDbi, m_txn); DocumentUrlDB docUrlDb(m_dbis.idTreeDbi, m_dbis.idFilenameDbi, m_txn); PostingDB postingDb(m_dbis.postingDbi, m_txn); - auto map = postingDb.toTestMap(); + const auto map = postingDb.toTestMap(); QSet allIds; - Q_FOREACH (const auto& list, map) { + for (const auto& list : map) { for (quint64 id : list) { allIds << id; } } QTextStream out(stdout); out << "Total Document IDs: " << allIds.size() << endl; int count = 0; for (quint64 id: qAsConst(allIds)) { QByteArray url = docUrlDb.get(id); if (url.isEmpty()) { auto terms = documentTermsDB.get(id); auto fileNameTerms = documentFileNameTermsDB.get(id); auto xAttrTerms = documentXattrTermsDB.get(id); // Lets reverse enginer the terms QList newTerms; QMapIterator it(map); while (it.hasNext()) { it.next(); if (it.value().contains(id)) { newTerms << it.key(); } } out << "Missing filePath for " << id << endl; out << "\tPostingDB Terms: "; for (const QByteArray& term : qAsConst(newTerms)) { out << term << " "; } out << endl; out << "\tDocumentTermsDB: "; for (const QByteArray& term : terms) { out << term << " "; } out << endl; out << "\tFileNameTermsDB: "; for (const QByteArray& term : fileNameTerms) { out << term << " "; } out << endl; out << "\tXAttrTermsDB: "; for (const QByteArray& term : xAttrTerms) { out << term << " "; } out << endl; count++; } else if (!QFileInfo::exists(QString::fromUtf8(url))) { out << "FilePath " << url << " for " << id << " does not exist"<< endl; count++; } } out << "Invalid Entries: " << count << " (" << count * 100.0 / allIds.size() << "%)" << endl; } void Transaction::checkTermsDbinPostingDb() { DocumentDB documentTermsDB(m_dbis.docTermsDbi, m_txn); DocumentDB documentXattrTermsDB(m_dbis.docXattrTermsDbi, m_txn); DocumentDB documentFileNameTermsDB(m_dbis.docFilenameTermsDbi, m_txn); PostingDB postingDb(m_dbis.postingDbi, m_txn); // Iterate over each document, and fetch all terms // check if each term maps to its own id in the posting db - auto map = postingDb.toTestMap(); + const auto map = postingDb.toTestMap(); QSet allIds; - Q_FOREACH (const auto& list, map) { + for (const auto& list : map) { for (quint64 id : list) { allIds << id; } } QTextStream out(stdout); out << "PostingDB check .." << endl; for (quint64 id : qAsConst(allIds)) { QVector terms = documentTermsDB.get(id); terms += documentXattrTermsDB.get(id); terms += documentFileNameTermsDB.get(id); for (const QByteArray& term : qAsConst(terms)) { PostingList plist = postingDb.get(term); if (!plist.contains(id)) { out << id << " is missing term " << term << endl; } } } } void Transaction::checkPostingDbinTermsDb() { DocumentDB documentTermsDB(m_dbis.docTermsDbi, m_txn); DocumentDB documentXattrTermsDB(m_dbis.docXattrTermsDbi, m_txn); DocumentDB documentFileNameTermsDB(m_dbis.docFilenameTermsDbi, m_txn); PostingDB postingDb(m_dbis.postingDbi, m_txn); QMap map = postingDb.toTestMap(); QMapIterator it(map); QTextStream out(stdout); out << "DocumentTermsDB check .." << endl; while (it.hasNext()) { it.next(); const QByteArray& term = it.key(); const PostingList& list = it.value(); for (quint64 id : list) { if (documentTermsDB.get(id).contains(term)) { continue; } if (documentFileNameTermsDB.get(id).contains(term)) { continue; } if (documentXattrTermsDB.get(id).contains(term)) { continue; } out << id << " is missing " << QString::fromUtf8(term) << " from document terms db" << endl; } } } diff --git a/src/file/extractor/app.cpp b/src/file/extractor/app.cpp index 85ec89cc..12919558 100644 --- a/src/file/extractor/app.cpp +++ b/src/file/extractor/app.cpp @@ -1,207 +1,207 @@ /* * This file is part of the KDE Baloo Project * Copyright (C) 2013-2015 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "app.h" #include "basicindexingjob.h" #include "result.h" #include "idutils.h" #include "transaction.h" #include "baloodebug.h" #include "global.h" #include #include #include #include #include #include #include #include //for STDIN_FILENO #include using namespace Baloo; App::App(QObject* parent) : QObject(parent) , m_notifyNewData(STDIN_FILENO, QSocketNotifier::Read) , m_input() , m_inputStream(&m_input) , m_outputStream(stdout) , m_tr(nullptr) { m_input.open(STDIN_FILENO, QIODevice::ReadOnly | QIODevice::Unbuffered ); m_inputStream.setByteOrder(QDataStream::BigEndian); connect(&m_notifyNewData, &QSocketNotifier::activated, this, &App::slotNewInput); } void App::slotNewInput() { m_inputStream.startTransaction(); m_inputStream >> m_ids; if (m_inputStream.status() != QDataStream::Ok) { QCoreApplication::quit(); return; } if (!m_inputStream.commitTransaction()) { m_ids.clear(); return; } Database *db = globalDatabaseInstance(); if (!db->open(Database::ReadWriteDatabase)) { qCritical() << "Failed to open the database"; exit(1); } Q_ASSERT(m_tr == nullptr); m_tr = new Transaction(db, Transaction::ReadWrite); // FIXME: The transaction is open for way too long. We should just open it for when we're // committing the data not during the extraction. QTimer::singleShot(0, this, &App::processNextFile); /** * A Single Batch seems to be triggering the SocketNotifier more than once * so we disable it till the batch is done. */ m_notifyNewData.setEnabled(false); } void App::processNextFile() { if (!m_ids.isEmpty()) { int delay = m_idleMonitor.isIdle() ? 0 : 10; quint64 id = m_ids.takeFirst(); QString url = QFile::decodeName(m_tr->documentUrl(id)); if (url.isEmpty() || !QFile::exists(url)) { m_tr->removeDocument(id); QTimer::singleShot(0, this, &App::processNextFile); return; } m_outputStream << "S " << url << endl; index(m_tr, url, id); m_outputStream << "F " << url << endl; m_updatedFiles << url; QTimer::singleShot(delay, this, &App::processNextFile); } else { m_tr->commit(); delete m_tr; m_tr = nullptr; /* * TODO we're already sending out each file as we start we can simply send out a done * signal isntead of sending out the list of files, that will need changes in whatever * uses this signal, Dolphin I think? */ QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/files"), QStringLiteral("org.kde"), QStringLiteral("changed")); QVariantList vl; vl.reserve(1); vl << QVariant(m_updatedFiles); m_updatedFiles.clear(); message.setArguments(vl); QDBusConnection::sessionBus().send(message); // Enable the SocketNotifier for the next batch m_notifyNewData.setEnabled(true); m_outputStream << "B" << endl; } } void App::index(Transaction* tr, const QString& url, quint64 id) { QString mimetype = m_mimeDb.mimeTypeForFile(url, QMimeDatabase::MatchContent).name(); qCDebug(BALOO) << "Indexing" << id << url << mimetype; if (!m_config.shouldBeIndexed(url)) { // This apparently happens when the config has changed after the document // was added to the content indexing db qCWarning(BALOO) << "Found" << url << "in the ContentIndexingDB, although it should be skipped"; tr->removeDocument(id); return; } // The initial BasicIndexingJob run has been supplied with the file extension // mimetype only, skip based on the "real" mimetype if (!m_config.shouldMimeTypeBeIndexed(mimetype)) { qCDebug(BALOO) << "Skipping" << url << "- mimetype:" << mimetype; tr->removePhaseOne(id); return; } // HACK: Also, we're ignoring ttext files which are greater tha 10 Mb as we // have trouble processing them // if (mimetype.startsWith(QStringLiteral("text/"))) { QFileInfo fileInfo(url); if (fileInfo.size() >= 10 * 1024 * 1024) { tr->removePhaseOne(id); return; } } // We always run the basic indexing again. This is mostly so that the proper // mimetype is set and we get proper type information. // The mimetype fetched in the BasicIndexingJob is fast but not accurate BasicIndexingJob basicIndexer(url, mimetype, BasicIndexingJob::NoLevel); basicIndexer.index(); Baloo::Document doc = basicIndexer.document(); Result result(url, mimetype, KFileMetaData::ExtractionResult::ExtractEverything); result.setDocument(doc); - QList exList = m_extractorCollection.fetchExtractors(mimetype); + const QList exList = m_extractorCollection.fetchExtractors(mimetype); - Q_FOREACH (KFileMetaData::Extractor* ex, exList) { + for (KFileMetaData::Extractor* ex : exList) { ex->extract(&result); } result.finish(); if (doc.id() != id) { qWarning() << url << "id seems to have changed. Perhaps baloo was not running, and this file was deleted + re-created"; tr->removeDocument(id); if (!tr->hasDocument(doc.id())) { tr->addDocument(result.document()); } else { tr->replaceDocument(result.document(), DocumentTerms | DocumentData); } } else { tr->replaceDocument(result.document(), DocumentTerms | DocumentData); } tr->removePhaseOne(doc.id()); } diff --git a/src/file/fileindexerconfig.cpp b/src/file/fileindexerconfig.cpp index 1a5e859c..cbf56802 100644 --- a/src/file/fileindexerconfig.cpp +++ b/src/file/fileindexerconfig.cpp @@ -1,399 +1,399 @@ /* This file is part of the KDE Project Copyright (c) 2008-2010 Sebastian Trueg Copyright (c) 2013-2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "fileindexerconfig.h" #include "fileexcludefilters.h" #include "storagedevices.h" #include #include #include #include namespace { /// recursively check if a folder is hidden bool isDirHidden(const QDir& dir) { #ifdef __unix__ return dir.absolutePath().contains(QLatin1String("/.")); #else QDir d = dir; do { if (QFileInfo(d.path()).isHidden()) return true; } while (d.cdUp()); return false; #endif } QString normalizeTrailingSlashes(QString&& path) { while (path.endsWith(QLatin1Char('/'))) { path.chop(1); } path += QLatin1Char('/'); return path; } } using namespace Baloo; FileIndexerConfig::FileIndexerConfig(QObject* parent) : QObject(parent) , m_config(QStringLiteral("baloofilerc")) , m_folderCacheDirty(true) , m_indexHidden(false) , m_devices(nullptr) , m_maxUncomittedFiles(40) { forceConfigUpdate(); } FileIndexerConfig::~FileIndexerConfig() { } QStringList FileIndexerConfig::includeFolders() const { const_cast(this)->buildFolderCache(); QStringList fl; for (int i = 0; i < m_folderCache.count(); ++i) { if (m_folderCache[i].second) fl << m_folderCache[i].first; } return fl; } QStringList FileIndexerConfig::excludeFolders() const { const_cast(this)->buildFolderCache(); QStringList fl; for (int i = 0; i < m_folderCache.count(); ++i) { if (!m_folderCache[i].second) fl << m_folderCache[i].first; } return fl; } QStringList FileIndexerConfig::excludeFilters() const { KConfigGroup cfg = m_config.group("General"); // read configured exclude filters QSet filters = cfg.readEntry("exclude filters", defaultExcludeFilterList()).toSet(); // make sure we always keep the latest default exclude filters // TODO: there is one problem here. What if the user removed some of the default filters? if (cfg.readEntry("exclude filters version", 0) < defaultExcludeFilterListVersion()) { filters += defaultExcludeFilterList().toSet(); // write the config directly since the KCM does not have support for the version yet // TODO: make this class public and use it in the KCM KConfig config(m_config.name()); KConfigGroup cfg = config.group("General"); cfg.writeEntry("exclude filters", QStringList::fromSet(filters)); cfg.writeEntry("exclude filters version", defaultExcludeFilterListVersion()); } // remove duplicates return QStringList::fromSet(filters); } QStringList FileIndexerConfig::excludeMimetypes() const { return QStringList::fromSet(m_excludeMimetypes); } bool FileIndexerConfig::indexHiddenFilesAndFolders() const { return m_indexHidden; } bool FileIndexerConfig::onlyBasicIndexing() const { return m_onlyBasicIndexing; } bool FileIndexerConfig::isInitialRun() const { return m_config.group("General").readEntry("first run", true); } bool FileIndexerConfig::canBeSearched(const QString& folder) const { QFileInfo fi(folder); QString path = fi.absolutePath(); if (!fi.isDir()) { return false; } else if (shouldFolderBeIndexed(path)) { return true; } const_cast(this)->buildFolderCache(); // Look for included descendants for (const QPair& fld: m_folderCache) { if (fld.second && fld.first.startsWith(path)) { return true; } } return false; } bool FileIndexerConfig::shouldBeIndexed(const QString& path) const { QFileInfo fi(path); if (fi.isDir()) { return shouldFolderBeIndexed(path); } else { return (shouldFolderBeIndexed(fi.absolutePath()) && (!fi.isHidden() || indexHiddenFilesAndFolders()) && shouldFileBeIndexed(fi.fileName())); } } bool FileIndexerConfig::shouldFolderBeIndexed(const QString& path) const { QString folder; if (folderInFolderList(path, folder)) { // we always index the folders in the list // ignoring the name filters if (folder == normalizeTrailingSlashes(QString(path))) return true; // check for hidden folders QDir dir(path); if (!indexHiddenFilesAndFolders() && isDirHidden(dir)) return false; // check the exclude filters for all components of the path // after folder const QStringList pathComponents = path.mid(folder.count()).split(QLatin1Char('/'), QString::SkipEmptyParts); - Q_FOREACH (const QString& c, pathComponents) { + for (const QString& c : pathComponents) { if (!shouldFileBeIndexed(c)) { return false; } } return true; } else { return false; } } bool FileIndexerConfig::shouldFileBeIndexed(const QString& fileName) const { if (!indexHiddenFilesAndFolders() && fileName.startsWith(QLatin1Char('.'))) { return false; } return !m_excludeFilterRegExpCache.exactMatch(fileName); } bool FileIndexerConfig::shouldMimeTypeBeIndexed(const QString& mimeType) const { return !m_excludeMimetypes.contains(mimeType); } bool FileIndexerConfig::folderInFolderList(const QString& path) { QString str; return folderInFolderList(path, str); } bool FileIndexerConfig::folderInFolderList(const QString& path, QString& folder) const { const_cast(this)->buildFolderCache(); const QString p = normalizeTrailingSlashes(QString(path)); // we traverse the list backwards to catch all exclude folders int i = m_folderCache.count(); while (--i >= 0) { const QString& f = m_folderCache[i].first; const bool include = m_folderCache[i].second; if (p.startsWith(f)) { folder = f; return include; } } // path is not in the list, thus it should not be included folder.clear(); return false; } namespace { /** * Returns true if the specified folder f would already be excluded using the list * folders. */ bool alreadyExcluded(const QList >& folders, const QString& f) { bool included = false; for (int i = 0; i < folders.count(); ++i) { QString path = folders[i].first; if (f != folders[i].first && f.startsWith(path)) { included = folders[i].second; } } return !included; } /** * Simple insertion sort */ void insertSortFolders(const QStringList& folders, bool include, QList >& result) { - Q_FOREACH (QString path, folders) { + for (QString path : folders) { int pos = 0; path = normalizeTrailingSlashes(std::move(path)); while (result.count() > pos && result[pos].first < path) ++pos; result.insert(pos, qMakePair(path, include)); } } /** * Remove useless exclude entries which would confuse the folderInFolderList algo. * This makes sure all top-level items are include folders. * This runs in O(n^2) and could be optimized but what for. */ void cleanupList(QList >& result) { int i = 0; while (i < result.count()) { if (result[i].first.isEmpty() || (!result[i].second && alreadyExcluded(result, result[i].first))) result.removeAt(i); else ++i; } } } void FileIndexerConfig::buildFolderCache() { if (!m_folderCacheDirty) { return; } if (!m_devices) { m_devices = new StorageDevices(this); } KConfigGroup group = m_config.group("General"); QStringList includeFoldersPlain = group.readPathEntry("folders", QStringList() << QDir::homePath()); QStringList excludeFoldersPlain = group.readPathEntry("exclude folders", QStringList()); // Add all removable media and network shares as ignored unless they have // been explicitly added in the include list const auto allMedia = m_devices->allMedia(); for (const auto& device: allMedia) { const QString mountPath = device.mountPath(); if (!device.isUsable() && !mountPath.isEmpty()) { if (!includeFoldersPlain.contains(mountPath)) { excludeFoldersPlain << mountPath; } } } m_folderCache.clear(); insertSortFolders(includeFoldersPlain, true, m_folderCache); insertSortFolders(excludeFoldersPlain, false, m_folderCache); cleanupList(m_folderCache); m_folderCacheDirty = false; } void FileIndexerConfig::buildExcludeFilterRegExpCache() { QStringList newFilters = excludeFilters(); m_excludeFilterRegExpCache.rebuildCacheFromFilterList(newFilters); } void FileIndexerConfig::buildMimeTypeCache() { m_excludeMimetypes = m_config.group("General").readPathEntry("exclude mimetypes", defaultExcludeMimetypes()).toSet(); } void FileIndexerConfig::forceConfigUpdate() { m_config.reparseConfiguration(); m_folderCacheDirty = true; buildExcludeFilterRegExpCache(); buildMimeTypeCache(); m_indexHidden = m_config.group("General").readEntry("index hidden folders", false); m_onlyBasicIndexing = m_config.group("General").readEntry("only basic indexing", false); } void FileIndexerConfig::setInitialRun(bool isInitialRun) { m_config.group("General").writeEntry("first run", isInitialRun); m_config.sync(); } bool FileIndexerConfig::initialUpdateDisabled() const { return m_config.group("General").readEntry("disable initial update", true); } int FileIndexerConfig::databaseVersion() const { return m_config.group("General").readEntry("dbVersion", 0); } void FileIndexerConfig::setDatabaseVersion(int version) { m_config.group("General").writeEntry("dbVersion", version); m_config.sync(); } bool FileIndexerConfig::indexingEnabled() const { return m_config.group("Basic Settings").readEntry("Indexing-Enabled", true); } uint FileIndexerConfig::maxUncomittedFiles() const { return m_maxUncomittedFiles; } diff --git a/src/file/filewatch.cpp b/src/file/filewatch.cpp index 2a936190..059eb211 100644 --- a/src/file/filewatch.cpp +++ b/src/file/filewatch.cpp @@ -1,216 +1,216 @@ /* This file is part of the KDE Project Copyright (c) 2007-2011 Sebastian Trueg Copyright (c) 2012-2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filewatch.h" #include "metadatamover.h" #include "fileindexerconfig.h" #include "pendingfilequeue.h" #include "regexpcache.h" #include "database.h" #include "pendingfile.h" #include "baloodebug.h" #include "kinotify.h" #include #include #include #include using namespace Baloo; FileWatch::FileWatch(Database* db, FileIndexerConfig* config, Baloo::MainHub *dbusInterface, QObject* parent) : QObject(parent) , m_db(db) , m_config(config) , m_dirWatch(nullptr) { Q_ASSERT(db); Q_ASSERT(config); m_metadataMover = new MetadataMover(m_db, this); connect(m_metadataMover, &MetadataMover::movedWithoutData, this, &FileWatch::indexNewFile); connect(m_metadataMover, &MetadataMover::fileRemoved, this, &FileWatch::fileRemoved); m_pendingFileQueue = new PendingFileQueue(this); connect(m_pendingFileQueue, &PendingFileQueue::indexNewFile, this, &FileWatch::indexNewFile); connect(m_pendingFileQueue, &PendingFileQueue::indexModifiedFile, this, &FileWatch::indexModifiedFile); connect(m_pendingFileQueue, &PendingFileQueue::indexXAttr, this, &FileWatch::indexXAttr); connect(m_pendingFileQueue, &PendingFileQueue::removeFileIndex, m_metadataMover, &MetadataMover::removeFileMetadata); // monitor the file system for changes (restricted by the inotify limit) m_dirWatch = new KInotify(m_config, this); connect(m_dirWatch, &KInotify::moved, this, &FileWatch::slotFileMoved); connect(m_dirWatch, &KInotify::deleted, this, &FileWatch::slotFileDeleted); connect(m_dirWatch, &KInotify::created, this, &FileWatch::slotFileCreated); connect(m_dirWatch, &KInotify::modified, this, &FileWatch::slotFileModified); connect(m_dirWatch, &KInotify::closedWrite, this, &FileWatch::slotFileClosedAfterWrite); connect(m_dirWatch, &KInotify::attributeChanged, this, &FileWatch::slotAttributeChanged); connect(m_dirWatch, &KInotify::watchUserLimitReached, this, &FileWatch::slotInotifyWatchUserLimitReached); connect(m_dirWatch, &KInotify::installedWatches, this, &FileWatch::installedWatches); } FileWatch::~FileWatch() { } void FileWatch::watchIndexedFolders() { // Watch all indexed folders - QStringList folders = m_config->includeFolders(); - Q_FOREACH (const QString& folder, folders) { + const QStringList folders = m_config->includeFolders(); + for (const QString& folder : folders) { watchFolder(folder); } } void FileWatch::registerBalooWatcher(const QString &service) { if (!m_metadataMover) { return; } m_metadataMover->registerBalooWatcher(service); } // FIXME: listen to Create for folders! void FileWatch::watchFolder(const QString& path) { qCDebug(BALOO) << path; if (m_dirWatch && !m_dirWatch->watchingPath(path)) { KInotify::WatchEvents flags(KInotify::EventMove | KInotify::EventDelete | KInotify::EventDeleteSelf | KInotify::EventCloseWrite | KInotify::EventCreate | KInotify::EventAttributeChange | KInotify::EventModify); m_dirWatch->addWatch(path, flags, KInotify::FlagOnlyDir); } } void FileWatch::slotFileMoved(const QString& urlFrom, const QString& urlTo) { if (m_config->shouldBeIndexed(urlTo)) { m_metadataMover->moveFileMetadata(urlFrom, urlTo); } else { QFileInfo src(urlFrom); QString url = urlFrom; if (src.isDir()) { Q_ASSERT(!url.endsWith('/')); url.append(QLatin1Char('/')); } PendingFile file(url); file.setDeleted(); m_pendingFileQueue->enqueue(file); } } void FileWatch::slotFileDeleted(const QString& urlString, bool isDir) { // Directories must always end with a trailing slash '/' QString url = urlString; if (isDir) { Q_ASSERT(!url.endsWith('/')); url.append(QLatin1Char('/')); } PendingFile file(url); file.setDeleted(); m_pendingFileQueue->enqueue(file); } void FileWatch::slotFileCreated(const QString& path, bool isDir) { Q_UNUSED(isDir); PendingFile file(path); file.setCreated(); m_pendingFileQueue->enqueue(file); } void FileWatch::slotFileModified(const QString& path) { PendingFile file(path); file.setModified(); //qCDebug(BALOO) << "MOD" << path; m_pendingFileQueue->enqueue(file); } void FileWatch::slotFileClosedAfterWrite(const QString& path) { QDateTime current = QDateTime::currentDateTime(); QDateTime fileModification = QFileInfo(path).lastModified(); QDateTime dirModification = QFileInfo(QFileInfo(path).absoluteDir().absolutePath()).lastModified(); // If we have received a FileClosedAfterWrite event, then the file must have been // closed within the last minute. // This is being done cause many applications open the file under write mode, do not // make any modifications and then close the file. This results in us getting // the FileClosedAfterWrite event if (fileModification.secsTo(current) <= 1000 * 60 || dirModification.secsTo(current) <= 1000 * 60) { PendingFile file(path); file.setClosedOnWrite(); //qCDebug(BALOO) << "CLOSE" << path; m_pendingFileQueue->enqueue(file); } } void FileWatch::slotAttributeChanged(const QString& path) { PendingFile file(path); file.setAttributeChanged(); m_pendingFileQueue->enqueue(file); } // This slot is connected to a signal emitted in KInotify when // inotify_add_watch fails with ENOSPC. void FileWatch::slotInotifyWatchUserLimitReached(const QString&) { //If we got here, we hit the limit and couldn't authenticate to raise it, // so put something in the syslog so someone notices. syslog(LOG_USER | LOG_WARNING, "KDE Baloo File Indexer has reached the inotify folder watch limit. File changes will be ignored."); // we do it the brutal way for now hoping with new kernels and defaults this will never happen // Delete the KInotify // FIXME: Maybe we should be aborting? if (m_dirWatch) { m_dirWatch->deleteLater(); m_dirWatch = nullptr; } Q_EMIT installedWatches(); } void FileWatch::updateIndexedFoldersWatches() { if (m_dirWatch) { - QStringList folders = m_config->includeFolders(); - Q_FOREACH (const QString& folder, folders) { + const QStringList folders = m_config->includeFolders(); + for (const QString& folder : folders) { m_dirWatch->removeWatch(folder); watchFolder(folder); } } } diff --git a/src/file/metadatamover.cpp b/src/file/metadatamover.cpp index 663daff9..09668b35 100644 --- a/src/file/metadatamover.cpp +++ b/src/file/metadatamover.cpp @@ -1,198 +1,198 @@ /* This file is part of the KDE Project Copyright (c) 2009-2011 Sebastian Trueg Copyright (c) 2013-2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "metadatamover.h" #include "database.h" #include "transaction.h" #include "basicindexingjob.h" #include "idutils.h" #include "mainhub.h" #include "baloodebug.h" #include "baloowatcherapplication_interface.h" #include #include #include using namespace Baloo; MetadataMover::MetadataMover(Database* db, QObject* parent) : QObject(parent) , m_db(db) { m_serviceWatcher.setConnection(QDBusConnection::sessionBus()); m_serviceWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration); connect(&m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &MetadataMover::watcherServiceUnregistered); } MetadataMover::~MetadataMover() { } bool MetadataMover::hasWatcher() const { return !m_watcherApplications.isEmpty(); } static void buildRecursiveList(quint64 parentId, QList &fileList, Transaction &tr) { fileList.push_back(QFile::decodeName(tr.documentUrl(parentId))); const auto childrenIds = tr.childrenDocumentId(parentId); for (const auto oneChildren : childrenIds) { buildRecursiveList(oneChildren, fileList, tr); } } void MetadataMover::moveFileMetadata(const QString& from, const QString& to) { // qCDebug(BALOO) << from << to; Q_ASSERT(!from.isEmpty() && from != QLatin1String("/")); Q_ASSERT(!to.isEmpty() && to != QLatin1String("/")); Transaction tr(m_db, Transaction::ReadWrite); quint64 id = tr.documentId(QFile::encodeName(from)); QList filesList; qCDebug(BALOO) << "MetadataMover::moveFileMetadata" << (hasWatcher() ? "has watcher" : "has no watcher"); qCDebug(BALOO) << "MetadataMover::moveFileMetadata" << "id" << id; if (id && hasWatcher()) { buildRecursiveList(id, filesList, tr); } // We do NOT get deleted messages for overwritten files! Thus, we // have to remove all metadata for overwritten files first. removeMetadata(&tr, to); // and finally update the old statements updateMetadata(&tr, from, to); tr.commit(); if (hasWatcher()) { qCDebug(BALOO) << "MetadataMover::moveFileMetadata" << "notifyWatchers" << filesList; notifyWatchers(from, to, filesList); } } void MetadataMover::removeFileMetadata(const QString& file) { Q_ASSERT(!file.isEmpty() && file != QLatin1String("/")); Transaction tr(m_db, Transaction::ReadWrite); removeMetadata(&tr, file); tr.commit(); } void MetadataMover::registerBalooWatcher(const QString &service) { int firstSlash = service.indexOf('/'); if (firstSlash == -1) { return; } QString dbusServiceName = service.left(firstSlash); QString dbusPath = service.mid(firstSlash); m_serviceWatcher.addWatchedService(dbusServiceName); m_watcherApplications.insert(dbusServiceName, new org::kde::BalooWatcherApplication(dbusServiceName, dbusPath, QDBusConnection::sessionBus(), this)); qCDebug(BALOO) << "MetadataMover::registerBalooWatcher" << service << dbusServiceName << dbusPath; } void MetadataMover::removeMetadata(Transaction* tr, const QString& url) { Q_ASSERT(!url.isEmpty()); quint64 id = tr->documentId(QFile::encodeName(url)); if (!id) { Q_EMIT fileRemoved(url); return; } bool isDir = url.endsWith('/'); if (!isDir) { tr->removeDocument(id); } else { tr->removeRecursively(id); } Q_EMIT fileRemoved(url); } void MetadataMover::updateMetadata(Transaction* tr, const QString& from, const QString& to) { qCDebug(BALOO) << from << "->" << to; Q_ASSERT(!from.isEmpty() && !to.isEmpty()); Q_ASSERT(from[from.size()-1] != QLatin1Char('/')); Q_ASSERT(to[to.size()-1] != QLatin1Char('/')); QByteArray toPath = QFile::encodeName(to); quint64 id = filePathToId(toPath); if (!id) { qWarning() << "File moved to path which now no longer exists -" << to; return; } if (!tr->hasDocument(id)) { // // If we have no metadata yet we need to tell the file indexer so it can // create the metadata in case the target folder is configured to be indexed. // qCDebug(BALOO) << "Moved without data"; Q_EMIT movedWithoutData(to); return; } BasicIndexingJob job(toPath, QString(), BasicIndexingJob::NoLevel); job.index(); tr->replaceDocument(job.document(), DocumentUrl | FileNameTerms); // Possible scenarios // 1. file moves to the same device - id is preserved // 2. file moves to a different device - id is not preserved } void MetadataMover::notifyWatchers(const QString &from, const QString &to, const QList &filesList) { - Q_FOREACH(org::kde::BalooWatcherApplication *watcherApplication, m_watcherApplications) { + for (org::kde::BalooWatcherApplication *watcherApplication : qAsConst(m_watcherApplications)) { qCDebug(BALOO) << "MetadataMover::notifyWatchers" << watcherApplication->service() << watcherApplication->objectName() << watcherApplication->path(); watcherApplication->renamedFiles(from, to, filesList); } } void MetadataMover::watcherServiceUnregistered(const QString &serviceName) { auto itService = m_watcherApplications.find(serviceName); if (itService == m_watcherApplications.end()) { return; } qCDebug(BALOO) << "MetadataMover::watcherServiceUnregistered" << itService.key(); delete itService.value(); m_watcherApplications.erase(itService); } diff --git a/src/file/optimizedbytearray.h b/src/file/optimizedbytearray.h index da060a44..fda39024 100644 --- a/src/file/optimizedbytearray.h +++ b/src/file/optimizedbytearray.h @@ -1,98 +1,98 @@ /* * This file is part of the KDE Nepomuk Project * Copyright (C) 2012 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef OPTIMIZED_BYTE_ARRAY_H #define OPTIMIZED_BYTE_ARRAY_H #include #include #include #include /** * This class holds a QByteArray which corresponds to a file url in a * more memory efficient manner. * * It does so by spliting the url along the directory separator '/', and * saving each path separately in a QVector. This is more memory efficient * than storing it together cause it uses a QSet cache to lookup * each part of the vector. This way, multiple OptimizedByteArrays share common * parts of the url. This happens because Qt's containers are reference counted. * * \author Vishesh Handa * */ class OptimizedByteArray { public: OptimizedByteArray() {} OptimizedByteArray(const QByteArray& array, QSet& cache) { - QList list = array.split('/'); + const QList list = array.split('/'); QVector vec; vec.reserve(list.size()); - Q_FOREACH (const QByteArray& ba, list) { + for (const QByteArray& ba : list) { if (!ba.isEmpty()) vec << ba; } m_data.reserve(vec.size()); - Q_FOREACH (const QByteArray& arr, vec) { + for (const QByteArray& arr : qAsConst(vec)) { QSet< QByteArray >::iterator it = cache.find(arr); if (it != cache.end()) m_data.append(*it); else m_data.append(*cache.insert(arr)); } } QByteArray toByteArray() const { int size = 0; - Q_FOREACH (const QByteArray& arr, m_data) + for (const QByteArray& arr : qAsConst(m_data)) size += arr.size() + 1; QByteArray array; array.reserve(size); - Q_FOREACH (const QByteArray& arr, m_data) { + for (const QByteArray& arr : qAsConst(m_data)) { array.append('/'); array.append(arr); } return array; } bool operator==(const OptimizedByteArray& other) const { return m_data == other.m_data; } private: QVector m_data; }; uint qHash(const OptimizedByteArray& arr) { return qHash(arr.toByteArray()); } Q_DECLARE_METATYPE(OptimizedByteArray) Q_DECLARE_TYPEINFO(OptimizedByteArray, Q_MOVABLE_TYPE); #endif // OPTIMIZED_BYTE_ARRAY_H diff --git a/src/file/regexpcache.cpp b/src/file/regexpcache.cpp index 61f07d61..7e2a3a2b 100644 --- a/src/file/regexpcache.cpp +++ b/src/file/regexpcache.cpp @@ -1,59 +1,59 @@ /* This file is part of the KDE Baloo project. Copyright (C) 2010 Sebastian Trueg Copyright (C) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "regexpcache.h" #include RegExpCache::RegExpCache() { } RegExpCache::~RegExpCache() { } bool RegExpCache::exactMatch(const QString& s) const { - Q_FOREACH(const QRegularExpression& filter, m_regexpCache) { + for (const QRegularExpression& filter : qAsConst(m_regexpCache)) { if (filter.match(s).hasMatch()) { return true; } } return false; } void RegExpCache::rebuildCacheFromFilterList(const QStringList& filters) { m_regexpCache.clear(); - Q_FOREACH (const QString& filter, filters) { + for (const QString& filter : filters) { QString f = filter; f.replace(QLatin1Char('.'), QStringLiteral("\\.")); f.replace(QLatin1Char('?'), QLatin1Char('.')); f.replace(QStringLiteral("*"), QStringLiteral(".*")); f = QLatin1String("^") + f + QLatin1String("$"); m_regexpCache.append(QRegularExpression(f)); } } diff --git a/src/file/storagedevices.cpp b/src/file/storagedevices.cpp index 307b6e03..6fccf45e 100644 --- a/src/file/storagedevices.cpp +++ b/src/file/storagedevices.cpp @@ -1,193 +1,193 @@ /* This file is part of the KDE Baloo project. Copyright (C) 2011 Sebastian Trueg Copyright (C) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "storagedevices.h" #include "baloodebug.h" #include #include #include #include #include #include #include #include #include #include using namespace Baloo; StorageDevices::StorageDevices(QObject* parent) : QObject(parent) { initCacheEntries(); connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceAdded, this, &StorageDevices::slotSolidDeviceAdded); connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceRemoved, this, &StorageDevices::slotSolidDeviceRemoved); } StorageDevices::~StorageDevices() { } void StorageDevices::initCacheEntries() { - QList devices + const QList devices = Solid::Device::listFromQuery(QStringLiteral("StorageVolume.usage=='FileSystem'")) + Solid::Device::listFromType(Solid::DeviceInterface::NetworkShare); - Q_FOREACH (const Solid::Device& dev, devices) { + for (const Solid::Device& dev : devices) { createCacheEntry(dev); } } QList StorageDevices::allMedia() const { return m_metadataCache.values(); } StorageDevices::Entry* StorageDevices::createCacheEntry(const Solid::Device& dev) { if (dev.udi().isEmpty()) return nullptr; const Solid::StorageAccess* storage = dev.as(); if (!storage) { return nullptr; } Entry entry(dev); auto it = m_metadataCache.insert(dev.udi(), entry); connect(storage, &Solid::StorageAccess::accessibilityChanged, this, &StorageDevices::slotAccessibilityChanged); //connect(storage, SIGNAL(teardownRequested(QString)), // this, SLOT(slotTeardownRequested(QString))); return &it.value(); } bool StorageDevices::isEmpty() const { return m_metadataCache.isEmpty(); } void StorageDevices::slotSolidDeviceAdded(const QString& udi) { qCDebug(BALOO) << udi; Entry* e = createCacheEntry(Solid::Device(udi)); if (e) { Q_EMIT deviceAdded(e); } } void StorageDevices::slotSolidDeviceRemoved(const QString& udi) { QHash< QString, Entry >::iterator it = m_metadataCache.find(udi); if (it != m_metadataCache.end()) { qCDebug(BALOO) << "Found removable storage volume for Baloo undocking:" << udi; Q_EMIT deviceRemoved(&it.value()); m_metadataCache.erase(it); } } void StorageDevices::slotAccessibilityChanged(bool accessible, const QString& udi) { qCDebug(BALOO) << accessible << udi; Q_UNUSED(accessible); // // cache new mount path // Entry* entry = &m_metadataCache[udi]; Q_ASSERT(entry != nullptr); Q_EMIT deviceAccessibilityChanged(entry); } StorageDevices::Entry::Entry() { } StorageDevices::Entry::Entry(const Solid::Device& device) : m_device(device) { } QString StorageDevices::Entry::mountPath() const { if (const Solid::StorageAccess* sa = m_device.as()) { return sa->filePath(); } else { return QString(); } } bool StorageDevices::Entry::isMounted() const { if (const Solid::StorageAccess* sa = m_device.as()) { return sa->isAccessible(); } else { return false; } } bool StorageDevices::Entry::isUsable() const { if (mountPath().isEmpty()) { return false; } bool usable = true; const Solid::Device& dev = m_device; if (dev.is() && dev.parent().is()) { auto parent = dev.parent().as(); if (parent->isRemovable() || parent->isHotpluggable()) { usable = false; } const Solid::StorageVolume* volume = dev.as(); if (volume->isIgnored() || volume->usage() != Solid::StorageVolume::FileSystem) { usable = false; } } if (dev.is()) { usable = false; } else if (dev.is()) { usable = false; } if (usable) { if (const Solid::StorageAccess* sa = dev.as()) { usable = sa->isAccessible(); } } return usable; } diff --git a/src/kioslaves/timeline/timelinetools.cpp b/src/kioslaves/timeline/timelinetools.cpp index 82de2b15..5a1aecf2 100644 --- a/src/kioslaves/timeline/timelinetools.cpp +++ b/src/kioslaves/timeline/timelinetools.cpp @@ -1,138 +1,138 @@ /* This file is part of the Nepomuk KDE project. Copyright (C) 2010 Sebastian Trueg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "timelinetools.h" #include "kio_timeline_debug.h" #include #include #include #include #include #include namespace { QDate applyRelativeDateModificators(const QDate& date, const QMap& modificators) { QDate newDate(date); const QString dayKey = QStringLiteral("relDays"); const QString weekKey = QStringLiteral("relWeeks"); const QString monthKey = QStringLiteral("relMonths"); const QString yearKey = QStringLiteral("relYears"); bool ok = false; if (modificators.contains(yearKey)) { int relYears = modificators[yearKey].toInt(&ok); if (ok) { newDate = newDate.addYears(relYears); } } if (modificators.contains(monthKey)) { int relMonths = modificators[monthKey].toInt(&ok); if (ok) { newDate = newDate.addMonths(relMonths); } } if (modificators.contains(weekKey)) { int relWeeks = modificators[weekKey].toInt(&ok); if (ok) { newDate = newDate.addDays(relWeeks * 7); // we assume weeks have 7 days everywhere. QDate seems to make that assumption too, should be OK. } } if (modificators.contains(dayKey)) { int relDays = modificators[dayKey].toInt(&ok); if (ok) { newDate = newDate.addDays(relDays); } } return newDate; } } Baloo::TimelineFolderType Baloo::parseTimelineUrl(const QUrl& url, QDate* date, QString* filename) { qCDebug(KIO_TIMELINE) << url; static QRegExp s_dateRegexp(QStringLiteral("\\d{4}-\\d{2}(?:-(\\d{2}))?")); // reset *date = QDate(); QString path = url.path(); if (path.endsWith(QLatin1Char('/'))) path = path.mid(0, path.length()-1); if (path.isEmpty() || path == QLatin1String("/")) { qCDebug(KIO_TIMELINE) << url << "is root folder"; return RootFolder; } else if (path.startsWith(QLatin1String("/today"))) { *date = QDate::currentDate(); if (filename) *filename = path.mid(7); qCDebug(KIO_TIMELINE) << url << "is today folder:" << *date; return DayFolder; } else if (path == QLatin1String("/calendar")) { qCDebug(KIO_TIMELINE) << url << "is calendar folder"; return CalendarFolder; } else { QStringList sections = path.split(QStringLiteral("/"), QString::SkipEmptyParts); QString dateString; if (s_dateRegexp.exactMatch(sections.last())) { dateString = sections.last(); } else if (sections.count() > 1 && s_dateRegexp.exactMatch(sections[sections.count() - 2])) { dateString = sections[sections.count() - 2]; if (filename) *filename = sections.last(); } else { qCWarning(KIO_TIMELINE) << url << "COULD NOT PARSE"; return NoFolder; } if (s_dateRegexp.cap(1).isEmpty()) { // no day -> month listing qCDebug(KIO_TIMELINE) << "parsing " << dateString; *date = QDate::fromString(dateString, QStringLiteral("yyyy-MM")); qCDebug(KIO_TIMELINE) << url << "is month folder:" << date->month() << date->year(); if (date->month() > 0 && date->year() > 0) return MonthFolder; } else { qCDebug(KIO_TIMELINE) << "parsing " << dateString; typedef QPair StringPair; QUrlQuery query(url); - QList queryItems = query.queryItems(); + const QList queryItems = query.queryItems(); QMap map; - Q_FOREACH (const StringPair& pair, queryItems) { + for (const StringPair& pair : queryItems) { map.insert(pair.first, pair.second); } *date = applyRelativeDateModificators(QDate::fromString(dateString, QStringLiteral("yyyy-MM-dd")), map); // only in day folders we can have filenames qCDebug(KIO_TIMELINE) << url << "is day folder:" << *date; if (date->isValid()) return DayFolder; } } return NoFolder; } diff --git a/src/lib/filemonitor.cpp b/src/lib/filemonitor.cpp index f1fc9151..ed48bf1c 100644 --- a/src/lib/filemonitor.cpp +++ b/src/lib/filemonitor.cpp @@ -1,87 +1,87 @@ /* * * Copyright (C) 2013 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "filemonitor.h" #include #include #include #include using namespace Baloo; class FileMonitor::Private { public: QSet m_files; }; FileMonitor::FileMonitor(QObject* parent) : QObject(parent) , d(new Private) { QDBusConnection con = QDBusConnection::sessionBus(); con.connect(QString(), QStringLiteral("/files"), QStringLiteral("org.kde"), QStringLiteral("changed"), this, SLOT(slotFileMetaDataChanged(QStringList))); } FileMonitor::~FileMonitor() { delete d; } void FileMonitor::addFile(const QString& fileUrl) { QString f = fileUrl; if (f.endsWith(QLatin1Char('/'))) f = f.mid(0, f.length()-1); d->m_files.insert(f); } void FileMonitor::addFile(const QUrl& url) { const QString localFile = url.toLocalFile(); if (!localFile.isEmpty()) addFile(localFile); } void FileMonitor::setFiles(const QStringList& fileList) { d->m_files = fileList.toSet(); } QStringList FileMonitor::files() const { return QStringList(d->m_files.toList()); } void FileMonitor::clear() { d->m_files.clear(); } void FileMonitor::slotFileMetaDataChanged(const QStringList& fileUrls) { - Q_FOREACH (const QString& url, fileUrls) { + for (const QString& url : fileUrls) { if (d->m_files.contains(url)) { Q_EMIT fileMetaDataChanged(url); } } } diff --git a/src/lib/query.cpp b/src/lib/query.cpp index 981e8782..7637f649 100644 --- a/src/lib/query.cpp +++ b/src/lib/query.cpp @@ -1,352 +1,352 @@ /* * This file is part of the KDE Baloo Project * Copyright (C) 2013 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "query.h" #include "term.h" #include "advancedqueryparser.h" #include "searchstore.h" #include #include #include #include #include #include #include using namespace Baloo; const int defaultLimit = -1; class Baloo::Query::Private { public: Private() { m_limit = defaultLimit; m_offset = 0; m_yearFilter = 0; m_monthFilter = 0; m_dayFilter = 0; m_sortingOption = SortAuto; } Term m_term; QStringList m_types; QString m_searchString; int m_limit; uint m_offset; int m_yearFilter; int m_monthFilter; int m_dayFilter; SortingOption m_sortingOption; QString m_includeFolder; }; Query::Query() : d(new Private) { } Query::Query(const Query& rhs) : d(new Private(*rhs.d)) { } Query::~Query() { delete d; } void Query::addType(const QString& type) { d->m_types << type.split(QLatin1Char('/'), QString::SkipEmptyParts); } void Query::addTypes(const QStringList& typeList) { - Q_FOREACH (const QString& type, typeList) { + for (const QString& type : typeList) { addType(type); } } void Query::setType(const QString& type) { d->m_types.clear(); addType(type); } void Query::setTypes(const QStringList& types) { d->m_types = types; } QStringList Query::types() const { return d->m_types; } QString Query::searchString() const { return d->m_searchString; } void Query::setSearchString(const QString& str) { d->m_searchString = str; AdvancedQueryParser parser; d->m_term = parser.parse(str); } uint Query::limit() const { return d->m_limit; } void Query::setLimit(uint limit) { d->m_limit = limit; } uint Query::offset() const { return d->m_offset; } void Query::setOffset(uint offset) { d->m_offset = offset; } void Query::setDateFilter(int year, int month, int day) { d->m_yearFilter = year; d->m_monthFilter = month; d->m_dayFilter = day; } int Query::yearFilter() const { return d->m_yearFilter; } int Query::monthFilter() const { return d->m_monthFilter; } int Query::dayFilter() const { return d->m_dayFilter; } void Query::setSortingOption(Query::SortingOption option) { d->m_sortingOption = option; } Query::SortingOption Query::sortingOption() const { return d->m_sortingOption; } QString Query::includeFolder() const { return d->m_includeFolder; } void Query::setIncludeFolder(const QString& folder) { d->m_includeFolder = folder; } ResultIterator Query::exec() { Term term(d->m_term); if (!d->m_types.isEmpty()) { for (const QString& type : qAsConst(d->m_types)) { term = term && Term(QStringLiteral("type"), type); } } if (!d->m_includeFolder.isEmpty()) { term = term && Term(QStringLiteral("includefolder"), d->m_includeFolder); } if (d->m_yearFilter || d->m_monthFilter || d->m_dayFilter) { QByteArray ba = QByteArray::number(d->m_yearFilter); if (d->m_monthFilter < 10) ba += '0'; ba += QByteArray::number(d->m_monthFilter); if (d->m_dayFilter < 10) ba += '0'; ba += QByteArray::number(d->m_dayFilter); term = term && Term(QStringLiteral("modified"), ba, Term::Equal); } SearchStore searchStore; QStringList result = searchStore.exec(term, d->m_offset, d->m_limit, d->m_sortingOption == SortAuto); return ResultIterator(result); } QByteArray Query::toJSON() { QVariantMap map; if (!d->m_types.isEmpty()) map[QStringLiteral("type")] = d->m_types; if (d->m_limit != defaultLimit) map[QStringLiteral("limit")] = d->m_limit; if (d->m_offset) map[QStringLiteral("offset")] = d->m_offset; if (!d->m_searchString.isEmpty()) map[QStringLiteral("searchString")] = d->m_searchString; if (d->m_term.isValid()) map[QStringLiteral("term")] = QVariant(d->m_term.toVariantMap()); if (d->m_yearFilter >= 0) map[QStringLiteral("yearFilter")] = d->m_yearFilter; if (d->m_monthFilter >= 0) map[QStringLiteral("monthFilter")] = d->m_monthFilter; if (d->m_dayFilter >= 0) map[QStringLiteral("dayFilter")] = d->m_dayFilter; if (d->m_sortingOption != SortAuto) map[QStringLiteral("sortingOption")] = static_cast(d->m_sortingOption); if (!d->m_includeFolder.isEmpty()) map[QStringLiteral("includeFolder")] = d->m_includeFolder; QJsonObject jo = QJsonObject::fromVariantMap(map); QJsonDocument jdoc; jdoc.setObject(jo); return jdoc.toJson(); } // static Query Query::fromJSON(const QByteArray& arr) { QJsonDocument jdoc = QJsonDocument::fromJson(arr); const QVariantMap map = jdoc.object().toVariantMap(); Query query; query.d->m_types = map[QStringLiteral("type")].toStringList(); if (map.contains(QStringLiteral("limit"))) query.d->m_limit = map[QStringLiteral("limit")].toUInt(); else query.d->m_limit = defaultLimit; query.d->m_offset = map[QStringLiteral("offset")].toUInt(); query.d->m_searchString = map[QStringLiteral("searchString")].toString(); query.d->m_term = Term::fromVariantMap(map[QStringLiteral("term")].toMap()); if (map.contains(QStringLiteral("yearFilter"))) query.d->m_yearFilter = map[QStringLiteral("yearFilter")].toInt(); if (map.contains(QStringLiteral("monthFilter"))) query.d->m_monthFilter = map[QStringLiteral("monthFilter")].toInt(); if (map.contains(QStringLiteral("dayFilter"))) query.d->m_dayFilter = map[QStringLiteral("dayFilter")].toInt(); if (map.contains(QStringLiteral("sortingOption"))) { int option = map.value(QStringLiteral("sortingOption")).toInt(); query.d->m_sortingOption = static_cast(option); } if (map.contains(QStringLiteral("includeFolder"))) { query.d->m_includeFolder = map.value(QStringLiteral("includeFolder")).toString(); } return query; } QUrl Query::toSearchUrl(const QString& title) { QUrl url; url.setScheme(QStringLiteral("baloosearch")); QUrlQuery urlQuery; urlQuery.addQueryItem(QStringLiteral("json"), QString::fromUtf8(toJSON())); if (!title.isEmpty()) urlQuery.addQueryItem(QStringLiteral("title"), title); url.setQuery(urlQuery); return url; } Query Query::fromSearchUrl(const QUrl& url) { if (url.scheme() != QLatin1String("baloosearch")) return Query(); QUrlQuery urlQuery(url); QString jsonString = urlQuery.queryItemValue(QStringLiteral("json"), QUrl::FullyDecoded); return Query::fromJSON(jsonString.toUtf8()); } QString Query::titleFromQueryUrl(const QUrl& url) { QUrlQuery urlQuery(url); return urlQuery.queryItemValue(QStringLiteral("title"), QUrl::FullyDecoded); } bool Query::operator==(const Query& rhs) const { if (rhs.d->m_limit != d->m_limit || rhs.d->m_offset != d->m_offset || rhs.d->m_dayFilter != d->m_dayFilter || rhs.d->m_monthFilter != d->m_monthFilter || rhs.d->m_yearFilter != d->m_yearFilter || rhs.d->m_includeFolder != d->m_includeFolder || rhs.d->m_searchString != d->m_searchString || rhs.d->m_sortingOption != d->m_sortingOption) { return false; } if (rhs.d->m_types.size() != d->m_types.size()) return false; - Q_FOREACH (const QString& type, rhs.d->m_types) { + for (const QString& type : qAsConst(rhs.d->m_types)) { if (!d->m_types.contains(type)) return false; } return d->m_term == rhs.d->m_term; } bool Query::operator!=(const Query& rhs) const { return !(*this == rhs); } Query& Query::operator=(const Query& rhs) { *d = *rhs.d; return *this; } diff --git a/src/lib/term.cpp b/src/lib/term.cpp index e4a8f629..3d4a83b9 100644 --- a/src/lib/term.cpp +++ b/src/lib/term.cpp @@ -1,470 +1,470 @@ /* * This file is part of the KDE Baloo Project * Copyright (C) 2013 Vishesh Handa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "term.h" #include #include using namespace Baloo; class Baloo::Term::Private { public: Operation m_op; Comparator m_comp; QString m_property; QVariant m_value; bool m_isNegated; QList m_subTerms; QVariantHash m_userData; Private() { m_op = None; m_comp = Auto; m_isNegated = false; } }; Term::Term() : d(new Private) { } Term::Term(const Term& t) : d(new Private(*t.d)) { } Term::Term(const QString& property) : d(new Private) { d->m_property = property; } Term::Term(const QString& property, const QVariant& value, Term::Comparator c) : d(new Private) { d->m_property = property; d->m_value = value; if (c == Auto) { if (value.type() == QVariant::String) d->m_comp = Contains; else if (value.type() == QVariant::DateTime) d->m_comp = Contains; else d->m_comp = Equal; } else { d->m_comp = c; } } /* Term::Term(const QString& property, const QVariant& start, const QVariant& end) : d(new Private) { d->m_property = property; d->m_op = Range; // FIXME: How to save range queries? } */ Term::Term(Term::Operation op) : d(new Private) { d->m_op = op; } Term::Term(Term::Operation op, const Term& t) : d(new Private) { d->m_op = op; d->m_subTerms << t; } Term::Term(Term::Operation op, const QList& t) : d(new Private) { d->m_op = op; d->m_subTerms = t; } Term::Term(const Term& lhs, Term::Operation op, const Term& rhs) : d(new Private) { d->m_op = op; if (lhs.operation() == op) { d->m_subTerms << lhs.subTerms(); } else { d->m_subTerms << lhs; } if (rhs.operation() == op) { d->m_subTerms << rhs.subTerms(); } else { d->m_subTerms << rhs; } } Term::~Term() { delete d; } bool Term::isValid() const { // Terms with an operator but no subterms are still valid if (d->m_op != Term::None) { return true; } if (d->m_comp == Term::Auto) { return false; } return true; } void Term::setNegation(bool isNegated) { d->m_isNegated = isNegated; } bool Term::isNegated() const { return d->m_isNegated; } bool Term::negated() const { return d->m_isNegated; } void Term::addSubTerm(const Term& term) { d->m_subTerms << term; } void Term::setSubTerms(const QList& terms) { d->m_subTerms = terms; } Term Term::subTerm() const { if (!d->m_subTerms.isEmpty()) return d->m_subTerms.first(); return Term(); } QList Term::subTerms() const { return d->m_subTerms; } void Term::setOperation(Term::Operation op) { d->m_op = op; } Term::Operation Term::operation() const { return d->m_op; } bool Term::empty() const { return isEmpty(); } bool Term::isEmpty() const { return d->m_property.isEmpty() && d->m_value.isNull() && d->m_subTerms.isEmpty(); } QString Term::property() const { return d->m_property; } void Term::setProperty(const QString& property) { d->m_property = property; } void Term::setValue(const QVariant& value) { d->m_value = value; } QVariant Term::value() const { return d->m_value; } Term::Comparator Term::comparator() const { return d->m_comp; } void Term::setComparator(Term::Comparator c) { d->m_comp = c; } void Term::setUserData(const QString& name, const QVariant& value) { d->m_userData.insert(name, value); } QVariant Term::userData(const QString& name) const { return d->m_userData.value(name); } QVariantMap Term::toVariantMap() const { QVariantMap map; if (d->m_op != None) { QVariantList variantList; - Q_FOREACH (const Term& term, d->m_subTerms) { + for (const Term& term : qAsConst(d->m_subTerms)) { variantList << QVariant(term.toVariantMap()); } if (d->m_op == And) map[QStringLiteral("$and")] = variantList; else map[QStringLiteral("$or")] = variantList; return map; } QString op; switch (d->m_comp) { case Equal: map[d->m_property] = d->m_value; return map; case Contains: op = QStringLiteral("$ct"); break; case Greater: op = QStringLiteral("$gt"); break; case GreaterEqual: op = QStringLiteral("$gte"); break; case Less: op = QStringLiteral("$lt"); break; case LessEqual: op = QStringLiteral("$lte"); break; case Auto: Q_ASSERT(0); } QVariantMap m; m[op] = d->m_value; map[d->m_property] = QVariant(m); return map; } namespace { // QJson does not recognize QDate/QDateTime parameters. We try to guess // and see if they can be converted into date/datetime. QVariant tryConvert(const QVariant& var) { if (var.canConvert(QVariant::DateTime)) { QDateTime dt = var.toDateTime(); if (!dt.isValid()) return var; if (!var.toString().contains(QLatin1String("T"))) { return QVariant(var.toDate()); } return dt; } return var; } } Term Term::fromVariantMap(const QVariantMap& map) { if (map.size() != 1) return Term(); Term term; QString andOrString; if (map.contains(QStringLiteral("$and"))) { andOrString = QStringLiteral("$and"); term.setOperation(And); } else if (map.contains(QStringLiteral("$or"))) { andOrString = QStringLiteral("$or"); term.setOperation(Or); } if (!andOrString.isEmpty()) { QList subTerms; - QVariantList list = map[andOrString].toList(); - Q_FOREACH (const QVariant& var, list) + const QVariantList list = map[andOrString].toList(); + for (const QVariant& var : list) subTerms << Term::fromVariantMap(var.toMap()); term.setSubTerms(subTerms); return term; } QString prop = map.cbegin().key(); term.setProperty(prop); QVariant value = map.value(prop); if (value.type() == QVariant::Map) { QVariantMap mapVal = value.toMap(); if (mapVal.size() != 1) return term; QString op = mapVal.cbegin().key(); Term::Comparator com; if (op == QLatin1String("$ct")) com = Contains; else if (op == QLatin1String("$gt")) com = Greater; else if (op == QLatin1String("$gte")) com = GreaterEqual; else if (op == QLatin1String("$lt")) com = Less; else if (op == QLatin1String("$lte")) com = LessEqual; else return term; term.setComparator(com); term.setValue(tryConvert(mapVal.value(op))); return term; } term.setComparator(Equal); term.setValue(tryConvert(value)); return term; } bool Term::operator==(const Term& rhs) const { if (d->m_op != rhs.d->m_op || d->m_comp != rhs.d->m_comp || d->m_isNegated != rhs.d->m_isNegated || d->m_property != rhs.d->m_property || d->m_value != rhs.d->m_value) { return false; } if (d->m_subTerms.size() != rhs.d->m_subTerms.size()) return false; if (d->m_subTerms.isEmpty()) return true; - Q_FOREACH (const Term& t, d->m_subTerms) { + for (const Term& t : qAsConst(d->m_subTerms)) { if (!rhs.d->m_subTerms.contains(t)) return false; } return true; } Term& Term::operator=(const Term& rhs) { *d = *rhs.d; return *this; } namespace { QString comparatorToString(Baloo::Term::Comparator c) { switch (c) { case Baloo::Term::Auto: return QStringLiteral("Auto"); case Baloo::Term::Equal: return QStringLiteral("="); case Baloo::Term::Contains: return QStringLiteral(":"); case Baloo::Term::Less: return QStringLiteral("<"); case Baloo::Term::LessEqual: return QStringLiteral("<="); case Baloo::Term::Greater: return QStringLiteral(">"); case Baloo::Term::GreaterEqual: return QStringLiteral(">="); } return QString(); } QString operationToString(Baloo::Term::Operation op) { switch (op) { case Baloo::Term::None: return QStringLiteral("NONE"); case Baloo::Term::And: return QStringLiteral("AND"); case Baloo::Term::Or: return QStringLiteral("OR"); } return QString(); } } QDebug operator <<(QDebug d, const Baloo::Term& t) { if (t.subTerms().isEmpty()) { d << QStringLiteral("(%1 %2 %3(%4))").arg(t.property(), comparatorToString(t.comparator()), QString::fromLatin1(t.value().typeName()), t.value().toString()); } else { d << "[" << operationToString(t.operation()).toUtf8().constData(); const auto subTerms = t.subTerms(); for (const Term& term : subTerms) { d << term; } d << "]"; } return d; } diff --git a/src/tools/balooshow/main.cpp b/src/tools/balooshow/main.cpp index 4419bc19..32bc18ff 100644 --- a/src/tools/balooshow/main.cpp +++ b/src/tools/balooshow/main.cpp @@ -1,255 +1,255 @@ /* Copyright (c) 2012-2013 Vishesh Handa This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "global.h" #include "idutils.h" #include "database.h" #include "transaction.h" #include #include QString colorString(const QString& input, int color) { if(isatty(fileno(stdout))) { QString colorStart = QStringLiteral("\033[0;%1m").arg(color); QLatin1String colorEnd("\033[0;0m"); return colorStart + input + colorEnd; } else { return input; } } int main(int argc, char* argv[]) { QCoreApplication app(argc, argv); KAboutData aboutData(QStringLiteral("balooshow"), i18n("Baloo Show"), PROJECT_VERSION, i18n("The Baloo data Viewer - A debugging tool"), KAboutLicense::GPL, i18n("(c) 2012, Vishesh Handa")); aboutData.addAuthor(i18n("Vishesh Handa"), i18n("Maintainer"), QStringLiteral("me@vhanda.in")); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("files"), i18n("The file urls")); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("x"), i18n("Print internal info"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("i"), i18n("Inode number of the file to show"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("d"), i18n("Device id for the files"), QStringLiteral("deviceId"), QString())); parser.addHelpOption(); parser.process(app); - QStringList args = parser.positionalArguments(); + const QStringList args = parser.positionalArguments(); if (args.isEmpty()) { parser.showHelp(1); } // // File Urls // QStringList urls; - Q_FOREACH (const QString& arg, args) { + for (const QString& arg : args) { const QString url = QFileInfo(arg).absoluteFilePath(); if (QFile::exists(url)) { urls.append(url); } else { if (parser.isSet(QStringLiteral("i"))) { urls.append(QLatin1String("inode:") + arg); } else { urls.append(QLatin1String("file:") + arg); } } } QTextStream stream(stdout); QString text; Baloo::Database *db = Baloo::globalDatabaseInstance(); if (!db->open(Baloo::Database::ReadOnlyDatabase)) { stream << i18n("The Baloo index could not be opened. Please run \"%1\" to see if Baloo is enabled and working.", QStringLiteral("balooctl status")) << endl; return 1; } Baloo::Transaction tr(db, Baloo::Transaction::ReadOnly); for (QString url : urls) { quint64 fid = 0; if (url.startsWith(QLatin1String("file:"))) { fid = url.midRef(5).toULongLong(); url = QFile::decodeName(tr.documentUrl(fid)); // Debugging aid quint64 actualFid = Baloo::filePathToId(QFile::encodeName(url)); if (fid != actualFid) { stream << i18n("The fileID is not equal to the actual Baloo fileID") << "\n"; stream << i18n("This is a bug") << "\n"; stream << "GivenID: " << fid << " ActualID: " << actualFid << "\n"; stream << "GivenINode: " << Baloo::idToInode(fid) << " ActualINode: " << Baloo::idToInode(actualFid) << "\n"; stream << "GivenDeviceID: " << Baloo::idToDeviceId(fid) << " ActualDeviceID: " << Baloo::idToDeviceId(actualFid) << endl; } } else if (url.startsWith(QStringLiteral("inode:"))) { quint32 inode = url.midRef(6).toULong(); quint32 devId = parser.value(QStringLiteral("d")).toULong(); fid = Baloo::devIdAndInodeToId(devId, inode); url = QFile::decodeName(tr.documentUrl(fid)); } else { fid = Baloo::filePathToId(QFile::encodeName(url)); } bool hasFile = tr.hasDocument(fid); if (hasFile) { text = colorString(QString::number(fid), 31); text += QLatin1String(" "); text += colorString(QString::number(Baloo::idToDeviceId(fid)), 28); text += QLatin1String(" "); text += colorString(QString::number(Baloo::idToInode(fid)), 28); text += QLatin1String(" "); text += colorString(url, 32); stream << text << endl; } else { stream << i18n("No index information found") << endl; continue; } const QJsonDocument jdoc = QJsonDocument::fromJson(tr.documentData(fid)); const QVariantMap varMap = jdoc.object().toVariantMap(); KFileMetaData::PropertyMap propMap = KFileMetaData::toPropertyMap(varMap); KFileMetaData::PropertyMap::const_iterator it = propMap.constBegin(); for (; it != propMap.constEnd(); ++it) { QString str; if (it.value().type() == QVariant::List) { QStringList list; const auto vars = it.value().toList(); for (const QVariant& var : vars) { list << var.toString(); } str = list.join(QStringLiteral(", ")); } else { str = it.value().toString(); } KFileMetaData::PropertyInfo pi(it.key()); stream << "\t" << pi.displayName() << ": " << str << endl; } if (parser.isSet(QStringLiteral("x"))) { QVector terms = tr.documentTerms(fid); QVector fileNameTerms = tr.documentFileNameTerms(fid); QVector xAttrTerms = tr.documentXattrTerms(fid); auto join = [](const QVector& v) { QByteArray ba; for (const QByteArray& arr : v) { ba.append(arr); ba.append(' '); } return QString(ba); }; stream << "\n" << i18n("Internal Info") << "\n"; stream << i18n("Terms: %1", join(terms)) << "\n"; stream << i18n("File Name Terms: %1", join(fileNameTerms)) << "\n"; stream << i18n("%1 Terms: %2", QStringLiteral("XAttr"), join(xAttrTerms)) << endl; QHash propertyWords; KLocalizedString errorPrefix = ki18nc("Prefix string for internal errors", "Internal Error - %1"); for (const QByteArray& arr : terms) { auto arrAsPrintable = [arr]() { return QString::fromLatin1(arr.toPercentEncoding()); }; if (arr.length() < 1) { auto error = QString("malformed term (short): '%1'\n").arg(arrAsPrintable()); stream << errorPrefix.subs(error).toString(); continue; } QString word = QString::fromUtf8(arr); if (word[0].isUpper()) { if (word[0] == QLatin1Char('X')) { if (word.length() < 4) { // 'X- auto error = QString("malformed property term (short): '%1' in '%2'\n").arg(word).arg(arrAsPrintable()); stream << errorPrefix.subs(error).toString(); continue; } int posOfNonNumeric = word.indexOf('-', 2); if ((posOfNonNumeric < 0) || ((posOfNonNumeric + 1) == word.length())) { auto error = QString("malformed property term (no data): '%1' in '%2'\n").arg(word).arg(arrAsPrintable()); stream << errorPrefix.subs(error).toString(); continue; } bool ok; QStringRef prop = word.midRef(1, posOfNonNumeric-1); int propNum = prop.toInt(&ok); QString value = word.mid(posOfNonNumeric + 1); if (!ok) { auto error = QString("malformed property term (bad index): '%1' in '%2'\n").arg(prop).arg(arrAsPrintable()); stream << errorPrefix.subs(error).toString(); continue; } propertyWords[propNum].append(value); } } } for (auto it = propertyWords.constBegin(); it != propertyWords.constEnd(); it++) { auto prop = static_cast(it.key()); KFileMetaData::PropertyInfo pi(prop); stream << pi.name() << ": " << it.value().join(QStringLiteral(" ")) << endl; } } } return 0; }