golden hour
/usr/share/perl5/vendor_perl
⬆️ Go Up
Upload
File/Folder
Size
Actions
App
-
Del
OK
Archive
-
Del
OK
B
-
Del
OK
Bundle
-
Del
OK
Business
-
Del
OK
CGI
-
Del
OK
CGI.pm
255.24 KB
Del
OK
CPAN
-
Del
OK
CPANPLUS
-
Del
OK
CPANPLUS.pm
7.05 KB
Del
OK
Carp
-
Del
OK
Carp.pm
19.93 KB
Del
OK
DBIx
-
Del
OK
Data
-
Del
OK
Date
-
Del
OK
Devel
-
Del
OK
Digest
-
Del
OK
Digest.pm
10.35 KB
Del
OK
Encode
-
Del
OK
Env.pm
5.39 KB
Del
OK
Error
-
Del
OK
Error.pm
24.27 KB
Del
OK
Expect.pm
53.44 KB
Del
OK
Expect.pod
42.71 KB
Del
OK
Exporter
-
Del
OK
Exporter.pm
18.31 KB
Del
OK
ExtUtils
-
Del
OK
Fatal.pm
45.16 KB
Del
OK
File
-
Del
OK
Getopt
-
Del
OK
Git
-
Del
OK
Git.pm
42.55 KB
Del
OK
HTML
-
Del
OK
HTTP
-
Del
OK
IO
-
Del
OK
IPC
-
Del
OK
JSON
-
Del
OK
LWP
-
Del
OK
Locale
-
Del
OK
Log
-
Del
OK
Module
-
Del
OK
Mozilla
-
Del
OK
Net
-
Del
OK
POD2
-
Del
OK
Params
-
Del
OK
Parse
-
Del
OK
Perl
-
Del
OK
Perl4
-
Del
OK
Pod
-
Del
OK
RPC
-
Del
OK
Switch.pm
27.79 KB
Del
OK
TAP
-
Del
OK
Term
-
Del
OK
Test
-
Del
OK
Text
-
Del
OK
Thread
-
Del
OK
Time
-
Del
OK
Try
-
Del
OK
Types
-
Del
OK
Version
-
Del
OK
WWW
-
Del
OK
abbrev.pl
819 B
Del
OK
assert.pl
1.27 KB
Del
OK
autodie
-
Del
OK
autodie.pm
11.83 KB
Del
OK
bigfloat.pl
7.14 KB
Del
OK
bigint.pl
8.71 KB
Del
OK
bigrat.pl
4.35 KB
Del
OK
cacheout.pl
1.1 KB
Del
OK
chat2.pl
9.73 KB
Del
OK
common
-
Del
OK
complete.pl
3.12 KB
Del
OK
constant.pm
13.04 KB
Del
OK
ctime.pl
1.93 KB
Del
OK
dotsh.pl
2.12 KB
Del
OK
exceptions.pl
1.7 KB
Del
OK
fastcwd.pl
1019 B
Del
OK
find.pl
1.16 KB
Del
OK
finddepth.pl
1.1 KB
Del
OK
flush.pl
642 B
Del
OK
ftp.pl
23.53 KB
Del
OK
getcwd.pl
1.37 KB
Del
OK
getopt.pl
1.27 KB
Del
OK
getopts.pl
1.35 KB
Del
OK
hostname.pl
727 B
Del
OK
importenv.pl
283 B
Del
OK
inc
-
Del
OK
lib
-
Del
OK
local
-
Del
OK
look.pl
1.23 KB
Del
OK
newgetopt.pl
2.15 KB
Del
OK
open2.pl
185 B
Del
OK
open3.pl
185 B
Del
OK
parent.pm
2.83 KB
Del
OK
perldoc.pod
8.25 KB
Del
OK
pwd.pl
1.44 KB
Del
OK
shellwords.pl
280 B
Del
OK
stat.pl
525 B
Del
OK
syslog.pl
4.69 KB
Del
OK
tainted.pl
164 B
Del
OK
termcap.pl
4.02 KB
Del
OK
timelocal.pl
690 B
Del
OK
validate.pl
3.64 KB
Del
OK
Edit: Expect.pm
# -*-cperl-*- # Please see the .pod files for documentation. This module is copyrighted # as per the usual perl legalese: # Copyright (c) 1997 Austin Schutz. # expect() interface & functionality enhancements (c) 1999 Roland Giersig. # # All rights reserved. This program is free software; you can # redistribute it and/or modify it under the same terms as Perl # itself. # # Don't blame/flame me if you bust your stuff. # Austin Schutz <ASchutz@users.sourceforge.net> # # This module now is maintained by # Roland Giersig <RGiersig@cpan.org> # use 5.006; # 4 won't cut it. package Expect; use IO::Pty 1.03; # We need make_slave_controlling_terminal() use IO::Tty; use strict 'refs'; use strict 'vars'; use strict 'subs'; use POSIX qw(:sys_wait_h :unistd_h); # For WNOHANG and isatty use Fcntl qw(:DEFAULT); # For checking file handle settings. use Carp qw(cluck croak carp confess); use IO::Handle (); use Exporter (); use Errno; # This is necessary to make routines within Expect work. @Expect::ISA = qw(IO::Pty Exporter); @Expect::EXPORT = qw(expect exp_continue exp_continue_timeout); BEGIN { $Expect::VERSION = '1.21'; # These are defaults which may be changed per object, or set as # the user wishes. # This will be unset, since the default behavior differs between # spawned processes and initialized filehandles. # $Expect::Log_Stdout = 1; $Expect::Log_Group = 1; $Expect::Debug = 0; $Expect::Exp_Max_Accum = 0; # unlimited $Expect::Exp_Internal = 0; $Expect::IgnoreEintr = 0; $Expect::Manual_Stty = 0; $Expect::Multiline_Matching = 1; $Expect::Do_Soft_Close = 0; @Expect::Before_List = (); @Expect::After_List = (); %Expect::Spawned_PIDs = (); } sub version { my($version) = shift; warn "Version $version is later than $Expect::VERSION. It may not be supported" if (defined ($version) && ($version > $Expect::VERSION)); die "Versions before 1.03 are not supported in this release" if ((defined ($version)) && ($version < 1.03)); return $Expect::VERSION; } sub new { my ($class) = shift; $class = ref($class) if ref($class); # so we can be called as $exp->new() # Create the pty which we will use to pass process info. my($self) = new IO::Pty; die "$class: Could not assign a pty" unless $self; bless $self => $class; $self->autoflush(1); # This is defined here since the default is different for # initialized handles as opposed to spawned processes. ${*$self}{exp_Log_Stdout} = 1; $self->_init_vars(); if (@_) { # we got add'l parms, so pass them to spawn return $self->spawn(@_); } return $self; } sub spawn { my ($class) = shift; my $self; if (ref($class)) { $self = $class; } else { $self = $class->new(); } croak "Cannot reuse an object with an already spawned command" if exists ${*$self}{"exp_Command"}; my(@cmd) = @_; # spawn is passed command line args. ${*$self}{"exp_Command"} = \@cmd; # set up pipe to detect childs exec error pipe(FROM_CHILD, TO_PARENT) or die "Cannot open pipe: $!"; pipe(FROM_PARENT, TO_CHILD) or die "Cannot open pipe: $!"; TO_PARENT->autoflush(1); TO_CHILD->autoflush(1); eval { fcntl(TO_PARENT, Fcntl::F_SETFD, Fcntl::FD_CLOEXEC); }; my $pid = fork; unless (defined ($pid)) { warn "Cannot fork: $!" if $^W; return undef; } if($pid) { # parent my $errno; ${*$self}{exp_Pid} = $pid; close TO_PARENT; close FROM_PARENT; $self->close_slave(); $self->set_raw() if $self->raw_pty and isatty($self); close TO_CHILD; # so child gets EOF and can go ahead # now wait for child exec (eof due to close-on-exit) or exec error my $errstatus = sysread(FROM_CHILD, $errno, 256); die "Cannot sync with child: $!" if not defined $errstatus; close FROM_CHILD; if ($errstatus) { $! = $errno+0; warn "Cannot exec(@cmd): $!\n" if $^W; return undef; } } else { # child close FROM_CHILD; close TO_CHILD; $self->make_slave_controlling_terminal(); my $slv = $self->slave() or die "Cannot get slave: $!"; $slv->set_raw() if $self->raw_pty; close($self); # wait for parent before we detach my $buffer; my $errstatus = sysread(FROM_PARENT, $buffer, 256); die "Cannot sync with parent: $!" if not defined $errstatus; close FROM_PARENT; close(STDIN); open(STDIN,"<&". $slv->fileno()) or die "Couldn't reopen STDIN for reading, $!\n"; close(STDOUT); open(STDOUT,">&". $slv->fileno()) or die "Couldn't reopen STDOUT for writing, $!\n"; close(STDERR); open(STDERR,">&". $slv->fileno()) or die "Couldn't reopen STDERR for writing, $!\n"; { exec(@cmd) }; print TO_PARENT $!+0; die "Cannot exec(@cmd): $!\n"; } # This is sort of for code compatibility, and to make debugging a little # easier. By code compatibility I mean that previously the process's # handle was referenced by $process{Pty_Handle} instead of just $process. # This is almost like 'naming' the handle to the process. # I think this also reflects Tcl Expect-like behavior. ${*$self}{exp_Pty_Handle} = "spawn id(".$self->fileno().")"; if ((${*$self}{"exp_Debug"}) or (${*$self}{"exp_Exp_Internal"})) { cluck("Spawned '@cmd'\r\n", "\t${*$self}{exp_Pty_Handle}\r\n", "\tPid: ${*$self}{exp_Pid}\r\n", "\tTty: ".$self->SUPER::ttyname()."\r\n", ); } $Expect::Spawned_PIDs{${*$self}{exp_Pid}} = undef; return $self; } sub exp_init { # take a filehandle, for use later with expect() or interconnect() . # All the functions are written for reading from a tty, so if the naming # scheme looks odd, that's why. my ($class) = shift; my($self) = shift; bless $self, $class; croak "exp_init not passed a file object, stopped" unless defined($self->fileno()); $self->autoflush(1); # Define standard variables.. debug states, etc. $self->_init_vars(); # Turn of logging. By default we don't want crap from a file to get spewed # on screen as we read it. ${*$self}{exp_Log_Stdout} = 0; ${*$self}{exp_Pty_Handle} = "handle id(".$self->fileno().")"; ${*$self}{exp_Pty_Handle} = "STDIN" if $self->fileno() == fileno (STDIN); print STDERR "Initialized ${*$self}{exp_Pty_Handle}.'\r\n" if ${*$self}{"exp_Debug"}; return $self; } # make an alias *init = \&exp_init; ###################################################################### # We're happy OOP people. No direct access to stuff. # For standard read-writeable parameters, we define some autoload magic... my %Writeable_Vars = ( debug => 'exp_Debug', exp_internal => 'exp_Exp_Internal', do_soft_close => 'exp_Do_Soft_Close', max_accum => 'exp_Max_Accum', match_max => 'exp_Max_Accum', notransfer => 'exp_NoTransfer', log_stdout => 'exp_Log_Stdout', log_user => 'exp_Log_Stdout', log_group => 'exp_Log_Group', manual_stty => 'exp_Manual_Stty', restart_timeout_upon_receive => 'exp_Continue', raw_pty => 'exp_Raw_Pty', ); my %Readable_Vars = ( pid => 'exp_Pid', exp_pid => 'exp_Pid', exp_match_number => 'exp_Match_Number', match_number => 'exp_Match_Number', exp_error => 'exp_Error', error => 'exp_Error', exp_command => 'exp_Command', command => 'exp_Command', exp_match => 'exp_Match', match => 'exp_Match', exp_matchlist => 'exp_Matchlist', matchlist => 'exp_Matchlist', exp_before => 'exp_Before', before => 'exp_Before', exp_after => 'exp_After', after => 'exp_After', exp_exitstatus => 'exp_Exit', exitstatus => 'exp_Exit', exp_pty_handle => 'exp_Pty_Handle', pty_handle => 'exp_Pty_Handle', exp_logfile => 'exp_Log_File', logfile => 'exp_Log_File', %Writeable_Vars, ); sub AUTOLOAD { my $self = shift; my $type = ref($self) or croak "$self is not an object"; use vars qw($AUTOLOAD); my $name = $AUTOLOAD; $name =~ s/.*:://; # strip fully-qualified portion unless (exists $Readable_Vars{$name}) { croak "ERROR: cannot find method `$name' in class $type"; } my $varname = $Readable_Vars{$name}; my $tmp; $tmp = ${*$self}{$varname} if exists ${*$self}{$varname}; if (@_) { if (exists $Writeable_Vars{$name}) { my $ref = ref($tmp); if ($ref eq 'ARRAY') { ${*$self}{$varname} = [ @_ ]; } elsif ($ref eq 'HASH') { ${*$self}{$varname} = { @_ }; } else { ${*$self}{$varname} = shift; } } else { carp "Trying to set read-only variable `$name'" if $^W; } } my $ref = ref($tmp); return (wantarray? @{$tmp} : $tmp) if ($ref eq 'ARRAY'); return (wantarray? %{$tmp} : $tmp) if ($ref eq 'HASH'); return $tmp; } ###################################################################### sub set_seq { # Set an escape sequence/function combo for a read handle for interconnect. # Ex: $read_handle->set_seq('',\&function,\@parameters); my($self) = shift; my($escape_sequence,$function) = (shift,shift); ${${*$self}{exp_Function}}{$escape_sequence} = $function; if ((!defined($function)) ||($function eq 'undef')) { ${${*$self}{exp_Function}}{$escape_sequence} = \&_undef; } ${${*$self}{exp_Parameters}}{$escape_sequence} = shift; # This'll be a joy to execute. :) if ( ${*$self}{"exp_Debug"} ) { print STDERR "Escape seq. '" . $escape_sequence; print STDERR "' function for ${*$self}{exp_Pty_Handle} set to '"; print STDERR ${${*$self}{exp_Function}}{$escape_sequence}; print STDERR "(" . join(',', @_) . ")'\r\n"; } } sub set_group { my($self) = shift; my($write_handle); # Make sure we can read from the read handle if (! defined($_[0])) { if (defined (${*$self}{exp_Listen_Group})) { return @{${*$self}{exp_Listen_Group}}; } else { # Refrain from referencing an undef return undef; } } @{${*$self}{exp_Listen_Group}} = (); if ($self->_get_mode() !~ 'r') { warn("Attempting to set a handle group on ${*$self}{exp_Pty_Handle}, ", "a non-readable handle!\r\n"); } while ($write_handle = shift) { if ($write_handle->_get_mode() !~ 'w') { warn("Attempting to set a non-writeable listen handle ", "${*$write_handle}{exp_Pty_handle} for ", "${*$self}{exp_Pty_Handle}!\r\n"); } push (@{${*$self}{exp_Listen_Group}},$write_handle); } } sub log_file { my $self = shift; return(${*$self}{exp_Log_File}) if not @_; # we got no param, return filehandle my $file = shift; my $mode = shift || "a"; if (${*$self}{exp_Log_File} and ref(${*$self}{exp_Log_File}) ne 'CODE') { close(${*$self}{exp_Log_File}); } ${*$self}{exp_Log_File} = undef; return if (not $file); my $fh = $file; if (not ref($file)) { # it's a filename $fh = new IO::File $file, $mode or croak "Cannot open logfile $file: $!"; } if (ref($file) ne 'CODE') { croak "Given logfile doesn't have a 'print' method" if not $fh->can("print"); $fh->autoflush(1); # so logfile is up to date } ${*$self}{exp_Log_File} = $fh; } # I'm going to leave this here in case I might need to change something. # Previously this was calling `stty`, in a most bastardized manner. sub exp_stty { my($self) = shift; my($mode) = "@_"; return undef unless defined($mode); if (not defined $INC{"IO/Stty.pm"}) { carp "IO::Stty not installed, cannot change mode"; return undef; } if (${*$self}{"exp_Debug"}) { print STDERR "Setting ${*$self}{exp_Pty_Handle} to tty mode '$mode'\r\n"; } unless (POSIX::isatty($self)) { if (${*$self}{"exp_Debug"} or $^W) { warn "${*$self}{exp_Pty_Handle} is not a tty. Not changing mode"; } return ''; # No undef to avoid warnings elsewhere. } IO::Stty::stty($self, split(/\s/,$mode)); } *stty = \&exp_stty; # If we want to clear the buffer. Otherwise Accum will grow during send_slow # etc. and contain the remainder after matches. sub clear_accum { my ($self) = shift; my ($temp) = (${*$self}{exp_Accum}); ${*$self}{exp_Accum} = ''; # return the contents of the accumulator. return $temp; } sub set_accum { my ($self) = shift; my ($temp) = (${*$self}{exp_Accum}); ${*$self}{exp_Accum} = shift; # return the contents of the accumulator. return $temp; } ###################################################################### # define constants for pattern subs sub exp_continue() { "exp_continue" } sub exp_continue_timeout() { "exp_continue_timeout" } ###################################################################### # Expect on multiple objects at once. # # Call as Expect::expect($timeout, -i => \@exp_list, @patternlist, # -i => $exp, @pattern_list, ...); # or $exp->expect($timeout, @patternlist, -i => \@exp_list, @patternlist, # -i => $exp, @pattern_list, ...); # # Patterns are arrays that consist of # [ $pattern_type, $pattern, $sub, @subparms ] # # Optional $pattern_type is '-re' (RegExp, default) or '-ex' (exact); # # $sub is optional CODE ref, which is called as &{$sub}($exp, @subparms) # if pattern matched; may return exp_continue or exp_continue_timeout. # # Old-style syntax (pure pattern strings with optional type) also supported. # sub expect { my $self; print STDERR ("expect(@_) called...\n") if $Expect::Debug; if (defined($_[0])) { if (ref($_[0]) and $_[0]->isa('Expect')) { $self = shift; } elsif ($_[0] eq 'Expect') { shift; # or as Expect->expect } } croak "expect(): not enough arguments, should be expect(timeout, [patterns...])" if @_ < 1; my $timeout = shift; my $timeout_hook = undef; my @object_list; my %patterns; my @pattern_list; my @timeout_list; my $curr_list; if ($self) { $curr_list = [$self]; } else { # called directly, so first parameter must be '-i' to establish # object list. $curr_list = []; croak "expect(): ERROR: if called directly (not as \$obj->expect(...), but as Expect::expect(...), first parameter MUST be '-i' to set an object (list) for the patterns to work on." if ($_[0] ne '-i'); } # Let's make a list of patterns wanting to be evaled as regexps. my $parm; my $parm_nr = 1; while (defined($parm = shift)) { print STDERR ("expect(): handling param '$parm'...\n") if $Expect::Debug; if (ref($parm)) { if (ref($parm) eq 'ARRAY') { my $err = _add_patterns_to_list(\@pattern_list, \@timeout_list, $parm_nr, $parm); carp ("expect(): Warning: multiple `timeout' patterns (", scalar(@timeout_list), ").\r\n") if @timeout_list > 1; $timeout_hook = $timeout_list[-1] if $timeout_list[-1]; croak $err if $err; $parm_nr++; } else { croak ("expect(): Unknown pattern ref $parm"); } } else { # not a ref, is an option or raw pattern if (substr($parm, 0, 1) eq '-') { # it's an option print STDERR ("expect(): handling option '$parm'...\n") if $Expect::Debug; if ($parm eq '-i') { # first add collected patterns to object list if (scalar(@$curr_list)) { push @object_list, $curr_list if not exists $patterns{"$curr_list"}; push @{$patterns{"$curr_list"}}, @pattern_list; @pattern_list = (); } # now put parm(s) into current object list if (ref($_[0]) eq 'ARRAY') { $curr_list = shift; } else { $curr_list = [ shift ]; } } elsif ($parm eq '-re' or $parm eq '-ex') { if (ref($_[1]) eq 'CODE') { push @pattern_list, [ $parm_nr, $parm, shift, shift ]; } else { push @pattern_list, [ $parm_nr, $parm, shift, undef ]; } $parm_nr++; } else { croak ("Unknown option $parm"); } } else { # a plain pattern, check if it is followed by a CODE ref if (ref($_[0]) eq 'CODE') { if ($parm eq 'timeout') { push @timeout_list, shift; carp ("expect(): Warning: multiple `timeout' patterns (", scalar(@timeout_list), ").\r\n") if @timeout_list > 1; $timeout_hook = $timeout_list[-1] if $timeout_list[-1]; } elsif ($parm eq 'eof') { push @pattern_list, [ $parm_nr, "-$parm", undef, shift ]; } else { push @pattern_list, [ $parm_nr, '-ex', $parm, shift ]; } } else { print STDERR ("expect(): exact match '$parm'...\n") if $Expect::Debug; push @pattern_list, [ $parm_nr, '-ex', $parm, undef ]; } $parm_nr++; } } } # add rest of collected patterns to object list carp "expect(): Empty object list" unless $curr_list; push @object_list, $curr_list if not exists $patterns{"$curr_list"}; push @{$patterns{"$curr_list"}}, @pattern_list; my $debug = $self ? ${*$self}{exp_Debug} : $Expect::Debug; my $internal = $self ? ${*$self}{exp_Exp_Internal} : $Expect::Exp_Internal; # now start matching... if (@Expect::Before_List) { print STDERR ("Starting BEFORE pattern matching...\r\n") if ($debug or $internal); _multi_expect(0, undef, @Expect::Before_List); } cluck ("Starting EXPECT pattern matching...\r\n") if ($debug or $internal); my @ret; @ret = _multi_expect($timeout, $timeout_hook, map { [$_, @{$patterns{"$_"}}] } @object_list); if (@Expect::After_List) { print STDERR ("Starting AFTER pattern matching...\r\n") if ($debug or $internal); _multi_expect(0, undef, @Expect::After_List); } wantarray ? @ret : $ret[0]; } ###################################################################### # the real workhorse # sub _multi_expect($$@) { my $timeout = shift; my $timeout_hook = shift; if ($timeout_hook) { croak "Unknown timeout_hook type $timeout_hook" unless (ref($timeout_hook) eq 'CODE' or ref($timeout_hook) eq 'ARRAY'); } foreach my $pat (@_) { my @patterns = @{$pat}[1..$#{$pat}]; foreach my $exp (@{$pat->[0]}) { ${*$exp}{exp_New_Data} = 1; # first round we always try to match if (exists ${*$exp}{"exp_Max_Accum"} and ${*$exp}{"exp_Max_Accum"}) { ${*$exp}{exp_Accum} = $exp->_trim_length(${*$exp}{exp_Accum}, ${*$exp}{exp_Max_Accum}); } print STDERR ("${*$exp}{exp_Pty_Handle}: beginning expect.\r\n", "\tTimeout: ", (defined($timeout) ? $timeout : "unlimited" ), " seconds.\r\n", "\tCurrent time: ". localtime(). "\r\n", ) if $Expect::Debug; # What are we expecting? What do you expect? :-) if (${*$exp}{exp_Exp_Internal}) { print STDERR "${*$exp}{exp_Pty_Handle}: list of patterns:\r\n"; foreach my $pattern (@patterns) { print STDERR (' ', defined($pattern->[0])? '#'. $pattern->[0].': ' : '', $pattern->[1], " `", _make_readable($pattern->[2]), "'\r\n"); } print STDERR "\r\n"; } } } my $successful_pattern; my $exp_matched; my $err; my $before; my $after; my $match; my @matchlist; # Set the last loop time to now for time comparisons at end of loop. my $start_loop_time = time(); my $exp_cont = 1; READLOOP: while ($exp_cont) { $exp_cont = 1; $err = ""; my $rmask = ''; my $time_left = undef; if (defined $timeout) { $time_left = $timeout - (time() - $start_loop_time); $time_left = 0 if $time_left < 0; } $exp_matched = undef; # Test for a match first so we can test the current Accum w/out # worrying about an EOF. foreach my $pat (@_) { my @patterns = @{$pat}[1..$#{$pat}]; foreach my $exp (@{$pat->[0]}) { # build mask for select in next section... my $fn = $exp->fileno(); vec($rmask, $fn, 1) = 1 if defined $fn; next unless ${*$exp}{exp_New_Data}; # clear error status ${*$exp}{exp_Error} = undef; # This could be huge. We should attempt to do something # about this. Because the output is used for debugging # I'm of the opinion that showing smaller amounts if the # total is huge should be ok. # Thus the 'trim_length' print STDERR ("\r\n${*$exp}{exp_Pty_Handle}: Does `", $exp->_trim_length(_make_readable(${*$exp}{exp_Accum})), "'\r\nmatch:\r\n") if ${*$exp}{exp_Exp_Internal}; # we don't keep the parameter number anymore # (clashes with before & after), instead the parameter number is # stored inside the pattern; we keep the pattern ref # and look up the number later. foreach my $pattern (@patterns) { print STDERR (" pattern", defined($pattern->[0])? ' #' . $pattern->[0] : '', ": ", $pattern->[1], " `", _make_readable($pattern->[2]), "'? ") if (${*$exp}{exp_Exp_Internal}); # Matching exactly if ($pattern->[1] eq '-ex') { my $match_index = index(${*$exp}{exp_Accum}, $pattern->[2]); # We matched if $match_index > -1 if ($match_index > -1) { $before = substr(${*$exp}{exp_Accum}, 0, $match_index); $match = substr(${*$exp}{exp_Accum}, $match_index, length($pattern->[2])); $after = substr(${*$exp}{exp_Accum}, $match_index + length($pattern->[2])) ; ${*$exp}{exp_Before} = $before; ${*$exp}{exp_Match} = $match; ${*$exp}{exp_After} = $after; ${*$exp}{exp_Match_Number} = $pattern->[0]; $exp_matched = $exp; } } elsif ($pattern->[1] eq '-re') { # m// in array context promises to return an empty list # but doesn't if the pattern doesn't contain brackets (), # so we kludge around by adding an empty bracket # at the end. if ($Expect::Multiline_Matching) { @matchlist = (${*$exp}{exp_Accum} =~ m/$pattern->[2]()/m); ($match, $before, $after) = ($&, $`, $'); } else { @matchlist = (${*$exp}{exp_Accum} =~ m/$pattern->[2]()/); ($match, $before, $after) = ($&, $`, $'); } if (@matchlist) { # Matching regexp ${*$exp}{exp_Before} = $before; ${*$exp}{exp_Match} = $match; ${*$exp}{exp_After} = $after; pop @matchlist; # remove kludged empty bracket from end @{${*$exp}{exp_Matchlist}} = @matchlist; ${*$exp}{exp_Match_Number} = $pattern->[0]; $exp_matched = $exp; } } else { # 'timeout' or 'eof' } if ($exp_matched) { ${*$exp}{exp_Accum} = $after unless ${*$exp}{exp_NoTransfer}; print STDERR "YES!!\r\n" if ${*$exp}{exp_Exp_Internal}; print STDERR (" Before match string: `", $exp->_trim_length(_make_readable(($before))), "'\r\n", " Match string: `", _make_readable($match), "'\r\n", " After match string: `", $exp->_trim_length(_make_readable(($after))), "'\r\n", " Matchlist: (", join(", ", map { "`".$exp->_trim_length(_make_readable(($_)))."'" } @matchlist, ), ")\r\n", ) if (${*$exp}{exp_Exp_Internal}); # call hook function if defined if ($pattern->[3]) { print STDERR ("Calling hook $pattern->[3]...\r\n", ) if (${*$exp}{exp_Exp_Internal} or $Expect::Debug); if ($#{$pattern} > 3) { # call with parameters if given $exp_cont = &{$pattern->[3]}($exp, @{$pattern}[4..$#{$pattern}]); } else { $exp_cont = &{$pattern->[3]}($exp); } } if ($exp_cont and $exp_cont eq exp_continue) { print STDERR ("Continuing expect, restarting timeout...\r\n") if (${*$exp}{exp_Exp_Internal} or $Expect::Debug); $start_loop_time = time(); # restart timeout count next READLOOP; } elsif ($exp_cont and $exp_cont eq exp_continue_timeout) { print STDERR ("Continuing expect...\r\n") if (${*$exp}{exp_Exp_Internal} or $Expect::Debug); next READLOOP; } last READLOOP; } print STDERR "No.\r\n" if ${*$exp}{exp_Exp_Internal}; } print STDERR "\r\n" if ${*$exp}{exp_Exp_Internal}; # don't have to match again until we get new data ${*$exp}{exp_New_Data} = 0; } } # End of matching section # No match, let's see what is pending on the filehandles... print STDERR ("Waiting for new data (", defined($time_left)? $time_left : 'unlimited', " seconds)...\r\n", ) if ($Expect::Exp_Internal or $Expect::Debug); my $nfound; SELECT: { $nfound = select($rmask, undef, undef, $time_left); if ($nfound < 0) { if ($!{EINTR} and $Expect::IgnoreEintr) { print STDERR ("ignoring EINTR, restarting select()...\r\n") if ($Expect::Exp_Internal or $Expect::Debug); next SELECT; } print STDERR ("select() returned error code '$!'\r\n") if ($Expect::Exp_Internal or $Expect::Debug); # returned error $err = "4:$!"; last READLOOP; } } # go until we don't find something (== timeout). if ($nfound == 0) { # No pattern, no EOF. Did we time out? $err = "1:TIMEOUT"; foreach my $pat (@_) { foreach my $exp (@{$pat->[0]}) { $before = ${*$exp}{exp_Before} = ${*$exp}{exp_Accum}; next if not defined $exp->fileno(); # skip already closed ${*$exp}{exp_Error} = $err unless ${*$exp}{exp_Error}; } } print STDERR ("TIMEOUT\r\n") if ($Expect::Debug or $Expect::Exp_Internal); if ($timeout_hook) { my $ret; print STDERR ("Calling timeout function $timeout_hook...\r\n") if ($Expect::Debug or $Expect::Exp_Internal); if (ref($timeout_hook) eq 'CODE') { $ret = &{$timeout_hook}($_[0]->[0]); } else { if ($#{$timeout_hook} > 3) { $ret = &{$timeout_hook->[3]}($_[0]->[0], @{$timeout_hook}[4..$#{$timeout_hook}]); } else { $ret = &{$timeout_hook->[3]}($_[0]->[0]); } } if ($ret and $ret eq exp_continue) { $start_loop_time = time(); # restart timeout count next READLOOP; } } last READLOOP; } my @bits = split(//,unpack('b*',$rmask)); foreach my $pat (@_) { foreach my $exp (@{$pat->[0]}) { next if not defined $exp->fileno(); # skip already closed if ($bits[$exp->fileno()]) { print STDERR ("${*$exp}{exp_Pty_Handle}: new data.\r\n") if $Expect::Debug; # read in what we found. my $buffer; my $nread = sysread($exp, $buffer, 2048); # Make errors (nread undef) show up as EOF. $nread = 0 unless defined ($nread); if ($nread == 0) { print STDERR ("${*$exp}{exp_Pty_Handle}: EOF\r\n") if ($Expect::Debug); $before = ${*$exp}{exp_Before} = $exp->clear_accum(); $err = "2:EOF"; ${*$exp}{exp_Error} = $err; ${*$exp}{exp_Has_EOF} = 1; $exp_cont = undef; foreach my $eof_pat (grep {$_->[1] eq '-eof'} @{$pat}[1..$#{$pat}]) { my $ret; print STDERR ("Calling EOF hook $eof_pat->[3]...\r\n", ) if ($Expect::Debug); if ($#{$eof_pat} > 3) { # call with parameters if given $ret = &{$eof_pat->[3]}($exp, @{$eof_pat}[4..$#{$eof_pat}]); } else { $ret = &{$eof_pat->[3]}($exp); } if ($ret and ($ret eq exp_continue or $ret eq exp_continue_timeout)) { $exp_cont = $ret; } } # is it dead? if (defined(${*$exp}{exp_Pid})) { my $ret = waitpid(${*$exp}{exp_Pid}, POSIX::WNOHANG); if ($ret == ${*$exp}{exp_Pid}) { printf STDERR ("%s: exit(0x%02X)\r\n", ${*$exp}{exp_Pty_Handle}, $?) if ($Expect::Debug); $err = "3:Child PID ${*$exp}{exp_Pid} exited with status $?"; ${*$exp}{exp_Error} = $err; ${*$exp}{exp_Exit} = $?; delete $Expect::Spawned_PIDs{${*$exp}{exp_Pid}}; ${*$exp}{exp_Pid} = undef; } } print STDERR ("${*$exp}{exp_Pty_Handle}: closing...\r\n") if ($Expect::Debug); $exp->hard_close(); next; } print STDERR ("${*$exp}{exp_Pty_Handle}: read $nread byte(s).\r\n") if ($Expect::Debug); # ugly hack for broken solaris ttys that spew <blank><backspace> # into our pretty output $buffer =~ s/ \cH//g if not ${*$exp}{exp_Raw_Pty}; # Append it to the accumulator. ${*$exp}{exp_Accum} .= $buffer; if (exists ${*$exp}{exp_Max_Accum} and ${*$exp}{exp_Max_Accum}) { ${*$exp}{exp_Accum} = $exp->_trim_length(${*$exp}{exp_Accum}, ${*$exp}{exp_Max_Accum}); } ${*$exp}{exp_New_Data} = 1; # next round we try to match again $exp_cont = exp_continue if (exists ${*$exp}{exp_Continue} and ${*$exp}{exp_Continue}); # Now propagate what we have read to other listeners... $exp->_print_handles($buffer); # End handle reading section. } } } # end read loop $start_loop_time = time() # restart timeout count if ($exp_cont and $exp_cont eq exp_continue); } # End READLOOP # Post loop. Do we have anything? # Tell us status if ($Expect::Debug or $Expect::Exp_Internal) { if ($exp_matched) { print STDERR ("Returning from expect ", ${*$exp_matched}{exp_Error} ? 'un' : '', "successfully.", ${*$exp_matched}{exp_Error} ? "\r\n Error: ${*$exp_matched}{exp_Error}." : '', "\r\n"); } else { print STDERR ("Returning from expect with TIMEOUT or EOF\r\n"); } if ($Expect::Debug and $exp_matched) { print STDERR " ${*$exp_matched}{exp_Pty_Handle}: accumulator: `"; if (${*$exp_matched}{exp_Error}) { print STDERR ($exp_matched->_trim_length (_make_readable(${*$exp_matched}{exp_Before})), "'\r\n"); } else { print STDERR ($exp_matched->_trim_length (_make_readable(${*$exp_matched}{exp_Accum})), "'\r\n"); } } } if ($exp_matched) { return wantarray? (${*$exp_matched}{exp_Match_Number}, ${*$exp_matched}{exp_Error}, ${*$exp_matched}{exp_Match}, ${*$exp_matched}{exp_Before}, ${*$exp_matched}{exp_After}, $exp_matched, ) : ${*$exp_matched}{exp_Match_Number}; } return wantarray? (undef, $err, undef, $before, undef, undef) : undef; } # Patterns are arrays that consist of # [ $pattern_type, $pattern, $sub, @subparms ] # optional $pattern_type is '-re' (RegExp, default) or '-ex' (exact); # $sub is optional CODE ref, which is called as &{$sub}($exp, @subparms) # if pattern matched; # the $parm_nr gets unshifted onto the array for reporting purposes. sub _add_patterns_to_list($$$@) { my $listref = shift; my $timeoutlistref = shift; # gets timeout patterns my $store_parm_nr = shift; my $parm_nr = $store_parm_nr || 1; foreach my $parm (@_) { if (not ref($parm) eq 'ARRAY') { return "Parameter #$parm_nr is not an ARRAY ref."; } $parm = [@$parm]; # make copy if ($parm->[0] =~ m/\A-/) { # it's an option if ($parm->[0] ne '-re' and $parm->[0] ne '-ex') { return "Unknown option $parm->[0] in pattern #$parm_nr"; } } else { if ($parm->[0] eq 'timeout') { if (defined $timeoutlistref) { splice @$parm, 0, 1, ("-$parm->[0]", undef); unshift @$parm, $store_parm_nr? $parm_nr: undef; push @$timeoutlistref, $parm; } next; } elsif ($parm->[0] eq 'eof') { splice @$parm, 0, 1, ("-$parm->[0]", undef); } else { unshift @$parm, '-re'; # defaults to RegExp } } if (@$parm > 2) { if (ref($parm->[2]) ne 'CODE') { croak ("Pattern #$parm_nr doesn't have a CODE reference", "after the pattern."); } } else { push @$parm, undef; # make sure we have three elements } unshift @$parm, $store_parm_nr? $parm_nr: undef; push @$listref, $parm; $parm_nr++ } return undef; } ###################################################################### # $process->interact([$in_handle],[$escape sequence]) # If you don't specify in_handle STDIN will be used. sub interact { my ($self) = (shift); my ($infile) = (shift); my ($escape_sequence) = shift; my ($in_object,$in_handle,@old_group,$return_value); my ($old_manual_stty_val,$old_log_stdout_val); my ($outfile,$out_object); @old_group = $self->set_group(); # If the handle is STDIN we'll # $infile->fileno == 0 should be stdin.. follow stdin rules. no strict 'subs'; # Allow bare word 'STDIN' unless (defined($infile)) { # We need a handle object Associated with STDIN. $infile = new IO::File; $infile->IO::File::fdopen(STDIN,'r'); $outfile = new IO::File; $outfile->IO::File::fdopen(STDOUT,'w'); } elsif (fileno($infile) == fileno(STDIN)) { # With STDIN we want output to go to stdout. $outfile = new IO::File; $outfile->IO::File::fdopen(STDOUT,'w'); } else { undef ($outfile); } # Here we assure ourselves we have an Expect object. $in_object = Expect->exp_init($infile); if (defined($outfile)) { # as above.. we want output to go to stdout if we're given stdin. $out_object = Expect->exp_init($outfile); $out_object->manual_stty(1); $self->set_group($out_object); } else { $self->set_group($in_object); } $in_object->set_group($self); $in_object->set_seq($escape_sequence,undef) if defined($escape_sequence); # interconnect normally sets stty -echo raw. Interact really sort # of implies we don't do that by default. If anyone wanted to they could # set it before calling interact, of use interconnect directly. $old_manual_stty_val = $self->manual_stty(); $self->manual_stty(1); # I think this is right. Don't send stuff from in_obj to stdout by default. # in theory whatever 'self' is should echo what's going on. $old_log_stdout_val = $self->log_stdout(); $self->log_stdout(0); $in_object->log_stdout(0); # Allow for the setting of an optional EOF escape function. # $in_object->set_seq('EOF',undef); # $self->set_seq('EOF',undef); Expect::interconnect($self,$in_object); $self->log_stdout($old_log_stdout_val); $self->set_group(@old_group); # If old_group was undef, make sure that occurs. This is a slight hack since # it modifies the value directly. # Normally an undef passed to set_group will return the current groups. # It is possible that it may be of worth to make it possible to undef # The current group without doing this. unless (@old_group) { @{${*$self}{exp_Listen_Group}} = (); } $self->manual_stty($old_manual_stty_val); return $return_value; } sub interconnect { # my ($handle)=(shift); call as Expect::interconnect($spawn1,$spawn2,...) my ($rmask,$nfound,$nread); my ($rout, @bits, $emask, $eout, @ebits ) = (); my ($escape_sequence,$escape_character_buffer); my (@handles) = @_; my ($handle,$read_handle,$write_handle); my ($read_mask,$temp_mask) = ('',''); # Get read/write handles foreach $handle (@handles) { $temp_mask = ''; vec($temp_mask,$handle->fileno(),1) = 1; # Under Linux w/ 5.001 the next line comes up w/ 'Uninit var.'. # It appears to be impossible to make the warning go away. # doing something like $temp_mask='' unless defined ($temp_mask) # has no effect whatsoever. This may be a bug in 5.001. $read_mask = $read_mask | $temp_mask; } if ($Expect::Debug) { print STDERR "Read handles:\r\n"; foreach $handle (@handles) { print STDERR "\tRead handle: "; print STDERR "'${*$handle}{exp_Pty_Handle}'\r\n"; print STDERR "\t\tListen Handles:"; foreach $write_handle (@{${*$handle}{exp_Listen_Group}}) { print STDERR " '${*$write_handle}{exp_Pty_Handle}'"; } print STDERR ".\r\n"; } } # I think if we don't set raw/-echo here we may have trouble. We don't # want a bunch of echoing crap making all the handles jabber at each other. foreach $handle (@handles) { unless (${*$handle}{"exp_Manual_Stty"}) { # This is probably O/S specific. ${*$handle}{exp_Stored_Stty} = $handle->exp_stty('-g'); print STDERR "Setting tty for ${*$handle}{exp_Pty_Handle} to 'raw -echo'.\r\n"if ${*$handle}{"exp_Debug"}; $handle->exp_stty("raw -echo"); } foreach $write_handle (@{${*$handle}{exp_Listen_Group}}) { unless (${*$write_handle}{"exp_Manual_Stty"}) { ${*$write_handle}{exp_Stored_Stty} = $write_handle->exp_stty('-g'); print STDERR "Setting ${*$write_handle}{exp_Pty_Handle} to 'raw -echo'.\r\n"if ${*$handle}{"exp_Debug"}; $write_handle->exp_stty("raw -echo"); } } } print STDERR "Attempting interconnection\r\n" if $Expect::Debug; # Wait until the process dies or we get EOF # In the case of !${*$handle}{exp_Pid} it means # the handle was exp_inited instead of spawned. CONNECT_LOOP: # Go until we have a reason to stop while (1) { # test each handle to see if it's still alive. foreach $read_handle (@handles) { waitpid(${*$read_handle}{exp_Pid}, WNOHANG) if (exists (${*$read_handle}{exp_Pid}) and ${*$read_handle}{exp_Pid}); if (exists(${*$read_handle}{exp_Pid}) and (${*$read_handle}{exp_Pid}) and (! kill(0,${*$read_handle}{exp_Pid}))) { print STDERR "Got EOF (${*$read_handle}{exp_Pty_Handle} died) reading ${*$read_handle}{exp_Pty_Handle}\r\n" if ${*$read_handle}{"exp_Debug"}; last CONNECT_LOOP unless defined(${${*$read_handle}{exp_Function}}{"EOF"}); last CONNECT_LOOP unless &{${${*$read_handle}{exp_Function}}{"EOF"}}(@{${${*$read_handle}{exp_Parameters}}{"EOF"}}); } } # Every second? No, go until we get something from someone. ($nfound) = select($rout = $read_mask, undef, $eout = $emask, undef); # Is there anything to share? May be -1 if interrupted by a signal... next CONNECT_LOOP if not defined $nfound or $nfound < 1; # Which handles have stuff? @bits = split(//,unpack('b*',$rout)); $eout = 0 unless defined ($eout); @ebits = split(//,unpack('b*',$eout)); # print "Ebits: $eout\r\n"; foreach $read_handle (@handles) { if ($bits[$read_handle->fileno()]) { $nread = sysread( $read_handle, ${*$read_handle}{exp_Pty_Buffer}, 1024 ); # Appease perl -w $nread = 0 unless defined ($nread); print STDERR "interconnect: read $nread byte(s) from ${*$read_handle}{exp_Pty_Handle}.\r\n" if ${*$read_handle}{"exp_Debug"} > 1; # Test for escape seq. before printing. # Appease perl -w $escape_character_buffer = '' unless defined ($escape_character_buffer); $escape_character_buffer .= ${*$read_handle}{exp_Pty_Buffer}; foreach $escape_sequence (keys(%{${*$read_handle}{exp_Function}})) { print STDERR "Tested escape sequence $escape_sequence from ${*$read_handle}{exp_Pty_Handle}"if ${*$read_handle}{"exp_Debug"} > 1; # Make sure it doesn't grow out of bounds. $escape_character_buffer = $read_handle->_trim_length($escape_character_buffer,${*$read_handle}{"exp_Max_Accum"}) if (${*$read_handle}{"exp_Max_Accum"}); if ($escape_character_buffer =~ /($escape_sequence)/) { if (${*$read_handle}{"exp_Debug"}) { print STDERR "\r\ninterconnect got escape sequence from ${*$read_handle}{exp_Pty_Handle}.\r\n"; # I'm going to make the esc. seq. pretty because it will # probably contain unprintable characters. print STDERR "\tEscape Sequence: '"._trim_length(undef,_make_readable($escape_sequence))."'\r\n"; print STDERR "\tMatched by string: '"._trim_length(undef,_make_readable($&))."'\r\n"; } # Print out stuff before the escape. # Keep in mind that the sequence may have been split up # over several reads. # Let's get rid of it from this read. If part of it was # in the last read there's not a lot we can do about it now. if (${*$read_handle}{exp_Pty_Buffer} =~ /($escape_sequence)/) { $read_handle->_print_handles($`); } else { $read_handle->_print_handles(${*$read_handle}{exp_Pty_Buffer}) } # Clear the buffer so no more matches can be made and it will # only be printed one time. ${*$read_handle}{exp_Pty_Buffer} = ''; $escape_character_buffer = ''; # Do the function here. Must return non-zero to continue. # More cool syntax. Maybe I should turn these in to objects. last CONNECT_LOOP unless &{${${*$read_handle}{exp_Function}}{$escape_sequence}}(@{${${*$read_handle}{exp_Parameters}}{$escape_sequence}}); } } $nread = 0 unless defined($nread); # Appease perl -w? waitpid(${*$read_handle}{exp_Pid}, WNOHANG) if (defined (${*$read_handle}{exp_Pid}) &&${*$read_handle}{exp_Pid}); if ($nread == 0) { print STDERR "Got EOF reading ${*$read_handle}{exp_Pty_Handle}\r\n"if ${*$read_handle}{"exp_Debug"}; last CONNECT_LOOP unless defined(${${*$read_handle}{exp_Function}}{"EOF"}); last CONNECT_LOOP unless &{${${*$read_handle}{exp_Function}}{"EOF"}}(@{${${*$read_handle}{exp_Parameters}}{"EOF"}}); } last CONNECT_LOOP if ($nread < 0); # This would be an error $read_handle->_print_handles(${*$read_handle}{exp_Pty_Buffer}); } # I'm removing this because I haven't determined what causes exceptions # consistently. if (0) #$ebits[$read_handle->fileno()]) { print STDERR "Got Exception reading ${*$read_handle}{exp_Pty_Handle}\r\n"if ${*$read_handle}{"exp_Debug"}; last CONNECT_LOOP unless defined(${${*$read_handle}{exp_Function}}{"EOF"}); last CONNECT_LOOP unless &{${${*$read_handle}{exp_Function}}{"EOF"}}(@{${${*$read_handle}{exp_Parameters}}{"EOF"}}); } } } foreach $handle (@handles) { unless (${*$handle}{"exp_Manual_Stty"}) { $handle->exp_stty(${*$handle}{exp_Stored_Stty}); } foreach $write_handle (@{${*$handle}{exp_Listen_Group}}) { unless (${*$write_handle}{"exp_Manual_Stty"}) { $write_handle->exp_stty(${*$write_handle}{exp_Stored_Stty}); } } } } # user can decide if log output gets also sent to logfile sub print_log_file { my $self = shift; if (${*$self}{exp_Log_File}) { if (ref(${*$self}{exp_Log_File}) eq 'CODE') { ${*$self}{exp_Log_File}->(@_); } else { ${*$self}{exp_Log_File}->print(@_); } } } # we provide our own print so we can debug what gets sent to the # processes... sub print (@) { my ($self, @args) = @_; return if not defined $self->fileno(); # skip if closed if (${*$self}{exp_Exp_Internal}) { my $args = _make_readable(join('', @args)); cluck "Sending '$args' to ${*$self}{exp_Pty_Handle}\r\n"; } foreach my $arg (@args) { while (length($arg) > 80) { $self->SUPER::print(substr($arg, 0, 80)); $arg = substr($arg, 80); } $self->SUPER::print($arg); } } # make an alias for Tcl/Expect users for a DWIM experience... *send = \&print; # This is an Expect standard. It's nice for talking to modems and the like # where from time to time they get unhappy if you send items too quickly. sub send_slow{ my ($self) = shift; my($char,@linechars,$nfound,$rmask); return if not defined $self->fileno(); # skip if closed my($sleep_time) = shift; # Flushing makes it so each character can be seen separately. my $chunk; while ($chunk = shift) { @linechars = split ('', $chunk); foreach $char (@linechars) { # How slow? select (undef,undef,undef,$sleep_time); print $self $char; print STDERR "Printed character \'"._make_readable($char)."\' to ${*$self}{exp_Pty_Handle}.\r\n" if ${*$self}{"exp_Debug"} > 1; # I think I can get away with this if I save it in accum if (${*$self}{"exp_Log_Stdout"} ||${*$self}{exp_Log_Group}) { $rmask = ""; vec($rmask,$self->fileno(),1) = 1; # .01 sec granularity should work. If we miss something it will # probably get flushed later, maybe in an expect call. while (select($rmask,undef,undef,.01)) { my $ret = sysread($self,${*$self}{exp_Pty_Buffer},1024); last if not defined $ret or $ret == 0; # Is this necessary to keep? Probably.. # # if you need to expect it later. ${*$self}{exp_Accum}.= ${*$self}{exp_Pty_Buffer}; ${*$self}{exp_Accum} = $self->_trim_length(${*$self}{exp_Accum},${*$self}{"exp_Max_Accum"}) if (${*$self}{"exp_Max_Accum"}); $self->_print_handles(${*$self}{exp_Pty_Buffer}); print STDERR "Received \'".$self->_trim_length(_make_readable($char))."\' from ${*$self}{exp_Pty_Handle}\r\n" if ${*$self}{"exp_Debug"} > 1; } } } } } sub test_handles { # This should be called by Expect::test_handles($timeout,@objects); my ($rmask, $allmask, $rout, $nfound, @bits); my ($timeout) = shift; my (@handle_list) = @_; my($handle); foreach $handle (@handle_list) { $rmask = ''; vec($rmask,$handle->fileno(),1) = 1; $allmask = '' unless defined ($allmask); $allmask = $allmask | $rmask; } ($nfound) = select($rout = $allmask, undef, undef, $timeout); return () unless $nfound; # Which handles have stuff? @bits = split(//,unpack('b*',$rout)); my $handle_num = 0; my @return_list = (); foreach $handle (@handle_list) { # I go to great lengths to get perl -w to shut the hell up. if (defined($bits[$handle->fileno()]) and ($bits[$handle->fileno()])) { push(@return_list,$handle_num); } } continue { $handle_num++; } return (@return_list); } # Be nice close. This should emulate what an interactive shell does after a # command finishes... sort of. We're not as patient as a shell. sub soft_close { my($self) = shift; my($nfound,$nread,$rmask,$returned_pid); my($end_time,$select_time,$temp_buffer); my($close_status); # Give it 15 seconds to cough up an eof. cluck "Closing ${*$self}{exp_Pty_Handle}.\r\n" if ${*$self}{exp_Debug}; return -1 if not defined $self->fileno(); # skip if handle already closed unless (exists ${*$self}{exp_Has_EOF} and ${*$self}{exp_Has_EOF}) { $end_time = time() + 15; while ($end_time > time()) { $select_time = $end_time - time(); # Sanity check. $select_time = 0 if $select_time < 0; $rmask = ''; vec($rmask,$self->fileno(),1) = 1; ($nfound) = select($rmask,undef,undef,$select_time); last unless (defined($nfound) && $nfound); $nread = sysread($self,$temp_buffer,8096); # 0 = EOF. unless (defined($nread) && $nread) { print STDERR "Got EOF from ${*$self}{exp_Pty_Handle}.\r\n" if ${*$self}{exp_Debug}; last; } $self->_print_handles($temp_buffer); } if (($end_time <= time()) && ${*$self}{exp_Debug}) { print STDERR "Timed out waiting for an EOF from ${*$self}{exp_Pty_Handle}.\r\n"; } } if ( ($close_status = $self->close()) && ${*$self}{exp_Debug}) { print STDERR "${*$self}{exp_Pty_Handle} closed.\r\n"; } # quit now if it isn't a process. return $close_status unless defined(${*$self}{exp_Pid}); # Now give it 15 seconds to die. $end_time = time() + 15; while ($end_time > time()) { $returned_pid = waitpid(${*$self}{exp_Pid}, &WNOHANG); # Stop here if the process dies. if (defined($returned_pid) && $returned_pid) { delete $Expect::Spawned_PIDs{$returned_pid}; if (${*$self}{exp_Debug}) { printf STDERR ("Pid %d of %s exited, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $?); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return ${*$self}{exp_Exit}; } sleep 1; # Keep loop nice. } # Send it a term if it isn't dead. if (${*$self}{exp_Debug}) { print STDERR "${*$self}{exp_Pty_Handle} not exiting, sending TERM.\r\n"; } kill TERM => ${*$self}{exp_Pid}; # Now to be anal retentive.. wait 15 more seconds for it to die. $end_time = time() + 15; while ($end_time > time()) { $returned_pid = waitpid(${*$self}{exp_Pid}, &WNOHANG); if (defined($returned_pid) && $returned_pid) { delete $Expect::Spawned_PIDs{$returned_pid}; if (${*$self}{exp_Debug}) { printf STDERR ("Pid %d of %s terminated, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $?); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return $?; } sleep 1; } # Since this is a 'soft' close, sending it a -9 would be inappropriate. return undef; } # 'Make it go away' close. sub hard_close { my($self) = shift; my($nfound,$nread,$rmask,$returned_pid); my($end_time,$select_time,$temp_buffer); my($close_status); cluck "Closing ${*$self}{exp_Pty_Handle}.\r\n" if ${*$self}{exp_Debug}; # Don't wait for an EOF. if ( ($close_status = $self->close()) && ${*$self}{exp_Debug}) { print STDERR "${*$self}{exp_Pty_Handle} closed.\r\n"; } # Return now if handle. return $close_status unless defined(${*$self}{exp_Pid}); # Now give it 5 seconds to die. Less patience here if it won't die. $end_time = time() + 5; while ($end_time > time()) { $returned_pid = waitpid(${*$self}{exp_Pid}, &WNOHANG); # Stop here if the process dies. if (defined($returned_pid) && $returned_pid) { delete $Expect::Spawned_PIDs{$returned_pid}; if (${*$self}{exp_Debug}) { printf STDERR ("Pid %d of %s terminated, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $?); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return ${*$self}{exp_Exit}; } sleep 1; # Keep loop nice. } # Send it a term if it isn't dead. if (${*$self}{exp_Debug}) { print STDERR "${*$self}{exp_Pty_Handle} not exiting, sending TERM.\r\n"; } kill TERM => ${*$self}{exp_Pid}; # wait 15 more seconds for it to die. $end_time = time() + 15; while ($end_time > time()) { $returned_pid = waitpid(${*$self}{exp_Pid}, &WNOHANG); if (defined($returned_pid) && $returned_pid) { delete $Expect::Spawned_PIDs{$returned_pid}; if (${*$self}{exp_Debug}) { printf STDERR ("Pid %d of %s terminated, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $?); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return ${*$self}{exp_Exit}; } sleep 1; } kill KILL => ${*$self}{exp_Pid}; # wait 5 more seconds for it to die. $end_time = time() + 5; while ($end_time > time()) { $returned_pid = waitpid(${*$self}{exp_Pid}, &WNOHANG); if (defined($returned_pid) && $returned_pid) { delete $Expect::Spawned_PIDs{$returned_pid}; if (${*$self}{exp_Debug}) { printf STDERR ("Pid %d of %s killed, Status: 0x%02X\r\n", ${*$self}{exp_Pid}, ${*$self}{exp_Pty_Handle}, $?); } ${*$self}{exp_Pid} = undef; ${*$self}{exp_Exit} = $?; return ${*$self}{exp_Exit}; } sleep 1; } warn "Pid ${*$self}{exp_Pid} of ${*$self}{exp_Pty_Handle} is HUNG.\r\n"; ${*$self}{exp_Pid} = undef; return undef; } # These should not be called externally. sub _init_vars { my($self) = shift; # for every spawned process or filehandle. ${*$self}{exp_Log_Stdout} = $Expect::Log_Stdout if defined ($Expect::Log_Stdout); ${*$self}{exp_Log_Group} = $Expect::Log_Group; ${*$self}{exp_Debug} = $Expect::Debug; ${*$self}{exp_Exp_Internal} = $Expect::Exp_Internal; ${*$self}{exp_Manual_Stty} = $Expect::Manual_Stty; ${*$self}{exp_Stored_Stty} = 'sane'; ${*$self}{exp_Do_Soft_Close} = $Expect::Do_Soft_Close; # sysread doesn't like my or local vars. ${*$self}{exp_Pty_Buffer} = ''; # Initialize accumulator. ${*$self}{exp_Max_Accum} = $Expect::Exp_Max_Accum; ${*$self}{exp_Accum} = ''; ${*$self}{exp_NoTransfer} = 0; # create empty expect_before & after lists ${*$self}{exp_expect_before_list} = []; ${*$self}{exp_expect_after_list} = []; } sub _make_readable { my $s = shift; $s = '' if not defined ($s); study $s; # Speed things up? $s =~ s/\\/\\\\/g; # So we can tell easily(?) what is a backslash $s =~ s/\n/\\n/g; $s =~ s/\r/\\r/g; $s =~ s/\t/\\t/g; $s =~ s/\'/\\\'/g; # So we can tell whassa quote and whassa notta quote. $s =~ s/\"/\\\"/g; # Formfeed (does anyone use formfeed?) $s =~ s/\f/\\f/g; $s =~ s/\010/\\b/g; # escape control chars high/low, but allow ISO 8859-1 chars $s =~ s/[\000-\037\177-\237\377]/sprintf("\\%03lo",ord($&))/ge; return $s; } sub _trim_length { # This is sort of a reverse truncation function # Mostly so we don't have to see the full output when we're using # Also used if Max_Accum gets set to limit the size of the accumulator # for matching functions. # exp_internal my($self) = shift; my($string) = shift; my($length) = shift; # If we're not passed a length (_trim_length is being used for debugging # purposes) AND debug >= 3, don't trim. return($string) if (defined ($self) and ${*$self}{"exp_Debug"} >= 3 and (!(defined($length)))); my($indicate_truncation) = '...' unless $length; $length = 1021 unless $length; return($string) unless $length < length($string); # We wouldn't want the accumulator to begin with '...' if max_accum is passed # This is because this funct. gets called internally w/ max_accum # and is also used to print information back to the user. return $indicate_truncation.substr($string,(length($string) - $length),$length); } sub _print_handles { # Given crap from 'self' and the handles self wants to print to, print to # them. these are indicated by the handle's 'group' my($self) = shift; my($print_this) = shift; my($handle); if (${*$self}{exp_Log_Group}) { foreach $handle (@{${*$self}{exp_Listen_Group}}) { $print_this = '' unless defined ($print_this); # Appease perl -w print STDERR "Printed '".$self->_trim_length(_make_readable($print_this))."' to ${*$handle}{exp_Pty_Handle} from ${*$self}{exp_Pty_Handle}.\r\n" if (${*$handle}{"exp_Debug"} > 1); print $handle $print_this; } } # If ${*$self}{exp_Pty_Handle} is STDIN this would make it echo. print STDOUT $print_this if ${*$self}{"exp_Log_Stdout"}; $self->print_log_file($print_this); $|= 1; # This should not be necessary but autoflush() doesn't always work. } sub _get_mode { my($fcntl_flags) = ''; my($handle) = shift; # What mode are we opening with? use fcntl to find out. $fcntl_flags = fcntl(\*{$handle},Fcntl::F_GETFL,$fcntl_flags); die "fcntl returned undef during exp_init of $handle, $!\r\n" unless defined($fcntl_flags); if ($fcntl_flags | (Fcntl::O_RDWR)) { return 'rw'; } elsif ($fcntl_flags | (Fcntl::O_WRONLY)) { return 'w' } else { # Under Solaris (among others?) O_RDONLY is implemented as 0. so |O_RDONLY would fail. return 'r'; } } sub _undef { return undef; # Seems a little retarded but &CORE::undef fails in interconnect. # This is used for the default escape sequence function. # w/out the leading & it won't compile. } # clean up child processes sub DESTROY { my $status = $?; # save this as it gets mangled by the terminating spawned children my $self = shift; if (${*$self}{exp_Do_Soft_Close}) { $self->soft_close(); } $self->hard_close(); $? = $status; # restore it. otherwise deleting an Expect object may mangle $?, which is unintuitive } 1;
Save