#!/usr/bin/perl -w
#
# $Id: calamaris.pl,v 1.20 1997/04/11 18:34:37 cord Exp cord $ 
#
#
# DESCRIPTION: calamaris.pl - a program to get the interesting parts out of
#	the Squid Internet Object Cache Native Log. See
#	http://squid.nlanr.net/Squid/
#
# URL: http://Calamaris.Cord.de/
#
# AUTHOR: Cord Beermann (cord@Wunder-Nett.org)
#
# Thanks to: John Heaton (John@MCC.ac.uk),
#	     Andreas Lamprecht (Andreas.Lamprecht@siemens.at)
#	     Kenny Ng (kennyng@cyberway.com.sg)
#	     Claus Langhans (langhans@rz.uni-frankfurt.de)
#	     Andreas Jung (ajung@sz-sb.de)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#
# A Perl script is "correct" if it gets the job done before your boss fires
# you.
# ('Programming Perl Second Edition' by Larry Wall, Tom Christiansen
#   & Randal. L. Schwartz)
#
#
# Instructions:
# * Switch 'emulate_httpd_log' off
# * Pipe your Logfile in calamaris
# 
# Bugs and shortcomings
# * The peak values are wrong if you put an unsorted Logfile in calamaris
#
# Todo
# * use strict;
# * make the peak-measure faster, more effective
# * add -fritti-option for HTML-Table-Output
#
### Configuration ###
#
# How many content-type-lines?
$topcontent="19";
#
# How many (user@)host-lines?
$tophost="19";
#
# How many url-lines?
$topurl="19";
#
# How many url-lines?
$topurltld="19";
#
### End of Configuration ###

# initialize variables
$peak_all_hour = 0;
$peak_all_min = 0;
$peak_all_sec = 0;
$peak_tcp_hour = 0;
$peak_tcp_min = 0;
$peak_tcp_sec = 0;
$peak_udp_hour = 0;
$peak_udp_min = 0;
$peak_udp_sec = 0;
$tcp_content_counter = 0;
$tcp_content_size_sum = 0;
$tcp_counter = 0;
$tcp_hit_content_counter = 0;
$tcp_hit_counter = 0;
$tcp_hit_size_sum = 0;
$tcp_hit_time_sum = 0;
$tcp_miss_counter = 0;
$tcp_miss_direct_counter = 0;
$tcp_miss_direct_size_sum = 0;
$tcp_miss_direct_time_sum = 0;
$tcp_miss_neighbor_hit_counter = 0;
$tcp_miss_neighbor_hit_size_sum = 0;
$tcp_miss_neighbor_hit_time_sum = 0;
$tcp_miss_neighbor_miss_counter = 0;
$tcp_miss_neighbor_miss_size_sum=0;
$tcp_miss_neighbor_miss_time_sum=0;
$tcp_miss_none_counter = 0;
$tcp_miss_none_size_sum = 0; 
$tcp_miss_none_time_sum = 0;
$tcp_miss_size_sum = 0;
$tcp_miss_time_sum = 0;
$tcp_size_sum = 0;
$tcp_time_sum = 0;
$udp_hit_counter = 0;
$udp_hit_size_sum = 0;
$udp_hit_time_sum = 0;
$udp_miss_counter = 0;
$udp_miss_size_sum = 0;
$udp_miss_time_sum = 0;

while (<>) {($log_date, $log_reqtime, $log_requester, $log_status,
	     $log_size, $log_method, $log_url, $log_ident, $log_hierarchie,
	     $log_content) = split;

    unless (defined $log_content) {
	warn ("invalid line:\"$_\"\n");
	next;
    }

    if ($log_reqtime == 0) {
	$log_reqtime = .1;
    }

    if ($log_requester =~ /^([0-9][0-9]{0,2}\.){3}[0-9][0-9]{0,2}$/) {
	if (defined $nscache{$log_requester}) {
	    $requesterhost = $nscache{$log_requester};
	} else {
	    unless ($requesterhost = &address_to_name($log_requester)) {
		$requesterhost = $log_requester;
	    }
	    $nscache{$log_requester} = $requesterhost;
	}
    } else {
	$requesterhost = $log_requester;
    }
    ($log_hitorfail)=(split(/\//,$log_status))[0];

    if ($log_size == 0) {
	$log_size = .0000001;
    }

    $urlhost = $log_url;
    $urlhost =~ s#^[^:]+://##;
    $urlhost =~ s#/.*$##;
    $urlhost =~ s#^.*@##;
    $urlhost =~ s#:.*$##;

    if ($urlhost =~ /^(([0-9][0-9]{0,2}\.){3})[0-9][0-9]{0,2}$/) {
	$urlhost = "$1*";
	$urltld = "unresolved";
    } elsif ($urlhost =~ /^(.*\.)?([^\.]+\.([^\.]+))\.?$/) {
	$urlhost = "*.$2";
	$urltld = "*.$3";
    } else {
	$urltld = $urlhost;
    }
    if ($log_ident eq "-") {
	$requester = "$requesterhost";
    } else {
	$requester = "$log_ident@$requesterhost"
    }

    ($log_hierarchie_method,
     $log_hierarchie_host)=(split(/\//,$log_hierarchie))[0,1];

    $log_content = "-" unless defined $log_content;
    $log_content =~ tr/A-Z/a-z/;

    $starttime = $log_date
	unless (defined($starttime));
    if ($log_date < $starttime) {
	$starttime = $log_date;
    }
    $endtime = $log_date
	unless (defined($endtime));
    if ($log_date > $endtime) {
	$endtime = $log_date;
    }

    push(@peak_all_hour,$log_date);
    push(@peak_all_min,$log_date);
    push(@peak_all_sec,$log_date);
    while ($peak_all_hour[0] < ($log_date - 3600)) {
	shift(@peak_all_hour);
    }
    while ($peak_all_min[0] < ($log_date - 60)) {
	shift(@peak_all_min);
    }
    while ($peak_all_sec[0] < ($log_date - 1)) {
	shift(@peak_all_sec);
    }
    if ($peak_all_hour < @peak_all_hour) {
	$peak_all_hour = @peak_all_hour;
	$peak_all_hour_time = $log_date - 3600;
    }
    if ($peak_all_min < @peak_all_min) {
	$peak_all_min = @peak_all_min;
	$peak_all_min_time = $log_date - 60;
    }
    if ($peak_all_sec < @peak_all_sec) {
	$peak_all_sec = @peak_all_sec;
	$peak_all_sec_time = $log_date - 1;
    }

    if ($log_method eq "ICP_QUERY") {
	unless (defined($udp_miss_requester_counter{$requester})) {
	    $udp_hit_requester_counter{$requester} = 0;
	    $udp_hit_requester_size_sum{$requester} = 0;
	    $udp_hit_requester_time_sum{$requester} = 0;
	    $udp_miss_requester_counter{$requester} = 0;
	    $udp_miss_requester_size_sum{$requester} = 0;
	    $udp_miss_requester_time_sum{$requester} = 0;
	}
	push(@peak_udp_hour,$log_date);
	push(@peak_udp_min,$log_date);
	push(@peak_udp_sec,$log_date);
	while ($peak_udp_hour[0] < ($log_date - 3600)) {
	    shift(@peak_udp_hour);
	}
	while ($peak_udp_min[0] < ($log_date - 60)) {
	    shift(@peak_udp_min);
	}
	while ($peak_udp_sec[0] < ($log_date - 1)) {
	    shift(@peak_udp_sec);
	}
	if ($peak_udp_hour < @peak_udp_hour) {
	    $peak_udp_hour = @peak_udp_hour;
	    $peak_udp_hour_time = $log_date - 3600;
	}
	if ($peak_udp_min < @peak_udp_min) {
	    $peak_udp_min = @peak_udp_min;
	    $peak_udp_min_time = $log_date - 60;
	}
	if ($peak_udp_sec < @peak_udp_sec) {
	    $peak_udp_sec = @peak_udp_sec;
	    $peak_udp_sec_time = $log_date - 1;
	}

	if ($log_hitorfail =~ /^UDP_HIT/) {
	    ++$udp_hit_counter;
	    $udp_hit_size_sum += $log_size;
	    $udp_hit_time_sum += $log_reqtime;
	    ++$udp_hit_requester_counter{$requester};
	    $udp_hit_requester_size_sum{$requester} += $log_size;
	    $udp_hit_requester_time_sum{$requester} += $log_reqtime;
	    unless (defined($udp_hit_counter{$log_hitorfail})) {
		$udp_hit_counter{$log_hitorfail} = 0;
		$udp_hit_size_sum{$log_hitorfail} = 0;
		$udp_hit_time_sum{$log_hitorfail} = 0;
	    }
	    ++$udp_hit_counter{$log_hitorfail};
	    $udp_hit_size_sum{$log_hitorfail} += $log_size;
	    $udp_hit_time_sum{$log_hitorfail} += $log_reqtime;
	} else {
	    ++$udp_miss_counter;
	    $udp_miss_size_sum += $log_size;
	    $udp_miss_time_sum += $log_reqtime;
	    ++$udp_miss_requester_counter{$requester};
	    $udp_miss_requester_size_sum{$requester} += $log_size;
	    $udp_miss_requester_time_sum{$requester} += $log_reqtime;
	    unless (defined($udp_miss_counter{$log_hitorfail})) {
		$udp_miss_counter{$log_hitorfail} = 0;
		$udp_miss_size_sum{$log_hitorfail} = 0;
		$udp_miss_time_sum{$log_hitorfail} = 0;
	    }
	    ++$udp_miss_counter{$log_hitorfail};
	    $udp_miss_size_sum{$log_hitorfail} += $log_size;
	    $udp_miss_time_sum{$log_hitorfail} += $log_reqtime;
	}
    } elsif ($log_hitorfail =~ /^(TCP|ERR)_/) {
	++$tcp_counter;
	$tcp_size_sum += $log_size;
	$tcp_time_sum += $log_reqtime;
	unless (defined($tcp_requester_counter{$requester})) {
	    $tcp_requester_counter{$requester} = 0;
	    $tcp_requester_size_sum{$requester} = 0;
	    $tcp_hit_requester_counter{$requester} = 0;
	}
	++$tcp_requester_counter{$requester};
	$tcp_requester_size_sum{$requester} += $log_size;
	unless (defined($tcp_url_counter{$urlhost})) {
	    $tcp_url_counter{$urlhost} = 0;
	    $tcp_url_size_sum{$urlhost} = 0;
	    $tcp_hit_url_counter{$urlhost} = 0;
	}
	++$tcp_url_counter{$urlhost};
	$tcp_url_size_sum{$urlhost} += $log_size;
	unless (defined($tcp_urltld_counter{$urltld})) {
	    $tcp_urltld_counter{$urltld} = 0;
	    $tcp_urltld_size_sum{$urltld} = 0;
	    $tcp_hit_urltld_counter{$urltld} = 0;
	}
	++$tcp_urltld_counter{$urltld};
	$tcp_urltld_size_sum{$urltld} += $log_size;

	push(@peak_tcp_hour,$log_date);
	push(@peak_tcp_min,$log_date);
	push(@peak_tcp_sec,$log_date);
	while ($peak_tcp_hour[0] < ($log_date - 3600)) {
	    shift(@peak_tcp_hour);
	}
	while ($peak_tcp_min[0] < ($log_date - 60)) {
	    shift(@peak_tcp_min);
	}
	while ($peak_tcp_sec[0] < ($log_date - 1)) {
	    shift(@peak_tcp_sec);
	}
	if ($peak_tcp_hour < @peak_tcp_hour) {
	    $peak_tcp_hour = @peak_tcp_hour;
	    $peak_tcp_hour_time = $log_date - 3600;
	}
	if ($peak_tcp_min < @peak_tcp_min) {
	    $peak_tcp_min = @peak_tcp_min;
	    $peak_tcp_min_time = $log_date - 60;
	}
	if ($peak_tcp_sec < @peak_tcp_sec) {
	    $peak_tcp_sec = @peak_tcp_sec;
	    $peak_tcp_sec_time = $log_date - 1;
	}

	unless ($log_content eq "-") {
	    ++$tcp_content_counter;
	    $tcp_content_size_sum += $log_size;
	    unless (defined($tcp_content_counter{$log_content})) {
		$tcp_content_counter{$log_content} = 0;
		$tcp_content_size_sum{$log_content} = 0;
		$tcp_hit_content_counter{$log_content} = 0;
	    }
	    ++$tcp_content_counter{$log_content};
	    $tcp_content_size_sum{$log_content} += $log_size;
	}

	if ($log_hitorfail =~ /^TCP\w+HIT/) {
	    ++$tcp_hit_counter;
	    $tcp_hit_size_sum += $log_size;
	    $tcp_hit_time_sum += $log_reqtime;
	    unless (defined($tcp_hit_counter{$log_hitorfail})) {
		$tcp_hit_counter{$log_hitorfail} = 0;
		$tcp_hit_size_sum{$log_hitorfail} = 0;
		$tcp_hit_time_sum{$log_hitorfail} = 0;
	    }
	    ++$tcp_hit_counter{$log_hitorfail};
	    ++$tcp_hit_requester_counter{$requester};
	    ++$tcp_hit_url_counter{$urlhost};
	    ++$tcp_hit_urltld_counter{$urltld};
	    $tcp_hit_size_sum{$log_hitorfail} += $log_size;
	    $tcp_hit_time_sum{$log_hitorfail} += $log_reqtime;
	    unless ($log_content eq "-") {
		++$tcp_hit_content_counter;
		++$tcp_hit_content_counter{$log_content};
	    }
	} elsif (($log_hierarchie_method =~ /NONE/) or
		 ($log_hitorfail =~ /^ERR_/)) {
	    ++$tcp_miss_none_counter;
	    $tcp_miss_none_size_sum += $log_size;
	    $tcp_miss_none_time_sum += $log_reqtime;
	    unless (defined($tcp_miss_none_counter{$log_hitorfail})) {
		$tcp_miss_none_counter{$log_hitorfail} = 0;
		$tcp_miss_none_size_sum{$log_hitorfail} = 0;
		$tcp_miss_none_time_sum{$log_hitorfail} = 0;
	    }
	    ++$tcp_miss_none_counter{$log_hitorfail};
	    $tcp_miss_none_size_sum{$log_hitorfail} += $log_size;
	    $tcp_miss_none_time_sum{$log_hitorfail} += $log_reqtime;
	} else {
	    ++$tcp_miss_counter;
	    $tcp_miss_size_sum += $log_size;
	    $tcp_miss_time_sum += $log_reqtime;
	    unless (defined($tcp_miss_counter{$log_hitorfail})) {
		$tcp_miss_counter{$log_hitorfail} = 0;
		$tcp_miss_size_sum{$log_hitorfail} = 0;
		$tcp_miss_time_sum{$log_hitorfail} = 0;
	    }
	    ++$tcp_miss_counter{$log_hitorfail};
	    $tcp_miss_size_sum{$log_hitorfail} += $log_size;
	    $tcp_miss_time_sum{$log_hitorfail} += $log_reqtime;
	    unless (defined($tcp_miss_requester_counter{$requester})) {
		$tcp_miss_requester_counter{$requester} = 0;
		$tcp_miss_requester_size_sum{$requester} = 0;
	    }
	    ++$tcp_miss_requester_counter{$requester};
	    $tcp_miss_requester_size_sum{$requester} += $log_size;

	    if ($log_hierarchie_method =~ /(DIRECT|SOURCE_FASTEST)/) {
		++$tcp_miss_direct_counter;
		$tcp_miss_direct_size_sum += $log_size;
		$tcp_miss_direct_time_sum += $log_reqtime;
	    } elsif ($log_hierarchie_method =~ /(PARENT|SIBLING)\w+HIT/) {
		++$tcp_miss_neighbor_hit_counter;
		$tcp_miss_neighbor_hit_time_sum += $log_reqtime;
		$tcp_miss_neighbor_hit_size_sum += $log_size;

		unless (defined($tcp_miss_neighbor_hit_counter{$log_hierarchie_host})) {
		    $tcp_miss_neighbor_hit_counter{$log_hierarchie_host} = 0;
		    $tcp_miss_neighbor_hit_size_sum{$log_hierarchie_host} = 0;
		    $tcp_miss_neighbor_hit_time_sum{$log_hierarchie_host} = 0;
		}
		++$tcp_miss_neighbor_hit_counter{$log_hierarchie_host};
		$tcp_miss_neighbor_hit_size_sum{$log_hierarchie_host} +=
		    $log_size;
		$tcp_miss_neighbor_hit_time_sum{$log_hierarchie_host} +=
		    $log_reqtime;
	    } elsif ($log_hierarchie_method =~
		     /(PARENT_MISS|(DEFAULT|FIRST_UP|SINGLE|PASSTHROUGH|ROUNDROBIN)_PARENT)/) {
		++$tcp_miss_neighbor_miss_counter;
		$tcp_miss_neighbor_miss_size_sum += $log_size;
		$tcp_miss_neighbor_miss_time_sum += $log_reqtime;
		unless (defined($tcp_miss_neighbor_counter{$log_hierarchie_host})) {
		    $tcp_miss_neighbor_counter{$log_hierarchie_host} = 0;
		    $tcp_miss_neighbor_miss_size_sum{$log_hierarchie_host} = 0;
		    $tcp_miss_neighbor_miss_time_sum{$log_hierarchie_host} = 0;
		}
		++$tcp_miss_neighbor_miss_counter{$log_hierarchie_host};
		$tcp_miss_neighbor_miss_size_sum{$log_hierarchie_host} +=
		    $log_size;
		$tcp_miss_neighbor_miss_time_sum{$log_hierarchie_host} +=
		    $log_reqtime;
	    }
	}
    }
}

### Yea! File read. Now give the output...

if ($peak_all_sec == 0) {
    print "no requests found\n";
    exit(0);
}

    $date_start = convert_date($starttime);
    $date_stop = convert_date($endtime);
    $date_peak_udp_hour = convert_date($peak_udp_hour_time);
    $date_peak_tcp_hour = convert_date($peak_tcp_hour_time);
    $date_peak_all_hour = convert_date($peak_all_hour_time);
    $date_peak_udp_min = convert_date($peak_udp_min_time);
    $date_peak_tcp_min = convert_date($peak_tcp_min_time);
    $date_peak_all_min = convert_date($peak_all_min_time);
    $date_peak_udp_sec = convert_date($peak_udp_sec_time);
    $date_peak_tcp_sec = convert_date($peak_tcp_sec_time);
    $date_peak_all_sec = convert_date($peak_all_sec_time);

printf("cacheserverreport for %s - %s\n", $date_start, $date_stop);

print "\n# Request peak per Protocol\n";
print "     sec  peak begins at     min   peak begins at      hour   peak begins at\n";
print "--- ---- ------------------ ----- ------------------ ------- ------------------\n";
printf "%-3s %4d %s %5d %s %7d %s\n",
       UDP,
       $peak_udp_sec,
       $date_peak_udp_sec,
       $peak_udp_min,
       $date_peak_udp_min,
       $peak_udp_hour,
       $date_peak_udp_hour;
printf "%-3s %4d %s %5d %s %7d %s\n",
       TCP,
       $peak_tcp_sec,
       $date_peak_tcp_sec,
       $peak_tcp_min,
       $date_peak_tcp_min,
       $peak_tcp_hour,
       $date_peak_tcp_hour;
print "--- ---- ------------------ ----- ------------------ ------- ------------------\n";
printf "%-3s %4d %s %5d %s %7d %s\n",
       ALL,
       $peak_all_sec,
       $date_peak_all_sec,
       $peak_all_min,
       $date_peak_all_min,
       $peak_all_hour,
       $date_peak_all_hour;

if (($udp_hit_counter + $udp_miss_counter) == 0) {
    print("\n# UDP-Request State: none\n");
} else {
    print("\n# UDP-Request State                 Request     %    kByte      %  msec  KB/sec\n");
    print("----------------------------------- ------- ------ -------- ------ ---- -------\n");

    if ($udp_hit_counter == 0) {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n", HIT, 0, 0, 0, 0,
	       0, 0);
    } else {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	       HIT,
	       $udp_hit_counter,
	       100 * $udp_hit_counter / ($udp_hit_counter + $udp_miss_counter), 
	       $udp_hit_size_sum / 1024,
	       100 * $udp_hit_size_sum / ($udp_hit_size_sum +
					  $udp_miss_size_sum),
	       $udp_hit_time_sum / $udp_hit_counter,
	       1000 * $udp_hit_size_sum / (1024 * $udp_hit_time_sum)
	       );

	foreach $hitorfail (sort {$udp_hit_counter{$b} <=>
			    $udp_hit_counter{$a}} keys(%udp_hit_counter)) {
	    printf(" %-34s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
		   $hitorfail,
		   $udp_hit_counter{$hitorfail},
		   100 * $udp_hit_counter{$hitorfail} / ($udp_hit_counter +
							 $udp_miss_counter),
		   $udp_hit_size_sum{$hitorfail} / 1024,
		   100 * $udp_hit_size_sum{$hitorfail} / ($udp_hit_size_sum +
							  $udp_miss_size_sum),
		   $udp_hit_time_sum{$hitorfail} / $udp_hit_counter{$hitorfail},
		   1000 * $udp_hit_size_sum{$hitorfail} /
		    (1024 * $udp_hit_time_sum{$hitorfail})
		   );
	}
    }

    if ($udp_miss_counter == 0) {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n", MISS, 0, 0, 0, 0,
	       0, 0);
    } else {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	       MISS,
	       $udp_miss_counter,
	       100 * $udp_miss_counter / ($udp_miss_counter + $udp_hit_counter),
	       $udp_miss_size_sum / 1024, 
	       100 * $udp_miss_size_sum / ($udp_miss_size_sum +
					   $udp_hit_size_sum),
	       $udp_miss_time_sum / $udp_miss_counter,
	       1000 * $udp_miss_size_sum / (1024 * $udp_miss_time_sum)
	       );
	foreach $hitorfail (sort {$udp_miss_counter{$b} <=>
			    $udp_miss_counter{$a}} keys(%udp_miss_counter)) {
	    printf(" %-34s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
		   $hitorfail,
		   $udp_miss_counter{$hitorfail},
		   100 * $udp_miss_counter{$hitorfail} / ($udp_miss_counter
							  + $udp_hit_counter),
		   $udp_miss_size_sum{$hitorfail} / 1024,
		   100 * $udp_miss_size_sum{$hitorfail} / ($udp_miss_size_sum
							   + $udp_hit_size_sum),
		   $udp_miss_time_sum{$hitorfail} /
		    $udp_miss_counter{$hitorfail},
		   1000 * $udp_miss_size_sum{$hitorfail} /
		    (1024 * $udp_miss_time_sum{$hitorfail})
		   );
	}
    }

    print("----------------------------------- ------- ------ -------- ------ ---- -------\n");

    printf("%-35s %7d %6s %8d %6s %-4.4s %7.2f\n", 
	   Sum,
	   $udp_miss_counter + $udp_hit_counter,
	   "",
	   ($udp_miss_size_sum + $udp_hit_size_sum) / 1024,
	   "",
	   ($udp_miss_time_sum + $udp_hit_time_sum) / ($udp_miss_counter +
						       $udp_hit_counter),
	   1000 * ($udp_miss_size_sum + $udp_hit_size_sum) /
	    (1024 * ($udp_miss_time_sum + $udp_hit_time_sum))
	   );
}

if ($tcp_counter == 0) {
    print("\n# TCP-Request State: none\n");
} else {
    print("\n# TCP-Request State                 Request     %    kByte      %   sec  KB/sec\n");
    print("----------------------------------- ------- ------ -------- ------ ---- -------\n");

    if ($tcp_hit_counter == 0) {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n", HIT, 0, 0, 0, 0,
	       0, 0);
    } else {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	       HIT,
	       $tcp_hit_counter,
	       100 * $tcp_hit_counter / $tcp_counter, 
	       $tcp_hit_size_sum / 1024,
	       100 * $tcp_hit_size_sum / $tcp_size_sum,
	       $tcp_hit_time_sum / (1000 * $tcp_hit_counter),
	       1000 * $tcp_hit_size_sum / (1024 * $tcp_hit_time_sum)
	       );

	foreach $hitorfail (sort {$tcp_hit_counter{$b} <=>
			    $tcp_hit_counter{$a}} keys(%tcp_hit_counter)) {
	    printf(" %-34s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
		   $hitorfail,
		   $tcp_hit_counter{$hitorfail},
		   100 * $tcp_hit_counter{$hitorfail} / $tcp_counter,
		   $tcp_hit_size_sum{$hitorfail} / 1024,
		   100 * $tcp_hit_size_sum{$hitorfail} / $tcp_size_sum,
		   $tcp_hit_time_sum{$hitorfail} /
		    (1000 * $tcp_hit_counter{$hitorfail}),
		   1000 * $tcp_hit_size_sum{$hitorfail} /
		    (1024 * $tcp_hit_time_sum{$hitorfail})
		   );
	}
    }

    if ($tcp_miss_counter == 0) {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n", MISS, 0, 0, 0, 0,
	       0, 0);
    } else {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	       MISS,
	       $tcp_miss_counter,
	       100 * $tcp_miss_counter / $tcp_counter,
	       $tcp_miss_size_sum / 1024, 
	       100 * $tcp_miss_size_sum / $tcp_size_sum, 
	       $tcp_miss_time_sum / (1000 * $tcp_miss_counter),
	       1000 * $tcp_miss_size_sum / (1024 * $tcp_miss_time_sum)
	       );

	foreach $hitorfail (sort {$tcp_miss_counter{$b} <=>
			    $tcp_miss_counter{$a}} keys(%tcp_miss_counter)) {

	    printf(" %-34s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
		   $hitorfail,
		   $tcp_miss_counter{$hitorfail},
		   100 * $tcp_miss_counter{$hitorfail} / $tcp_counter,
		   $tcp_miss_size_sum{$hitorfail} / 1024,
		   100 * $tcp_miss_size_sum{$hitorfail} / $tcp_size_sum,
		   $tcp_miss_time_sum{$hitorfail} /
		    (1000 * $tcp_miss_counter{$hitorfail}),
		   1000 * $tcp_miss_size_sum{$hitorfail} /
		    (1024 * $tcp_miss_time_sum{$hitorfail})
		   );
	}
    }

    if ($tcp_miss_none_counter == 0) {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n", ERROR, 0, 0, 0,
	       0, 0, 0);
    } else {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	       ERROR,
	       $tcp_miss_none_counter,
	       100 * $tcp_miss_none_counter / $tcp_counter,
	       $tcp_miss_none_size_sum / 1024,
	       100 * $tcp_miss_none_size_sum / $tcp_size_sum,
	       $tcp_miss_none_time_sum / (1000 * $tcp_miss_none_counter),
	       1000 * $tcp_miss_none_size_sum /
		    (1024 * $tcp_miss_none_time_sum)
	       );

	foreach $hitorfail (sort {$tcp_miss_none_counter{$b} <=>
			    $tcp_miss_none_counter{$a}}
			    keys(%tcp_miss_none_counter)) {

	    printf(" %-34s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
		   $hitorfail,
		   $tcp_miss_none_counter{$hitorfail},
		   100 * $tcp_miss_none_counter{$hitorfail} / $tcp_counter,
		   $tcp_miss_none_size_sum{$hitorfail} / 1024,
		   100 * $tcp_miss_none_size_sum{$hitorfail} / $tcp_size_sum,
		   $tcp_miss_none_time_sum{$hitorfail} /
		    (1000 * $tcp_miss_none_counter{$hitorfail}),
		   1000 * $tcp_miss_none_size_sum{$hitorfail} /
		    (1024 * $tcp_miss_none_time_sum{$hitorfail})
		   );
	}
    }

    print("----------------------------------- ------- ------ -------- ------ ---- -------\n");

    printf("%-35s %7d %6s %8d %6s %-4.4s %7.2f\n", 
	   Sum,
	   $tcp_counter,
	   "",
	   $tcp_size_sum / 1024,
	   "",
	   $tcp_time_sum / (1000 * $tcp_counter),
	   1000 * $tcp_size_sum / (1024 * $tcp_time_sum)
	   );
}

if ($tcp_miss_counter == 0) {
    print "\n# external Fetches: none\n";
} else {
    print "\n# external Fetches\n";
    print("Type Neighbor                       Request     %    kByte      %   sec  KB/sec\n");
    print("----------------------------------- ------- ------ -------- ------ ---- -------\n");

    unless ($tcp_miss_direct_counter == 0) {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	       DIRECT,
	       $tcp_miss_direct_counter,
	       100 * $tcp_miss_direct_counter / $tcp_miss_counter,
	       $tcp_miss_direct_size_sum / 1024, 
	       100 * $tcp_miss_direct_size_sum / $tcp_miss_size_sum,
	       $tcp_miss_direct_time_sum / (1000 * $tcp_miss_direct_counter),
	       1000 * $tcp_miss_direct_size_sum /
		(1024 * $tcp_miss_direct_time_sum)
	       );
    }

    unless ($tcp_miss_neighbor_hit_counter == 0) {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	       SIBLING,
	       $tcp_miss_neighbor_hit_counter,
	       100 * $tcp_miss_neighbor_hit_counter / $tcp_miss_counter,
	       $tcp_miss_neighbor_hit_size_sum / 1024,
	       100 * $tcp_miss_neighbor_hit_size_sum / $tcp_miss_size_sum,
	       $tcp_miss_neighbor_hit_time_sum /
		(1000 * $tcp_miss_neighbor_hit_counter),
	       1000 * $tcp_miss_neighbor_hit_size_sum /
		(1024 * $tcp_miss_neighbor_hit_time_sum)
	       );
	foreach $neighbor (sort {$tcp_miss_neighbor_hit_counter{$b} <=>
			   $tcp_miss_neighbor_hit_counter{$a}}
			   keys(%tcp_miss_neighbor_hit_counter)) {
	    if (length($neighbor) > 35) {
		printf(" %s\n                                   ", $neighbor);
	    } else {
		printf(" %-34s", $neighbor);
	    }
	    printf(" %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
		   $tcp_miss_neighbor_hit_counter{$neighbor},
		   100 * $tcp_miss_neighbor_hit_counter{$neighbor} /
		    $tcp_miss_counter,
		   $tcp_miss_neighbor_hit_size_sum{$neighbor} / 1024,
		   100 * $tcp_miss_neighbor_hit_size_sum{$neighbor} /
		    $tcp_miss_size_sum,
		   $tcp_miss_neighbor_hit_time_sum{$neighbor} /
		    (1000 * $tcp_miss_neighbor_hit_counter{$neighbor}),
		   1000 * $tcp_miss_neighbor_hit_size_sum{$neighbor} /
		    (1024 * $tcp_miss_neighbor_hit_time_sum{$neighbor})
		   );
	}
    }

    unless ($tcp_miss_neighbor_miss_counter == 0) {
	printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n", 
	       PARENT,
	       $tcp_miss_neighbor_miss_counter,
	       100 * $tcp_miss_neighbor_miss_counter / $tcp_miss_counter,
	       $tcp_miss_neighbor_miss_size_sum / 1024,
	       100 * $tcp_miss_neighbor_miss_size_sum / $tcp_miss_size_sum,
	       $tcp_miss_neighbor_miss_time_sum /
		(1000 * $tcp_miss_neighbor_miss_counter),
	       1000 * $tcp_miss_neighbor_miss_size_sum /
		(1024 * $tcp_miss_neighbor_miss_time_sum)
	       );
	foreach $neighbor (sort {$tcp_miss_neighbor_miss_counter{$b} <=>
			   $tcp_miss_neighbor_miss_counter{$a}}
			   keys(%tcp_miss_neighbor_miss_counter)) {
	    if (length($neighbor) > 35) {
		printf(" %s\n                                   ", $neighbor);
	    } else {
		printf(" %-34s", $neighbor);
	    }
	    printf(" %7d %6.2f %8d %6.2f %-4.4s %7.2f\n", 
		   $tcp_miss_neighbor_miss_counter{$neighbor}, 
		   100 * $tcp_miss_neighbor_miss_counter{$neighbor} /
		    $tcp_miss_counter,
		   $tcp_miss_neighbor_miss_size_sum{$neighbor} / 1024,
		   100 * $tcp_miss_neighbor_miss_size_sum{$neighbor} /
		    $tcp_miss_size_sum,
		   $tcp_miss_neighbor_miss_time_sum{$neighbor} /
		    (1000 * $tcp_miss_neighbor_miss_counter{$neighbor}),
		   1000 * $tcp_miss_neighbor_miss_size_sum{$neighbor} /
		   (1024 * $tcp_miss_neighbor_miss_time_sum{$neighbor})
		   );
	}
    }

    print("----------------------------------- ------- ------ -------- ------ ---- -------\n");

    printf("%-35s %7d %6s %8d %6s %-4.4s %7.2f\n",
	   Sum,
	   $tcp_miss_counter,
	   "",
	   $tcp_miss_size_sum / 1024,
	   "",
	   $tcp_miss_time_sum / (1000 * $tcp_miss_counter),
	   1000 * $tcp_miss_size_sum / (1024 * $tcp_miss_time_sum)
	   );
}

if ($tcp_counter == 0) {
    print "\n# Request-Peak: none\n";
} else {
    print "\n# Request-Peak by 2ndlevel-domain         Request     %    kByte      %   Hit-%\n";
    print "----------------------------------------- ------- ------ -------- ------ ------\n";

    foreach $urlhost (sort {$tcp_url_counter{$b} <=> $tcp_url_counter{$a}}
		  keys(%tcp_url_counter)) {
	printf("%-41.41s %7d %6.2f %8d %6.2f %6.2f\n", 
	       $urlhost,
	       $tcp_url_counter{$urlhost}, 
	       100 * ($tcp_url_counter{$urlhost} / $tcp_counter), 
	       $tcp_url_size_sum{$urlhost} / 1024, 
	       100 * ($tcp_url_size_sum{$urlhost} / $tcp_size_sum), 
	       100 * $tcp_hit_url_counter{$urlhost} / $tcp_url_counter{$urlhost});
	last if (--$topurl == 0);
    }

    print "----------------------------------------- ------- ------ -------- ------ ------\n";

    printf("%-41s %7d %6.2f %8d %6.2f %6.2f\n",
	   Sum,
	   $tcp_counter,	
	   100,
	   $tcp_size_sum / 1024, 
	   100,
	   100 * $tcp_hit_counter / $tcp_counter
	   );

    print "\n# Request-Peak by toplevel-domain         Request     %    kByte      %   Hit-%\n";
    print "----------------------------------------- ------- ------ -------- ------ ------\n";

    foreach $urltld (sort {$tcp_urltld_counter{$b} <=>
	      $tcp_urltld_counter{$a}} keys(%tcp_urltld_counter)) {
	printf("%-41.41s %7d %6.2f %8d %6.2f %6.2f\n", 
	       $urltld,
	       $tcp_urltld_counter{$urltld}, 
	       100 * ($tcp_urltld_counter{$urltld} / $tcp_counter), 
	       $tcp_urltld_size_sum{$urltld} / 1024, 
	       100 * ($tcp_urltld_size_sum{$urltld} / $tcp_size_sum), 
	       100 * $tcp_hit_urltld_counter{$urltld} /
		    $tcp_urltld_counter{$urltld}
	       );
	last if (--$topurltld == 0);
    }

    print "----------------------------------------- ------- ------ -------- ------ ------\n";

    printf("%-41s %7d %6.2f %8d %6.2f %6.2f\n",
	   Sum,
	   $tcp_counter,	
	   100,
	   $tcp_size_sum / 1024, 
	   100,
	   100 * $tcp_hit_counter / $tcp_counter
	   );
}
if ($tcp_content_counter == 0) {
    print "\n# requested content-type: none\n";
} else {
    print "\n# requested content-type                  Request     %    kByte      %   Hit-%\n";
    print "----------------------------------------- ------- ------ -------- ------ ------\n";

    foreach $content (sort {$tcp_content_counter{$b} <=>
		      $tcp_content_counter{$a}} keys(%tcp_content_counter)) {
	printf("%-41s %7d %6.2f %8d %6.2f %6.2f\n",
	       substr($content,0,41),
	       $tcp_content_counter{$content},
	       100 * $tcp_content_counter{$content} / $tcp_content_counter,
	       $tcp_content_size_sum{$content} / 1024,
	       100 * $tcp_content_size_sum{$content} / $tcp_content_size_sum,
	       100 * $tcp_hit_content_counter{$content} /
		    $tcp_content_counter{$content}
	       );
	last if (--$topcontent == 0);
    }

    print "----------------------------------------- ------- ------ -------- ------ ------\n";

    printf("%-41s %7d %6.2f %8d %6.2f %6.2f\n",
	   Sum,
	   $tcp_content_counter,
	   100,
	   $tcp_content_size_sum / 1024,
	   100,
	   100 * $tcp_hit_content_counter / $tcp_content_counter
	   );
}
if (($udp_hit_counter + $udp_miss_counter) == 0) {
    print("\n# UDP-Requests: none\n");
} else {
    print("\n# UDP-Requests                      Request  Hit-%   kByte   Hit-% msec  KB/sec\n");
    print("----------------------------------- ------- ------ -------- ------ ---- -------\n");
    foreach $neighbor (sort {($udp_miss_requester_counter{$b} +
			      $udp_hit_requester_counter{$b})
		       <=> ($udp_miss_requester_counter{$a} +
			    $udp_hit_requester_counter{$a})}
		       keys(%udp_miss_requester_counter)) {
	if (length($neighbor) > 36) {
	    printf("%s\n                                    ", $neighbor);
	} else {
	    printf("%-35s", $neighbor);
	}
	printf(" %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	       $udp_hit_requester_counter{$neighbor} +
		$udp_miss_requester_counter{$neighbor},
	       100 * $udp_hit_requester_counter{$neighbor} /
		($udp_hit_requester_counter{$neighbor} +
		 $udp_miss_requester_counter{$neighbor}),
	       ($udp_hit_requester_size_sum{$neighbor} +
		$udp_miss_requester_size_sum{$neighbor}) / 1024,
	       100 * $udp_hit_requester_size_sum{$neighbor} /
		($udp_hit_requester_size_sum{$neighbor} +
		 $udp_miss_requester_size_sum{$neighbor}),
	       ($udp_hit_requester_time_sum{$neighbor} +
		$udp_miss_requester_time_sum{$neighbor}) /
		($udp_hit_requester_counter{$neighbor} +
		 $udp_miss_requester_counter{$neighbor}),
	       1000 * ($udp_hit_requester_size_sum{$neighbor} +
		       $udp_miss_requester_size_sum{$neighbor}) /
		(1024 * ($udp_hit_requester_time_sum{$neighbor} +
			    $udp_miss_requester_time_sum{$neighbor}))
	       );
    }

    print("----------------------------------- ------- ------ -------- ------ ---- -------\n");

    printf("%-35s %7d %6.2f %8d %6.2f %-4.4s %7.2f\n",
	   Sum,
	   $udp_hit_counter + $udp_miss_counter,
	   100 * $udp_hit_counter / ($udp_hit_counter + $udp_miss_counter),
	   ($udp_hit_size_sum + $udp_miss_size_sum) / 1024,
	   100 * $udp_hit_size_sum / ($udp_hit_size_sum + $udp_miss_size_sum),
	   ($udp_miss_time_sum + $udp_hit_time_sum) / ($udp_miss_counter +
						       $udp_hit_counter),
	   1000 * ($udp_hit_size_sum + $udp_miss_size_sum) /
	    (1024 * ($udp_miss_time_sum + $udp_hit_time_sum))
	   );
}
if ($tcp_counter == 0) {
    print "\n# TCP-Requests: none\n";
} else {
    print "\n# TCP-Requests                            Request     %    kByte      %   Hit-%\n";
    print("----------------------------------------- ------- ------ -------- ------ ------\n");

    foreach $requester (sort {$tcp_requester_size_sum{$b} <=>
	$tcp_requester_size_sum{$a}} keys(%tcp_requester_size_sum)) {
	if (length($requester) > 41) {
	    printf("%s\n                                         ",
		   $requester);
	} else {
	    printf("%-41s", $requester);
	}
	printf(" %7d %6.2f %8d %6.2f %6.2f\n",
	       $tcp_requester_counter{$requester},
	       100 * $tcp_requester_counter{$requester} / $tcp_counter,
	       $tcp_requester_size_sum{$requester} / 1024,
	       100 * $tcp_requester_size_sum{$requester} / $tcp_size_sum,
	       100 * $tcp_hit_requester_counter{$requester} /
		    $tcp_requester_counter{$requester}
	       );
	last if(--$tophost==0);
    }

    print("----------------------------------------- ------- ------ -------- ------ ------\n");

    printf("%-41s %7d %6.2f %8d %6.2f %6.2f\n",
	   Sum,
	   $tcp_counter,
	   100,
	   $tcp_size_sum / 1024,
	   100,
	   100 * $tcp_hit_counter / $tcp_counter
	   );
}

print("\n\n\n" . 'This analysis was produced by calamaris $Revision: 1.20 $ by Cord Beermann' . "\n");

sub address_to_name {
    local ($address) = shift (@_);
    local (@octets);
    local ($name, $aliases, $type, $len, $addr);
    local ($ip_number);
    @octets = split ('\.', $address) ;
    if ($#octets != 3) {
	undef;
    }
    $ip_number = pack ("CCCC", @octets[0..3]);
    ($name, $aliases, $type, $len, $addr) = gethostbyaddr ($ip_number, 2) ;
    if ($name) {
	$name;
    } else {
	undef;
    }
}

sub convert_date {
    my $date = shift(@_);
    if ($date) {
	my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($date))[0,1,2,3,4,5,6];
	my $month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
	my $retdate = sprintf("%02d.%s %02d %02d:%02d:%02d\n",
			      $mday,
			      $month,
			      $year,
			      $hour,
			      $min,
			      $sec
			      );
	chomp($retdate);
	return $retdate;
    } else {
	return "                  ";
    }
}
