#! /usr/bin/ruby

class Tail
	BUF_SIZE = 4096
	
	attr_accessor :lines, :follow, :file, :quit, :_f
	private :_f
	
	def initialize(lines,follow,file)
		self.lines = lines ? lines.to_i : 10
		self.follow = follow ? follow : false
		self.file = file
		quit = false
		self.send(:_f=,File.open(file,'r').binmode)
		self
	end
	
	def _tailn
		lines = self.lines
		size = _f.lstat.size
		pos = size
		tail = ''
		tails = []
		until(pos <= 0) do
			buf = ''
			if pos < BUF_SIZE
				_f.pos = 0
				buf = _f.read(pos)
			else
				_f.pos = pos - BUF_SIZE
				buf = _f.read(BUF_SIZE)
			end
			raise IOError.new('cannot read file.') unless(buf)
			pos -= buf.size
			tail = buf + tail
			break if tail.split(/(?:\x0d\x0a|\x0d|\x0a)/).size > lines
		end
		tail = tail.gsub(/(\x0d\x0a|\x0d|\x0a)/,"\n")
		tails = tail.split(/\n/)
		tails.push("") if tail =~ /\n\z/
		print tails.reverse.first(lines).reverse.join("\n")
		STDOUT.flush
		_f.pos = size
		self
	end
	
	def _tailf
		until(quit) do
			buf = ''
			buf = _f.read
			raise IOException.new('cannot read file.') unless(buf)
			if (buf.size > 0)
				buf = buf.gsub(/(\x0d\x0a|\x0d|\x0a)/,"\n")
				print buf
				STDOUT.flush
			end
			sleep(1)
		end
		self
	end
	
	def tail
		_tailn
		_tailf if follow
	end
	
	def finalize
		_f.close
	end
end

require "optparse"

tail = nil

conf = Hash.new
opts = OptionParser.new
opts.on("-n MANDATORY") { |v| conf[:n] = v }
opts.on("-f") { |v| conf[:f] = v }
opts.parse!

if ARGV.size == 0
	puts "usage: ruby #{$0} [-n lines] [-f] filename"
else
	begin
		tail = Tail.new(conf[:n],conf[:f],ARGV.shift)
		Signal.trap(:INT) { tail.quit = true }
		tail.tail
		tail.finalize
	rescue => e
		puts e.message
	end
end
