#! /usr/bin/perl

use strict;

package Tail;

$Tail::BUF_SIZE = 4096;

sub new {
	my	($pkg,$lines,$follow,$file) = @_;
	my	$self = bless({},$pkg);
	$self->file($file);
	$self->lines(defined($lines) ? $lines : 10);
	$self->follow(defined($follow) ? $follow : 0);
	$self;
}

sub lines {
	my	($self,$lines) = @_;
	$self->{'lines'} = $lines if (defined($lines));
	$self->{'lines'};
}

sub follow {
	my	($self,$follow) = @_;
	$self->{'follow'} = $follow if (defined($follow));
	$self->{'follow'};
}

sub file {
	my	($self,$file) = @_;
	$self->{'file'} = $file if (defined($file));
	$self->{'file'};
}

sub quit {
	my	($self,$quit) = @_;
	$self->{'quit'} = $quit if (defined($quit));
	$self->{'quit'};
}

sub _fh {
	my	($self,$fh) = @_;
	$self->{'_fh'} = $fh if (defined($fh));
	$self->{'_fh'};
}

sub initialize {
	my	($self) = @_;
	my	$fh;
	open ($fh,"<".$self->file()) || die "cannot open file.";
	binmode($fh);
	$self->_fh($fh);
	$self->quit(0);
	$|=1;
	$self;
}

sub _tailn {
	my	($self) = @_;
	my	$lines = $self->lines();
	my	$size = -s $self->file();
	my	$fh = $self->_fh();
	my	$pos = $size;
	my	$head;
	my	$tail = '';
	while ($pos > 0) {
		my	$buf = '';
		my	$ret;
		if ($pos <= $Tail::BUF_SIZE) {
			seek($fh,0,0);
			$ret = read($fh,$buf,$pos);
		} else {
			seek($fh,$pos-$Tail::BUF_SIZE,0);
			$ret = read($fh,$buf,$Tail::BUF_SIZE);
		}
		die "cannot read file." unless (defined($ret));
		$pos -= $ret;
		$tail = $buf.$tail;
		last if (scalar(split(/(?:\0x0d\x0a|\x0d|\x0d)/,$tail)) > $lines);
	}
	$pos = length($tail) - 1;
	while ($pos >= 0) {
		my	$c = substr($tail,$pos,1);
		if ($c eq "\x0d" || $c eq "\x0a") {
			last if (--$lines <= 0);
			$pos-- if ($c eq "\x0a" && $pos > 0 && substr($tail,$pos-1,1) eq "\x0d");
		}
		$pos--;
	};
	$tail =  substr($tail,$pos+1,length($tail)-$pos-1);
	$tail =~ s/(\x0d\x0a|\x0d|\x0a)/\n/g;
	print $tail;
	seek($fh,$size,0);
	$self;
}

sub _tailf {
	my	($self) = @_;
	my	$fh = $self->_fh();
	until ($self->quit()) {
		my	$buf = '';
		seek($fh,tell($fh),0);
		until (eof($fh)) {
			die "cannot read file." unless (defined(read($fh,$buf,$Tail::BUF_SIZE)));
			$buf =~ s/(\x0d\x0a|\x0d|\x0a)/\n/g;
			print $buf;
		}
		sleep(1);
	}
	$self;
}

sub tail {
	my	($self) = @_;
	$self->_tailn();
	$self->_tailf() if ($self->follow());
}

sub finalize {
	my	($self) = @_;
	close($self->_fh());
}

package main;

use Getopt::Std;

my	$opt = {};
my	$tail;
eval {
	getopts('n:f',$opt);
	$tail = new Tail($opt->{'n'},$opt->{'f'},shift(@ARGV))->initialize();
	$SIG{INT} = sub { $tail->quit(1); };
	$tail->tail();
	$tail->finalize();
};
print "error: $@" if ($@);

1;
