@@ -323,6 +323,11 @@ trying to be a little cross-platform here. The only difference is
323323that "\" is *not* treated as an escape except when it precedes
324324punctuation, since it's used all over the place in DOS path specs.
325325
326+ TODO: strip caret escapes?
327+
328+ TODO: use
329+ https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#parsing-c-command-line-arguments
330+
326331TODO: globbing? probably not (it's unDOSish).
327332
328333TODO: shebang emulation? Probably, but perhaps that should be part
@@ -396,6 +401,26 @@ sub _dup2_gently {
396401 IPC::Run::_dup2_rudely( $fd1 , $fd2 );
397402}
398403
404+ # Quote a string for use in the lpCommandLine argument of CreateProcessA(), such
405+ # that the string yields a single argv element in the callee.
406+ # https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#parsing-c-command-line-arguments
407+ # gives the algorithm for translating a command line to an argv.
408+ sub _quote_cmd_line_arg {
409+ my $arg = shift ;
410+
411+ # Skip optional quoting.
412+ return $arg unless $arg =~ / [\"\s ]|^$ / ;
413+
414+ # Change N >= 0 backslashes before a double quote to 2N+1 backslashes.
415+ $arg =~ s / (\\ *)"/ ${\( $1 . $1 )}\\ "/ gs ;
416+
417+ # Change N >= 1 backslashes at end of argument to 2N backslashes.
418+ $arg =~ s / (\\ +)\z / ${\( $1 . $1 )}/ gs ;
419+
420+ # Wrap the whole thing in unescaped double quotes.
421+ return " \" $arg \" " ;
422+ }
423+
399424sub win32_spawn {
400425 my ( $cmd , $ops ) = @_ ;
401426
@@ -442,11 +467,7 @@ sub win32_spawn {
442467 }
443468
444469 my $process ;
445- my $cmd_line = join " " , map {
446- ( my $s = $_ ) =~ s / "/ """/ g ;
447- $s = qq{ "$s "} if / [\"\s ]|^$ / ;
448- $s ;
449- } @$cmd ;
470+ my $cmd_line = join " " , map { _quote_cmd_line_arg $_ } @$cmd ;
450471
451472 _debug " cmd line: " , $cmd_line
452473 if _debugging;
0 commit comments