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;