our $debug = 0; our $edebug = 0; our $sdebug = 0; { package IO::Event; our $VERSION = 0.813; use strict; no strict 'refs'; use warnings; use Carp qw(confess); our $base; our @ISA; sub idle { IO::Event->import('no_emulate_Event') unless $base; &{$base . "::idle"}(@_); } sub loop { IO::Event->import('no_emulate_Event') unless $base; &{$base . "::loop"}(@_); } sub unloop { &{$base . "::unloop"}(@_); } sub unloop_all { &{$base . "::unloop_all"}(@_); } sub timer { shift; IO::Event->import('no_emulate_Event') unless $base; $base->timer(@_); } sub new { IO::Event->import('no_emulate_Event') unless $base; &{$base . "::new"}(@_); } sub import { my ($pkg, @stuff) = @_; for my $s (@stuff) { if ($s eq 'emulate_Event') { $base = 'IO::Event::Emulate'; require IO::Event::Emulate; } elsif ($s eq 'no_emulate_Event') { require Event; require IO::Event::Event; $base = 'IO::Event::Event'; } elsif ($s eq 'AnyEvent') { require AnyEvent; require IO::Event::AnyEvent; $base = 'IO::Event::AnyEvent'; } else { die "unknown import: $s"; } @ISA = $base; } return 1; } sub AUTOLOAD { my $self = shift; our $AUTOLOAD; my $a = $AUTOLOAD; $a =~ s/.*:://; # for whatever reason, UNIVERSAL::can() # doesn't seem to work on some filehandles my $r; my @r; my $fh = ${*$self}{ie_fh}; if ($fh) { if (wantarray) { eval { @r = $fh->$a(@_) }; } else { eval { $r = $fh->$a(@_) }; } if ($@ && $@ =~ /Can't locate object method "(.*?)" via package/) { my $event = ${*$self}{ie_event}; if ($1 ne $a) { # nothing to do } elsif ($event && $event->can($a)) { if (wantarray) { eval { @r = $event->$a(@_) }; } else { eval { $r = $event->$a(@_) }; } } else { confess qq{Can't locate object method "$a" via "@{[ ref($self) ]}", "@{[ ref($fh)||'IO::Handle' ]}", or "@{[ ref($event) ]}"}; } } } else { my $event = ${*$self}{ie_event}; if ($event && $event->can($a)) { if (wantarray) { eval { @r = $event->$a(@_) }; } else { eval { $r = $event->$a(@_) }; } } else { confess qq{Can't locate object method "$a" via "@{[ ref($self) ]}" or "@{[ ref($event) ]}"}; } } confess $@ if $@; return @r if wantarray; return $r; } }{package IO::Event::Common; use strict; use warnings; use Symbol; use Carp; require IO::Handle; use POSIX qw(BUFSIZ EAGAIN EBADF EINVAL ETIMEDOUT); use Socket; use Scalar::Util qw(weaken reftype); use Time::HiRes qw(time); our $in_callback = 0; my %fh_table; my %rxcache; my @pending_callbacks; sub display_bits { print STDERR unpack("b*", $_[0]); } sub count_bits { scalar(grep { $_ } split(//, unpack("b*", $_[0]))); } sub display_want { my ($name, $vec, %hash) = @_; my ($pkg, $file, $line) = caller; print STDERR "\n\nAT $file: $line\n"; print STDERR "$name\n"; for my $ioe (values %hash) { printf STDERR "%03d-", fileno(${*$ioe}{ie_fh}); # display_bits(${*$ioe}{ie_vec}); print STDERR "\n"; } print STDERR "----------"; display_bits($vec); printf STDERR " - %d\n", count_bits($vec); print STDERR scalar(keys(%hash)); print STDERR "\n"; exit 1; } my $counter = 1; sub new { my ($pkg, $fh, $handler, $options) = @_; # stolen from IO::Handle my $self = bless gensym(), $pkg; $handler = (caller(2))[0] unless $handler; confess unless ref $fh; unless (ref $options) { $options = { description => $options, }; } # some bits stolen from IO::Socket ${*$self}{ie_fh} = $fh; ${*$self}{ie_handler} = $handler; ${*$self}{ie_ibuf} = ''; ${*$self}{ie_obuf} = ''; ${*$self}{ie_obufsize} = BUFSIZ*4; ${*$self}{ie_autoread} = 1; ${*$self}{ie_pending} = {}; ${*$self}{ie_desc} = $options->{description} || "wrapper for $fh"; ${*$self}{ie_writeclosed} = EINVAL if $options->{read_only}; ${*$self}{ie_readclosed} = EINVAL if $options->{write_only}; $self->ie_register(); $fh->blocking(0); print "New IO::Event: ${*$self}{ie_desc} - now nonblocking\n" if $debug; # stolen from IO::Multiplex tie(*$self, $pkg, $self); return $self; } sub reset { my $self = shift; delete ${*$self}{ie_writeclosed}; delete ${*$self}{ie_readclosed}; delete ${*$self}{ie_eofinvoked}; delete ${*$self}{ie_overflowinvoked}; } # mark as listener sub listener { my ($self, $listener) = @_; $listener = 1 unless defined $listener; my $o = ${*$self}{ie_listener}; ${*$self}{ie_listener} = $listener; return $o; } # call out sub ie_invoke { my ($self, $required, $method, @args) = @_; if ($in_callback && ! ${*$self}->{ie_reentrant}) { # we'll do this later push(@pending_callbacks, [ $self, $required, $method, @args ]) unless exists ${*$self}{ie_pending}{$method}; ${*$self}{ie_pending}{$method} = 1; # prevent double invocation. needed? print STDERR "Delaying invocation of $method on ${*$self}{ie_desc} because we're already in a callback\n" if $debug; return; } local($in_callback) = 1; $self->ie_do_invoke($required, $method, @args); while (@pending_callbacks) { my ($ie, $req, $meth, @a) = @{shift @pending_callbacks}; delete ${*$ie}{ie_pending}{$meth}; print STDERR "Processing delayed invocation of $meth on ${*$ie}{ie_desc}\n" if $debug; $ie->ie_do_invoke($req, $meth, @a); } return; } sub ie_do_invoke { my ($self, $required, $method, @args) = @_; print STDERR "invoking ${*$self}{ie_fileno} ${*$self}{ie_handler}->$method\n" if $debug; return if ! $required && ! ${*$self}{ie_handler}->can($method); if ($debug) { my ($pkg, $line, $func) = caller(); print "DISPATCHING $method on ${*$self}{ie_desc} from $func at line $line\n"; } eval { ${*$self}{ie_handler}->$method($self, @args); }; print STDERR "return from ${*$self}{ie_fileno} ${*$self}{ie_handler}->$method handler: $@\n" if $debug; return unless $@; if (${*$self}{ie_handler}->can('ie_died')) { ${*$self}{ie_handler}->ie_died($self, $method, $@); } else { confess $@; exit 1; } } # # we use a single event handler so that the AUTOLOAD # function can try a single $event object when looking for # methods # sub ie_dispatch { print STDERR "D" if $sdebug; my ($self, $ievent) = @_; my $fh = ${*$self}{ie_fh}; my $got = $ievent->got; { if ($got & Event::Watcher::R()) { last if $self->ie_dispatch_read($fh); } if ($got & Event::Watcher::W()) { last if $self->ie_dispatch_write($fh); } if ($got & Event::Watcher::E()) { $self->ie_dispatch_exception($fh); } if ($got & Event::Watcher::T()) { $self->ie_dispatch_timer(); } } } sub ie_dispatch_read { my ($self, $fh) = @_; printf STDERR "R%d", $self->fileno if $sdebug; if (${*$self}{ie_listener}) { $self->ie_invoke(1, 'ie_connection'); } elsif (${*$self}{ie_autoread}) { $self->ie_input(); } else { $self->ie_invoke(1, 'ie_read_ready', $fh); } return 1 if ${*$self}{ie_writeclosed} && ${*$self}{ie_readclosed}; return 0; } sub ie_dispatch_write { my ($self, $fh) = @_; printf STDERR "W%d", $self->fileno if $sdebug; if (${*$self}{ie_connecting}) { $self->writeevents(0); delete ${*$self}{ie_connecting}; delete ${*$self}{ie_connect_timeout}; $self->ie_invoke(0, 'ie_connected'); } else { my $obuf = \${*$self}{ie_obuf}; my $rv; if (length($$obuf)) { $rv = syswrite($fh, $$obuf); if (defined $rv) { substr($$obuf, 0, $rv) = ''; } elsif ($! == EAGAIN) { # this shouldn't happen, but # it's not that big a deal } else { # the file descriptor is toast ${*$self}{ie_writeclosed} = $!; $self->ie_invoke(0, 'ie_werror', $obuf); } } if (${*$self}{ie_closerequested}) { if (! length($$obuf)) { $self->ie_deregister(); ${*$self}{ie_fh}->close(); delete ${*$self}{ie_closerequested}; } } elsif (${*$self}{ie_shutdownrequested}) { if (! length($$obuf)) { shutdown(${*$self}{ie_fh}, 1); ${*$self}{ie_writeclosed} = 1; delete ${*$self}{ie_shutdownrequested}; $self->ie_invoke(0, 'ie_outputdone', $obuf, 0); } } else { $self->ie_invoke(0, 'ie_output', $obuf, $rv); return 1 if ${*$self}{ie_writeclosed} && ${*$self}{ie_readclosed}; if (! length($$obuf)) { $self->ie_invoke(0, 'ie_outputdone', $obuf, 1); return 1 if ${*$self}{ie_writeclosed} && ${*$self}{ie_readclosed}; if (! length($$obuf)) { $self->writeevents(0); } } if (length($$obuf) > ${*$self}{ie_obufsize}) { ${*$self}{ie_overflowinvoked} = 1; $self->ie_invoke(0, 'ie_outputoverflow', 1, $obuf); } elsif (${*$self}{ie_overflowinvoked}) { ${*$self}{ie_overflowinvoked} = 0; $self->ie_invoke(0, 'ie_outputoverflow', 0, $obuf); } } } return 1 if ${*$self}{ie_writeclosed} && ${*$self}{ie_readclosed}; return 0; } sub ie_dispatch_exception { my ($self, $fh) = @_; printf STDERR "E%d", fileno(${*$self}{ie_fh}) if $sdebug; if (${*$self}{ie_closerequested}) { $self->forceclose; } elsif (${*$self}{ie_writeclosed} && ${*$self}{ie_readclosed}) { $self->forceclose; } elsif ($fh->eof) { if (length(${*$self}{ie_ibuf})) { $self->ie_invoke(0, 'ie_input', \${*$self}{ie_ibuf}); } if (${*$self}{ie_eofinvoked}++) { warn "EOF repeat"; } else { ${*$self}{ie_closecalled} = 0; $self->ie_invoke(0, 'ie_eof', \${*$self}{ie_ibuf}); unless (${*$self}{ie_closecalled}) { $self->close; } } } else { # print STDERR "!?!"; $self->ie_invoke(0, 'ie_exception'); } } sub ie_dispatch_timer { my ($self) = @_; printf STDERR "T%d", fileno(${*$self}{ie_fh}) if $sdebug; if (${*$self}{ie_connecting} && ${*$self}{ie_connect_timeout} && time >= ${*$self}{ie_connect_timeout}) { delete ${*$self}{ie_connect_timeout}; $self->ie_invoke(0, 'ie_connect_failed', ETIMEDOUT) or $self->ie_invoke(0, 'ie_timer'); } else { $self->ie_invoke(0, 'ie_timer'); } } # same name as handler since we want to intercept invocations # when processing pending callbacks. Why? sub ie_input { my $self = shift; my $ibuf = \${*$self}{ie_ibuf}; # # We'll loop just to make sure we don't miss an event # for (;;) { my $ol = length($$ibuf); my $rv = ${*$self}{ie_fh}->sysread($$ibuf, BUFSIZ, $ol); # my $x = defined($rv) ? $rv : "$!"; # LOST EVENTS # print STDERR ""; # LOST EVENTS if ($rv) { delete ${*$self}{ie_readclosed}; } elsif (defined($rv)) { # must be 0 and closed! ${*$self}{ie_readclosed} = 1; last; } elsif ($! == EAGAIN) { # readclosed = 0? last; } else { # errors other than EAGAIN aren't recoverable ${*$self}{ie_readclosed} = $!; last; } $self->ie_invoke(1, 'ie_input', $ibuf); last if ${*$self}{ie_readclosed}; } if (${*$self}{ie_readclosed}) { $self->ie_invoke(1, 'ie_input', $ibuf) if length($$ibuf); if (${*$self}{ie_connecting}) { ${*$self}{ie_writeclosed} = $!; $self->writeevents(0); delete ${*$self}{ie_connecting}; delete ${*$self}{ie_connect_timeout}; $self->ie_invoke(0, 'ie_connect_failed', $!); } else { $self->ie_invoke(0, 'ie_eof', $ibuf) unless ${*$self}{ie_eofinvoked}++; } $self->readevents(0); } } sub reentrant { my $self = shift; my $old = ${*$self}{ie_reentrant}; if (@_) { ${*$self}{ie_reentrant} = $_[0]; } return $old; } sub output_bufsize { my $self = shift; my $old = ${*$self}{ie_obufsize}; if (@_) { ${*$self}{ie_obufsize} = $_[0]; if (length(${*$self}{ie_obuf}) > ${*$self}{ie_obufsize}) { $self->ie_invoke(0, 'ie_outputoverflow', 1, ${*$self}{ie_obuf}); ${*$self}{ie_overflowinvoked} = 1; } elsif (${*$self}{ie_overflowinvoked}) { $self->ie_invoke(0, 'ie_outputoverflow', 0, ${*$self}{ie_obuf}); ${*$self}{ie_overflowinvoked} = 0; } # while this should trigger callbacks, we don't want to assume # that our caller's code is re-enterant. } return $old; } # get/set autoread sub autoread { my $self = shift; my $old = ${*$self}{ie_autoread}; if (@_) { ${*$self}{ie_autoread} = $_[0]; if (${*$self}{ie_readclosed}) { delete ${*$self}{ie_readclosed}; $self->readevents(1); } } return $old; } sub writeevents { my $self = shift; my $old = ${*$self}{ie_want_write_events}; return !! $old unless @_; my $new = !! shift; return $old if defined($old) && $old eq $new; ${*$self}{ie_want_write_events} = $new; $self->set_write_polling($new); return $old; } sub readevents { my $self = shift; my $old = ${*$self}{ie_want_read_events}; return !! $old unless @_; my $new = !! shift; # print STDERR ""; # LOST EVENTS return $old if defined($old) && $old eq $new; ${*$self}{ie_want_read_events} = $new; $self->set_read_polling($new); return $old; } sub drain { my $self = shift; $self->writeevents(1); } # register with Event sub ie_register { my ($self) = @_; my $fh = ${*$self}{ie_fh}; $fh->blocking(0); $fh->autoflush(1); my $fileno = ${*$self}{ie_fileno} = $fh->fileno; return ($fh, $fileno); } # deregister with Event sub ie_deregister { my ($self) = @_; my $fh = ${*$self}{ie_fh}; delete $fh_table{$fh}; $self->readevents(0); $self->writeevents(0); } # the standard max() function sub ie_max { my ($max, @stuff) = @_; for my $t (@stuff) { $max = $t if $t > $max; } return $max; } # get the Filehandle sub filehandle { my ($self) = @_; return ${*$self}{ie_fh}; } # get the Event sub event { my ($self) = @_; return ${*$self}{ie_event}; } # set the handler sub handler { my $self = shift; my $old = ${*$self}{ie_handler}; ${*$self}{ie_handler} = $_[0] if @_; return $old; } # is there enough? sub can_read { my ($self, $length) = @_; my $l = length(${*$self}{ie_ibuf}); return $l if $l && $l >= $length; return "0 but true" if $length <= 0; return 0; } # reads N characters or returns undef if it can't sub getsome { my ($self, $length) = @_; return undef unless ${*$self}{ie_autoread}; my $ibuf = \${*$self}{ie_ibuf}; $length = length($$ibuf) unless defined $length; my $tmp = substr($$ibuf, 0, $length); substr($$ibuf, 0, $length) = ''; return undef if ! length($tmp) && ! $self->eof2; return $tmp; } # from base perl # will this work right for SOCK_DGRAM? sub connect { my $self = shift; my $fh = ${*$self}{ie_fh}; my $rv = $fh->connect(@_); $self->reset; $self->readevents(1); unless($fh->connected()) { ${*$self}{ie_connecting} = 1; $self->writeevents(1); ${*$self}{ie_connect_timeout} = time + ${*$self}{ie_socket_timeout} if ${*$self}{ie_socket_timeout}; } return $rv; } # from IO::Socket sub listen { my $self = shift; my $fh = ${*$self}{ie_fh}; my $rv = $fh->listen(); $self->listener(1); return $rv; } # from IO::Socket sub accept { my ($self, $handler) = @_; my $fh = ${*$self}{ie_fh}; my $newfh = $fh->accept(); return undef unless $newfh; # it appears that sockdomain isn't set on accept()ed sockets my $sd = $fh->sockdomain; my $desc; if ($sd == &AF_INET) { $desc = sprintf "Accepted socket from %s:%s to %s:%s", $newfh->peerhost, $newfh->peerport, $newfh->sockhost, $newfh->sockport; } elsif ($sd == &AF_UNIX) { # Unset peerpath crashes on FreeBSD 9 my $pp = eval { $newfh->peerpath }; if ($pp) { $desc = sprintf "Accepted socket from %s to %s", $pp, $newfh->hostpath; } else { $desc = sprintf "Accepted socket from to %s", $newfh->hostpath; } } else { $desc = "Accept for ${*$self}{ie_desc}"; } $handler = ${*$self}{ie_handler} unless defined $handler; my $new = IO::Event->new($newfh, $handler, $desc); ${*$new}{ie_obufsize} = ${*$self}{ie_obufsize}; ${*$new}{ie_reentrant} = ${*$self}{ie_reentrant}; return $new; } # not the same as IO::Handle sub input_record_separator { my $self = shift; my $old = ${*$self}{ie_irs}; ${*$self}{ie_irs} = $_[0] if @_; if ($debug) { my $fn = $self->fileno; my $x = ${*$self}{ie_irs}; $x =~ s/\n/\\n/g; print "input_record_separator($fn) = '$x'\n"; } return $old; } # 0 = read # 1 = write # 2 = both sub shutdown { my ($self, $what) = @_; my $r; if ($what == 1 || $what == 2) { if (length(${*$self}{ie_obuf})) { ${*$self}{ie_shutdownrequested} = $what; if ($what == 2) { $r = shutdown(${*$self}{ie_fh}, 0) } } else { $r = shutdown(${*$self}{ie_fh}, $what); ${*$self}{ie_writeclosed} = 1; } } elsif ($what == 0) { $r = shutdown(${*$self}{ie_fh}, 0); } else { die; } if ($what == 0 || $what == 2) { ${*$self}{ie_readclosed} = 1; } return 1 unless defined($r); return $r; } # from IO::Handle sub close { my ($self) = @_; my $obuf = \${*$self}{ie_obuf}; ${*$self}{ie_closecalled} = 1; if (length($$obuf)) { ${*$self}{ie_closerequested} = 1; ${*$self}{ie_writeclosed} = 1; ${*$self}{ie_readclosed} = 1; } else { return $self->forceclose; } } sub forceclose { my ($self) = @_; $self->ie_deregister(); my $ret = ${*$self}{ie_fh}->close(); ${*$self}{ie_writeclosed} = 1; ${*$self}{ie_readclosed} = 1; ${*$self}{ie_totallyclosed} = 1; print STDERR "forceclose(${*$self}{ie_desc})\n" if $debug; return $ret; } # from IO::Handle sub open { my $self = shift; my $fh = ${*$self}{ie_fh}; $self->ie_deregister(); $self->close() if $fh->opened; $self->reset; my $r; if (@_ == 1) { $r = CORE::open($fh, $_[0]); } elsif (@_ == 2) { $r = CORE::open($fh, $_[0], $_[1]); } elsif (@_ == 3) { $r = CORE::open($fh, $_[0], $_[1], $_[4]); } elsif (@_ > 3) { $r = CORE::open($fh, $_[0], $_[1], $_[4], @_); } else { confess("open w/o enoug args"); } return undef unless defined $r; $self->ie_register(); return $r; } # from IO::Handle VAR LENGTH [OFFSET] # # this returns nothing unless there is enough to fill # the request or it's at eof # sub sysread { my $self = shift; unless (${*$self}{ie_autoread}) { my $buf = shift; my $length = shift; my $rv = ${*$self}{ie_fh}->sysread($buf, $length, @_); if ($rv) { delete ${*$self}{ie_readclosed}; } elsif (defined($rv)) { # must be 0 and closed! ${*$self}{ie_readclosed} = 1; } elsif ($! == EAGAIN) { # nothing there } else { # errors other than EAGAIN aren't recoverable ${*$self}{ie_readclosed} = $!; } return $rv; } my $ibuf = \${*$self}{ie_ibuf}; my $length = length($$ibuf); return undef unless $length >= $_[1] || $self->eof2; (defined $_[2] ? substr ($_[0], $_[2], length($_[0])) : $_[0]) = substr($$ibuf, 0, $_[1]); substr($$ibuf, 0, $_[1]) = ''; return ($length-length($$ibuf)); } # from IO::Handle sub syswrite { my ($self, $data, $length, $offset) = @_; if (defined $offset or defined $length) { return $self->print(substr($data, $offset, $length)); } else { return $self->print($data); } } # like Data::LineBuffer sub get { my $self = shift; return undef unless ${*$self}{ie_autoread}; my $ibuf = \${*$self}{ie_ibuf}; my $irs = "\n"; my $index = index($$ibuf, $irs); if ($index < 0) { return undef unless $self->eof2; my $l = $$ibuf; $$ibuf = ''; return undef unless length($l); return $l; } my $line = substr($$ibuf, 0, $index - length($irs) + 1); substr($$ibuf, 0, $index + 1) = ''; return $line; } # like Data::LineBuffer # input_record_separator is always "\n". sub unget { my $self = shift; my $irs = "\n"; no warnings; substr(${*$self}{ie_ibuf}, 0, 0) = join($irs, @_, undef); } # from IO::Handle sub getline { my $self = shift; return undef unless ${*$self}{ie_autoread}; my $ibuf = \${*$self}{ie_ibuf}; my $fh = ${*$self}{ie_fh}; my $irs = exists ${*$self}{ie_irs} ? ${*$self}{ie_irs} : $/; my $line; # perl's handling if input record separators is # not completely simple. $irs = $$irs if ref $irs; my $index; if ($irs =~ /^\d/ && int($irs)) { if ($irs > 0 && length($$ibuf) >= $irs) { $line = substr($$ibuf, 0, $irs); } elsif ($self->eof2) { $line = $$ibuf; } } elsif (! defined $irs) { if ($self->eof2) { $line = $$ibuf; } } elsif ($irs eq '') { # paragraph mode $$ibuf =~ s/^\n+//; $irs = "\n\n"; $index = index($$ibuf, "\n\n"); } else { # multi-character (or just \n) $index = index($$ibuf, $irs); } if (defined $index) { $line = $index > -1 ? substr($$ibuf, 0, $index+length($irs)) : ($self->eof2 ? $$ibuf : undef); } if ($debug) { no warnings; my $x = $$ibuf; substr($x, 0, length($line)) = ''; $x =~ s/\n/\\n/g; my $y = $irs; $y =~ s/\n/\\n/g; print "looked for '$y', returning undef, keeping '$x'\n" unless defined $line; my $z = $line; $z =~ s/\n/\\n/g; print "looked for '$y', returning '$z', keeping '$x'\n" if defined $line; } return undef unless defined($line) && length($line); substr($$ibuf, 0, length($line)) = ''; return $line; } # is the following a good idea? #sub tell #{ # my ($self) = @_; # return ${*$self}{ie_fh}->tell() + length(${*$self}{ie_obuf}); #} # from IO::Handle sub getlines { my $self = shift; return undef unless ${*$self}{ie_autoread}; my $ibuf = \${*$self}{ie_ibuf}; #my $ol = length($$ibuf); my $irs = exists ${*$self}{ie_irs} ? ${*$self}{ie_irs} : $/; my @lines; if ($debug) { my $x = $irs; $x =~ s/\n/\\n/g; my $fn = $self->fileno; print "getlines($fn, '$x')\n"; } if ($irs =~ /^\d/ && int($irs)) { if ($irs > 0) { @lines = unpack("(a$irs)*", $$ibuf); $$ibuf = ''; $$ibuf = pop(@lines) if length($lines[$#lines]) != $irs && ! $self->eof2; } else { return undef unless $self->eof2; @lines = $$ibuf; $$ibuf = ''; } } elsif (! defined $irs) { return undef unless $self->eof2; @lines = $$ibuf; $$ibuf = ''; } elsif ($irs eq '') { # paragraphish mode. $$ibuf =~ s/^\n+//; @lines = grep($_ ne '', split(/(.*?\n\n)\n*/s, $$ibuf)); $$ibuf = ''; $$ibuf = pop(@lines) if @lines && substr($lines[$#lines], -2) ne "\n\n" && ! $self->eof2; if ($debug) { my $x = join('|', @lines); $x =~ s/\n/\\n/g; my $y = $$ibuf; $y =~ s/\n/\\n/g; print "getlines returns '$x' but holds onto '$y'\n"; } } else { # multicharacter $rxcache{$irs} = qr/(.*?\Q$irs\E)/s unless exists $rxcache{$irs}; my $irsrx = $rxcache{$irs}; @lines = grep($_ ne '', split(/$rxcache{$irs}/, $$ibuf)); return undef unless @lines; $$ibuf = ''; $$ibuf = pop(@lines) if substr($lines[$#lines], 0-length($irs)) ne $irs && ! $self->eof2; } return @lines; } # from IO::Handle sub ungetc { my ($self, $ord) = @_; my $ibuf = \${*$self}{ie_ibuf}; substr($$ibuf, 0, 0) = chr($ord); } # from FileHandle::Unget & original sub ungets { my $self = shift; substr(${*$self}{ie_ibuf}, 0, 0) = join('', @_); } *xungetc = \&ungets; *ungetline = \&ungets; # from IO::Handle sub getc { my ($self) = @_; $self->getsome(1); } # from IO::Handle sub print { my ($self, @data) = @_; $! = ${*$self}{ie_writeclosed} && return undef if ${*$self}{ie_writeclosed}; my $ol; my $rv; my $er; my $obuf = \${*$self}{ie_obuf}; if ($ol = length($$obuf)) { $$obuf .= join('', @data); $rv = length($$obuf) - $ol; } else { my $fh = ${*$self}{ie_fh}; my $data = join('', @data); $rv = CORE::syswrite($fh, $data); if (defined($rv) && $rv < length($data)) { $$obuf = substr($data, $rv, length($data)-$rv); $self->writeevents(1); $rv = 1; } elsif ((! defined $rv) && $! == EAGAIN) { $$obuf = $data; $self->writeevents(1); $rv = 1; } else { $er = 0+$!; } } if (length($$obuf) > ${*$self}{ie_obufsize}) { $self->ie_invoke(0, 'ie_outputoverflow', 1, $obuf); ${*$self}{ie_overflowinvoked} = 1; } elsif (${*$self}{ie_overflowinvoked}) { $self->ie_invoke(0, 'ie_outputoverflow', 0, $obuf); ${*$self}{ie_overflowinvoked} = 0; } $! = $er; return $rv; } # from IO::Handle sub eof { my ($self) = @_; return 0 if length(${*$self}{ie_ibuf}); return 1 if ${*$self}{ie_readclosed}; return 0; # return ${*$self}{ie_fh}->eof; } # internal use only. # just like eof, but we assume the input buffer is empty sub eof2 { my ($self) = @_; if ($debug) { my $fn = $self->fileno; print "eof2($fn)..."; print " readclosed" if ${*$self}{ie_readclosed}; #print " EOF" if ${*$self}{ie_fh}->eof; my $x = 0; $x = 1 if ${*$self}{ie_readclosed}; # $x = ${*$self}{ie_fh}->eof unless defined $x; print " =$x\n"; } return 1 if ${*$self}{ie_readclosed}; return 0; # return ${*$self}{ie_fh}->eof; } sub fileno { my $self = shift; return undef unless $self && ref($self) && reftype($self) eq 'GLOB'; return ${*$self}{ie_fileno} if defined ${*$self}{ie_fileno}; return undef unless ${*$self}{ie_fh} && reftype(${*$self}{ie_fh}) eq 'GLOB'; return ${*$self}{ie_fh}->fileno(); } sub DESTROY { my $self = shift; my $no = $self->fileno; $no = '?' unless defined $no; print "DESTROY $no...\n" if $debug; return undef unless $self && ref($self) && reftype($self) eq 'GLOB'; ${*$self}{ie_event}->cancel if ${*$self}{ie_event}; } sub TIEHANDLE { my ($pkg, $self) = @_; return $self; } sub PRINTF { my $self = shift; $self->print(sprintf(shift, @_)); } sub READLINE { my $self = shift; wantarray ? $self->getlines : $self->getline; } sub ie_desc { my ($self, $new) = @_; my $r = ${*$self}{ie_desc} || "no description"; ${*$self}{ie_desc} = $new if defined $new; return $r; } no warnings; *PRINT = \&print; *READ = \&sysread; # from IO::Handle *read = \&sysread; *WRITE = \&syswrite; *CLOSE = \&close; *EOF = \&eof; *TELL = \&tell; *FILENO = \&fileno; *SEEK = \&seek; *BINMODE = \&binmode; *OPEN = \&open; *GETC = \&getc; use warnings; }{package IO::Event::Socket::INET; # XXX version 1.26 required for IO::Socket::INET use strict; use warnings; use List::MoreUtils qw(any); our @ISA = qw(IO::Event); sub new { my ($pkg, $a, $b, %sock) = @_; # emulate behavior in the IO::Socket::INET API if (! %sock && ! $b) { $sock{PeerAddr} = $a; } else { $sock{$a} = $b; } my $handler = $sock{Handler} || (caller)[0]; delete $sock{Handler}; my $timeout; if ($sock{Timeout}) { $timeout = $sock{Timeout}; delete $sock{Timeout}; } $sock{Blocking} = 0; my (%ds) = %sock; delete $sock{Description}; require IO::Socket::INET; my $fh = new IO::Socket::INET(%sock); return undef unless defined $fh; my $peer = any { /Peer/ } keys %sock; if ($peer) { $ds{LocalPort} = $fh->sockport unless defined $ds{LocalPort}; $ds{LocalHost} = $fh->sockhost unless defined $ds{LocalHost}; } my $desc = $ds{Description} || join(" ", map { defined $ds{$_} ? "$_=$ds{$_}" : $_ } sort keys %ds); return undef unless $fh; my $self = $pkg->SUPER::new($fh, $handler, $desc); bless $self, $pkg; $self->listener(1) if $sock{Listen}; $fh->blocking(0); # XXX may be redundant if ($peer) { if ($fh->connected()) { $self->ie_invoke(0, 'ie_connected'); } else { ${*$self}{ie_connecting} = 1; $self->writeevents(1); ${*$self}{ie_connect_timeout} = $timeout + time if $timeout; } } ${*$self}{ie_socket_timeout} = $timeout if $timeout; return $self; } }{ package IO::Event::Socket::UNIX; use strict; use warnings; our @ISA = qw(IO::Event); sub new { my ($pkg, $a, $b, %sock) = @_; # emulate behavior in the IO::Socket::INET API if (! %sock && ! $b) { $sock{Peer} = $a; } else { $sock{$a} = $b; } my $handler = $sock{Handler} || (caller)[0]; delete $sock{Handler}; my $desc = $sock{Description} || join(" ", map { "$_=$sock{$_}" } sort keys %sock); delete $sock{Description}; require IO::Socket::UNIX; my $fh = new IO::Socket::UNIX(%sock); return undef unless $fh; my $self = $pkg->SUPER::new($fh, $handler, $desc); bless $self, $pkg; $self->listener(1) if $sock{Listen}; $fh->blocking(0); if ($sock{Peer}) { if ($fh->connected()) { $self->ie_invoke(0, 'ie_connected'); } else { ${*$self}{ie_connecting} = 1; $self->writeevents(1); } } return $self; } }#end package 1;