@@ -12,6 +12,7 @@ Date: August 2012
12
12
13
13
#ifdef _WIN32
14
14
#include < process.h>
15
+ #include < windows.h>
15
16
#else
16
17
17
18
#include < cstring>
@@ -40,14 +41,85 @@ int run(const std::string &what, const std::vector<std::string> &argv)
40
41
return run (what, argv, " " , " " , " " );
41
42
}
42
43
43
- #ifndef _WIN32
44
+ #ifdef _WIN32
45
+ #define STDIN_FILENO 0
46
+ #define STDOUT_FILENO 1
47
+ #define STDERR_FILENO 2
48
+ using fdt = HANDLE;
49
+ #else
50
+ using fdt = int ;
51
+ #endif
52
+
44
53
// / open given file to replace either stdin, stderr, stdout
45
- static int stdio_redirection (int fd, const std::string &file)
54
+ static fdt stdio_redirection (int fd, const std::string &file)
46
55
{
47
- int result_fd = fd;
56
+ fdt result_fd;
57
+
58
+ #ifdef _WIN32
59
+ std::string name;
60
+
61
+ SECURITY_ATTRIBUTES SecurityAttributes;
62
+ ZeroMemory (&SecurityAttributes, sizeof SecurityAttributes);
63
+ SecurityAttributes.bInheritHandle = true ;
64
+
65
+ switch (fd)
66
+ {
67
+ case STDIN_FILENO:
68
+ name = " stdin" ;
69
+ if (file.empty ())
70
+ result_fd = GetStdHandle (STD_INPUT_HANDLE);
71
+ else
72
+ result_fd = CreateFileW (
73
+ widen (file).c_str (),
74
+ GENERIC_READ,
75
+ 0 ,
76
+ &SecurityAttributes,
77
+ OPEN_EXISTING,
78
+ FILE_ATTRIBUTE_READONLY,
79
+ NULL );
80
+ break ;
81
+
82
+ case STDOUT_FILENO:
83
+ name = " stdout" ;
84
+ if (file.empty ())
85
+ result_fd = GetStdHandle (STD_OUTPUT_HANDLE);
86
+ else
87
+ result_fd = CreateFileW (
88
+ widen (file).c_str (),
89
+ GENERIC_WRITE,
90
+ 0 ,
91
+ &SecurityAttributes,
92
+ CREATE_ALWAYS,
93
+ FILE_ATTRIBUTE_NORMAL,
94
+ NULL );
95
+ break ;
96
+
97
+ case STDERR_FILENO:
98
+ name = " stderr" ;
99
+ if (file.empty ())
100
+ result_fd = GetStdHandle (STD_ERROR_HANDLE);
101
+ else
102
+ result_fd = CreateFileW (
103
+ widen (file).c_str (),
104
+ GENERIC_WRITE,
105
+ 0 ,
106
+ &SecurityAttributes,
107
+ CREATE_ALWAYS,
108
+ FILE_ATTRIBUTE_NORMAL,
109
+ NULL );
110
+ break ;
111
+
112
+ default :
113
+ UNREACHABLE;
114
+ }
115
+
116
+ if (result_fd == INVALID_HANDLE_VALUE)
117
+ perror ((" Failed to open " + name + " file " + file).c_str ());
118
+
119
+ #else
48
120
49
121
if (file.empty ())
50
- return result_fd ;
122
+ return fd ;
51
123
52
124
int flags = 0 , mode = 0 ;
53
125
std::string name;
@@ -71,74 +143,160 @@ static int stdio_redirection(int fd, const std::string &file)
71
143
}
72
144
73
145
result_fd = open (file.c_str (), flags, mode);
146
+
74
147
if (result_fd == -1 )
75
148
perror ((" Failed to open " + name + " file " + file).c_str ());
149
+ #endif
76
150
77
151
return result_fd;
78
152
}
79
- #endif
80
153
81
- int run (
82
- const std::string &what,
83
- const std::vector<std::string> &argv,
84
- const std::string &std_input,
85
- const std::string &std_output,
86
- const std::string &std_error)
154
+ #ifdef _WIN32
155
+ // Read
156
+ // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
157
+ std::wstring quote_windows_arg (const std::wstring &src)
87
158
{
88
- #ifdef _WIN32
89
- // we use the cmd.exe shell to do stdin/stdout/stderr redirection on Windows
90
- if (!std_input.empty () || !std_output.empty () || !std_error.empty ())
159
+ if (src.find_first_of (L" \t\n\v\" " ) == src.npos )
160
+ return src;
161
+
162
+ std::wstring result = L" \" " ;
163
+
164
+ for (auto it = src.begin ();; ++it)
91
165
{
92
- std::vector<std::string> new_argv = argv;
93
- new_argv.insert (new_argv.begin (), " cmd.exe" );
94
- new_argv.insert (new_argv.begin () + 1 , " /c" );
166
+ std::size_t NumberBackslashes = 0 ;
95
167
96
- if (!std_input. empty () )
168
+ while (it != src. end () && *it == L ' \\ ' )
97
169
{
98
- new_argv. push_back ( " < " ) ;
99
- new_argv. push_back (std_input) ;
170
+ ++it ;
171
+ ++NumberBackslashes ;
100
172
}
101
173
102
- if (!std_output. empty ())
174
+ if (it == src. end ())
103
175
{
104
- new_argv.push_back (" >" );
105
- new_argv.push_back (std_output);
176
+ //
177
+ // Escape all backslashes, but let the terminating
178
+ // double quotation mark we add below be interpreted
179
+ // as a metacharacter.
180
+ //
181
+
182
+ result.append (NumberBackslashes * 2 , L' \\ ' );
183
+ break ;
106
184
}
185
+ else if (*it == L' "' )
186
+ {
187
+ //
188
+ // Escape all backslashes and the following
189
+ // double quotation mark.
190
+ //
107
191
108
- if (!std_error.empty ())
192
+ result.append (NumberBackslashes * 2 + 1 , L' \\ ' );
193
+ result.push_back (*it);
194
+ }
195
+ else
109
196
{
110
- new_argv.push_back (" 2>" );
111
- new_argv.push_back (std_error);
197
+ //
198
+ // Backslashes aren't special here.
199
+ //
200
+
201
+ result.append (NumberBackslashes, L' \\ ' );
202
+ result.push_back (*it);
112
203
}
204
+ }
205
+
206
+ result.push_back (L' "' );
207
+
208
+ return result;
209
+ }
210
+ #endif
113
211
114
- // this is recursive
115
- return run (new_argv[0 ], new_argv, " " , " " , " " );
212
+ int run (
213
+ const std::string &what,
214
+ const std::vector<std::string> &argv,
215
+ const std::string &std_input,
216
+ const std::string &std_output,
217
+ const std::string &std_error)
218
+ {
219
+ #ifdef _WIN32
220
+ // unicode commandline, quoted
221
+ std::wstring cmdline;
222
+
223
+ // we replace argv[0] by what
224
+ cmdline = quote_windows_arg (widen (what));
225
+
226
+ for (std::size_t i = 1 ; i < argv.size (); i++)
227
+ {
228
+ cmdline += L" " ;
229
+ cmdline += quote_windows_arg (widen (argv[i]));
116
230
}
117
231
118
- // unicode version of the arguments
119
- std::vector<std::wstring> wargv;
232
+ PROCESS_INFORMATION piProcInfo;
233
+ STARTUPINFOW siStartInfo;
234
+
235
+ ZeroMemory (&piProcInfo, sizeof piProcInfo);
236
+ ZeroMemory (&siStartInfo, sizeof siStartInfo);
120
237
121
- wargv. resize (argv. size ()) ;
238
+ siStartInfo. cb = sizeof siStartInfo ;
122
239
123
- for (std::size_t i=0 ; i<argv.size (); i++)
124
- wargv[i]=widen (argv[i]);
240
+ siStartInfo.hStdInput = stdio_redirection (STDIN_FILENO, std_input);
241
+ siStartInfo.hStdOutput = stdio_redirection (STDOUT_FILENO, std_output);
242
+ siStartInfo.hStdError = stdio_redirection (STDERR_FILENO, std_error);
125
243
126
- std::vector< const wchar_t *> _argv (argv. size ()+ 1 ) ;
244
+ siStartInfo. dwFlags |= STARTF_USESTDHANDLES ;
127
245
128
- for (std::size_t i=0 ; i<wargv.size (); i++)
129
- _argv[i]=wargv[i].c_str ();
246
+ // CreateProcessW wants to modify the command line
247
+ std::vector<wchar_t > mutable_cmdline (cmdline.begin (), cmdline.end ());
248
+ mutable_cmdline.push_back (0 ); // zero termination
249
+ wchar_t *cmdline_ptr = mutable_cmdline.data ();
130
250
131
- _argv[argv.size ()]=NULL ;
251
+ BOOL bSuccess = CreateProcessW (
252
+ NULL , // application name
253
+ cmdline_ptr, // command line
254
+ NULL , // process security attributes
255
+ NULL , // primary thread security attributes
256
+ true , // handles are inherited
257
+ 0 , // creation flags
258
+ NULL , // use parent's environment
259
+ NULL , // use parent's current directory
260
+ &siStartInfo, // STARTUPINFO
261
+ &piProcInfo); // PROCESS_INFORMATION
132
262
133
- // warning: the arguments may still need escaping,
134
- // as windows will concatenate the argv strings back together,
135
- // separating them with spaces
263
+ if (!bSuccess)
264
+ {
265
+ if (!std_input.empty ())
266
+ CloseHandle (siStartInfo.hStdInput );
267
+ if (!std_output.empty ())
268
+ CloseHandle (siStartInfo.hStdOutput );
269
+ if (!std_error.empty ())
270
+ CloseHandle (siStartInfo.hStdError );
271
+ return -1 ;
272
+ }
136
273
137
- std::wstring wide_what=widen (what);
138
- int status=_wspawnvp (_P_WAIT, wide_what.c_str (), _argv.data ());
139
- return status;
274
+ // wait for child to finish
275
+ WaitForSingleObject (piProcInfo.hProcess , INFINITE);
140
276
141
- #else
277
+ if (!std_input.empty ())
278
+ CloseHandle (siStartInfo.hStdInput );
279
+ if (!std_output.empty ())
280
+ CloseHandle (siStartInfo.hStdOutput );
281
+ if (!std_error.empty ())
282
+ CloseHandle (siStartInfo.hStdError );
283
+
284
+ DWORD exit_code;
285
+
286
+ // get exit code
287
+ if (!GetExitCodeProcess (piProcInfo.hProcess , &exit_code))
288
+ {
289
+ CloseHandle (piProcInfo.hProcess );
290
+ CloseHandle (piProcInfo.hThread );
291
+ return -1 ;
292
+ }
293
+
294
+ CloseHandle (piProcInfo.hProcess );
295
+ CloseHandle (piProcInfo.hThread );
296
+
297
+ return exit_code;
298
+
299
+ #else
142
300
int stdin_fd = stdio_redirection (STDIN_FILENO, std_input);
143
301
int stdout_fd = stdio_redirection (STDOUT_FILENO, std_output);
144
302
int stderr_fd = stdio_redirection (STDERR_FILENO, std_error);
@@ -229,7 +387,7 @@ int run(
229
387
230
388
return 1 ;
231
389
}
232
- #endif
390
+ #endif
233
391
}
234
392
235
393
int run (
0 commit comments