Blame String.pm

Packit be0f65
package IO::String;
Packit be0f65
Packit be0f65
# Copyright 1998-2005 Gisle Aas.
Packit be0f65
#
Packit be0f65
# This library is free software; you can redistribute it and/or
Packit be0f65
# modify it under the same terms as Perl itself.
Packit be0f65
Packit be0f65
require 5.005_03;
Packit be0f65
use strict;
Packit be0f65
use vars qw($VERSION $DEBUG $IO_CONSTANTS);
Packit be0f65
$VERSION = "1.08";  # $Date: 2005/12/05 12:00:47 $
Packit be0f65
Packit be0f65
use Symbol ();
Packit be0f65
Packit be0f65
sub new
Packit be0f65
{
Packit be0f65
    my $class = shift;
Packit be0f65
    my $self = bless Symbol::gensym(), ref($class) || $class;
Packit be0f65
    tie *$self, $self;
Packit be0f65
    $self->open(@_);
Packit be0f65
    return $self;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub open
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    return $self->new(@_) unless ref($self);
Packit be0f65
Packit be0f65
    if (@_) {
Packit be0f65
	my $bufref = ref($_[0]) ? $_[0] : \$_[0];
Packit be0f65
	$$bufref = "" unless defined $$bufref;
Packit be0f65
	*$self->{buf} = $bufref;
Packit be0f65
    }
Packit be0f65
    else {
Packit be0f65
	my $buf = "";
Packit be0f65
	*$self->{buf} = \$buf;
Packit be0f65
    }
Packit be0f65
    *$self->{pos} = 0;
Packit be0f65
    *$self->{lno} = 0;
Packit be0f65
    return $self;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub pad
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    my $old = *$self->{pad};
Packit be0f65
    *$self->{pad} = substr($_[0], 0, 1) if @_;
Packit be0f65
    return "\0" unless defined($old) && length($old);
Packit be0f65
    return $old;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub dump
Packit be0f65
{
Packit be0f65
    require Data::Dumper;
Packit be0f65
    my $self = shift;
Packit be0f65
    print Data::Dumper->Dump([$self], ['*self']);
Packit be0f65
    print Data::Dumper->Dump([*$self{HASH}], ['$self{HASH}']);
Packit be0f65
    return;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub TIEHANDLE
Packit be0f65
{
Packit be0f65
    print "TIEHANDLE @_\n" if $DEBUG;
Packit be0f65
    return $_[0] if ref($_[0]);
Packit be0f65
    my $class = shift;
Packit be0f65
    my $self = bless Symbol::gensym(), $class;
Packit be0f65
    $self->open(@_);
Packit be0f65
    return $self;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub DESTROY
Packit be0f65
{
Packit be0f65
    print "DESTROY @_\n" if $DEBUG;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub close
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    delete *$self->{buf};
Packit be0f65
    delete *$self->{pos};
Packit be0f65
    delete *$self->{lno};
Packit be0f65
    undef *$self if $] eq "5.008";  # workaround for some bug
Packit be0f65
    return 1;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub opened
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    return defined *$self->{buf};
Packit be0f65
}
Packit be0f65
Packit be0f65
sub binmode
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    return 1 unless @_;
Packit be0f65
    # XXX don't know much about layers yet :-(
Packit be0f65
    return 0;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub getc
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    my $buf;
Packit be0f65
    return $buf if $self->read($buf, 1);
Packit be0f65
    return undef;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub ungetc
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    $self->setpos($self->getpos() - 1);
Packit be0f65
    return 1;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub eof
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    return length(${*$self->{buf}}) <= *$self->{pos};
Packit be0f65
}
Packit be0f65
Packit be0f65
sub print
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    if (defined $\) {
Packit be0f65
	if (defined $,) {
Packit be0f65
	    $self->write(join($,, @_).$\);
Packit be0f65
	}
Packit be0f65
	else {
Packit be0f65
	    $self->write(join("",@_).$\);
Packit be0f65
	}
Packit be0f65
    }
Packit be0f65
    else {
Packit be0f65
	if (defined $,) {
Packit be0f65
	    $self->write(join($,, @_));
Packit be0f65
	}
Packit be0f65
	else {
Packit be0f65
	    $self->write(join("",@_));
Packit be0f65
	}
Packit be0f65
    }
Packit be0f65
    return 1;
Packit be0f65
}
Packit be0f65
*printflush = \*print;
Packit be0f65
Packit be0f65
sub printf
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    print "PRINTF(@_)\n" if $DEBUG;
Packit be0f65
    my $fmt = shift;
Packit be0f65
    $self->write(sprintf($fmt, @_));
Packit be0f65
    return 1;
Packit be0f65
}
Packit be0f65
Packit be0f65
Packit be0f65
my($SEEK_SET, $SEEK_CUR, $SEEK_END);
Packit be0f65
Packit be0f65
sub _init_seek_constants
Packit be0f65
{
Packit be0f65
    if ($IO_CONSTANTS) {
Packit be0f65
	require IO::Handle;
Packit be0f65
	$SEEK_SET = &IO::Handle::SEEK_SET;
Packit be0f65
	$SEEK_CUR = &IO::Handle::SEEK_CUR;
Packit be0f65
	$SEEK_END = &IO::Handle::SEEK_END;
Packit be0f65
    }
Packit be0f65
    else {
Packit be0f65
	$SEEK_SET = 0;
Packit be0f65
	$SEEK_CUR = 1;
Packit be0f65
	$SEEK_END = 2;
Packit be0f65
    }
Packit be0f65
}
Packit be0f65
Packit be0f65
Packit be0f65
sub seek
Packit be0f65
{
Packit be0f65
    my($self,$off,$whence) = @_;
Packit be0f65
    my $buf = *$self->{buf} || return 0;
Packit be0f65
    my $len = length($$buf);
Packit be0f65
    my $pos = *$self->{pos};
Packit be0f65
Packit be0f65
    _init_seek_constants() unless defined $SEEK_SET;
Packit be0f65
Packit be0f65
    if    ($whence == $SEEK_SET) { $pos = $off }
Packit be0f65
    elsif ($whence == $SEEK_CUR) { $pos += $off }
Packit be0f65
    elsif ($whence == $SEEK_END) { $pos = $len + $off }
Packit be0f65
    else                         { die "Bad whence ($whence)" }
Packit be0f65
    print "SEEK(POS=$pos,OFF=$off,LEN=$len)\n" if $DEBUG;
Packit be0f65
Packit be0f65
    $pos = 0 if $pos < 0;
Packit be0f65
    $self->truncate($pos) if $pos > $len;  # extend file
Packit be0f65
    *$self->{pos} = $pos;
Packit be0f65
    return 1;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub pos
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    my $old = *$self->{pos};
Packit be0f65
    if (@_) {
Packit be0f65
	my $pos = shift || 0;
Packit be0f65
	my $buf = *$self->{buf};
Packit be0f65
	my $len = $buf ? length($$buf) : 0;
Packit be0f65
	$pos = $len if $pos > $len;
Packit be0f65
	*$self->{pos} = $pos;
Packit be0f65
    }
Packit be0f65
    return $old;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub getpos { shift->pos; }
Packit be0f65
Packit be0f65
*sysseek = \&see;;
Packit be0f65
*setpos  = \&pos;
Packit be0f65
*tell    = \&getpos;
Packit be0f65
Packit be0f65
Packit be0f65
Packit be0f65
sub getline
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    my $buf  = *$self->{buf} || return;
Packit be0f65
    my $len  = length($$buf);
Packit be0f65
    my $pos  = *$self->{pos};
Packit be0f65
    return if $pos >= $len;
Packit be0f65
Packit be0f65
    unless (defined $/) {  # slurp
Packit be0f65
	*$self->{pos} = $len;
Packit be0f65
	return substr($$buf, $pos);
Packit be0f65
    }
Packit be0f65
Packit be0f65
    unless (length $/) {  # paragraph mode
Packit be0f65
	# XXX slow&lazy implementation using getc()
Packit be0f65
	my $para = "";
Packit be0f65
	my $eol = 0;
Packit be0f65
	my $c;
Packit be0f65
	while (defined($c = $self->getc)) {
Packit be0f65
	    if ($c eq "\n") {
Packit be0f65
		$eol++;
Packit be0f65
		next if $eol > 2;
Packit be0f65
	    }
Packit be0f65
	    elsif ($eol > 1) {
Packit be0f65
		$self->ungetc($c);
Packit be0f65
		last;
Packit be0f65
	    }
Packit be0f65
	    else {
Packit be0f65
		$eol = 0;
Packit be0f65
	    }
Packit be0f65
	    $para .= $c;
Packit be0f65
	}
Packit be0f65
	return $para;   # XXX wantarray
Packit be0f65
    }
Packit be0f65
Packit be0f65
    my $idx = index($$buf,$/,$pos);
Packit be0f65
    if ($idx < 0) {
Packit be0f65
	# return rest of it
Packit be0f65
	*$self->{pos} = $len;
Packit be0f65
	$. = ++ *$self->{lno};
Packit be0f65
	return substr($$buf, $pos);
Packit be0f65
    }
Packit be0f65
    $len = $idx - $pos + length($/);
Packit be0f65
    *$self->{pos} += $len;
Packit be0f65
    $. = ++ *$self->{lno};
Packit be0f65
    return substr($$buf, $pos, $len);
Packit be0f65
}
Packit be0f65
Packit be0f65
sub getlines
Packit be0f65
{
Packit be0f65
    die "getlines() called in scalar context\n" unless wantarray;
Packit be0f65
    my $self = shift;
Packit be0f65
    my($line, @lines);
Packit be0f65
    push(@lines, $line) while defined($line = $self->getline);
Packit be0f65
    return @lines;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub READLINE
Packit be0f65
{
Packit be0f65
    goto &getlines if wantarray;
Packit be0f65
    goto &getline;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub input_line_number
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    my $old = *$self->{lno};
Packit be0f65
    *$self->{lno} = shift if @_;
Packit be0f65
    return $old;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub truncate
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    my $len = shift || 0;
Packit be0f65
    my $buf = *$self->{buf};
Packit be0f65
    if (length($$buf) >= $len) {
Packit be0f65
	substr($$buf, $len) = '';
Packit be0f65
	*$self->{pos} = $len if $len < *$self->{pos};
Packit be0f65
    }
Packit be0f65
    else {
Packit be0f65
	$$buf .= ($self->pad x ($len - length($$buf)));
Packit be0f65
    }
Packit be0f65
    return 1;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub read
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    my $buf = *$self->{buf};
Packit be0f65
    return undef unless $buf;
Packit be0f65
Packit be0f65
    my $pos = *$self->{pos};
Packit be0f65
    my $rem = length($$buf) - $pos;
Packit be0f65
    my $len = $_[1];
Packit be0f65
    $len = $rem if $len > $rem;
Packit be0f65
    return undef if $len < 0;
Packit be0f65
    if (@_ > 2) { # read offset
Packit be0f65
	substr($_[0],$_[2]) = substr($$buf, $pos, $len);
Packit be0f65
    }
Packit be0f65
    else {
Packit be0f65
	$_[0] = substr($$buf, $pos, $len);
Packit be0f65
    }
Packit be0f65
    *$self->{pos} += $len;
Packit be0f65
    return $len;
Packit be0f65
}
Packit be0f65
Packit be0f65
sub write
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    my $buf = *$self->{buf};
Packit be0f65
    return unless $buf;
Packit be0f65
Packit be0f65
    my $pos = *$self->{pos};
Packit be0f65
    my $slen = length($_[0]);
Packit be0f65
    my $len = $slen;
Packit be0f65
    my $off = 0;
Packit be0f65
    if (@_ > 1) {
Packit be0f65
	$len = $_[1] if $_[1] < $len;
Packit be0f65
	if (@_ > 2) {
Packit be0f65
	    $off = $_[2] || 0;
Packit be0f65
	    die "Offset outside string" if $off > $slen;
Packit be0f65
	    if ($off < 0) {
Packit be0f65
		$off += $slen;
Packit be0f65
		die "Offset outside string" if $off < 0;
Packit be0f65
	    }
Packit be0f65
	    my $rem = $slen - $off;
Packit be0f65
	    $len = $rem if $rem < $len;
Packit be0f65
	}
Packit be0f65
    }
Packit be0f65
    substr($$buf, $pos, $len) = substr($_[0], $off, $len);
Packit be0f65
    *$self->{pos} += $len;
Packit be0f65
    return $len;
Packit be0f65
}
Packit be0f65
Packit be0f65
*sysread = \&read;
Packit be0f65
*syswrite = \&write;
Packit be0f65
Packit be0f65
sub stat
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    return unless $self->opened;
Packit be0f65
    return 1 unless wantarray;
Packit be0f65
    my $len = length ${*$self->{buf}};
Packit be0f65
Packit be0f65
    return (
Packit be0f65
     undef, undef,  # dev, ino
Packit be0f65
     0666,          # filemode
Packit be0f65
     1,             # links
Packit be0f65
     $>,            # user id
Packit be0f65
     $),            # group id
Packit be0f65
     undef,         # device id
Packit be0f65
     $len,          # size
Packit be0f65
     undef,         # atime
Packit be0f65
     undef,         # mtime
Packit be0f65
     undef,         # ctime
Packit be0f65
     512,           # blksize
Packit be0f65
     int(($len+511)/512)  # blocks
Packit be0f65
    );
Packit be0f65
}
Packit be0f65
Packit be0f65
sub FILENO {
Packit be0f65
    return undef;   # XXX perlfunc says this means the file is closed
Packit be0f65
}
Packit be0f65
Packit be0f65
sub blocking {
Packit be0f65
    my $self = shift;
Packit be0f65
    my $old = *$self->{blocking} || 0;
Packit be0f65
    *$self->{blocking} = shift if @_;
Packit be0f65
    return $old;
Packit be0f65
}
Packit be0f65
Packit be0f65
my $notmuch = sub { return };
Packit be0f65
Packit be0f65
*fileno    = $notmuch;
Packit be0f65
*error     = $notmuch;
Packit be0f65
*clearerr  = $notmuch; 
Packit be0f65
*sync      = $notmuch;
Packit be0f65
*flush     = $notmuch;
Packit be0f65
*setbuf    = $notmuch;
Packit be0f65
*setvbuf   = $notmuch;
Packit be0f65
Packit be0f65
*untaint   = $notmuch;
Packit be0f65
*autoflush = $notmuch;
Packit be0f65
*fcntl     = $notmuch;
Packit be0f65
*ioctl     = $notmuch;
Packit be0f65
Packit be0f65
*GETC   = \&get;;
Packit be0f65
*PRINT  = \&prin;;
Packit be0f65
*PRINTF = \&printf;
Packit be0f65
*READ   = \&read;
Packit be0f65
*WRITE  = \&write;
Packit be0f65
*SEEK   = \&see;;
Packit be0f65
*TELL   = \&getpos;
Packit be0f65
*EOF    = \&eof;
Packit be0f65
*CLOSE  = \&close;
Packit be0f65
*BINMODE = \&binmode;
Packit be0f65
Packit be0f65
Packit be0f65
sub string_ref
Packit be0f65
{
Packit be0f65
    my $self = shift;
Packit be0f65
    return *$self->{buf};
Packit be0f65
}
Packit be0f65
*sref = \&string_ref;
Packit be0f65
Packit be0f65
1;
Packit be0f65
Packit be0f65
__END__
Packit be0f65
Packit be0f65
=head1 NAME
Packit be0f65
Packit be0f65
IO::String - Emulate file interface for in-core strings
Packit be0f65
Packit be0f65
=head1 SYNOPSIS
Packit be0f65
Packit be0f65
 use IO::String;
Packit be0f65
 $io = IO::String->new;
Packit be0f65
 $io = IO::String->new($var);
Packit be0f65
 tie *IO, 'IO::String';
Packit be0f65
Packit be0f65
 # read data
Packit be0f65
 <$io>;
Packit be0f65
 $io->getline;
Packit be0f65
 read($io, $buf, 100);
Packit be0f65
Packit be0f65
 # write data
Packit be0f65
 print $io "string\n";
Packit be0f65
 $io->print(@data);
Packit be0f65
 syswrite($io, $buf, 100);
Packit be0f65
Packit be0f65
 select $io;
Packit be0f65
 printf "Some text %s\n", $str;
Packit be0f65
Packit be0f65
 # seek
Packit be0f65
 $pos = $io->getpos;
Packit be0f65
 $io->setpos(0);        # rewind
Packit be0f65
 $io->seek(-30, -1);
Packit be0f65
 seek($io, 0, 0);
Packit be0f65
Packit be0f65
=head1 DESCRIPTION
Packit be0f65
Packit be0f65
The C<IO::String> module provides the C<IO::File> interface for in-core
Packit be0f65
strings.  An C<IO::String> object can be attached to a string, and
Packit be0f65
makes it possible to use the normal file operations for reading or
Packit be0f65
writing data, as well as for seeking to various locations of the string.
Packit be0f65
This is useful when you want to use a library module that only
Packit be0f65
provides an interface to file handles on data that you have in a string
Packit be0f65
variable.
Packit be0f65
Packit be0f65
Note that perl-5.8 and better has built-in support for "in memory"
Packit be0f65
files, which are set up by passing a reference instead of a filename
Packit be0f65
to the open() call. The reason for using this module is that it
Packit be0f65
makes the code backwards compatible with older versions of Perl.
Packit be0f65
Packit be0f65
The C<IO::String> module provides an interface compatible with
Packit be0f65
C<IO::File> as distributed with F<IO-1.20>, but the following methods
Packit be0f65
are not available: new_from_fd, fdopen, format_write,
Packit be0f65
format_page_number, format_lines_per_page, format_lines_left,
Packit be0f65
format_name, format_top_name.
Packit be0f65
Packit be0f65
The following methods are specific to the C<IO::String> class:
Packit be0f65
Packit be0f65
=over 4
Packit be0f65
Packit be0f65
=item $io = IO::String->new
Packit be0f65
Packit be0f65
=item $io = IO::String->new( $string )
Packit be0f65
Packit be0f65
The constructor returns a newly-created C<IO::String> object.  It
Packit be0f65
takes an optional argument, which is the string to read from or write
Packit be0f65
into.  If no $string argument is given, then an internal buffer
Packit be0f65
(initially empty) is allocated.
Packit be0f65
Packit be0f65
The C<IO::String> object returned is tied to itself.  This means
Packit be0f65
that you can use most Perl I/O built-ins on it too: readline, <>, getc,
Packit be0f65
print, printf, syswrite, sysread, close.
Packit be0f65
Packit be0f65
=item $io->open
Packit be0f65
Packit be0f65
=item $io->open( $string )
Packit be0f65
Packit be0f65
Attaches an existing IO::String object to some other $string, or
Packit be0f65
allocates a new internal buffer (if no argument is given).  The
Packit be0f65
position is reset to 0.
Packit be0f65
Packit be0f65
=item $io->string_ref
Packit be0f65
Packit be0f65
Returns a reference to the string that is attached to
Packit be0f65
the C<IO::String> object.  Most useful when you let the C<IO::String>
Packit be0f65
create an internal buffer to write into.
Packit be0f65
Packit be0f65
=item $io->pad
Packit be0f65
Packit be0f65
=item $io->pad( $char )
Packit be0f65
Packit be0f65
Specifies the padding to use if
Packit be0f65
the string is extended by either the seek() or truncate() methods.  It
Packit be0f65
is a single character and defaults to "\0".
Packit be0f65
Packit be0f65
=item $io->pos
Packit be0f65
Packit be0f65
=item $io->pos( $newpos )
Packit be0f65
Packit be0f65
Yet another interface for reading and setting the current read/write
Packit be0f65
position within the string (the normal getpos/setpos/tell/seek
Packit be0f65
methods are also available).  The pos() method always returns the
Packit be0f65
old position, and if you pass it an argument it sets the new
Packit be0f65
position.
Packit be0f65
Packit be0f65
There is (deliberately) a difference between the setpos() and seek()
Packit be0f65
methods in that seek() extends the string (with the specified
Packit be0f65
padding) if you go to a location past the end, whereas setpos()
Packit be0f65
just snaps back to the end.  If truncate() is used to extend the string,
Packit be0f65
then it works as seek().
Packit be0f65
Packit be0f65
=back
Packit be0f65
Packit be0f65
=head1 BUGS
Packit be0f65
Packit be0f65
In Perl versions < 5.6, the TIEHANDLE interface was incomplete.
Packit be0f65
If you use such a Perl, then seek(), tell(), eof(), fileno(), binmode() will
Packit be0f65
not do anything on an C<IO::String> handle.  See L<perltie> for
Packit be0f65
details.
Packit be0f65
Packit be0f65
=head1 SEE ALSO
Packit be0f65
Packit be0f65
L<IO::File>, L<IO::Stringy>, L<perlfunc/open>
Packit be0f65
Packit be0f65
=head1 COPYRIGHT
Packit be0f65
Packit be0f65
Copyright 1998-2005 Gisle Aas.
Packit be0f65
Packit be0f65
This library is free software; you can redistribute it and/or
Packit be0f65
modify it under the same terms as Perl itself.
Packit be0f65
Packit be0f65
=cut