X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/de8ea90ca78ccfb26dd501dc3bc089fb24ad9094..dd07a327:/src/kitemviews/private/kdirectorycontentscounterworker.cpp diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp index 73799e739..57cf7b1bc 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.cpp +++ b/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -7,117 +7,154 @@ #include "kdirectorycontentscounterworker.h" -// Required includes for subItemsCount(): -#ifdef Q_OS_WIN +// Required includes for countDirectoryContents(): +#if defined(Q_OS_WIN) || defined(Q_OS_HAIKU) #include #else -#include -#include +#include +#include +#include +#include #endif -#include "dolphin_detailsmodesettings.h" - -KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) : - QObject(parent) +KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent) + : QObject(parent) { qRegisterMetaType(); } -#ifndef Q_OS_WIN -KDirectoryContentsCounterWorker::CountResult walkDir(const QString &dirPath, - const bool countHiddenFiles, - const bool countDirectoriesOnly, - QT_DIRENT *dirEntry, - const uint allowedRecursiveLevel) +#if !defined(Q_OS_WIN) && !defined(Q_OS_HAIKU) +void KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, bool countHiddenFiles, uint allowedRecursiveLevel) { - int count = -1; - long size = -1; - auto dir = QT_OPENDIR(QFile::encodeName(dirPath)); - if (dir) { - count = 0; - size = 0; - QT_STATBUF buf; - - while ((dirEntry = QT_READDIR(dir))) { - if (dirEntry->d_name[0] == '.') { - if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { - // Skip "." or hidden files - continue; - } - if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') { - // Skip ".." - continue; - } + QByteArray text = dirPath.toLocal8Bit(); + char *rootPath = new char[text.size() + 1]; + ::strncpy(rootPath, text.constData(), text.size() + 1); + char *path[2]{rootPath, nullptr}; + + // follow symlink only for root dir + auto tree = ::fts_open(path, FTS_COMFOLLOW | FTS_PHYSICAL | FTS_XDEV, nullptr); + if (!tree) { + delete[] rootPath; + return; + } + + FTSENT *node; + long long totalSize = -1; + int totalCount = -1; + QElapsedTimer timer; + timer.start(); + + while ((node = fts_read(tree)) && !m_stopping) { + auto info = node->fts_info; + + if (info == FTS_DC) { + // ignore directories clausing cycles + continue; + } + if (info == FTS_DNR) { + // ignore directories that can’t be read + continue; + } + if (info == FTS_ERR) { + // ignore directories causing errors + fts_set(tree, node, FTS_SKIP); + continue; + } + if (info == FTS_DP) { + // ignore end traversal of dir + continue; + } + + if (!countHiddenFiles && node->fts_name[0] == '.' && strncmp(".git", node->fts_name, 4) != 0) { + // skip hidden files, except .git dirs + if (info == FTS_D) { + fts_set(tree, node, FTS_SKIP); } + continue; + } - // If only directories are counted, consider an unknown file type and links also - // as directory instead of trying to do an expensive stat() - // (see bugs 292642 and 299997). - const bool countEntry = !countDirectoriesOnly || - dirEntry->d_type == DT_DIR || - dirEntry->d_type == DT_LNK || - dirEntry->d_type == DT_UNKNOWN; - if (countEntry) { - ++count; + if (info == FTS_F) { + // only count files that are physical (aka skip /proc/kcore...) + // naive size counting not taking into account effective disk space used (aka size/block_size * block_size) + // skip directory size (usually a 4KB block) + if (node->fts_statp->st_blocks > 0) { + totalSize += node->fts_statp->st_size; } + } - if (allowedRecursiveLevel > 0) { - - bool linkFound = false; - QString nameBuf = QStringLiteral("%1/%2").arg(dirPath, dirEntry->d_name); - - if (dirEntry->d_type == DT_REG || dirEntry->d_type == DT_LNK) { - if (QT_STAT(nameBuf.toLocal8Bit(), &buf) == 0) { - if (S_ISDIR(buf.st_mode)) { - // was a dir link, recurse - linkFound = true; - } - size += buf.st_size; - } - } - if (dirEntry->d_type == DT_DIR || linkFound) { - // recursion for dirs and dir links - size += walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, dirEntry, allowedRecursiveLevel - 1).size; - } + if (info == FTS_D) { + if (node->fts_level == 0) { + // first read was sucessful, we can init counters + totalSize = 0; + totalCount = 0; } + + if (node->fts_level > (int)allowedRecursiveLevel) { + // skip too deep nodes + fts_set(tree, node, FTS_SKIP); + continue; + } + } + // count first level elements + if (node->fts_level == 1) { + ++totalCount; } - QT_CLOSEDIR(dir); + + // delay intermediate results + if (timer.hasExpired(200) || node->fts_level == 0) { + Q_EMIT intermediateResult(dirPath, totalCount, totalSize); + timer.restart(); + } + } + + delete[] rootPath; + fts_close(tree); + if (errno != 0) { + return; + } + + if (!m_stopping) { + Q_EMIT result(dirPath, totalCount, totalSize); } - return KDirectoryContentsCounterWorker::CountResult{count, size}; } #endif -KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options) +void KDirectoryContentsCounterWorker::stop() +{ + m_stopping = true; +} + +bool KDirectoryContentsCounterWorker::stopping() const +{ + return m_stopping; +} + +QString KDirectoryContentsCounterWorker::scannedPath() const +{ + return m_scannedPath; +} + +void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options, int maxRecursiveLevel) { const bool countHiddenFiles = options & CountHiddenFiles; - const bool countDirectoriesOnly = options & CountDirectoriesOnly; -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) || defined(Q_OS_HAIKU) QDir dir(path); - QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; + QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System | QDir::AllEntries; if (countHiddenFiles) { filters |= QDir::Hidden; } - if (countDirectoriesOnly) { - filters |= QDir::Dirs; - } else { - filters |= QDir::AllEntries; - } - return {dir.entryList(filters).count(), 0}; -#else - - const uint maxRecursiveLevel = DetailsModeSettings::directorySizeCount() ? 1 : DetailsModeSettings::recursiveDirectorySizeLimit(); - QT_DIRENT *dirEntry = nullptr; + Q_EMIT result(path, static_cast(dir.entryList(filters).count()), 0); +#else - auto res = walkDir(path, countHiddenFiles, countDirectoriesOnly, dirEntry, maxRecursiveLevel); + m_scannedPath = path; + walkDir(path, countHiddenFiles, maxRecursiveLevel); - return res; #endif -} -void KDirectoryContentsCounterWorker::countDirectoryContents(const QString& path, Options options) -{ - auto res = subItemsCount(path, options); - Q_EMIT result(path, res.count, res.size); + m_stopping = false; + Q_EMIT finished(); } + +#include "moc_kdirectorycontentscounterworker.cpp"