Article 11514 of comp.lang.perl:
Path: feenix.metronet.com!news.utdallas.edu!wupost!math.ohio-state.edu!howland.reston.ans.net!agate!boulder!wraeththu.cs.colorado.edu!tchrist
From: Tom Christiansen <[email protected]>
Newsgroups: comp.lang.perl
Subject: Re: how to unread a line
Date: 14 Mar 1994 18:59:42 GMT
Organization: University of Colorado, Boulder
Lines: 154
Message-ID: <[email protected]>
References: <[email protected]>
Reply-To: [email protected] (Tom Christiansen)
NNTP-Posting-Host: wraeththu.cs.colorado.edu
Originator: [email protected]

:-> In comp.lang.perl, [email protected] writes:
:Hi all,
:
:how do I unread a line being read from a file?
:
:ie: given
:               $/ = "\nFrom ";
:               $rest = <FILE>;
:
:the last characters of $rest will be  ".....\nFrom "
:and i'd like to put the "From " back onto the input stack as it were
:I guess this is possible, but how.
:What i actually want to do is read the rest of a email message until the next
:message.

While I think you should probably find a better way to do it, I once wrote
this as an exercise.  I found it to be more complex than I thought it would
be, mostly due to the interaction of putting and getting both lines and
characters from the same filehandle.

-tom

package pushback;

# Functions:
#       &open(FH, path)                 like regular open
#       &close(FH)                      like regular close
#
#       &getline(FH)                    like <FH>
#       &getc(FH)                       like getc(FH)
#
#       &ungetline(FH,list)             puts list of lines back in LIFO order
#       &ungetc(FH,string)              prepends string to next gotten line
#
#       &flushin(FH)                    clears FH's pushback buffer
#       &eof(FH)                        is pushback empty AND real eof?


# PACKAGE "GLOBALS"  (In Capital Letters)
#       1) Each filehandle in any package has an array @pushback'${package}_${fh}__lines
#               containing the pushback buffer.
#
#       2) @PB_Lines is an alias for that per-filehandle buffer
#
#       3) %Ungotten is indexed by filehandle and tells us whether
#               there are pushed back chars (rather than lines)

sub main'getline {
   local($fh, *PB_Lines) = &getputback;

   # shouldn't have to check wantarray here -- it should know better
   if (!@PB_Lines) {
       if (wantarray) {
          local(@list);
          return @list = <$fh>;
       } else {
          return scalar <$fh>;
       }
   }

   if (!wantarray) {
       if ($Ungotten{$fh}) {
           $Ungotten{$fh} = 0;
           return pop(@PB_Lines).<$fh>;
       }
       return pop @PB_Lines;
   }

   if ($Ungotten{$fh}) {
       $PB_Lines[0] .= <$fh>;
       $Ungotten{$fh} = 0;
   }

   local(@rlist) = reverse(@PB_Lines);
   @PB_Lines = ();
   push(@rlist, <$fh>);
   return @rlist;
}

sub main'ungetline {
   local($fh, *PB_Lines) = &getputback;
   if ($Ungotten{$fh}) {
       $PB_Lines[0] .= shift;
       $Ungotten{$fh} = 0;
   }
   push(@PB_Lines,reverse @_) if @_;
}

sub main'getc {
   local($fh, *PB_Lines) = &getputback;
   local($char);

   if (@PB_Lines) {
       $char = substr($PB_Lines[0], 0, 1);
       $Ungotten{$fh}--;
       substr($PB_Lines[0], 0, 1) = '';
   } else {
       $char = getc($fh);
   }
   return $char;
}

sub main'ungetc {
   local($fh, *PB_Lines) = &getputback;
   local($char) = $_[0];
   $Ungotten{$fh} += length($char)     unless @PB_Lines;
   substr($PB_Lines[0], 0, 0) = $char;
}


sub main'eof {
   local($fh, *PB_Lines) = &getputback;
   @PB_Lines == 0 && eof($fh);
}

sub main'open {
   local($fh, *PB_Lines) = &getputback;
   local($path) = shift;
   @PB_Lines = ();
   open($fh, $path);
}

sub main'close {
   local($fh, *PB_Lines) = &getputback;
   @PB_Lines = ();
   close($fh);
}

sub main'flushin {
   local($fh, *PB_Lines) = &getputback;
   local(@buf) = @PB_Lines;
   @PB_Lines = ();
   return @buf;
}

sub getputback {
   local($myfh) = shift || STDIN;
   local($package) = caller(1); # my grandparent
   local($ptrnam) = $myfh;
   $ptrnam =~ s/^[^']+$/$package'$&/;
   $ptrnam =~ s/'/_/;
   return ($myfh, "pushback'${ptrnam}__lines");
}

# make some aliases just in case...

*main'ungetchar = *main'ungetc;
*main'getchar   = *main'getc;

1;
--
   Tom Christiansen      [email protected]
     "Will Hack Perl for Fine Food and Fun"
       Boulder Colorado  303-444-3212