diff --git a/src/libs/core/extractor.cpp b/src/libs/core/extractor.cpp index 5e3440d..76f2889 100644 --- a/src/libs/core/extractor.cpp +++ b/src/libs/core/extractor.cpp @@ -27,6 +27,8 @@ #include #include +#include + using namespace Zeal::Core; Extractor::Extractor(QObject *parent) : @@ -34,31 +36,28 @@ Extractor::Extractor(QObject *parent) : { } -void Extractor::extract(const QString &filePath, const QString &destination, const QString &root) +void Extractor::extract(const QString &sourceFile, const QString &destination, const QString &root) { ExtractInfo info = { - this, // extractor archive_read_new(), // archiveHandle - filePath, // filePath - QFileInfo(filePath).size(), // totalBytes + sourceFile, // filePath + QFileInfo(sourceFile).size(), // totalBytes 0 // extractedBytes }; archive_read_support_filter_all(info.archiveHandle); archive_read_support_format_all(info.archiveHandle); - int r = archive_read_open_filename(info.archiveHandle, qPrintable(filePath), 10240); + int r = archive_read_open_filename(info.archiveHandle, qPrintable(sourceFile), 10240); if (r) { - emit error(filePath, QString::fromLocal8Bit(archive_error_string(info.archiveHandle))); + emit error(sourceFile, QString::fromLocal8Bit(archive_error_string(info.archiveHandle))); return; } - archive_read_extract_set_progress_callback(info.archiveHandle, &Extractor::progressCallback, - &info); - QDir destinationDir(destination); - if (!root.isEmpty()) + if (!root.isEmpty()) { destinationDir = destinationDir.filePath(root); + } // TODO: Do not strip root directory in archive if it equals to 'root' archive_entry *entry; @@ -69,25 +68,62 @@ void Extractor::extract(const QString &filePath, const QString &destination, con // TODO: Remove once https://github.com/libarchive/libarchive/issues/587 is resolved. QString pathname = QString::fromWCharArray(archive_entry_pathname_w(entry)); #endif - if (!root.isEmpty()) + + if (!root.isEmpty()) { pathname.remove(0, pathname.indexOf(QLatin1String("/")) + 1); - archive_entry_set_pathname(entry, qPrintable(destinationDir.filePath(pathname))); - archive_read_extract(info.archiveHandle, entry, 0); + } + + const QString filePath = destinationDir.absoluteFilePath(pathname); + + const auto filetype = archive_entry_filetype(entry); + if (filetype == S_IFDIR) { + QDir().mkpath(QFileInfo(filePath).absolutePath()); + continue; + } else if (filetype != S_IFREG) { + qWarning("Unsupported filetype %d for %s!", filetype, qPrintable(pathname)); + continue; + } + + QScopedPointer file(new QFile(filePath)); + if (!file->open(QIODevice::WriteOnly)) { + qWarning("Cannot open file for writing: %s", qPrintable(pathname)); + continue; + } + + const void *buffer; + size_t size; + std::int64_t offset; + for (;;) { + int rc = archive_read_data_block(info.archiveHandle, &buffer, &size, &offset); + if (rc != ARCHIVE_OK) { + if (rc == ARCHIVE_EOF) { + break; + } + + qWarning("Cannot read from archive: %s", archive_error_string(info.archiveHandle)); + emit error(sourceFile, + QString::fromLocal8Bit(archive_error_string(info.archiveHandle))); + return; + } + + file->write(static_cast(buffer), size); + } + + emitProgress(info); } - emit completed(filePath); + emit completed(sourceFile); archive_read_free(info.archiveHandle); } -void Extractor::progressCallback(void *ptr) +void Extractor::emitProgress(ExtractInfo &info) { - ExtractInfo *info = static_cast(ptr); - - const qint64 extractedBytes = archive_filter_bytes(info->archiveHandle, -1); - if (extractedBytes == info->extractedBytes) + const qint64 extractedBytes = archive_filter_bytes(info.archiveHandle, -1); + if (extractedBytes == info.extractedBytes) { return; + } - info->extractedBytes = extractedBytes; + info.extractedBytes = extractedBytes; - emit info->extractor->progress(info->filePath, extractedBytes, info->totalBytes); + emit progress(info.filePath, extractedBytes, info.totalBytes); } diff --git a/src/libs/core/extractor.h b/src/libs/core/extractor.h index 13b9bef..aee9b67 100644 --- a/src/libs/core/extractor.h +++ b/src/libs/core/extractor.h @@ -37,7 +37,9 @@ public: explicit Extractor(QObject *parent = nullptr); public slots: - void extract(const QString &filePath, const QString &destination, const QString &root = QString()); + void extract(const QString &sourceFile, + const QString &destination, + const QString &root = QString()); signals: void error(const QString &filePath, const QString &message); @@ -46,14 +48,13 @@ signals: private: struct ExtractInfo { - Extractor *extractor; archive *archiveHandle; QString filePath; qint64 totalBytes; qint64 extractedBytes; }; - static void progressCallback(void *ptr); + void emitProgress(ExtractInfo &info); }; } // namespace Core