File indexing completed on 2024-05-12 05:54:50
0001 /* 0002 SPDX-FileCopyrightText: 2006 Csaba Karai <krusader@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2006-2022 Krusader Krew <https://krusader.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "synchronizertask.h" 0009 0010 // QtCore 0011 #include <QElapsedTimer> 0012 #include <QFile> 0013 #include <QTimer> 0014 0015 #include <KI18n/KLocalizedString> 0016 #include <KWidgetsAddons/KMessageBox> 0017 0018 #include "../FileSystem/filesystem.h" 0019 #include "synchronizer.h" 0020 #include "synchronizerdirlist.h" 0021 #include "synchronizerfileitem.h" 0022 0023 CompareTask::CompareTask(SynchronizerFileItem *parentIn, 0024 const QString &leftURL, 0025 const QString &rightURL, 0026 const QString &leftDir, 0027 const QString &rightDir, 0028 bool hidden) 0029 : m_parent(parentIn) 0030 , m_url(leftURL) 0031 , m_dir(leftDir) 0032 , m_otherUrl(rightURL) 0033 , m_otherDir(rightDir) 0034 , m_duplicate(true) 0035 , m_dirList(nullptr) 0036 , m_otherDirList(nullptr) 0037 { 0038 ignoreHidden = hidden; 0039 } 0040 0041 CompareTask::CompareTask(SynchronizerFileItem *parentIn, const QString &urlIn, const QString &dirIn, bool isLeftIn, bool hidden) 0042 : m_parent(parentIn) 0043 , m_url(urlIn) 0044 , m_dir(dirIn) 0045 , m_isLeft(isLeftIn) 0046 , m_duplicate(false) 0047 , m_dirList(nullptr) 0048 , m_otherDirList(nullptr) 0049 { 0050 ignoreHidden = hidden; 0051 } 0052 0053 CompareTask::~CompareTask() 0054 { 0055 if (m_dirList) { 0056 delete m_dirList; 0057 m_dirList = nullptr; 0058 } 0059 if (m_otherDirList) { 0060 delete m_otherDirList; 0061 m_otherDirList = nullptr; 0062 } 0063 } 0064 0065 void CompareTask::start() 0066 { 0067 if (m_state == ST_STATE_NEW) { 0068 m_state = ST_STATE_PENDING; 0069 m_loadFinished = m_otherLoadFinished = false; 0070 0071 m_dirList = new SynchronizerDirList(parentWidget, ignoreHidden); 0072 connect(m_dirList, &SynchronizerDirList::finished, this, &CompareTask::slotFinished); 0073 m_dirList->load(m_url, false); 0074 0075 if (m_duplicate) { 0076 m_otherDirList = new SynchronizerDirList(parentWidget, ignoreHidden); 0077 connect(m_otherDirList, &SynchronizerDirList::finished, this, &CompareTask::slotOtherFinished); 0078 m_otherDirList->load(m_otherUrl, false); 0079 } 0080 } 0081 } 0082 0083 void CompareTask::slotFinished(bool result) 0084 { 0085 if (!result) { 0086 m_state = ST_STATE_ERROR; 0087 return; 0088 } 0089 m_loadFinished = true; 0090 0091 if (m_otherLoadFinished || !m_duplicate) 0092 m_state = ST_STATE_READY; 0093 } 0094 0095 void CompareTask::slotOtherFinished(bool result) 0096 { 0097 if (!result) { 0098 m_state = ST_STATE_ERROR; 0099 return; 0100 } 0101 m_otherLoadFinished = true; 0102 0103 if (m_loadFinished) 0104 m_state = ST_STATE_READY; 0105 } 0106 0107 CompareContentTask::CompareContentTask(Synchronizer *syn, SynchronizerFileItem *itemIn, const QUrl &leftURLIn, const QUrl &rightURLIn, KIO::filesize_t sizeIn) 0108 : leftURL(leftURLIn) 0109 , rightURL(rightURLIn) 0110 , size(sizeIn) 0111 , errorPrinted(false) 0112 , leftReadJob(nullptr) 0113 , rightReadJob(nullptr) 0114 , compareArray() 0115 , owner(-1) 0116 , item(itemIn) 0117 , timer(nullptr) 0118 , leftFile(nullptr) 0119 , rightFile(nullptr) 0120 , received(0) 0121 , sync(syn) 0122 { 0123 } 0124 0125 CompareContentTask::~CompareContentTask() 0126 { 0127 abortContentComparing(); 0128 0129 if (timer) 0130 delete timer; 0131 if (leftFile) 0132 delete leftFile; 0133 if (rightFile) 0134 delete rightFile; 0135 } 0136 0137 void CompareContentTask::start() 0138 { 0139 m_state = ST_STATE_PENDING; 0140 0141 if (leftURL.isLocalFile() && rightURL.isLocalFile()) { 0142 leftFile = new QFile(leftURL.path()); 0143 if (!leftFile->open(QIODevice::ReadOnly)) { 0144 KMessageBox::error(parentWidget, i18n("Error at opening %1.", leftURL.path())); 0145 m_state = ST_STATE_ERROR; 0146 return; 0147 } 0148 0149 rightFile = new QFile(rightURL.path()); 0150 if (!rightFile->open(QIODevice::ReadOnly)) { 0151 KMessageBox::error(parentWidget, i18n("Error at opening %1.", rightURL.path())); 0152 m_state = ST_STATE_ERROR; 0153 return; 0154 } 0155 0156 timer = new QTimer(this); 0157 connect(timer, &QTimer::timeout, this, &CompareContentTask::sendStatusMessage); 0158 timer->setSingleShot(true); 0159 timer->start(1000); 0160 0161 localFileCompareCycle(); 0162 } else { 0163 leftReadJob = KIO::get(leftURL, KIO::NoReload, KIO::HideProgressInfo); 0164 rightReadJob = KIO::get(rightURL, KIO::NoReload, KIO::HideProgressInfo); 0165 0166 connect(leftReadJob, &KIO::TransferJob::data, this, &CompareContentTask::slotDataReceived); 0167 connect(rightReadJob, &KIO::TransferJob::data, this, &CompareContentTask::slotDataReceived); 0168 connect(leftReadJob, &KIO::TransferJob::result, this, &CompareContentTask::slotFinished); 0169 connect(rightReadJob, &KIO::TransferJob::result, this, &CompareContentTask::slotFinished); 0170 0171 rightReadJob->suspend(); 0172 0173 timer = new QTimer(this); 0174 connect(timer, &QTimer::timeout, this, &CompareContentTask::sendStatusMessage); 0175 timer->setSingleShot(true); 0176 timer->start(1000); 0177 } 0178 } 0179 0180 void CompareContentTask::localFileCompareCycle() 0181 { 0182 bool different = false; 0183 0184 char leftBuffer[1440]; 0185 char rightBuffer[1440]; 0186 0187 QElapsedTimer timer; 0188 timer.start(); 0189 0190 int cnt = 0; 0191 0192 while (!leftFile->atEnd() && !rightFile->atEnd()) { 0193 int leftBytes = static_cast<int>(leftFile->read(leftBuffer, sizeof(leftBuffer))); 0194 int rightBytes = static_cast<int>(rightFile->read(rightBuffer, sizeof(rightBuffer))); 0195 0196 if (leftBytes != rightBytes) { 0197 different = true; 0198 break; 0199 } 0200 0201 if (leftBytes <= 0) 0202 break; 0203 0204 received += leftBytes; 0205 0206 if (memcmp(leftBuffer, rightBuffer, leftBytes)) { 0207 different = true; 0208 break; 0209 } 0210 0211 if ((++cnt % 16) == 0 && timer.elapsed() >= 250) 0212 break; 0213 } 0214 0215 if (different) { 0216 sync->compareContentResult(item, false); 0217 m_state = ST_STATE_READY; 0218 return; 0219 } 0220 0221 if (leftFile->atEnd() && rightFile->atEnd()) { 0222 sync->compareContentResult(item, true); 0223 m_state = ST_STATE_READY; 0224 return; 0225 } 0226 0227 QTimer::singleShot(0, this, &CompareContentTask::localFileCompareCycle); 0228 } 0229 0230 void CompareContentTask::slotDataReceived(KIO::Job *job, const QByteArray &data) 0231 { 0232 int jobowner = (job == leftReadJob) ? 1 : 0; 0233 int bufferLen = compareArray.size(); 0234 int dataLen = data.size(); 0235 0236 if (job == leftReadJob) 0237 received += dataLen; 0238 0239 if (jobowner == owner) { 0240 compareArray.append(data); 0241 return; 0242 } 0243 0244 do { 0245 if (bufferLen == 0) { 0246 compareArray = QByteArray(data.data(), dataLen); 0247 owner = jobowner; 0248 break; 0249 } 0250 0251 int minSize = (dataLen < bufferLen) ? dataLen : bufferLen; 0252 0253 for (int i = 0; i != minSize; i++) 0254 if (data[i] != compareArray[i]) { 0255 abortContentComparing(); 0256 return; 0257 } 0258 0259 if (minSize == bufferLen) { 0260 compareArray = QByteArray(data.data() + bufferLen, dataLen - bufferLen); 0261 if (dataLen == bufferLen) { 0262 owner = -1; 0263 return; 0264 } 0265 owner = jobowner; 0266 break; 0267 } else { 0268 compareArray = QByteArray(compareArray.data() + dataLen, bufferLen - dataLen); 0269 return; 0270 } 0271 0272 } while (false); 0273 0274 KIO::TransferJob *otherJob = (job == leftReadJob) ? rightReadJob : leftReadJob; 0275 0276 if (otherJob == nullptr) { 0277 if (compareArray.size()) 0278 abortContentComparing(); 0279 } else { 0280 if (!(qobject_cast<KIO::TransferJob *>(job))->isSuspended()) { 0281 (qobject_cast<KIO::TransferJob *>(job))->suspend(); 0282 otherJob->resume(); 0283 } 0284 } 0285 } 0286 0287 void CompareContentTask::slotFinished(KJob *job) 0288 { 0289 KIO::TransferJob *otherJob = (job == leftReadJob) ? rightReadJob : leftReadJob; 0290 0291 if (job == leftReadJob) 0292 leftReadJob = nullptr; 0293 else 0294 rightReadJob = nullptr; 0295 0296 if (otherJob) 0297 otherJob->resume(); 0298 0299 if (job->error()) { 0300 timer->stop(); 0301 abortContentComparing(); 0302 } 0303 0304 if (job->error() && job->error() != KIO::ERR_USER_CANCELED && !errorPrinted) { 0305 errorPrinted = true; 0306 KMessageBox::error(parentWidget, 0307 i18n("I/O error while comparing file %1 with %2.", 0308 leftURL.toDisplayString(QUrl::PreferLocalFile), 0309 rightURL.toDisplayString(QUrl::PreferLocalFile))); 0310 } 0311 0312 if (leftReadJob == nullptr && rightReadJob == nullptr) { 0313 if (!compareArray.size()) 0314 sync->compareContentResult(item, true); 0315 else 0316 sync->compareContentResult(item, false); 0317 0318 m_state = ST_STATE_READY; 0319 } 0320 } 0321 0322 void CompareContentTask::abortContentComparing() 0323 { 0324 if (timer) 0325 timer->stop(); 0326 0327 if (leftReadJob) 0328 leftReadJob->kill(KJob::EmitResult); 0329 if (rightReadJob) 0330 rightReadJob->kill(KJob::EmitResult); 0331 0332 if (item->task() >= TT_UNKNOWN) 0333 sync->compareContentResult(item, false); 0334 0335 m_state = ST_STATE_READY; 0336 } 0337 0338 void CompareContentTask::sendStatusMessage() 0339 { 0340 double perc = (size == 0) ? 1. : (double)received / (double)size; 0341 auto percent = (int)(perc * 10000. + 0.5); 0342 QString statstr = QString("%1.%2%3").arg(percent / 100).arg((percent / 10) % 10).arg(percent % 10) + '%'; 0343 setStatusMessage(i18n("Comparing file %1 (%2)...", leftURL.fileName(), statstr)); 0344 timer->setSingleShot(true); 0345 timer->start(500); 0346 }