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 subItemsCount():
11 #ifdef Q_OS_WIN
12 #include <QDir>
13 #else
14 #include <QFile>
15 #include <qplatformdefs.h>
16 #endif
17
18 #include "dolphin_detailsmodesettings.h"
19
KDirectoryContentsCounterWorker(QObject * parent)20 KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) :
21 QObject(parent)
22 {
23 qRegisterMetaType<KDirectoryContentsCounterWorker::Options>();
24 }
25
26 #ifndef Q_OS_WIN
walkDir(const QString & dirPath,const bool countHiddenFiles,const bool countDirectoriesOnly,QT_DIRENT * dirEntry,const uint allowedRecursiveLevel)27 KDirectoryContentsCounterWorker::CountResult walkDir(const QString &dirPath,
28 const bool countHiddenFiles,
29 const bool countDirectoriesOnly,
30 QT_DIRENT *dirEntry,
31 const uint allowedRecursiveLevel)
32 {
33 int count = -1;
34 long size = -1;
35 auto dir = QT_OPENDIR(QFile::encodeName(dirPath));
36 if (dir) {
37 count = 0;
38 size = 0;
39 QT_STATBUF buf;
40
41 while ((dirEntry = QT_READDIR(dir))) {
42 if (dirEntry->d_name[0] == '.') {
43 if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
44 // Skip "." or hidden files
45 continue;
46 }
47 if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
48 // Skip ".."
49 continue;
50 }
51 }
52
53 // If only directories are counted, consider an unknown file type and links also
54 // as directory instead of trying to do an expensive stat()
55 // (see bugs 292642 and 299997).
56 const bool countEntry = !countDirectoriesOnly ||
57 dirEntry->d_type == DT_DIR ||
58 dirEntry->d_type == DT_LNK ||
59 dirEntry->d_type == DT_UNKNOWN;
60 if (countEntry) {
61 ++count;
62 }
63
64 if (allowedRecursiveLevel > 0) {
65
66 bool linkFound = false;
67 QString nameBuf = QStringLiteral("%1/%2").arg(dirPath, dirEntry->d_name);
68
69 if (dirEntry->d_type == DT_REG || dirEntry->d_type == DT_LNK) {
70 if (QT_STAT(nameBuf.toLocal8Bit(), &buf) == 0) {
71 if (S_ISDIR(buf.st_mode)) {
72 // was a dir link, recurse
73 linkFound = true;
74 }
75 size += buf.st_size;
76 }
77 }
78 if (dirEntry->d_type == DT_DIR || linkFound) {
79 // recursion for dirs and dir links
80 size += walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, dirEntry, allowedRecursiveLevel - 1).size;
81 }
82 }
83 }
84 QT_CLOSEDIR(dir);
85 }
86 return KDirectoryContentsCounterWorker::CountResult{count, size};
87 }
88 #endif
89
subItemsCount(const QString & path,Options options)90 KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options)
91 {
92 const bool countHiddenFiles = options & CountHiddenFiles;
93 const bool countDirectoriesOnly = options & CountDirectoriesOnly;
94
95 #ifdef Q_OS_WIN
96 QDir dir(path);
97 QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System;
98 if (countHiddenFiles) {
99 filters |= QDir::Hidden;
100 }
101 if (countDirectoriesOnly) {
102 filters |= QDir::Dirs;
103 } else {
104 filters |= QDir::AllEntries;
105 }
106 return {dir.entryList(filters).count(), 0};
107 #else
108
109 const uint maxRecursiveLevel = DetailsModeSettings::directorySizeCount() ? 1 : DetailsModeSettings::recursiveDirectorySizeLimit();
110
111 QT_DIRENT *dirEntry = nullptr;
112
113 auto res = walkDir(path, countHiddenFiles, countDirectoriesOnly, dirEntry, maxRecursiveLevel);
114
115 return res;
116 #endif
117 }
118
countDirectoryContents(const QString & path,Options options)119 void KDirectoryContentsCounterWorker::countDirectoryContents(const QString& path, Options options)
120 {
121 auto res = subItemsCount(path, options);
122 Q_EMIT result(path, res.count, res.size);
123 }
124