diff --git a/3rdparty/cli7zplugin/cli7zplugin.cpp b/3rdparty/cli7zplugin/cli7zplugin.cpp index e9cc4a9e2..2c6312d97 100644 --- a/3rdparty/cli7zplugin/cli7zplugin.cpp +++ b/3rdparty/cli7zplugin/cli7zplugin.cpp @@ -121,7 +121,8 @@ bool Cli7zPlugin::isCorruptArchiveMsg(const QString &line) bool Cli7zPlugin::isDiskFullMsg(const QString &line) { - return line.contains(QLatin1String("No space left on device")); + // 7-Zip prints strerror() in the user's language ("errno=28 : 设备上没有空间"); 28 is ENOSPC + return line.contains(QLatin1String("No space left on device")) || line.contains(QLatin1String("errno=28 ")); } bool Cli7zPlugin::isFileExistsMsg(const QString &line) @@ -365,7 +366,8 @@ bool Cli7zPlugin::handleLine(const QString &line, WorkType workStatus) } if (isDiskFullMsg(line)) { -// m_eErrorType = ; + m_eErrorType = ET_InsufficientDiskSpace; + m_finishType = PFT_Error; return false; } diff --git a/3rdparty/clirarplugin/clirarplugin.cpp b/3rdparty/clirarplugin/clirarplugin.cpp index 61f4ccec7..ba175a1da 100644 --- a/3rdparty/clirarplugin/clirarplugin.cpp +++ b/3rdparty/clirarplugin/clirarplugin.cpp @@ -157,6 +157,19 @@ bool CliRarPlugin::isOpenFileFailed(const QString &line) return line.startsWith("Cannot create "); } +ErrorType CliRarPlugin::errorTypeForExitCode(int exitCode) const +{ + switch (exitCode) { + case 5: // write error + case 9: // cannot create a file + return ET_FileWriteError; + case 11: // wrong password + return ET_WrongPassword; + default: // 3 checksum error, 2 fatal error, ... + return ET_ArchiveDamaged; + } +} + void CliRarPlugin::killProcess(bool emitFinished) { Q_UNUSED(emitFinished); diff --git a/3rdparty/clirarplugin/clirarplugin.h b/3rdparty/clirarplugin/clirarplugin.h index 5f98b540f..2c83d5108 100644 --- a/3rdparty/clirarplugin/clirarplugin.h +++ b/3rdparty/clirarplugin/clirarplugin.h @@ -69,6 +69,11 @@ class CliRarPlugin : public CliInterface void killProcess(bool emitFinished) override; + /** + * @brief errorTypeForExitCode unrar: 5 write error, 9 cannot create a file, 11 wrong password + */ + ErrorType errorTypeForExitCode(int exitCode) const override; + private: ParseState m_parseState = ParseStateTitle; FileEntry m_fileEntry; diff --git a/3rdparty/interface/archiveinterface/archiveinterface.cpp b/3rdparty/interface/archiveinterface/archiveinterface.cpp index e953a4bbe..0f856ed28 100644 --- a/3rdparty/interface/archiveinterface/archiveinterface.cpp +++ b/3rdparty/interface/archiveinterface/archiveinterface.cpp @@ -76,6 +76,11 @@ ErrorType ReadOnlyArchiveInterface::errorType() return m_eErrorType; } +void ReadOnlyArchiveInterface::resetResult() +{ + m_eErrorType = ET_NoError; +} + bool ReadOnlyArchiveInterface::doKill() { return false; // 修改默认为未取消 diff --git a/3rdparty/interface/archiveinterface/archiveinterface.h b/3rdparty/interface/archiveinterface/archiveinterface.h index 6721fc703..8445eab61 100644 --- a/3rdparty/interface/archiveinterface/archiveinterface.h +++ b/3rdparty/interface/archiveinterface/archiveinterface.h @@ -116,6 +116,12 @@ class ReadOnlyArchiveInterface : public QObject */ ErrorType errorType(); + /** + * @brief resetResult forget the result of the previous operation; the interface is reused for + * later operations on the same archive, so a job starts with this + */ + virtual void resetResult(); + /** * @brief pauseOperation 暂停操作 */ diff --git a/3rdparty/interface/archiveinterface/cliinterface.cpp b/3rdparty/interface/archiveinterface/cliinterface.cpp index 0db6cc57f..7ea39e4c1 100644 --- a/3rdparty/interface/archiveinterface/cliinterface.cpp +++ b/3rdparty/interface/archiveinterface/cliinterface.cpp @@ -66,6 +66,12 @@ CliInterface::~CliInterface() deleteProcess(); } +void CliInterface::resetResult() +{ + ReadOnlyArchiveInterface::resetResult(); + m_finishType = PFT_Nomral; +} + PluginFinishType CliInterface::list() { setPassword(QString()); @@ -1488,6 +1494,12 @@ void CliInterface::processFinished(int exitCode, QProcess::ExitStatus exitStatus emit signalFinished(m_finishType); } +ErrorType CliInterface::errorTypeForExitCode(int exitCode) const +{ + Q_UNUSED(exitCode); + return ET_ArchiveDamaged; +} + void CliInterface::extractProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { qCInfo(ifaceLog) << "Extraction process finished, exitcode:" << exitCode << " exitstatus:" << exitStatus; @@ -1499,6 +1511,16 @@ void CliInterface::extractProcessFinished(int exitCode, QProcess::ExitStatus exi m_finishType = PFT_Nomral; } + // A failure the output parsing did not recognise is still a failure: unrar exits 3 after a + // checksum error and 5 when it cannot write, 7z exits 2 after any fatal error. Exit code 1 is + // "warnings" for both (the files were extracted), so it stays a success. + if (QProcess::NormalExit == exitStatus && exitCode >= 2 && PFT_Nomral == m_finishType) { + m_finishType = PFT_Error; + if (ET_NoError == m_eErrorType) { + m_eErrorType = errorTypeForExitCode(exitCode); + } + } + m_indexOfListRootEntry = 0; m_isEmptyArchive = false; diff --git a/3rdparty/interface/archiveinterface/cliinterface.h b/3rdparty/interface/archiveinterface/cliinterface.h index 64913354f..b2d4546b3 100644 --- a/3rdparty/interface/archiveinterface/cliinterface.h +++ b/3rdparty/interface/archiveinterface/cliinterface.h @@ -66,6 +66,12 @@ class CliInterface : public ReadWriteArchiveInterface explicit CliInterface(QObject *parent, const QVariantList &args); ~CliInterface() override; + /** + * @brief resetResult also forget the previous finish type: an operation that succeeds after a + * failed one on the same archive must not end as an error + */ + void resetResult() override; + // ReadOnlyArchiveInterface interface public: PluginFinishType list() override; @@ -143,6 +149,12 @@ class CliInterface : public ReadWriteArchiveInterface */ virtual void killProcess(bool emitFinished = true) = 0; + /** + * @brief errorTypeForExitCode the error of an extraction that exited with this code (2 or more) + * although its output showed no error the plugin recognises + */ + virtual ErrorType errorTypeForExitCode(int exitCode) const; + /** * @brief handleProgress 解析进度并发送进度信号 * @param line diff --git a/src/source/archivemanager/singlejob.cpp b/src/source/archivemanager/singlejob.cpp index 9cb2a0e6b..0b92ffef8 100644 --- a/src/source/archivemanager/singlejob.cpp +++ b/src/source/archivemanager/singlejob.cpp @@ -56,6 +56,10 @@ void SingleJob::start() return; } + // The interface is reused for later operations on the same archive: start without the result of + // the previous one (errors found during this job, e.g. while listing first, are kept) + m_pInterface->resetResult(); + // 判断是否通过线程的方式调用 if (m_pInterface->waitForFinished()) { qCDebug(appLog) << "Executing work directly"; @@ -532,6 +536,10 @@ void UpdateJob::start() return; } + // The interface is reused for later operations on the same archive: start without the result of + // the previous one (errors found during this job, e.g. while listing first, are kept) + m_pInterface->resetResult(); + d->start(); // 开启线程,执行操作 }