/************************************************************************** ** ** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "qtpatch.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include QHash QtPatch::readQmakeOutput(const QByteArray &data) { QHash qmakeValueHash; QTextStream stream(data, QIODevice::ReadOnly); while (!stream.atEnd()) { const QString line = stream.readLine(); const int index = line.indexOf(QLatin1Char(':')); if (index != -1) { QString value = line.mid(index+1); if (value != QLatin1String("**Unknown**") ) qmakeValueHash.insert(line.left(index), value.toUtf8()); } } return qmakeValueHash; } QHash QtPatch::qmakeValues(const QString &qmakePath, QByteArray *qmakeOutput) { QHash qmakeValueHash; // in some cases qmake is not runable, because another process is blocking it(filewatcher ...) int waitCount = 0; while (qmakeValueHash.isEmpty() && waitCount < 60) { QFileInfo qmake(qmakePath); if (!qmake.exists()) { qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath)); return qmakeValueHash; } if (!qmake.isExecutable()) { qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath)); return qmakeValueHash; } QStringList args; args << QLatin1String("-query"); QProcess process; process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly); if (process.waitForFinished(2000)) { if (process.exitStatus() == QProcess::CrashExit) { qDebug() << qmakePath << "was crashed"; return qmakeValueHash; } QByteArray output = process.readAllStandardOutput(); qmakeOutput->append(output); qmakeValueHash = readQmakeOutput(output); } if (qmakeValueHash.isEmpty()) { ++waitCount; static const int waitTimeInMilliSeconds = 500; QInstaller::uiDetachedWait(waitTimeInMilliSeconds); } if (process.state() > QProcess::NotRunning ) { qDebug() << "qmake process is still running, need to kill it."; process.kill(); } } if (qmakeValueHash.isEmpty()) qDebug() << "Can't get any query output from qmake."; return qmakeValueHash; } bool QtPatch::patchBinaryFile(const QString &fileName, const QByteArray &oldQtPath, const QByteArray &newQtPath) { QFile file(fileName); if (!file.exists()) { qDebug() << "qpatch: warning: file" << fileName << "not found"; return false; } openFileForPatching(&file); if (!file.isOpen()) { qDebug() << "qpatch: warning: file" << qPrintable(fileName) << "can not open."; qDebug() << qPrintable(file.errorString()); return false; } bool isPatched = patchBinaryFile(&file, oldQtPath, newQtPath); file.close(); return isPatched; } // device must be open bool QtPatch::patchBinaryFile(QIODevice *device, const QByteArray &oldQtPath, const QByteArray &newQtPath) { if (!(device->openMode() == QIODevice::ReadWrite)) { qDebug() << "qpatch: warning: This function needs an open device for writing."; return false; } const QByteArray source = device->readAll(); device->seek(0); int offset = 0; QByteArray overwritePath(newQtPath); if (overwritePath.size() < oldQtPath.size()) { QByteArray fillByteArray(oldQtPath.size() - overwritePath.size(), '\0'); overwritePath.append(fillByteArray); } QByteArrayMatcher byteArrayMatcher(oldQtPath); forever { offset = byteArrayMatcher.indexIn(source, offset); if (offset == -1) break; device->seek(offset); device->write(overwritePath); offset += overwritePath.size(); } device->seek(0); //for next reading we should be at the beginning return true; } bool QtPatch::patchTextFile(const QString &fileName, const QHash &searchReplacePairs) { QFile file(fileName); if (!file.open(QFile::ReadOnly)) { qDebug() << QString::fromLatin1("qpatch: warning: Open the file '%1' stopped: %2").arg( fileName, file.errorString()); return false; } QByteArray source = file.readAll(); file.close(); QHashIterator it(searchReplacePairs); while (it.hasNext()) { it.next(); source.replace(it.key(), it.value()); } if (!file.open(QFile::WriteOnly | QFile::Truncate)) { qDebug() << QString::fromLatin1("qpatch: error: file '%1' not writable").arg(fileName); return false; } file.write(source); return true; } bool QtPatch::openFileForPatching(QFile *file) { if (file->openMode() == QIODevice::NotOpen) { // in some cases the file can not open, because another process is blocking it(filewatcher ...) int waitCount = 0; while (!file->open(QFile::ReadWrite) && waitCount < 60) { ++waitCount; static const int waitTimeInMilliSeconds = 500; QInstaller::uiDetachedWait(waitTimeInMilliSeconds); } return file->openMode() == QFile::ReadWrite; } qDebug() << QString::fromLatin1("qpatch: error: File '%1 is open, so it can not open it again.").arg( file->fileName()); return false; }