Acme-InputRecordSeparatorIsRegexp

 view release on metacpan or  search on metacpan

lib/Acme/InputRecordSeparatorIsRegexp.pm  view on Meta::CPAN

}

our $VERSION = '0.07';

sub TIEHANDLE {
    my ($pkg, @opts) = @_;
    my $handle;
    if (@opts % 2) {
	$handle = Symbol::gensym;
    } else {
	my $fh = *{shift @opts};
        # will fail if open for $fh failed, but that's not important
	eval { CORE::open $handle, '<&+', $fh };
    }
    my $rs = shift @opts;
    my %opts = @opts;
    $opts{maxrecsize} ||= ($opts{bufsize} || 16384) / 4;
    $opts{bufsize} ||= $opts{maxrecsize} * 4;
    my $self = bless {
	%opts,
	handle => $handle,
	rs => $rs,
	records => [],
	buffer => ''
    }, $pkg;
    $self->_compile_rs;
    return $self;
}

# We abuse the PerlIO layers syntax to attach
# a regexp specification to a filehandle. This
# function extracts an ':irs(REGEXP)' layer from
# a string.
sub _extract_irs {
    my ($mode) = @_;
    
    my $irs = "";
    my $p0 = index($mode,":irs(");
    my $p1 = $p0 + 5;
    my $nest = 1;
    while ($nest) {
        my $c = eval { substr($mode,$p1++,1) };
        if ($@ || !defined($c)) {
            carp "Argument list not closed for PerlIO layer \"$irs\"";
            return;
        }
        if ($c eq "\\") {
            $c .= substr($mode,$p1++,1);
        }
        if ($c eq "(") { $nest++ }
        if ($c eq ")") { $nest-- }
        if ($nest) { $irs .= $c; }
    }
    substr($mode,$p0,length($irs)+6, "");
    $_[0] = $mode;
    return $irs;
}

sub open (*;$@) {
    no strict 'refs';        # or else bareword file handles will break
    my (undef,$mode,$expr,@list) = @_;
    if (!defined $_[0]) {
        $_[0] = Symbol::gensym;
    }
    my $glob = $_[0];
    if (!ref($glob) && $glob !~ /::/) {
        $glob = join("::",caller(0) || "", $glob);
    }

    if ($mode && index($mode,":irs(") >= 0) {
        my $irs = _extract_irs($mode);
        my $z = @list ? CORE::open *$glob, $mode, $expr, @list
                      : CORE::open *$glob, $mode, $expr;
        tie *$glob, __PACKAGE__, *$glob, $irs;
        return $z;
    }
    if (@list) {
        return CORE::open(*$glob,$mode,$expr,@list);
    } elsif ($expr) {
        return CORE::open(*$glob,$mode,$expr);
    } elsif ($mode) {
        return CORE::open(*$glob,$mode);
    } else {
        return CORE::open(*$glob);
    }
}

sub binmode (*;$) {
    my ($glob,$mode) = @_;
    $mode ||= ":raw";

    if (index($mode,":irs(") >= 0) {
        my $irs = _extract_irs($mode);
        input_record_separator($glob,$irs);
        return 1 unless $mode;
    }
    return CORE::binmode($glob,$mode);
}

sub _compile_rs {
    my $self = shift;
    my $rs = $self->{rs};

    my $q = eval { my @q = split /(?<=${rs})/,""; 1 };
    if ($q) {
	$self->{rsc} = qr/(?<=${rs})/s;
        if ($rs =~ /\?\^\w*m/) {
            $self->{rsc} = qr/(?<=${rs})/ms;
        }
	$self->{can_use_lookbehind} = 1;
    } else {
	$self->{rsc} = qr/(.*?(?:${rs}))/s;
        if ($rs =~ /\?\^\w*m/) {
            $self->{rsc} = qr/(.*?(?:${rs}))/ms;
        }
	$self->{can_use_lookbehind} = 0;
    }
    return;
}

sub READLINE {
    my $self = shift;
    if (wantarray) {
	local $/ = undef;
	$self->{buffer} .= readline($self->{handle});
	push @{$self->{records}}, $self->_split;
	$self->{buffer} = "";
	my @rec = splice @{$self->{records}};
	if (@rec && $self->{autochomp}) {
	    $self->chomp( @rec );
	}
	return @rec;
    }
    # want scalar
    if (!@{$self->{records}}) {
	$self->_populate_buffer;
    }
    my $rec = shift @{$self->{records}};
    if (defined($rec) && $self->{autochomp}) {
	$self->chomp( $rec );
    }
    return $rec;
}

sub _populate_buffer {
    my $self = shift;
    my $handle = $self->{handle};
    return if !$handle || eof($handle);
    
    my @rec;
    {
	my $buffer = '';
	my $n = read $handle, $buffer, $self->{bufsize};
	$self->{buffer} .= $buffer;
	@rec = $self->_split;
	redo if !eof($handle) && @rec == 1;
    }
    push @{$self->{records}}, @rec;
    $self->{buffer} = '';
    if (eof($handle)) {
	return;
    }

    if (@{$self->{records}} > 1) {
	$self->{buffer} = pop @{$self->{records}};
    }
    return;
}

sub EOF {
    my $self = shift;
    foreach my $rec (@{$self->{records}}, $self->{buffer}) {
	return if length($rec) > 0;
    }
    return eof($self->{handle});
}

sub _split {
    my $self = shift;
    if (!defined $self->{can_use_lookbehind}) {
	$self->_compile_rs;
    }
    my $rs = $self->{rsc};
    my @rec = split $rs, $self->{buffer};

lib/Acme/InputRecordSeparatorIsRegexp.pm  view on Meta::CPAN

}

sub CLOSE {
    my $self = shift;
    $self->_clear_buffer;
    my $z = close $self->{handle};
    # delete $self->{handle};
    return $z;
}

sub _clear_buffer {
    my $self = shift;
    $self->{buffer} = '';
    $self->{records} = [];
}

sub OPEN {
    my ($self, $mode, @args) = @_;
    if ($self->{handle}) {
	# close $self->{handle};
    }
    my $z = CORE::open $self->{handle}, $mode, @args;
    if ($z) {
	$self->_clear_buffer;
    }
    return $z;
}

sub FILENO {
    my $self = shift;
    return fileno($self->{handle});
}

sub WRITE {
    my ($self, $buf, $len, $offset) = @_;
    $offset ||= 0;
    if (!defined $len) {
	$len = length($buf)-$offset;
    }
    $self->PRINT( substr($buf,$offset,$len) );
}

sub PRINT {
    my ($self, @msg) = @_;
    if ($self->TELL() != tell($self->{handle})) {
	$self->SEEK(0,1);
    } else {
	$self->_clear_buffer;
    }
    print {$self->{handle}} @msg;
}

sub PRINTF {
    my ($self, $template, @args) = @_;
    $self->PRINT(sprintf($template,@args));
}

sub READ {
    my $self = shift;
    my $bufref = \$_[0];
    my (undef, $len, $offset) = @_;
    my $nread = 0;

    while ($len > 0 && @{$self->{records}}) {
	if (length($self->{records}[0])>=$len) {
	    my $rec = shift @{$self->{records}};
	    my $reclen = length($rec);
	    substr( $$bufref, $offset, $reclen, $rec);
	    $len -= $reclen;
	    $offset += $reclen;
	    $nread += $reclen;
	} else {
	    my $rec = substr($self->{records}[0], 0, $len, "");
	    substr( $$bufref, $offset, $len, $rec);
	    $offset += $len;
	    $nread += $len;
	    $len = 0;
	}
    }
    if ($len > 0 && length($self->{buffer}) > 0) {
	my $reclen = length($self->{buffer});
	if ($reclen >= $len) {
	    my $rec = substr( $self->{buffer}, 0, $len, "" );
	    substr( $$bufref, $offset, $len, $rec );
	    $offset += $len;
	    $nread += $len;
	    $len = 0;
	} else {
	    substr( $$bufref, $offset, $reclen, $self->{buffer} );
	    $self->{buffer} = "";
	    $offset += $reclen;
	    $nread += $reclen;
	    $len -= $reclen;
	}
    }
    if ($len > 0) {
	return $nread + read $self->{handle}, $$bufref, $len, $offset;
    } else {
	return $nread;
    }
}

sub GETC {
    my $self = shift;
    if (@{$self->{records}}==0 && 0 == length($self->{buffer})) {
	$self->_populate_buffer;
    }

    if (@{$self->{records}}) {
	my $c = substr( $self->{records}[0], 0, 1, "" );
	if (0 == length($self->{records}[0])) {
	    shift @{$self->{records}};
	}
	return $c;
    } elsif (0 != length($self->{buffer})) {
	my $c = substr( $self->{buffer}, 0, 1, "" );
	return $c;
    } else {
	# eof?
	return undef;
    }
}

sub BINMODE {
    my $self = shift;
    my $handle = $self->{handle};
    if (@_) {
	CORE::binmode $handle, @_;
    } else {
	CORE::binmode $handle;
    }    
}

sub SEEK {
    my ($self, $pos, $whence) = @_;

    if ($whence == 1) {
	$whence = 0;
	$pos += $self->TELL;
    }

    # easy implementation:
    #     on any seek, clear records, buffer

    $self->_clear_buffer;
    seek $self->{handle}, $pos, $whence;

    # more sophisticated implementation
    #     on a seek forward, remove bytes from the front
    #     of buffered data
}

sub TELL {
    my $self = shift;
    # virtual cursor position is actual position on the file handle
    # minus the length of any buffered data
    my $tell = tell $self->{handle};
    $tell -= length($self->{buffer});
    $tell -= length($_) for @{$self->{records}};
    return $tell;
}


no warnings 'redefine';
sub IO::Handle::input_record_separator {
    my $self = shift;
    if (ref($self) eq 'GLOB' || ref(\$self) eq 'GLOB') {
        if (tied(*$self)) {
            if (ref(tied(*$self)) eq __PACKAGE__) {
                return input_record_separator($self,@_);
            }
            my $z = eval { (tied *$self)->input_record_separator(@_) };
            if ($@) {
                carp "input_record_separator is not supported on tied handle";
            }
            return $z;
        }
        if (!@_) { return $/ }
        $self = tie *$self, __PACKAGE__, $self, quotemeta($/);
        return input_record_separator($self,@_);



( run in 0.811 second using v1.01-cache-2.11-cpan-d80b1682f3f )