File indexing completed on 2024-04-14 03:52:51
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000, 2006 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "directorysizejob.h" 0009 #include "global.h" 0010 #include "listjob.h" 0011 #include <QDebug> 0012 #include <QTimer> 0013 #include <kio/jobuidelegatefactory.h> 0014 0015 #include "job_p.h" 0016 0017 #include <set> 0018 0019 namespace KIO 0020 { 0021 class DirectorySizeJobPrivate : public KIO::JobPrivate 0022 { 0023 public: 0024 DirectorySizeJobPrivate() 0025 : m_totalSize(0L) 0026 , m_totalFiles(0L) 0027 , m_totalSubdirs(0L) 0028 , m_currentItem(0) 0029 { 0030 } 0031 explicit DirectorySizeJobPrivate(const KFileItemList &lstItems) 0032 : m_totalSize(0L) 0033 , m_totalFiles(0L) 0034 , m_totalSubdirs(0L) 0035 , m_lstItems(lstItems) 0036 , m_currentItem(0) 0037 { 0038 } 0039 KIO::filesize_t m_totalSize; 0040 KIO::filesize_t m_totalFiles; 0041 KIO::filesize_t m_totalSubdirs; 0042 KFileItemList m_lstItems; 0043 int m_currentItem; 0044 QHash<long, std::set<long>> m_visitedInodes; // device -> set of inodes 0045 0046 void startNextJob(const QUrl &url); 0047 void slotEntries(KIO::Job *, const KIO::UDSEntryList &); 0048 void processNextItem(); 0049 0050 Q_DECLARE_PUBLIC(DirectorySizeJob) 0051 0052 static inline DirectorySizeJob *newJob(const QUrl &directory) 0053 { 0054 DirectorySizeJobPrivate *d = new DirectorySizeJobPrivate; 0055 DirectorySizeJob *job = new DirectorySizeJob(*d); 0056 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0057 d->startNextJob(directory); 0058 return job; 0059 } 0060 0061 static inline DirectorySizeJob *newJob(const KFileItemList &lstItems) 0062 { 0063 DirectorySizeJobPrivate *d = new DirectorySizeJobPrivate(lstItems); 0064 DirectorySizeJob *job = new DirectorySizeJob(*d); 0065 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0066 QTimer::singleShot(0, job, [d]() { 0067 d->processNextItem(); 0068 }); 0069 return job; 0070 } 0071 }; 0072 0073 } // namespace KIO 0074 0075 using namespace KIO; 0076 0077 DirectorySizeJob::DirectorySizeJob(DirectorySizeJobPrivate &dd) 0078 : KIO::Job(dd) 0079 { 0080 } 0081 0082 DirectorySizeJob::~DirectorySizeJob() 0083 { 0084 } 0085 0086 KIO::filesize_t DirectorySizeJob::totalSize() const 0087 { 0088 return d_func()->m_totalSize; 0089 } 0090 0091 KIO::filesize_t DirectorySizeJob::totalFiles() const 0092 { 0093 return d_func()->m_totalFiles; 0094 } 0095 0096 KIO::filesize_t DirectorySizeJob::totalSubdirs() const 0097 { 0098 return d_func()->m_totalSubdirs; 0099 } 0100 0101 void DirectorySizeJobPrivate::processNextItem() 0102 { 0103 Q_Q(DirectorySizeJob); 0104 while (m_currentItem < m_lstItems.count()) { 0105 const KFileItem item = m_lstItems[m_currentItem++]; 0106 // qDebug() << item; 0107 if (!item.isLink()) { 0108 if (item.isDir()) { 0109 // qDebug() << "dir -> listing"; 0110 const auto localPath = item.localPath(); 0111 if (!localPath.isNull()) { 0112 startNextJob(QUrl::fromLocalFile(localPath)); 0113 } else { 0114 startNextJob(item.targetUrl()); 0115 } 0116 return; // we'll come back later, when this one's finished 0117 } else { 0118 m_totalSize += item.size(); 0119 m_totalFiles++; 0120 // qDebug() << "file -> " << m_totalSize; 0121 } 0122 } else { 0123 m_totalFiles++; 0124 } 0125 } 0126 // qDebug() << "finished"; 0127 q->emitResult(); 0128 } 0129 0130 void DirectorySizeJobPrivate::startNextJob(const QUrl &url) 0131 { 0132 Q_Q(DirectorySizeJob); 0133 // qDebug() << url; 0134 KIO::ListJob *listJob = KIO::listRecursive(url, KIO::HideProgressInfo); 0135 listJob->addMetaData(QStringLiteral("details"), QString::number(KIO::StatBasic | KIO::StatResolveSymlink | KIO::StatInode)); 0136 q->connect(listJob, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &list) { 0137 slotEntries(job, list); 0138 }); 0139 q->addSubjob(listJob); 0140 } 0141 0142 void DirectorySizeJobPrivate::slotEntries(KIO::Job *, const KIO::UDSEntryList &list) 0143 { 0144 KIO::UDSEntryList::ConstIterator it = list.begin(); 0145 const KIO::UDSEntryList::ConstIterator end = list.end(); 0146 for (; it != end; ++it) { 0147 const KIO::UDSEntry &entry = *it; 0148 0149 const long device = entry.numberValue(KIO::UDSEntry::UDS_DEVICE_ID, 0); 0150 if (device && !entry.isLink()) { 0151 // Hard-link detection (#67939) 0152 const long inode = entry.numberValue(KIO::UDSEntry::UDS_INODE, 0); 0153 std::set<long> &visitedInodes = m_visitedInodes[device]; // find or insert 0154 const auto [it, isNewInode] = visitedInodes.insert(inode); 0155 if (!isNewInode) { 0156 continue; 0157 } 0158 } 0159 const KIO::filesize_t size = entry.numberValue(KIO::UDSEntry::UDS_SIZE, 0); 0160 const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); 0161 if (name == QLatin1Char('.')) { 0162 m_totalSize += size; 0163 // qDebug() << "'.': added" << size << "->" << m_totalSize; 0164 } else if (name != QLatin1String("..")) { 0165 if (!entry.isLink()) { 0166 m_totalSize += size; 0167 } 0168 if (!entry.isDir()) { 0169 m_totalFiles++; 0170 } else { 0171 m_totalSubdirs++; 0172 } 0173 // qDebug() << name << ":" << size << "->" << m_totalSize; 0174 } 0175 } 0176 } 0177 0178 void DirectorySizeJob::slotResult(KJob *job) 0179 { 0180 Q_D(DirectorySizeJob); 0181 // qDebug() << d->m_totalSize; 0182 removeSubjob(job); 0183 if (d->m_currentItem < d->m_lstItems.count()) { 0184 d->processNextItem(); 0185 } else { 0186 if (job->error()) { 0187 setError(job->error()); 0188 setErrorText(job->errorText()); 0189 } 0190 emitResult(); 0191 } 0192 } 0193 0194 // static 0195 DirectorySizeJob *KIO::directorySize(const QUrl &directory) 0196 { 0197 return DirectorySizeJobPrivate::newJob(directory); // useless - but consistent with other jobs 0198 } 0199 0200 // static 0201 DirectorySizeJob *KIO::directorySize(const KFileItemList &lstItems) 0202 { 0203 return DirectorySizeJobPrivate::newJob(lstItems); 0204 } 0205 0206 #include "moc_directorysizejob.cpp"