File indexing completed on 2024-05-12 05:47:29
0001 /* 0002 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com> 0003 * SPDX-FileCopyrightText: 2013 Frank Reininghaus <frank78ac@googlemail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kdirectorycontentscounterworker.h" 0009 0010 // Required includes for countDirectoryContents(): 0011 #ifdef Q_OS_WIN 0012 #include <QDir> 0013 #else 0014 #include <QElapsedTimer> 0015 #include <fts.h> 0016 #include <sys/stat.h> 0017 #include <sys/types.h> 0018 #endif 0019 0020 KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent) 0021 : QObject(parent) 0022 { 0023 qRegisterMetaType<KDirectoryContentsCounterWorker::Options>(); 0024 } 0025 0026 #ifndef Q_OS_WIN 0027 void KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, bool countHiddenFiles, uint allowedRecursiveLevel) 0028 { 0029 QByteArray text = dirPath.toLocal8Bit(); 0030 char *rootPath = new char[text.size() + 1]; 0031 ::strncpy(rootPath, text.constData(), text.size() + 1); 0032 char *path[2]{rootPath, nullptr}; 0033 0034 // follow symlink only for root dir 0035 auto tree = ::fts_open(path, FTS_COMFOLLOW | FTS_PHYSICAL | FTS_XDEV, nullptr); 0036 if (!tree) { 0037 delete[] rootPath; 0038 return; 0039 } 0040 0041 FTSENT *node; 0042 long long totalSize = -1; 0043 int totalCount = -1; 0044 QElapsedTimer timer; 0045 timer.start(); 0046 0047 while ((node = fts_read(tree)) && !m_stopping) { 0048 auto info = node->fts_info; 0049 0050 if (info == FTS_DC) { 0051 // ignore directories clausing cycles 0052 continue; 0053 } 0054 if (info == FTS_DNR) { 0055 // ignore directories that can’t be read 0056 continue; 0057 } 0058 if (info == FTS_ERR) { 0059 // ignore directories causing errors 0060 fts_set(tree, node, FTS_SKIP); 0061 continue; 0062 } 0063 if (info == FTS_DP) { 0064 // ignore end traversal of dir 0065 continue; 0066 } 0067 0068 if (!countHiddenFiles && node->fts_name[0] == '.' && strncmp(".git", node->fts_name, 4) != 0) { 0069 // skip hidden files, except .git dirs 0070 if (info == FTS_D) { 0071 fts_set(tree, node, FTS_SKIP); 0072 } 0073 continue; 0074 } 0075 0076 if (info == FTS_F) { 0077 // only count files that are physical (aka skip /proc/kcore...) 0078 // naive size counting not taking into account effective disk space used (aka size/block_size * block_size) 0079 // skip directory size (usually a 4KB block) 0080 if (node->fts_statp->st_blocks > 0) { 0081 totalSize += node->fts_statp->st_size; 0082 } 0083 } 0084 0085 if (info == FTS_D) { 0086 if (node->fts_level == 0) { 0087 // first read was sucessful, we can init counters 0088 totalSize = 0; 0089 totalCount = 0; 0090 } 0091 0092 if (node->fts_level > (int)allowedRecursiveLevel) { 0093 // skip too deep nodes 0094 fts_set(tree, node, FTS_SKIP); 0095 continue; 0096 } 0097 } 0098 // count first level elements 0099 if (node->fts_level == 1) { 0100 ++totalCount; 0101 } 0102 0103 // delay intermediate results 0104 if (timer.hasExpired(200) || node->fts_level == 0) { 0105 Q_EMIT intermediateResult(dirPath, totalCount, totalSize); 0106 timer.restart(); 0107 } 0108 } 0109 0110 delete[] rootPath; 0111 fts_close(tree); 0112 if (errno != 0) { 0113 return; 0114 } 0115 0116 if (!m_stopping) { 0117 Q_EMIT result(dirPath, totalCount, totalSize); 0118 } 0119 } 0120 #endif 0121 0122 void KDirectoryContentsCounterWorker::stop() 0123 { 0124 m_stopping = true; 0125 } 0126 0127 bool KDirectoryContentsCounterWorker::stopping() const 0128 { 0129 return m_stopping; 0130 } 0131 0132 QString KDirectoryContentsCounterWorker::scannedPath() const 0133 { 0134 return m_scannedPath; 0135 } 0136 0137 void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options, int maxRecursiveLevel) 0138 { 0139 const bool countHiddenFiles = options & CountHiddenFiles; 0140 0141 #ifdef Q_OS_WIN 0142 QDir dir(path); 0143 QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System | QDir::AllEntries; 0144 if (countHiddenFiles) { 0145 filters |= QDir::Hidden; 0146 } 0147 0148 Q_EMIT result(path, static_cast<int>(dir.entryList(filters).count()), 0); 0149 #else 0150 0151 m_scannedPath = path; 0152 walkDir(path, countHiddenFiles, maxRecursiveLevel); 0153 0154 #endif 0155 0156 m_stopping = false; 0157 Q_EMIT finished(); 0158 } 0159 0160 #include "moc_kdirectorycontentscounterworker.cpp"