make shellQuoteWin() be more sparing with circumflexes

... as newer versions of nmake (and jom, for compatibility) have botched
circumflex processing (they simply don't do it when shortcutting the shell
evaluation).

as a side effect, the output is also more readable if the string contains
quotes.

Change-Id: I0506b59ceecb70da258c482f9973156b2803066d
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
This commit is contained in:
Oswald Buddenhagen 2014-11-20 10:38:37 +01:00
parent fde33949f4
commit cf38b12a27
2 changed files with 38 additions and 16 deletions

View File

@ -99,12 +99,19 @@ QString IoUtils::resolvePath(const QString &baseDir, const QString &fileName)
return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName); return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName);
} }
inline static
bool isSpecialChar(ushort c, const uchar (&iqm)[16])
{
if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
return true;
return false;
}
inline static inline static
bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16]) bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
{ {
for (int x = arg.length() - 1; x >= 0; --x) { for (int x = arg.length() - 1; x >= 0; --x) {
ushort c = arg.unicode()[x].unicode(); if (isSpecialChar(arg.unicode()[x].unicode(), iqm))
if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
return true; return true;
} }
return false; return false;
@ -140,23 +147,38 @@ QString IoUtils::shellQuoteWin(const QString &arg)
0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78, 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
}; };
// Shell meta chars that need escaping.
static const uchar ism[] = {
0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x50,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
}; // &()<>^|
if (!arg.length()) if (!arg.length())
return QString::fromLatin1("\"\""); return QString::fromLatin1("\"\"");
QString ret(arg); QString ret(arg);
if (hasSpecialChars(ret, iqm)) { if (hasSpecialChars(ret, iqm)) {
// Quotes are escaped and their preceding backslashes are doubled. // The process-level standard quoting allows escaping quotes with backslashes (note
// It's impossible to escape anything inside a quoted string on cmd // that backslashes don't escape themselves, unless they are followed by a quote).
// level, so the outer quoting must be "suspended". // Consequently, quotes are escaped and their preceding backslashes are doubled.
ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\"")); ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
// The argument must not end with a \ since this would be interpreted // Trailing backslashes must be doubled as well, as they are followed by a quote.
// as escaping the quote -- rather put the \ behind the quote: e.g. ret.replace(QRegExp(QLatin1String("(\\\\+)$")), QLatin1String("\\1\\1"));
// rather use "foo"\ than "foo\" // However, the shell also interprets the command, and no backslash-escaping exists
int i = ret.length(); // there - a quote always toggles the quoting state, but is nonetheless passed down
while (i > 0 && ret.at(i - 1) == QLatin1Char('\\')) // to the called process verbatim. In the unquoted state, the circumflex escapes
--i; // meta chars (including itself and quotes), and is removed from the command.
ret.insert(i, QLatin1Char('"')); bool quoted = true;
for (int i = 0; i < ret.length(); i++) {
QChar c = ret.unicode()[i];
if (c.unicode() == '"')
quoted = !quoted;
else if (!quoted && isSpecialChar(c.unicode(), ism))
ret.insert(i++, QLatin1Char('^'));
}
if (!quoted)
ret.append(QLatin1Char('^'));
ret.append(QLatin1Char('"'));
ret.prepend(QLatin1Char('"')); ret.prepend(QLatin1Char('"'));
} }
return ret; return ret;

View File

@ -156,9 +156,9 @@ testReplace($$relative_path("/fake/trolls", "/fake/path"), "../trolls", "relativ
testReplace($$relative_path(""), "", "relative_path of empty") testReplace($$relative_path(""), "", "relative_path of empty")
#this test is very rudimentary. the backend function is thoroughly tested in qt creator #this test is very rudimentary. the backend function is thoroughly tested in qt creator
in = "some nasty\" path\\" in = "some nasty & ugly\" path & thing\\"
out_cmd = "\"some nasty\"\\^\"\" path\"\\" out_cmd = "\"some nasty & ugly\\\" path ^& thing\\\\^\""
out_sh = "'some nasty\" path\\'" out_sh = "'some nasty & ugly\" path & thing\\'"
equals(QMAKE_HOST.os, Windows): \ equals(QMAKE_HOST.os, Windows): \
out = $$out_cmd out = $$out_cmd
else: \ else: \