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