#!/usr/bin/perl -w
#
# $Id: calamaris,v 2.99.1.3 2004/12/23 20:05:27 cord Exp $
#
# DESCRIPTION: calamaris - statistic package for diverse Proxy-Cache-Servers
#
# Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Cord Beermann
#
# URL: http://Calamaris.Cord.de/
# Announcement-Mailing-list: send Mail with 'subscribe' in the Mail-Body to
#			     Calamaris-announce-request@Cord.de
#
# AUTHORS: Cord Beermann <Cord@Wunder-Nett.org>
#	Michael Pophal <michael.pophal@nefkom.net>
#
# Thanks to these contributors, bug reporters, and feature requesters:
#	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>
#	Ernst Heiri <heiri@switch.ch>
#	Shamil R. Yahin <SSHY@cclib.nsu.ru>
#	Thoralf Freitag <Thoralf.Freitag@isst.fhg.de>
#	Marco Paganini <paganini@paganini.net>
#	Michael Riedel <mr@fto.de>
#	Kris Boulez <krbou@pgsgent.be>
#	Mark Visser <mark@snt.utwente.nl>
#	Gary Palmer <gjp@erols.com>
#	Stefan Watermann <stefan@metronet.de>
#	Roar Smith <Roar.Smith@Ericsson.Dk>
#	Bernd Lienau <lienau@tli.de>
#	Gary Lindstrom <gplindstrom@exodus.nnc.edu>
#	Jost Krieger <Jost.Krieger@ruhr-uni-bochum.de>
#	Gerd Michael Hoffmann <Hoffmann@dvgw.de>
#	Gerold Meerkoetter <gerold@noc.fh-lippe.de>
#	Iain Lea <iain@bricbrac.de>
#	Emmanuel Adeline <emmanuel.adeline@mail.dotcom.fr>
#	John Line <webadm@info.cam.ac.uk>
#	Christos Cheretakis <xalkina@otenet.gr>
#	Ryan Donnelly <rmd@doit.wisc.edu>
#	Richard Vaughan <richard_vaughan@timewarp.co.uk>
#	Jonas Luster <jonas@nethammer.qad.org>
#	Clare Lahiff <Clare.Lahiff@anu.edu.au>
#	Toni Andjelkovic <toni@telecom.at>
#	Chris Teakle <ccteakle@its.uq.edu.au>
#	Dancer Vesperman <dancer@zeor.simegen.com>
#	Vincent ? <vincent@aib.net>
#	Elrond ? <Elrond@Wunder-Nett.org>
#	Holger Marzen <holger@marzen.de>
#	Panagiotis Christias <P.Christias@noc.ntua.gr>
#	Patrik Rak <patrik@ash.ein.cz>
#	Steve Snyder <swsnyder@insightbb.com>
#	Michael Copeland <michael.copeland@bell.ca>
#	Warren Brown <wbrown@inktomi.com>
#	Andy Nik <nik@che.nsk.su>
#	Frank Roechter <frank@fhd.de>
#	Antonio Casado Rodrguez <acasado@ualm.es>
#	Pavol Adamec <pavol_adamec@tempest.sk>
#	Ram Cherukuri <ram@edgix.com>
#	Marco Koch <MK@electricpaper.de>
#	Stephen Welker <stephen.welker@nemostar.com.au>
#	Christian Niederdorfer <christian.niederdorfer@infineon.com>
#	Klaus Brinkmeyer <Klaus_Brinkmeyer@inasys.de>
#	Filip ? <mechanix@digibel.org>
#	Matt Hubbard <m.hubbard@ic.ac.uk>
#	James Crocker <jcrocker@menasha.com>
#	Enrico Ardizzoni <enrico@unife.it>
#	Shawn Switenky <S.Switenky@telesat.ca>
#	Jarkko Saloranta <jjs@kpo.fi>
#	Jigar Rasalawala <jrasalawala@fourelle.com>
#	Philipp Frauenfelder <philipp.frauenfelder@swissonline.ch>
#	Alexey Markov <markov@crpi.ru>
#	Mark Gthling <privat@mague-pcservice.de>
#	Sergey Zarubin <serge-home@yandex.ru>
#	Helge Oldach <calamaris@oldach.net>
#	Michael R. Schwarzbach <spg@fs.tum.de>
#	Radu - Eosif Mihailescu <rmihailescu@lumina.ro>
#	Steffen Sledz <sledz@zone42.org>
#	Kenytt Avery <kavery@willingminds.com>
#	SO Kwok Tsun <ktso@cuhk.edu.hk>
#	Chris Knight <chris@aims.com.au>
#	ycdtosa ? <ycdtosa@eupla.unizar.es>
#	Peter W. Osel <pwo@Infineon.COM>
#	Pawel Worach <pawel.worach@nordea.com>
#	Franck Bourdonnec <fbourdonnec@chez.com>
#	Chris Clemson <Chris.Clemson@softwareag.co.uk>

# 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.

# (If you modify and want to publish it under the name 'Calamaris', please ask
# me. I don't want to confuse the 'audience' with many different versions of
# the same name and/or Version number. (This is not part of the license, it
# is only a favour i asked of you.))

# 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., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, 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

# If you have to remove this, read the README!
require 5.002;

use Getopt::Long;
use Sys::Hostname;
use lib '/usr/local';

Getopt::Long::Configure ("bundling");
GetOptions( "all-useful-reports|a" => \$opt_a, "benchmark|b=i" => \$opt_b,
	   "cache-input-file|i=s" => \$opt_i,
	   "cache-output-file|o=s" => \$opt_o,
	   "config-file=s" => \$opt_config_file, "copyright|C" => \$opt_C,
	   "domain-report|d=i" => \$opt_d,
	   "domain-report-limit=i" => \$opt_domain_report_limit,
	   "domain-report-n-level|N=i" => \$opt_N, "dump-loop|L" => \$opt_L,
	   "errorcode-distribution-report" => \$opt_errorcode_distribution,
	   "help|h" => \$opt_h, "hostname|H=s" => \$opt_H,
	   "input-format|f=s" => \$opt_f,
	   "image-type=s" => \$opt_image_type,
	   "ipfilter-exclude=s" => \$opt_ipfilter_exclude,
	   "ipfilter-include=s" => \$opt_ipfilter_include,
	   "logo|l=s" => \$opt_l, "meta|M=s" => \$opt_M,
	   "no-input|z" => \$opt_z, "output-file=s" => \$opt_output_file,
	   "output-format|F=s" => \$opt_F,
	   "output-path=s" => \$opt_output_path,
	   "peak-report|p=s" => \$opt_p, "performance-report|P=i" => \$opt_P,
	   "performance-report_adjust|T=i" => \$opt_T,
	   "requester-report|r=i" => \$opt_r,
	   "requester-report-no-dns-lookup|n" => \$opt_n,
	   "requester-report-use-user-info|u" => \$opt_u,
	   "requester-report-with-targets|R=i" => \$opt_R,
	   "response-time-report" => \$opt_response_time,
	   "show-reports|S=s" => \$opt_S,
	   "size-distribution-report|D=i" => \$opt_D,
	   "sort-order|O" => \$opt_O, "status-report|s" => \$opt_s,
	   "time-interval|I=s" => \$opt_I, "type-report|t=i" => \$opt_t,
	   "type-report-ignore-case|c" => \$opt_c, "unit|U=s" => \$opt_U,
	   "verbose|v" => \$opt_v,
	   "version|V" => \$opt_V, );

readconfig();

print "$USAGE\n\n" if $opt_h;
print "$VERSION\n\n" if $opt_V;
print "$COPYRIGHT\n\n" if $opt_C or $opt_h or $opt_V;

exit 0 if $opt_h or $opt_C or $opt_V;

if ($usage_err) {
  print STDERR "run '$0 -h' for help.\n\n";
  exit 1;
}

# initialize variables
$test_string = "\nTest results:\n\n" if $test;
$counter = $hier = $hier_direct = $hier_direct_size = $hier_direct_time =
  $hier_parent = $hier_parent_size = $hier_parent_time = $hier_sibling =
  $hier_sibling_size = $hier_sibling_time = $hier_size = $hier_time =
  $invalid = $ordered_tcp_req_time_max = $ordered_tcp_req_time_max_interval =
  $ordered_udp_req_time_max = $ordered_udp_req_time_max_interval =
  $peak_all_hour = $peak_all_hour_time = $peak_all_min = $peak_all_min_time =
  $peak_all_sec = $peak_all_sec_time = $peak_tcp_hour = $peak_tcp_hour_time =
  $peak_tcp_min = $peak_tcp_min_time = $peak_tcp_sec = $peak_tcp_sec_time =
  $peak_udp_hour = $peak_udp_hour_time = $peak_udp_min = $peak_udp_min_time =
  $peak_udp_sec = $peak_udp_sec_time = $size = $skipped = $tcp = $tcp_hit =
  $tcp_hit_size = $tcp_hit_time = $tcp_miss = $tcp_miss_none =
  $tcp_miss_none_size = $tcp_miss_none_time = $tcp_miss_size = $tcp_miss_time =
  $tcp_size = $tcp_time = $time = $time_end = $time_run = $udp = $udp_hit =
  $udp_hit_size = $udp_hit_time = $udp_miss = $udp_miss_size = $udp_miss_time =
  $udp_size = $udp_time = 0;
$time_begin = 9999999999;
@months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
undef(%udp_reqtime_size);
undef(%udp_hit_reqtime_size);
undef(%udp_hit_reqtime);
undef(%tcp_reqtime_size);
undef(%tcp_hit_reqtime_size);
undef(%tcp_hit_reqtime);
undef(%perf_ip);

readcache() if ($opt_i);

unless ($opt_z) {
  while ( defined( $line = <> ) ) {
    if ( not defined $opt_f or $opt_f eq 'auto' ) {
      if ( $line =~
	  m#^\d+\.\d+\s+\d+\s+[\w\-\.:]+\s+\w+/\d+\s+\d+\s+\w+\s+\S+\s+\S+\s+\w+/\S+\s+\S+$#
	  ) {
	$opt_f = 'squid';
	print STDERR "guessing... using '-f squid'\n" if ($opt_v);
	last;
      } elsif ( $line =~
  m#^\d+\s+\d+\s+[\w\-\.:]+\s+\w+/\d+\s+\d+\s+\w+\s+\S+\s+\S+\s+\w+/\S+\s+\S+$#
	       ) {
	$opt_f = 'its';
	print STDERR "guessing... using '-f its'\n" if ($opt_v);
	last;
      } elsif ( $line =~
   m#^\d+\.\d+\s+\d+\s+[\w\-\.:]+\s+\w+/\d+\s+\d+\s+\w+\s+\S+\s+\S+\s+\w+/\S+$#
	       ) {
	$opt_f = 'squid-old';
	print STDERR "guessing... using '-f squid-old'\n" if ($opt_v);
	last;
      } elsif ( $line =~
	       m#^\d+\.\d+\s+\d+\s+[\w\-\.:]+\s+\w+/\d+\s+\d+\s+\w+\s+\S+\s+\S+\s+\w+/\S+\s+(\S|; c)+\s+\S+$#
	       ) {
	$opt_f = 'nc';
	print STDERR "guessing... using '-f nc'\n" if ($opt_v);
	last;
      } elsif ( $line =~ s/^\s*#\s*Fields:\s*// ) {
	$opt_f = 'elff';
	print STDERR "guessing... using '-f elff'\n" if ($opt_v);
	last;
      } elsif ( $line =~ s/^\s*format=\s*// ) {
	$opt_f = 'nse';
	print STDERR "guessing... using '-f nse'\n" if ($opt_v);
	last;
      } elsif ( $line =~
	       m#^\d+\.\d+\s+\d+\s+[\w\-\.:]+\s+\w+/\-?\d+\s+\d+\s+\w+\s+\S+\s+\S+\s+\w+/\S+\s+\S+\s+#
	       ) {
	$opt_f = 'squid-extended';
	print STDERR "guessing... using '-f squid-extended'\n" if ($opt_v);
	last;
      } elsif ( $line =~
	       m#^[\w\-\.:]+\s+\S+\s+\S+\s+\[.+\]\s+\"\w+\s+\S+\s+\S+\"\s+\d+\s+\d+\s+\S+(\s+\[.*\]\s+\[.*\])?$#
	       ) {
	print STDERR "$0: The first line of the input looks to me as if you
switched 'emulate_httpd_log' to on. I can't parse that format. Please read
the README on this.\n\n";
	exit(1);
      } elsif ( $line =~ m/^\s*(#|$)/ ) {
	print STDERR "skipping: $line\n" if ($opt_v);
	next;
      } else {
	print STDERR "$0: I don't know this input format. Please check the
input. If you\'re sure that the following line is NOT corrupt and the error
also occurs with the recent version of Calamaris (see the README for pointers
and known bugs) then report it with the following line to
<Calamaris-bug\@Cord.de>. Thank You.\n\n$line\n\n";
	exit(1);
      }
    } else {
      last;
    }
  }
  if ( not defined $opt_f or $opt_f eq 'auto' ) {
    print "\nno requests found\n";
    exit(0);
  }

# IP Filter
  my %filter_ip;
  if ($opt_ipfilter_exclude or $opt_ipfilter_include) {
    die( "$0: Couldn't load package NetAddr::IP,
  maybe it is not installed: $!\n" ) unless (eval "require NetAddr::IP");
    my $to_split = ($opt_ipfilter_exclude) ?
      $opt_ipfilter_exclude : $opt_ipfilter_include;
    foreach ( split /:/, $to_split ) {
      $ip = new NetAddr::IP( (split /\//, $_) );
      foreach ( $ip->split('32') ) {
	$filter_ip{$_->addr()} = 1;
      }
    }
  }

  print STDERR "print a hash-sign for each $opt_b lines:\n" if ($opt_b);
  $loop = '
for ( ; $line; $line = <> ) {';
  if ( $opt_f eq 'squid' ) {
    $loop .= '
  ( $log_date, $log_reqtime, $log_requester, $log_status, $log_size,
   $log_method, $log_url, $log_ident, $log_hier, $log_content, $foo ) =
    split ( /\s+/, $line );
  if ( not defined $foo or not defined $log_content or $foo ne \'\'
      or $log_content eq \'\' or $log_reqtime < 0
      or $log_date !~ m#^\d+\.\d{3}$# ) {';
  } elsif ( $opt_f eq 'squid-extended' ) {
    $loop .= '
  $line =~ s/ \[[^\[\]]*\]//g;
  ( $log_date, $log_reqtime, $log_requester, $log_status, $log_size,
   $log_method, $log_url, $log_ident, $log_hier, $log_content) =
    split ( /\s+/, $line, 10 );
  chomp($log_content);
  if ( not defined $log_content or $log_content eq \'\' or $log_reqtime < 0
      or $log_date !~ m#^\d+\.\d{3}$# ) {';
  } elsif ( $opt_f eq 'its' ) {
    $loop .= '
  ( $log_date, $log_reqtime, $log_requester, $log_status, $log_size,
   $log_method, $log_url, $log_ident, $log_hier, $log_content, $foo ) =
    split ( /\s+/, $line );
  if ( not defined $foo or not defined $log_content or $foo ne \'\'
      or $log_content eq \'\' or $log_reqtime < 0
      or $log_date !~ m#^\d+$# ) {';
  } elsif ( $opt_f eq 'squid-old' ) {
    $loop .= '
  ( $log_date, $log_reqtime, $log_requester, $log_status, $log_size,
   $log_method, $log_url, $log_ident, $log_hier, $foo ) =
    split ( /\s+/, $line );
  unless ( not defined $foo or not defined $log_hier or $foo ne \'\'
	  or $log_hier eq \'\' or $log_reqtime < 0
	  or $log_date !~ m#^\d+\.\d{3}$# ) {';
  } elsif ( $opt_f eq 'nc' ) {
    $loop .= '
  $line =~ s#\; c#\;_c#og; # Hack to handle buggy logfiles of NetCache V3.2.x
  ( $log_date, $log_reqtime, $log_requester, $log_status, $log_size,
   $log_method, $log_url, $log_ident, $log_hier, $log_content, $log_abort,
   $foo ) = split ( /\s+/, $line );
  if ( not defined $foo or not defined $log_abort or $foo ne \'\'
	or $log_abort eq \'\' or $log_reqtime < 0
	or $log_date !~ m#^\d+\.\d{3}$# ) {';
  } elsif ( $opt_f eq 'elff' ) {
    @fields = split ( /\s+/, $line );
    $loop .= '
  use Time::Local;
  if ( $line =~
      m#^';
    foreach (@fields) {
      $tmpline1 .= '\s+' if ($tmpline1);
      if ( $_ eq 'date' ) {
	$tmpline1 .= '(\d+)-(\d+)-(\d+)';
	$tmpline2 .= '
    $log_year = $' . ++$offset . ';
    $log_month = $' . ++$offset . ';
    $log_day = $' . ++$offset . ';';
      } elsif ( $_ eq 'time' ) {
	$tmpline1 .= '(\d+):(\d+):(\d+)';
	$tmpline2 .= '
    $log_hour = $' . ++$offset . ';
    $log_min = $' . ++$offset . ';
    $log_sec = $' . ++$offset . ';';
      } elsif ( $_ eq 'x-timestamp' ) {
	$tmpline1 .= '(\d+\.\d+)';
	$tmpline2 .= '
    $log_date = $' . ++$offset . ';';
	$log_date = 0;
      } elsif ( $_ eq 'c-ip' ) {
	$tmpline1 .= '([\w\-\.:]+)';
	$tmpline2 .= '
    $log_requester = $' . ++$offset . ';';
      } elsif ( $_ eq 'cs-authname' or $_ eq 'x-remote-id'
	       or $_ eq 'x-username' or $_ eq 'cs-username' ) {
	$tmpline1 .= '(\S+)';
	$tmpline2 .= '
    $log_ident = $' . ++$offset . ';';
      } elsif ( $_ eq 's-ip' or $_ eq 's-sitename' ) {
	$tmpline1 .= '[\w\-\.]+';
      } elsif ( $_ eq 'cs-method' ) {
	$tmpline1 .= '([\w\-]+)';
	$tmpline2 .= '
    $log_method = $' . ++$offset . ';';
      } elsif ( $_ eq 'cs-uri' ) {
	$tmpline1 .= '(\S+)';
	$tmpline2 .= '
    $log_url = $' . ++$offset . ';';
      } elsif ( $_ eq 'cs-uri-stem' or $_ eq 'cs-uri-query'
	       or $_ eq 'x-note' ) {
	$tmpline1 .= '\S+';
      } elsif ( $_ eq 'c-version' ) {
	$tmpline1 .= '\w+/[\d\.]+';
      } elsif ( $_ eq 'sc-status' ) {
	$tmpline1 .= '(\d+)';
	$tmpline2 .= '
    $log_code = $' . ++$offset . ';';
      } elsif ( $_ eq 'sc-bytes' or $_ eq 'bytes' ) {
	$tmpline1 .= '(\d+)';
	$tmpline2 .= '
    $log_size = $' . ++$offset . ';';
      } elsif ( $_ eq 'cs-bytes' ) {
	$tmpline1 .= '\d+';
      } elsif ( $_ eq 'x-elapsed-milliseconds' ) {
	$tmpline1 .= '(\d+)';
	$tmpline2 .= '
    $log_reqtime = $' . ++$offset . ';';
      } elsif ( $_ eq 'time-taken' ) {
	$tmpline1 .= '([\d\.]+)';
	$tmpline2 .= '
    $log_reqtime = $' . ++$offset . ' * 1000;';
      } elsif ( $_ eq 'cs(User-Agent)' or $_ eq 'cs(Cookie)'
	       or $_ eq 'cs(Referer)' or $_ eq 'sc(Referer)'
	       or $_ eq 'cs(X-Forwarded-For)' or $_ eq 'x-hiername' ) {
	$tmpline1 .= '(\"[^\"]*\"|-)';
	$tmpline2 .= '
    ++$offset;';
      } elsif ( $_ eq 'cached' ) {
	$tmpline1 .= '(\d+)';
	$tmpline2 .= '
    $log_cached = $' . ++$offset . ';';
      } elsif ( $_ eq 'x-transaction' ) {
	$tmpline1 .= '(\w+\/[\d\-]+)';
	$tmpline2 .= '
    $log_status = $' . ++$offset . ';';
	$log_status = 0;
      } elsif ( $_ eq 'x-fill-proxy-ip' ) {
	$tmpline1 .= '([\w\-\.]+)';
	$tmpline2 .= '
    $log_proxy_ip = $' . ++$offset . ';';
      } elsif ( $_ eq 'x-origin-ip' ) {
	$tmpline1 .= '([\w\-\.]+)';
	$tmpline2 .= '
    $log_origin_ip = $' . ++$offset . ';';
      } elsif ( $_ eq 'x-hiercode' ) {
	$tmpline1 .= '([\w\-\.\/]+)';
	$tmpline2 .= '
    $log_hier = $' . ++$offset . ';';
	$log_hier = 0;
      } elsif ( $_ eq 'rs(Content-Type)' ) {
	$tmpline1 .= '\"[^\"]*\"';
      } else {
	print STDERR "$0: I don't know this input format. Please check the
input. If you\'re sure that the following is NOT corrupt and the error also
occurs with the recent version of Calamaris (see the README for pointers and
known bugs) then report it with the following line to <Calamaris-bug\@Cord.de>.
Thank You.\n\n$_\n\n";
	exit(1);
      }
    }
    foreach $pattern ( qw(date time c-ip cs-method cs-uri sc-status sc-bytes
			  time-taken cached x-fill-proxy-ip x-origin-ip) ) {
      unless ( grep $pattern, @fields ) {
	print STDERR "$0: Your input file format is missing at least the field
\'$pattern\'. I can\'t parse it. Sorry. If you think that this field isn't
important to you, please report this error to <Calamaris-bug\@Cord.de>.
Thank You.\n\n";
	exit(1);
      }
    }
    foreach $pattern (qw(cs-authname)) {
      unless ( grep $pattern, @fields ) {
	if ( $pattern eq 'cs-authname' ) {
	  $tmpline2 .= '
  $log_ident = "-";';
	}
      }
    }
    $loop .= $tmpline1 . '.?$#
    ) {' . $tmpline2;
    $loop .= '
    $log_date = timegm( $log_sec, $log_min, $log_hour, $log_day,
		       $log_month - 1, $log_year - 1900 );'
      unless defined $log_date;
    $loop .= '
    $log_status = "$log_cached/$log_code";' unless defined $log_status;
    $loop .= '
    if ( $log_origin_ip ne \'-\' ) {
      $log_hier = "DIRECT/$log_origin_ip";
    } elsif ( $log_proxy_ip ne \'-\' ) {
      if ( $log_cached eq \'0\' ) {
	$log_hier = "PARENT_MISS/$log_proxy_ip";
      } else {
	$log_hier = "PARENT_HIT/$log_proxy_ip";
      }
    } else {
      $log_hier = "NONE/-";
    }' unless defined $log_hier;
    $loop .= '
  } else {';
    while ( defined( $line = <> ) ) {
      last unless $line =~ /^\s*#/;
    }
  } elsif ( $opt_f eq 'nse' ) {
    $line =~ s#^format=##og;
    @fields = split ( /\s+/, $line );
    $loop .= '
  use Time::Local;
  if ( $line =~
    m#^';
    foreach (@fields) {
      $tmpline1 .= '\s+' if ($tmpline1);
      if ( $_ eq '[%SYSDATE%]' ) {
	$tmpline1 .= '\[(\d+)/(\w+)/(\d+):(\d+):(\d+):(\d+)\s+\S+\]';
	$tmpline2 .= '
    $log_day = $' . ++$offset . ';
    $log_month = $' . ++$offset . ';
    $log_year = $' . ++$offset . ';
    $log_hour = $' . ++$offset . ';
    $log_min = $' . ++$offset . ';
    $log_sec = $' . ++$offset . ';';
      } elsif ( $_ eq '%Ses->client.ip%' ) {
	$tmpline1 .= '([\w\-\.:]+)';
	$tmpline2 .= '
    $log_requester = $' . ++$offset . ';';
      } elsif ( $_ eq '%Req->vars.pauth-user%' ) {
	$tmpline1 .= '(\S+)';
	$tmpline2 .= '
    $log_ident = $' . ++$offset . ';';
      } elsif ( $_ eq '%Req->vars.remote-status%' or $_ eq '%Req->vars.r2p-cl%'
	       or $_ eq '%Req->vars.cli-status%'
	       or $_ eq '%Req->vars.svr-status%'
	       or $_ eq '%Req->vars.cch-status%' ) {
	$tmpline1 .= '[\w\-\.]+';
      } elsif ( $_ eq '"%Req->reqpb.proxy-request%"' ) {
	$tmpline1 .= '\"(\w+)\s+([^\"]+)\s+\S+\"';
	$tmpline2 .= '
    $log_method = $' . ++$offset . ';
    $log_url = $' . ++$offset . ';';
      } elsif ( $_ eq '%Req->srvhdrs.clf-status%' ) {
	$tmpline1 .= '([\d\-]+)';
	$tmpline2 .= '
    $log_code = $' . ++$offset . ';';
      } elsif ( $_ eq '%Req->vars.p2c-cl%' ) {
	$tmpline1 .= '([\d\-]+)';
	$tmpline2 .= '
    $log_size = $' . ++$offset . ';';
      } elsif ( $_ eq '%Req->headers.content-length%'
	       or $_ eq '%Req->vars.p2r-cl%' or $_ eq '%Req->vars.c2p-hl%'
	       or $_ eq '%Req->vars.p2c-hl%' or $_ eq '%Req->vars.p2r-hl%'
	       or $_ eq '%Req->vars.r2p-hl%' ) {
	$tmpline1 .= '[\d\-]+';
      } elsif ( $_ eq '%Req->vars.xfer-time%' ) {
	$tmpline1 .= '([\d\.]+)';
	$tmpline2 .= '
    $log_reqtime = $' . ++$offset . ' * 1000;';
      } elsif ( $_ eq '%route%' ) {
	$tmpline1 .= '(\d+)';
	$tmpline2 .= '
    $log_cached = $' . ++$offset . ';';
      } elsif ( $_ eq '%Req->vars.actual-route%' ) {
	$tmpline1 .= '(\S+)';
	$tmpline2 .= '
    $log_hier = $' . ++$offset . ';
    if ( $log_hier =~ m#[\(\)]# ) {
      $log_hier =~ s#^(\w+)(\((\S+)\))?$#$1/$3#;
      $log_status = \'TCP_MISS/-\';
    } elsif ( $log_hier =~ m#-# ) {
      $log_hier = $log_hier . \'/-\';
      $log_status = \'TCP_HIT/-\';
    } else {
      $log_hier = $log_hier . \'/-\';
      $log_status = \'TCP_MISS/-\';
    }';
      } elsif ( $_ eq '-' ) {
	$tmpline1 .= '-';
      } else {
	print STDERR "$0: I don't know this input format. Please check the
input. If you\'re sure that the following is NOT corrupt and the error also
occurs with the recent version of Calamaris (see the README for pointers and
known bugs) then report it with the following line to <Calamaris-bug\@Cord.de>.
Thank You.\n\n$_\n\n";
	exit(1);
      }
    }
    foreach $pattern ( qw(%Ses->client.ip% [%SYSDATE%]
			  %Req->reqpb.proxy-request% %Req->srvhdrs.clf-status%
			  %Req->vars.p2c-cl% %Req->vars.xfer-time%) ) {
      unless ( grep $pattern, @fields ) {
	print STDERR "$0: Your input file format is missing at least the field
\'$pattern\'. I can\'t parse it. Sorry. If you think that this field isn't
important to you, please report this error to <Calamaris-bug\@Cord.de>.
Thank You.\n\n";
	exit(1);
      }
    }
    foreach $pattern (qw(%Req->vars.pauth-user% %Req->vars.actual-route%)) {
      unless ( grep /$pattern/, @fields ) {
	if ( $pattern eq '%Req->vars.pauth-user%' ) {
	  $tmpline2 .= '
  $log_ident = "-";';
	} elsif ( $pattern eq '%Req->vars.actual-route%' ) {
	  $tmpline2 .= '
    $log_hier = \'-/-\';
    $log_status = \'-/-\';';
	}
      }
    }
    $loop .= $tmpline1 . '.?$#
    ) {' . $tmpline2 . '
    $monthcount = -1;
    foreach $month (@months) {
      $monthcount++;
      last if ( $month eq $log_month );
    }
    $log_date = timegm( $log_sec, $log_min, $log_hour, $log_day, $monthcount,
		       $log_year - 1900 );
  } else {';
    while ( defined( $line = <> ) ) {
      last unless $line =~ /^\s*#/;
    }
  } else {
    print STDERR "$0: unknown value at -f -option: \"$opt_f\"\n\n$USAGE\n\n";
    exit 1;
  }
  $loop .= '
    chomp($line);';
  $loop .= '
    warn(\'invalid line: "\' . $line . "\"\n");' if $opt_v;
  $loop .= '
    $invalid++;
    next;
  }';
  $loop .= '
    if (($log_date < $interval_begin) or ($log_date > $interval_end)) {
      $skipped++;
      next;
    }
  ' if $opt_I;
  $loop .= '
  if($filter_ip{$log_requester}) {
    $skipped++;
    next;
  } ' if $opt_ipfilter_exclude;
  $loop .= '
  if(! $filter_ip{$log_requester}) {
    $skipped++;
    next;
  } ' if $opt_ipfilter_include;
  $loop .= '
  $log_reqtime = .1 if $log_reqtime == 0;
  ( $log_hitfail, $log_code ) = split \'/\', $log_status;
  $log_size = 0 if ( $log_size eq \'-\' );
  $log_url =~ s/\?.*$/?/;
  @url = split m#[/\\\]#o, $log_url;
  ( $urlprot, $urlhost, $urlext ) = (@url)[ 0, 2, $#url ];
  $urlext = \'.<none>\' if $#url <= 2;
  if ( $#url <= -1 ) {
    $urlext = \'.<error>\';
    $urlprot = $urlhost = \'<error>\';
  }
  $urlext = \'.<dynamic>\'
    if ( $urlext =~ m#[\?;&\$,!@=|%]#o or $log_method eq \'POST\' );
  unless ( defined $urlhost ) {
    $urlhost = $urlprot;';
  $loop .= '
    $log_content = '
    unless $opt_f eq 'squid-old' or $opt_f eq 'elff' or $opt_f eq 'nse';
  $loop .= '
    $urlprot = \'<secure>\';
    $urlext = \'.<secure>\';
  }
  $urlhost =~ s#^.*@##o;
  $urlhost =~ s#[:\?].*$##o;
  $urlhost =~ tr/A-Z/a-z/;
  @urlext = split \'\.\', $urlext;
  $urlext = (@urlext)[$#urlext];
  $urlext = \'<none>\' if $#urlext <= 0;';
  $loop .= '
  $urlext =~ tr/A-Z/a-z/;' if $opt_c;
  $loop .= '
  if ( $urlhost =~ m#^(([0-9][0-9]{0,2}\.){3})[0-9][0-9]{0,2}$#o ) {';
  $loop .= '
    $urlhost = $1 . \'*\';' unless $opt_N == -1;
  $loop .= '
    $urltld = \'<unresolved>\';
  } elsif ( $urlhost =~ m#^(.*\.([^\.]+\.)?)?([^\.]+\.([^\.]+))\.?$#o ) {
    @list = split \'\.\', $urlhost;
    $urltld = pop @list;';
  if ( $opt_N != -1 ) {
    $loop .= '
    $urlhost = $urltld;';
    for ( $i = $opt_N; $i != 1; $i-- ) {
      $loop .= '
    $urlhost = pop (@list) . \'.\' . $urlhost if $#list >= 0;';
    }
    $loop .= '
    $urlhost = pop (@list) . \'.\' . $urlhost
      if ( $urltld =~
	  m#^(a[rtu]|br|c[no]|hk|i[dlm]|jp|kr|l[by]|m[oxy]|nz|p[elnry]|sg|t[hrw]|u[aks]|ve|yu|za)$#o
	  and $#list >= 0 );
    $urlhost = \'*.\' . $urlhost if $#list >= 0;
    $urltld = \'*.\' . $urltld;';
  }
  $loop .= '
  } elsif ( $urlhost =~ m#([!a-z0-9\.\-]|\.\.)#o ) {
    $urlhost = $urltld = $urlext = $urlprot = \'<error>\';
  } else {
    $urltld = $urlhost;
  }';
  if ($opt_n) {
    $loop .= '
  $requester = $log_requester;';
  } else {
    $loop .= '
  $requester = getfqdn($log_requester);';
  }
  $loop .= '
  $requester = $log_ident . \'@\' . $requester if $log_ident ne \'-\';'
    if $opt_u;
  $loop .= '
  ( $log_hier_method, $log_hier_host ) = ( split \'/\', $log_hier )[ 0, 1 ];';
  $loop .= '
  $log_content = \'<unknown>\' if $log_content eq \'-\';
  $log_content =~ tr/A-Z/a-z/;
  $log_content = '
    unless $opt_f eq 'squid-old' or $opt_f eq 'elff' or $opt_f eq 'nse';
  $loop .= '
  $urlhost = $urltld = $urlext = $urlprot = \'<error>\'
    if ( $log_code =~ m#^[45]#o );';
  $loop .= "
  print STDERR '#' if (0 == (\$counter % $opt_b));" if $opt_b;
  $loop .= '
  $counter++;
  $size += $log_size;
  $time += $log_reqtime;
  $method{$log_method} = $method_size{$log_method} =
    $method_time{$log_method} = 0
    unless defined $method{$log_method};
  $method{$log_method}++;
  $method_size{$log_method} += $log_size;
  $method_time{$log_method} += $log_reqtime;
  $time_begin = $log_date if $log_date < $time_begin;
  $time_end = $log_date if $log_date > $time_end;';
  if ( defined $opt_p ) {
    $loop .= '
  if ( defined(@peak_all) ) {
    if ( $log_date < $peak_all[$#peak_all] ) {
      $peak_warn =
	\'Peak values are most likely wrong due to unsorted input!\';
      undef(@peak_all);
      undef(@peak_udp);
      undef(@peak_tcp);
      $peak_all_min_pointer = $peak_all_sec_pointer = $peak_tcp_min_pointer =
	$peak_tcp_sec_pointer = $peak_udp_min_pointer =
	$peak_udp_sec_pointer = 0;
      chomp($line);
      warn( \'unsorted input: "\' . $line . "\"\n" ) if $opt_v;
    }
  }';
    if ( $opt_p eq 'old' ) {
      $loop .= '
  $peak_all_sec_pointer++;
  $peak_all_min_pointer++;
  unshift ( @peak_all, $log_date );
  $peak_all_sec_pointer--
    while $peak_all[ $peak_all_sec_pointer - 1 ] < ( $log_date - 1 );
  $peak_all_min_pointer--
    while $peak_all[ $peak_all_min_pointer - 1 ] < ( $log_date - 60 );
  pop (@peak_all) while $peak_all[$#peak_all] < ( $log_date - 3600 );
  if ( $peak_all_hour < @peak_all ) {
    $peak_all_hour = @peak_all;
    $peak_all_hour_time = $log_date - 3600;
  }
  if ( $peak_all_min < $peak_all_min_pointer ) {
    $peak_all_min = $peak_all_min_pointer;
    $peak_all_min_time = $log_date - 60;
  }
  if ( $peak_all_sec < $peak_all_sec_pointer ) {
    $peak_all_sec = $peak_all_sec_pointer;
    $peak_all_sec_time = $log_date - 1;
  }';
    } elsif ( $opt_p eq 'new' ) {
      $loop .= '
  $date_hour = int( ( $log_date - ( $log_reqtime / 1000 ) ) / 3600 ) * 3600;
  foreach $i ( ( $date_hour / 3600 ) .. int( $log_date / 3600 ) ) {
    $peak_all_hour{ $i * 3600 } = $peak_all_hour_size{ $i * 3600 } = 0
      unless defined $peak_all_hour{ $i * 3600 };
    $peak_all_hour{ $i * 3600 }++;
    $peak_all_hour_size{ $i * 3600 } +=
      $log_size / int( $log_reqtime / 3600000 + 1 );
  }
  $peak_all_sec_pointer++;
  unshift ( @peak_all, $log_date );
  $peak_all_sec_pointer--
    while $peak_all[ $peak_all_sec_pointer - 1 ] < ( $log_date - 1 );
  pop (@peak_all) while $peak_all[$#peak_all] < ( $log_date - 60 );
  if ( $peak_all_min < @peak_all ) {
    $peak_all_min = @peak_all;
    $peak_all_min_time = $log_date - 60;
  }
  if ( $peak_all_sec < $peak_all_sec_pointer ) {
    $peak_all_sec = $peak_all_sec_pointer;
    $peak_all_sec_time = $log_date - 1;
  }';
    } else {
      print STDERR
	"$0: unknown value at -p -option: \"$opt_p\"\n\n$USAGE\n\n";
      exit 1;
    }
  }
  $loop .= '
  if ( ( $log_method =~ m#^ICP_#o ) or ( $log_status =~ m#^ICP#o ) ) {
    $udp++;
    $udp_size += $log_size;
    $udp_time += $log_reqtime;';
  $loop .= '
    $udp_reqtime{$log_reqtime} = $udp_hit_reqtime{$log_reqtime} =
      $udp_reqtime_size{$log_reqtime} = $udp_hit_reqtime_size{$log_reqtime} = 0
      unless defined $udp_reqtime{$log_reqtime};
    $udp_reqtime{$log_reqtime}++;
    $udp_reqtime_size{$log_reqtime} += $log_size;' if $opt_response_time;
  $loop .= '
    $udp_code{$log_code} = $udp_code_time{$log_code} =
      $udp_code_size{$log_code} = $udp_hit_code{$log_code} =
      $udp_hit_code_size{$log_code} = 0
      unless defined $udp_code{$log_code};
    $udp_code{$log_code}++;
    $udp_code_time{$log_code} += $log_reqtime;
    $udp_code_size{$log_code} += $log_size;' if $opt_errorcode_distribution;
  $loop .= '
    $udp_requester{$requester} = $udp_requester_size{$requester} =
      $udp_requester_time{$requester} = $udp_hit_requester{$requester} =
      $udp_hit_requester_size{$requester} = 0
      unless defined $udp_requester{$requester};
    $udp_requester{$requester}++;
    $udp_requester_size{$requester} += $log_size;
    $udp_requester_time{$requester} += $log_reqtime;' if ($opt_r);
  $loop .= '
    $udp_requester_urlhost{$requester}{$urlhost} =
      $udp_requester_urlhost_size{$requester}{$urlhost} =
      $udp_requester_urlhost_time{$requester}{$urlhost} =
      $udp_hit_requester_urlhost{$requester}{$urlhost} =
      $udp_hit_requester_urlhost_size{$requester}{$urlhost} = 0
      unless defined $udp_requester_urlhost{$requester}{$urlhost};
    $udp_requester_urlhost{$requester}{$urlhost}++;
    $udp_requester_urlhost_size{$requester}{$urlhost} += $log_size;
    $udp_requester_urlhost_time{$requester}{$urlhost} += $log_reqtime;'
    if ($opt_R);
  if ( not defined $opt_p ) {
  } elsif ( $opt_p eq 'old' ) {
    $loop .= '
    $peak_udp_sec_pointer++;
    $peak_udp_min_pointer++;
    unshift ( @peak_udp, $log_date );
    $peak_udp_sec_pointer--
      while $peak_udp[ $peak_udp_sec_pointer - 1 ] < ( $log_date - 1 );
    $peak_udp_min_pointer--
      while $peak_udp[ $peak_udp_min_pointer - 1 ] < ( $log_date - 60 );
    pop @peak_udp while $peak_udp[$#peak_udp] < ( $log_date - 3600 );
    if ( $peak_udp_hour < @peak_udp ) {
      $peak_udp_hour = @peak_udp;
      $peak_udp_hour_time = $log_date - 3600;
    }
    if ( $peak_udp_min < $peak_udp_min_pointer ) {
      $peak_udp_min = $peak_udp_min_pointer;
      $peak_udp_min_time = $log_date - 60;
    }
    if ( $peak_udp_sec < $peak_udp_sec_pointer ) {
      $peak_udp_sec = $peak_udp_sec_pointer;
      $peak_udp_sec_time = $log_date - 1;
    }';
  } elsif ( $opt_p eq 'new' ) {
    $loop .= '
    foreach $i ( ( $date_hour / 3600 ) .. int( $log_date / 3600 ) ) {
      $peak_udp_hour{ $i * 3600 } = $peak_udp_hour_size{ $i * 3600 } = 0
	unless defined $peak_udp_hour{ $i * 3600 };
      $peak_udp_hour{ $i * 3600 }++;
      $peak_udp_hour_size{ $i * 3600 } +=
	$log_size / int( $log_reqtime / 3600000 + 1 );
    }
    $peak_udp_sec_pointer++;
    unshift ( @peak_udp, $log_date );
    $peak_udp_sec_pointer--
      while $peak_udp[ $peak_udp_sec_pointer - 1 ] < ( $log_date - 1 );
    pop @peak_udp while $peak_udp[$#peak_udp] < ( $log_date - 60 );
    if ( $peak_udp_min < @peak_udp ) {
      $peak_udp_min = @peak_udp;
      $peak_udp_min_time = $log_date - 60;
    }
    if ( $peak_udp_sec < $peak_udp_sec_pointer ) {
      $peak_udp_sec = $peak_udp_sec_pointer;
      $peak_udp_sec_time = $log_date - 1;
    }';
  }
  $loop .= '
    if ( $log_hitfail =~ m#^(UDP|ICP)_HIT#o ) {
      $udp_hit++;
      $udp_hit_size += $log_size;
      $udp_hit_time += $log_reqtime;';
  $loop .= '
      $udp_hit_reqtime{$log_reqtime}++;
      $udp_hit_reqtime_size{$log_reqtime} += $log_size;'
    if $opt_response_time;
  $loop .= '
      $udp_hit_code{$log_code}++;
      $udp_hit_code_size{$log_code} += $log_size;'
    if $opt_errorcode_distribution;
  $loop .= '
      $udp_hit_requester{$requester}++;
      $udp_hit_requester_size{$requester} += $log_size;' if $opt_r;
  $loop .= '
      $udp_hit_requester_urlhost{$requester}{$urlhost}++;
      $udp_hit_requester_urlhost_size{$requester}{$urlhost} += $log_size;'
    if ($opt_R);
  $loop .= '
      $udp_hit{$log_hitfail} = $udp_hit_size{$log_hitfail} =
	$udp_hit_time{$log_hitfail} = 0
	unless defined $udp_hit{$log_hitfail};
      $udp_hit{$log_hitfail}++;
      $udp_hit_size{$log_hitfail} += $log_size;
      $udp_hit_time{$log_hitfail} += $log_reqtime;' if ($opt_s);
  $loop .= '
    } else {
      $udp_miss++;
      $udp_miss_size += $log_size;
      $udp_miss_time += $log_reqtime;';
  $loop .= '
      $udp_miss{$log_hitfail} = $udp_miss_size{$log_hitfail} =
	$udp_miss_time{$log_hitfail} = 0
	unless defined $udp_miss{$log_hitfail};
      $udp_miss{$log_hitfail}++;
      $udp_miss_size{$log_hitfail} += $log_size;
      $udp_miss_time{$log_hitfail} += $log_reqtime;' if ($opt_s);
  $loop .= '
    }
  } else {
    $tcp++;
    $tcp_size += $log_size;
    $tcp_time += $log_reqtime;';
  $loop .= '
    $tcp_reqtime{$log_reqtime} = $tcp_hit_reqtime{$log_reqtime} =
      $tcp_reqtime_size{$log_reqtime} = $tcp_hit_reqtime_size{$log_reqtime} = 0
      unless defined $tcp_reqtime{$log_reqtime};
    $tcp_reqtime{$log_reqtime}++;
    $tcp_reqtime_size{$log_reqtime} += $log_size;' if $opt_response_time;
  $loop .= '
    $tcp_code{$log_code} = $tcp_code_time{$log_code} =
      $tcp_code_size{$log_code} = $tcp_hit_code_size{$log_code} =
      $tcp_hit_code{$log_code} = 0
      unless defined $tcp_code{$log_code};
    $tcp_code{$log_code}++;
    $tcp_code_time{$log_code} += $log_reqtime;
    $tcp_code_size{$log_code} += $log_size;' if $opt_errorcode_distribution;
  $loop .= '
    $perf_date = ( int( ( $log_date + ' . "( $opt_T * 60 ) ) /
      ( 60 * $opt_P ) ) * 60 * $opt_P ) - ( $opt_T * 60 );" . '
    unless ( defined $perf_counter{$perf_date} ) {
      $perf_counter{$perf_date} = $perf_size{$perf_date} =
	$perf_tcp_hit_size{$perf_date} = $perf_tcp_miss_size{$perf_date} =
	$perf_hier_direct_size{$perf_date} = $perf_tcp_hit{$perf_date} =
	$perf_hier_sibling_size{$perf_date} =
	$perf_hier_parent_size{$perf_date} = 0;
      $perf_time{$perf_date} = $perf_tcp_hit_time{$perf_date} =
	$perf_tcp_miss_time{$perf_date} = $perf_hier_direct_time{$perf_date} =
	$perf_hier_sibling_time{$perf_date} =
	$perf_hier_parent_time{$perf_date} = .0000000001;
    }
    $perf_counter{$perf_date}++;
    $perf_ip{$perf_date}{$log_requester} = 1;
    $perf_size{$perf_date} += $log_size;
    $perf_time{$perf_date} += $log_reqtime;' if ($opt_P);
  $loop .= '
    $tcp_requester{$requester} = $tcp_requester_size{$requester} =
      $tcp_requester_time{$requester} = $tcp_hit_requester{$requester} =
      $tcp_hit_requester_size{$requester} = 0
      unless defined $tcp_requester{$requester};
    $tcp_requester{$requester}++;
    $tcp_requester_size{$requester} += $log_size;
    $tcp_requester_time{$requester} += $log_reqtime;' if ($opt_r);
  $loop .= '
    $distribution = $log_size ? int( log($log_size) / log($opt_D) ) : -1;
    $tcp_distribution{$distribution} = $tcp_distribution_size{$distribution} =
      $tcp_distribution_time{$distribution} =
      $tcp_hit_distribution{$distribution} =
      $tcp_hit_distribution_size{$distribution} = 0
      unless defined $tcp_distribution{$distribution};
    $tcp_distribution{$distribution}++;
    $tcp_distribution_size{$distribution} += $log_size;
    $tcp_distribution_time{$distribution} += $log_reqtime;' if ($opt_D);
  $loop .= '
    $tcp_requester_urlhost{$requester}{$urlhost} =
      $tcp_requester_urlhost_size{$requester}{$urlhost} =
      $tcp_requester_urlhost_time{$requester}{$urlhost} =
      $tcp_hit_requester_urlhost{$requester}{$urlhost} =
      $tcp_hit_requester_urlhost_size{$requester}{$urlhost} = 0
      unless defined $tcp_requester_urlhost{$requester}{$urlhost};
    $tcp_requester_urlhost{$requester}{$urlhost}++;
    $tcp_requester_urlhost_size{$requester}{$urlhost} += $log_size;
    $tcp_requester_urlhost_time{$requester}{$urlhost} += $log_reqtime;'
    if ($opt_R);
  $loop .= '
    $tcp_urlhost{$urlhost} = $tcp_urlhost_size{$urlhost} =
      $tcp_urlhost_time{$urlhost} = $tcp_hit_urlhost{$urlhost} =
      $tcp_hit_urlhost_size{$urlhost} = 0
      unless defined $tcp_urlhost{$urlhost};
    $tcp_urlhost{$urlhost}++;
    $tcp_urlhost_size{$urlhost} += $log_size;
    $tcp_urlhost_time{$urlhost} += $log_reqtime;
    $tcp_urltld{$urltld} = $tcp_urltld_size{$urltld} =
      $tcp_urltld_time{$urltld} = $tcp_hit_urltld{$urltld} =
      $tcp_hit_urltld_size{$urltld} = 0
      unless defined $tcp_urltld{$urltld};
    $tcp_urltld{$urltld}++;
    $tcp_urltld_time{$urltld} += $log_reqtime;
    $tcp_urltld_size{$urltld} += $log_size;' if ($opt_d);
  $loop .= '
    $tcp_urlprot{$urlprot} = $tcp_urlprot_size{$urlprot} =
      $tcp_hit_urlprot{$urlprot} = $tcp_hit_urlprot_size{$urlprot} = 0
      unless defined $tcp_urlprot{$urlprot};
    $tcp_urlprot{$urlprot}++;
    $tcp_urlprot_time{$urlprot} += $log_reqtime;
    $tcp_urlprot_size{$urlprot} += $log_size;' if ($opt_t);
  if ( not defined $opt_p ) {
  } elsif ( $opt_p eq 'old' ) {
    $loop .= '
    $peak_tcp_sec_pointer++;
    $peak_tcp_min_pointer++;
    unshift ( @peak_tcp, $log_date );
    $peak_tcp_sec_pointer--
      while $peak_tcp[ $peak_tcp_sec_pointer - 1 ] < ( $log_date - 1 );
    $peak_tcp_min_pointer--
      while $peak_tcp[ $peak_tcp_min_pointer - 1 ] < ( $log_date - 60 );
    pop (@peak_tcp) while $peak_tcp[$#peak_tcp] < ( $log_date - 3600 );
    if ( $peak_tcp_hour < @peak_tcp ) {
      $peak_tcp_hour = @peak_tcp;
      $peak_tcp_hour_time = $log_date - 3600;
    }
    if ( $peak_tcp_min < $peak_tcp_min_pointer ) {
      $peak_tcp_min = $peak_tcp_min_pointer;
      $peak_tcp_min_time = $log_date - 60;
    }
    if ( $peak_tcp_sec < $peak_tcp_sec_pointer ) {
      $peak_tcp_sec = $peak_tcp_sec_pointer;
      $peak_tcp_sec_time = $log_date - 1;
    }';
  } elsif ( $opt_p eq 'new' ) {
    $loop .= '
    foreach $i ( $date_hour / 3600 .. int( $log_date / 3600 ) ) {
      $peak_tcp_hour{ $i * 3600 } = $peak_tcp_hour_size{ $i * 3600 } = 0
	unless defined $peak_tcp_hour{ $i * 3600 };
      $peak_tcp_hour{ $i * 3600 }++;
      $peak_tcp_hour_size{ $i * 3600 } +=
	$log_size / int( $log_reqtime / 3600000	+ 1 );
    }
    $peak_tcp_sec_pointer++;
    unshift ( @peak_tcp, $log_date );
    $peak_tcp_sec_pointer--
      while $peak_tcp[ $peak_tcp_sec_pointer - 1 ] < ( $log_date - 1 );
    pop (@peak_tcp) while $peak_tcp[$#peak_tcp] < ( $log_date - 60 );
    if ( $peak_tcp_min < @peak_tcp ) {
      $peak_tcp_min = @peak_tcp;
      $peak_tcp_min_time = $log_date - 60;
    }
    if ( $peak_tcp_sec < $peak_tcp_sec_pointer ) {
      $peak_tcp_sec = $peak_tcp_sec_pointer;
      $peak_tcp_sec_time = $log_date - 1;
    }';
  }
  $loop .= '
    $tcp_content{$log_content} = $tcp_content_size{$log_content} =
      $tcp_content_time{$log_content} = $tcp_hit_content{$log_content} =
      $tcp_hit_content_size{$log_content} = 0
      unless defined $tcp_content{$log_content};
    $tcp_content{$log_content}++;
    $tcp_content_time{$log_content} += $log_reqtime;
    $tcp_content_size{$log_content} += $log_size;'
    unless $opt_f eq 'squid-old' or $opt_f eq 'elff' or $opt_f eq 'nse';
  $loop .= '
    $tcp_urlext{$urlext} = $tcp_urlext_size{$urlext} =
      $tcp_urlext_time{$urlext} = $tcp_hit_urlext{$urlext} =
      $tcp_hit_urlext_size{$urlext} = $tcp_urlext_fresh{$urlext} =
      $tcp_urlext_stale{$urlext} = $tcp_urlext_refresh{$urlext} =
      $tcp_urlext_mod{$urlext} = $tcp_urlext_unmod{$urlext} = 0
      unless defined $tcp_urlext{$urlext};
    $tcp_urlext{$urlext}++;
    $tcp_urlext_time{$urlext} += $log_reqtime;
    $tcp_urlext_size{$urlext} += $log_size;' if ($opt_t);
  $loop .= '
    $tcp_urlext_fresh{$urlext}++ if
      grep { /^$log_hitfail$/ } @{$fresh_tags{$opt_f}};
    $tcp_urlext_stale{$urlext}++ if
      grep { /^$log_hitfail$/ } @{$stale_tags{$opt_f}};
    $tcp_urlext_refresh{$urlext}++ if
      grep { /^$log_hitfail$/ } @{$refresh_tags{$opt_f}};
    $tcp_urlext_mod{$urlext}++ if
      grep { /^$log_hitfail$/ } @{$mod_tags{$opt_f}};
    $tcp_urlext_unmod{$urlext}++ if
      grep { /^$log_hitfail$/ } @{$unmod_tags{$opt_f}};'
    if ($object_freshness_report and $opt_t);
  if ( $opt_f eq 'elff' ) {
    $loop .= '
    if ( $log_hitfail eq \'1\' or $log_hitfail =~ m#^(TCP_HIT|HIT_)#o ) {';
  } else {
    $loop .= '
    if ( $log_hitfail =~ m#^TCP_\w*HIT#o ) {';
  }
  $loop .= '
      $tcp_hit++;
      $tcp_hit_size += $log_size;
      $tcp_hit_time += $log_reqtime;';
  $loop .= '
      $tcp_hit_reqtime{$log_reqtime}++;
      $tcp_hit_reqtime_size{$log_reqtime} += $log_size;'
    if $opt_response_time;
  $loop .= '
      $tcp_hit_code{$log_code}++;
      $tcp_hit_code_size{$log_code} += $log_size;'
    if $opt_errorcode_distribution;
  $loop .= '
      $perf_tcp_hit{$perf_date}++;
      $perf_tcp_hit_size{$perf_date} += $log_size;
      $perf_tcp_hit_time{$perf_date} += $log_reqtime;' if ($opt_P);
  $loop .= '
      $tcp_hit{$log_hitfail} = $tcp_hit_size{$log_hitfail} =
	$tcp_hit_time{$log_hitfail} = 0
	unless defined $tcp_hit{$log_hitfail};
      $tcp_hit{$log_hitfail}++;
      $tcp_hit_size{$log_hitfail} += $log_size;
      $tcp_hit_time{$log_hitfail} += $log_reqtime;'
    if ($opt_s) and $opt_f ne 'elff';
  $loop .= '
      $tcp_hit_requester{$requester}++;
      $tcp_hit_requester_size{$requester} += $log_size;' if ($opt_r);
  $loop .= '
      $tcp_hit_requester_urlhost{$requester}{$urlhost}++;
      $tcp_hit_requester_urlhost_size{$requester}{$urlhost} += $log_size;'
    if ($opt_R);
  $loop .= '
      $tcp_hit_distribution{$distribution}++;
      $tcp_hit_distribution_size{$distribution} += $log_size;' if ($opt_D);
  $loop .= '
      $tcp_hit_urlhost{$urlhost}++;
      $tcp_hit_urlhost_size{$urlhost} += $log_size;
      $tcp_hit_urltld{$urltld}++;
      $tcp_hit_urltld_size{$urltld} += $log_size;' if ($opt_d);
  $loop .= '
      $tcp_hit_content{$log_content}++;
      $tcp_hit_content_time{$log_content} += $log_reqtime;
      $tcp_hit_content_size{$log_content} += $log_size;'
    unless $opt_f eq 'squid-old' or $opt_f eq 'elff' or $opt_f eq 'nse';
  $loop .= '
      $tcp_hit_urlext{$urlext}++;
      $tcp_hit_urlext_size{$urlext} += $log_size;
      $tcp_hit_urlprot{$urlprot}++;
      $tcp_hit_urlprot_size{$urlprot} += $log_size;' if ($opt_t);
  $loop .= '
    } elsif ( $log_hier_method =~ m#EMPTY|NONE|NULL|^\-$#o
	     or $log_hitfail =~ m#^ERR_#o ) {
      $tcp_miss_none++;
      $tcp_miss_none_size += $log_size;
      $tcp_miss_none_time += $log_reqtime;' if $opt_f ne 'elff';
  $loop .= '
      $tcp_miss_none{$log_hitfail} = $tcp_miss_none_size{$log_hitfail} =
	$tcp_miss_none_time{$log_hitfail} = 0
	unless defined $tcp_miss_none{$log_hitfail};
      $tcp_miss_none{$log_hitfail}++;
      $tcp_miss_none_size{$log_hitfail} += $log_size;
      $tcp_miss_none_time{$log_hitfail} += $log_reqtime;'
    if ($opt_s) and $opt_f ne 'elff';
  $loop .= '
    } else {
      $tcp_miss++;
      $tcp_miss_size += $log_size;
      $tcp_miss_time += $log_reqtime;';
  $loop .= '
      $perf_tcp_miss_size{$perf_date} += $log_size;
      $perf_tcp_miss_time{$perf_date} += $log_reqtime;' if ($opt_P);
  $loop .= '
      $tcp_miss{$log_hitfail} = $tcp_miss_size{$log_hitfail} =
	$tcp_miss_time{$log_hitfail} = 0
	unless defined $tcp_miss{$log_hitfail};
      $tcp_miss{$log_hitfail}++;
      $tcp_miss_size{$log_hitfail} += $log_size;
      $tcp_miss_time{$log_hitfail} += $log_reqtime;'
    if ($opt_s) and $opt_f ne 'elff';
  $loop .= '
      $tcp_miss_requester{$requester} = $tcp_miss_requester_size{$requester} =
	0
	unless defined $tcp_miss_requester{$requester};
      $tcp_miss_requester{$requester}++;
      $tcp_miss_requester_size{$requester} += $log_size;' if ($opt_r);
  $loop .= '
    }
    if ( $log_hier_method !~ m#EMPTY|NONE|NULL|^\-$#o ) {
      $hier++;
      $hier_size += $log_size;
      $hier_time += $log_reqtime;
      if ( $log_hier_method =~ m#DIRECT|SOURCE_FASTEST#o ) {
	$hier_direct++;
	$hier_direct_size += $log_size;
	$hier_direct_time += $log_reqtime;';
  $loop .= '
	$perf_hier_direct_size{$perf_date} += $log_size;
	$perf_hier_direct_time{$perf_date} += $log_reqtime;' if ($opt_P);
  $loop .= '
	$hier_direct{$log_hier_method} = $hier_direct_size{$log_hier_method} =
	  $hier_direct_time{$log_hier_method} = 0
	  unless defined $hier_direct{$log_hier_method};
	$hier_direct{$log_hier_method}++;
	$hier_direct_size{$log_hier_method} += $log_size;
	$hier_direct_time{$log_hier_method} += $log_reqtime;'
    if ($opt_s) and $opt_f ne 'elff';
  $loop .= '
      } elsif ( $log_hier_method =~
	       m#(CACHE_DIGEST|NEIGHBOR|PARENT|SIBLING)_\w*HIT|SIBLING#o ) {
	$hier_sibling++;
	$hier_sibling_size += $log_size;
	$hier_sibling_time += $log_reqtime;';
  $loop .= '
	$perf_hier_sibling_size{$perf_date} += $log_size;
	$perf_hier_sibling_time{$perf_date} += $log_reqtime;' if ($opt_P);
  $loop .= '
	$hier_sibling{$log_hier_method} =
	  $hier_sibling_size{$log_hier_method} =
	  $hier_sibling_time{$log_hier_method} = 0
	  unless defined $hier_sibling{$log_hier_method};
	$hier_sibling{$log_hier_method}++;
	$hier_sibling_size{$log_hier_method} += $log_size;
	$hier_sibling_time{$log_hier_method} += $log_reqtime;'
    if ($opt_s) and $opt_f ne 'elff';
  $loop .= '
	$hier_neighbor{$log_hier_host} = $hier_neighbor_size{$log_hier_host} =
	  $hier_neighbor_time{$log_hier_host} = 0
	  unless defined $hier_neighbor{$log_hier_host};
	$hier_neighbor{$log_hier_host}++;
	$hier_neighbor_size{$log_hier_host} += $log_size;
	$hier_neighbor_time{$log_hier_host} += $log_reqtime;';
  $loop .= '
	$hier_neighbor_status{$log_hier_host}{$log_hier_method} =
	  $hier_neighbor_status_size{$log_hier_host}{$log_hier_method} =
	  $hier_neighbor_status_time{$log_hier_host}{$log_hier_method} = 0
	  unless
	  defined $hier_neighbor_status{$log_hier_host}{$log_hier_method};
	$hier_neighbor_status{$log_hier_host}{$log_hier_method}++;
	$hier_neighbor_status_size{$log_hier_host}{$log_hier_method} +=
	  $log_size;
	$hier_neighbor_status_time{$log_hier_host}{$log_hier_method} +=
	  $log_reqtime;' if ($opt_s);
  $loop .= '
      } elsif ( $log_hier_method =~
	       m#(ANY|CLOSEST|DEFAULT|FIRST_UP|PASSTHROUGH|ROUNDROBIN|SINGLE)_PARENT|CARP|PARENT_MISS|PARENT|PROXY#o
	       ) {
	$hier_parent++;
	$hier_parent_size += $log_size;
	$hier_parent_time += $log_reqtime;';
  $loop .= '
	$perf_hier_parent_size{$perf_date} += $log_size;
	$perf_hier_parent_time{$perf_date} += $log_reqtime;' if ($opt_P);
  $loop .= '
	$hier_parent{$log_hier_method} = $hier_parent_size{$log_hier_method} =
	  $hier_parent_time{$log_hier_method} = 0
	  unless defined $hier_parent{$log_hier_method};
	$hier_parent{$log_hier_method}++;
	$hier_parent_size{$log_hier_method} += $log_size;
	$hier_parent_time{$log_hier_method} += $log_reqtime;'
    if ($opt_s) and $opt_f ne 'elff';
  $loop .= '
	$hier_neighbor{$log_hier_host} = $hier_neighbor_size{$log_hier_host} =
	  $hier_neighbor_time{$log_hier_host} = 0
	  unless defined $hier_neighbor{$log_hier_host};
	$hier_neighbor{$log_hier_host}++;
	$hier_neighbor_size{$log_hier_host} += $log_size;
	$hier_neighbor_time{$log_hier_host} += $log_reqtime;';
  $loop .= '
	$hier_neighbor_status{$log_hier_host}{$log_hier_method} =
	  $hier_neighbor_status_size{$log_hier_host}{$log_hier_method} =
	  $hier_neighbor_status_time{$log_hier_host}{$log_hier_method} = 0
	  unless
	  defined $hier_neighbor_status{$log_hier_host}{$log_hier_method};
	$hier_neighbor_status{$log_hier_host}{$log_hier_method}++;
	$hier_neighbor_status_size{$log_hier_host}{$log_hier_method} +=
	  $log_size;
	$hier_neighbor_status_time{$log_hier_host}{$log_hier_method} +=
	  $log_reqtime;' if ($opt_s);
  $loop .= '
      } else {
	chomp($log_hier_method);
	unless ( defined $errormsg ) {
	  print STDERR "
Please check the following error(s). If you\'re sure that the offending
line(s) are NOT corrupt and the error also occurs with the recent version of
Calamaris (see the README for pointers and known bugs) then report them.
Don\'t send me thousands of similar errors. use <Calamaris-bug\@Cord.de>.
Thank You.
" unless $errormsg;
	  $errormsg = 1;
	}
	warn( "
unknown log_hier_method: \"$log_hier_method\" found in line $counter of input:
 $line" );
      }
    }
  }
}';
  $time_run = time - $time_run;
  print STDERR "$loop\n" if $opt_L;
  eval $loop;
  die $@ if $@;
  $time_run = time - $time_run;
}

### Yea! File read. Now for something completely different ;-)

$outref{E} = 1;
$outref{0} = 1;
$outref{1} = ($opt_p) ? 1 : 0;
$outref{2} = ($opt_p and $opt_p eq 'new') ? 1 : 0;
$outref{3} = 1;
$outref{4} = 1;
$outref{5} = 1;
$outref{6} = 1;
$outref{7} = 1;
$outref{8} = ($opt_d) ? 1 : 0;
$outref{9} = ($opt_d) ? 1 : 0;
$outref{10} = ($opt_t) ? 1 : 0;
$outref{11} = ($opt_t) ? 1 : 0;
$outref{12} = ($opt_t) ? 1 : 0;
$outref{13} = ($opt_r) ? 1 : 0;
$outref{14} = ($opt_r) ? 1 : 0;
$outref{15} = ($opt_D) ? 1 : 0;
$outref{16} = ($opt_P) ? 1 : 0;
$outref{17} = ($opt_response_time) ? 1 : 0;
$outref{18} = ($opt_response_time) ? 1 : 0;
$outref{19} = ($opt_errorcode_distribution) ? 1 : 0;
$outref{20} = ($opt_errorcode_distribution) ? 1 : 0;

if ( $counter == 0 ) {
  print "\nno requests found\n";
  exit(0);
}
open( CACHE, ">$opt_o" ) or die ("$0: can't open $opt_o for writing: $!\n")
  if ($opt_o);
$report_index = 0;
writecache( $report_index, $time_begin, $time_end, $counter, $size, $time,
	   $invalid, $time_run, $udp, $udp_size, $udp_time, $udp_hit,
	   $udp_hit_size, $udp_hit_time, $udp_miss, $udp_miss_size,
	   $udp_miss_time, $tcp, $tcp_size, $tcp_time, $tcp_hit,
	   $tcp_hit_size, $tcp_hit_time, $tcp_miss, $tcp_miss_size,
	   $tcp_miss_time, $tcp_miss_none, $tcp_miss_none_size,
	   $tcp_miss_none_time, $hier, $hier_size, $hier_time, $hier_direct,
	   $hier_direct_size, $hier_direct_time, $hier_sibling,
	   $hier_sibling_size, $hier_sibling_time, $hier_parent,
	   $hier_parent_size, $hier_parent_time );
$date_start = convertdate($time_begin);
$date_stop = convertdate($time_end);
$generated = convertdate(time);

$out_head = '';
$out_head .= "MIME-Version: 1.0\nContent-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit\n" if ( $outtype_mail and $outtype_html );
$out_head .= "Subject: ${host_name}Proxy Report ($date_start - $date_stop)\n\n"
  if ($outtype_mail);
if ( $outtype_html or $outtype_htmlembed ) {
  $out_head .= "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"
	\"http://www.w3.org/TR/REC-html40/loose.dtd\">
<html>\n <head>
  <title>${host_name}Proxy Report ($date_start - $date_stop)</title>
  <meta name=\"generator\" content=\"Calamaris/" . '$Revision: 2.99.1.3 $' . "\">
" . ( $opt_M ? '  ' . $opt_M . "\n" : '' ) . " </head>
 <body bgcolor=\"#FFFFFF\">" if ($outtype_html);
  $out_head .= "\n  <table border=\"0\">"
    if ($outtype_html or $outtype_htmlembed);
  $out_head .= "\n   <tr>\n    <td colspan=\"2\">$opt_l</td>\n   </tr>
   <tr>" if ($opt_l);
  $out_head .= "\n   <tr>\n    <td colspan=\"2\" height=\"35\">
     <h1 class=\"headline\"><a name=\"top\">${host_name}Proxy Report</a></h1>
    </td>\n   </tr>" if ($outtype_html);
  $out_head .= "\n   <tr>\n    <td>\n     <h4>Report period:</h4>\n    </td>
    <td>\n     <h4>$date_start - $date_stop</h4>\n    </td>\n   </tr>\n   <tr>
    <td>\n     <h4>Generated at:</h4>\n    </td>\n    <td>
     <h4>$generated</h4>\n    </td>\n   </tr>\n  </table>\n  <hr>"
    if ($outtype_html or $outtype_htmlembed);
} else {
  $out_head .= "\n${host_name}Proxy-Report\n
Report period: $date_start - $date_stop\nGenerated at:  $generated\n";
}

if ( ( $invalid / $counter ) > .05 and $counter > 1000 ) {
  @format = (60);
  outstart('E');
  outline( 'E', '' );
  outline( 'E', 'More than 5% discarded logfile-lines.' );
  outline( 'E', 'Please check your logfile with calamaris -v' )
    unless ($opt_v);
  outstop('E');
}

if ( defined($peak_warn) ) {
  @format = (60);
  outstart('E');
  outline( 'E', '' );
  outline( 'E', "$peak_warn" );
  outline( 'E', 'Please read the README on unsorted input' );
  outline( 'E', 'To find out which line caused this, try calamaris -v' )
    unless ($opt_v);
  outstop('E');
}

if ( defined($cache_warn) ) {
  @format = (60);
  outstart('E');
  outline( 'E', '' );
  outline( 'E', 'with Calamaris V3.x the Cache-File-Format is completely' );
  outline( 'E', 'changed.' );
  outline( 'E', '' );
  outline( 'E', 'To re-use your old Cachefiles you\'ll first have to' );
  outline( 'E', 'convert them with' );
  outline( 'E', 'calamaris-cache-convert old.cache new.cache' );
  outstop('E');
}

if ( $opt_I and $opt_i ) {
  @format = (60);
  outstart('E');
  outline( 'E', '' );
  outline( 'E', 'You have run Calamaris with the -I (Interval) and the -i' );
  outline( 'E', '(input cache) option. This is normally not useful,' );
  outline( 'E', 'because the time-interval cannot be applied to' );
  outline( 'E', 'cache-files.' );
  outstop('E');
}

if ($opt_ipfilter_exclude) {
  @format = (60);
  outstart('E');
  outline( 'E', '' );
  outline( 'E', 'The following IP adresses have been ignored:' );
  foreach ( split ( ':', $opt_ipfilter_exclude ) ) {
    outline( 'E', "    $_" );
  }
  outstop('E');
}

if ($opt_ipfilter_include) {
  @format = (60);
  outstart('E');
  outline( 'E', '' );
  outline( 'E', 'Only the following IP adresses have been recognized:' );
  foreach ( split ( ':', $opt_ipfilter_include ) ) {
    outline( 'E', "    $_" );
  }
  outstop('E');
}

# Summary, index = 0
@format = ( 57, 14, 6 );
outstart($report_index);
outheader( $report_index, 'Program statistics', '', '');
outseperator($report_index);
outline( $report_index, 'lines parsed:', 'lines', $counter );
outline( $report_index, 'invalid lines:', 'lines', $invalid );
outline( $report_index, 'skipped lines:', 'lines', $skipped )
  if ($opt_I or $opt_ipfilter_exclude or $opt_ipfilter_include);
outline( $report_index, 'unique hosts/users:', 'hosts',
	scalar keys %tcp_requester ) if $opt_r;
outline( $report_index, 'parse time:', 'sec', $time_run );
outline( $report_index, 'parse speed:', 'lines/sec', $time_run
	  ? ( $counter + $invalid + $skipped ) / $time_run
	  : ( $counter + $invalid + $skipped ) );
outseperator($report_index);
outheader( $report_index, 'Proxy statistics', '', '');
outseperator($report_index);
outline( $report_index, 'Total amount:', 'requests', $tcp + $udp);
outline( $report_index, 'Total amount cached:', 'requests',
	$tcp_hit + $udp_hit );
@format = ( 57, 14, '%' );
outline( $report_index, 'Request hit rate:', '%',
	($tcp + $udp) ? 100 * ($tcp_hit + $udp_hit) / ($tcp + $udp) : 0 );
@format = ( 57, 14, 6 );
outline( $report_index, 'Total Bandwidth:', 'Byte',
	kilomegagigatera( $tcp_size + $udp_size, $format[2] ) );
outline( $report_index, 'Bandwidth savings:', 'Byte',
	kilomegagigatera( $tcp_hit_size + $udp_hit_size, $format[2] ) );
@format = ( 57, 14, '%' );
$peak_all_hour_size_time =
  ( sort { $peak_all_hour_size{$b} <=> $peak_all_hour_size{$a} }
   keys(%peak_all_hour_size) )[0]
    or $peak_all_hour_size_time = $peak_all_hour_size{0} = 0;
outline( $report_index, 'Max. Bandwidth usage:', 'MBit/sec',
	$peak_all_hour_size{$peak_all_hour_size_time} * 8 /
	  ( 1024**2 * 3600 ) ) if $opt_p;
outline( $report_index, 'Bandwidth savings in Percent (Byte hit rate):', '%',
	($tcp_size + $udp_size)
	? 100 * ($tcp_hit_size + $udp_hit_size) / ($tcp_size + $udp_size)
	: 0 );
outline( $report_index, 'Proxy efficiency (HIT [kB/sec] / DIRECT [kB/sec]):',
	'factor', ($tcp_hit_time and ($tcp_miss_size + $tcp_miss_none_size))
	? $tcp_hit_size/$tcp_hit_time *
	  ($tcp_miss_none_time + $tcp_miss_time) /
	  ($tcp_miss_size + $tcp_miss_none_size) : 0 );
outline( $report_index, 'Average speed increase:', '%',
	($tcp_miss_size + $tcp_miss_none_size)
	? 100 * ( -1 + $tcp_size/$tcp_time *
		 ($tcp_miss_none_time + $tcp_miss_time) /
		 ($tcp_miss_size + $tcp_miss_none_size)) : 0 );
$max_value[$report_index] = "-</td>\n    <td>-</td>\n    <td>-";
outseperator($report_index);
outstop($report_index);

# Incoming request peak per protocol, index = 1;
if ( not defined $opt_p ) {
} elsif ( $opt_p eq 'old' ) {
  @format = ( 3, 4, 18, 5, 18, 7, 18 );
  $report_index = 1;
  writecache( $report_index, $peak_udp_sec, $peak_udp_sec_time, $peak_udp_min,
	     $peak_udp_min_time, $peak_udp_hour, $peak_udp_hour_time,
	     $peak_tcp_sec, $peak_tcp_sec_time, $peak_tcp_min,
	     $peak_tcp_min_time, $peak_tcp_hour, $peak_tcp_hour_time,
	     $peak_all_sec, $peak_all_sec_time, $peak_all_min,
	     $peak_all_min_time, $peak_all_hour, $peak_all_hour_time );
  outstart($report_index);
  outheader( $report_index, 'prt', ' sec', 'peak begins at', ' min',
	    'peak begins at', ' hour', 'peak begins at' );
  outseperator($report_index);
  outline( $report_index, 'UDP', $peak_udp_sec,
	  convertdate($peak_udp_sec_time), $peak_udp_min,
	  convertdate($peak_udp_min_time), $peak_udp_hour,
	  convertdate($peak_udp_hour_time) );
  outline( $report_index, 'TCP', $peak_tcp_sec,
	  convertdate($peak_tcp_sec_time), $peak_tcp_min,
	  convertdate($peak_tcp_min_time), $peak_tcp_hour,
	  convertdate($peak_tcp_hour_time) );
  outseperator($report_index);
  outline( $report_index, 'ALL', $peak_all_sec,
	  convertdate($peak_all_sec_time), $peak_all_min,
	  convertdate($peak_all_min_time), $peak_all_hour,
	  convertdate($peak_all_hour_time) );
  $max_value[$report_index] = "-</td>\n    <td>-</td>\n    <td>-";
  outstop($report_index);
} elsif ( $opt_p eq 'new' ) {
  @format = ( 3, 4, 18, 5, 18, 7, 18 );
  $report_index = 2;
  $peak_udp_hour_time = ( sort { $peak_udp_hour{$b} <=> $peak_udp_hour{$a} }
			 keys(%peak_udp_hour) )[0]
    or $peak_udp_hour_time = $peak_udp_hour{0} = 0;
  $peak_udp_hour_size_time =
    ( sort { $peak_udp_hour_size{$b} <=> $peak_udp_hour_size{$a} }
     keys(%peak_udp_hour_size) )[0]
    or $peak_udp_hour_size_time = $peak_udp_hour_size{0} = 0;
  $peak_tcp_hour_time = ( sort { $peak_tcp_hour{$b} <=> $peak_tcp_hour{$a} }
			 keys(%peak_tcp_hour) )[0]
    or $peak_tcp_hour_time = $peak_tcp_hour{0} = 0;
  $peak_tcp_hour_size_time =
    ( sort { $peak_tcp_hour_size{$b} <=> $peak_tcp_hour_size{$a} }
     keys(%peak_tcp_hour_size) )[0]
    or $peak_tcp_hour_size_time = $peak_tcp_hour_size{0} = 0;
  $peak_all_hour_time = ( sort { $peak_all_hour{$b} <=> $peak_all_hour{$a} }
			 keys(%peak_all_hour) )[0]
    or $peak_all_hour_time = $peak_all_hour{0} = 0;
  $peak_all_hour_size_time =
    ( sort { $peak_all_hour_size{$b} <=> $peak_all_hour_size{$a} }
     keys(%peak_all_hour_size) )[0]
    or $peak_all_hour_size_time = $peak_all_hour_size{0} = 0;
  writecache( $report_index, $peak_udp_sec, $peak_udp_sec_time, $peak_udp_min,
	     $peak_udp_min_time, $peak_udp_hour{$peak_udp_hour_time},
	     $peak_udp_hour_time,
	     $peak_udp_hour_size{$peak_udp_hour_size_time},
	     $peak_udp_hour_size_time, $peak_tcp_sec, $peak_tcp_sec_time,
	     $peak_tcp_min, $peak_tcp_min_time,
	     $peak_tcp_hour{$peak_tcp_hour_time}, $peak_tcp_hour_time,
	     $peak_tcp_hour_size{$peak_tcp_hour_size_time},
	     $peak_tcp_hour_size_time, $peak_all_sec, $peak_all_sec_time,
	     $peak_all_min, $peak_all_min_time,
	     $peak_all_hour{$peak_all_hour_time}, $peak_all_hour_time,
	     $peak_all_hour_size{$peak_all_hour_size_time},
	     $peak_all_hour_size_time );
  outstart($report_index);
  outheader( $report_index, 'prt', ' sec', 'peak begins at', ' min',
	    'peak begins at', ' hour', 'peak begins at' );
  outseperator($report_index);
  outline( $report_index, 'UDP', $peak_udp_sec,
	  convertdate($peak_udp_sec_time), $peak_udp_min,
	  convertdate($peak_udp_min_time), $peak_udp_hour{$peak_udp_hour_time},
	  convertdate($peak_udp_hour_time) );
  outline( $report_index, 'TCP', $peak_tcp_sec,
	  convertdate($peak_tcp_sec_time), $peak_tcp_min,
	  convertdate($peak_tcp_min_time), $peak_tcp_hour{$peak_tcp_hour_time},
	  convertdate($peak_tcp_hour_time) );
  outseperator($report_index);
  outline( $report_index, 'ALL', $peak_all_sec,
	  convertdate($peak_all_sec_time), $peak_all_min,
	  convertdate($peak_all_min_time), $peak_all_hour{$peak_all_hour_time},
	  convertdate($peak_all_hour_time) );
  $max_value[$report_index] = "-</td>\n    <td>-</td>\n    <td>-";
  outstop($report_index);

  # Incoming transfer volume per protocol, index = 2
  @format = ( 5, 8, 18 );
  $report_index = 2;
  outstart($report_index);
  outheader( $report_index, 'proto', ' kB/hour', 'peak begins at' );
  outseperator($report_index);
  outline( $report_index, 'UDP',
	  $peak_udp_hour_size{$peak_udp_hour_size_time} / 1024,
	  convertdate($peak_udp_hour_size_time) );
  outline( $report_index, 'TCP',
	  $peak_tcp_hour_size{$peak_tcp_hour_size_time} / 1024,
	  convertdate($peak_tcp_hour_size_time) );
  outseperator($report_index);
  outline( $report_index, 'ALL',
	  $peak_all_hour_size{$peak_all_hour_size_time} / 1024,
	  convertdate($peak_all_hour_size_time) );
  $max_value[$report_index] = "-</td>\n    <td>-</td>\n    <td>-";
  outstop($report_index);
}

# Incoming requests by method, index = 3
@format = ( 30, 9, '%', 'spr', 8, '%', 'kbps' );
$report_index = 3;
@format = @{$formats[$report_index]} if (ref($formats[$report_index]));
outstart($report_index);
if ( $counter == 0 ) {
  outline( $report_index, 'no matching requests' );
} else {
  my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
  outimg($report_index) if ($outtype_graph);
  outheader( $report_index, 'method', ' request', '% ', 'auto',
	    $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ', 'auto' );
  outseperator($report_index);
  foreach $method ( sort { ${"method$sortorder"}{$b} <=>
			  ${"method$sortorder"}{$a} } keys(%method) ) {
    push @xaxis, $method;
    push @yaxis1, $method{$method};
    push @yaxis2, $method_size{$method};
    writecache( $report_index, $method, $method{$method},
	       $method_size{$method}, $method_time{$method} );
    outline( $report_index, $method, $method{$method},
	    100 * $method{$method} / $counter,
	    $method_time{$method} / ( 1000 * $method{$method} ),
	    kilomegagigatera( $method_size{$method}, $format[4] ),
	    $size ? 100 * $method_size{$method} / $size : 0,
	    $method_size{$method} / ( 1.024 * $method_time{$method} ) );
  }
  outseperator($report_index);
  outline( $report_index, 'Sum', $counter, 100, $time / ( $counter * 1000 ),
	  kilomegagigatera( $size, $format[4] ),
	  100, $size / ( 1.024 * $time ) );
  outgraph($report_index, \@graph_legend, $x_scale, \@xaxis, \@yaxis1,
	   \@yaxis2) if ($outtype_graph and $xaxis[0]);
  $max_value[$report_index] = "most requested method</td>\n    <td>"
    . htmlescape($xaxis[0]) . "</td>\n    <td>";
  $max_value[$report_index] .= ($opt_O)
    ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
}
outstop($report_index);

# Incoming UDP-requests by status, index = 4
@format = ( 30, 9, '%', 'mspr', 8, '%', 'kbps' );
$report_index = 4;
@format = @{$formats[$report_index]} if (ref($formats[$report_index]));
outstart($report_index);
if ( $udp == 0 ) {
  outline( $report_index, 'no matching requests' );
} else {
  my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
  outimg($report_index) if ($outtype_graph);
  outheader( $report_index, 'status', ' request', '% ', 'auto',
	    $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ', 'auto' );
  outseperator($report_index);
  if ( $udp_hit == 0 ) {
    outline( $report_index, 'HIT', 0, 0, 0, 0, 0, 0 );
    push @xaxis, 'HIT';
    push @yaxis1, 0;
    push @yaxis2, 0;
  } else {
    push @xaxis, 'HIT';
    push @yaxis1, $udp_hit;
    push @yaxis2, $udp_hit_size;
    outline( $report_index, 'HIT', $udp_hit, 100 * $udp_hit / $udp,
	    $udp_hit_time / (1000 * $udp_hit),
	    kilomegagigatera( $udp_hit_size, $format[4] ),
	    $udp_size ? 100 * $udp_hit_size / $udp_size : 0,
	    $udp_hit_size / ( 1.024 * $udp_hit_time ) );
    foreach $hitfail ( sort { ${"udp_hit$sortorder"}{$b} <=>
			    ${"udp_hit$sortorder"}{$a} } keys(%udp_hit) ) {
      writecache( "$report_index.1", $hitfail, $udp_hit{$hitfail},
		 $udp_hit_size{$hitfail}, $udp_hit_time{$hitfail} );
      outline( $report_index, ' ' . $hitfail, $udp_hit{$hitfail},
	      100 * $udp_hit{$hitfail} / $udp,
	      $udp_hit_time{$hitfail} / (1000 * $udp_hit{$hitfail}),
	      kilomegagigatera( $udp_hit_size{$hitfail}, $format[4] ),
	      $udp_size ? 100 * $udp_hit_size{$hitfail} / $udp_size : 0,
	      $udp_hit_size{$hitfail} / ( 1.024 * $udp_hit_time{$hitfail} ) );
    }
  }
  if ( $udp_miss == 0 ) {
    outline( $report_index, 'MISS', 0, 0, 0, 0, 0, 0 );
    push @xaxis, 'MISS';
    push @yaxis1, $udp_miss;
    push @yaxis2, $udp_miss_size;
  } else {
    push @xaxis, 'MISS';
    push @yaxis1, $udp_miss;
    push @yaxis2, $udp_miss_size;
    outline( $report_index, 'MISS', $udp_miss, 100 * $udp_miss / $udp,
	    $udp_miss_time / (1000 * $udp_miss),
	    kilomegagigatera( $udp_miss_size, $format[4] ),
	    $udp_size ? 100 * $udp_miss_size / $udp_size : 0,
	    $udp_miss_size / ( 1.024 * $udp_miss_time ) );
    foreach $hitfail ( sort { ${"udp_miss$sortorder"}{$b} <=>
			    ${"udp_miss$sortorder"}{$a} } keys(%udp_miss) ) {
      writecache( "$report_index.2", $hitfail, $udp_miss{$hitfail},
		 $udp_miss_size{$hitfail}, $udp_miss_time{$hitfail} );
      outline( $report_index, ' ' . $hitfail, $udp_miss{$hitfail},
	      100 * $udp_miss{$hitfail} / $udp,
	      $udp_miss_time{$hitfail} / (1000 * $udp_miss{$hitfail}),
	      kilomegagigatera( $udp_miss_size{$hitfail}, $format[4] ),
	      $udp_size ? 100 * $udp_miss_size{$hitfail} / $udp_size : 0,
	      $udp_miss_size{$hitfail} /
		( 1.024 * $udp_miss_time{$hitfail} ) );
    }
  }
  outseperator($report_index);
  outline( $report_index, 'Sum', $udp, 100, $udp_time / (1000 * $udp),
	  kilomegagigatera( $udp_size, $format[4] ),
	  100, $udp_size / ( 1.024 * $udp_time ) );
  outgraph( $report_index, \@graph_legend, $x_scale, \@xaxis, \@yaxis1,
	   \@yaxis2) if ($outtype_graph and $xaxis[0]);
  $max_value[$report_index] = "Hits</td>\n    <td>"
    . sprintf("%.2f", 100 * $udp_hit / $udp) . "%</td>\n    <td>";
  $max_value[$report_index] .= ($opt_O)
    ? kilomegagigatera($yaxis2[1], 6) . " Byte" : $yaxis1[1]. " Requests";
}
outstop($report_index);

# Incoming TCP-requests by status, index = 5
@format = ( 30, 9, '%', 'spr', 8, '%', 'kbps' );
$report_index = 5;
@format = @{$formats[$report_index]} if (ref($formats[$report_index]));
outstart($report_index);
if ( $tcp_hit + $tcp_miss + $tcp_miss_none == 0 ) {
  outline( $report_index, 'no matching requests' );
} else {
  my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
  outimg($report_index) if ($outtype_graph);
  outheader( $report_index, 'status', ' request', '% ', 'auto',
	    $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ', 'auto' );
  outseperator($report_index);
  if ( $tcp_hit == 0 ) {
    outline( $report_index, 'HIT', 0, 0, 0, 0, 0, 0 );
    push @xaxis, 'HIT';
    push @yaxis1, 0;
    push @yaxis2, 0;
  } else {
    push @xaxis, 'HIT';
    push @yaxis1, $tcp_hit;
    push @yaxis2, $tcp_hit_size;
    outline( $report_index, 'HIT', $tcp_hit, 100 * $tcp_hit / $tcp,
	    $tcp_hit_time / ( 1000 * $tcp_hit ),
	    kilomegagigatera( $tcp_hit_size, $format[4] ),
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    $tcp_hit_size / ( 1.024 * $tcp_hit_time ) );
    foreach $hitfail ( sort { ${"tcp_hit$sortorder"}{$b} <=>
			      ${"tcp_hit$sortorder"}{$a} } keys(%tcp_hit) ) {
      writecache( "$report_index.1", $hitfail, $tcp_hit{$hitfail},
		 $tcp_hit_size{$hitfail}, $tcp_hit_time{$hitfail} );
      outline( $report_index, ' ' . $hitfail, $tcp_hit{$hitfail},
	      100 * $tcp_hit{$hitfail} / $tcp,
	      $tcp_hit_time{$hitfail} / ( 1000 * $tcp_hit{$hitfail} ),
	      kilomegagigatera( $tcp_hit_size{$hitfail}, $format[4] ),
	      $tcp_size ? 100 * $tcp_hit_size{$hitfail} / $tcp_size : 0,
	      $tcp_hit_size{$hitfail} / ( 1.024 * $tcp_hit_time{$hitfail} ) );
    }
  }
  if ( $tcp_miss == 0 ) {
    outline( $report_index, 'MISS', 0, 0, 0, 0, 0, 0 );
    push @xaxis, 'MISS';
    push @yaxis1, 0;
    push @yaxis2, 0;
  } else {
    push @xaxis, 'MISS';
    push @yaxis1, $tcp_miss;
    push @yaxis2, $tcp_miss_size;
    outline( $report_index, 'MISS', $tcp_miss, 100 * $tcp_miss / $tcp,
	    $tcp_miss_time / ( 1000 * $tcp_miss ),
	    kilomegagigatera( $tcp_miss_size, $format[4] ),
	    $tcp_size ? 100 * $tcp_miss_size / $tcp_size : 0,
	    $tcp_miss_size / ( 1.024 * $tcp_miss_time ) );
    foreach $hitfail ( sort { ${"tcp_miss$sortorder"}{$b} <=>
			      ${"tcp_miss$sortorder"}{$a} }
		      keys(%tcp_miss) ) {
      writecache( "$report_index.2", $hitfail, $tcp_miss{$hitfail},
		 $tcp_miss_size{$hitfail}, $tcp_miss_time{$hitfail} );
      outline( $report_index, ' ' . $hitfail, $tcp_miss{$hitfail},
	      100 * $tcp_miss{$hitfail} / $tcp,
	      $tcp_miss_time{$hitfail} / ( 1000 * $tcp_miss{$hitfail} ),
	      kilomegagigatera( $tcp_miss_size{$hitfail}, $format[4] ),
	      $tcp_size ? 100 * $tcp_miss_size{$hitfail} / $tcp_size : 0,
	      $tcp_miss_size{$hitfail} /
		( 1.024 * $tcp_miss_time{$hitfail} ) );
    }
  }
  if ( $tcp_miss_none == 0 ) {
    outline( $report_index, 'ERROR', 0, 0, 0, 0, 0, 0 );
    push @xaxis, 'ERROR';
    push @yaxis1, 0;
    push @yaxis2, 0;
  } else {
    push @xaxis, 'ERROR';
    push @yaxis1, $tcp_miss_none;
    push @yaxis2, $tcp_miss_none_size;
    outline( $report_index, 'ERROR', $tcp_miss_none,
	    100 * $tcp_miss_none / $tcp,
	    $tcp_miss_none_time / ( 1000 * $tcp_miss_none ),
	    kilomegagigatera( $tcp_miss_none_size, $format[4] ),
	    $tcp_size ? 100 * $tcp_miss_none_size / $tcp_size : 0,
	    $tcp_miss_none_size / ( 1.024 * $tcp_miss_none_time ) );
    foreach $hitfail ( sort {
      ${"tcp_miss_none$sortorder"}{$b} <=> ${"tcp_miss_none$sortorder"}{$a}
	} keys(%tcp_miss_none) ) {
      writecache( "$report_index.3", $hitfail, $tcp_miss_none{$hitfail},
		 $tcp_miss_none_size{$hitfail},
		 $tcp_miss_none_time{$hitfail} );
      outline( $report_index, ' ' .  $hitfail, $tcp_miss_none{$hitfail},
	      100 * $tcp_miss_none{$hitfail} / $tcp,
	      $tcp_miss_none_time{$hitfail} /
		( 1000 * $tcp_miss_none{$hitfail} ),
	      kilomegagigatera( $tcp_miss_none_size{$hitfail}, $format[4] ),
	      $tcp_size ? 100 * $tcp_miss_none_size{$hitfail} / $tcp_size : 0,
	      $tcp_miss_none_size{$hitfail} /
		( 1.024 * $tcp_miss_none_time{$hitfail} ) );
    }
  }
  outseperator($report_index);
  outline( $report_index, 'Sum', $tcp, 100, $tcp_time / ( 1000 * $tcp ),
	  kilomegagigatera( $tcp_size, $format[4] ),
	  100, $tcp_size / ( 1.024 * $tcp_time ) );
  outgraph( $report_index, \@graph_legend, $x_scale, \@xaxis, \@yaxis1,
	   \@yaxis2) if ($outtype_graph and $xaxis[0]);
  $max_value[$report_index] = "Hits</td>\n    <td>"
    . sprintf("%.2f", 100 * $tcp_hit / $tcp) . "%</td>\n    <td>";
  $max_value[$report_index] .= ($opt_O)
    ? kilomegagigatera($yaxis2[1], 6) . " Byte" : $yaxis1[1]. " Requests";
}
outstop($report_index);

# Outgoing requests by status, index = 6
@format = ( 30, 9, '%', 'spr', 8, '%', 'kbps' );
$report_index = 6;
@format = @{$formats[$report_index]} if (ref($formats[$report_index]));
outstart($report_index);
if ( $hier == 0 ) {
  outline( $report_index, 'no matching requests' );
} else {
  my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
  outimg($report_index) if ($outtype_graph);
  outheader( $report_index, 'status', ' request', '% ', 'auto',
	    $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ', 'auto' );
  outseperator($report_index);
  if ( $hier_direct == 0 ) {
    outline( $report_index, 'DIRECT', 0, 0, 0, 0, 0, 0 );
    push @xaxis, 'DIRECT';
    push @yaxis1, 0;
    push @yaxis2, 0;
  } else {
    push @xaxis, 'DIRECT Fetch from Source';
    push @yaxis1, $hier_direct;
    push @yaxis2, $hier_direct_size;
    outline( $report_index, 'DIRECT Fetch from Source', $hier_direct,
	    100 * $hier_direct / $hier,
	    $hier_direct_time / ( 1000 * $hier_direct ),
	    kilomegagigatera( $hier_direct_size, $format[4] ),
	    $hier_size ? 100 * $hier_direct_size / $hier_size : 0,
	    $hier_direct_size / ( 1.024 * $hier_direct_time ) );
    foreach $hitfail ( sort {
      ${"hier_direct$sortorder"}{$b} <=> ${"hier_direct$sortorder"}{$a}
	} keys(%hier_direct) ) {
      writecache( "$report_index.1", $hitfail, $hier_direct{$hitfail},
		 $hier_direct_size{$hitfail}, $hier_direct_time{$hitfail} );
      outline( $report_index, ' ' . $hitfail, $hier_direct{$hitfail},
	      100 * $hier_direct{$hitfail} / $hier,
	      $hier_direct_time{$hitfail} / ( 1000 * $hier_direct{$hitfail} ),
	      kilomegagigatera( $hier_direct_size{$hitfail}, $format[4] ),
	      $hier_size ? 100 * $hier_direct_size{$hitfail} / $hier_size : 0,
	      $hier_direct_size{$hitfail} /
		( 1.024 * $hier_direct_time{$hitfail} ) );
    }
  }
  if ( $hier_sibling == 0 ) {
    outline( $report_index, 'SIBLING', 0, 0, 0, 0, 0, 0 );
    push @xaxis, 'SIBLING';
    push @yaxis1, 0;
    push @yaxis2, 0;
  } else {
    push @xaxis, 'HIT on Sibling or Parent Cache';
    push @yaxis1, $hier_sibling;
    push @yaxis2, $hier_sibling_size;
    outline( $report_index, 'HIT on Sibling or Parent Cache', $hier_sibling,
	    100 * $hier_sibling / $hier,
	    $hier_sibling_time / ( 1000 * $hier_sibling ),
	    kilomegagigatera( $hier_sibling_size, $format[4] ),
	    $hier_size ? 100 * $hier_sibling_size / $hier_size : 0,
	    $hier_sibling_size / ( 1.024 * $hier_sibling_time ) );
    foreach $hitfail ( sort {
      ${"hier_sibling$sortorder"}{$b} <=> ${"hier_sibling$sortorder"}{$a}
	} keys(%hier_sibling) ) {
      writecache( "$report_index.2", $hitfail, $hier_sibling{$hitfail},
		 $hier_sibling_size{$hitfail}, $hier_sibling_time{$hitfail} );
      outline( $report_index, ' ' . $hitfail, $hier_sibling{$hitfail},
	      100 * $hier_sibling{$hitfail} / $hier,
	      $hier_sibling_time{$hitfail} /
		( 1000 * $hier_sibling{$hitfail} ),
	      kilomegagigatera( $hier_sibling_size{$hitfail}, $format[4] ),
	      $hier_size ? 100 * $hier_sibling_size{$hitfail} / $hier_size : 0,
	      $hier_sibling_size{$hitfail} /
		( 1.024 * $hier_sibling_time{$hitfail} ) );
    }
  }
  if ( $hier_parent == 0 ) {
    outline( $report_index, 'PARENT', 0, 0, 0, 0, 0, 0 );
    push @xaxis, 'PARENT';
    push @yaxis1, 0;
    push @yaxis2, 0;
  } else {
    push @xaxis, 'FETCH from Parent Cache';
    push @yaxis1, $hier_parent;
    push @yaxis2, $hier_parent_size;
    outline( $report_index, 'FETCH from Parent Cache', $hier_parent,
	    100 * $hier_parent / $hier,
	    $hier_parent_time / ( 1000 * $hier_parent ),
	    kilomegagigatera( $hier_parent_size, $format[4] ),
	    $hier_size ? 100 * $hier_parent_size / $hier_size : 0,
	    $hier_parent_size / ( 1.024 * $hier_parent_time ) );
    foreach $hitfail ( sort {
      ${"hier_parent$sortorder"}{$b} <=> ${"hier_parent$sortorder"}{$a}
	} keys(%hier_parent) ) {
      writecache( "$report_index.3", $hitfail, $hier_parent{$hitfail},
		 $hier_parent_size{$hitfail}, $hier_parent_time{$hitfail} );
      outline( $report_index, ' ' . $hitfail, $hier_parent{$hitfail},
	      100 * $hier_parent{$hitfail} / $hier,
	      $hier_parent_time{$hitfail} / ( 1000 * $hier_parent{$hitfail} ),
	      kilomegagigatera( $hier_parent_size{$hitfail}, $format[4] ),
	      $hier_size ? 100 * $hier_parent_size{$hitfail} / $hier_size : 0,
	      $hier_parent_size{$hitfail} /
		( 1.024 * $hier_parent_time{$hitfail} ) );
    }
  }
  outseperator($report_index);
  outline( $report_index, 'Sum', $hier, 100, $hier_time / ( 1000 * $hier ),
	  kilomegagigatera( $hier_size, $format[4] ),
	  100, $hier_size / ( 1.024 * $hier_time ) );
  outgraph($report_index, \@graph_legend, $x_scale, \@xaxis, \@yaxis1,
	   \@yaxis2) if ($outtype_graph and $xaxis[0]);
  $max_value[$report_index] = "most outgoing request to</td>\n    <td>"
    . htmlescape($xaxis[0]) . "</td>\n    <td>";
  $max_value[$report_index] .= ($opt_O)
    ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
}
outstop($report_index);

# Outgoing requests by destination, index = 7
@format = ( 30, 9, '%', 'spr', 8, '%', 'kbps' );
$report_index = 7;
@format = @{$formats[$report_index]} if (ref($formats[$report_index]));
outstart($report_index);
if ( $hier == 0 ) {
  outline( $report_index, 'no matching requests' );
} else {
  my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
  outimg($report_index) if ($outtype_graph);
  outheader( $report_index, 'neighbor type', ' request', '% ', 'auto',
	    $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ', 'auto' );
  outseperator($report_index);
  outline( $report_index, 'DIRECT', $hier_direct, 100 * $hier_direct / $hier,
	  $hier_direct_time / ( 1000 * $hier_direct ),
	  kilomegagigatera( $hier_direct_size, $format[4] ),
	  $hier_size ? 100 * $hier_direct_size / $hier_size : 0,
	  $hier_direct_size / ( 1.024 * $hier_direct_time ) )
    unless $hier_direct == 0;
  push @xaxis, 'DIRECT' unless $hier_direct == 0;
  push @yaxis1, $hier_direct unless $hier_direct == 0;
  push @yaxis2, $hier_direct_size unless $hier_direct == 0;

  foreach $neighbor ( sort {
    ${"hier_neighbor$sortorder"}{$b} <=> ${"hier_neighbor$sortorder"}{$a}
    } keys(%hier_neighbor) ) {
    push @xaxis, $neighbor;
    push @yaxis1, $hier_neighbor{$neighbor};
    push @yaxis2, $hier_neighbor_size{$neighbor};
    writecache( "$report_index.1", $neighbor, $hier_neighbor{$neighbor},
	       $hier_neighbor_size{$neighbor},
	       $hier_neighbor_time{$neighbor} );
    outline( $report_index, $neighbor, $hier_neighbor{$neighbor},
	    100 * $hier_neighbor{$neighbor} / $hier,
	    $hier_neighbor_time{$neighbor} / ( 1000 * $hier ),
	    kilomegagigatera( $hier_neighbor_size{$neighbor}, $format[4] ),
	    $hier_size ? 100 * $hier_neighbor_size{$neighbor} / $hier_size : 0,
	    $hier_neighbor_size{$neighbor} /
	      ( 1.024 * $hier_neighbor_time{$neighbor} ) );
    foreach $status ( sort {
      ${"hier_neighbor_status$sortorder"}{$neighbor}{$b} <=>
	${"hier_neighbor_status$sortorder"}{$neighbor}{$a}
	  } keys(%{$hier_neighbor_status{$neighbor} } ) ) {
      writecache( "$report_index.2", $neighbor, $status,
		 $hier_neighbor_status{$neighbor}{$status},
		 $hier_neighbor_status_size{$neighbor}{$status},
		 $hier_neighbor_status_time{$neighbor}{$status} );
      outline( $report_index, ' ' . $status,
	      $hier_neighbor_status{$neighbor}{$status},
	      100 * $hier_neighbor_status{$neighbor}{$status} / $hier,
	      $hier_neighbor_status_time{$neighbor}{$status} /
		( 1000 * $hier_neighbor_status{$neighbor}{$status} ),
	      kilomegagigatera( $hier_neighbor_status_size{$neighbor}{$status},
			       $format[4] ),
	      $hier_size
		? 100 * $hier_neighbor_status_size{$neighbor}{$status} /
		  $hier_size : 0,
	      $hier_neighbor_status_size{$neighbor}{$status} /
		( 1.024 * $hier_neighbor_status_time{$neighbor}{$status} ) );
    }
  }
  outseperator($report_index);
  outline( $report_index, 'Sum', $hier, 100, $hier_time / ( 1000 * $hier ),
	  kilomegagigatera( $hier_size, $format[4] ),
	  100, $hier_size / ( 1.024 * $hier_time ) );
  outgraph( $report_index, \@graph_legend, $x_scale, \@xaxis, \@yaxis1,
	   \@yaxis2) if ($outtype_graph and $xaxis[0]);
  $max_value[$report_index] = "most requested destination</td>\n    <td>"
    . htmlescape($xaxis[0]) . "</td>\n    <td>";
  $max_value[$report_index] .= ($opt_O)
    ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
}
outstop($report_index);

# Request-destinations by ${N}-level-domain, index = 8
if ($opt_d) {
  @format = ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off' );
  $report_index = 8;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $tcp == 0 ) {
    outline( $report_index, 'no matching requests' );
    outstop($report_index);
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    $max_x_data = ($opt_d < $x_scale and $opt_d != -1) ? $opt_d : $x_scale;
    $i = 0;
    outimg($report_index) if ($outtype_graph);
    @counter = keys %tcp_urlhost;
    $other_urlhost = $#counter + 1;
    $other = $tcp;
    $other_size = $tcp_size;
    $other_hit = $tcp_hit;
    $other_time = $tcp_time;
    $other_hit_size = $tcp_hit_size;
    $other_count = $opt_d;
    outheader( $report_index, 'destination', ' request', '% ', 'hit-%', 'auto',
	      $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ', 'hit-%',
	      'auto' );
    outseperator($report_index);
    foreach $urlhost ( sort {
      ${"tcp_urlhost$sortorder"}{$b} <=> ${"tcp_urlhost$sortorder"}{$a}
      } keys(%tcp_urlhost) ) {
      next if $urlhost eq '<other>';
      $other_urlhost--;
      $other -= $tcp_urlhost{$urlhost};
      $other_size -= $tcp_urlhost_size{$urlhost};
      $other_time -= $tcp_urlhost_time{$urlhost};
      $other_hit -= $tcp_hit_urlhost{$urlhost};
      $other_hit_size -= $tcp_hit_urlhost_size{$urlhost};
      $i++;
      push @xaxis, $urlhost if ($i < $max_x_data);
      push @yaxis1, $tcp_urlhost{$urlhost} if ($i < $max_x_data);
      push @yaxis2, $tcp_urlhost_size{$urlhost} if ($i < $max_x_data);
      push @yaxis3, $tcp_urlhost{$urlhost}
	? $tcp_hit_urlhost{$urlhost} / $tcp_urlhost{$urlhost}
	: 0 if ($i < $max_x_data);
      push @yaxis4, $tcp_urlhost_size{$urlhost}
	? $tcp_hit_urlhost_size{$urlhost} / $tcp_urlhost_size{$urlhost}
	: 0 if ($i < $max_x_data);
      writecache( $report_index, $urlhost, $tcp_urlhost{$urlhost},
		 $tcp_urlhost_size{$urlhost}, $tcp_hit_urlhost{$urlhost},
		 $tcp_hit_urlhost_size{$urlhost}, $tcp_urlhost_time{$urlhost}
		 );
      outline( $report_index, $urlhost, $tcp_urlhost{$urlhost},
	      100 * $tcp_urlhost{$urlhost} / $tcp,
	      100 * $tcp_hit_urlhost{$urlhost} / $tcp_urlhost{$urlhost},
	      $tcp_urlhost_time{$urlhost} / (1000 * $tcp_urlhost{$urlhost}),
	      kilomegagigatera( $tcp_urlhost_size{$urlhost}, $format[5] ),
	      $tcp_size ? 100 * $tcp_urlhost_size{$urlhost} / $tcp_size : 0,
	      $tcp_urlhost_size{$urlhost}
		? 100 * $tcp_hit_urlhost_size{$urlhost} /
		  $tcp_urlhost_size{$urlhost} :	0,
	      $tcp_urlhost_time{$urlhost}
		? $tcp_urlhost_size{$urlhost} /
		  ( 1.024 * $tcp_urlhost_time{$urlhost} ) : 0 );
      last if ( ( --$other_count == 0 and $other != 1 ) or
	       ($tcp_urlhost{$urlhost} < $opt_domain_report_limit) );
    }
    if ($other) {
      push @xaxis, '<other>';
      push @yaxis1, $other;
      push @yaxis2, $other_size;
      push @yaxis3, $other ? $other_hit / $other : 0;
      push @yaxis4, $other_size ? $other_hit_size / $other_size : 0;
      $max_x_data = ( $opt_d < $x_scale and $opt_d != -1 )
	? $opt_d + 1 : $x_scale + 1;
      writecache( $report_index, '<other>', $other, $other_size, $other_hit,
		 $other_hit_size, $other_time );
      outline( $report_index,
	      'other: ' . $other_urlhost . " $N-level-domains", $other,
	      100 * $other / $tcp,
	      100 * $other_hit / $other,
	      $other_time / (1000 * $other),
	      kilomegagigatera( $other_size, $format[5] ),
	      $tcp_size ? 100 * $other_size / $tcp_size : 0,
	      $other_size ? 100 * $other_hit_size / $other_size : 0,
	      $other_size ? $other_size/ (1.024 * $other_time ) : 0 );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	    $tcp_time / (1000 * $tcp),
	    kilomegagigatera( $tcp_size, $format[5] ), 100,
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    $tcp_time ? $tcp_size / ( 1.024 * $tcp_time ) : 0 );
    outgraph($report_index, \@graph_legend, $max_x_data, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4) if ($outtype_graph and $xaxis[0]);
    $max_value[$report_index] = "most requested $N-level-domain</td>
    <td>" . htmlescape($xaxis[0]) . "</td>\n    <td>";
    $max_value[$report_index] .= ($opt_O)
      ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
    test( $report_index, \%tcp_urlhost, \%tcp_urlhost_size,
	 \%tcp_urlhost_time, 'tcp') if $test;
    outstop($report_index);

# Request-destinations by toplevel-domain
    @format = ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off' );
    $report_index = 9;
    @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
    outstart($report_index);
    @xaxis = @yaxis1 = @yaxis2 = @yaxis3 = @yaxis4 = ();
    $max_x_data = ($opt_d < $x_scale and $opt_d != -1) ? $opt_d : $x_scale;
    $i = 0;
    outimg($report_index) if ($outtype_graph);
    outheader( $report_index, 'destination', ' request', '% ', 'hit-%', 'auto',
	      $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ',
	      'hit-%', 'auto' );
    outseperator($report_index);
    @counter = keys %tcp_urltld;
    $other_tld = $#counter + 1;
    $other = $tcp;
    $other_size = $tcp_size;
    $other_hit = $tcp_hit;
    $other_time = $tcp_time;
    $other_hit_size = $tcp_hit_size;
    $other_count = $opt_d;
    foreach $urltld ( sort { ${"tcp_urltld$sortorder"}{$b} <=>
			   ${"tcp_urltld$sortorder"}{$a} }
		     keys(%tcp_urltld) ) {
      next if $urltld eq '<other>';
      $other_tld--;
      $other -= $tcp_urltld{$urltld};
      $other_size -= $tcp_urltld_size{$urltld};
      $other_time -= $tcp_urltld_time{$urltld};
      $other_hit -= $tcp_hit_urltld{$urltld};
      $other_hit_size -= $tcp_hit_urltld_size{$urltld};
      $i++;
      push @xaxis, $urltld if ($i < $max_x_data);
      push @yaxis1, $tcp_urltld{$urltld} if ($i < $max_x_data);
      push @yaxis2, $tcp_urltld_size{$urltld} if ($i < $max_x_data);
      push @yaxis3, $tcp_urltld{$urltld}
	? $tcp_hit_urltld{$urltld} / $tcp_urltld{$urltld}
	: 0 if ($i < $max_x_data);
      push @yaxis4, $tcp_urltld_size{$urltld}
	? $tcp_hit_urltld_size{$urltld} / $tcp_urltld_size{$urltld}
	: 0 if ($i < $max_x_data);
      writecache( $report_index, $urltld, $tcp_urltld{$urltld},
		 $tcp_urltld_size{$urltld}, $tcp_hit_urltld{$urltld},
		 $tcp_hit_urltld_size{$urltld}, $tcp_urltld_time{$urltld} );
      outline( $report_index, $urltld, $tcp_urltld{$urltld},
	      100 * $tcp_urltld{$urltld} / $tcp,
	      100 * $tcp_hit_urltld{$urltld} / $tcp_urltld{$urltld},
	      $tcp_urltld_time{$urltld} / (1000 * $tcp_urltld{$urltld}),
	      kilomegagigatera( $tcp_urltld_size{$urltld}, $format[5] ),
	      $tcp_size ? 100 * $tcp_urltld_size{$urltld} / $tcp_size : 0,
	      $tcp_urltld_size{$urltld}
		? 100 * $tcp_hit_urltld_size{$urltld} /
		$tcp_urltld_size{$urltld}
		: 0, $tcp_urltld_time{$urltld}
		? $tcp_urltld_size{$urltld} /
		  ( 1.024 * $tcp_urltld_time{$urltld} ) : 0 );
      last if ( ( --$other_count == 0 and $other != 1 ) or
	       ($tcp_urltld{$urltld} < $opt_domain_report_limit) );
    }
    if ($other) {
      push @xaxis, '<other>';
      push @yaxis1, $other;
      push @yaxis2, $other_size;
      push @yaxis3, $other ? $other_hit / $other : 0;
      push @yaxis4, $other_size ? $other_hit_size / $other_size : 0;
      $max_x_data = ( $opt_d < $x_scale and $opt_d != -1 )
	? $opt_d + 1 : $x_scale + 1;
      writecache( $report_index, '<other>', $other, $other_size, $other_hit,
		 $other_hit_size, $other_time );
      outline( $report_index, 'other: ' . $other_tld . ' top-level-domains',
	      $other, 100 * $other / $tcp, 100 * $other_hit / $other,
	      $other_time / (1000 * $other),
	      kilomegagigatera( $other_size, $format[5] ),
	      $tcp_size ? 100 * $other_size / $tcp_size : 0,
	      $other_size ? 100 * $other_hit_size / $other_size : 0,
	      $other_size ? $other_size/ (1.024 * $other_time ) : 0 );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	    $tcp_time / (1000 * $tcp),
	    kilomegagigatera( $tcp_size, $format[5] ), 100,
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    $tcp_time ? $tcp_size / ( 1.024 * $tcp_time ) : 0 );
    outgraph($report_index, \@graph_legend, $max_x_data, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4) if ($outtype_graph and $xaxis[0]);
    outstop($report_index);
    test( $report_index, \%tcp_urltld, \%tcp_urltld_size, \%tcp_urltld_time,
	 'tcp') if $test;
    $max_value[$report_index] = "most requested toplevel-domain</td>
    <td>" . htmlescape($xaxis[0]) . "</td>\n    <td>";
    $max_value[$report_index] .= ($opt_O)
      ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
  }
}

# TCP-Request-protocol
if ($opt_t) {
  @format = ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off' );
  $report_index = 10;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $tcp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    $max_x_data = ($opt_t < $x_scale and $opt_t != -1) ? $opt_t : $x_scale;
    $i = 0;
    outimg($report_index) if ($outtype_graph);
    outheader( $report_index, 'protocol', ' request', '% ', 'hit-%', 'auto',
	      $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ', 'hit-%',
	      'auto' );
    outseperator($report_index);
    foreach $urlprot ( sort {
      ${"tcp_urlprot$sortorder"}{$b} <=> ${"tcp_urlprot$sortorder"}{$a}
	} keys(%tcp_urlprot) ) {
      push @xaxis, $urlprot;
      push @yaxis1, $tcp_urlprot{$urlprot};
      push @yaxis2, $tcp_urlprot_size{$urlprot};
      push @yaxis3, $tcp_urlprot{$urlprot}
	? $tcp_hit_urlprot{$urlprot} / $tcp_urlprot{$urlprot} : 0;
      push @yaxis4, $tcp_urlprot_size{$urlprot}
	? $tcp_hit_urlprot_size{$urlprot} / $tcp_urlprot_size{$urlprot} : 0;
      writecache( $report_index, $urlprot, $tcp_urlprot{$urlprot},
		 $tcp_urlprot_size{$urlprot}, $tcp_hit_urlprot{$urlprot},
		 $tcp_hit_urlprot_size{$urlprot}, $tcp_urlprot_time{$urlprot}
		 );
      outline( $report_index, $urlprot, $tcp_urlprot{$urlprot},
	      100 * $tcp_urlprot{$urlprot} / $tcp,
	      100 * $tcp_hit_urlprot{$urlprot} / $tcp_urlprot{$urlprot},
	      $tcp_urlprot_time{$urlprot} / (1000 * $tcp_urlprot{$urlprot}),
	      kilomegagigatera( $tcp_urlprot_size{$urlprot}, $format[5] ),
	      $tcp_size ? 100 * $tcp_urlprot_size{$urlprot} / $tcp_size : 0,
	      $tcp_urlprot_size{$urlprot}
		? 100 * $tcp_hit_urlprot_size{$urlprot} /
		  $tcp_urlprot_size{$urlprot} : 0,
	      $tcp_urlprot_time{$urlprot}
		? $tcp_urlprot_size{$urlprot} /
		  ( 1.024 * $tcp_urlprot_time{$urlprot} ) : 0 );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	    $tcp_time / (1000 * $tcp),
	    kilomegagigatera( $tcp_size, $format[5] ), 100,
	    100 * $tcp_hit_size / $tcp_size, $tcp_size / $tcp_time );
    outgraph($report_index, \@graph_legend, $max_x_data, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%tcp_urlprot, \%tcp_urlprot_size, \%tcp_urlprot_time,
	 'tcp') if $test;
    $max_value[$report_index] = "most requested protocol</td>\n    <td>"
      . htmlescape($xaxis[0]) . "</td>\n    <td>";
    $max_value[$report_index] .= ($opt_O)
      ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
  }
  outstop($report_index);

# Requested content-type
  if ( defined(%tcp_content) ) {
    @format = ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off' );
    $report_index = 11;
    @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
    outstart($report_index);
    if ( $tcp == 0 ) {
      outline( $report_index, 'no matching requests' );
    } else {
      my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
      $max_x_data = ($opt_t < $x_scale and $opt_t != -1) ? $opt_t : $x_scale;
      $i = 0;
      outimg($report_index) if ($outtype_graph);
      @counter = keys %tcp_content;
      $other_content = $#counter + 1;
      $other = $tcp;
      $other_size = $tcp_size;
      $other_time = $tcp_time;
      $other_hit = $tcp_hit;
      $other_hit_size = $tcp_hit_size;
      $other_count = $opt_t;

      outheader( $report_index, 'content-type', ' request', '% ', 'hit-%',
		'auto', $outtype_unformatted ? " ${opt_U}Byte" : '  Byte',
		'% ', 'hit-%', 'auto' );
      outseperator($report_index);
      foreach $content ( sort {
	${"tcp_content$sortorder"}{$b} <=> ${"tcp_content$sortorder"}{$a}
	  } keys(%tcp_content) ) {
	next if $content eq '<other>';
	$other_content--;
	$other -= $tcp_content{$content};
	$other_size -= $tcp_content_size{$content};
	$other_time -= $tcp_content_time{$content};
	$other_hit -= $tcp_hit_content{$content};
	$other_hit_size -= $tcp_hit_content_size{$content};
	$i++;
	push @xaxis, $content if ($i < $max_x_data);
	push @yaxis1, $tcp_content{$content} if ($i < $max_x_data);
	push @yaxis2, $tcp_content_size{$content} if ($i < $max_x_data);
	push @yaxis3, $tcp_content{$content}
	  ? $tcp_hit_content{$content} / $tcp_content{$content}
	  : 0 if ($i < $max_x_data);
	push @yaxis4, $tcp_content_size{$content}
	  ? $tcp_hit_content_size{$content} / $tcp_content_size{$content}
	  : 0 if ($i < $max_x_data);
	writecache( $report_index, $content, $tcp_content{$content},
		   $tcp_content_size{$content}, $tcp_hit_content{$content},
		   $tcp_hit_content_size{$content},
		   $tcp_content_time{$content} );
	outline( $report_index, $content, $tcp_content{$content},
		100 * $tcp_content{$content} / $tcp,
		100 * $tcp_hit_content{$content} / $tcp_content{$content},
		$tcp_content_time{$content} / (1000 * $tcp_content{$content}),
		kilomegagigatera( $tcp_content_size{$content}, $format[5] ),
		$tcp_size ? 100 * $tcp_content_size{$content} / $tcp_size : 0,
		$tcp_content_size{$content}
		  ? 100 * $tcp_hit_content_size{$content} /
		    $tcp_content_size{$content} : 0,
		$tcp_content_time{$content}
		  ? $tcp_content_size{$content} /
		    ( 1.024 * $tcp_content_time{$content} ) : 0 );
	last if ( --$other_count == 0 and $other != 1 );
      }
      if ($other) {
	push @xaxis, '<other>';
	push @yaxis1, $other;
	push @yaxis2, $other_size;
	push @yaxis3, $other ? $other_hit / $other : 0;
	push @yaxis4, $other_size ? $other_hit_size / $other_size : 0;
	$max_x_data = ($opt_t < $x_scale and $opt_t != -1)
	  ? $opt_t + 1 : $x_scale + 1;
	writecache( $report_index, '<other>', $other, $other_size, $other_hit,
		   $other_hit_size, $other_time );
	outline( $report_index, 'other: ' . $other_content . ' content-types',
		$other, 100 * $other / $tcp, 100 * $other_hit / $other,
		$other_time / (1000 * $other),
		kilomegagigatera( $other_size, $format[5] ),
		$tcp_size ? 100 * $other_size / $tcp_size : 0,
		$other_size ? 100 * $other_hit_size / $other_size : 0,
		$other_size ? $other_size/ (1.024 * $other_time ) : 0 );
      }
      outseperator($report_index);
      outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	      $tcp_time / (1000 * $tcp),
	      kilomegagigatera( $tcp_size, $format[5] ), 100,
	      $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	      $tcp_time ? $tcp_size / ( 1.024 * $tcp_time ) : 0 );
      outgraph($report_index, \@graph_legend, $max_x_data, \@xaxis, \@yaxis1,
	       \@yaxis2, \@yaxis3, \@yaxis4) if ($outtype_graph and $xaxis[0]);
      test( $report_index, \%tcp_content, \%tcp_content_size,
	   \%tcp_content_time, 'tcp') if $test;
      $max_value[$report_index] = "most requested content-type</td>
    <td>" . htmlescape($xaxis[0]) . "</td>\n    <td>";
      $max_value[$report_index] .= ($opt_O)
	? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
    }
    outstop($report_index);
  }
# Requested extensions
  @format = ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off', 'off', 'off' );
  $report_index = 12;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $tcp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    $max_x_data = ($opt_t < $x_scale and $opt_t != -1) ? $opt_t : $x_scale;
    $i = 0;
    outimg($report_index) if ($outtype_graph);
    @counter = keys %tcp_urlext;
    $other_urlext = $#counter + 1;
    $other = $tcp;
    $other_size = $tcp_size;
    $other_time = $tcp_time;
    $other_hit = $tcp_hit;
    $other_hit_size = $tcp_hit_size;
    $other_count = $opt_t;
    $sum_tcp_urlext_fresh = 0;
    $sum_tcp_urlext_stale = 0;
    $sum_tcp_urlext_refresh = 0;
    $sum_tcp_urlext_mod = 0;
    $sum_tcp_urlext_unmod = 0;
    foreach $urlext ( sort {${"tcp_urlext$sortorder"}{$b} <=>
			   ${"tcp_urlext$sortorder"}{$a} }
		     keys(%tcp_urlext) ) {
      $sum_tcp_urlext_fresh += $tcp_urlext_fresh{$urlext};
      $sum_tcp_urlext_stale += $tcp_urlext_stale{$urlext};
      $sum_tcp_urlext_refresh += $tcp_urlext_refresh{$urlext};
      $sum_tcp_urlext_mod += $tcp_urlext_mod{$urlext};
      $sum_tcp_urlext_unmod += $tcp_urlext_unmod{$urlext};
    }
    $other_fresh = $sum_tcp_urlext_fresh;
    $other_stale = $sum_tcp_urlext_stale;
    $other_mod = $sum_tcp_urlext_mod;
    $other_unmod = $sum_tcp_urlext_unmod;
    $other_refresh = $sum_tcp_urlext_refresh;
    outheader( $report_index, 'extensions', ' request', '% ', 'hit-%', 'auto',
	      $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ',
	      'hit-%', 'auto', 'fresh/stale', 'unmod/mod' );
    outseperator($report_index);
    foreach $urlext ( sort {${"tcp_urlext$sortorder"}{$b} <=>
			   ${"tcp_urlext$sortorder"}{$a} }
		     keys(%tcp_urlext) ) {
      $sum_tcp_urlext_fresh += $tcp_urlext_fresh{$urlext};
      $sum_tcp_urlext_stale += $tcp_urlext_stale{$urlext};
      $sum_tcp_urlext_refresh += $tcp_urlext_refresh{$urlext};
      $sum_tcp_urlext_mod += $tcp_urlext_mod{$urlext};
      $sum_tcp_urlext_unmod += $tcp_urlext_unmod{$urlext};
    }
    foreach $urlext ( sort {${"tcp_urlext$sortorder"}{$b} <=>
			   ${"tcp_urlext$sortorder"}{$a} }
		     keys(%tcp_urlext) ) {
      next if $urlext eq '<other>';
      $other_urlext--;
      $other -= $tcp_urlext{$urlext};
      $other_size -= $tcp_urlext_size{$urlext};
      $other_time -= $tcp_urlext_time{$urlext};
      $other_hit -= $tcp_hit_urlext{$urlext};
      $other_hit_size -= $tcp_hit_urlext_size{$urlext};
      $other_fresh -= $tcp_urlext_fresh{$urlext};
      $other_stale -= $tcp_urlext_stale{$urlext};
      $other_mod -= $tcp_urlext_mod{$urlext};
      $other_unmod -= $tcp_urlext_unmod{$urlext};
      $other_refresh -= $tcp_urlext_refresh{$urlext};
      $i++;
      push @xaxis, $urlext if ($i < $max_x_data);
      push @yaxis1, $tcp_urlext{$urlext} if ($i < $max_x_data);
      push @yaxis2, $tcp_urlext_size{$urlext} if ($i < $max_x_data);
      push @yaxis3, $tcp_urlext{$urlext}
	? $tcp_hit_urlext{$urlext} / $tcp_urlext{$urlext}
	: 0 if ($i < $max_x_data);
      push @yaxis4, $tcp_urlext_size{$urlext}
	? $tcp_hit_urlext_size{$urlext} / $tcp_urlext_size{$urlext}
	: 0 if ($i < $max_x_data);
      writecache( $report_index, $urlext, $tcp_urlext{$urlext},
		 $tcp_urlext_size{$urlext}, $tcp_hit_urlext{$urlext},
		 $tcp_hit_urlext_size{$urlext}, $tcp_urlext_time{$urlext},
		 $tcp_urlext_fresh{$urlext}, $tcp_urlext_stale{$urlext},
		 $tcp_urlext_refresh{$urlext}, $tcp_urlext_mod{$urlext},
		 $tcp_urlext_unmod{$urlext}
		 );
      outline( $report_index, $urlext, $tcp_urlext{$urlext},
	      100 * $tcp_urlext{$urlext} / $tcp,
	      100 * $tcp_hit_urlext{$urlext} / $tcp_urlext{$urlext},
	      $tcp_urlext_time{$urlext} / ( 1000 *  $tcp_urlext{$urlext} ),
	      kilomegagigatera( $tcp_urlext_size{$urlext}, $format[5] ),
	      $tcp_size ? 100 * $tcp_urlext_size{$urlext} / $tcp_size : 0,
	      $tcp_urlext_size{$urlext}
		? 100 * $tcp_hit_urlext_size{$urlext} /
		  $tcp_urlext_size{$urlext} : 0,
	      $tcp_urlext_time{$urlext}
		? $tcp_urlext_size{$urlext} /
		( 1.024 * $tcp_urlext_time{$urlext} ) : 0,
	      join ('/', gcd($tcp_urlext_fresh{$urlext},
			     $tcp_urlext_stale{$urlext})),
	      join ('/', gcd($tcp_urlext_unmod{$urlext},
			     $tcp_urlext_mod{$urlext})) );
      last if ( --$other_count == 0 and $other != 1 );
    }
    if ($other) {
      push @xaxis, '<other>';
      push @yaxis1, $other;
      push @yaxis2, $other_size;
      push @yaxis3, $other ? $other_hit / $other : 0;
      push @yaxis4, $other_size ? $other_hit_size / $other_size : 0;
      $max_x_data = ( $opt_t < $x_scale and $opt_t != -1 )
	? $opt_t + 1 : $x_scale + 1;
      writecache( $report_index, '<other>', $other, $other_size, $other_hit,
		 $other_hit_size, $other_time, $other_fresh, $other_stale,
		 $other_refresh, $other_mod, $other_unmod );
      outline( $report_index, 'other: ' . $other_urlext . ' extensions',
	      $other, 100 * $other / $tcp, 100 * $other_hit / $other,
	      $other_time / (1000 * $other),
	      kilomegagigatera( $other_size, $format[5] ),
	      $tcp_size ? 100 * $other_size / $tcp_size : 0,
	      $other_size ? 100 * $other_hit_size / $other_size : 0,
	      $other_size ? $other_size/ (1.024 * $other_time ) : 0,
	      join ('/', gcd($other_fresh, $other_stale)),
	      join ('/', gcd($other_unmod, $other_mod)) );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	    $tcp_time / (1000 * $tcp),
	    kilomegagigatera( $tcp_size, $format[5] ), 100,
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    $tcp_time ? $tcp_size / ( 1.024 * $tcp_time ) : 0,
	    join ('/', gcd($sum_tcp_urlext_fresh, $sum_tcp_urlext_stale)),
	    join ('/', gcd($sum_tcp_urlext_mod, $sum_tcp_urlext_unmod)) );
    outgraph($report_index, \@graph_legend, $max_x_data, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%tcp_urlext, \%tcp_urlext_size, \%tcp_urlext_time,
	 'tcp') if $test;
    $max_value[$report_index] = "most requested extension</td>\n    <td>"
      . htmlescape($xaxis[0]) . "</td>\n    <td>";
    $max_value[$report_index] .= ($opt_O)
      ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
  }
  outstop($report_index);
}

if ($opt_r) {
# Incoming UDP-requests by host
  @format = ( 38, 9, 'off', '%', 'off', 8, 'off', '%', 'kbps' );
  $report_index = 13;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $udp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    $max_x_data = ($opt_t < $x_scale and $opt_t != -1) ? $opt_t : $x_scale;
    $i = 0;
    outimg($report_index) if ($outtype_graph);
    if ($opt_R) {
      outheader( $report_index, 'host / target', ' request', '%', 'hit-%',
		'auto',	$outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '%',
		'hit-%', 'auto' );
    } else {
      outheader( $report_index, 'host', ' request', '%', 'hit-%', 'auto',
		$outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '%',
		'hit-%', 'auto' );
    }
    outseperator($report_index);
    @counter = keys %udp_requester;
    $other_requester = $#counter + 1;
    $other = $udp;
    $other_size = $udp_size;
    $other_time = $udp_time;
    $other_hit = $udp_hit;
    $other_hit_size = $udp_hit_size;
    $other_count = $opt_r;
    foreach $requester ( sort {
      ${"udp_requester$sortorder"}{$b} <=> ${"udp_requester$sortorder"}{$a}
	} keys(%udp_requester) ) {
      next if $requester eq '<other>';
      $other_requester--;
      $other -= $udp_requester{$requester};
      $other_size -= $udp_requester_size{$requester};
      $other_time -= $udp_requester_time{$requester};
      $other_hit -= $udp_hit_requester{$requester};
      $other_hit_size -= $udp_hit_requester_size{$requester};
      $i++;
      push @xaxis, $requester if ($i < $max_x_data);
      push @yaxis1, $udp_requester{$requester} if ($i < $max_x_data);
      push @yaxis2, $udp_requester_size{$requester} if ($i < $max_x_data);
      push @yaxis3, $udp_requester{$requester}
	? $udp_hit_requester{$requester} / $udp_requester{$requester}
	: 0 if ($i < $max_x_data);
      push @yaxis4, $udp_requester_size{$requester}
	? $udp_hit_requester_size{$requester} / $udp_requester_size{$requester}
	: 0 if ($i < $max_x_data);
      writecache( "$report_index.1", $requester, $udp_requester{$requester},
		 $udp_requester_size{$requester},
		 $udp_requester_time{$requester},
		 $udp_hit_requester{$requester},
		 $udp_hit_requester_size{$requester}
		 );
      outline( $report_index, $requester, $udp_requester{$requester},
	      $udp ? 100 * $udp_requester{$requester} / $udp : 0,
	      100 * $udp_hit_requester{$requester} /
		$udp_requester{$requester},
	      $udp_requester{$requester}
		? $udp_requester_time{$requester} /
		  ( 1000 * $udp_requester{$requester} ) : 0,
	      kilomegagigatera( $udp_requester_size{$requester}, $format[5] ),
	      $udp_size
		? 100 * $udp_requester_size{$requester} / $udp_size : 0,
	      $udp_requester_size{$requester}
		? 100 * $udp_hit_requester_size{$requester} /
		  $udp_requester_size{$requester} : 0,
	      $udp_requester_size{$requester} /
		( 1.024 * $udp_requester_time{$requester} ) );
      if ($opt_R) {
	@counter2 = keys( %{ $udp_requester_urlhost{$requester} } );
	$other2_requester_urlhost = $#counter2 + 1;
	$other2 = $udp_requester{$requester};
	$other2_size = $udp_requester_size{$requester};
	$other2_time = $udp_requester_time{$requester};
	$other2_hit = $udp_hit_requester{$requester};
	$other2_hit_size = $udp_hit_requester_size{$requester};
	$other2_count = $opt_R;
	foreach $urlhost ( sort {
	  ${"udp_requester_urlhost$sortorder"}{$requester}{$b} <=>
		 ${"udp_requester_urlhost$sortorder"}{$requester}{$a}
	    } keys( %{ $udp_requester_urlhost{$requester} } ) ) {
	  next if $urlhost eq '<other>';
	  $other2_requester_urlhost--;
	  $other2 -= $udp_requester_urlhost{$requester}{$urlhost};
	  $other2_size -= $udp_requester_urlhost_size{$requester}{$urlhost};
	  $other2_time -= $udp_requester_urlhost_time{$requester}{$urlhost};
	  $other2_hit -= $udp_hit_requester_urlhost{$requester}{$urlhost};
	  $other2_hit_size -=
	    $udp_hit_requester_urlhost_size{$requester}{$urlhost};
	  writecache( "$report_index.2", $requester, $urlhost,
		     $udp_requester_urlhost{$requester}{$urlhost},
		     $udp_requester_urlhost_size{$requester}{$urlhost},
		     $udp_requester_urlhost_time{$requester}{$urlhost},
		     $udp_hit_requester_urlhost{$requester}{$urlhost},
		     $udp_hit_requester_urlhost_size{$requester}{$urlhost} );
	  outline( $report_index, ' ' .  $urlhost,
		  $udp_requester_urlhost{$requester}{$urlhost},
		  '',
		  100 * $udp_hit_requester_urlhost{$requester}{$urlhost} /
		    $udp_requester_urlhost{$requester}{$urlhost},
		  $udp_requester_urlhost_time{$requester}{$urlhost} /
		    (1000 * $udp_requester_urlhost{$requester}{$urlhost}),
		  kilomegagigatera( $udp_requester_urlhost_size{$requester}{$urlhost},
				   $format[5] ),
		  '',
		  $udp_requester_urlhost_size{$requester}{$urlhost}
		    ? 100 *
		      $udp_hit_requester_urlhost_size{$requester}{$urlhost} /
		      $udp_requester_urlhost_size{$requester}{$urlhost} : 0,
		  $udp_requester_urlhost_size{$requester}{$urlhost} /
		    ( 1.024 *
		     $udp_requester_urlhost_time{$requester}{$urlhost} ) );
	  last if ( --$other2_count == 0 and $other2 != 1 );
	}
	if ($other2) {
	  writecache( "$report_index.2", $requester, '<other>', $other2,
		     $other2_size, $other2_time, $other2_hit,
		     $other2_hit_size );
	  outline( $report_index, ' other: ' . $other2_requester_urlhost
		    . ' requested urlhosts', $other2, '',
		  100 * $other2_hit / $other2,
		  $other2_time / ( 1000 * $other2_requester_urlhost ),
		  kilomegagigatera( $other2_size, $format[5] ), '',
		  $other2_size ? 100 * $other2_hit_size / $other2_size : 0,
		  $other2_size / ( 1.024 * $other2_time ) );
	}
      }
      last if ( --$other_count == 0 and $other != 1 );
    }
    if ($other) {
      push @xaxis, '<other>';
      push @yaxis1, $other;
      push @yaxis2, $other_size;
      push @yaxis3, $other ? $other_hit / $other : 0;
      push @yaxis4, $other_size ? $other_hit_size / $other_size : 0;
      $max_x_data = ( $opt_r < $x_scale and $opt_r != -1 )
	? $opt_r + 1 : $x_scale + 1;
      writecache( "$report_index.1", '<other>', $other, $other_size,
		 $other_time, $other_hit, $other_hit_size );
      outline( $report_index,
	      'other: ' . $other_requester . ' requesting hosts', $other, '',
	      100 * $other_hit / $other, $other_time / ( 1000 * $udp ),
	      kilomegagigatera( $other_size, $format[5] ), '',
	      100 * $other_hit_size / $other_size,
	      $other_size / ( 1.024 * $udp_time ) );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $udp, 100, 100 * $udp_hit / $udp,
	    $udp_time / (1000 * $udp),
	    kilomegagigatera( $udp_size, $format[5] ), 100,
	    $udp_size ? 100 * $udp_hit_size / $udp_size : 0,
	    $udp_time ? $udp_size / ( 1.024 * $udp_time ) : 0 );
    outgraph( $report_index, \@graph_legend, $max_x_data, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4 ) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%udp_requester, \%udp_requester_size,
	 \%udp_requester_time, 'udp') if $test;
    $max_value[$report_index] = "most active host</td>\n    <td>"
      . htmlescape($xaxis[0]) . "</td>\n    <td>";
    $max_value[$report_index] .= ($opt_O)
      ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
  }
  outstop($report_index);

# Incoming TCP-requests by host
  @format = ( 30, 9, 'off', '%', 'spr', 8, 'off', '%', 'kbps' );
  $report_index = 14;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $tcp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    $max_x_data = ($opt_r < $x_scale and $opt_r != -1) ? $opt_r : $x_scale;
    $i = 0;
    outimg($report_index) if ($outtype_graph);
    if ($opt_R) {
      outheader( $report_index, 'host / target', ' request', '%', 'hit-%',
		'auto',	$outtype_unformatted ? " ${opt_U}Byte" : '  Byte',
		'%', 'hit-%', 'auto' );
    } else {
      outheader( $report_index, 'host', ' request', '%', 'hit-%', 'auto',
		$outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '%',
		'hit-%', 'auto' );
    }
    outseperator($report_index);
    @counter = keys %tcp_requester;
    $other_requester = $#counter + 1;
    $other = $tcp;
    $other_size = $tcp_size;
    $other_time = $tcp_time;
    $other_hit = $tcp_hit;
    $other_hit_size = $tcp_hit_size;
    $other_count = $opt_r;
    foreach $requester ( sort {
			${"tcp_requester$sortorder"}{$b} <=>
			${"tcp_requester$sortorder"}{$a}
			} keys(%tcp_requester) ) {
      next if $requester eq '<other>';
      $other_requester--;
      $other -= $tcp_requester{$requester};
      $other_size -= $tcp_requester_size{$requester};
      $other_time -= $tcp_requester_time{$requester};
      $other_hit -= $tcp_hit_requester{$requester};
      $other_hit_size -= $tcp_hit_requester_size{$requester};
      $i++;
      push @xaxis, $requester if ($i < $max_x_data);
      push @yaxis1, $tcp_requester{$requester} if ($i < $max_x_data);
      push @yaxis2, $tcp_requester_size{$requester} if ($i < $max_x_data);
      push @yaxis3, $tcp_requester{$requester}
	? $tcp_hit_requester{$requester} / $tcp_requester{$requester}
	: 0 if ($i < $max_x_data);
      push @yaxis4, $tcp_requester_size{$requester}
	? $tcp_hit_requester_size{$requester} / $tcp_requester_size{$requester}
	: 0 if ($i < $max_x_data);
      writecache( "$report_index.1", $requester, $tcp_requester{$requester},
		 $tcp_requester_size{$requester},
		 $tcp_requester_time{$requester},
		 $tcp_hit_requester{$requester},
		 $tcp_hit_requester_size{$requester}
		 );
      outline( $report_index, uri_unescape($requester),
	      $tcp_requester{$requester},
	      $tcp ? 100 * $tcp_requester{$requester} / $tcp : 0,
	      100 * $tcp_hit_requester{$requester} /
		$tcp_requester{$requester},
	      $tcp_requester_time{$requester} /
		( 1000 * $tcp_requester{$requester} ),
	      kilomegagigatera( $tcp_requester_size{$requester}, $format[5] ),
	      $tcp_size
		? 100 * $tcp_requester_size{$requester} / $tcp_size : 0,
	      $tcp_requester_size{$requester}
		? 100 * $tcp_hit_requester_size{$requester} /
		$tcp_requester_size{$requester} : 0,
	      $tcp_requester_size{$requester} /
		( 1.024 * $tcp_requester_time{$requester} ) );
      if ($opt_R) {
	@counter2 = keys( %{ $tcp_requester_urlhost{$requester} } );
	$other2_requester_urlhost = $#counter2 + 1;
	$other2 = $tcp_requester{$requester};
	$other2_size = $tcp_requester_size{$requester};
	$other2_time = $tcp_requester_time{$requester};
	$other2_hit = $tcp_hit_requester{$requester};
	$other2_hit_size = $tcp_hit_requester_size{$requester};
	$other2_count = $opt_R;
	foreach $urlhost ( sort {
	  ${"tcp_requester_urlhost$sortorder"}{$requester}{$b} <=>
		 ${"tcp_requester_urlhost$sortorder"}{$requester}{$a}
	  } keys( %{ $tcp_requester_urlhost{$requester} } ) ) {
	  next if $urlhost eq '<other>';
	  $other2_requester_urlhost--;
	  $other2 -= $tcp_requester_urlhost{$requester}{$urlhost};
	  $other2_size -= $tcp_requester_urlhost_size{$requester}{$urlhost};
	  $other2_time -= $tcp_requester_urlhost_time{$requester}{$urlhost};
	  $other2_hit -= $tcp_hit_requester_urlhost{$requester}{$urlhost};
	  $other2_hit_size -=
	    $tcp_hit_requester_urlhost_size{$requester}{$urlhost};
	  writecache( "$report_index.2", $requester, $urlhost,
		     $tcp_requester_urlhost{$requester}{$urlhost},
		     $tcp_requester_urlhost_size{$requester}{$urlhost},
		     $tcp_requester_urlhost_time{$requester}{$urlhost},
		     $tcp_hit_requester_urlhost{$requester}{$urlhost},
		     $tcp_hit_requester_urlhost_size{$requester}{$urlhost} );
	  outline( $report_index, ' ' .  $urlhost,
		  $tcp_requester_urlhost{$requester}{$urlhost}, '',
		  100 * $tcp_hit_requester_urlhost{$requester}{$urlhost} /
		    $tcp_requester_urlhost{$requester}{$urlhost},
		  $tcp_requester_urlhost_time{$requester}{$urlhost} /
		    ( 1000 * $tcp_requester_urlhost{$requester}{$urlhost} ),
		  kilomegagigatera(
				   $tcp_requester_urlhost_size{$requester}{$urlhost},
				   $format[5] ), '',
		  $tcp_requester_urlhost_size{$requester}{$urlhost}
		    ? 100 *
		      $tcp_hit_requester_urlhost_size{$requester}{$urlhost} /
		      $tcp_requester_urlhost_size{$requester}{$urlhost} : 0,
		  $tcp_requester_urlhost_size{$requester}{$urlhost} /
		    ( 1.024 *
		     $tcp_requester_urlhost_time{$requester}{$urlhost} ) );
	  last if ( --$other2_count == 0 and $other2 != 1 );
	}
	if ($other2) {
	  writecache( "$report_index.2", $requester, '<other>', $other2,
		     $other2_size, $other2_time, $other2_hit,
		     $other2_hit_size );
	  outline( $report_index, ' other: ' . $other2_requester_urlhost
		    . ' requested urlhosts', $other2, '',
		  100 * $other2_hit / $other2,
		  $other2_time / ( 1000 * $other2_requester_urlhost ),
		  kilomegagigatera( $other2_size, $format[5] ), '',
		  $other2_size ? 100 * $other2_hit_size / $other2_size : 0,
		  $other2_size / ( 1.024 * $other2_time ) );
	}
      }
      last if ( --$other_count == 0 and $other != 1 );
    }
    if ($other) {
      push @xaxis, '<other>';
      push @yaxis1, $other;
      push @yaxis2, $other_size;
      push @yaxis3, $other ? $other_hit / $other : 0;
      push @yaxis4, $other_size ? $other_hit_size / $other_size : 0;
      $max_x_data = ( $opt_r < $x_scale and $opt_r != -1 )
	? $opt_r + 1 : $x_scale + 1;
      writecache( "$report_index.1", '<other>', $other, $other_size,
		 $other_time, $other_hit, $other_hit_size );
      outline( $report_index,
	      'other: ' . $other_requester . ' requesting hosts', $other, '',
	      100 * $other_hit / $other, $other_time / ( 1000 * $tcp ),
	      kilomegagigatera( $other_size, $format[5] ), '',
	      $other_size ? 100 * $other_hit_size / $other_size : 0,
	      $other_size / ( 1.024 * $tcp_time ) );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	    $tcp_time / (1000 * $tcp),
	    kilomegagigatera( $tcp_size, $format[5] ), 100,
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    $tcp_time ? $tcp_size / ( 1.024 * $tcp_time ) : 0 );
    outgraph( $report_index, \@graph_legend, $max_x_data, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4 ) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%tcp_requester, \%tcp_requester_size,
	 \%tcp_requester_time, 'tcp') if $test;
    $max_value[$report_index] = "most active host</td>\n    <td>"
      . htmlescape($xaxis[0]) . "</td>\n    <td>";
    $max_value[$report_index] .= ($opt_O)
      ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests";
  }
  outstop($report_index);
}

# Size Distribution Diagram
if ($opt_D) {
  @format = ( 30, 9, 'off', '%', 'spr', 8, 'off', '%', 'kbps' );
  $report_index = 15;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $tcp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    outimg($report_index) if ($outtype_graph);
    outheader( $report_index, 'object-size (bytes)', ' request', '%', 'hit-%',
	      'auto', $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '%',
	      'hit-%', 'auto' );
    outseperator($report_index);
    foreach $distribution ( sort { $a <=> $b } keys(%tcp_distribution) ) {
      push @xaxis, int( $opt_D**$distribution ) . '-'
	. int( $opt_D**( $distribution + 1 ) - 1 );
      push @yaxis1, $tcp_distribution{$distribution};
      push @yaxis2, $tcp_distribution_size{$distribution};
      push @yaxis3, $tcp_distribution{$distribution}
	? $tcp_hit_distribution{$distribution} /
	$tcp_distribution{$distribution} : 0;
      push @yaxis4, $tcp_distribution_size{$distribution}
	? $tcp_hit_distribution_size{$distribution} /
	  $tcp_distribution_size{$distribution} : 0;
      writecache( $report_index, $distribution,
		 $tcp_distribution{$distribution},
		 $tcp_distribution_size{$distribution},
		 $tcp_distribution_time{$distribution},
		 $tcp_hit_distribution{$distribution},
		 $tcp_hit_distribution_size{$distribution} );
      outline( $report_index, int( $opt_D**$distribution ) . '-'
		. int( $opt_D**( $distribution + 1 ) - 1 ),
	      $tcp_distribution{$distribution},
	      100 * $tcp_distribution{$distribution} / $tcp,
	      100 * $tcp_hit_distribution{$distribution} /
		$tcp_distribution{$distribution},
	      $tcp_distribution_time{$distribution} /
		( 1000 * $tcp_distribution{$distribution} ),
	      kilomegagigatera( $tcp_distribution_size{$distribution},
			       $format[5] ),
	      $tcp_size
		? 100 * $tcp_distribution_size{$distribution} / $tcp_size : 0,
	      $tcp_distribution_size{$distribution}
		? 100 * $tcp_hit_distribution_size{$distribution} /
		$tcp_distribution_size{$distribution} : 0,
	      $tcp_distribution_size{$distribution} /
		( 1.024 * $tcp_distribution_time{$distribution} ) );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	    $tcp_time / (1000 * $tcp),
	    kilomegagigatera( $tcp_size, $format[5] ), 100,
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    $tcp_time ? $tcp_size / ( 1.024 * $tcp_time ) : 0 );
    outgraph( $report_index, \@graph_legend, $x_scale, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4 ) if ($outtype_graph and $xaxis[0]);
    my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
    if ($maxi and $xaxis[$maxi]) {
      $max_value[$report_index] = "most requested object_size</td>\n    <td>"
        . htmlescape($xaxis[$maxi]) . "</td>\n    <td>";
      $max_value[$report_index] .= ($opt_O)
        ? kilomegagigatera($max, 6) . " Byte" : $max. " Requests";
    }
    test( $report_index, \%tcp_distribution, \%tcp_distribution_size,
	 \%tcp_distribution_time, 'tcp') if $test;
  }
  outstop($report_index);
}

# Performance in $P steps
if ($opt_P) {
  @format = ( 15, 9, 'off', 5, 'off', 'off', 'kbps', 'kbps', 'kbps', 'kbps',
	     'kbps', 'kbps' );
  $report_index = 16;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $tcp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    outimg($report_index) if ($outtype_graph);
    outheader( $report_index, '', '', '', '', '', '', 'incomin', '   hit',
	      '  miss', ' direct', 'sibling', ' fetch' );
    outheader( $report_index, 'date', ' request', 'hit-%', ' Byte', 'hit-%',
	      'IPs', 'auto', 'auto', 'auto', 'auto', 'auto', 'auto');
    outseperator($report_index);
    foreach $perf_date ( sort { $a <=> $b } keys(%perf_counter) ) {
      $perf_requester{$perf_date} = scalar keys %{$perf_ip{$perf_date}};
      push @xaxis, substr( convertdate($perf_date), 0, 15 );
      push @yaxis1, $perf_counter{$perf_date};
      push @yaxis2, $perf_size{$perf_date};
      push @yaxis3, $perf_counter{$perf_date}
	? $perf_tcp_hit{$perf_date} / $perf_counter{$perf_date} : 0;
      push @yaxis4, $tcp_size
	? $perf_tcp_hit_size{$perf_date} / $tcp_size : 0;
      writecache( $report_index, $perf_date, $perf_counter{$perf_date},
		 $perf_size{$perf_date}, $perf_time{$perf_date},
		 $perf_tcp_hit_size{$perf_date},
		 $perf_tcp_hit_time{$perf_date},
		 $perf_tcp_miss_size{$perf_date},
		 $perf_tcp_miss_time{$perf_date},
		 $perf_hier_direct_size{$perf_date},
		 $perf_hier_direct_time{$perf_date},
		 $perf_hier_sibling_size{$perf_date},
		 $perf_hier_sibling_time{$perf_date},
		 $perf_hier_parent_size{$perf_date},
		 $perf_hier_parent_time{$perf_date},
		 $perf_requester{$perf_date},
		 $perf_tcp_hit{$perf_date}
		 );
      outline( $report_index, substr( convertdate($perf_date), 0, 15 ),
	      $perf_counter{$perf_date},
	      $perf_counter{$perf_date}
		? 100 * $perf_tcp_hit{$perf_date} / $perf_counter{$perf_date}
		: 0,
	      kilomegagigatera( $perf_size{$perf_date}, $format[3] ),
	      $tcp_size ? 100 * $perf_tcp_hit_size{$perf_date} / $tcp_size : 0,
	      $perf_requester{$perf_date},
	      removezerotimes( $perf_size{$perf_date}, $perf_time{$perf_date}
			      ),
	      removezerotimes( $perf_tcp_hit_size{$perf_date},
			      $perf_tcp_hit_time{$perf_date} ),
	      removezerotimes( $perf_tcp_miss_size{$perf_date},
			      $perf_tcp_miss_time{$perf_date} ),
	      removezerotimes( $perf_hier_direct_size{$perf_date},
			      $perf_hier_direct_time{$perf_date} ),
	      removezerotimes( $perf_hier_sibling_size{$perf_date},
			      $perf_hier_sibling_time{$perf_date} ),
	      removezerotimes( $perf_hier_parent_size{$perf_date},
			      $perf_hier_parent_time{$perf_date} ) );
    }
    outseperator($report_index);
    outline( $report_index, 'overall', $tcp,
	    100 * $tcp_hit / $tcp,
	    kilomegagigatera( $tcp_size, $format[3] ),
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    scalar keys %tcp_requester,
	    removezerotimes( $tcp_size, $tcp_time ),
	    removezerotimes( $tcp_hit_size, $tcp_hit_time ),
	    removezerotimes( $tcp_miss_size, $tcp_miss_time ),
	    removezerotimes( $hier_direct_size, $hier_direct_time ),
	    removezerotimes( $hier_sibling_size, $hier_sibling_time ),
	    removezerotimes( $hier_parent_size, $hier_parent_time ) );
    outgraph( $report_index, \@graph_legend, 31, \@xaxis, \@yaxis1, \@yaxis2,
	     \@yaxis3, \@yaxis4 ) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%perf_counter, \%perf_size, \%perf_time, 'tcp')
      if $test;
    my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
    if ($maxi and $xaxis[$maxi]) {
      $max_value[$report_index] = "most active day</td>\n    <td>"
        . htmlescape($xaxis[$maxi]) . "</td>\n    <td>";
      $max_value[$report_index] .= ($opt_O)
        ? kilomegagigatera($max, 6) . " Byte" : $max. " Requests";
    }
  }
  outstop($report_index);
}

# UDP-Request duration distribution in msec
if ($opt_response_time) {
  @format = ( 16, 9, '%', '%', 'spr', 8, '%', '%', 'kbps' );
  $report_index = 17;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $udp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my $max = 0;
    my $max_interval = 0;
    foreach $reqtime ( keys %udp_reqtime ) {
      for ($i = 0; $i <= $#response_time_report_interval; $i++) {
	if ($reqtime <= $response_time_report_interval[$i]) {
	  if (${"udp_reqtime$sortorder"}{$reqtime} > $max) {
	    $max = ${"udp_reqtime$sortorder"}{$reqtime};
	    $max_interval = "<= $response_time_report_interval[$i]";
	  }
	  $ordered_udp_req_time_time{"<= $response_time_report_interval[$i]"}
	    += $reqtime * $udp_reqtime{$reqtime};
	  $ordered_udp_req_time{"<= $response_time_report_interval[$i]"} +=
	    $udp_reqtime{$reqtime};
	  $ordered_udp_req_time_size{"<= $response_time_report_interval[$i]"}
	    += $udp_reqtime_size{$reqtime};
	  $ordered_udp_hit_req_time{"<= $response_time_report_interval[$i]"} +=
	    $udp_hit_reqtime{$reqtime};
	  $ordered_udp_hit_req_time_size{"<= $response_time_report_interval[$i]"}
	    += $udp_hit_reqtime_size{$reqtime};
	  if ( ${"ordered_udp_req_time$sortorder"}{"<= $response_time_report_interval[$i]"}
	      - (($i == 0 or
		!defined(${"ordered_udp_req_time$sortorder"}{"<= $response_time_report_interval[$i-1]"}))
		? 0
		: ${"ordered_udp_req_time$sortorder"}{"<= $response_time_report_interval[$i-1]"})
		  > $max) {
	    $max =
	      ${"ordered_udp_req_time$sortorder"}{"<= $response_time_report_interval[$i]"}
	      - (($i == 0 or
		!defined(${"ordered_udp_req_time$sortorder"}{"<= $response_time_report_interval[$i-1]"}))
		? 0
		: ${"ordered_udp_req_time$sortorder"}{"<= $response_time_report_interval[$i-1]"});
	    $max_interval = "<= $response_time_report_interval[$i]";
	  }
	}
      }
    }

    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    outimg($report_index) if ($outtype_graph);
    outheader( $report_index, 'time', ' request', '% ', 'hit-%', 'auto',
	      $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ',
	      'hit-%', 'auto');
    outseperator($report_index);

    writecache( $report_index, $ordered_udp_req_time_max_interval,
	       $ordered_udp_req_time_max );
    foreach $time_interval
      ( sort { $a =~ m/^(>|<=)\s*(\S+)$/;
	my $a1 = $2; $b =~ m/^(>|<=)\s*(\S+)$/;
	my $b1 = $2; return $a1 <=> $b1 } keys(%ordered_udp_req_time) ) {
      push @xaxis, $time_interval;
      push @yaxis1, $ordered_udp_req_time{$time_interval};
      push @yaxis2, $ordered_udp_req_time_size{$time_interval};
      push @yaxis3, $ordered_udp_req_time{$time_interval}
	? $ordered_udp_hit_req_time{$time_interval} /
	  $ordered_udp_req_time{$time_interval} : 0;
      push @yaxis4, $ordered_udp_req_time_size{$time_interval}
	? $ordered_udp_hit_req_time_size{$time_interval} /
	  $ordered_udp_req_time_size{$time_interval} : 0;

      writecache( $report_index, $time_interval,
		 $ordered_udp_req_time{$time_interval},
		 $ordered_udp_req_time_size{$time_interval},
		 $ordered_udp_hit_req_time{$time_interval},
		 $ordered_udp_hit_req_time_size{$time_interval},
		 $ordered_udp_req_time_time{$time_interval});
      outline( $report_index, $time_interval,
	      $ordered_udp_req_time{$time_interval},
	      100 * $ordered_udp_req_time{$time_interval} / $udp,
	      100 * $ordered_udp_hit_req_time{$time_interval} /
		$ordered_udp_req_time{$time_interval},
	      $ordered_udp_req_time{$time_interval}
		? $ordered_udp_req_time_time{$time_interval} /
		  (1000 * $ordered_udp_req_time{$time_interval}) : 0,
	      kilomegagigatera( $ordered_udp_req_time_size{$time_interval},
			       $format[5] ),
	      $udp_size ? 100 * $ordered_udp_req_time_size{$time_interval} /
		$udp_size : 0,
	      $ordered_udp_req_time_size{$time_interval}
		? 100 * $ordered_udp_hit_req_time_size{$time_interval} /
		$ordered_udp_req_time_size{$time_interval} : 0,
	      $ordered_udp_req_time_time{$time_interval}
		? $ordered_udp_req_time_size{$time_interval} /
		  (1.024 * $ordered_udp_req_time_time{$time_interval}) : 0 );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $udp, 100, 100 * $udp_hit / $udp,
	    $udp_time / (1000 * $udp),
	    kilomegagigatera( $udp_size, $format[5] ), 100,
	    $udp_size ? 100 * $udp_hit_size / $udp_size : 0,
	    $udp_time ? $udp_size / ( 1.024 * $udp_time ) : 0);
    outgraph( $report_index, \@graph_legend,
	     scalar(@response_time_report_interval), \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4 ) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%udp_reqtime, \%udp_reqtime_size, 0, 'udp') if $test;
    $max_value[$report_index] = "most frequent response time</td>\n    <td>"
      . htmlescape($max_interval) . "</td>\n    <td>";
    $max_value[$report_index] .= ($opt_O)
      ? kilomegagigatera($max, 6) . " Byte" : $max. " Requests";
  }
  outstop($report_index);

# TCP-Request duration distribution in msec
  @format = ( 16, 9, '%', '%', 'spr', 8, '%', '%', 'kbps' );
  $report_index = 18;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $tcp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    foreach $reqtime ( keys %tcp_reqtime ) {
      for ( $i = 0; $i <= $#response_time_report_interval; $i++ ) {
	if ($reqtime <= $response_time_report_interval[$i]) {
	  $ordered_tcp_req_time_time{"<= $response_time_report_interval[$i]"}
	    += $reqtime * $tcp_reqtime{$reqtime};
	  $ordered_tcp_req_time{"<= $response_time_report_interval[$i]"} +=
	    $tcp_reqtime{$reqtime};
	  $ordered_tcp_req_time_size{"<= $response_time_report_interval[$i]"}
	    += $tcp_reqtime_size{$reqtime};
	  $ordered_tcp_hit_req_time{"<= $response_time_report_interval[$i]"} +=
	    $tcp_hit_reqtime{$reqtime};
	  $ordered_tcp_hit_req_time_size{"<= $response_time_report_interval[$i]"}
	    += $tcp_hit_reqtime_size{$reqtime};
	  if ( ${"ordered_tcp_req_time$sortorder"}{"<= $response_time_report_interval[$i]"}
	      - (( $i == 0 or
		!defined(${"ordered_tcp_req_time$sortorder"}{"<= $response_time_report_interval[$i-1]"}))
	       ? 0
	       : ${"ordered_tcp_req_time$sortorder"}{"<= $response_time_report_interval[$i-1]"}) > $ordered_tcp_req_time_max) {
	    $ordered_tcp_req_time_max =
	      ${"ordered_tcp_req_time$sortorder"}{"<= $response_time_report_interval[$i]"}
	      - (( $i == 0 or
		!defined(${"ordered_tcp_req_time$sortorder"}{"<= $response_time_report_interval[$i-1]"}))
		? 0
		: ${"ordered_tcp_req_time$sortorder"}{"<= $response_time_report_interval[$i-1]"});
	      $ordered_tcp_req_time_max_interval =
		"<= $response_time_report_interval[$i]";
	  }
	}
      }
    }

    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    outimg($report_index) if ($outtype_graph);
    outheader( $report_index, 'time', ' request', '% ', 'hit-%', 'auto',
	    $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ', 'hit-%',
	      'auto');
    outseperator($report_index);

    writecache( $report_index, $ordered_tcp_req_time_max_interval,
	       $ordered_tcp_req_time_max );
    foreach $time_interval ( sort { $a =~ m/^(>|<=)\s*(\S+)$/;
      my $a1 = $2; $b =~ m/^(>|<=)\s*(\S+)$/;
      my $b1 = $2;
      return $a1 <=> $b1
    } keys(%ordered_tcp_req_time) ) {
      push @xaxis, $time_interval;
      push @yaxis1, $ordered_tcp_req_time{$time_interval};
      push @yaxis2, $ordered_tcp_req_time_size{$time_interval};
      push @yaxis3, $ordered_tcp_req_time{$time_interval}
	? $ordered_tcp_hit_req_time{$time_interval} /
	  $ordered_tcp_req_time{$time_interval} : 0;
      push @yaxis4, $ordered_tcp_req_time_size{$time_interval}
	? $ordered_tcp_hit_req_time_size{$time_interval} /
	  $ordered_tcp_req_time_size{$time_interval} : 0;
      writecache( $report_index, $time_interval,
		 $ordered_tcp_req_time{$time_interval},
		 $ordered_tcp_req_time_size{$time_interval},
		 $ordered_tcp_hit_req_time{$time_interval},
		 $ordered_tcp_hit_req_time_size{$time_interval},
		 $ordered_tcp_req_time_time{$time_interval});
      outline( $report_index, $time_interval,
	      $ordered_tcp_req_time{$time_interval},
	      100 * $ordered_tcp_req_time{$time_interval} / $tcp,
	      100 * $ordered_tcp_hit_req_time{$time_interval} /
		$ordered_tcp_req_time{$time_interval},
	      $ordered_tcp_req_time{$time_interval}
		? $ordered_tcp_req_time_time{$time_interval} /
		  (1000 * $ordered_tcp_req_time{$time_interval}) : 0,
	      kilomegagigatera( $ordered_tcp_req_time_size{$time_interval},
				$format[5] ),
	      $tcp_size
		? 100 * $ordered_tcp_req_time_size{$time_interval} / $tcp_size
		: 0,
	      $ordered_tcp_req_time_size{$time_interval}
		? 100 * $ordered_tcp_hit_req_time_size{$time_interval} /
		  $ordered_tcp_req_time_size{$time_interval} : 0,
	      $ordered_tcp_req_time_time{$time_interval}
		? $ordered_tcp_req_time_size{$time_interval} /
		  (1.024 * $ordered_tcp_req_time_time{$time_interval}) : 0 );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	    $tcp_time / (1000 * $tcp),
	    kilomegagigatera( $tcp_size, $format[5] ), 100,
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    $tcp_time ? $tcp_size / ( 1.024 * $tcp_time ) : 0);
    outgraph( $report_index, \@graph_legend,
	     scalar(@response_time_report_interval), \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4 ) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%tcp_reqtime, \%tcp_reqtime_size, 0, 'tcp') if $test;
    $max_value[$report_index] = "most frequent response time</td>
    <td>" . htmlescape($ordered_tcp_req_time_max_interval) . "</td>\n    <td>";
    $max_value[$report_index] .= ($opt_O)
      ? kilomegagigatera($ordered_tcp_req_time_max, 6) . " Byte"
      : $ordered_tcp_req_time_max. " Requests";
  }
  outstop($report_index);
}

# UDP Response code distribution
if ($opt_errorcode_distribution) {
  %err_code = geterrcode();
  @format = ( 46, 9, '%', 'off', 'off', 8, '%', 'off', 'off' );
  $report_index = 19;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
#  @format = ( 46, 9, '%', 8, '%' );
  outstart($report_index);
  if ( $udp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    outimg($report_index) if ($outtype_graph);
    outheader( $report_index, 'status-code', ' request', '% ', 'hit-%', 'auto',
	      $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ',
	      'hit-%', 'auto' );
    outseperator($report_index);

    foreach $code ( sort { $a cmp $b } keys %udp_code ) {
      push @xaxis, $code;
      push @yaxis1, $udp_code{$code};
      push @yaxis2, $udp_code_size{$code};
      push @yaxis3, $udp_code{$code}
	? $udp_hit_code{$code} / $udp_code{$code} : 0;
      push @yaxis4, $udp_code_size{$code}
	? $udp_hit_code_size{$code} / $udp_code_size{$code} : 0;
      writecache( $report_index, $code, $udp_code{$code},
		 $udp_code_size{$code}, $udp_hit_code{$code},
		 $udp_hit_code_size{$code}, $udp_code_time{$code});
      outline( $report_index,
	      defined($err_code{$code})
		? "$code ($err_code{$code})" : "$code (unknown)",
	      $udp_code{$code}, $udp ? 100 * $udp_code{$code} / $udp : 0,
	      $udp_code{$code}
		? 100 * $udp_hit_code{$code} / $udp_code{$code} : 0,
	      $udp_code{$code}
		? $udp_code_time{$code} / (1000 * $udp_code{$code}) : 0,
	      kilomegagigatera( $udp_code_size{$code}, $format[5] ),
	      $udp_size ? 100 * $udp_code_size{$code} / $udp_size : 0,
	      $udp_code_size{$code}
		? 100 * $udp_hit_code_size{$code} / $udp_code_size{$code} : 0,
	      $udp_code_time{$code}
		? $udp_hit_code_size{$code} / (1.024 * $udp_code_time{$code})
		: 0 );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $udp, 100, 100 * $udp_hit / $udp,
	    $udp_time / (1000 * $udp),
	    kilomegagigatera( $udp_size, $format[5] ), 100,
	    $udp_size ? 100 * $udp_hit_size / $udp_size : 0,
	    $udp_time ? $udp_size / ( 1.024 * $udp_time ) : 0);
    outgraph( $report_index, \@graph_legend, $x_scale, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4 ) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%udp_code, \%udp_code_size, \%udp_code_time, 'udp')
      if $test;
    my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
    if ($maxi and $xaxis[$maxi]) {
      $max_value[$report_index] = "most frequent response code</td>\n    <td>"
        . htmlescape($xaxis[$maxi]) . "</td>\n    <td>";
      $max_value[$report_index] .= ($opt_O)
        ? kilomegagigatera($max, 6) . " Byte" : $max. " Requests";
    }
  }
  outstop($report_index);

# TCP Response code distribution
  @format = ( 46, 9, '%', 'off', 'off', 8, '%', 'off', 'off' );
  $report_index = 20;
  @format = @{$formats[$report_index]} if (ref($formats[$report_index]));
  outstart($report_index);
  if ( $tcp == 0 ) {
    outline( $report_index, 'no matching requests' );
  } else {
    my (@xaxis, @yaxis1, @yaxis2, @yaxis3, @yaxis4);
    outimg($report_index) if ($outtype_graph);
    outheader( $report_index, 'status-code', ' request', '% ', 'hit-%', 'auto',
	      $outtype_unformatted ? " ${opt_U}Byte" : '  Byte', '% ',
	      'hit-%', 'auto' );
    outseperator($report_index);

    foreach $code ( sort { $a cmp $b } keys %tcp_code ) {
      push @xaxis, $code;
      push @yaxis1, $tcp_code{$code};
      push @yaxis2, $tcp_code_size{$code};
      push @yaxis3, $tcp_code{$code}
	? $tcp_hit_code{$code} / $tcp_code{$code} : 0;
      push @yaxis4, $tcp_code_size{$code}
	? $tcp_hit_code_size{$code} / $tcp_code_size{$code} : 0;
      writecache( $report_index, $code, $tcp_code{$code},
		 $tcp_code_size{$code}, $tcp_hit_code{$code},
		 $tcp_hit_code_size{$code}, $tcp_code_time{$code});
      outline( $report_index, defined($err_code{$code})
	      ? "$code ($err_code{$code})" : "$code (unknown)",
	      $tcp_code{$code}, $tcp ? 100 * $tcp_code{$code} / $tcp : 0,
	      $tcp_code{$code}
		? 100 * $tcp_hit_code{$code} / $tcp_code{$code} : 0,
	      $tcp_code{$code}
		? $tcp_code_time{$code} / (1000 * $tcp_code{$code}) : 0,
	      kilomegagigatera( $tcp_code_size{$code}, $format[5] ),
	      $tcp_size ? 100 * $tcp_code_size{$code} / $tcp_size : 0,
	      $tcp_code_size{$code}
		? 100 * $tcp_hit_code_size{$code} / $tcp_code_size{$code} : 0,
	      $tcp_code_time{$code}
		? $tcp_hit_code_size{$code} / (1.024 * $tcp_code_time{$code})
		: 0 );
    }
    outseperator($report_index);
    outline( $report_index, 'Sum', $tcp, 100, 100 * $tcp_hit / $tcp,
	    $tcp_time / (1000 * $tcp),
	    kilomegagigatera( $tcp_size, $format[5] ), 100,
	    $tcp_size ? 100 * $tcp_hit_size / $tcp_size : 0,
	    $tcp_time ? $tcp_size / ( 1.024 * $tcp_time ) : 0);
    outgraph( $report_index, \@graph_legend, $x_scale, \@xaxis, \@yaxis1,
	     \@yaxis2, \@yaxis3, \@yaxis4) if ($outtype_graph and $xaxis[0]);
    test( $report_index, \%tcp_code, \%tcp_code_size, \%tcp_code_time, 'tcp')
      if $test;
    my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
    if ($maxi and $xaxis[$maxi]) {
      $max_value[$report_index] = "most frequent response code</td>\n    <td>"
        . htmlescape($xaxis[$maxi]) . "</td>\n    <td>";
      $max_value[$report_index] .= ($opt_O)
        ? kilomegagigatera($max, 6) . " Byte" : $max. " Requests";
    }
  }
  outstop($report_index);
}

##################################################

close(CACHE);

# now print it out.
my $fh = *STDOUT;
if ($opt_output_path) {
  open( OUT, ">$path/$filename" )
    or die ("$0: can't open $path/$filename for writing: $!\n");
  $fh = *OUT;
}

if ( $outtype_html or $outtype_htmlembed ) {
  $out_head .= "\n  <table border=\"1\" summary=\"\">\n   <tr>
    <td class=\"TableHeadline\" colspan=\"4\">
     <h2 class=\"superhead\">Table of Content / Overview</h2>\n    </td>
   </tr>";
  if ($opt_S) {
    foreach ( split ( ",", $opt_S ) ) {
      outref($_) if $outref{$_};
    }
  } else {
    foreach ( 0 .. $#reports ) {
      outref($_) if $outref{$_};
    }
  }
  $out_head .= "\n  </table>";
}

print $fh $out_head;
if ($opt_S) {
  print $fh $out_body{'E'} if defined $out_body{'E'};
  foreach $index ( split ( ",", $opt_S ) ) {
    if ($outref{"$index"}) {
      if ( not defined( $out_body{$index} ) ) {
	outstart($index);
	outline( $index, 'no matching requests' );
	outstop($index);
      }
      print $fh $out_body{$index};
    }
  }
} else {
  foreach ( 'E', 0 .. $#reports ) {
    if ($outref{"$_"}) {
      print $fh $out_body{$_} if defined( $out_body{$_} );
    }
  }
}

if ( $outtype_html or $outtype_htmlembed ) {
  print $fh '
  <hr>
  <address>
   <a href="http://Calamaris.Cord.de/">Calamaris</a> $Revision: 2.99.1.3 $,
   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
   <a href="http://Cord.de/">Cord Beermann</a>.
  </address>
  <p>Calamaris comes with ABSOLUTELY NO WARRANTY. It is free software, and you
  are welcome to redistribute it under certain conditions.  See source for
  details.</p>
';
  print $fh "\n </body>\n</html>\n" if ($outtype_html);
} else {
  print $fh "\n\n";
  print $fh "-- \n" if ($outtype_mail);
  print $fh "$VERSION\n$COPYRIGHT\n";
}

print "$test_string\n" if $test;

sub kilomegagigatera {
  my ($value) = shift (@_);
  my ($length) = $outtype_unformatted ? 999 : shift (@_);
  if ( $value > 10**( $length + 8 ) or $opt_U eq 'T' ) {
    return sprintf( "%d%s", int( ( $value / 1024**4 ) + .5 ),
		   $outtype_unformatted ? '' : 'T' );
  } elsif ( $value > 10**( $length + 5 ) or $opt_U eq 'G' ) {
    return sprintf( "%d%s", int( ( $value / 1024**3 ) + .5 ),
		   $outtype_unformatted ? '' : 'G' );
  } elsif ( $value > 10**( $length + 2 ) or $opt_U eq 'M' ) {
    return sprintf( "%d%s", int( ( $value / 1024**2 ) + .5 ),
		   $outtype_unformatted ? '' : 'M' );
  } elsif ( $value > 10**($length) or $opt_U eq 'K' ) {
    return sprintf( "%d%s", int( ( $value / 1024 ) + .5 ),
		   $outtype_unformatted ? '' : 'K' );
  } else {
    return $value;
  }
}

sub removezerotimes {
  my ($size) = shift (@_);
  my ($time) = shift (@_);
  if ( $size == 0 or $time == 0 ) {
    return '-';
  } else {
    return $size / ( 1.024 * $time );
  }
}

sub getfqdn {
  my ($host) = @_;
  if ( $host =~
      m#^([^@]+@)?(::ffff:)?(([0-9][0-9]{0,2}\.){3}[0-9][0-9]{0,2}$)#o ) {
    $hostcache{$3} = addtonam($3) unless defined $hostcache{$3};
    return $1 . $hostcache{$3} if defined $1;
    return $hostcache{$3};
  } else {
    return $host;
  }
}

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

sub convertdate {
  my $date = shift (@_);
  if ($date) {
    my ( $sec, $min, $hour, $mday, $mon, $year ) =
      ( localtime($date) )[ 0, 1, 2, 3, 4, 5, 6 ];
    my $month = $months[$mon];
    $year += 1900;
    my $retdate =
      sprintf( "%02d.%s %02d %02d:%02d:%02d\n", $mday, $month,
	      substr( $year, -2 ), $hour, $min, $sec );
    chomp($retdate);
    return $retdate;
  } else {
    return '                  ';
  }
}

sub outref {
  my $name = shift (@_);
  if (defined($max_value[$name])) {
    $out_head .= "\n   <tr>
    <td><a href=\"#$name\">$reports[$name]</a></td>
    <td>$max_value[$name]</td>\n   </tr>";
  } else {
    $out_head .= "\n   <tr>
    <td><a href=\"#$name\">$reports[$name]</a></td>\n    <td>-</td>
    <td>-</td>\n    <td>no requests found</td>\n   </tr>";
  }
}

sub outstart {
  my $index = shift (@_);
  if ( $outtype_html or $outtype_htmlembed ) {
    $out_body{$index} .= "\n  <hr>
  <table border=\"1\" bgcolor=\"#FFFFFF\">";
    $out_body{$index} .= "\n   <tr>
    <td class=\"TableHeadline\" colspan=\"" . scalar(@format) . "\">
     <a name=\"$index\">$reports[$index]</a>\n    </td>\n   </tr>"
      unless ( $index eq 'E' );
  } else {
    $out_body{$index} .= "\n# $reports[$index]\n" unless ( $index eq 'E' );
  }
}

sub outheader {
  my $index = shift (@_);
  my $print;
  my $no = 0;
  $out_body{$index} .= "\n   <tr>" if ( $outtype_html or $outtype_htmlembed );
  foreach (@_) {
    $p = $_;
    $p = 'kB/sec' if ($format[$no] eq 'kbps' and $p eq 'auto');
    $p = 'sec/kB' if ($format[$no] eq 'spkb' and $p eq 'auto');
    $p = 'req/sec' if ($format[$no] eq 'rps' and $p eq 'auto');
    $p = 'sec/req' if ($format[$no] eq 'spr' and $p eq 'auto');
    $p = 'Byte/sec' if ($format[$no] eq 'bps' and $p eq 'auto');
    $p = 'sec/Byte' if ($format[$no] eq 'spb' and $p eq 'auto');
    $p = 'req/msec' if ($format[$no] eq 'rpms' and $p eq 'auto');
    $p = 'msec/req' if ($format[$no] eq 'mspr' and $p eq 'auto');
    if ($format[$no] eq 'off') { # do nothing
    } elsif ($outtype_unformatted) {
      $out_body{$index} .= "$p ";
    } elsif ( $outtype_html or $outtype_htmlembed ) {
      $p =~ s# +# #go;
      $p =~ s#(^ | $)##go;
      $p = '&nbsp;' if ( $p eq '' );
      $out_body{$index} .= "\n    <th class=\"TableDefinition\">$p</th>";
    } elsif ( $format[$no] eq '%' ) {
      $out_body{$index} .=
	sprintf( ' ' x ( 6 - length($p) ) ) . substr( $p, 0, 6 ) . ' ';
    } elsif ( $format[$no] =~ m/[bps|spb|spkb|rps|rpms|spr]$/ ) {
      $out_body{$index} .=
	sprintf( substr( $p, 0, 7 ) . ' ' x ( 7 - length($p) ) . ' ' );
    } else {
      $out_body{$index} .= sprintf( substr( $p, 0, $format[$no] )
		  . ' ' x ( $format[$no] - length($p) ) . ' ' );
    }
    $no++;
  }
  $out_body{$index} .= "\n";
  $out_body{$index} .= '   </tr>' if ( $outtype_html or $outtype_htmlembed );
}

sub outimg {
  my $index = shift;
  $out_body{$index} .= "\n  <tr>\n   <td colspan=\"" . scalar(@format) . "\">
    <img src=\"${index}.${opt_image_type}\" alt=\"Graphic: $reports[$index]\">\n   </td>
  </tr>";
}

sub outgraph {
  my $index = shift;
  my $legend_ref = shift;
  my $max_x_data = shift;
  my $xaxis_ref = shift;
  my $yaxis1_ref = shift;
  my $yaxis2_ref = shift;
  my $yaxis3_ref = shift;
  my $yaxis4_ref = shift;
  my ($factor0, $factor1, $unit0, $unit1, $min_x, $max_x);
  die( "$0: Couldn't load package calamaris::calBars3d,
  maybe it is not installed: $!\n" )
    unless (eval "require calamaris::calBars3d");

  if ($max_x_data < 0) {
    # show last $max_x_data Values
    $min_x = ($#{@$xaxis_ref} + $max_x_data < 0) ? 0
      : $#{@$xaxis_ref} + $max_x_data + 1;
    $max_x = $#{@$xaxis_ref};
  } else {
    # show first $max_x_data Values
    $min_x = 0;
    $max_x = ($#{@$xaxis_ref} > $max_x_data) ? $max_x_data : $#{@$xaxis_ref};
  }

  my $graph = calamaris::calBars3d->new($width, int($width/3*2));

  # check image-type
  if (! $verified) {
    @img_format = $graph->export_format;
    foreach ( @img_format ) {
      $format{$_} = 1;
    }
    if ( ! defined($format{$opt_image_type}) ) {
      # Image type not supported
      print STDERR "$0: $opt_image_type is not supported by GD::Graph!\n
Please use one of the supported image types: @img_format \n\n";
      exit(1);
    }
    $verified = 1; # don't do it on every 'sub outgraph' call
  }


  # 1 axis graph
  my @data = ([@$xaxis_ref[$min_x..$max_x]], [@$yaxis1_ref[$min_x..$max_x]]);
  ($factor0, $unit0) = getfactor(max(@$yaxis1_ref[$min_x..$max_x]),9);
  $yaxis1_ref = reformatarray($factor0, $yaxis1_ref) if ($factor0 > 1);
  my %graph_label = (x_label		=> '',
		     y_label		=> "$unit0 ${@$legend_ref}[0]",
		     title		=> '',
		     two_axes		=> '0',
		     x_labels_vertical	=> '1',
		     y_long_ticks	=> '1',
		     y_tick_number	=> '5',
		     x_ticks		=> '0',
		     box_axis		=> '0',
		     show_values	=> '0',
		     values_vertical	=> '0',
		     bar_spacing	=> '6',
		     set_spacing	=> '0',
		     shadowclr		=> 'lgray',
		     shadow_depth	=> '0',
		     boxclr		=> "$bg_color",
		     fgclr		=> "$text_color",
		     labelclr		=> "$text_color",
		     axislabelclr	=> "$text_color",
		     legendclr		=> "$text_color",
		     valuesclr		=> "$text_color",
		     textclr		=> "$text_color" );

  $graph->set(%graph_label) or die $graph->error;

  # 2 axis graph
  if (scalar(@$legend_ref) > 1) {
    ($factor1, $unit1) = getfactor(max(@$yaxis2_ref[$min_x..$max_x]),5);
    $yaxis2_ref = reformatarray($factor1, $yaxis2_ref) if ($factor1 > 1);
    push @data, [@$yaxis2_ref[$min_x..$max_x]];
    push @data, [@$yaxis3_ref[$min_x..$max_x]] if ref($yaxis3_ref);
    push @data, [@$yaxis4_ref[$min_x..$max_x]] if ref($yaxis4_ref);
    %graph_label = ( y1_label		=> "$unit0 ${@$legend_ref}[0]",
		    y2_label		=> "$unit1 ${@$legend_ref}[1]",
		    two_axes		=> '1',
		    bar_spacing		=> '0',
		    set_spacing		=> '6', );
  }

  $graph->set(%graph_label) or die $graph->error;
  $graph->set_text_clr("$text_color");
  $graph->set_legend(@$legend_ref);
  $graph->set( dclrs => [$column2_color, $column1_color] );
  open(IMG, ">$path/${index}.${opt_image_type}")
    or die ("$0: can't open $path/${index}.${opt_image_type} for writing: $!\n");
  binmode IMG;
  print IMG $graph->plot(\@data)->$opt_image_type;
  close(IMG);
}

sub maxi {
  my $max = -10e10; # an absolute small number
  my $i = 0;
  my @array = @_;
  for ( $i = 0; $i <= $#array; $i++ ) {
    if ($array[$i] > $max ) {
      $max = $array[$i];
      $maxi = $i;
    }
  }
  return $max, $maxi;
}

sub max {
  my $max = -10e10; # an absolute small number
  foreach (@_) {
    $max = $_ if $_ > $max
  }
  return $max;
}

sub reformatarray {
  my $factor = shift;
  my $array_ref = shift;
  my @array;
  foreach ( @$array_ref ) {
    push @array, $_/$factor
  }
  return \@array;
}

sub getfactor {
  my ($value) = shift (@_);
  my ($length) = $outtype_unformatted ? 999 : shift (@_);
  my ($factor, $unit);
  if ( $value > 10**( $length + 8 ) or $opt_U eq 'T' ) {
    $factor = 1024**4;
    $unit = 'Tera';
  } elsif ( $value > 10**( $length + 5 ) or $opt_U eq 'G' ) {
    $factor = 1024**3;
    $unit = 'Giga';
  } elsif ( $value > 10**( $length + 2 ) or $opt_U eq 'M' ) {
    $factor = 1024**2;
    $unit = 'Mega';
  } elsif ( $value > 10**($length) or $opt_U eq 'K' ) {
    $factor = 1024;
    $unit = 'Kilo';
  } else {
    $factor = 1;
    $unit = '';
  }
  return $factor, $unit;
}

sub outline {
  my $index = shift (@_);
  my $print;
  my $no = 0;
  $out_body{$index} .= "\n   <tr>" if ( $outtype_html or $outtype_htmlembed );
  foreach (@_) {
    $print = $_;
    if ($format[$no] eq 'off') { # do nothing
    } elsif ($outtype_unformatted) {
      $out_body{$index} .= "$print ";
    } elsif ( $outtype_html or $outtype_htmlembed ) {
      $print =~ s# +# #go;
      $print =~ s# $##go;
      $print =~ s#<#\&lt\;#go;
      $print =~ s#>#\&gt\;#go;
      if ( $no == 0 ) {
	unless ( $print =~ s#^ ##go ) {
	  $out_body{$index} .= "\n    <th align=\"left\">$print</th>";
	} else {
	  $out_body{$index} .= "\n    <td>&nbsp;&nbsp;$print</td>";
	}
      } elsif ( $format[$no] eq '%' ) {
	if ( $print eq '' or $print eq '-' ) {
	  $out_body{$index} .= "\n    <td>&nbsp;</td>";
	} else {
	  $out_body{$index} .=
	    sprintf( "\n    <td align=\"right\">%.2f</td>", $print );
	}
      } elsif ( $format[$no] =~ m/[bps|spb|spkb|rps|rpms|spr]$/ ) {
	my $factor = 1;
	$factor = 1000 if ($format[$no] =~ m#^rpms$#
			   or $format[$no] =~ m#^mspr$#);
	$factor = 1024 if ($format[$no] =~ m#^bps$#
			   or $format[$no] =~ m#^spb$#);
	if ( $print eq '' or $print eq '-' ) {
	  $out_body{$index} .= "\n    <td>&nbsp;</td>";
# kByte/sec
	} elsif ($format[$no] =~ m/bps/) {
	  $out_body{$index} .=
	    sprintf( "\n    <td align=\"right\">%.2f</td>", $print * $factor );
# sec/kByte
	} elsif ($format[$no] =~ m/spkb/ or $format[$no] =~ m/spb/) {
	  $out_body{$index} .= sprintf( "
    <td align=\"right\">%.2f</td>", 1 / ($factor * $print) );
# req/[m]sec
	} elsif ($format[$no] =~ m/rps/ or $format[$no] =~ m/rpms/) {
	  $out_body{$index} .= sprintf( "
    <td align=\"right\">%.2f</td>", 1 / ($print * $factor) );
# [m]sec/req
	} elsif ($format[$no] =~ m/spr/) {
	  $out_body{$index} .= sprintf( "
    <td align=\"right\">%.2f</td>", $print * $factor );
# %
	} else {
	  $out_body{$index} .= sprintf( "
    <td align=\"right\">%.2f</td>", $print );
	}
      } elsif ( $no == 1 and  $print !~ m#^[\d\.e\-\+]+$#o ) {
	$out_body{$index} .= sprintf( "
    <td align=\"right\">%s</td>", $print );
      } elsif ( $no == 1 or $print =~ m#^[\d\.e\-\+]+$#o ) {
	$out_body{$index} .= sprintf( "
    <td align=\"right\">%d</td>", $print );
      } else {
	if ($print) {
	  $out_body{$index} .= "\n    <td align=\"right\">$print</td>";
	} else {
	  $out_body{$index} .= "\n    <td align=\"right\">&nbsp;</td>";
	}
      }
    } else {
      if ( $no == 0 ) {
	if ( length($print) > $format[$no] ) {
	  $out_body{$index} .=
	    sprintf( $print . "\n" . ' ' x $format[$no] . ' ' );
	} else {
	  $out_body{$index} .=
	    sprintf( $print . ' ' x ( $format[$no] - length($print) ) . ' ' );
	}
      } elsif ( $format[$no] eq '%' ) {
	if ( $print eq '' or $print eq '-' ) {
	  $out_body{$index} .= ' ' x 7;
	} else {
	  $out_body{$index} .= sprintf( "%6.2f ", $print );
	}

      } elsif ( $format[$no] =~ m/[bps|spb|spkb|rps|rpms|spr]$/ ) {
	my $factor = 1;
	$factor = 1000 if ($format[$no] =~ m#^rpms$#
			   or $format[$no] =~ m#^mspr$#);
	$factor = 1024 if ($format[$no] =~ m#^bps$#
			   or $format[$no] =~ m#^spb$#);
	if ( $print eq '-' ) {
	  $out_body{$index} .= '    -   ';
# [k]Byte/sec
	} elsif ($format[$no] =~ m/bps/) {
	  if ( $print >= 10000 ) {
	    $out_body{$index} .= sprintf( "%7.0f ", $factor * $print );
	  } else {
	    $out_body{$index} .= sprintf( "%7.2f ", $factor * $print );
	  }
# sec/[k]Byte
	} elsif ($format[$no] =~ m/spkb/ or $format[$no] =~ m/spb/) {
	  if ( $print >= 1000 ) {
	    $out_body{$index} .= sprintf( "%7.0f ", 1 / ($factor * $print) );
	  } else {
	    $out_body{$index} .= sprintf( "%7.3f ", 1 / ($factor * $print) );
	  }
# req/[m]sec
	} elsif ($format[$no] =~ m/rps/ or $format[$no] =~ m/rpms/) {
	  if ( $print >= 10000 ) {
	    $out_body{$index} .= sprintf( "%7.0f ", 1 / ($factor * $print) );
	  } else {
	    $out_body{$index} .= sprintf( "%7.2f ", 1 / ($factor * $print) );
	  }
# [m]sec/req
	} elsif ($format[$no] =~ m/spr/) {
	  if ( $print >= 10000 ) {
	    $out_body{$index} .= sprintf( "%7.0f ", $factor * $print );
	  } else {
	    $out_body{$index} .= sprintf( "%7.2f ", $factor * $print );
	  }
	}
      } else {
	$print = sprintf( "%d", $print + .5 ) if $print =~ m#^[\d\.e\-\+]+$#o;
	$out_body{$index} .=
	  sprintf( ' ' x ( $format[$no] - length($print) ) )
		    . substr( $print, 0, $format[$no] ) . ' ';
      }
    }
    $no++;
  }
  $out_body{$index} .= "\n";
  $out_body{$index} .= '   </tr>' if ( $outtype_html or $outtype_htmlembed );
}

sub outseperator {
  my $index = shift (@_);
  my $print;
  $out_body{$index} .= "\n   <tr>" if ( $outtype_html or $outtype_htmlembed );
  foreach $print (@format) {
    next if $print eq 'off';
    if ($outtype_unformatted) {
      $out_body{$index} .= "--- ";
    } elsif ( $outtype_html or $outtype_htmlembed ) {
      $out_body{$index} .= "\n    <td></td>";
    } elsif ( $print eq '%' ) {
      $out_body{$index} .= sprintf( '-' x 6 . ' ' );
    } elsif ( $print =~ m/[bps|spb|spkb|rps|rpms|spr]$/ ) {
      $out_body{$index} .= sprintf( '-' x 7 . ' ' );
    } else {
      $out_body{$index} .= sprintf( '-' x $print . ' ' );
    }
  }
  $out_body{$index} .= "\n";
  $out_body{$index} .= '   </tr>' if ( $outtype_html or $outtype_htmlembed );
}

sub outstop {
  my $index = shift (@_);
  if ( $outtype_html or $outtype_htmlembed ) {
    $out_body{$index} .= "\n  </table>";
    $out_body{$index} .= "
  <p class=\"backToTop\"><a href=\"#top\">Back to Top</a></p>";
  }
}

sub writecache {
  print CACHE join ( '', @_ ) . "\n" if $opt_o;
}

sub geterrcode {
  my %err_code;
  $err_code{'000'} = 'Used mostly with UDP traffic';
  $err_code{'100'} = 'Continue';
  $err_code{'101'} = 'Switching Protocols';
  $err_code{'102'} = 'Processing';
  $err_code{'200'} = 'OK';
  $err_code{'201'} = 'Created';
  $err_code{'202'} = 'Accepted';
  $err_code{'203'} = 'Non-Authoritative Information';
  $err_code{'204'} = 'No Content';
  $err_code{'205'} = 'Reset Content';
  $err_code{'206'} = 'Partial Content';
  $err_code{'207'} = 'Multi Status';
  $err_code{'300'} = 'Multiple Choices';
  $err_code{'301'} = 'Moved Permanently';
  $err_code{'302'} = 'Moved Temporarily';
  $err_code{'303'} = 'See Other';
  $err_code{'304'} = 'Not Modified';
  $err_code{'305'} = 'Use Proxy';
  $err_code{'307'} = 'Temporary Redirect';
  $err_code{'400'} = 'Bad Request';
  $err_code{'401'} = 'Unauthorized';
  $err_code{'402'} = 'Payment Required';
  $err_code{'403'} = 'Forbidden';
  $err_code{'404'} = 'Not Found';
  $err_code{'405'} = 'Method Not Allowed';
  $err_code{'406'} = 'Not Acceptable';
  $err_code{'407'} = 'Proxy Authentication Required';
  $err_code{'408'} = 'Request Timeout';
  $err_code{'409'} = 'Conflict';
  $err_code{'410'} = 'Gone';
  $err_code{'411'} = 'Length Required';
  $err_code{'412'} = 'Precondition Failed';
  $err_code{'413'} = 'Request Entity Too Large';
  $err_code{'414'} = 'Request URI Too Large';
  $err_code{'415'} = 'Unsupported Media ';
  $err_code{'416'} = 'Request Range Not Satisfiable';
  $err_code{'417'} = 'Expectation Failed';
  $err_code{'424'} = 'Locked';
  $err_code{'424'} = 'Failed Dependency';
  $err_code{'433'} = 'Unprocessable Entity';
  $err_code{'500'} = 'Internal Server Error';
  $err_code{'501'} = 'Not Implemented';
  $err_code{'502'} = 'Bad Gateway';
  $err_code{'503'} = 'Service Unavailable';
  $err_code{'504'} = 'Gateway Timeout';
  $err_code{'505'} = 'HTTP Version Not Supported';
  $err_code{'507'} = 'Insufficient Storage';
  $err_code{'600'} = 'Squid header parsing error';
  return %err_code;
}

sub readconfig {
  # Default values
  undef($benchmark);
  undef($cache_input_file);
  undef($cache_output_file);
  undef($domain_report);
  undef($domain_report_limit);
  undef($domain_report_n_level);
  undef($errorcode_distribution_report);
  undef($hostname);
  undef($input_format);
  undef($ipfilter_exclude);
  undef($ipfilter_include);
  undef($logo);
  undef($meta);
  undef($no_input);
  undef($object_freshness_report);
  undef($output_file);
  undef($output_format);
  undef($output_path);
  undef($peak_report);
  undef($performance_report);
  undef($performance_report_adjust);
  undef($requester_report);
  undef($requester_report_no_dns_lookup);
  undef($requester_report_use_user_info);
  undef($requester_report_with_targets);
  undef($response_time_report);
  undef($show_reports);
  undef($size_distribution_report);
  undef($sort_order);
  undef($status_report);
  undef($time_interval);
  undef($type_report);
  undef($type_report_ignore_case);
  undef($unit);
  undef($verbose);
  @response_time_report_interval = qw( 0.001 0.01 0.02 0.05 0.1 0.2 0.5 1 2 5
				      10 20 50 100 200 500 1000 2000 5000 10000
				      20000 50000 100000 200000 500000 1000000
				      1e10 );
  # GRAPH SECTION
  $column1_color = '#6699cc';
  $column2_color = '#ff9900';
  $text_color    = '#222266';
  $bg_color      = '#ffffcc';
  $x_scale = 30;
  $image_type = 'png';
  $width = 600;
  $test = 0;
  $verified = 0;
  @graph_legend = ('Requests', 'Byte', 'Request Hit Rate', 'Byte Hit Rate');
  # HTML SECTION

  if ($opt_config_file) {
    my $file = $opt_config_file;
    my $return;
    unless ($return = do $file) {
      warn "$0: Parsing of $file failed: $@" if $@;
      warn "$0: do on $file failed: $!" unless defined $return;
      warn "$0: couldn't execute $file" unless $return;
    }
  }
  $opt_b = $benchmark unless $opt_b;
  $opt_i = $cache_input_file unless $opt_i;
  $opt_o = $cache_output_file unless $opt_o;
  $opt_d = $domain_report unless $opt_d;
  $opt_domain_report_limit = $domain_report_limit
    unless $opt_domain_report_limit;
  $opt_N = $domain_report_n_level unless $opt_N;
  $opt_errorcode_distribution = $errorcode_distribution_report
    unless $opt_errorcode_distribution;
  $opt_H = $hostname unless $opt_H;
  $opt_c = $type_report_ignore_case unless $opt_c;
  $opt_f = $input_format unless $opt_f;
  $opt_image_type = $image_type unless $opt_image_type;
  $opt_ipfilter_exclude = $ipfilter_exclude unless $opt_ipfilter_exclude;
  $opt_ipfilter_include = $ipfilter_include unless $opt_ipfilter_include;
  $opt_l = $logo unless $opt_l;
  $opt_M = $meta unless $opt_M;
  $opt_z = $no_input unless $opt_z;
  $opt_output_file = $output_file unless $opt_output_file;
  $opt_F = $output_format unless $opt_F;
  $opt_output_path = $output_path unless $opt_output_path;
  $opt_p = $peak_report unless $opt_p;
  $opt_P = $performance_report unless $opt_P;
  $opt_T = $performance_report_adjust unless $opt_T;
  $opt_r = $requester_report unless $opt_r;
  $opt_n = $requester_report_no_dns_lookup unless $opt_n;
  $opt_u = $requester_report_use_user_info unless $opt_u;
  $opt_R = $requester_report_with_targets unless $opt_R;
  $opt_response_time = $response_time_report
    unless $opt_response_time;
  undef($opt_response_time) unless scalar(@response_time_report_interval);
  $opt_S = $show_reports unless $opt_S;
  $opt_D = $size_distribution_report unless $opt_D;
  $opt_O = $sort_order unless $opt_O;
  $opt_s = $status_report unless $opt_s;
  $opt_I = $time_interval unless $opt_I;
  $opt_t = $type_report unless $opt_t;
  $opt_U = $unit unless $opt_U;
  $opt_v = $verbose unless $opt_v;

  # -M: if it's a file, read it and store it in $opt_M
  if ( defined($opt_M) and $opt_M !~ /\n/ and -e $opt_M ) {
    open(IN, "<$opt_M") or
      die "$0: Couldn't open file $opt_M for reading: $!\n";
    $opt_M = '';
    while (<IN>) {
      $opt_M .= $_
    }
  }
  # -l: if it's a file, read it and store it in $opt_l
  if ( defined($opt_l) and $opt_l !~ /\n/ and -e $opt_l ) {
    open(IN, "<$opt_l") or
      die "$0: Couldn't open file $opt_l for reading: $!\n";
    $opt_l = '';
    while (<IN>) {
      $opt_l .= $_
    }
  }
  if ( $opt_b and $opt_b < 1 ) {
    print STDERR "$0: wrong value at -b -option: \"$opt_b\"\n\n";
    $usage_err = 1;
  } else {
    $| = 1;
  }
  if ($opt_U) {
    unless ( $opt_U =~ m#^[KMGT]$# ) {
      print STDERR "$0: wrong value at -U -option: \"$opt_U\"\n\n";
      $usage_err = 1;
    }
  } else {
    $opt_U = '';
  }
  if ($opt_D) {
    if ( $opt_D <= 1 ) {
      print STDERR "$0: wrong value at -D -option: \"$opt_D\"\n\n";
      $usage_err = 1;
    }
  }
  if ($opt_H) {
    if ( $opt_H eq '1' or $opt_H eq 'lookup' ) {
      $host_name = hostname() . ' ';
    } else {
      $host_name = $opt_H . ' ';
    }
  } else {
    $host_name = '';
  }

  if ( $opt_N and $opt_N != -1 and $opt_N < 2 ) {
    print STDERR "$0: wrong value at -N -option: \"$opt_N\"\n\n";
    $usage_err = 1;
  }

  if ($opt_I) {
    use Time::Local;
    ( $interval_begin, $interval_end ) = split ( '-', $opt_I );
    if ( $interval_begin =~ m#^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$# ) {
      $interval_begin = timegm( $6, $5, $4, $3, $2 - 1, $1 - 1900 );
    } elsif ( $interval_begin eq '' ) {
      $interval_begin = 0;
    } else {
      print STDERR "$0: wrong value at -I -option: \"$opt_I\"\n\n";
      $usage_err = 1;
    }
    if ( $interval_end =~ m#^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$# ) {
      $interval_end = timegm( $6, $5, $4, $3, $2 - 1, $1 - 1900 );
    } elsif ( $interval_end eq '' ) {
      $interval_end = 9999999999;
    } else {
      print STDERR "$0: wrong value at -I -option: \"$opt_I\"\n\n";
      $usage_err = 1;
    }
    if ($interval_begin > $interval_end) {
      print STDERR "$0: wrong value at -I -option: \"$opt_I\".
 Interval begin newer than interval end!\n\n";
      $usage_err = 1;
    }
  }
  if ($opt_F) {
    foreach $output ( split ( /\s*,\s*/, $opt_F ) ) {
      if ( $output eq 'mail' ) {
	$outtype_mail = 1;
      } elsif ( $output eq 'html' ) {
	$outtype_html = 1;
      } elsif ( $output eq 'html-embed' ) {
	$outtype_htmlembed = 1;
      } elsif ( $output eq 'graph' ) {
	$outtype_graph = 1;
      } elsif ( $output eq 'unformatted' ) {
	$outtype_unformatted = 1;
      } else {
	print STDERR "$0: unknown output-format: $output\n\n";
	$usage_err = 1;
      }
    }
  }
  $path = $opt_output_path ? $opt_output_path : '.';
  $filename = $opt_output_file ? $opt_output_file : 'calamaris.txt';
  if ($outtype_graph) {
    $filename = $opt_output_file ? $opt_output_file : 'index.html';
    unless (defined($opt_output_path)) {
      print STDERR "$0: please use --output-path /path, when using
	-F 'graph'\n\n";
    }
  }

  $sortorder = '';
  $sortorder = '_size' if ($opt_O);
  if ($opt_a) {
    $opt_response_time = 1;
    $opt_errorcode_distribution = 1;
    $opt_D = 10 unless $opt_D;
    $opt_P = 60 unless $opt_P;
    $opt_d = 20 unless $opt_d;
    $opt_r = 20 unless $opt_r;
    $opt_s = 1;
    $opt_t = 20 unless $opt_t;
  }

  $opt_domain_report_limit = 0 unless $opt_domain_report_limit;
  $opt_N = 2 unless $opt_N;
  $opt_T = 0 unless $opt_T;
  $opt_r = $opt_R unless $opt_r;
  if ($object_freshness_report) {
    $opt_t = 20 unless $opt_t;
  }
  $P = $opt_P ? "$opt_P minute" : '60 minute';
  $P = $opt_P/60 . ' hour' if ( defined($opt_P) and ($opt_P % 60) == 0 and
				$opt_P >= 60 );
  $P = $opt_P/1440 . ' day' if ( defined($opt_P) and ($opt_P % 1440) == 0 and
				$opt_P >= 1440 );
  if ( $opt_N == -1 or $opt_N > 2 ) {
    if ( $opt_N == 3 ) {
      $N = '3rd';
    } elsif ( $opt_N == -1 ) {
      $N = 'all';
    } else {
      $N = $opt_N . 'th';
    }
  } else {
    $N = '2nd';
  }

  @reports = ( 'Summary', 'Incoming request peak per protocol',
	      'Incoming transfer volume peak per protocol',
	      'Incoming requests by method', 'Incoming UDP-requests by status',
	      'Incoming TCP-requests by status', 'Outgoing requests by status',
	      'Outgoing requests by destination',
	      "Request-destinations by ${N}-level-domain",
	      'Request-destinations by toplevel-domain',
	      'TCP-Request-protocol', 'Requested content-type',
	      'Requested extensions', 'Incoming UDP-requests by host',
	      'Incoming TCP-requests by host', 'Size Distribution Diagram',
	      "Performance in $P steps",
	      'UDP-Request duration distribution in msec',
	      'TCP-Request duration distribution in msec',
	      'UDP Response code distribution',
	      'TCP Response code distribution' );

  $VERSION='Calamaris $Revision: 2.99.1.3 $';

  $COPYRIGHT =
'Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Cord Beermann,
Calamaris comes with ABSOLUTELY NO WARRANTY. It is free software, and you are
welcome to redistribute it under certain conditions. See source for details.
Calamaris-Homepage: http://Calamaris.Cord.de/';

  $USAGE = '
Usage: cat log | ' . $0 . ' --config-file /path/to/calamaris.conf [switches]
       or
       cat log | ' . $0 . ' [switches]

--config-file file
	Not all reports and modification can be made through
	command-line-switches.  To use all options of Calamaris you\'ll have
	to use the configuration file. see the manpage for the
	configuration-file syntax.

Reports:
--all-useful-reports|-a
	extracts all useful reports available,
	--all-useful-reports equals
	  --size-distribution-report 10 \
	  --domain-report 20 \
	  --performance-report 60 \
	  --requester-report 20 \
	  --status-report \
	  --type-report 20 \
	  --response-time-report \
	  --errorcode-distribution-report
--domain-report|-d n
	show n Top-level and n second-level destinations, -1 = unlimited
--domain-report-limit n
	limit display of domains to those with n requests or more.
--domain-report-n-level|-N n
	change all 2nd-level-reports to n-level-reports. n can be any number
	from 2 up. -1 means full report.
--errorcode-distribution-report
	shows the Response code distribution over all objects
--peak-report|-p type
	measure peak requests
	old = make old request-peak mesurement
	new = make new request&byte-peak measurement
	(both slow Calamaris significantly down.)
--performance-report|-P n
	show throughput data for every n minutes
--performance-report-adjust|-T n
	adjust the Performance-Report in minutes
--requester-report|-r n
	show n Requesters, -1 = unlimited
--requester-report-no-dns-lookup|-n
	don\'t look IP-Numbers up
--requester-report-use-user-info|-u
	use ident information if available (*)
--requester-report-with-targets|-R n
	show n targets for every Requester, -1 = unlimited),
	implies --requester-report (*)
--response-time-report
	shows the time distribution over all objects
--size-distribution-report|-D n
	shows size-based distribution of requested objects, smaller numbers
	result in more verbose reports. choose 2, 10 or 100 for useful output.
	(You can also play with this ;-))
--status-report|-s
	show verbose status reports
--type-report|-t n
	show n content-type, n extensions and requested protocols,
	-1 = unlimited
--type-report-ignore-case|-c
	switch to case-insensitive (useful for extensions-report)

More reports and report-modifications are available via the
configuration-file. see manpage.


Input:
--input-format|-f type
	sets the type of input logfiles
	auto = tries to guess the input format (This is the Default)
	squid = Native-Logfile derived from Squid V1.1.beta26-V2.x
	squid-extended = Native-Logfile with log_mime_hdrs enabled
	  derived from Squid V1.1.beta26-V2.x (*) or Cisco Content Engines (*)
	  or Squid with SmartFilter-patch (*)
	squid-old = Native-Logfile derived from Squid V1.1.alpha1-V1.1.beta25
	nc = Squid-style Logfiles derived from NetCache V?? (<5.x)
	its = Logfiles derived from Inktomi Traffic Server
	elff = Extended Logfile Format (i.e Compaq Tasksmart, Novell Internet
					Caching System, NetCache 5.x)
	nse = Netscape Extended-1/2 Logfile Format
--ipfilter-exclude IP/range
	all IPs are analyzed, except IP/range. (*)
	Format: 1.1.1.1/32:1.1.2.0/24
		1.1.1.1/255.255.255.255:1.1.2.0/255.255.255.0
	IP list separated by \':\'
	This switch needs the perl Module NetAddr::IP.
--ipfilter-include IP/range
	no IPs are analyzed, except IP/range. (*)
	Format: see --ipfilter-exclude
--no-input|-z
	no input via stdin
--time-interval|-I t-t
	defines which time-interval should be parsed. t has to be the format
	yyyymmddhhmmss . omitting the beginning or ending is allowed.

Output: (Default is plain formatted text)
--hostname|-H name
	a name for the Output, -H \'lookup\' issues a lookup for the
	current host
--image-type
	Sets the image type to gif, png, jpeg, gd or gd2. Only usefull when
	--output-format graph is set. The supported images types are dependend
	on your GD::Graph installation.
--logo|-l string
	add this string to the head of the report. works only in combination
	with --output-format html
--meta|-M string/file
	includes the given strings in html-<head>. If a file is given, the
	file is included in html-<head>. works only in combination with
	--output-format html
--output-format|-F type
	output format (comma-seperated list)
	mail	    = mail format
	html	    = HTML format
	html-embed  = HTML format without HTML-headers
	graph	    = enable graphics, needs GD::Graph, only useful with html
		oder html-embed, see --output-path
	unformatted = plain unformatted output
--output-path /path
	output calamaris statistics to /path. In case of graph output, the
	graphics destination is /path and the filename is index.html, else it
	is calamaris.txt. If --output-path is not given, all graphics are
	written to the working directory.
--output-file filename
	alters the output-filename from the default (see --output-path)
--show-reports|-S list
	Shows only the defined reports (comma-seperated list) in the specified
	order. The following numbers are defined:
';
  foreach ( 0 .. $#reports ) {
    $USAGE .= "\t  $_\t$reports[$_]\n";
  }

  $USAGE .=
'	Note: only putting out one report does not speed up Calamaris as the
	internal operations were done based on the report-switches.  Default:
	Reports are displayed based on activated reports.
--sort-order|-O
	changes the sort order in the reports to request size, default is
	sorting by number of requests
--unit|-U string
	define the Unit for the Byte-values, else it will be auto.
	K(ilo), M(ega), G(iga), T(era)

There are more options to modify the output of Calamaris. Please see the
man-page.


Caching:
--cache-input-file|-i file
	input-datafile for caching, to add many files separate them
	with a \':\')
--cache-output-file|-o file
	output-datafile for caching, can be the same as --cache-input-file

Misc:
--benchmark|-b n
	prints a hash-sign (#) to stderr for each n lines processed
--copyright|-C
	prints the copyright
--help|-h
	prints out this message
--version|-V
	prints version-info

Debug:
--verbose|-v
	print information what Calamaris is doing. Useful for debugging.
--dump-loop|-L
	dumps the generated internal loop to STDERR for debugging.


(*) These options break the privacy of your users. Please read the README
on this.';

}

sub readcache {
### Read Cache.
  foreach $file ( split ':', $opt_i ) {
    open( CACHE, "$file" ) or die ("$0: can't open $file for reading: $!\n");
    while (<CACHE>) {
      chomp;
      next if m#^$#;
      @cache = split '';
      $x = shift (@cache);
      next unless ($x ne '');
      if ( $x eq '0' and $#cache == 39 ) {
	$time_begin = $cache[0] if $cache[0] < $time_begin;
	$time_end = $cache[1] if $cache[1] > $time_end;
	$counter += $cache[2];
	$size += $cache[3];
	$time += $cache[4];
	$invalid += $cache[5];
	$time_run += $cache[6];
	$udp += $cache[7];
	$udp_size += $cache[8];
	$udp_time += $cache[9];
	$udp_hit += $cache[10];
	$udp_hit_size += $cache[11];
	$udp_hit_time += $cache[12];
	$udp_miss += $cache[13];
	$udp_miss_size += $cache[14];
	$udp_miss_time += $cache[15];
	$tcp += $cache[16];
	$tcp_size += $cache[17];
	$tcp_time += $cache[18];
	$tcp_hit += $cache[19];
	$tcp_hit_size += $cache[20];
	$tcp_hit_time += $cache[21];
	$tcp_miss += $cache[22];
	$tcp_miss_size += $cache[23];
	$tcp_miss_time += $cache[24];
	$tcp_miss_none += $cache[25];
	$tcp_miss_none_size += $cache[26];
	$tcp_miss_none_time += $cache[27];
	$hier += $cache[28];
	$hier_size += $cache[29];
	$hier_time += $cache[30];
	$hier_direct += $cache[31];
	$hier_direct_size += $cache[32];
	$hier_direct_time += $cache[33];
	$hier_sibling += $cache[34];
	$hier_sibling_size += $cache[35];
	$hier_sibling_time += $cache[36];
	$hier_parent += $cache[37];
	$hier_parent_size += $cache[38];
	$hier_parent_time += $cache[39];
      } elsif ( $x eq '1' and $#cache == 17 ) {
	unless ( $peak_udp_sec == 0 ) {
	  warn("multiple cache files.\n") if $opt_v;
	  $peak_warn = 'Peak values are possibly wrong!';
	}
	if ( $peak_udp_sec < $cache[0] ) {
	  $peak_udp_sec = $cache[0];
	  $peak_udp_sec_time = $cache[1];
	}
	if ( $peak_udp_min < $cache[2] ) {
	  $peak_udp_min = $cache[2];
	  $peak_udp_min_time = $cache[3];
	}
	$peak_udp_hour{ $cache[5] } = 0
	  unless defined $peak_udp_hour{ $cache[5] };
	$peak_udp_hour{ $cache[5] } += $cache[4];
	if ( $peak_tcp_sec < $cache[6] ) {
	  $peak_tcp_sec = $cache[6];
	  $peak_tcp_sec_time = $cache[7];
	}
	if ( $peak_tcp_min < $cache[8] ) {
	  $peak_tcp_min = $cache[8];
	  $peak_tcp_min_time = $cache[9];
	}
	$peak_tcp_hour{ $cache[11] } = 0
	  unless defined $peak_tcp_hour{ $cache[11] };
	$peak_tcp_hour{ $cache[11] } += $cache[10];
	if ( $peak_all_sec < $cache[12] ) {
	  $peak_all_sec = $cache[12];
	  $peak_all_sec_time = $cache[13];
	}
	if ( $peak_all_min < $cache[14] ) {
	  $peak_all_min = $cache[14];
	  $peak_all_min_time = $cache[15];
	}
	$peak_all_hour{ $cache[17] } = 0
	  unless defined $peak_all_hour{ $cache[17] };
	$peak_all_hour{ $cache[17] } += $cache[16];
      } elsif ( $x eq '2' and $#cache == 23 ) {
	unless ( $peak_udp_sec == 0 ) {
	  warn("multiple cache files.\n") if $opt_v;
	  $peak_warn = 'Peak values are possibly wrong!';
	}
	if ( $peak_udp_sec < $cache[0] ) {
	  $peak_udp_sec = $cache[0];
	  $peak_udp_sec_time = $cache[1];
	}
	if ( $peak_udp_min < $cache[2] ) {
	  $peak_udp_min = $cache[2];
	  $peak_udp_min_time = $cache[3];
	}
	$peak_udp_hour{ $cache[5] } = 0
	  unless defined $peak_udp_hour{ $cache[5] };
	$peak_udp_hour{ $cache[5] } += $cache[4];
	$peak_udp_hour_size{ $cache[7] } = 0
	  unless defined $peak_udp_hour_size{ $cache[7] };
	$peak_udp_hour_size{ $cache[7] } += $cache[6];
	if ( $peak_tcp_sec < $cache[8] ) {
	  $peak_tcp_sec = $cache[8];
	  $peak_tcp_sec_time = $cache[9];
	}
	if ( $peak_tcp_min < $cache[10] ) {
	  $peak_tcp_min = $cache[10];
	  $peak_tcp_min_time = $cache[11];
	}
	$peak_tcp_hour{ $cache[13] } = 0
	  unless defined $peak_tcp_hour{ $cache[13] };
	$peak_tcp_hour{ $cache[13] } += $cache[12];
	$peak_tcp_hour_size{ $cache[15] } = 0
	  unless defined $peak_tcp_hour_size{ $cache[15] };
	$peak_tcp_hour_size{ $cache[15] } += $cache[14];
	if ( $peak_all_sec < $cache[16] ) {
	  $peak_all_sec = $cache[16];
	  $peak_all_sec_time = $cache[17];
	}
	if ( $peak_all_min < $cache[18] ) {
	  $peak_all_min = $cache[18];
	  $peak_all_min_time = $cache[19];
	}
	$peak_all_hour{ $cache[21] } = 0
	  unless defined $peak_all_hour{ $cache[21] };
	$peak_all_hour{ $cache[21] } += $cache[20];
	$peak_all_hour_size{ $cache[23] } = 0
	  unless defined $peak_all_hour_size{ $cache[23] };
	$peak_all_hour_size{ $cache[23] } += $cache[22];
      } elsif ( $x eq '3' and $#cache == 3 ) {
	$y = shift (@cache);
	$method{$y} = $method_size{$y} = $method_time{$y} = 0
	  unless defined $method{$y};
	$method{$y} += $cache[0];
	$method_size{$y} += $cache[1];
	$method_time{$y} += $cache[2];
      } elsif ( $x eq '4.1' and $#cache == 3 ) {
	$y = shift (@cache);
	$udp_hit{$y} = $udp_hit_size{$y} = $udp_hit_time{$y} = 0
	  unless defined $udp_hit{$y};
	$udp_hit{$y} += $cache[0];
	$udp_hit_size{$y} += $cache[1];
	$udp_hit_time{$y} += $cache[2];
      } elsif ( $x eq '4.2' and $#cache == 3 ) {
	$y = shift (@cache);
	$udp_miss{$y} = $udp_miss_size{$y} = $udp_miss_time{$y} = 0
	  unless defined $udp_miss{$y};
	$udp_miss{$y} += $cache[0];
	$udp_miss_size{$y} += $cache[1];
	$udp_miss_time{$y} += $cache[2];
      } elsif ( $x eq '5.1' and $#cache == 3 ) {
	$y = shift (@cache);
	$tcp_hit{$y} = $tcp_hit_size{$y} = $tcp_hit_time{$y} = 0
	  unless defined $tcp_hit{$y};
	$tcp_hit{$y} += $cache[0];
	$tcp_hit_size{$y} += $cache[1];
	$tcp_hit_time{$y} += $cache[2];
      } elsif ( $x eq '5.2' and $#cache == 3 ) {
	$y = shift (@cache);
	$tcp_miss{$y} = $tcp_miss_size{$y} = $tcp_miss_time{$y} = 0
	  unless defined $tcp_miss{$y};
	$tcp_miss{$y} += $cache[0];
	$tcp_miss_size{$y} += $cache[1];
	$tcp_miss_time{$y} += $cache[2];
      } elsif ( $x eq '5.3' and $#cache == 3 ) {
	$y = shift (@cache);
	$tcp_miss_none{$y} = $tcp_miss_none_size{$y} =
	  $tcp_miss_none_time{$y} = 0
	  unless defined $tcp_miss_none{$y};
	$tcp_miss_none{$y} += $cache[0];
	$tcp_miss_none_size{$y} += $cache[1];
	$tcp_miss_none_time{$y} += $cache[2];
      } elsif ( $x eq '6.1' and $#cache == 3 ) {
	$y = shift (@cache);
	$hier_direct{$y} = $hier_direct_size{$y} = $hier_direct_time{$y} = 0
	  unless defined $hier_direct{$y};
	$hier_direct{$y} += $cache[0];
	$hier_direct_size{$y} += $cache[1];
	$hier_direct_time{$y} += $cache[2];
      } elsif ( $x eq '6.2' and $#cache == 3 ) {
	$y = shift (@cache);
	$hier_sibling{$y} = $hier_sibling_size{$y} = $hier_sibling_time{$y} = 0
	  unless defined $hier_sibling{$y};
	$hier_sibling{$y} += $cache[0];
	$hier_sibling_size{$y} += $cache[1];
	$hier_sibling_time{$y} += $cache[2];
      } elsif ( $x eq '6.3' and $#cache == 3 ) {
	$y = shift (@cache);
	$hier_parent{$y} = $hier_parent_size{$y} = $hier_parent_time{$y} = 0
	  unless defined $hier_parent{$y};
	$hier_parent{$y} += $cache[0];
	$hier_parent_size{$y} += $cache[1];
	$hier_parent_time{$y} += $cache[2];
      } elsif ( $x eq '7.1' and $#cache == 3 ) {
	$y = shift (@cache);
	$hier_neighbor{$y} = $hier_neighbor_size{$y} =
	  $hier_neighbor_time{$y} = 0
	  unless defined $hier_neighbor{$y};
	$hier_neighbor{$y} += $cache[0];
	$hier_neighbor_size{$y} += $cache[1];
	$hier_neighbor_time{$y} += $cache[2];
      } elsif ( $x eq '7.2' and $#cache == 4 ) {
	$y = shift (@cache);
	$z = shift (@cache);
	$hier_neighbor_status{$y}{$z} = $hier_neighbor_status_size{$y}{$z} =
	  $hier_neighbor_status_time{$y}{$z} = 0
	  unless defined $hier_neighbor_status{$y}{$z};
	$hier_neighbor_status{$y}{$z} += $cache[0];
	$hier_neighbor_status_size{$y}{$z} += $cache[1];
	$hier_neighbor_status_time{$y}{$z} += $cache[2];
      } elsif ( $x eq '8' and $#cache == 5 ) {
	$y = shift (@cache);
	$tcp_urlhost{$y} = $tcp_urlhost_size{$y} = $tcp_urlhost_time{$y} =
	  $tcp_hit_urlhost{$y} = $tcp_hit_urlhost_size{$y} = 0
	  unless defined $tcp_urlhost{$y};
	$tcp_urlhost{$y} += $cache[0];
	$tcp_urlhost_size{$y} += $cache[1];
	$tcp_hit_urlhost{$y} += $cache[2];
	$tcp_hit_urlhost_size{$y} += $cache[3];
	$tcp_urlhost_time{$y} += $cache[4];
      } elsif ( $x eq '9' and $#cache == 5 ) {
	$y = shift (@cache);
	$tcp_urltld{$y} = $tcp_urltld_size{$y} = $tcp_urltld_time{$y} =
	  $tcp_hit_urltld{$y} = $tcp_hit_urltld_size{$y} = 0
	  unless defined $tcp_urltld{$y};
	$tcp_urltld{$y} += $cache[0];
	$tcp_urltld_size{$y} += $cache[1];
	$tcp_hit_urltld{$y} += $cache[2];
	$tcp_hit_urltld_size{$y} += $cache[3];
	$tcp_urltld_time{$y} += $cache[4];
      } elsif ( $x eq '10' and $#cache == 5 ) {
	$y = shift (@cache);
	$tcp_urlprot{$y} = $tcp_urlprot_size{$y} = $tcp_urlprot_time{$y} =
	  $tcp_hit_urlprot{$y} = $tcp_hit_urlprot_size{$y} = 0
	  unless defined $tcp_urlprot{$y};
	$tcp_urlprot{$y} += $cache[0];
	$tcp_urlprot_size{$y} += $cache[1];
	$tcp_hit_urlprot{$y} += $cache[2];
	$tcp_hit_urlprot_size{$y} += $cache[3];
	$tcp_urlprot_time{$y} += $cache[4];
      } elsif ( $x eq '11' and $#cache == 5 ) {
	$y = shift (@cache);
	$tcp_content{$y} = $tcp_content_size{$y} = $tcp_content_time{$y} =
	  $tcp_hit_content{$y} = $tcp_hit_content_size{$y} = 0
	  unless defined $tcp_content{$y};
	$tcp_content{$y} += $cache[0];
	$tcp_content_size{$y} += $cache[1];
	$tcp_hit_content{$y} += $cache[2];
	$tcp_hit_content_size{$y} += $cache[3];
	$tcp_content_time{$y} += $cache[4];
      } elsif ( $x eq '12' and $#cache == 10 ) {
	$y = shift (@cache);
	$tcp_urlext{$y} = $tcp_urlext_size{$y} = $tcp_urlext_time{$y} =
	  $tcp_hit_urlext{$y} = $tcp_hit_urlext_size{$y} = 0
	  unless defined $tcp_urlext{$y};
	$tcp_urlext{$y} += $cache[0];
	$tcp_urlext_size{$y} += $cache[1];
	$tcp_hit_urlext{$y} += $cache[2];
	$tcp_hit_urlext_size{$y} += $cache[3];
	$tcp_urlext_time{$y} += $cache[4];
	$tcp_urlext_fresh{$y} += $cache[5];
	$tcp_urlext_stale{$y} += $cache[6];
	$tcp_urlext_refresh{$y} += $cache[7];
	$tcp_urlext_mod{$y} += $cache[8];
	$tcp_urlext_unmod{$y} += $cache[9];
      } elsif ( $x eq '13.1' and $#cache == 5 ) {
	$y = shift (@cache);
	$udp_requester{$y} = $udp_requester_size{$y} =
	  $udp_requester_time{$y} = $udp_hit_requester{$y} =
	  $udp_hit_requester_size{$y} = 0
	  unless defined $udp_requester{$y};
	$udp_requester{$y} += $cache[0];
	$udp_requester_size{$y} += $cache[1];
	$udp_requester_time{$y} += $cache[2];
	$udp_hit_requester{$y} += $cache[3];
	$udp_hit_requester_size{$y} += $cache[4];
      } elsif ( $x eq '14.1' and $#cache == 5 ) {
	$y = shift (@cache);
	$tcp_requester{$y} = $tcp_requester_size{$y} =
	  $tcp_requester_time{$y} = $tcp_hit_requester{$y} =
	  $tcp_hit_requester_size{$y} = 0
	  unless defined $tcp_requester{$y};
	$tcp_requester{$y} += $cache[0];
	$tcp_requester_size{$y} += $cache[1];
	$tcp_requester_time{$y} += $cache[2];
	$tcp_hit_requester{$y} += $cache[3];
	$tcp_hit_requester_size{$y} += $cache[4];
      } elsif ( $x eq '16' and $#cache == 15 ) {
	$y = shift (@cache);
	$perf_counter{$y} += $cache[0];
	$perf_size{$y} += $cache[1];
	$perf_time{$y} += $cache[2];
	$perf_tcp_hit_size{$y} += $cache[3];
	$perf_tcp_hit_time{$y} += $cache[4];
	$perf_tcp_miss_size{$y} += $cache[5];
	$perf_tcp_miss_time{$y} += $cache[6];
	$perf_hier_direct_size{$y} += $cache[7];
	$perf_hier_direct_time{$y} += $cache[8];
	$perf_hier_sibling_size{$y} += $cache[9];
	$perf_hier_sibling_time{$y} += $cache[10];
	$perf_hier_parent_size{$y} += $cache[11];
	$perf_hier_parent_time{$y} += $cache[12];
	$perf_requester{$y} += $cache[13];
	$perf_tcp_hit{$y} += $cache[14];
      } elsif ( $x eq '13.2' and $#cache == 6 ) {
	$y = shift (@cache);
	$z = shift (@cache);
	$udp_requester_urlhost{$y}{$z} = $udp_requester_urlhost_size{$y}{$z} =
	  $udp_requester_urlhost_time{$y}{$z} =
	  $udp_hit_requester_urlhost{$y}{$z} =
	  $udp_hit_requester_urlhost_size{$y}{$z} = 0
	  unless defined $udp_requester_urlhost{$y}{$z};
	$udp_requester_urlhost{$y}{$z} += $cache[0];
	$udp_requester_urlhost_size{$y}{$z} += $cache[1];
	$udp_requester_urlhost_time{$y}{$z} += $cache[2];
	$udp_hit_requester_urlhost{$y}{$z} += $cache[3];
	$udp_hit_requester_urlhost_size{$y}{$z} += $cache[4];
      } elsif ( $x eq '14.2' and $#cache == 6 ) {
	$y = shift (@cache);
	$z = shift (@cache);
	$tcp_requester_urlhost{$y}{$z} = $tcp_requester_urlhost_size{$y}{$z} =
	  $tcp_requester_urlhost_time{$y}{$z} =
	  $tcp_hit_requester_urlhost{$y}{$z} =
	  $tcp_hit_requester_urlhost_size{$y}{$z} = 0
	  unless defined $tcp_requester_urlhost{$y}{$z};
	$tcp_requester_urlhost{$y}{$z} += $cache[0];
	$tcp_requester_urlhost_size{$y}{$z} += $cache[1];
	$tcp_requester_urlhost_time{$y}{$z} += $cache[2];
	$tcp_hit_requester_urlhost{$y}{$z} += $cache[3];
	$tcp_hit_requester_urlhost_size{$y}{$z} += $cache[4];
      } elsif ( $x eq '15' and $#cache == 5 ) {
	$y = shift (@cache);
	$tcp_distribution{$y} = $tcp_distribution_size{$y} =
	  $tcp_distribution_time{$y} = $tcp_hit_distribution{$y} =
	  $tcp_hit_distribution_size{$y} = 0
	  unless defined $tcp_distribution{$y};
	$tcp_distribution{$y} += $cache[0];
	$tcp_distribution_size{$y} += $cache[1];
	$tcp_distribution_time{$y} += $cache[2];
	$tcp_hit_distribution{$y} += $cache[3];
	$tcp_hit_distribution_size{$y} += $cache[4];
# read max values
      } elsif ( $x eq '18' and $#cache == 1 ) {
	$ordered_tcp_req_time_max_interval = $cache[0];
	$ordered_tcp_req_time_max = $cache[1];
      } elsif ( $x eq '18' and $#cache == 5 ) {
	$y = shift (@cache);
	$ordered_tcp_req_time{$y} = $ordered_tcp_req_time_size{$y} =
	  $ordered_tcp_hit_req_time{$y} = $ordered_tcp_hit_req_time_size{$y} =
	  $ordered_tcp_req_time_time{$y} = 0
	  unless defined $ordered_tcp_req_time{$y};
	$ordered_tcp_req_time{$y} += $cache[0];
	$ordered_tcp_req_time_size{$y} += $cache[1];
	$ordered_tcp_hit_req_time{$y} += $cache[2];
	$ordered_tcp_hit_req_time_size{$y} += $cache[3];
	$ordered_tcp_req_time_time{$y} += $cache[4];
# read max values
      } elsif ( $x eq '17' and $#cache == 1 ) {
	$ordered_udp_req_time_max_interval = $cache[0];
	$ordered_udp_req_time_max = $cache[1];
      } elsif ( $x eq '17' and $#cache == 5 ) {
	$y = shift (@cache);
	$ordered_udp_req_time{$y} = $ordered_udp_req_time_size{$y} =
	  $ordered_udp_hit_req_time{$y} = $ordered_udp_hit_req_time_size{$y} =
	  $ordered_udp_req_time_time{$y} = 0
	  unless defined $ordered_udp_req_time{$y};
	$ordered_udp_req_time{$y} += $cache[0];
	$ordered_udp_req_time_size{$y} += $cache[1];
	$ordered_udp_hit_req_time{$y} += $cache[2];
	$ordered_udp_hit_req_time_size{$y} += $cache[3];
	$ordered_udp_req_time_time{$y} += $cache[4];
      } elsif ( $x eq '19' and $#cache == 5 ) {
	$y = shift (@cache);
	$udp_code{$y} = $udp_code_size{$y} = 0
	  unless defined $udp_code{$y};
	$udp_code{$y} += $cache[0];
	$udp_code_size{$y} += $cache[1];
	$udp_hit_code{$y} += $cache[2];
	$udp_hit_code_size{$y} += $cache[3];
	$udp_code_time{$y} += $cache[4];
      } elsif ( $x eq '20' and $#cache == 5 ) {
	$y = shift (@cache);
	$tcp_code{$y} = $tcp_code_size{$y} = 0
	  unless defined $tcp_code{$y};
	$tcp_code{$y} += $cache[0];
	$tcp_code_size{$y} += $cache[1];
	$tcp_hit_code{$y} += $cache[2];
	$tcp_hit_code_size{$y} += $cache[3];
	$tcp_code_time{$y} += $cache[4];
      } else {
	print STDERR "can't parse cache-line: \"$x @cache\"\n";
	$cache_warn = 1 if ( $x =~ m#^[A-Z]$# );
      }
    }
    close(CACHE);
  }
}

sub htmlescape {
  my $toencode = shift;
  return undef unless defined($toencode);
  $toencode=~s/&/&amp;/gso;
  $toencode=~s/\"/&quot;/gso;
  $toencode=~s/>/&gt;/gso;
  $toencode=~s/</&lt;/gso;
  return $toencode;
}

sub test {
  my $report_index = shift;
  my $counter_ref = shift;
  my $size_ref = shift;
  my $time_ref = shift;
  my $code = shift;
  my $size = 0;
  my $count = 0;
  my $time = 0;
  $test_string .= "Index: $report_index, $reports[$report_index]\n";
  foreach ( keys %$counter_ref ) {
    $count+= ${%$counter_ref}{$_} if ref($counter_ref);
    $size += ${%$size_ref}{$_} if ref($size_ref);
    $time += ${%$time_ref}{$_} if ref($time_ref);
  }
  if (ref($counter_ref)) {
    if ($count == ${"${code}"}) {
      $test_string .= "Count: OK\n";
    } else {
      $test_string .= "Count: not OK\t$count neq " . ${"${code}"} . "\n";
    }
  }
  if (ref($size_ref)) {
    if ($size == ${"${code}_size"}) {
      $test_string .= "Size:  OK\n";
    } else {
      $test_string .= "Size:  not OK\t$size neq " . ${"${code}_size"} . "\n";
    }
  }
  if (ref($time_ref)) {
    if (int($time*100+.5)/100 == int(${"${code}_time"}*100+.5)/100) {
      $test_string .= "Time:  OK\n";
    } else {
      $test_string .= "Time:  not OK\t$time neq " . ${"${code}_time"} . "\n";
    }
  }
  $test_string .= "\n";
}

# find greatest common divisor
sub gcd {
  use integer;
  my $a = shift;
  my $b = shift;
  my $gcd = ($a >= $b) ? $a : $b;
  my $next = ($a >= $b) ? $b : $a;
  return $a, $b if !$gcd;

  if ($next) {
    foreach (1..3) { # reduces accuracy, but increases readability :-)
      my $r = $gcd % $next;
      $r += $next if $r < 0; # fix for % in integer mode
      $gcd = $next;
      $next = $r;
      last if ($next == 0);
    }
  }
  return $a/$gcd, $b/$gcd;
}

sub uri_unescape {
  # Note from RFC1630:  "Sequences which start with a percent sign
  # but are not followed by two hexadecimal characters are reserved
  # for future extension"
  my $str = shift;
  if (@_ && wantarray) {
    # not executed for the common case of a single argument
    my @str = ($str, @_);  # need to copy
    foreach (@str) {
      s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg
    }
    return @str;
  }
  $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
  $str;
}
