Android: de-duplicate shellquote helpers code in deploy and test tools

Move shellquote helper functions into a common place instead of having a
copy in each tool's code.

Change-Id: I9723c11f894a211864788a7635773610c0fde739
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Assam Boudjelthia 2022-04-15 16:00:57 +03:00
parent 905bfb8503
commit a9f18a6ec0
4 changed files with 112 additions and 144 deletions

View File

@ -47,6 +47,7 @@
#include <QMap>
#include <depfile_shared.h>
#include <shellquote_shared.h>
#include <algorithm>
@ -249,77 +250,6 @@ static const QHash<QByteArray, QByteArray> elfArchitectures = {
{"x86_64", "x86_64"}
};
// Copy-pasted from qmake/library/ioutil.cpp
inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
{
for (int x = arg.length() - 1; x >= 0; --x) {
ushort c = arg.unicode()[x].unicode();
if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
return true;
}
return false;
}
static QString shellQuoteUnix(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
}; // 0-32 \'"$`<>|;&(){}*?#!~[]
if (!arg.length())
return "\"\""_L1;
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
ret.replace(u'\'', "'\\''"_L1);
ret.prepend(u'\'');
ret.append(u'\'');
}
return ret;
}
static QString shellQuoteWin(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
// - control chars & space
// - the shell meta chars "&()<>^|
// - the potential separators ,;=
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
};
if (!arg.length())
return "\"\""_L1;
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
// Quotes are escaped and their preceding backslashes are doubled.
// It's impossible to escape anything inside a quoted string on cmd
// level, so the outer quoting must be "suspended".
ret.replace(QRegularExpression("(\\\\*)\""_L1), "\"\\1\\1\\^\"\""_L1);
// The argument must not end with a \ since this would be interpreted
// as escaping the quote -- rather put the \ behind the quote: e.g.
// rather use "foo"\ than "foo\"
qsizetype i = ret.length();
while (i > 0 && ret.at(i - 1) == u'\\')
--i;
ret.insert(i, u'"');
ret.prepend(u'"');
}
return ret;
}
static QString shellQuote(const QString &arg)
{
if (QDir::separator() == u'\\')
return shellQuoteWin(arg);
else
return shellQuoteUnix(arg);
}
QString architectureFromName(const QString &name)
{
QRegularExpression architecture(QStringLiteral("_(armeabi-v7a|arm64-v8a|x86|x86_64).so$"));
@ -362,8 +292,6 @@ static QString llvmReadobjPath(const Options &options)
options.ndkHost));
}
QString fileArchitecture(const Options &options, const QString &path)
{
auto arch = architectureFromName(path);

View File

@ -16,6 +16,8 @@ qt_internal_add_tool(${target_name}
QT_NO_FOREACH
PUBLIC_LIBRARIES
Qt::Gui
INCLUDE_DIRECTORIES
../shared
)
qt_internal_return_unless_building_tools()
set_target_properties(${target_name} PROPERTIES

View File

@ -39,6 +39,8 @@
#include <functional>
#include <thread>
#include <shellquote_shared.h>
#ifdef Q_CC_MSVC
#define popen _popen
#define QT_POPEN_READ "rb"
@ -189,77 +191,6 @@ static bool execCommand(const QString &command, QByteArray *output = nullptr, bo
return pclose(process) == 0;
}
// Copy-pasted from qmake/library/ioutil.cpp
inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
{
for (int x = arg.length() - 1; x >= 0; --x) {
ushort c = arg.unicode()[x].unicode();
if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
return true;
}
return false;
}
static QString shellQuoteUnix(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
}; // 0-32 \'"$`<>|;&(){}*?#!~[]
if (!arg.length())
return QStringLiteral("\"\"");
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
ret.replace(u'\'', QStringLiteral("'\\''"));
ret.prepend(u'\'');
ret.append(u'\'');
}
return ret;
}
static QString shellQuoteWin(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
// - control chars & space
// - the shell meta chars "&()<>^|
// - the potential separators ,;=
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
};
if (!arg.length())
return QStringLiteral("\"\"");
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
// Quotes are escaped and their preceding backslashes are doubled.
// It's impossible to escape anything inside a quoted string on cmd
// level, so the outer quoting must be "suspended".
ret.replace(QRegularExpression(QStringLiteral("(\\\\*)\"")), QStringLiteral("\"\\1\\1\\^\"\""));
// The argument must not end with a \ since this would be interpreted
// as escaping the quote -- rather put the \ behind the quote: e.g.
// rather use "foo"\ than "foo\"
qsizetype i = ret.length();
while (i > 0 && ret.at(i - 1) == u'\\')
--i;
ret.insert(i, u'"');
ret.prepend(u'"');
}
return ret;
}
static QString shellQuote(const QString &arg)
{
if (QDir::separator() == u'\\')
return shellQuoteWin(arg);
else
return shellQuoteUnix(arg);
}
static bool parseOptions()
{
QStringList arguments = QCoreApplication::arguments();

View File

@ -0,0 +1,107 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $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$
**
****************************************************************************/
#ifndef SHELLQUOTE_SHARED_H
#define SHELLQUOTE_SHARED_H
#include <QDir>
#include <QRegularExpression>
#include <QString>
// Copy-pasted from qmake/library/ioutil.cpp
inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
{
for (int x = arg.length() - 1; x >= 0; --x) {
ushort c = arg.unicode()[x].unicode();
if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
return true;
}
return false;
}
static QString shellQuoteUnix(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
}; // 0-32 \'"$`<>|;&(){}*?#!~[]
if (!arg.length())
return QLatin1String("\"\"");
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
ret.prepend(QLatin1Char('\''));
ret.append(QLatin1Char('\''));
}
return ret;
}
static QString shellQuoteWin(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
// - control chars & space
// - the shell meta chars "&()<>^|
// - the potential separators ,;=
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
};
if (!arg.length())
return QLatin1String("\"\"");
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
// Quotes are escaped and their preceding backslashes are doubled.
// It's impossible to escape anything inside a quoted string on cmd
// level, so the outer quoting must be "suspended".
ret.replace(QRegularExpression(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\""));
// The argument must not end with a \ since this would be interpreted
// as escaping the quote -- rather put the \ behind the quote: e.g.
// rather use "foo"\ than "foo\"
int i = ret.length();
while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
--i;
ret.insert(i, QLatin1Char('"'));
ret.prepend(QLatin1Char('"'));
}
return ret;
}
static QString shellQuote(const QString &arg)
{
if (QDir::separator() == QLatin1Char('\\'))
return shellQuoteWin(arg);
else
return shellQuoteUnix(arg);
}
#endif // SHELLQUOTE_SHARED_H