#!/usr/bin/perl
#------------------------------------------------------------------------------
# Sort access log from xitami by time (taking account of time zone).
#------------------------------------------------------------------------------

use DateTime;

	# To install DateTime with ActiveState Perl as a PPM, see http://www.bribes.org/perl/ppmdir.html
	# The commands needed are:
	#   ppm install http://www.bribes.org/perl/ppm/Scalar-List-Utils.ppd
	#   ppm install http://www.bribes.org/perl/ppm/List-MoreUtils.ppd
	#   ppm install http://www.bribes.org/perl/ppm/DateTime-Locale.ppd
	#   ppm install http://www.bribes.org/perl/ppm/DateTime-TimeZone.ppd
	#   ppm install http://www.bribes.org/perl/ppm/DateTime.ppd
	#
	# See also http://datetime.perl.org/

use Acme::Comment type => 'C++'; # http://www.perl.com/lpt/a/663

my %months = ( "Jan"=>1,  "Feb"=>2,  "Mar"=>3,
               "Apr"=>4,  "May"=>5,  "Jun"=>6,
               "Jul"=>7,  "Aug"=>8,  "Sep"=>9,
               "Oct"=>10, "Nov"=>11, "Dec"=>12 );

$inputfilename = shift;
$outputfilename = shift;
if ($outputfilename eq "" or $outputfilename eq "") {
  die "Syntax: sort_access_log.pl inputfile outputfile\n";
}

open(INPUT, "<$inputfilename") or die "Can't open input filename: $inputfilename\n";
print "Input file opened: $inputfilename\n";
open(OUTPUT, ">$outputfilename") or die "Can't open output filename: $outputfilename\n";
print "Output file opened: $outputfilename\n";

my(@not_sorted) = <INPUT>; # read file into list
print "Input file read. Now sorting...\n";

/*
while (<INPUT>) {
  my($line) = $_;
  chomp($line);
  print $line . "\n";
  if ($line =~ m/^\d+\.\d+.\d+\.\d+ .+ \[(\d+)\/(\w+)\/(\d+):(\d+):(\d+):(\d+) (\S+?)\]/ ) {
    my($day) = $1;
    my($month) = $2;
    my($year) = $3;
    my($hour) = $4;
    my($minute) = $5;
    my($second) = $6;
    my($timezone) = $7;
    my($dt) = DateTime->new( year => $year,
  			     month => $months{$month},
  			     day => $day,
  			     hour => $hour,
  			     minute => $minute,
  			     second => $second,
  			     nanosecond => 0,
  			     time_zone => $timezone );
    print "DATE: " . $dt->ymd . " " . $dt->hms . " - offset " . $dt->offset() . " sec\n";
    $dt->set_time_zone("UTC");
    print "UTC: " . $dt->ymd . " " . $dt->hms . " - offset " . $dt->offset() . " sec\n";
  }
  else {
    print "No match\n";
  }
}
*/

@sorted = sort {
  # sort functions should return 1 if $a should come after $b, -1 if $b should come after $a, 0 if equal
  # built-in functions are <=> (for numbers) and cmp (for strings);
  # <=> returns -1 if $a<$b, 0 if $a==$b, 1 if $a>$b
  # see http://www.perlfect.com/articles/sorting.shtml
  
  # log file lines have the following format:
  # 117.96.95.48 - - [28/Jun/2008:15:04:33 +0000] "GET /whisker/version_tracker/SeekTake.txt HTTP/1.1" 200 16779 "http://www.google.co.in/search?q=CRecordset+CTime&btnG=Search&hl=en&sa=2" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
  
  # See http://www.troubleshooters.com/codecorn/littperl/perlreg.htm
  # note:
  # ^ beginning of line
  # \d digit
  # + one or more of them
  # \. escaped full stop
  # \[ escaped square bracket
  # ( ) mark things we want to retrieve
  # . any character
  # ? make matching non-greedy
  
  $a =~ m/^\d+\.\d+.\d+\.\d+ .+ \[(\d+)\/(\w+)\/(\d+):(\d+):(\d+):(\d+) (\S+?)\]/;
  my($day) = $1;
  my($month) = $2;
  my($year) = $3;
  my($hour) = $4;
  my($minute) = $5;
  my($second) = $6;
  my($timezone) = $7;
  my($dt1) = DateTime->new( year => $year,
  			    month => $months{$month},
  			    day => $day,
  			    hour => $hour,
  			    minute => $minute,
  			    second => $second,
  			    nanosecond => 0,
  			    time_zone => $timezone );
  $dt1->set_time_zone("UTC"); # do all comparisons in UTC
  
  $b =~ m/^\d+\.\d+.\d+\.\d+ .+ \[(\d+)\/(\w+)\/(\d+):(\d+):(\d+):(\d+) (\S+?)\]/;
  $day = $1;
  $month = $2;
  $year = $3;
  $hour = $4;
  $minute = $5;
  $second = $6;
  $timezone = $7;
  my($dt2) = DateTime->new( year => $year,
  			    month => $months{$month},
  			    day => $day,
  			    hour => $hour,
  			    minute => $minute,
  			    second => $second,
  			    nanosecond => 0,
  			    time_zone => $timezone );  
  $dt2->set_time_zone("UTC");
  
  my($cmp) = DateTime->compare($dt1, $dt2);
  return $cmp;
} @not_sorted;

print "Sorted. Now writing to output file...\n";
print OUTPUT @sorted;
print "All done.\n";
