summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2014-11-20 10:38:37 +0100
committerOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2015-02-04 16:34:02 +0000
commitcf38b12a27627161e6799a3883285d05bc6420e5 (patch)
tree07305955621546cf9bb961a0d31250eb2ddcfc83
parentfde33949f4992034626980aaaa237f05128fc310 (diff)
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>
-rw-r--r--qmake/library/ioutils.cpp48
-rw-r--r--tests/auto/tools/qmake/testdata/functions/functions.pro6
2 files changed, 38 insertions, 16 deletions
diff --git a/qmake/library/ioutils.cpp b/qmake/library/ioutils.cpp
index 432a7934a9..8039e876d7 100644
--- a/qmake/library/ioutils.cpp
+++ b/qmake/library/ioutils.cpp
@@ -100,11 +100,18 @@ QString IoUtils::resolvePath(const QString &baseDir, const QString &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
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))))
+ if (isSpecialChar(arg.unicode()[x].unicode(), iqm))
return true;
}
return false;
@@ -140,23 +147,38 @@ QString IoUtils::shellQuoteWin(const QString &arg)
0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
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())
return QString::fromLatin1("\"\"");
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(QRegExp(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('"'));
+ // The process-level standard quoting allows escaping quotes with backslashes (note
+ // that backslashes don't escape themselves, unless they are followed by a quote).
+ // Consequently, quotes are escaped and their preceding backslashes are doubled.
+ ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
+ // Trailing backslashes must be doubled as well, as they are followed by a quote.
+ ret.replace(QRegExp(QLatin1String("(\\\\+)$")), QLatin1String("\\1\\1"));
+ // However, the shell also interprets the command, and no backslash-escaping exists
+ // there - a quote always toggles the quoting state, but is nonetheless passed down
+ // to the called process verbatim. In the unquoted state, the circumflex escapes
+ // meta chars (including itself and quotes), and is removed from the command.
+ 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('"'));
}
return ret;
diff --git a/tests/auto/tools/qmake/testdata/functions/functions.pro b/tests/auto/tools/qmake/testdata/functions/functions.pro
index a0e88300d4..5db8036188 100644
--- a/tests/auto/tools/qmake/testdata/functions/functions.pro
+++ b/tests/auto/tools/qmake/testdata/functions/functions.pro
@@ -156,9 +156,9 @@ testReplace($$relative_path("/fake/trolls", "/fake/path"), "../trolls", "relativ
testReplace($$relative_path(""), "", "relative_path of empty")
#this test is very rudimentary. the backend function is thoroughly tested in qt creator
-in = "some nasty\" path\\"
-out_cmd = "\"some nasty\"\\^\"\" path\"\\"
-out_sh = "'some nasty\" path\\'"
+in = "some nasty & ugly\" path & thing\\"
+out_cmd = "\"some nasty & ugly\\\" path ^& thing\\\\^\""
+out_sh = "'some nasty & ugly\" path & thing\\'"
equals(QMAKE_HOST.os, Windows): \
out = $$out_cmd
else: \