File Coverage

lib/IPC/Run/Fused/Win32.pm
Criterion Covered Total %
statement 24 117 20.5
branch 0 46 0.0
condition 0 21 0.0
subroutine 8 16 50.0
pod 1 1 100.0
total 33 201 16.4


line stmt bran cond sub pod time code
1 1     1   663 use 5.008003;
  1         3  
  1         32  
2 1     1   3 use strict;
  1         1  
  1         30  
3 1     1   3 use warnings;
  1         1  
  1         52  
4              
5             package IPC::Run::Fused::Win32;
6              
7             our $VERSION = '1.000000';
8              
9             # ABSTRACT: Implementation of IPC::Run::Fused for Win32
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 1     1   564 use IO::Handle;
  1         5417  
  1         49  
14 1     1   492 use Module::Runtime;
  1         1357  
  1         5  
15              
16              
17              
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28              
29              
30              
31              
32              
33              
34              
35              
36              
37              
38              
39              
40              
41 1     1   406 use IPC::Run::Fused qw(_fail);
  1         2  
  1         53  
42 1     1   562 use Socket qw( AF_UNIX SOCK_STREAM PF_UNSPEC );
  1         3155  
  1         171  
43              
44 1     1   7 use Exporter qw(import);
  1         1  
  1         946  
45             our @EXPORT_OK = qw( run_fused );
46              
47             sub run_fused {
48 0     0 1   my ( undef, @params ) = @_;
49 0 0 0       if ( ref $params[0] and 'CODE' eq ref $params[0] ) {
50 0           goto \&_run_fused_coderef;
51             }
52 0           goto \&_run_fused_job;
53             }
54              
55             sub _run_fused_job { ## no critic (Subroutines::RequireArgUnpacking)
56 0     0     my ( $read_handle, @params ) = ( \shift @_, @_ );
57              
58 0           my $config = _run_fused_jobdecode(@params);
59              
60 0           Module::Runtime::require_module('File::Which');
61              
62 0           $config->{which} = File::Which::which( $config->{executable} );
63              
64 0           local $IPC::Run::Fused::FAIL_CONTEXT{which} = $config->{which};
65 0           local $IPC::Run::Fused::FAIL_CONTEXT{executable} = $config->{executable};
66 0           local $IPC::Run::Fused::FAIL_CONTEXT{command} = $config->{command};
67              
68 0 0         if ( not $config->{which} ) {
69 0           _fail('Failed to resolve executable to path');
70             }
71              
72 0           Module::Runtime::require_module('Win32::Job');
73              
74 0           pipe ${$read_handle}, my $writer;
  0            
75              
76 0 0         if ( my $pid = fork ) {
77 0           return $pid;
78             }
79              
80 0           my $job = Win32::Job->new();
81 0 0         $job->spawn(
82             $config->{which},
83             $config->{command},
84             {
85             stdout => $writer,
86             stderr => $writer,
87             },
88             ) or _fail('Could not spawn job');
89 0           my $result = $job->run( -1, 0 );
90 0 0         if ( not $result ) {
91 0           my $status = $job->status();
92 0 0 0       if ( exists $status->{exitcode} and 293 == $status->{exitcode} ) {
93 0           _fail('Process used more than allotted time');
94             }
95 0           _fail( 'Child process terminated with exit code' . $status->{exitcode} );
96             }
97 0           exit;
98             }
99              
100             sub _run_fused_jobdecode {
101 0     0     my (@params) = @_;
102              
103 0 0 0       if ( ref $params[0] and 'SCALAR' eq ref $params[0] ) {
104 0           my $command = ${ $params[0] };
  0            
105 0           $command =~ s/\A\s*//msx;
106             return {
107 0           command => $command,
108             executable => _win32_command_find_invocant($command),
109             };
110             }
111             return {
112 0           executable => $params[0],
113             command => _win32_escape_command(@params),
114             };
115             }
116              
117             sub _run_fused_coderef { ## no critic (Subroutines::RequireArgUnpacking)
118 0     0     my ( $read_handle, $code ) = ( \shift @_, @_ );
119 0           my ( $reader, $writer );
120              
121 0 0         socketpair $reader, $writer, AF_UNIX, SOCK_STREAM, PF_UNSPEC or _fail('creating socketpair');
122 0 0         shutdown $reader, 1 or _fail('Cant close write to reader');
123 0 0         shutdown $writer, 0 or _fail('Cant close read to writer');
124              
125 0 0         if ( my $pid = fork ) {
126 0           ${$read_handle} = $reader;
  0            
127 0           return $pid;
128             }
129              
130 0 0         close *STDERR or _fail('Closing STDERR');
131 0 0         close *STDOUT or _fail('Closing STDOUT');
132 0 0         open *STDOUT, '>>&=', $writer or _fail('Assigning to STDOUT');
133 0 0         open *STDERR, '>>&=', $writer or _fail('Assigning to STDERR');
134 0           $code->();
135 0           exit;
136              
137             }
138              
139             our $BACKSLASH = chr 92;
140             our $DBLBACKSLASH = $BACKSLASH x 2;
141             our $DOS_SPECIAL_CHARS = {
142             chr 92 => [ 'backslash ', $BACKSLASH x 2 ],
143             chr 34 => [ 'double-quotes', $BACKSLASH . chr 34 ],
144              
145             #chr(60) => ['open angle bracket', $backslash . chr(60)],
146             #chr(62) => ['close angle bracket', $backslash . chr(62)],
147             };
148             our $DOS_REV_CHARS = {
149             map { ( $DOS_SPECIAL_CHARS->{$_}->[1], [ $DOS_SPECIAL_CHARS->{$_}->[0], $_ ] ) }
150             keys %{$DOS_SPECIAL_CHARS},
151             };
152              
153             sub _win32_escape_command_char {
154 0     0     my ($char) = @_;
155 0 0         return $char unless exists $DOS_SPECIAL_CHARS->{$char};
156 0           return $DOS_SPECIAL_CHARS->{$char}->[1];
157             }
158              
159             sub _win32_escape_command_token {
160             ## no critic (RegularExpressions)
161 0     0     my $chars = join q{}, map { _win32_escape_command_char($_) } split //, shift;
  0            
162 0           return qq{"$chars"};
163             }
164              
165             sub _win32_escape_command {
166 0     0     my (@tokens) = @_;
167 0           return join q{ }, map { _win32_escape_command_token($_) } @tokens;
  0            
168             }
169              
170             sub _win32_command_find_invocant {
171 0     0     my ($command) = @_;
172 0           $command = "$command";
173 0           my $first = q[];
174             ## no critic (RegularExpressions)
175 0           my @chars = split //, $command;
176 0           my $inquote;
177              
178 0           while (@chars) {
179 0           my $char = $chars[0];
180 0           my $dchar = $chars[0] . $chars[1];
181              
182 0 0 0       if ( not $inquote and q["] eq $char ) {
183 0           $inquote = 1;
184 0           shift @chars;
185 0           next;
186             }
187 0 0 0       if ( $inquote and q["] eq $char ) {
188 0           $inquote = undef;
189 0           shift @chars;
190 0           next;
191             }
192 0 0         if ( exists $DOS_REV_CHARS->{$dchar} ) {
193 0           $first .= $DOS_REV_CHARS->{$dchar}->[1];
194 0           shift @chars;
195 0           shift @chars;
196 0           next;
197             }
198 0 0 0       if ( q[ ] eq $char and not $inquote ) {
199 0 0         if ( not length $first ) {
200 0           shift @chars;
201 0           next;
202             }
203 0           return $first;
204             }
205 0 0 0       if ( q[ ] eq $char and $inquote ) {
206 0           $first .= $char;
207 0           shift @chars;
208 0           next;
209             }
210 0           $first .= $char;
211 0           shift @chars;
212             }
213 0 0         if ($inquote) {
214 0           _fail('Could not parse command from commandline');
215             }
216 0           return $first;
217             }
218              
219             1;
220              
221             __END__