File indexing completed on 2023-09-24 04:08:30
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 startNextJob(item.targetUrl()); 0111 return; // we'll come back later, when this one's finished 0112 } else { 0113 m_totalSize += item.size(); 0114 m_totalFiles++; 0115 // qDebug() << "file -> " << m_totalSize; 0116 } 0117 } else { 0118 m_totalFiles++; 0119 } 0120 } 0121 // qDebug() << "finished"; 0122 q->emitResult(); 0123 } 0124 0125 void DirectorySizeJobPrivate::startNextJob(const QUrl &url) 0126 { 0127 Q_Q(DirectorySizeJob); 0128 // qDebug() << url; 0129 KIO::ListJob *listJob = KIO::listRecursive(url, KIO::HideProgressInfo); 0130 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 69) 0131 // TODO KF6: remove legacy details code path 0132 listJob->addMetaData(QStringLiteral("details"), QStringLiteral("3")); 0133 #endif 0134 listJob->addMetaData(QStringLiteral("statDetails"), QString::number(KIO::StatBasic | KIO::StatResolveSymlink | KIO::StatInode)); 0135 q->connect(listJob, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &list) { 0136 slotEntries(job, list); 0137 }); 0138 q->addSubjob(listJob); 0139 } 0140 0141 void DirectorySizeJobPrivate::slotEntries(KIO::Job *, const KIO::UDSEntryList &list) 0142 { 0143 KIO::UDSEntryList::ConstIterator it = list.begin(); 0144 const KIO::UDSEntryList::ConstIterator end = list.end(); 0145 for (; it != end; ++it) { 0146 const KIO::UDSEntry &entry = *it; 0147 0148 const long device = entry.numberValue(KIO::UDSEntry::UDS_DEVICE_ID, 0); 0149 if (device && !entry.isLink()) { 0150 // Hard-link detection (#67939) 0151 const long inode = entry.numberValue(KIO::UDSEntry::UDS_INODE, 0); 0152 std::set<long> &visitedInodes = m_visitedInodes[device]; // find or insert 0153 const auto [it, isNewInode] = visitedInodes.insert(inode); 0154 if (!isNewInode) { 0155 continue; 0156 } 0157 } 0158 const KIO::filesize_t size = entry.numberValue(KIO::UDSEntry::UDS_SIZE, 0); 0159 const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); 0160 if (name == QLatin1Char('.')) { 0161 m_totalSize += size; 0162 // qDebug() << "'.': added" << size << "->" << m_totalSize; 0163 } else if (name != QLatin1String("..")) { 0164 if (!entry.isLink()) { 0165 m_totalSize += size; 0166 } 0167 if (!entry.isDir()) { 0168 m_totalFiles++; 0169 } else { 0170 m_totalSubdirs++; 0171 } 0172 // qDebug() << name << ":" << size << "->" << m_totalSize; 0173 } 0174 } 0175 } 0176 0177 void DirectorySizeJob::slotResult(KJob *job) 0178 { 0179 Q_D(DirectorySizeJob); 0180 // qDebug() << d->m_totalSize; 0181 removeSubjob(job); 0182 if (d->m_currentItem < d->m_lstItems.count()) { 0183 d->processNextItem(); 0184 } else { 0185 if (job->error()) { 0186 setError(job->error()); 0187 setErrorText(job->errorText()); 0188 } 0189 emitResult(); 0190 } 0191 } 0192 0193 // static 0194 DirectorySizeJob *KIO::directorySize(const QUrl &directory) 0195 { 0196 return DirectorySizeJobPrivate::newJob(directory); // useless - but consistent with other jobs 0197 } 0198 0199 // static 0200 DirectorySizeJob *KIO::directorySize(const KFileItemList &lstItems) 0201 { 0202 return DirectorySizeJobPrivate::newJob(lstItems); 0203 } 0204 0205 #include "moc_directorysizejob.cpp"