File-ReadBackwards-1.06/0000755000175000017500000000000014040433660014356 5ustar ollisgollisgFile-ReadBackwards-1.06/META.yml0000664000175000017500000000125214040433660015631 0ustar ollisgollisg--- abstract: unknown author: - unknown build_requires: ExtUtils::MakeMaker: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: File-ReadBackwards no_index: directory: - t - inc resources: bugtracker: https://github.com/uperl/File-ReadBackwards/issues homepage: https://metacpan.org/pod/File::ReadBackwards repository: https://github.com/uperl/File-ReadBackwards.git version: '1.06' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' File-ReadBackwards-1.06/t/0000755000175000017500000000000014040433660014621 5ustar ollisgollisgFile-ReadBackwards-1.06/t/large_file.t0000755000175000017500000000742014040432044017100 0ustar ollisgollisg#!/usr/local/bin/perl -ws use strict ; use warnings ; use Carp ; use Config ; use Fcntl qw( :seek ) ; use File::Temp qw( tempfile ); use Test::More ; use File::ReadBackwards ; # NOTE: much of this code was taken from the core perl test script # ops/lfs.t. it was modified to test File::ReadBackwards and large files my (undef, $test_file) = tempfile('bw-XXXXXX', SUFFIX => '.data', TMPDIR => 1, CLEANUP => 1); my @test_lines = ( "3rd from last line\n", "2nd from last\n", "last line\n", ) ; my $test_text = join '', @test_lines ; sub skip_all_tests { my( $skip_text ) = @_ ; # unlink $test_file ; plan skip_all => $skip_text ; } if( $Config{lseeksize} < 8 ) { skip_all_tests( "no 64-bit file offsets\n" ) ; } unless( $Config{uselargefiles} ) { skip_all_tests( "no large file support\n" ) ; } unless ( have_sparse_files() ) { skip_all_tests( "no sparse file support\n" ) ; } # run the long seek code below in a subprocess in case it exits with a # signal my $rc = system $^X, '-e', <<"EOF"; open(BIG, ">$test_file"); seek(BIG, 5_000_000_000, 0); print BIG "$test_text" ; exit 0; EOF if( $rc ) { my $error = 'signal ' . ($rc & 0x7f) ; skip_all_tests( "seeking past 2GB failed: $error" ) ; } open(BIG, ">$test_file"); unless( seek(BIG, 5_000_000_000, 0) ) { skip_all_tests( "seeking past 2GB failed: $!" ) ; } # Either the print or (more likely, thanks to buffering) the close will # fail if there are are filesize limitations (process or fs). my $print = print BIG $test_text ; my $close = close BIG; unless ($print && $close) { print "# print failed: $!\n" unless $print; print "# close failed: $!\n" unless $close; if( $! =~/too large/i ) { skip_all_tests( 'writing past 2GB failed: process limits?' ) ; } if( $! =~ /quota/i ) { skip_all_tests( 'filesystem quota limits?' ) ; } skip_all_tests( "large file error: $!" ) ; } plan tests => 2 ; my $bw = File::ReadBackwards->new( $test_file ) or die "can't open $test_file: $!" ; my $line = $bw->readline() ; is( $line, $test_lines[-1], 'last line' ) ; $line = $bw->readline() ; is( $line, $test_lines[-2], 'next to last line' ) ; unlink $test_file ; exit ; ######## subroutines # this is lifted wholesale from t/op/lfs.t in perl. Also, Uri is the # wind beneath my wings. sub have_sparse_files { # don't even try for spare files on some OSs return 0 if { map { $_ => 1 } qw( MSWin32 NetWare VMS unicos ) }->{ $^O }; # take that, readability. my (undef,$big0) = tempfile(); my (undef,$big1) = tempfile(); my (undef,$big2) = tempfile(); # We'll start off by creating a one megabyte file which has # only three "true" bytes. If we have sparseness, we should # consume less blocks than one megabyte (assuming nobody has # one megabyte blocks...) open(BIG, ">$big1") or die "open $big1 failed: $!"; binmode(BIG) or die "binmode $big1 failed: $!"; seek(BIG, 1_000_000, SEEK_SET) or die "seek $big1 failed: $!"; print BIG "big" or die "print $big1 failed: $!"; close(BIG) or die "close $big1 failed: $!"; my @s1 = stat($big1); # diag "s1 = @s1"; open(BIG, ">$big2") or die "open $big2 failed: $!"; binmode(BIG) or die "binmode $big2 failed: $!"; seek(BIG, 2_000_000, SEEK_SET) or die "seek $big2 failed: $!"; print BIG "big" or die "print $big2 failed: $!"; close(BIG) or die "close $big2 failed: $!"; my @s2 = stat($big2); # diag "s2 = @s2"; unless ( $s1[7] == 1_000_003 && $s2[7] == 2_000_003 && $s1[11] == $s2[11] && $s1[12] == $s2[12] && $s1[12] > 0 ) { # diag 'no sparse files. sad face.'; return 0; } # diag 'we seem to have sparse files...'; return 1 ; } File-ReadBackwards-1.06/t/bw.t0000755000175000017500000001254714040432044015425 0ustar ollisgollisg#!/usr/local/bin/perl -ws use strict ; use warnings ; use Test::More ; use Fcntl qw( :seek ) ; use File::ReadBackwards ; use Carp ; use File::Temp qw( tempfile ); our $opt_v ; my (undef, $file) = tempfile('bw-XXXXXX', SUFFIX => '.data', TMPDIR => 1, CLEANUP => 1); my $is_crlf = ( $^O =~ /win32/i || $^O =~ /vms/i ) ; print "nl\n" ; my @nl_data = init_data( "\n" ) ; plan( tests => 10 * @nl_data + 1 ) ; test_read_backwards( \@nl_data ) ; print "crlf\n" ; my @crlf_data = init_data( "\015\012" ) ; test_read_backwards( \@crlf_data, "\015\012" ) ; test_close() ; unlink $file ; exit ; sub init_data { my ( $rec_sep ) = @_ ; return map { ( my $data = $_ ) =~ s/RS/$rec_sep/g ; $data } '', 'RS', 'RSRS', 'RSRSRS', "\015", "\015RSRS", 'abcd', "abcdefghijRS", "abcdefghijRS" x 512, 'a' x (8 * 1024), 'a' x (8 * 1024) . '0', '0' x (8 * 1024) . '0', 'a' x (32 * 1024), join( 'RS', '00' .. '99', '' ), join( 'RS', '00' .. '99' ), join( 'RS', '0000' .. '9999', '' ), join( 'RS', '0000' .. '9999' ), ; } sub test_read_backwards { my( $data_list_ref, $rec_sep ) = @_ ; foreach my $data ( @$data_list_ref ) { # write the test data to a file in text or bin_mode if ( defined $rec_sep ) { write_bin_file( $file, $data ) ; # print "cnt: ${\scalar @rev_file_lines}\n" ; } else { write_file( $file, $data ) ; } test_data( $rec_sep ) ; test_tell_handle( $rec_sep ) ; } } sub test_data { my( $rec_sep ) = @_ ; # slurp in the file and reverse the list of lines to get golden data my @rev_file_lines = reverse read_bin_file( $file ) ; # convert CR/LF to \n if needed - based on OS or we are testing CR/LF if ( $is_crlf || $rec_sep && $rec_sep eq "\015\012" ) { s/\015\012\z/\n/ for @rev_file_lines ; } # open the file with backwards and read in the lines my $bw = File::ReadBackwards->new( $file, $rec_sep ) or die "can't open $file: $!" ; my( @bw_file_lines ) ; while ( 1 ) { my $line = $bw->readline() ; last unless defined( $line ) ; push( @bw_file_lines, $line ) ; $line = $bw->getline() ; last unless defined( $line ) ; push( @bw_file_lines, $line ) ; } # while ( defined( my $line = $bw->readline() ) ) { # push( @bw_file_lines, $line) ; # } # see if we close cleanly ok( $bw->close(), 'close' ) ; # compare the golden lines to the backwards lines if ( eq_array( \@rev_file_lines, \@bw_file_lines ) ) { ok( 1, 'read' ) ; return ; } # test failed so dump the different lines if verbose ok( 0, 'read' ) ; return unless $opt_v ; print "[$rev_file_lines[0]]\n" ; print unpack( 'H*', $rev_file_lines[0] ), "\n" ; print unpack( 'H*', $bw_file_lines[0] ), "\n" ; #print "REV ", unpack( 'H*', join '',@rev_file_lines ), "\n" ; #print "BW ", unpack( 'H*', join '',@bw_file_lines ), "\n" ; } sub test_tell_handle { my( $rec_sep ) = @_ ; # open the file backwards again to test tell and get_handle methods my $bw = File::ReadBackwards->new( $file, $rec_sep ) or die "can't open $file: $!" ; # read the last line in my $bw_line = $bw->readline() ; # get the current seek position my $pos = $bw->tell() ; #print "BW pos = $pos\n" ; if ( $bw->eof() ) { ok( 1, "skip tell - at eof" ) ; ok( 1, "skip get_handle - at eof" ) ; } else { # save the current $/ so we can reassign it if it $rec_sep isn't set my $old_rec_sep = $/ ; local $/ = $rec_sep || $old_rec_sep ; # open a new regular file and seek to this spot. open FH, $file or die "tell open $!" ; seek FH, $pos, SEEK_SET or die "tell seek $!" ; # read in the next line and clean up the ending CR/LF my $fw_line = ; $fw_line =~ s/\015\012\z/\n/ ; # print "BW [", unpack( 'H*', $bw_line ), # "] TELL [", unpack( 'H*', $fw_line), "]\n" if $bw_line ne $fw_line ; # compare the backwards and forwards lines is ( $bw_line, $fw_line, "tell check" ) ; # get the handle and seek to the current spot my $fh = $bw->get_handle() ; # read in the next line and clean up the ending CR/LF my $fh_line = <$fh> ; $fh_line =~ s/\015\012\z/\n/ ; # print "BW [", unpack( 'H*', $bw_line ), # "] HANDLE [", unpack( 'H*', $fh_line), "]\n" if $bw_line ne $fh_line ; # compare the backwards and forwards lines is ( $bw_line, $fh_line, "get_handle" ) ; } ok( $bw->close(), 'close2' ) ; } sub test_close { write_file( $file, <new( $file ) or die "can't open $file: $!" ; my $line = $bw->readline() ; $bw->close() ; if ( $bw->readline() ) { ok( 0, 'close' ) ; return ; } ok( 1, 'close' ) ; } sub read_file { my( $file_name ) = shift ; local( *FH ) ; open( FH, $file_name ) || carp "can't open $file_name $!" ; local( $/ ) unless wantarray ; } # utility sub to write a file. takes a file name and a list of strings sub write_file { my( $file_name ) = shift ; local( *FH ) ; open( FH, ">$file_name" ) || carp "can't create $file_name $!" ; print FH @_ ; } sub read_bin_file { my( $file_name ) = shift ; local( *FH ) ; open( FH, $file_name ) || carp "can't open $file_name $!" ; binmode( FH ) ; local( $/ ) = shift if @_ ; local( $/ ) unless wantarray ; } # utility sub to write a file. takes a file name and a list of strings sub write_bin_file { my( $file_name ) = shift ; local( *FH ) ; open( FH, ">$file_name" ) || carp "can't create $file_name $!" ; binmode( FH ) ; print FH @_ ; } File-ReadBackwards-1.06/META.json0000664000175000017500000000223114040433660015777 0ustar ollisgollisg{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "File-ReadBackwards", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/uperl/File-ReadBackwards/issues" }, "homepage" : "https://metacpan.org/pod/File::ReadBackwards", "repository" : { "type" : "git", "url" : "https://github.com/uperl/File-ReadBackwards.git", "web" : "https://github.com/uperl/File-ReadBackwards" } }, "version" : "1.06", "x_serialization_backend" : "JSON::PP version 4.06" } File-ReadBackwards-1.06/Makefile.PL0000644000175000017500000000146214040433017016327 0ustar ollisgollisguse ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( 'NAME' => 'File::ReadBackwards', 'VERSION_FROM' => 'ReadBackwards.pm', # finds $VERSION META_MERGE => { 'meta-spec' => { version => 2 }, resources => { bugtracker => { web => 'https://github.com/uperl/File-ReadBackwards/issues', }, homepage => 'https://metacpan.org/pod/File::ReadBackwards', repository => { type => 'git', url => 'https://github.com/uperl/File-ReadBackwards.git', web => 'https://github.com/uperl/File-ReadBackwards', }, }, }, clean => { FILES => 'File-ReadBackwards-*' }, ); File-ReadBackwards-1.06/Changes0000644000175000017500000000605214040433624015654 0ustar ollisgollisgRevision history for Perl extension File::ReadBackwards. 1.06 - Drop support for Perls prior to 5.6 - Fix bug in tests that fail could fail with parallel build (rt#92313, HAARG++) - Fixed typo in documentation (rt#90009, dsteinbrunner++) - Incidentally fixed compatability with Perls that do not have `.` in @INC (rt#121002) 1.05 Sat Jun 4 02:24:35 EDT 2011 reordered Changes to be recent at the top rewritten t/large_file.t to skip non-sparse file systems code borrowed from lfs.t in newer perl - coding done by Robert Buels 1.04 Thu May 5 01:10:44 EDT 2005 added getline method and tests for it - Slaven Rezic added support and test for large files (>2GB) - Slaven Rezic 1.03 Mon Jan 24 17:57:54 EST 2005 added get_handle method and tests for it. - Mark-Jason Dominus 1.02 Fri Nov 21 01:53:42 EST 2003 fixed test problems with cr/lf files. modified module to better handle them and when the rec_sep is set all tests now work on unix and windows 1.01 Tue Oct 7 01:31:40 EDT 2003 (not released to cpan) fixed bug in test script in close_test. the write_file fails on winblows since the file is still open from the main loop. now the file is closed explicitly and tests added to cover that. - Peter J. Acklam 1.00 Mon Aug 18 02:04:24 EDT 2003 fixed doc bug for the tied interface. the module name needs quotes - from Madeleine Price added support for the tell method (including tests) - Slaven Rezic 0.99 Tue Dec 3 00:50:23 EST 2002 fixed bug where readline returns data after a close - from Khamdy 0.98 Wed Aug 21 22:51:41 EDT 2002 fixed bug with a partial buffer of '0'. - from Joe Schaefer 0.97 Sun May 26 00:28:49 EDT 2002 D'oh! call CORE::close inside close(). robin had it correct in his patch and i didn't use that. 0.96 Sun May 26 00:28:49 EDT 2002 fixed bug in close method and added test for close - from Robin Houston 0.95 Fri Apr 26 13:48:40 EDT 2002 readline returns undef on a sysseek error so it fails cleanly on pipes - from Carl Edwards added eof() method - from Antti S Lankila added flag to new() that marks the record separator as a regular expression. that used to be the default behavior and now the default is that is it a plain string like $/. - from Robin Houston added close() method - from kdamundson@mmm.com updated pod to reflect the changes 0.94 Wed Mar 8 00:12:28 EST 2000 removed use of for modifier so it works under MacPerl 0.93 Tue Mar 7 23:31:40 EST 2000 removed use of qr// so it works under MacPerl added README edited documentation 0.92 edited documentation 0.91 fixed test suite 0.90 Mon Feb 28 21:37:29 2000 - original version; created by h2xs 1.19 File-ReadBackwards-1.06/ReadBackwards.pm0000644000175000017500000002366414040433076017425 0ustar ollisgollisg# File::ReadBackwards.pm # Copyright (C) 2000-2021 by Uri Guttman. All rights reserved. # mail bugs, comments and feedback to uri@stemsystems.com package File::ReadBackwards ; use strict ; use Symbol ; use Fcntl qw( :seek O_RDONLY ) ; use Carp ; our $VERSION = '1.06' ; my $max_read_size = 1 << 13 ; my $default_rec_sep ; BEGIN { # set the default record separator according to this OS # this needs testing and expansion. # look for CR/LF types # then look for CR types # else it's a LF type if ( $^O =~ /win32/i || $^O =~ /vms/i ) { $default_rec_sep = "\015\012" ; } elsif ( $^O =~ /mac/i ) { $default_rec_sep = "\015" ; } else { $default_rec_sep = "\012" ; } # the tied interface is exactly the same as the object one, so all we # need to do is to alias the subs with typeglobs *TIEHANDLE = \&new ; *READLINE = \&readline ; *EOF = \&eof ; *CLOSE = \&close ; *TELL = \&tell ; # added getline alias for compatibility with IO::Handle *getline = \&readline ; } # constructor for File::ReadBackwards sub new { my( $class, $filename, $rec_sep, $sep_is_regex ) = @_ ; # check that we have a filename defined( $filename ) || return ; # see if this file uses the default of a cr/lf separator # those files will get cr/lf converted to \n $rec_sep ||= $default_rec_sep ; my $is_crlf = $rec_sep eq "\015\012" ; # get a handle and open the file my $handle = gensym ; sysopen( $handle, $filename, O_RDONLY ) || return ; binmode $handle ; # seek to the end of the file and get its size my $seek_pos = sysseek( $handle, 0, SEEK_END ) or return ; # get the size of the first block to read, # either a trailing partial one (the % size) or full sized one (max read size) my $read_size = $seek_pos % $max_read_size || $max_read_size ; # create the object my $self = bless { 'file_name' => $filename, 'handle' => $handle, 'read_size' => $read_size, 'seek_pos' => $seek_pos, 'lines' => [], 'is_crlf' => $is_crlf, 'rec_sep' => $rec_sep, 'sep_is_regex' => $sep_is_regex, }, $class ; return( $self ) ; } # read the previous record from the file # sub readline { my( $self, $line_ref ) = @_ ; my $read_buf ; # get the buffer of lines my $lines_ref = $self->{'lines'} ; return unless $lines_ref ; while( 1 ) { # see if there is more than 1 line in the buffer if ( @{$lines_ref} > 1 ) { # we have a complete line so return it # and convert those damned cr/lf lines to \n $lines_ref->[-1] =~ s/\015\012/\n/ if $self->{'is_crlf'} ; return( pop @{$lines_ref} ) ; } # we don't have a complete, so have to read blocks until we do my $seek_pos = $self->{'seek_pos'} ; # see if we are at the beginning of the file if ( $seek_pos == 0 ) { # the last read never made more lines, so return the last line in the buffer # if no lines left then undef will be returned # and convert those damned cr/lf lines to \n $lines_ref->[-1] =~ s/\015\012/\n/ if @{$lines_ref} && $self->{'is_crlf'} ; return( pop @{$lines_ref} ) ; } # we have to read more text so get the handle and the current read size my $handle = $self->{'handle'} ; my $read_size = $self->{'read_size'} ; # after the first read, always read the maximum size $self->{'read_size'} = $max_read_size ; # seek to the beginning of this block and save the new seek position $seek_pos -= $read_size ; sysseek( $handle, $seek_pos, SEEK_SET ) ; $self->{'seek_pos'} = $seek_pos ; # read in the next (previous) block of text my $read_cnt = sysread( $handle, $read_buf, $read_size ) ; # prepend the read buffer to the leftover (possibly partial) line my $text = $read_buf ; $text .= shift @{$lines_ref} if @{$lines_ref} ; # split the buffer into a list of lines # this may want to be $/ but reading files backwards assumes plain text and # newline separators @{$lines_ref} = ( $self->{'sep_is_regex'} ) ? $text =~ /(.*?$self->{'rec_sep'}|.+)/gs : $text =~ /(.*?\Q$self->{'rec_sep'}\E|.+)/gs ; #print "Lines \n=>", join( "<=\n=>", @{$lines_ref} ), "<=\n" ; } } sub eof { my ( $self ) = @_ ; my $seek_pos = $self->{'seek_pos'} ; my $lines_count = @{ $self->{'lines'} } ; return( $seek_pos == 0 && $lines_count == 0 ) ; } sub tell { my ( $self ) = @_ ; my $seek_pos = $self->{'seek_pos'} ; $seek_pos + length(join "", @{ $self->{'lines'} }); } sub get_handle { my ( $self ) = @_ ; my $handle = $self->{handle} ; seek( $handle, $self->tell, SEEK_SET ) ; return $handle ; } sub close { my ( $self ) = @_ ; my $handle = delete( $self->{'handle'} ) ; delete( $self->{'lines'} ) ; CORE::close( $handle ) ; } __END__ =head1 NAME File::ReadBackwards.pm -- Read a file backwards by lines. =head1 SYNOPSIS use File::ReadBackwards ; # Object interface $bw = File::ReadBackwards->new( 'log_file' ) or die "can't read 'log_file' $!" ; while( defined( $log_line = $bw->readline ) ) { print $log_line ; } # ... or the alternative way of reading until ( $bw->eof ) { print $bw->readline ; } # Tied Handle Interface tie *BW, 'File::ReadBackwards', 'log_file' or die "can't read 'log_file' $!" ; while( ) { print ; } =head1 DESCRIPTION This module reads a file backwards line by line. It is simple to use, memory efficient and fast. It supports both an object and a tied handle interface. It is intended for processing log and other similar text files which typically have their newest entries appended to them. By default files are assumed to be plain text and have a line ending appropriate to the OS. But you can set the input record separator string on a per file basis. =head1 OBJECT INTERFACE These are the methods in C' object interface: =head2 new( $file, [$rec_sep], [$sep_is_regex] ) C takes as arguments a filename, an optional record separator and an optional flag that marks the record separator as a regular expression. It either returns the object on a successful open or undef upon failure. $! is set to the error code if any. =head2 readline C takes no arguments and it returns the previous line in the file or undef when there are no more lines in the file. If the file is a non-seekable file (e.g. a pipe), then undef is returned. =head2 getline C is an alias for the readline method. It is here for compatibility with the IO::* classes which has a getline method. =head2 eof C takes no arguments and it returns true when readline() has iterated through the whole file. =head2 close C takes no arguments and it closes the handle =head2 tell C takes no arguments and it returns the current filehandle position. This value may be used to seek() back to this position using a normal file handle. =head2 get_handle C takes no arguments and it returns the internal Perl filehandle used by the File::ReadBackwards object. This handle may be used to read the file forward. Its seek position will be set to the position that is returned by the tell() method. Note that interleaving forward and reverse reads may produce unpredictable results. The only use supported at present is to read a file backward to a certain point, then use 'handle' to extract the handle, and read forward from that point. =head1 TIED HANDLE INTERFACE =head2 tie( *HANDLE, 'File::ReadBackwards', $file, [$rec_sep], [$sep_is_regex] ) The TIEHANDLE, READLINE, EOF, CLOSE and TELL methods are aliased to the new, readline, eof, close and tell methods respectively so refer to them for their arguments and API. Once you have tied a handle to File::ReadBackwards the only I/O operation permissible is <> which will read the previous line. You can call eof() and close() on the tied handle as well. All other tied handle operations will generate an unknown method error. Do not seek, write or perform any other unsupported operations on the tied handle. =head1 LINE AND RECORD ENDINGS Since this module needs to use low level I/O for efficiency, it can't portably seek and do block I/O without managing line ending conversions. This module supports the default record separators of normal line ending strings used by the OS. You can also set the separator on a per file basis. The record separator is a regular expression by default, which differs from the behavior of $/. Only if the record separator is B specified and it defaults to CR/LF (e.g, VMS, redmondware) will it will be converted to a single newline. Unix and MacOS files systems use only a single character for line endings and the lines are left unchanged. This means that for native text files, you should be able to process their lines backwards without any problems with line endings. If you specify a record separator, no conversions will be done and you will get the records as if you read them in binary mode. =head1 DESIGN It works by reading a large (8kb) block of data from the end of the file. It then splits them on the record separator and stores a list of records in the object. Each call to readline returns the top record of the list and if the list is empty it refills it by reading the previous block from the file and splitting it. When the beginning of the file is reached and there are no more lines, undef is returned. All boundary conditions are handled correctly i.e. if there is a trailing partial line (no newline) it will be the first line returned and lines larger than the read buffer size are handled properly. =head1 NOTES There is no support for list context in either the object or tied interfaces. If you want to slurp all of the lines into an array in backwards order (and you don't care about memory usage) just do: @back_lines = reverse . This module is only intended to read one line at a time from the end of a file to the beginning. =head1 AUTHOR Original author: Uri Guttman, C<< uri@stemsystems.com >> Current maintainer: Graham Ollis C<< plicease@cpan.org >> =head1 COPYRIGHT Copyright (C) 2000-2021 by Uri Guttman. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut File-ReadBackwards-1.06/MANIFEST0000644000175000017500000000035614040433660015513 0ustar ollisgollisgChanges MANIFEST Makefile.PL ReadBackwards.pm README t/bw.t t/large_file.t META.yml Module meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) File-ReadBackwards-1.06/README0000644000175000017500000000252014040432044015230 0ustar ollisgollisg File::ReadBackwards.pm This module reads a file backwards line by line. It is simple to use, memory efficient and fast. It supports both an object and a tied handle interface. It is intended for processing log and other similar text files which typically have their newest entries appended to them. By default files are assumed to be plain text and have a line ending appropriate to the OS. But you can set the input record separator string on a per file basis. PREREQUISITES Perl 5.005 or better. There are no prerequisite modules. INSTALLATION Installation is done as with most Perl modules by running these commands: perl Makefile.PL make make test make install DOCUMENTATION Documentation is in the module file in pod form. It will be installed in the normal documentation directories on your system. An HTML version of the documentation is at: https://metacpan.org/pod/File::ReadBackwards SUPPORT If you have any questions, bug reports or feedback, please open a ticket on the project GitHub: https://github.com/uperl/File-ReadBackwards/issues AVAILABILITY The latest version of File::ReadBackwards.pm will always be available in on CPAN. COPYRIGHT (C) 2000-2021 Uri Guttman. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.