#! /usr/local/bin/ruby

LOGDIR = '/var/log'
FILENAME = 'security'
SUFFIX_MAX = 10

CAT = '/bin/cat'
BZCAT = '/usr/bin/bzcat'
HEAD  = '/usr/bin/head'
TAIL  = '/usr/bin/tail'
GREP  = '/usr/bin/grep'

MONTHABBR = [false, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

def m_to_i(str)
  MONTHABBR.index(str)
end

def i_to_m(i)
  MONTHABBR[i]
end

MONTHLENGTH = [false, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

#month$B$r?t;z$GM?$($k$H!"(B1$B7n$+$i$=$N7n$NA0$^$G$NF|?t$r?t$($k!#(B
#$B0z?t$,(B1$B$N$H$-$O(B0$B$rJV$9!#(B
def monthlength(month)  
  sum = 0
  MONTHLENGTH[1...(month)].each do |m|
    sum += m
  end
  return sum
end

def date_to_i(datetbl)
    (((monthlength(datetbl[:mon]) + datetbl[:mday].to_i)*24 + datetbl[:hour].to_i)*60 + datetbl[:min].to_i) + datetbl[:sec].to_i
end

#'Nov 4 20:10:11'$B$H$$$&$h$&$JJ8;zNs$rM?$($F(B
# hash$B$rJV$9!#(B
def str_to_date_hash(str)
  month, mday, time = str.split(' ')
  hour, min, sec = time.split(':')
  {:mon=>m_to_i(month), :mday=>mday, :hour=>hour,:min=>min, :sec=>sec}
end

def date_compare(str1, str2)
  date_to_i(str_to_date_hash(str1)) <=> date_to_i(str_to_date_hash(str2))
end

#$BG/1[$7H=CG(B
def over_new_years_day(started, ended)
  date_compare(started, ended)
end

# $B%Q%9$H(Bbzip2$B$5$l$F$$$k$+$rM?$($F!"(B
# $B%Q%9$K=q$+$l$F$$$k%m%0$N=i$a$N;~9o$H:G8e$N;~9o$H$rI=$9(B
# $BJ8;zNs$rJV$9!#(B
# when flag == true, suppose that path is bzip2ed.
# 
def logdaterange(path, flag=false)

  cat = if flag then BZCAT else CAT end
  fline = `#{cat} #{path} | #{HEAD} -n 1`.split[0,3]
  lline = `#{cat} #{path} | #{TAIL} -n 1`.split[0,3]

  return fline, lline
end

#i$B$,(B0$B$J$i(B/var/log/security
#i$B$,(B1$B$J$i(B/var/log/security.0.bz2
#i$B$,(B2$B$J$i(B/var/log/security.1.bz2
#$B$J$I$HJV$9!#(B
def i_to_path(i)
  suffix = if i == 0
	     ''
	   else
	     '.' + (i - 1).to_s + '.bz2'
	   end
  return LOGDIR + '/' + FILENAME + suffix
end

#/var/log/security*$B$J%U%!%$%k$K=q$+$l$?%m%0$rD4$Y$F!"(B
#$B$=$l$>$l$N%U%!%$%k$N%m%0$N:G=i$N;~9o$H:G8e$N;~9o$NG[Ns$rJV$9!#(B
def chkall()
  date_ary = Array.new
  
  (SUFFIX_MAX+1).times do |i|
    date = logdaterange(i_to_path(i), (i != 0) )
    date_ary << date
  end
  return date_ary
end

#$B$"$k;~9o$,$I$N%U%!%$%k$K4^$^$l$F$$$k$+8!::$9$k!#(B
#$BJV$jCM$O(Bdate_ary$B$N(Bindex$B!#(B
def dateinclude(date_ary, date)
  target = nil

  date_ary.each_index do |i|
    if date_compare(date_ary[i][0].join(' '), date) != 1 then
      if date_compare(date, date_ary[i][1].join(' ')) != 1 then
	target = i
      end
    end
  end
  return target
end

#$B4|4V$r;XDj$7$F!"$=$N4|4V$N%m%0$,=q$+$l$?%U%!%$%k$N(Bindex$B$NG[Ns$rJV$9!#(B
#end_time$B$N=i4|CM$O(Bfalse$B!#(Bfalse$B$OB8:_$9$k%m%0$N:G?7$N;~9o$r<($9!#(B

# if end_time==false, search up to end of file*s*.
# return value is array of file path 
# which include logs from start_time to end_time.
def pickup(start_time, end_time=false)
  date_ary = chkall

  st = dateinclude(date_ary, start_time)
  et = if end_time
	 dateinclude(date_ary, end_time)
       end
  
  if !st or !et
    print "no logs within such range\n"
    exit
  end
  path_i_ary = Array.new
  (et..st).each do |i|
    path_i_ary << i
  end
  return path_i_ary
end

#$BHO0O$rJ8;zNs$G;XDj$7$F!"(B
#$B$=$NHO0O$r4^$`%m%0$r=PNO$9$k!#(B
#$BHO0O$r4^$^$J$$%m%0%U%!%$%k$r?($i$J$$$N$G!"C1$K(Bbzcat$B$9$k$h$j$bB.$$(B($B$O$:(B)
def rangeddata(s_t, e_t)
    fnameary = pickup(s_t, e_t)
    data = ''
    size = fnameary.size
    fnameary.each do |i|
      flag = (i != 0)
      cat = if flag then BZCAT else CAT end
      data = `#{cat} #{i_to_path(i)}` + data
    end
  return data
end


#$B0z?tI>2A(B
if $0 == __FILE__
  #$B0lMwI=<(%b!<%I(B
  if ARGV.size == 0
    date_ary = chkall()
    fmt1 = "%-23s: %-15s : %-15s\n"
    fmt2  = "%-23s: %s %2s %s : %s %2s %s\n"
    
    printf fmt1, 'path', 'start_time', 'end_time'
    date_ary.size.times do |i|
      fd = date_ary[i].first
      ld = date_ary[i].last
      printf fmt2, i_to_path(i), fd[0], fd[1], fd[2], ld[0], ld[1], ld[2]
    end
  end
end
