Arttu Tarkiainen c7066c9dc2 Update usage of Apple-related terminology
Replace deprecated Q_OS_OSX macro used in several places with the newer
Q_OS_MACOS. Old style reference of Apple's desktop operating system name
as "OS X" in documentation and files are also replaced with "macOS"
where applicable.

Task-number: QTIFW-1406
Change-Id: I0561d5e0d964917ac4fca0702d58a06b85d30e57
Reviewed-by: Katja Marttila <katja.marttila@qt.io>
2019-08-16 08:35:19 +00:00

271 lines
10 KiB
C++

/**************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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.
**
** $QT_END_LICENSE$
**
**************************************************************************/
#include "binarydump.h"
#include "binaryreplace.h"
#include "operationrunner.h"
#include <binarycontent.h>
#include <binaryformatenginehandler.h>
#include <errors.h>
#include <fileio.h>
#include <fileutils.h>
#include <init.h>
#include <utils.h>
#include <QCoreApplication>
#include <QCommandLineParser>
#include <QFileInfo>
#include <QResource>
#include <iomanip>
#include <iostream>
struct Command
{
const char* command;
const char* description;
qint32 argC;
const char* arguments;
const char* argDescription;
} Commands[] = {
{ "dump", "Dumps the binary content that belongs to an installer or maintenance tool into "
"target directory.", 2, "<binary> <targetdirecory>", "The <binary> containing the data to "
"dump.\nThe <targetdirectory> to dump the data in."
},
{ "update", "Updates existing installer or maintenance tool with a new installer base.", 2,
"<binary> <installerbase>", "The <binary> to update.\nThe <installerbase> to use as update."
},
{ "operation", "Executes an operation with with a given mode and a list of arguments. ", 2,
"<binary> <mode,name,args,...>", "The <binary> to run the operation with.\n"
"<mode,name,args,...> 'mode' can be DO or UNDO. 'name' of the operation. 'args,...' "
"used to run the operation."
}
};
#define DESCRITION_LENGTH 60
#define SETW_ALIGNLEFT(X) std::setw(X) << std::setiosflags(std::ios::left)
static int fail(const QString &message)
{
std::cerr << qPrintable(message) << " See 'devtool --help'." << std::endl;
return EXIT_FAILURE;
}
static QStringList split(int index, const QString &description)
{
QStringList result;
if (description.length() <= DESCRITION_LENGTH)
return result << description;
const int lastIndexOf = description.left(index + DESCRITION_LENGTH)
.lastIndexOf(QLatin1Char(' '));
result << description.left(lastIndexOf);
return result + split(lastIndexOf + 1, description.mid(lastIndexOf + 1));
}
// -- main
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
app.setApplicationVersion(QLatin1String("1.0.0"));
QCommandLineParser parser;
QCommandLineOption help = parser.addHelpOption();
QCommandLineOption version = parser.addVersionOption();
QCommandLineOption verbose(QLatin1String("verbose"), QLatin1String("Verbose mode. Prints out "
"more information."));
parser.addOption(verbose);
parser.parse(app.arguments());
if (parser.isSet(version)) {
parser.showVersion();
return EXIT_SUCCESS;
}
if (parser.isSet(help)) {
const QString command = parser.positionalArguments().value(0);
if (!command.isEmpty()) {
for (const auto &c : Commands) {
if (QLatin1String(c.command) == command) {
parser.clearPositionalArguments();
parser.addPositionalArgument(QString::fromLatin1("%1 %2").arg(QLatin1String(c
.command), QLatin1String(c.arguments)), QLatin1String(c.argDescription));
parser.showHelp(EXIT_SUCCESS);
}
}
return fail(QString::fromLatin1("\"%1\" is not a devtool command.").arg(command));
}
QString helpText = parser.helpText();
helpText.insert(helpText.indexOf(QLatin1Char(']')) + 1, QLatin1String(" command <args>"));
std::cout << qPrintable(helpText) << std::endl;
std::cout << "Available commands (mutually exclusive):" << std::endl;
for (const auto &c : Commands) {
QStringList lines = split(0, QLatin1String(c.description));
std::cout << SETW_ALIGNLEFT(2) << " " << SETW_ALIGNLEFT(16) << c.command
<< SETW_ALIGNLEFT(DESCRITION_LENGTH) << qPrintable(lines.takeFirst()) << std::endl;
foreach (const QString &line, lines) {
std::cout << SETW_ALIGNLEFT(18) << QByteArray(18, ' ').constData()
<< qPrintable(line) << std::endl;
}
}
std::cout << std::endl << "Use 'devtool --help <command>' to read about a specific command."
<< std::endl;
return EXIT_SUCCESS;
}
QStringList arguments = parser.positionalArguments();
if (arguments.isEmpty())
return fail(QLatin1String("Missing command."));
bool found = false;
const QString command = arguments.takeFirst();
for (const auto &c : Commands) {
if ((found = (QLatin1String(c.command) == command))) {
if (arguments.count() != c.argC)
return fail(QString::fromLatin1("%1: wrong argument count.").arg(command));
break;
}
}
if (!found)
return fail(QString::fromLatin1("\"%1\" is not a devtool command.").arg(command));
QInstaller::init();
QInstaller::setVerbose(parser.isSet(verbose));
QString bundlePath;
QString path = QFileInfo(arguments.first()).absoluteFilePath();
if (QInstaller::isInBundle(path, &bundlePath)) {
path = QDir(bundlePath).filePath(QLatin1String("Contents/Resources/installer.dat"));
}
#ifndef Q_OS_MACOS
QFileInfo fi = QFileInfo(path);
bundlePath = path;
QString tmp = QDir(fi.path()).filePath(QLatin1String("installer.dat"));
if (QFileInfo::exists(tmp))
path = tmp;
#endif
int result = EXIT_FAILURE;
QVector<QByteArray> resourceMappings;
quint64 cookie = QInstaller::BinaryContent::MagicCookie;
try {
{
QFile tmp(path);
QInstaller::openForRead(&tmp);
if (!tmp.seek(QInstaller::BinaryContent::findMagicCookie(&tmp, cookie) - sizeof(qint64)))
throw QInstaller::Error(QLatin1String("Cannot seek to read magic marker."));
QInstaller::BinaryLayout layout;
layout.magicMarker = QInstaller::retrieveInt64(&tmp);
if (layout.magicMarker == QInstaller::BinaryContent::MagicUninstallerMarker) {
QFileInfo fi(path);
QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath);
fi.setFile(bundlePath);
path = fi.absolutePath() + QLatin1Char('/') + fi.baseName() + QLatin1String(".dat");
tmp.close();
tmp.setFileName(path);
QInstaller::openForRead(&tmp);
cookie = QInstaller::BinaryContent::MagicCookieDat;
}
layout = QInstaller::BinaryContent::binaryLayout(&tmp, cookie);
tmp.close();
if (command == QLatin1String("update")) {
BinaryReplace br(layout); // To update the binary we do not need any mapping.
return br.replace(arguments.last(), QFileInfo(arguments.first())
.absoluteFilePath());
}
}
QFile file(path);
QInstaller::openForRead(&file);
qint64 magicMarker;
QList<QInstaller::OperationBlob> operations;
QInstaller::ResourceCollectionManager manager;
QInstaller::BinaryContent::readBinaryContent(&file, &operations, &manager, &magicMarker,
cookie);
// map the inbuilt resources
const QInstaller::ResourceCollection meta = manager.collectionByName("QResources");
foreach (const QSharedPointer<QInstaller::Resource> &resource, meta.resources()) {
const bool isOpen = resource->isOpen();
if ((!isOpen) && (!resource->open()))
continue; // TODO: should we throw here?
const QByteArray ba = resource->readAll();
if (!QResource::registerResource((const uchar*) ba.data(), QLatin1String(":/metadata")))
throw QInstaller::Error(QLatin1String("Cannot register in-binary resource."));
resourceMappings.append(ba);
if (!isOpen)
resource->close();
}
if (command == QLatin1String("dump")) {
// To dump the content we do not need the binary format engine.
BinaryDump bd;
result = bd.dump(manager, arguments.last());
} else if (command == QLatin1String("operation")) {
QInstaller::BinaryFormatEngineHandler::instance()->registerResources(manager
.collections()); // setup the binary format engine
OperationRunner runner(magicMarker, operations);
const QStringList operationArguments = arguments.last().split(QLatin1Char(','));
if (operationArguments.first() == QLatin1String("DO"))
result = runner.runOperation(operationArguments.mid(1), OperationRunner::RunMode::Do);
else if (operationArguments.first() == QLatin1String("UNDO"))
result = runner.runOperation(operationArguments.mid(1), OperationRunner::RunMode::Undo);
else
std::cerr << "Malformed argument: " << qPrintable(operationArguments.last()) << std::endl;
}
} catch (const QInstaller::Error &error) {
std::cerr << qPrintable(error.message()) << std::endl;
} catch (...) {
std::cerr << "Unknown exception caught." << std::endl;
}
// unmap resources
foreach (const QByteArray &rccData, resourceMappings)
QResource::unregisterResource((const uchar*) rccData.data(), QLatin1String(":/metadata"));
return result;
}