X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/4bfc28cb4bceb55996eec853da383efb44aaeeba..dd07a327:/src/kitemviews/private/kdirectorycontentscounterworker.cpp diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp index e649c20e1..57cf7b1bc 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.cpp +++ b/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -1,95 +1,160 @@ -/*************************************************************************** - * Copyright (C) 2011 by Peter Penz * - * Copyright (C) 2013 by Frank Reininghaus * - * * - * 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) any later version. * - * * - * 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, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ +/* + * SPDX-FileCopyrightText: 2011 Peter Penz + * SPDX-FileCopyrightText: 2013 Frank Reininghaus + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ #include "kdirectorycontentscounterworker.h" -// Required includes for subItemsCount(): -#ifdef Q_WS_WIN - #include +// Required includes for countDirectoryContents(): +#if defined(Q_OS_WIN) || defined(Q_OS_HAIKU) +#include #else - #include - #include +#include +#include +#include +#include #endif -KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) : - QObject(parent) +KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent) + : QObject(parent) { qRegisterMetaType(); } -int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options) +#if !defined(Q_OS_WIN) && !defined(Q_OS_HAIKU) +void KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, bool countHiddenFiles, uint allowedRecursiveLevel) { - const bool countHiddenFiles = options & CountHiddenFiles; - const bool countDirectoriesOnly = options & CountDirectoriesOnly; + QByteArray text = dirPath.toLocal8Bit(); + char *rootPath = new char[text.size() + 1]; + ::strncpy(rootPath, text.constData(), text.size() + 1); + char *path[2]{rootPath, nullptr}; -#ifdef Q_WS_WIN - QDir dir(path); - QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; - if (countHiddenFiles) { - filters |= QDir::Hidden; + // follow symlink only for root dir + auto tree = ::fts_open(path, FTS_COMFOLLOW | FTS_PHYSICAL | FTS_XDEV, nullptr); + if (!tree) { + delete[] rootPath; + return; } - if (countDirectoriesOnly) { - filters |= QDir::Dirs; - } else { - filters |= QDir::AllEntries; - } - return dir.entryList(filters).count(); -#else - // Taken from kdelibs/kio/kio/kdirmodel.cpp - // Copyright (C) 2006 David Faure - - int count = -1; - DIR* dir = ::opendir(QFile::encodeName(path)); - if (dir) { // krazy:exclude=syscalls - count = 0; - struct dirent *dirEntry = 0; - while ((dirEntry = ::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; - } + + 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 (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 (info == FTS_D) { + if (node->fts_level == 0) { + // first read was sucessful, we can init counters + totalSize = 0; + totalCount = 0; } - // 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 (node->fts_level > (int)allowedRecursiveLevel) { + // skip too deep nodes + fts_set(tree, node, FTS_SKIP); + continue; } } - ::closedir(dir); + // count first level elements + if (node->fts_level == 1) { + ++totalCount; + } + + // 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; } - return count; + + if (!m_stopping) { + Q_EMIT result(dirPath, totalCount, totalSize); + } +} #endif + +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) +void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options, int maxRecursiveLevel) { - emit result(path, subItemsCount(path, options)); + const bool countHiddenFiles = options & CountHiddenFiles; + +#if defined(Q_OS_WIN) || defined(Q_OS_HAIKU) + QDir dir(path); + QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System | QDir::AllEntries; + if (countHiddenFiles) { + filters |= QDir::Hidden; + } + + Q_EMIT result(path, static_cast(dir.entryList(filters).count()), 0); +#else + + m_scannedPath = path; + walkDir(path, countHiddenFiles, maxRecursiveLevel); + +#endif + + m_stopping = false; + Q_EMIT finished(); } + +#include "moc_kdirectorycontentscounterworker.cpp"