Commit e4c9db69 authored by Harald Sitter's avatar Harald Sitter 🏳️‍🌈
Browse files

rebuild the iteration tech using better architecture

the previous approach just didn't cut it for windows.

the new code sports a forward iterator that fronts for a
platform-dependent walker object that encapsulates the iteration logic

this looks and feels a lot like std::filesystem API but unfortunately we
cannot really use that API directly because I want this change to be
conservative enough to land in 22.04 as a bugfix for windows, also on
POSIX std::filesystem returns the st_size (size in bytes) whereas we
want the actual occupied blocks (st_blocks*size), and lastly it's also a
tad slower because of heavier abstraction

should we choose to go the std::filesystem route in the future anyway it
should be a trivial switch because of how similar the APIs are.

furthermore move to always convert from/to utf8. the QFile helpers
ultimately end up in the same code paths anyway, so it seems simpler to
just go with the utf8 variants directly (also on windows QFile somehow
produces bogus output for actual unico...
parent e635decc
Pipeline #169347 passed with stage
in 2 minutes and 31 seconds
......@@ -4,3 +4,16 @@ find_package(Qt${QT_MAJOR_VERSION}Test ${QT_MIN_VERSION} REQUIRED Test)
find_package(KF5CoreAddons ${KF5_DEP_VERSION} REQUIRED)
add_subdirectory(core)
# Synthesize links. On some windowses the git install is set to not support symlinks (plus it may not be able to
# create them). Instead synthesize the links at configure time and configure the test accordingly.
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/iterator-tree.in ${CMAKE_CURRENT_BINARY_DIR}/iterator-tree)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/iterator-tree.in DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(RENAME ${CMAKE_CURRENT_BINARY_DIR}/iterator-tree.in ${CMAKE_CURRENT_BINARY_DIR}/iterator-tree)
set(ITERATOR_TREE_WITH_SYMLINK ON)
file(CREATE_LINK ${CMAKE_CURRENT_BINARY_DIR}/iterator-tree/bar ${CMAKE_CURRENT_BINARY_DIR}/iterator-tree/symlink RESULT ITERATOR_TREE_WITH_SYMLINK SYMBOLIC)
set(ITERATOR_TREE_WITH_LINK ON)
file(CREATE_LINK ${CMAKE_CURRENT_BINARY_DIR}/iterator-tree/bar ${CMAKE_CURRENT_BINARY_DIR}/iterator-tree/link RESULT ITERATOR_TREE_WITH_LINK)
configure_file(test-config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/test-config.h)
ecm_add_test(directoryIteratorTest.cpp TEST_NAME directoryIteratorTest LINK_LIBRARIES Qt::Test filelightInternal)
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
#include <QDebug>
#include <QFileInfo>
#include <QTest>
#include "directoryIterator.h"
#include "test-config.h"
class DirectoryIteratorTest : public QObject
{
Q_OBJECT
const QString m_tree = QFINDTESTDATA("iterator-tree");
private Q_SLOTS:
void testIterate()
{
#if defined(ITERATOR_TREE_WITH_SYMLINK)
const auto withSymlink = true;
#else
const auto withSymlink = false;
#endif
#if defined(ITERATOR_TREE_WITH_LINK)
const auto withLink = true;
#else
const auto withLink = false;
#endif
QMap<QByteArray, DirectoryEntry> entries;
for (const auto &entry : DirectoryIterator(m_tree.toUtf8())) {
qDebug() << entry.name;
QVERIFY(!entries.contains(entry.name));
entries.insert(entry.name, entry);
}
qDebug() << entries.keys();
auto expectedEntries = 3;
if (withSymlink) {
++expectedEntries;
}
if (withLink) {
++expectedEntries;
}
QCOMPARE(entries.size(), expectedEntries);
QVERIFY(entries.contains(QByteArrayLiteral("foo")));
QVERIFY(entries.contains(QByteArrayLiteral("Con 자백")));
const auto dir = entries[QByteArrayLiteral("Con 자백")];
QVERIFY(dir.isDir);
QVERIFY(!dir.isFile);
QVERIFY(!dir.isSkipable);
// size doesn't matter, it's ignored
QVERIFY(entries.contains(QByteArrayLiteral("bar")));
const auto file = entries[QByteArrayLiteral("bar")];
QVERIFY(!file.isDir);
QVERIFY(file.isFile);
QVERIFY(!file.isSkipable);
#ifdef Q_OS_WINDOWS
QCOMPARE(file.size, 7682);
#elif defined(Q_OS_FREEBSD)
QCOMPARE(file.size, 1 * S_BLKSIZE);
#else
QCOMPARE(file.size, 16 * S_BLKSIZE);
#endif
if (withSymlink) {
QVERIFY(entries.contains(QByteArrayLiteral("symlink")));
const auto symlink = entries[QByteArrayLiteral("symlink")];
QVERIFY(!symlink.isDir);
QVERIFY(!symlink.isFile);
QVERIFY(symlink.isSkipable);
// size of skippables doesn't matter
}
if (withLink) {
QVERIFY(entries.contains(QByteArrayLiteral("link")));
const auto symlink = entries[QByteArrayLiteral("link")];
QVERIFY(!symlink.isDir);
QVERIFY(symlink.isFile);
QVERIFY(!symlink.isSkipable);
#ifdef Q_OS_WINDOWS
QCOMPARE(symlink.size, 7682);
#elif defined(Q_OS_FREEBSD)
QCOMPARE(file.size, 1 * S_BLKSIZE);
#else
QCOMPARE(symlink.size, 16 * S_BLKSIZE);
#endif
}
}
void testIterateInsideUnicode()
{
QByteArray tree = QFINDTESTDATA("iterator-tree/Con 자백").toUtf8();
QMap<QByteArray, DirectoryEntry> entries;
for (const auto &entry : DirectoryIterator(tree)) {
qDebug() << entry.name;
QVERIFY(!entries.contains(entry.name));
entries.insert(entry.name, entry);
}
qDebug() << entries.keys();
}
// During development there were some bugs with iterating C:/, make sure this finishes eventually and has some entries.
void testCDrive()
{
QMap<QByteArray, DirectoryEntry> entries;
for (const auto &entry : DirectoryIterator(m_tree.toUtf8())) {
QVERIFY(!entries.contains(entry.name));
entries.insert(entry.name, entry);
}
QVERIFY(entries.size() > 3); // windows, programs, users
}
void testBadPath()
{
for (const auto &entry : DirectoryIterator(QByteArrayLiteral("/tmp/filelighttest1312312312313123123123"))) {
Q_UNUSED(entry);
QVERIFY(false);
}
}
};
QTEST_GUILESS_MAIN(DirectoryIteratorTest)
#include "directoryIteratorTest.moc"
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
#pragma once
#cmakedefine01 ITERATOR_TREE_WITH_SYMLINK
#cmakedefine01 ITERATOR_TREE_WITH_LINK
......@@ -9,6 +9,17 @@ ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX FILELIGHT
VERSION_HEADER version.h)
add_library(filelightInternal STATIC fileTree.cpp directoryIterator.cpp)
if (WIN32)
target_sources(filelightInternal PRIVATE windowsWalker.cpp)
else()
target_sources(filelightInternal PRIVATE posixWalker.cpp)
endif()
target_link_libraries(filelightInternal
Qt${QT_MAJOR_VERSION}::Core
KF5::CoreAddons
)
add_executable(filelight)
target_sources(filelight PRIVATE
radialMap/widget.cpp
......@@ -19,7 +30,6 @@ target_sources(filelight PRIVATE
progressBox.cpp
Config.cpp
settingsDialog.cpp
fileTree.cpp
localLister.cpp
remoteLister.cpp
summaryWidget.cpp
......@@ -60,6 +70,7 @@ target_link_libraries(filelight
KF5::XmlGui
KF5::KIOWidgets # Only used for KDirLister, may be able to move away from that.
Qt${QT_MAJOR_VERSION}::Svg
filelightInternal
)
if (WIN32)
find_package(KDEWin REQUIRED)
......
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
#pragma once
#include <QByteArray>
struct DirectoryEntry {
QByteArray name;
bool isSkipable = true;
bool isDir = false;
bool isFile = false;
size_t size = 0;
};
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
#include "directoryIterator.h"
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
#pragma once
#include "directoryEntry.h"
#ifdef Q_OS_WINDOWS
#include "windowsWalker.h"
template<class T = WindowsWalker>
#else
#include "posixWalker.h"
template<class T = POSIXWalker>
#endif
class DirectoryIterator
{
public:
using value_type = DirectoryEntry;
using difference_type = std::ptrdiff_t;
using pointer = const DirectoryEntry *;
using reference = const DirectoryEntry &;
using iterator_category = std::input_iterator_tag;
DirectoryIterator() = default;
explicit DirectoryIterator(const QByteArray &path)
: m_walker(std::make_shared<T>(path))
{
}
const DirectoryEntry &operator*() const
{
return m_walker->m_entry;
};
DirectoryIterator &operator++()
{
m_walker->next();
return *this;
}
std::shared_ptr<T> m_walker = std::make_shared<T>(QByteArray()); // always have a valid walker to simplify accessing into it
private:
friend bool operator==(const DirectoryIterator &lhs, const DirectoryIterator &rhs) noexcept
{
return lhs.m_walker->m_entry.name == rhs.m_walker->m_entry.name;
}
friend bool operator!=(const DirectoryIterator &lhs, const DirectoryIterator &rhs) noexcept
{
return !(lhs == rhs);
}
};
template<class T>
DirectoryIterator<T> begin(DirectoryIterator<T> iter) noexcept
{
return DirectoryIterator<T>(iter.m_walker->m_path);
}
template<class T>
DirectoryIterator<T> end(DirectoryIterator<T> iter) noexcept
{
Q_UNUSED(iter);
return {};
}
......@@ -12,7 +12,7 @@
#include <QUrl>
QString File::displayName() const {
const QString decodedName = QFile::decodeName(m_name);
const QString decodedName = QString::fromUtf8(m_name);
return url().isLocalFile() ? QDir::toNativeSeparators(decodedName) : decodedName;
}
......@@ -34,7 +34,7 @@ QUrl File::url(const Folder *root) const
}
for (const File *d = this; d != root && d; d = d->parent()) {
path.prepend(QFile::decodeName(d->name8Bit()));
path.prepend(QString::fromUtf8(d->name8Bit()));
}
return QUrl::fromUserInput(path, QString(), QUrl::AssumeLocalFile);
......
......@@ -10,7 +10,6 @@
#define FILETREE_H
#include <QByteArray> //qstrdup
#include <QFile> //decodeName()
#include <KFormat>
#include <stdlib.h>
......@@ -45,7 +44,7 @@ public:
}
/** Decoded name. Use when you need a QString. */
QString decodedName() const {
return QFile::decodeName(m_name);
return QString::fromUtf8(m_name);
}
/**
* Human readable name (including native separators where applicable).
......
......@@ -16,14 +16,10 @@
#include <QStorageInfo>
#include <QElapsedTimer>
#include <QGuiApplication> //postEvent()
#include <QFile>
#include <QThreadPool>
#include <QSemaphore>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "directoryIterator.h"
#ifdef HAVE_MNTENT_H
#include <mntent.h>
......@@ -34,12 +30,9 @@ namespace Filelight
QStringList LocalLister::s_remoteMounts;
QStringList LocalLister::s_localMounts;
LocalLister::LocalLister(const QString &path, QList<Folder *> *cachedTrees, ScanManager *parent)
: QThread()
, m_path(path)
, m_trees(cachedTrees)
, m_parent(parent)
{
LocalLister::LocalLister(const QString &path, QList<Folder *> *cachedTrees,
ScanManager *parent)
: QThread(), m_path(path), m_trees(cachedTrees), m_parent(parent) {
//add empty directories for any mount points that are in the path
//TODO empty directories is not ideal as adds to fileCount incorrectly
......@@ -64,7 +57,7 @@ LocalLister::run()
QElapsedTimer timer;
timer.start();
//recursively scan the requested path
const QByteArray path = QFile::encodeName(m_path);
const QByteArray path = m_path.toUtf8();
Folder *tree = scan(path, path);
qCDebug(FILELIGHT_LOG) << "Scan completed in" << (timer.elapsed()/1000);
......@@ -83,140 +76,36 @@ LocalLister::run()
qCDebug(FILELIGHT_LOG) << "Thread terminating ...";
}
#ifndef S_BLKSIZE
#define S_BLKSIZE 512
#endif
#include <errno.h>
static void
outputError(const QByteArray &path)
Folder* LocalLister::scan(const QByteArray &path, const QByteArray &dirname)
{
///show error message that stat or opendir may give
#define out(s) qWarning() << s ": " << path; break
switch (errno) {
case EACCES:
out("Inadequate access permissions");
case EMFILE:
out("Too many file descriptors in use by Filelight");
case ENFILE:
out("Too many files are currently open in the system");
case ENOENT:
out("A component of the path does not exist, or the path is an empty string");
case ENOMEM:
out("Insufficient memory to complete the operation");
case ENOTDIR:
out("A component of the path is not a folder");
case EBADF:
out("Bad file descriptor");
case EFAULT:
out("Bad address");
#ifndef Q_OS_WIN
case ELOOP: //NOTE shouldn't ever happen
out("Too many symbolic links encountered while traversing the path");
#endif
case ENAMETOOLONG:
out("File name too long");
}
#undef out
}
Folder*
LocalLister::scan(const QByteArray &path, const QByteArray &dirname)
{
Folder *cwd = new Folder(dirname.constData());
DIR *dir = opendir(path.constData());
if (!dir) {
outputError(path);
return cwd;
}
auto cwd = new Folder(dirname.constData());
QVector<QPair<QByteArray, QByteArray>> subDirectories;
struct stat statbuf;
dirent *ent;
while ((ent = readdir(dir)))
{
if (m_parent->m_abort)
{
closedir(dir);
for (const auto &entry : DirectoryIterator(path)) {
if (m_parent->m_abort) {
return cwd;
}
if (qstrcmp(ent->d_name, ".") == 0 || qstrcmp(ent->d_name, "..") == 0) {
continue;
}
// QStringBuilder is used here. It assumes ent->d_name is char[NAME_MAX + 1],
// and thus copies only first NAME_MAX + 1 chars.
// Actually, while it's not fully POSIX-compatible, current behaviour may return d_name longer than NAME_MAX.
// Make full copy of this string.
QByteArray new_path = path + static_cast<const char*>(ent->d_name);
// get file information. Split this per-OS. File attributes on windows/ntfs are special and not properly
// represented through its _stat-like API.
#ifdef Q_OS_WINDOWS
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileattributesexa
FILE_BASIC_INFO basicInfo;
WIN32_FILE_ATTRIBUTE_DATA fileAttributeData;
BOOL result =
GetFileAttributesExA(new_path.constData(), GetFileExInfoStandard,
(LPVOID)&fileAttributeData);
if (result == 0) {
qWarning() << "failed to get attributes for" << new_path;
if (entry.isSkipable) {
continue;
}
const auto attributes = fileAttributeData.dwFileAttributes;
// Reparse points are symlinks or NTFS junctions.
const bool isSkipable = attributes & FILE_ATTRIBUTE_REPARSE_POINT || attributes & FILE_ATTRIBUTE_TEMPORARY;
const bool isDir = attributes & FILE_ATTRIBUTE_DIRECTORY;
const bool isFile = !isSkipable && !isDir; // fileness is implicit in win32 api
ULARGE_INTEGER ulargeInt;
ulargeInt.HighPart = fileAttributeData.nFileSizeHigh;
ulargeInt.LowPart = fileAttributeData.nFileSizeLow;
const auto size = ulargeInt.QuadPart;
#else
if (lstat(new_path.constData(), &statbuf) == -1) {
outputError(new_path);
continue;
}
const bool isSkipable = S_ISLNK(statbuf.st_mode) ||
S_ISCHR(statbuf.st_mode) ||
S_ISBLK(statbuf.st_mode) ||
S_ISFIFO(statbuf.st_mode)||
S_ISSOCK(statbuf.st_mode);
const bool isDir = S_ISDIR(statbuf.st_mode);
const bool isFile =S_ISREG(statbuf.st_mode);
const auto size = statbuf.st_blocks * S_BLKSIZE;
#endif
if (isSkipable) {
continue;
}
if (isFile) {
cwd->append(ent->d_name, size);
m_parent->m_totalSize += size;
} else if (isDir) {
if (entry.isFile) {
cwd->append(entry.name.constData(), entry.size);
m_parent->m_totalSize += entry.size;
} else if (entry.isDir) {
Folder *d = nullptr;
const QByteArray new_dirname = QByteArray(ent->d_name) + QByteArrayLiteral("/");
new_path += '/';
const QByteArray new_dirname = entry.name + QByteArrayLiteral("/");
qDebug() << new_dirname;
const QByteArray new_path = path + entry.name + '/';
qDebug() << new_path;
//check to see if we've scanned this section already
// check to see if we've scanned this section already
QMutexLocker lock(&m_treeMutex);
for (Folder *folder : *m_trees)
{
if (new_path == folder->name8Bit())
{
qCDebug(FILELIGHT_LOG) << "Tree pre-completed: " << folder->decodedName();
for (Folder *folder : std::as_const(*m_trees)) {
if (new_path == folder->name8Bit()) {
qDebug() << "Tree pre-completed: " << folder->decodedName();
d = folder;
m_trees->removeAll(folder);
m_parent->m_files += folder->children();
......@@ -225,7 +114,8 @@ LocalLister::scan(const QByteArray &path, const QByteArray &dirname)
}
lock.unlock();
if (!d) { //then scan
if (!d) { // then scan
// qDebug() << "Tree fresh" <<new_path << new_dirname;
subDirectories.append({new_path, new_dirname});
}
}
......@@ -233,8 +123,6 @@ LocalLister::scan(const QByteArray &path, const QByteArray &dirname)
++m_parent->m_files;
}
closedir(dir);
// Scan all subdirectories, either in separate threads or immediately,
// depending on how many free threads there are in the threadpool.
// Yes, it isn't optimal, but it's better than nothing and pretty simple.
......@@ -246,7 +134,7 @@ LocalLister::scan(const QByteArray &path, const QByteArray &dirname)
semaphore.release(1);
};
if (!QThreadPool::globalInstance()->tryStart(scanSubdir)) {
scanSubdir();
scanSubdir();
}
}
semaphore.acquire(subDirectories.count());
......
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
#include "posixWalker.h"
static void outputError(const QByteArray &path)
{
/// show error message that stat or opendir may give
#define out(s) \
qWarning() << s ": " << path; \
break
switch (errno) {
case EACCES:
out("Inadequate access permissions");
case EMFILE:
out("Too many file descriptors in use by Filelight");
case ENFILE:
out("Too many files are currently open in the system");
case ENOENT:
out("A component of the path does not exist, or the path is an empty string");
case ENOMEM:
out("Insufficient memory to complete the operation");
case ENOTDIR:
out("A component of the path is not a folder");
case EBADF:
out("Bad file descriptor");
case EFAULT:
out("Bad address");
case ELOOP: // NOTE shouldn't ever happen
out("Too many symbolic links encountered while traversing the path");
case ENAMETOOLONG:
out("File name too long");
}
#undef out
}
POSIXWalker::POSIXWalker(const QByteArray &path)
: m_path(path)
{
if (path.isEmpty()) {
return;
}
m_dir = opendir(path.constData());
if (!m_dir) {
outputError(QByteArray(path));
return;
}
m_dirfd = dirfd(m_dir);
// load first entry to achieve iterator behavior. If there are no entries then this results
// in a default constructed m_entry and thus ==end(); otherwise it is the first m_entry ==begin().
next();
}
POSIXWalker::~POSIXWalker()
{
close();
}
void POSIXWalker::close()
{
if (m_dir) {
closedir(m_dir);
m_dir = nullptr;