]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kdirectorycontentscounterworker.cpp
KDirectoryContentsCounter: show intermediate dir size counting results, improve stopp...
[dolphin.git] / src / kitemviews / private / kdirectorycontentscounterworker.cpp
1 /*
2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2013 Frank Reininghaus <frank78ac@googlemail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "kdirectorycontentscounterworker.h"
9
10 // Required includes for countDirectoryContents():
11 #ifdef Q_OS_WIN
12 #include <QDir>
13 #else
14 #include <QElapsedTimer>
15 #include <fts.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #endif
19
20 KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent)
21 : QObject(parent)
22 {
23 qRegisterMetaType<KDirectoryContentsCounterWorker::Options>();
24 }
25
26 #ifndef Q_OS_WIN
27 void KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, bool countHiddenFiles, uint allowedRecursiveLevel)
28 {
29 QByteArray text = dirPath.toLocal8Bit();
30 char *rootPath = new char[text.size() + 1];
31 ::strncpy(rootPath, text.constData(), text.size() + 1);
32 char *path[2]{rootPath, nullptr};
33
34 // follow symlink only for root dir
35 auto tree = ::fts_open(path, FTS_COMFOLLOW | FTS_PHYSICAL | FTS_XDEV, nullptr);
36 if (!tree) {
37 delete[] rootPath;
38 return;
39 }
40
41 FTSENT *node;
42 long long totalSize = -1;
43 int totalCount = -1;
44 QElapsedTimer timer;
45 timer.start();
46
47 while ((node = fts_read(tree)) && !m_stopping) {
48 auto info = node->fts_info;
49
50 if (info == FTS_DC) {
51 // ignore directories clausing cycles
52 continue;
53 }
54 if (info == FTS_DNR) {
55 // ignore directories that can’t be read
56 continue;
57 }
58 if (info == FTS_ERR) {
59 // ignore directories causing errors
60 fts_set(tree, node, FTS_SKIP);
61 continue;
62 }
63 if (info == FTS_DP) {
64 // ignore end traversal of dir
65 continue;
66 }
67
68 if (!countHiddenFiles && node->fts_name[0] == '.' && strncmp(".git", node->fts_name, 4) != 0) {
69 // skip hidden files, except .git dirs
70 if (info == FTS_D) {
71 fts_set(tree, node, FTS_SKIP);
72 }
73 continue;
74 }
75
76 if (info == FTS_F) {
77 // only count files that are physical (aka skip /proc/kcore...)
78 // naive size counting not taking into account effective disk space used (aka size/block_size * block_size)
79 // skip directory size (usually a 4KB block)
80 if (node->fts_statp->st_blocks > 0) {
81 totalSize += node->fts_statp->st_size;
82 }
83 }
84
85 if (info == FTS_D) {
86 if (node->fts_level == 0) {
87 // first read was sucessful, we can init counters
88 totalSize = 0;
89 totalCount = 0;
90 }
91
92 if (node->fts_level > (int)allowedRecursiveLevel) {
93 // skip too deep nodes
94 fts_set(tree, node, FTS_SKIP);
95 continue;
96 }
97 }
98 // count first level elements
99 if (node->fts_level == 1) {
100 ++totalCount;
101 }
102
103 // delay intermediate results
104 if (timer.hasExpired(200) || node->fts_level == 0) {
105 Q_EMIT intermediateResult(dirPath, totalCount, totalSize);
106 timer.restart();
107 }
108 }
109
110 delete[] rootPath;
111 fts_close(tree);
112 if (errno != 0) {
113 return;
114 }
115
116 if (!m_stopping) {
117 Q_EMIT result(dirPath, totalCount, totalSize);
118 }
119 }
120 #endif
121
122 void KDirectoryContentsCounterWorker::stop()
123 {
124 m_stopping = true;
125 }
126
127 bool KDirectoryContentsCounterWorker::stopping() const
128 {
129 return m_stopping;
130 }
131
132 QString KDirectoryContentsCounterWorker::scannedPath() const
133 {
134 return m_scannedPath;
135 }
136
137 void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options, int maxRecursiveLevel)
138 {
139 const bool countHiddenFiles = options & CountHiddenFiles;
140
141 #ifdef Q_OS_WIN
142 QDir dir(path);
143 QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System | QDir::AllEntries;
144 if (countHiddenFiles) {
145 filters |= QDir::Hidden;
146 }
147
148 Q_EMIT result(path, static_cast<int>(dir.entryList(filters).count()), 0);
149 #else
150
151 m_scannedPath = path;
152 walkDir(path, countHiddenFiles, maxRecursiveLevel);
153
154 #endif
155
156 m_stopping = false;
157 Q_EMIT finished();
158 }