13
13
#include " llbuild/Basic/ShellUtility.h"
14
14
#include " llvm/ADT/SmallString.h"
15
15
16
+ using namespace llvm ;
16
17
namespace llbuild {
17
18
namespace basic {
18
19
19
- void appendShellEscapedString (llvm::raw_ostream& os, StringRef string) {
20
20
21
+ #if defined(_WIN32)
22
+ std::string formatWindowsCommandArg (StringRef string) {
23
+ // Windows escaping, adapted from Daniel Colascione's "Everyone quotes
24
+ // command line arguments the wrong way" - Microsoft Developer Blog
25
+ const std::string needsQuote = " \t\n\v\" " ;
26
+ if (string.find_first_of (needsQuote) == std::string::npos) {
27
+ return string;
28
+ }
29
+
30
+ // To escape the command line, we surround the argument with quotes. However
31
+ // the complication comes due to how the Windows command line parser treats
32
+ // backslashes (\) and quotes (")
33
+ //
34
+ // - \ is normally treated as a literal backslash
35
+ // - e.g. foo\bar\baz => foo\bar\baz
36
+ // - However, the sequence \" is treated as a literal "
37
+ // - e.g. foo\"bar => foo"bar
38
+ //
39
+ // But then what if we are given a path that ends with a \? Surrounding
40
+ // foo\bar\ with " would be "foo\bar\" which would be an unterminated string
41
+ // since it ends on a literal quote. To allow this case the parser treats:
42
+ //
43
+ // - \\" as \ followed by the " metachar
44
+ // - \\\" as \ followed by a literal "
45
+ // - In general:
46
+ // - 2n \ followed by " => n \ followed by the " metachar
47
+ // - 2n+1 \ followed by " => n \ followed by a literal "
48
+ std::string escaped = " \" " ;
49
+ for (auto i = std::begin (string); i != std::end (string); ++i) {
50
+ int numBackslashes = 0 ;
51
+ while (i != string.end () && *i == ' \\ ' ) {
52
+ ++i;
53
+ ++numBackslashes;
54
+ }
55
+
56
+ if (i == string.end ()) {
57
+ // String ends with a backslash e.g. foo\bar\, escape all the backslashes
58
+ // then add the metachar " below
59
+ escaped.append (numBackslashes * 2 , ' \\ ' );
60
+ break ;
61
+ } else if (*i == ' "' ) {
62
+ // This is a string of \ followed by a " e.g. foo\"bar. Escape the
63
+ // backslashes and the quote
64
+ escaped.append (numBackslashes * 2 + 1 , ' \\ ' );
65
+ escaped.push_back (*i);
66
+ } else {
67
+ // These are just literal backslashes
68
+ escaped.append (numBackslashes, ' \\ ' );
69
+ escaped.push_back (*i);
70
+ }
71
+ }
72
+ escaped.push_back (' "' );
73
+
74
+ return escaped;
75
+ }
76
+
77
+ std::string formatWindowsCommandString (std::vector<std::string> args) {
78
+ std::string commandLine;
79
+ for (auto & arg : args)
80
+ commandLine += formatWindowsCommandArg (arg) + " " ;
81
+ if (commandLine.size ())
82
+ commandLine.pop_back ();
83
+ return commandLine;
84
+ }
85
+ #endif
86
+
87
+ void appendShellEscapedString (llvm::raw_ostream& os, StringRef string) {
88
+ #if defined(_WIN32)
89
+ os << formatWindowsCommandArg (string);
90
+ return ;
91
+ #else
21
92
static const std::string whitelist = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_/:@#%+=.," ;
22
93
auto pos = string.find_first_not_of (whitelist);
23
94
@@ -27,32 +98,27 @@ void appendShellEscapedString(llvm::raw_ostream& os, StringRef string) {
27
98
return ;
28
99
}
29
100
30
- #if defined(_WIN32)
31
- std::string escQuote = " \" " ;
32
- #else
33
101
std::string escQuote = " '" ;
34
- #endif
35
102
// We only need to escape the single quote, if it isn't present we can
36
103
// escape using single quotes.
37
- auto singleQuotePos = string.find_first_of (escQuote , pos);
104
+ auto singleQuotePos = string.find_first_of (" ' " , pos);
38
105
if (singleQuotePos == std::string::npos) {
39
- os << escQuote;
40
- os << string;
41
- os << escQuote;
106
+ os << " '" << string << " '" ;
42
107
return ;
43
108
}
44
109
45
110
// Otherwise iterate and escape all the single quotes.
46
- os << escQuote ;
111
+ os << " ' " ;
47
112
os << string.slice (0 , singleQuotePos);
48
113
for (auto idx = singleQuotePos; idx < string.size (); idx++) {
49
- if (string[idx] == escQuote[ 0 ] ) {
50
- os << escQuote << " \\ " << escQuote << escQuote ;
114
+ if (string[idx] == ' \' ' ) {
115
+ os << " ' \\ '' " ;
51
116
} else {
52
117
os << string[idx];
53
118
}
54
119
}
55
- os << escQuote;
120
+ os << " '" ;
121
+ #endif
56
122
}
57
123
58
124
std::string shellEscaped (StringRef string) {
0 commit comments