]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/private/kdirectorycontentscounterworker.cpp
GIT_SILENT Sync po/docbooks with svn
[dolphin.git] / src / kitemviews / private / kdirectorycontentscounterworker.cpp
index 59ff471aeee5e804b6ae8fada4bf642d0b602f8b..57cf7b1bc743527b6ebd970019bfe091bb160799 100644 (file)
 
 #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 <QDir>
 #else
-#include <QFile>
-#include <qplatformdefs.h>
+#include <QElapsedTimer>
+#include <fts.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #endif
 
-#include "dolphin_detailsmodesettings.h"
-
-KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) :
-    QObject(parent)
+KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent)
+    : QObject(parent)
 {
     qRegisterMetaType<KDirectoryContentsCounterWorker::Options>();
 }
 
-#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;
-        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<int>(dir.entryList(filters).count()), 0);
+#else
 
-    auto res = walkDir(QFile::encodeName(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);
-    emit result(path, res.count, res.size);
+    m_stopping = false;
+    Q_EMIT finished();
 }
+
+#include "moc_kdirectorycontentscounterworker.cpp"