diff --git a/Makefile.PL b/Makefile.PL index 0e9b351..385eb89 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -16,8 +16,9 @@ if ( $^O ne 'MSWin32' ) { } } else { - $PREREQ_PM{'Win32::Process'} = '0.14'; - $PREREQ_PM{'Win32API::File'} = '0.0901'; + $PREREQ_PM{'Win32::Process'} = '0.14'; + $PREREQ_PM{'Win32::ShellQuote'} = 0; + $PREREQ_PM{'Win32API::File'} = '0.0901'; if ( $] >= 5.021006 ) { $PREREQ_PM{'Win32API::File'} = '0.1203'; } diff --git a/lib/IPC/Run/Win32Helper.pm b/lib/IPC/Run/Win32Helper.pm index 8dc0a15..8475c62 100644 --- a/lib/IPC/Run/Win32Helper.pm +++ b/lib/IPC/Run/Win32Helper.pm @@ -40,6 +40,7 @@ require POSIX; use Text::ParseWords; use Win32::Process; +use Win32::ShellQuote (); use IPC::Run::Debug; use Win32API::File qw( FdGetOsFHandle @@ -323,6 +324,11 @@ trying to be a little cross-platform here. The only difference is that "\" is *not* treated as an escape except when it precedes punctuation, since it's used all over the place in DOS path specs. +TODO: strip caret escapes? + +TODO: use +https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#parsing-c-command-line-arguments + TODO: globbing? probably not (it's unDOSish). TODO: shebang emulation? Probably, but perhaps that should be part @@ -442,11 +448,7 @@ sub win32_spawn { } my $process; - my $cmd_line = join " ", map { - ( my $s = $_ ) =~ s/"/"""/g; - $s = qq{"$s"} if /[\"\s]|^$/; - $s; - } @$cmd; + my $cmd_line = Win32::ShellQuote::quote_native(@$cmd); _debug "cmd line: ", $cmd_line if _debugging; diff --git a/t/run.t b/t/run.t index 3980435..da18243 100644 --- a/t/run.t +++ b/t/run.t @@ -38,7 +38,7 @@ sub get_warnings { select STDERR; select STDOUT; -use Test::More tests => 268; +use Test::More tests => 272; use IPC::Run::Debug qw( _map_fds ); use IPC::Run qw( :filters :filter_imp start ); @@ -210,6 +210,31 @@ is $? >> 8, 42; is( _map_fds, $fd_map ); $fd_map = _map_fds; +## +## Arguments bearing most bytes, excluding NUL (unsupported) and BEL (noisy and +## not otherwise special). Arguments bearing special sequences of bytes. +## +{ + local $ENV{PERL_UNICODE}; + delete $ENV{PERL_UNICODE}; + + my @bytes = map { $_ == 7 ? () : pack( 'C', $_ ); } 1 .. 0xFF; + $r = run( + [ $perl, '-e', 'binmode STDOUT; print join "\0", @ARGV', @bytes ], + '>', \$out + ); + eok( $out, join "\0", @bytes ); + + my $sequences = qq{\\"\\az\\\\"\\\\\\}; + foreach my $payload ( join( '', @bytes ), $sequences, "$sequences\n" ) { + $r = run( + [ $perl, '-e', 'binmode STDOUT; print @ARGV', $payload ], + '>', \$out + ); + eok( $out, $payload ); + } +} + ## ## A function ## diff --git a/t/win32_compile.t b/t/win32_compile.t index 0c80838..da90f8b 100644 --- a/t/win32_compile.t +++ b/t/win32_compile.t @@ -35,7 +35,8 @@ BEGIN { plan( skip_all => "android does not support getprotobyname()" ); } - $INC{$_} = 1 for qw( Win32/Process.pm Win32API/File.pm ); + $INC{$_} = 1 for qw( + Win32/Process.pm Win32/ShellQuote.pm Win32API/File.pm ); package Win32API::File;