aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/utils/archive.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/utils/archive.cpp')
-rw-r--r--src/libs/utils/archive.cpp263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/libs/utils/archive.cpp b/src/libs/utils/archive.cpp
new file mode 100644
index 0000000000..19618ed8bc
--- /dev/null
+++ b/src/libs/utils/archive.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "archive.h"
+
+#include "algorithm.h"
+#include "checkablemessagebox.h"
+#include "environment.h"
+#include "mimetypes/mimedatabase.h"
+#include "qtcassert.h"
+#include "synchronousprocess.h"
+
+#include <QDir>
+#include <QPushButton>
+#include <QSettings>
+#include <QTimer>
+
+namespace {
+
+struct Tool
+{
+ QString executable;
+ QStringList arguments;
+ QStringList supportedMimeTypes;
+ QStringList additionalSearchDirs;
+};
+
+static QStringList additionalInstallDirs(const QString &registryKey, const QString &valueName)
+{
+#if defined(Q_OS_WIN)
+ const QSettings settings64(registryKey, QSettings::Registry64Format);
+ const QSettings settings32(registryKey, QSettings::Registry32Format);
+ return {settings64.value(valueName).toString(), settings32.value(valueName).toString()};
+#else
+ Q_UNUSED(registryKey)
+ Q_UNUSED(valueName)
+ return {};
+#endif
+}
+
+static const QVector<Tool> &sTools()
+{
+ static QVector<Tool> tools;
+ if (tools.isEmpty()) {
+ tools << Tool{{"unzip"}, {"-o", "%{src}", "-d", "%{dest}"}, {"application/zip"}, {}};
+ tools << Tool{{"7z"},
+ {"x", "-o%{dest}", "-y", "-bb", "%{src}"},
+ {"application/zip", "application/x-7z-compressed"},
+ additionalInstallDirs("HKEY_CURRENT_USER\\Software\\7-Zip", "Path")};
+ tools << Tool{{"tar"},
+ {"xvf", "%{src}"},
+ {"application/zip", "application/x-tar", "application/x-7z-compressed"},
+ {}};
+ tools << Tool{{"tar"}, {"xvzf", "%{src}"}, {"application/x-compressed-tar"}, {}};
+ tools << Tool{{"tar"}, {"xvJf", "%{src}"}, {"application/x-xz-compressed-tar"}, {}};
+ tools << Tool{{"tar"}, {"xvjf", "%{src}"}, {"application/x-bzip-compressed-tar"}, {}};
+ const QStringList additionalCMakeDirs = additionalInstallDirs(
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\Kitware\\CMake", "InstallDir");
+ tools << Tool{{"cmake"},
+ {"-E", "tar", "xvf", "%{src}"},
+ {"application/zip", "application/x-tar", "application/x-7z-compressed"},
+ additionalCMakeDirs};
+ tools << Tool{{"cmake"},
+ {"-E", "tar", "xvzf", "%{src}"},
+ {"application/x-compressed-tar"},
+ additionalCMakeDirs};
+ tools << Tool{{"cmake"},
+ {"-E", "tar", "xvJf", "%{src}"},
+ {"application/x-xz-compressed-tar"},
+ additionalCMakeDirs};
+ tools << Tool{{"cmake"},
+ {"-E", "tar", "xvjf", "%{src}"},
+ {"application/x-bzip-compressed-tar"},
+ additionalCMakeDirs};
+ }
+ return tools;
+}
+
+static QVector<Tool> toolsForMimeType(const Utils::MimeType &mimeType)
+{
+ return Utils::filtered(sTools(), [mimeType](const Tool &tool) {
+ return Utils::anyOf(tool.supportedMimeTypes,
+ [mimeType](const QString &mt) { return mimeType.inherits(mt); });
+ });
+}
+
+static QVector<Tool> toolsForFilePath(const Utils::FilePath &fp)
+{
+ return toolsForMimeType(Utils::mimeTypeForFile(fp.toString()));
+}
+
+static Utils::optional<Tool> resolveTool(const Tool &tool)
+{
+ const QString executable
+ = Utils::Environment::systemEnvironment()
+ .searchInPath(Utils::HostOsInfo::withExecutableSuffix(tool.executable),
+ Utils::transform(tool.additionalSearchDirs, &Utils::FilePath::fromString))
+ .toString();
+ Tool resolvedTool = tool;
+ resolvedTool.executable = executable;
+ return executable.isEmpty() ? Utils::nullopt : Utils::make_optional(resolvedTool);
+}
+
+Utils::optional<Tool> unzipTool(const Utils::FilePath &src, const Utils::FilePath &dest)
+{
+ const QVector<Tool> tools = toolsForFilePath(src);
+ for (const Tool &tool : tools) {
+ const Utils::optional<Tool> resolvedTool = resolveTool(tool);
+ if (resolvedTool) {
+ Tool result = *resolvedTool;
+ const QString srcStr = src.toString();
+ const QString destStr = dest.toString();
+ result.arguments
+ = Utils::transform(result.arguments, [srcStr, destStr](const QString &a) {
+ QString val = a;
+ return val.replace("%{src}", srcStr).replace("%{dest}", destStr);
+ });
+ return result;
+ }
+ }
+ return {};
+}
+
+} // namespace
+
+namespace Utils {
+
+bool Archive::supportsFile(const FilePath &filePath, QString *reason)
+{
+ const QVector<Tool> tools = toolsForFilePath(filePath);
+ if (tools.isEmpty()) {
+ if (reason)
+ *reason = tr("File format not supported.");
+ return false;
+ }
+ if (!anyOf(tools, [](const Tool &t) { return resolveTool(t); })) {
+ if (reason)
+ *reason = tr("Could not find any unarchiving executable in PATH (%1).")
+ .arg(transform<QStringList>(tools, &Tool::executable).join(", "));
+ return false;
+ }
+ return true;
+}
+
+bool Archive::unarchive(const FilePath &src, const FilePath &dest, QWidget *parent)
+{
+ Archive *archive = unarchive(src, dest);
+ QTC_ASSERT(archive, return false);
+
+ CheckableMessageBox box(parent);
+ box.setIcon(QMessageBox::Information);
+ box.setWindowTitle(tr("Unarchiving File"));
+ box.setText(tr("Unzipping \"%1\" to \"%2\".").arg(src.toUserOutput(), dest.toUserOutput()));
+ box.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ box.button(QDialogButtonBox::Ok)->setEnabled(false);
+ box.setCheckBoxVisible(false);
+ QObject::connect(archive, &Archive::outputReceived, &box, [&box](const QString &output) {
+ box.setDetailedText(box.detailedText() + output);
+ });
+ bool success = false;
+ QObject::connect(archive, &Archive::finished, [&box, &success](bool ret) {
+ box.button(QDialogButtonBox::Ok)->setEnabled(true);
+ box.button(QDialogButtonBox::Cancel)->setEnabled(false);
+ success = ret;
+ });
+ QObject::connect(&box, &QMessageBox::rejected, archive, &Archive::cancel);
+ box.exec();
+ return success;
+}
+
+Archive *Archive::unarchive(const FilePath &src, const FilePath &dest)
+{
+ const Utils::optional<Tool> tool = unzipTool(src, dest);
+ QTC_ASSERT(tool, return nullptr);
+
+ auto archive = new Archive;
+
+ const QString workingDirectory = dest.toFileInfo().absoluteFilePath();
+ QDir(workingDirectory).mkpath(".");
+
+ archive->m_process = new QProcess;
+ archive->m_process->setProcessChannelMode(QProcess::MergedChannels);
+ QObject::connect(
+ archive->m_process,
+ &QProcess::readyReadStandardOutput,
+ archive,
+ [archive]() {
+ if (!archive->m_process)
+ return;
+ archive->outputReceived(QString::fromUtf8(archive->m_process->readAllStandardOutput()));
+ },
+ Qt::QueuedConnection);
+ QObject::connect(
+ archive->m_process,
+ QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
+ archive,
+ [archive](int, QProcess::ExitStatus) {
+ if (!archive->m_process)
+ return;
+ archive->finished(archive->m_process->exitStatus() == QProcess::NormalExit
+ && archive->m_process->exitCode() == 0);
+ archive->m_process->deleteLater();
+ archive->m_process = nullptr;
+ archive->deleteLater();
+ },
+ Qt::QueuedConnection);
+ QObject::connect(
+ archive->m_process,
+ &QProcess::errorOccurred,
+ archive,
+ [archive](QProcess::ProcessError) {
+ if (!archive->m_process)
+ return;
+ archive->outputReceived(tr("Command failed."));
+ archive->finished(false);
+ archive->m_process->deleteLater();
+ archive->m_process = nullptr;
+ archive->deleteLater();
+ },
+ Qt::QueuedConnection);
+ QTimer::singleShot(0, archive, [archive, tool, workingDirectory] {
+ archive->outputReceived(
+ tr("Running %1\nin \"%2\".\n\n", "Running <cmd> in <workingdirectory>")
+ .arg(CommandLine(tool->executable, tool->arguments).toUserOutput(),
+ workingDirectory));
+ });
+ archive->m_process->setProgram(tool->executable);
+ archive->m_process->setArguments(tool->arguments);
+ archive->m_process->setWorkingDirectory(workingDirectory);
+ archive->m_process->start(QProcess::ReadOnly);
+ return archive;
+}
+
+void Archive::cancel()
+{
+ if (!m_process)
+ return;
+ SynchronousProcess::stopProcess(*m_process);
+}
+
+} // namespace Utils