home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / ScalarArray.pm < prev    next >
Encoding:
Perl POD Document  |  2002-06-14  |  17.7 KB  |  785 lines

  1. package IO::ScalarArray;
  2.  
  3.  
  4. =head1 NAME
  5.  
  6. IO::ScalarArray - IO:: interface for reading/writing an array of scalars
  7.  
  8.  
  9. =head1 SYNOPSIS
  10.  
  11. Perform I/O on strings, using the basic OO interface...
  12.  
  13.     use IO::ScalarArray;
  14.     @data = ("My mes", "sage:\n");
  15.  
  16.     ### Open a handle on an array, and append to it:
  17.     $AH = new IO::ScalarArray \@data;
  18.     $AH->print("Hello");
  19.     $AH->print(", world!\nBye now!\n");
  20.     print "The array is now: ", @data, "\n";
  21.  
  22.     ### Open a handle on an array, read it line-by-line, then close it:
  23.     $AH = new IO::ScalarArray \@data;
  24.     while (defined($_ = $AH->getline)) {
  25.     print "Got line: $_";
  26.     }
  27.     $AH->close;
  28.  
  29.     ### Open a handle on an array, and slurp in all the lines:
  30.     $AH = new IO::ScalarArray \@data;
  31.     print "All lines:\n", $AH->getlines;
  32.  
  33.     ### Get the current position (either of two ways):
  34.     $pos = $AH->getpos;
  35.     $offset = $AH->tell;
  36.  
  37.     ### Set the current position (either of two ways):
  38.     $AH->setpos($pos);
  39.     $AH->seek($offset, 0);
  40.  
  41.     ### Open an anonymous temporary array:
  42.     $AH = new IO::ScalarArray;
  43.     $AH->print("Hi there!");
  44.     print "I printed: ", @{$AH->aref}, "\n";      ### get at value
  45.  
  46.  
  47. Don't like OO for your I/O?  No problem.
  48. Thanks to the magic of an invisible tie(), the following now
  49. works out of the box, just as it does with IO::Handle:
  50.  
  51.     use IO::ScalarArray;
  52.     @data = ("My mes", "sage:\n");
  53.  
  54.     ### Open a handle on an array, and append to it:
  55.     $AH = new IO::ScalarArray \@data;
  56.     print $AH "Hello";
  57.     print $AH ", world!\nBye now!\n";
  58.     print "The array is now: ", @data, "\n";
  59.  
  60.     ### Open a handle on a string, read it line-by-line, then close it:
  61.     $AH = new IO::ScalarArray \@data;
  62.     while (<$AH>) {
  63.     print "Got line: $_";
  64.     }
  65.     close $AH;
  66.  
  67.     ### Open a handle on a string, and slurp in all the lines:
  68.     $AH = new IO::ScalarArray \@data;
  69.     print "All lines:\n", <$AH>;
  70.  
  71.     ### Get the current position (WARNING: requires 5.6):
  72.     $offset = tell $AH;
  73.  
  74.     ### Set the current position (WARNING: requires 5.6):
  75.     seek $AH, $offset, 0;
  76.  
  77.     ### Open an anonymous temporary scalar:
  78.     $AH = new IO::ScalarArray;
  79.     print $AH "Hi there!";
  80.     print "I printed: ", @{$AH->aref}, "\n";      ### get at value
  81.  
  82.  
  83. And for you folks with 1.x code out there: the old tie() style still works,
  84. though this is I<unnecessary and deprecated>:
  85.  
  86.     use IO::ScalarArray;
  87.  
  88.     ### Writing to a scalar...
  89.     my @a;
  90.     tie *OUT, 'IO::ScalarArray', \@a;
  91.     print OUT "line 1\nline 2\n", "line 3\n";
  92.     print "Array is now: ", @a, "\n"
  93.  
  94.     ### Reading and writing an anonymous scalar...
  95.     tie *OUT, 'IO::ScalarArray';
  96.     print OUT "line 1\nline 2\n", "line 3\n";
  97.     tied(OUT)->seek(0,0);
  98.     while (<OUT>) {
  99.         print "Got line: ", $_;
  100.     }
  101.  
  102.  
  103.  
  104. =head1 DESCRIPTION
  105.  
  106. This class is part of the IO::Stringy distribution;
  107. see L<IO::Stringy> for change log and general information.
  108.  
  109. The IO::ScalarArray class implements objects which behave just like
  110. IO::Handle (or FileHandle) objects, except that you may use them
  111. to write to (or read from) arrays of scalars.  Logically, an
  112. array of scalars defines an in-core "file" whose contents are
  113. the concatenation of the scalars in the array.  The handles created by
  114. this class are automatically tiehandle'd (though please see L<"WARNINGS">
  115. for information relevant to your Perl version).
  116.  
  117. For writing large amounts of data with individual print() statements,
  118. this class is likely to be more efficient than IO::Scalar.
  119.  
  120. Basically, this:
  121.  
  122.     my @a;
  123.     $AH = new IO::ScalarArray \@a;
  124.     $AH->print("Hel", "lo, ");         ### OO style
  125.     $AH->print("world!\n");            ### ditto
  126.  
  127. Or this:
  128.  
  129.     my @a;
  130.     $AH = new IO::ScalarArray \@a;
  131.     print $AH "Hel", "lo, ";           ### non-OO style
  132.     print $AH "world!\n";              ### ditto
  133.  
  134. Causes @a to be set to the following array of 3 strings:
  135.  
  136.     ( "Hel" ,
  137.       "lo, " ,
  138.       "world!\n" )
  139.  
  140. See L<IO::Scalar> and compare with this class.
  141.  
  142.  
  143. =head1 PUBLIC INTERFACE
  144.  
  145. =cut
  146.  
  147. use Carp;
  148. use strict;
  149. use vars qw($VERSION @ISA);
  150. use IO::Handle;
  151.  
  152. # The package version, both in 1.23 style *and* usable by MakeMaker:
  153. $VERSION = substr q$Revision: 2.103 $, 10;
  154.  
  155. # Inheritance:
  156. @ISA = qw(IO::Handle);
  157. require IO::WrapTie and push @ISA, 'IO::WrapTie::Slave' if ($] >= 5.004);
  158.  
  159.  
  160. #==============================
  161.  
  162. =head2 Construction
  163.  
  164. =over 4
  165.  
  166. =cut
  167.  
  168. #------------------------------
  169.  
  170. =item new [ARGS...]
  171.  
  172. I<Class method.>
  173. Return a new, unattached array handle.
  174. If any arguments are given, they're sent to open().
  175.  
  176. =cut
  177.  
  178. sub new {
  179.     my $proto = shift;
  180.     my $class = ref($proto) || $proto;
  181.     my $self = bless \do { local *FH }, $class;
  182.     tie *$self, $class, $self;
  183.     $self->open(@_);  ### open on anonymous by default
  184.     $self;
  185. }
  186. sub DESTROY { 
  187.     shift->close;
  188. }
  189.  
  190.  
  191. #------------------------------
  192.  
  193. =item open [ARRAYREF]
  194.  
  195. I<Instance method.>
  196. Open the array handle on a new array, pointed to by ARRAYREF.
  197. If no ARRAYREF is given, a "private" array is created to hold
  198. the file data.
  199.  
  200. Returns the self object on success, undefined on error.
  201.  
  202. =cut
  203.  
  204. sub open {
  205.     my ($self, $aref) = @_;
  206.  
  207.     ### Sanity:
  208.     defined($aref) or do {my @a; $aref = \@a};
  209.     (ref($aref) eq "ARRAY") or croak "open needs a ref to a array";
  210.  
  211.     ### Setup:
  212.     $self->setpos([0,0]);
  213.     *$self->{AR} = $aref;
  214.     $self;
  215. }
  216.  
  217. #------------------------------
  218.  
  219. =item opened
  220.  
  221. I<Instance method.>
  222. Is the array handle opened on something?
  223.  
  224. =cut
  225.  
  226. sub opened {
  227.     *{shift()}->{AR};
  228. }
  229.  
  230. #------------------------------
  231.  
  232. =item close
  233.  
  234. I<Instance method.>
  235. Disassociate the array handle from its underlying array.
  236. Done automatically on destroy.
  237.  
  238. =cut
  239.  
  240. sub close {
  241.     my $self = shift;
  242.     %{*$self} = ();
  243.     1;
  244. }
  245.  
  246. =back
  247.  
  248. =cut
  249.  
  250.  
  251.  
  252. #==============================
  253.  
  254. =head2 Input and output
  255.  
  256. =over 4
  257.  
  258. =cut
  259.  
  260. #------------------------------
  261.  
  262. =item flush
  263.  
  264. I<Instance method.>
  265. No-op, provided for OO compatibility.
  266.  
  267. =cut
  268.  
  269. sub flush {} 
  270.  
  271. #------------------------------
  272.  
  273. =item getc
  274.  
  275. I<Instance method.>
  276. Return the next character, or undef if none remain.
  277. This does a read(1), which is somewhat costly.
  278.  
  279. =cut
  280.  
  281. sub getc {
  282.     my $buf = '';
  283.     ($_[0]->read($buf, 1) ? $buf : undef);
  284. }
  285.  
  286. #------------------------------
  287.  
  288. =item getline
  289.  
  290. I<Instance method.>
  291. Return the next line, or undef on end of data.
  292. Can safely be called in an array context.
  293. Currently, lines are delimited by "\n".
  294.  
  295. =cut
  296.  
  297. sub getline {
  298.     my $self = shift;
  299.     my ($str, $line) = (undef, '');
  300.  
  301.  
  302.     ### Minimal impact implementation!
  303.     ### We do the fast fast thing (no regexps) if using the
  304.     ### classic input record separator.
  305.  
  306.     ### Case 1: $/ is undef: slurp all...    
  307.     if    (!defined($/)) {
  308.  
  309.     ### Get the rest of the current string, followed by remaining strings:
  310.     my $ar = *$self->{AR};
  311.     my @slurp = (
  312.              substr($ar->[*$self->{Str}], *$self->{Pos}),
  313.              @$ar[(1 + *$self->{Str}) .. $#$ar ] 
  314.              );
  315.              
  316.     ### Seek to end:
  317.     $self->_setpos_to_eof;
  318.     return join('', @slurp);
  319.     }
  320.  
  321.     ### Case 2: $/ is "\n": 
  322.     elsif ($/ eq "\012") {    
  323.     
  324.     ### Until we hit EOF (or exitted because of a found line):
  325.     until ($self->eof) {
  326.         ### If at end of current string, go fwd to next one (won't be EOF):
  327.         if ($self->_eos) {++*$self->{Str}, *$self->{Pos}=0};
  328.  
  329.         ### Get ref to current string in array, and set internal pos mark:
  330.         $str = \(*$self->{AR}[*$self->{Str}]); ### get current string
  331.         pos($$str) = *$self->{Pos};            ### start matching from here
  332.     
  333.         ### Get from here to either \n or end of string, and add to line:
  334.         $$str =~ m/\G(.*?)((\n)|\Z)/g;         ### match to 1st \n or EOS
  335.         $line .= $1.$2;                        ### add it
  336.         *$self->{Pos} += length($1.$2);        ### move fwd by len matched
  337.         return $line if $3;                    ### done, got line with "\n"
  338.         }
  339.         return ($line eq '') ? undef : $line;  ### return undef if EOF
  340.     }
  341.  
  342.     ### Case 3: $/ is ref to int.  Bail out.
  343.     elsif (ref($/)) {
  344.         croak '$/ given as a ref to int; currently unsupported';
  345.     }
  346.  
  347.     ### Case 4: $/ is either "" (paragraphs) or something weird...
  348.     ###         Bail for now.
  349.     else {                
  350.         croak '$/ as given is currently unsupported';
  351.     }
  352. }
  353.  
  354. #------------------------------
  355.  
  356. =item getlines
  357.  
  358. I<Instance method.>
  359. Get all remaining lines.
  360. It will croak() if accidentally called in a scalar context.
  361.  
  362. =cut
  363.  
  364. sub getlines {
  365.     my $self = shift;
  366.     wantarray or croak("can't call getlines in scalar context!");
  367.     my ($line, @lines);
  368.     push @lines, $line while (defined($line = $self->getline));
  369.     @lines;
  370. }
  371.  
  372. #------------------------------
  373.  
  374. =item print ARGS...
  375.  
  376. I<Instance method.>
  377. Print ARGS to the underlying array.
  378.  
  379. Currently, this always causes a "seek to the end of the array"
  380. and generates a new array entry.  This may change in the future.
  381.  
  382. =cut
  383.  
  384. sub print {
  385.     my $self = shift;
  386.     push @{*$self->{AR}}, join('', @_);      ### add the data
  387.     $self->_setpos_to_eof;
  388.     1;
  389. }
  390.  
  391. #------------------------------
  392.  
  393. =item read BUF, NBYTES, [OFFSET];
  394.  
  395. I<Instance method.>
  396. Read some bytes from the array.
  397. Returns the number of bytes actually read, 0 on end-of-file, undef on error.
  398.  
  399. =cut
  400.  
  401. sub read {
  402.     my $self = $_[0];
  403.     ### we must use $_[1] as a ref
  404.     my $n    = $_[2];
  405.     my $off  = $_[3] || 0;
  406.  
  407.     ### print "getline\n";
  408.     my $justread;
  409.     my $len;
  410.     ($off ? substr($_[1], $off) : $_[1]) = '';
  411.  
  412.     ### Stop when we have zero bytes to go, or when we hit EOF:
  413.     my @got;
  414.     until (!$n or $self->eof) {       
  415.         ### If at end of current string, go forward to next one (won't be EOF):
  416.         if ($self->_eos) {
  417.             ++*$self->{Str};
  418.             *$self->{Pos} = 0;
  419.         }
  420.  
  421.         ### Get longest possible desired substring of current string:
  422.         $justread = substr(*$self->{AR}[*$self->{Str}], *$self->{Pos}, $n);
  423.         $len = length($justread);
  424.         push @got, $justread;
  425.         $n            -= $len; 
  426.         *$self->{Pos} += $len;
  427.     }
  428.     $_[1] .= join('', @got);
  429.     return length($_[1])-$off;
  430. }
  431.  
  432. #------------------------------
  433.  
  434. =item write BUF, NBYTES, [OFFSET];
  435.  
  436. I<Instance method.>
  437. Write some bytes into the array.
  438.  
  439. =cut
  440.  
  441. sub write {
  442.     my $self = $_[0];
  443.     my $n    = $_[2];
  444.     my $off  = $_[3] || 0;
  445.  
  446.     my $data = substr($_[1], $n, $off);
  447.     $n = length($data);
  448.     $self->print($data);
  449.     return $n;
  450. }
  451.  
  452.  
  453. =back
  454.  
  455. =cut
  456.  
  457.  
  458.  
  459. #==============================
  460.  
  461. =head2 Seeking/telling and other attributes
  462.  
  463. =over 4
  464.  
  465. =cut
  466.  
  467. #------------------------------
  468.  
  469. =item autoflush
  470.  
  471. I<Instance method.>
  472. No-op, provided for OO compatibility.
  473.  
  474. =cut
  475.  
  476. sub autoflush {} 
  477.  
  478. #------------------------------
  479.  
  480. =item binmode
  481.  
  482. I<Instance method.>
  483. No-op, provided for OO compatibility.
  484.  
  485. =cut
  486.  
  487. sub binmode {} 
  488.  
  489. #------------------------------
  490.  
  491. =item clearerr
  492.  
  493. I<Instance method.>  Clear the error and EOF flags.  A no-op.
  494.  
  495. =cut
  496.  
  497. sub clearerr { 1 }
  498.  
  499. #------------------------------
  500.  
  501. =item eof
  502.  
  503. I<Instance method.>  Are we at end of file?
  504.  
  505. =cut
  506.  
  507. sub eof {
  508.     ### print "checking EOF [*$self->{Str}, *$self->{Pos}]\n";
  509.     ### print "SR = ", $#{*$self->{AR}}, "\n";
  510.  
  511.     return 0 if (*{$_[0]}->{Str} < $#{*{$_[0]}->{AR}});  ### before EOA
  512.     return 1 if (*{$_[0]}->{Str} > $#{*{$_[0]}->{AR}});  ### after EOA
  513.     ###                                                  ### at EOA, past EOS:
  514.     ((*{$_[0]}->{Str} == $#{*{$_[0]}->{AR}}) && ($_[0]->_eos)); 
  515. }
  516.  
  517. #------------------------------
  518. #
  519. # _eos
  520. #
  521. # I<Instance method, private.>  Are we at end of the CURRENT string?
  522. #
  523. sub _eos {
  524.     (*{$_[0]}->{Pos} >= length(*{$_[0]}->{AR}[*{$_[0]}->{Str}])); ### past last char
  525. }
  526.  
  527. #------------------------------
  528.  
  529. =item seek POS,WHENCE
  530.  
  531. I<Instance method.>
  532. Seek to a given position in the stream.
  533. Only a WHENCE of 0 (SEEK_SET) is supported.
  534.  
  535. =cut
  536.  
  537. sub seek {
  538.     my ($self, $pos, $whence) = @_; 
  539.  
  540.     ### Seek:
  541.     if    ($whence == 0) { $self->_seek_set($pos); }
  542.     elsif ($whence == 1) { $self->_seek_cur($pos); }
  543.     elsif ($whence == 2) { $self->_seek_end($pos); }
  544.     else                 { croak "bad seek whence ($whence)" }
  545. }
  546.  
  547. #------------------------------
  548. #
  549. # _seek_set POS
  550. #
  551. # Instance method, private.
  552. # Seek to $pos relative to start:
  553. #
  554. sub _seek_set {
  555.     my ($self, $pos) = @_; 
  556.  
  557.     ### Advance through array until done:
  558.     my $istr = 0;
  559.     while (($pos >= 0) && ($istr < scalar(@{*$self->{AR}}))) {
  560.     if (length(*$self->{AR}[$istr]) > $pos) {   ### it's in this string! 
  561.         return $self->setpos([$istr, $pos]);
  562.     }
  563.     else {                                      ### it's in next string
  564.         $pos -= length(*$self->{AR}[$istr++]);  ### move forward one string
  565.     }
  566.     }
  567.     ### If we reached this point, pos is at or past end; zoom to EOF:
  568.     return $self->_setpos_to_eof;
  569. }
  570.  
  571. #------------------------------
  572. #
  573. # _seek_cur POS
  574. #
  575. # Instance method, private.
  576. # Seek to $pos relative to current position.
  577. #
  578. sub _seek_cur {
  579.     my ($self, $pos) = @_; 
  580.     $self->_seek_set($self->tell + $pos);
  581. }
  582.  
  583. #------------------------------
  584. #
  585. # _seek_end POS
  586. #
  587. # Instance method, private.
  588. # Seek to $pos relative to end.
  589. # We actually seek relative to beginning, which is simple.
  590. #
  591. sub _seek_end {
  592.     my ($self, $pos) = @_; 
  593.     $self->_seek_set($self->_tell_eof + $pos);
  594. }
  595.  
  596. #------------------------------
  597.  
  598. =item tell
  599.  
  600. I<Instance method.>
  601. Return the current position in the stream, as a numeric offset.
  602.  
  603. =cut
  604.  
  605. sub tell {
  606.     my $self = shift;
  607.     my $off = 0;
  608.     my ($s, $str_s);
  609.     for ($s = 0; $s < *$self->{Str}; $s++) {   ### count all "whole" scalars
  610.     defined($str_s = *$self->{AR}[$s]) or $str_s = '';
  611.     ###print STDERR "COUNTING STRING $s (". length($str_s) . ")\n";
  612.     $off += length($str_s);
  613.     }
  614.     ###print STDERR "COUNTING POS ($self->{Pos})\n";
  615.     return ($off += *$self->{Pos});            ### plus the final, partial one
  616. }
  617.  
  618. #------------------------------
  619. #
  620. # _tell_eof
  621. #
  622. # Instance method, private.
  623. # Get position of EOF, as a numeric offset.
  624. # This is identical to the size of the stream - 1.
  625. #
  626. sub _tell_eof {
  627.     my $self = shift;
  628.     my $len = 0;
  629.     foreach (@{*$self->{AR}}) { $len += length($_) }
  630.     $len;
  631. }
  632.  
  633. #------------------------------
  634.  
  635. =item setpos POS
  636.  
  637. I<Instance method.>
  638. Seek to a given position in the array, using the opaque getpos() value.
  639. Don't expect this to be a number.
  640.  
  641. =cut
  642.  
  643. sub setpos { 
  644.     my ($self, $pos) = @_;
  645.     (ref($pos) eq 'ARRAY') or
  646.     die "setpos: only use a value returned by getpos!\n";
  647.     (*$self->{Str}, *$self->{Pos}) = @$pos;
  648. }
  649.  
  650. #------------------------------
  651. #
  652. # _setpos_to_eof
  653. #
  654. # Fast-forward to EOF.
  655. #
  656. sub _setpos_to_eof {
  657.     my $self = shift;
  658.     $self->setpos([scalar(@{*$self->{AR}}), 0]);
  659. }
  660.  
  661. #------------------------------
  662.  
  663. =item getpos
  664.  
  665. I<Instance method.>
  666. Return the current position in the array, as an opaque value.
  667. Don't expect this to be a number.
  668.  
  669. =cut
  670.  
  671. sub getpos {
  672.     [*{$_[0]}->{Str}, *{$_[0]}->{Pos}];
  673. }
  674.  
  675. #------------------------------
  676.  
  677. =item aref
  678.  
  679. I<Instance method.>
  680. Return a reference to the underlying array.
  681.  
  682. =cut
  683.  
  684. sub aref {
  685.     *{shift()}->{AR};
  686. }
  687.  
  688. =back
  689.  
  690. =cut
  691.  
  692. #------------------------------
  693. # Tied handle methods...
  694. #------------------------------
  695.  
  696. ### Conventional tiehandle interface:
  697. sub TIEHANDLE { (defined($_[1]) && UNIVERSAL::isa($_[1],"IO::ScalarArray"))
  698.             ? $_[1] 
  699.             : shift->new(@_) }
  700. sub GETC      { shift->getc(@_) }
  701. sub PRINT     { shift->print(@_) }
  702. sub PRINTF    { shift->print(sprintf(shift, @_)) }
  703. sub READ      { shift->read(@_) }
  704. sub READLINE  { wantarray ? shift->getlines(@_) : shift->getline(@_) }
  705. sub WRITE     { shift->write(@_); }
  706. sub CLOSE     { shift->close(@_); }
  707. sub SEEK      { shift->seek(@_); }
  708. sub TELL      { shift->tell(@_); }
  709. sub EOF       { shift->eof(@_); }
  710.  
  711. #------------------------------------------------------------
  712.  
  713. 1;
  714. __END__
  715.  
  716. # SOME PRIVATE NOTES:
  717. #
  718. #     * The "current position" is the position before the next
  719. #       character to be read/written.
  720. #
  721. #     * Str gives the string index of the current position, 0-based
  722. #
  723. #     * Pos gives the offset within AR[Str], 0-based.
  724. #
  725. #     * Inital pos is [0,0].  After print("Hello"), it is [1,0].
  726.  
  727.  
  728.  
  729. =head1 WARNINGS
  730.  
  731. Perl's TIEHANDLE spec was incomplete prior to 5.005_57;
  732. it was missing support for C<seek()>, C<tell()>, and C<eof()>.
  733. Attempting to use these functions with an IO::ScalarArray will not work
  734. prior to 5.005_57. IO::ScalarArray will not have the relevant methods
  735. invoked; and even worse, this kind of bug can lie dormant for a while.
  736. If you turn warnings on (via C<$^W> or C<perl -w>),
  737. and you see something like this...
  738.  
  739.     attempt to seek on unopened filehandle
  740.  
  741. ...then you are probably trying to use one of these functions
  742. on an IO::ScalarArray with an old Perl.  The remedy is to simply
  743. use the OO version; e.g.:
  744.  
  745.     $AH->seek(0,0);    ### GOOD: will work on any 5.005
  746.     seek($AH,0,0);     ### WARNING: will only work on 5.005_57 and beyond
  747.  
  748.  
  749.  
  750. =head1 VERSION
  751.  
  752. $Id: ScalarArray.pm,v 2.103 2001/08/09 08:04:44 eryq Exp $
  753.  
  754.  
  755. =head1 AUTHOR
  756.  
  757. =head2 Principal author
  758.  
  759. Eryq (F<eryq@zeegee.com>).
  760. President, ZeeGee Software Inc (F<http://www.zeegee.com>).
  761.  
  762.  
  763. =head2 Other contributors
  764.  
  765. Thanks to the following individuals for their invaluable contributions
  766. (if I've forgotten or misspelled your name, please email me!):
  767.  
  768. I<Andy Glew,>
  769. for suggesting C<getc()>.
  770.  
  771. I<Brandon Browning,>
  772. for suggesting C<opened()>.
  773.  
  774. I<Eric L. Brine,>
  775. for his offset-using read() and write() implementations.
  776.  
  777. I<Doug Wilson,>
  778. for the IO::Handle inheritance and automatic tie-ing.
  779.  
  780. =cut
  781.  
  782. #------------------------------
  783. 1;
  784.  
  785.