@@ -208,7 +208,7 @@ function launch_on_machine(manager::SSHManager, machine, cnt, params, launched,
208208 # shell login (-l) with string command (-c) to launch julia process
209209 remotecmd = shell_escape_posixly (` sh -l -c $cmds ` )
210210
211- elseif params[ : shell] == :wincmd
211+ elseif shell == :wincmd
212212 # ssh connects to Windows cmd.exe
213213
214214 # Passing the cookie via ssh stdin currently hangs with
@@ -270,36 +270,39 @@ end
270270
271271function print_shell_escaped_windows (io, args:: AbstractString... ):: Nothing
272272 first = true
273+ quoted = false
273274 for arg in args
274275 first || write (io, ' ' )
275276 first = false
276277 function isword (c:: AbstractChar )
277278 return ' a' <= c <= ' z' || ' A' <= c <= ' Z' || ' 0' <= c <= ' 9' ||
278279 c == ' \\ ' || c == ' .' || c == ' _' || c == ' -' || c == ' :' ||
279- c == ' /' || c == ' =' || c == ' "'
280+ c == ' /' || c == ' =' || c == ' "' || c == ' % '
280281 end
281282 quotes = ! all (isword, arg) || arg == " "
282- quotes && write (io, ' "' )
283+ if quotes; write (io, ' "' ); quoted = ! quoted; end
283284 backslashes = 0
284285 for c in arg
285286 if c == ' \\ '
286287 backslashes += 1
287288 else
288289 if c == ' "'
289290 backslashes = backslashes * 2 + 1
291+ quoted = ! quoted
290292 end
291293 for j= 1 : backslashes
292294 write (io, ' \\ ' )
293295 end
294296 backslashes = 0
297+ isword (c) || quoted || write (io, ' ^' ) # for cmd.exe
295298 write (io, c)
296299 end
297300 end
298301 if quotes; backslashes *= 2 ; end
299302 for j= 1 : backslashes
300303 write (io, ' \\ ' )
301304 end
302- quotes && write (io, ' "' )
305+ if quotes; write (io, ' "' ); quoted = ! quoted; end
303306 end
304307end
305308
@@ -308,16 +311,27 @@ end
308311
309312Convert the collection of strings `args` into a Windows command line.
310313
311- Windows `cmd.exe` passes the entire command line as a single string to
312- the application (unlike on POSIX systems, where the shell splits the
313- command line into a list of arguments). Many Windows API applications
314+ Windows passes the entire command line as a single string to the
315+ application (unlike POSIX systems, where the shell splits the command
316+ line into a list of arguments). Many Windows API applications
314317(including julia.exe), use the conventions of the [Microsoft C
315318runtime](https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments)
316- to split that command line into a list of strings. This function
317- implements the inverse of that command-line parser. It joins
318- command-line arguments to be passed to a Windows C/C++/Julia
319- application into a command line, escaping or quoting meta characters
320- such as space, double quotes and backslash where needed.
319+ to split that command line into a list of strings.
320+
321+ This function implements the inverse of such a C runtime command-line
322+ parser. It joins command-line arguments to be passed to a Windows
323+ C/C++/Julia application into a command line, escaping or quoting meta
324+ characters such as space, double quotes and backslash where needed.
325+
326+ In addition, this function also escapes meta characters processed by
327+ `cmd.exe`: it places a ^ in front of any potential metacharacter that
328+ follows an even number of quotation marks on the command line.
329+
330+ The percent sign (`%`) is not escaped such that shell variable
331+ references (like `%USER%`) can still be substituted by `cmd.exe`.
332+
333+ Input strings should avoid ASCII control characters, as many of these
334+ cannot be escaped (e.g., NUL, CR, LF).
321335
322336# Example
323337```jldoctest
0 commit comments