File Coverage

lib/App/Tel/Expect.pm
Criterion Covered Total %
statement 12 183 6.5
branch 0 68 0.0
condition 0 18 0.0
subroutine 4 6 66.6
pod n/a
total 16 275 5.8


line stmt bran cond sub pod time code
1             package App::Tel::Expect;
2 7     7   22 use strict;
  7         6  
  7         145  
3 7     7   21 use warnings;
  7         7  
  7         164  
4              
5             =head1 NAME
6              
7             App::Tel::Expect - Monkeypatching Expect to support callbacks and large buffer reads
8              
9             =cut
10              
11              
12 7     7   23 use POSIX qw(:sys_wait_h :unistd_h); # For WNOHANG and isatty
  7         8  
  7         35  
13              
14             $Expect::read_buffer_size = 10240;
15              
16             *Expect::set_cb = sub {
17 0     0     my ( $self, $object, $function, $params, @args ) = @_;
18              
19             # Set an escape sequence/function combo for a read handle for interconnect.
20             # Ex: $read_handle->set_seq('',\&function,\@parameters);
21 0           ${ ${*$object}{exp_cb_Function} } = $function;
  0            
  0            
22 0 0 0       if ( ( !defined($function) ) || ( $function eq 'undef' ) ) {
23 0           ${ ${*$object}{exp_cb_Function} } = \&_undef;
  0            
  0            
24             }
25 0           ${ ${*$object}{exp_cb_Parameters} } = $params;
  0            
  0            
26             };
27              
28 7     7   2726 no warnings 'redefine';
  7         9  
  7         7225  
29             *Expect::interconnect = sub {
30 0     0     my (@handles) = @_;
31              
32             # my ($handle)=(shift); call as Expect::interconnect($spawn1,$spawn2,...)
33 0           my ( $nread );
34 0           my ( $rout, $emask, $eout );
35 0           my ( $escape_character_buffer );
36 0           my ( $read_mask, $temp_mask ) = ( '', '' );
37              
38             # Get read/write handles
39 0           foreach my $handle (@handles) {
40 0           $temp_mask = '';
41 0           vec( $temp_mask, $handle->fileno(), 1 ) = 1;
42              
43             # Under Linux w/ 5.001 the next line comes up w/ 'Uninit var.'.
44             # It appears to be impossible to make the warning go away.
45             # doing something like $temp_mask='' unless defined ($temp_mask)
46             # has no effect whatsoever. This may be a bug in 5.001.
47 0           $read_mask = $read_mask | $temp_mask;
48             }
49 0 0         if ($Expect::Debug) {
50 0           print STDERR "Read handles:\r\n";
51 0           foreach my $handle (@handles) {
52 0           print STDERR "\tRead handle: ";
53 0           print STDERR "'${*$handle}{exp_Pty_Handle}'\r\n";
  0            
54 0           print STDERR "\t\tListen Handles:";
55 0           foreach my $write_handle ( @{ ${*$handle}{exp_Listen_Group} } ) {
  0            
  0            
56 0           print STDERR " '${*$write_handle}{exp_Pty_Handle}'";
  0            
57             }
58 0           print STDERR ".\r\n";
59             }
60             }
61              
62             # I think if we don't set raw/-echo here we may have trouble. We don't
63             # want a bunch of echoing crap making all the handles jabber at each other.
64 0           foreach my $handle (@handles) {
65 0 0         unless ( ${*$handle}{"exp_Manual_Stty"} ) {
  0            
66              
67             # This is probably O/S specific.
68 0           ${*$handle}{exp_Stored_Stty} = $handle->exp_stty('-g');
  0            
69 0           print STDERR "Setting tty for ${*$handle}{exp_Pty_Handle} to 'raw -echo'.\r\n"
70 0 0         if ${*$handle}{"exp_Debug"};
  0            
71 0           $handle->exp_stty("raw -echo");
72             }
73 0           foreach my $write_handle ( @{ ${*$handle}{exp_Listen_Group} } ) {
  0            
  0            
74 0 0         unless ( ${*$write_handle}{"exp_Manual_Stty"} ) {
  0            
75 0           ${*$write_handle}{exp_Stored_Stty} =
76 0           $write_handle->exp_stty('-g');
77 0           print STDERR "Setting ${*$write_handle}{exp_Pty_Handle} to 'raw -echo'.\r\n"
78 0 0         if ${*$handle}{"exp_Debug"};
  0            
79 0           $write_handle->exp_stty("raw -echo");
80             }
81             }
82             }
83              
84 0 0         print STDERR "Attempting interconnection\r\n" if $Expect::Debug;
85              
86             # Wait until the process dies or we get EOF
87             # In the case of !${*$handle}{exp_Pid} it means
88             # the handle was exp_inited instead of spawned.
89             CONNECT_LOOP:
90              
91             # Go until we have a reason to stop
92 0           while (1) {
93              
94             # test each handle to see if it's still alive.
95 0           foreach my $read_handle (@handles) {
96 0           waitpid( ${*$read_handle}{exp_Pid}, WNOHANG )
97 0           if ( exists( ${*$read_handle}{exp_Pid} )
98 0 0 0       and ${*$read_handle}{exp_Pid} );
  0            
99 0 0 0       if ( exists( ${*$read_handle}{exp_Pid} )
  0   0        
100 0           and ( ${*$read_handle}{exp_Pid} )
101 0           and ( !kill( 0, ${*$read_handle}{exp_Pid} ) ) )
102             {
103             print STDERR
104 0           "Got EOF (${*$read_handle}{exp_Pty_Handle} died) reading ${*$read_handle}{exp_Pty_Handle}\r\n"
  0            
105 0 0         if ${*$read_handle}{"exp_Debug"};
  0            
106             last CONNECT_LOOP
107 0 0         unless defined( ${ ${*$read_handle}{exp_Function} }{"EOF"} );
  0            
  0            
108             last CONNECT_LOOP
109 0           unless &{ ${ ${*$read_handle}{exp_Function} }{"EOF"} }
  0            
  0            
110 0 0         ( @{ ${ ${*$read_handle}{exp_Parameters} }{"EOF"} } );
  0            
  0            
  0            
111             }
112             }
113              
114             # Every second? No, go until we get something from someone.
115 0           my $nfound = select( $rout = $read_mask, undef, $eout = $emask, undef );
116              
117             # Is there anything to share? May be -1 if interrupted by a signal...
118 0 0 0       next CONNECT_LOOP if not defined $nfound or $nfound < 1;
119              
120             # Which handles have stuff?
121 0           my @bits = split( //, unpack( 'b*', $rout ) );
122 0 0         $eout = 0 unless defined($eout);
123 0           my @ebits = split( //, unpack( 'b*', $eout ) );
124              
125             # print "Ebits: $eout\r\n";
126 0           foreach my $read_handle (@handles) {
127 0 0         if ( $bits[ $read_handle->fileno() ] ) {
128             $nread = sysread(
129 0           $read_handle, ${*$read_handle}{exp_Pty_Buffer},
130 0           $Expect::read_buffer_size
131             );
132              
133 0 0         if (${*$read_handle}{exp_cb_Function}) {
  0            
134 0           &{ ${ ${*$read_handle}{exp_cb_Function} } }( @{ ${ ${*$read_handle}{exp_cb_Parameters} } } )
  0            
  0            
  0            
  0            
  0            
  0            
135             }
136              
137             # Appease perl -w
138 0 0         $nread = 0 unless defined($nread);
139 0           print STDERR "interconnect: read $nread byte(s) from ${*$read_handle}{exp_Pty_Handle}.\r\n"
140 0 0         if ${*$read_handle}{"exp_Debug"} > 1;
  0            
141              
142             # Test for escape seq. before printing.
143             # Appease perl -w
144 0 0         $escape_character_buffer = ''
145             unless defined($escape_character_buffer);
146 0           $escape_character_buffer .= ${*$read_handle}{exp_Pty_Buffer};
  0            
147 0           foreach my $escape_sequence ( keys( %{ ${*$read_handle}{exp_Function} } ) ) {
  0            
  0            
148 0           print STDERR "Tested escape sequence $escape_sequence from ${*$read_handle}{exp_Pty_Handle}"
149 0 0         if ${*$read_handle}{"exp_Debug"} > 1;
  0            
150              
151             # Make sure it doesn't grow out of bounds.
152             $escape_character_buffer = $read_handle->_trim_length(
153             $escape_character_buffer,
154 0           ${*$read_handle}{"exp_Max_Accum"}
155 0 0         ) if ( ${*$read_handle}{"exp_Max_Accum"} );
  0            
156 0 0         if ( $escape_character_buffer =~ /($escape_sequence)/ ) {
157 0           my $match = $1;
158 0 0         if ( ${*$read_handle}{"exp_Debug"} ) {
  0            
159 0           print STDERR
160 0           "\r\ninterconnect got escape sequence from ${*$read_handle}{exp_Pty_Handle}.\r\n";
161              
162             # I'm going to make the esc. seq. pretty because it will
163             # probably contain unprintable characters.
164 0           print STDERR "\tEscape Sequence: '"
165             . _trim_length(
166             undef,
167             _make_readable($escape_sequence)
168             ) . "'\r\n";
169 0           print STDERR "\tMatched by string: '" . _trim_length( undef, _make_readable($match) ) . "'\r\n";
170             }
171              
172             # Print out stuff before the escape.
173             # Keep in mind that the sequence may have been split up
174             # over several reads.
175             # Let's get rid of it from this read. If part of it was
176             # in the last read there's not a lot we can do about it now.
177 0 0         if ( ${*$read_handle}{exp_Pty_Buffer} =~ /([\w\W]*)($escape_sequence)/ ) {
  0            
178 0           $read_handle->_print_handles($1);
179             } else {
180 0           $read_handle->_print_handles( ${*$read_handle}{exp_Pty_Buffer} );
  0            
181             }
182              
183             # Clear the buffer so no more matches can be made and it will
184             # only be printed one time.
185 0           ${*$read_handle}{exp_Pty_Buffer} = '';
  0            
186 0           $escape_character_buffer = '';
187              
188             # Do the function here. Must return non-zero to continue.
189             # More cool syntax. Maybe I should turn these in to objects.
190             last CONNECT_LOOP
191 0           unless &{ ${ ${*$read_handle}{exp_Function} }{$escape_sequence} }
  0            
  0            
192 0 0         ( @{ ${ ${*$read_handle}{exp_Parameters} }{$escape_sequence} } );
  0            
  0            
  0            
193             }
194             }
195 0 0         $nread = 0 unless defined($nread); # Appease perl -w?
196 0           waitpid( ${*$read_handle}{exp_Pid}, WNOHANG )
197 0           if ( defined( ${*$read_handle}{exp_Pid} )
198 0 0 0       && ${*$read_handle}{exp_Pid} );
  0            
199 0 0         if ( $nread == 0 ) {
200 0           print STDERR "Got EOF reading ${*$read_handle}{exp_Pty_Handle}\r\n"
201 0 0         if ${*$read_handle}{"exp_Debug"};
  0            
202             last CONNECT_LOOP
203 0 0         unless defined( ${ ${*$read_handle}{exp_Function} }{"EOF"} );
  0            
  0            
204             last CONNECT_LOOP
205 0           unless &{ ${ ${*$read_handle}{exp_Function} }{"EOF"} }
  0            
  0            
206 0 0         ( @{ ${ ${*$read_handle}{exp_Parameters} }{"EOF"} } );
  0            
  0            
  0            
207             }
208 0 0         last CONNECT_LOOP if ( $nread < 0 ); # This would be an error
209 0           $read_handle->_print_handles( ${*$read_handle}{exp_Pty_Buffer} );
  0            
210             }
211              
212             # I'm removing this because I haven't determined what causes exceptions
213             # consistently.
214 0           if (0) #$ebits[$read_handle->fileno()])
215             {
216             print STDERR "Got Exception reading ${*$read_handle}{exp_Pty_Handle}\r\n"
217             if ${*$read_handle}{"exp_Debug"};
218             last CONNECT_LOOP
219             unless defined( ${ ${*$read_handle}{exp_Function} }{"EOF"} );
220             last CONNECT_LOOP
221             unless &{ ${ ${*$read_handle}{exp_Function} }{"EOF"} }
222             ( @{ ${ ${*$read_handle}{exp_Parameters} }{"EOF"} } );
223             }
224             }
225             }
226 0           foreach my $handle (@handles) {
227 0 0         unless ( ${*$handle}{"exp_Manual_Stty"} ) {
  0            
228 0           $handle->exp_stty( ${*$handle}{exp_Stored_Stty} );
  0            
229             }
230 0           foreach my $write_handle ( @{ ${*$handle}{exp_Listen_Group} } ) {
  0            
  0            
231 0 0         unless ( ${*$write_handle}{"exp_Manual_Stty"} ) {
  0            
232 0           $write_handle->exp_stty( ${*$write_handle}{exp_Stored_Stty} );
  0            
233             }
234             }
235             }
236              
237 0           return;
238             };
239              
240             1;