2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2013 Frank Reininghaus <frank78ac@googlemail.com>
5 * SPDX-License-Identifier: GPL-2.0-or-later
8 #include "kdirectorycontentscounterworker.h"
10 // Required includes for countDirectoryContents():
14 #include <QElapsedTimer>
17 #include <sys/types.h>
20 KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject
*parent
)
23 qRegisterMetaType
<KDirectoryContentsCounterWorker::Options
>();
27 void KDirectoryContentsCounterWorker::walkDir(const QString
&dirPath
, bool countHiddenFiles
, uint allowedRecursiveLevel
)
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};
34 // follow symlink only for root dir
35 auto tree
= ::fts_open(path
, FTS_COMFOLLOW
| FTS_PHYSICAL
| FTS_XDEV
, nullptr);
42 long long totalSize
= -1;
47 while ((node
= fts_read(tree
)) && !m_stopping
) {
48 auto info
= node
->fts_info
;
51 // ignore directories clausing cycles
54 if (info
== FTS_DNR
) {
55 // ignore directories that can’t be read
58 if (info
== FTS_ERR
) {
59 // ignore directories causing errors
60 fts_set(tree
, node
, FTS_SKIP
);
64 // ignore end traversal of dir
68 if (!countHiddenFiles
&& node
->fts_name
[0] == '.' && strncmp(".git", node
->fts_name
, 4) != 0) {
69 // skip hidden files, except .git dirs
71 fts_set(tree
, node
, FTS_SKIP
);
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
;
86 if (node
->fts_level
== 0) {
87 // first read was sucessful, we can init counters
92 if (node
->fts_level
> (int)allowedRecursiveLevel
) {
93 // skip too deep nodes
94 fts_set(tree
, node
, FTS_SKIP
);
98 // count first level elements
99 if (node
->fts_level
== 1) {
103 // delay intermediate results
104 if (timer
.hasExpired(200) || node
->fts_level
== 0) {
105 Q_EMIT
intermediateResult(dirPath
, totalCount
, totalSize
);
117 Q_EMIT
result(dirPath
, totalCount
, totalSize
);
122 void KDirectoryContentsCounterWorker::stop()
127 bool KDirectoryContentsCounterWorker::stopping() const
132 QString
KDirectoryContentsCounterWorker::scannedPath() const
134 return m_scannedPath
;
137 void KDirectoryContentsCounterWorker::countDirectoryContents(const QString
&path
, Options options
, int maxRecursiveLevel
)
139 const bool countHiddenFiles
= options
& CountHiddenFiles
;
143 QDir::Filters filters
= QDir::NoDotAndDotDot
| QDir::System
| QDir::AllEntries
;
144 if (countHiddenFiles
) {
145 filters
|= QDir::Hidden
;
148 Q_EMIT
result(path
, static_cast<int>(dir
.entryList(filters
).count()), 0);
151 m_scannedPath
= path
;
152 walkDir(path
, countHiddenFiles
, maxRecursiveLevel
);