#!/usr/bin/perl -w
#
# DESCRIPTION: calamaris - statistic package for diverse Proxy-Cache-Servers
#
$VERSION = '2.99.4.8';
#
# Copyright (C) 1997-2006, 2013, 2015, 2017, 2021, 2024
#	Cord Beermann
#
# URL: https://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>
#

# Als a big thanks to all contributors, bug reporters, and feature
# requesters:
# Please see the CONTRIBUTORS file.

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

# if you can't use Graphics, you should point this to a directory where calamaris/*.pm is located.
# use lib '/usr/local';

use Getopt::Long;

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,
	   "generate-index" => \$opt_generate_index,
	   "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-file-prefix=s" => \$opt_output_file_prefix,
	   "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();

unless ($opt_n) {
  use Socket qw(:DEFAULT inet_pton getnameinfo);
}

print "$USAGE\n\n" if $opt_h;
print "Calamaris $VERSION\n\n" if $opt_V;
print "$COPYRIGHT\n\n$LICENSE\n\n$HOMEPAGE\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 = 1e10;
@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);
$loop = '';

readcache() if ($opt_i);

unless ($opt_z) {
  while ( defined( $line = <> ) ) {
    if ( not defined $opt_f or $opt_f eq 'auto' ) {
      if ( $line =~
	  m#^\s*\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;
# Disable Netcache from automatic detection
#      } 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) {
    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 .= '
  $line =~ s#^\s+##;
  ( $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/ \[[^\[\]]*\]\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' or $_ eq 'sc-filter-result'
	       or $_ eq 'sc-filter-category' or $_ eq 'x-virus-id' ) {
	$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-scheme' ) {
	$tmpline1 .= '(\S+)';
	$tmpline2 .= '
    $log_cs_uri_scheme = $' . ++$offset . ';';
      } elsif ( $_ eq 'cs-host' ) {
	$tmpline1 .= '(\S+)';
	$tmpline2 .= '
    $log_cs_host = $' . ++$offset . ';';
      } elsif ( $_ eq 'cs-uri-path' ) {
	$tmpline1 .= '(\S+)';
	$tmpline2 .= '
    $log_cs_uri_path = $' . ++$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 'cs(Content-Type)' ) {
	$tmpline1 .= '(\"[^\"]*\"|-)';
	$tmpline2 .= '
    $log_content = $' . ++$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 's-hierarchy' ) {
	$tmpline1 .= '([\w\-\.\/]+)';
	$tmpline2 .= '
    $log_hierarchy = $' . ++$offset . ';';
      } elsif ( $_ eq 's-action' ) {
	$tmpline1 .= '([\w\-\.\/]+)';
	$tmpline2 .= '
    $log_s_action = $' . ++$offset . ';';
      } elsif ( $_ eq 's-supplier-name' ) {
	$tmpline1 .= '([\w\-\.\/]+)';
	$tmpline2 .= '
    $log_supplier_name = $' . ++$offset . ';';
      } 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 .= '
    if ($log_s_action) {
      $log_status = "$log_s_action/$log_code";
    } else {
      $log_status = "$log_cached/$log_code";
    }' unless defined $log_status;
    $loop .= '
    if ( $log_hierarchy and $log_supplier_name ) {
      $log_hier = "$log_hierarchy/$log_supplier_name";
    } elsif ( $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 = "$log_cs_uri_scheme://$log_cs_host/$log_cs_uri_path"
    unless ($log_url);
  $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 =~ s#\s+.*##;
  $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 (@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|REDIRECT|REFRESH_UNMODIFIED)#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|UNKNOWN|^\-$#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|UNKNOWN|^\-$#o ) {
      $hier++;
      $hier_size += $log_size;
      $hier_time += $log_reqtime;
      if ( $log_hier_method =~ m#DIRECT|ORIGINAL_DST|PINNED|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|STANDBY_POOL#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 ;-)

if ( $counter == 0 ) {
  print "\nno requests found\n";
  exit(0);
}
$loginterval = convertdate($time_begin) . ' - ' . convertdate($time_end);
$timestamp = convertdate($time_begin,1) . '-' . convertdate($time_end,1);

if ($path) {
  $path =~ s#%t#$timestamp#g if $timestamp;
  $path =~ s#%h#$host_name#g if $host_name;
  $path =~ s#%%#%#g;
}
if ($filename) {
  $filename =~ s#%t#$timestamp#g if $timestamp;
  $filename =~ s#%h#$host_name#g if $host_name;
  $filename =~ s#%%#%#g;
}
if ($file_prefix) {
  $file_prefix =~ s#%t#$timestamp#g if $timestamp;
  $file_prefix =~ s#%h#$host_name#g if $host_name;
  $file_prefix =~ s#%%#%#g;
}

open( CACHE, ">$opt_o" ) or die ("$0: can't open $opt_o for writing: $!\n")
  if ($opt_o);

# Summary
$report_index = 0;
@format = ( 53, 14, 10 );
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 );
outstart($report_index);
outheader( $report_index, 'Calamaris 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, '', '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, '', 'unique hosts/users:', 'hosts',
	scalar keys %tcp_requester ) if $opt_r;
outline( $report_index, '', 'Total Bandwidth:', 'Byte',
	kilomegagigatera( $tcp_size + $udp_size, $format[2] ) );
@format = ( 53, 14, '10%' );
$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, '', '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 );
@format = ( 53, 14, 10 );
if ($opt_response_time) {
  # average response time
  $reqtime_sum = $reqtime_num = $avg_udp_reqtime = $avg_tcp_reqtime = 0;
  my $skipped = 0;
  foreach $reqtime ( keys %udp_reqtime ) {
    if ($reqtime > $response_time_limit) {
      $skipped += $tcp_reqtime{$reqtime};
      next;
    }
    $reqtime_sum += $reqtime * $udp_reqtime{$reqtime};
    $reqtime_num += $tcp_reqtime{$reqtime} if $tcp_reqtime{$reqtime};
  }
  $avg_udp_reqtime = ($reqtime_num) ? $reqtime_sum / $reqtime_num : '0';
  my $time_skipped = ($response_time_limit == $response_time_report_interval[$#response_time_report_interval])
    ? '' :  " (requests > $response_time_limit msec skipped)";
  my $skipped_percent = (($skipped+$reqtime_num) ? round(100*$reqtime_num/($skipped+$reqtime_num),2) : 100 ) . ' percent';
  outline( $report_index, '', "UDP response time of $skipped% requests$time_skipped:", 'msec',
	kilomegagigatera( $avg_udp_reqtime, $format[2] ) ) if ($avg_udp_reqtime);

  $skipped = 0;
  $reqtime_sum = $reqtime_num = 0;
  foreach $reqtime ( keys %tcp_reqtime ) {
    if ($reqtime > $response_time_limit) {
      $skipped += $tcp_reqtime{$reqtime};
      next;
    }
    $reqtime_sum += $reqtime * $tcp_reqtime{$reqtime};
    $reqtime_num += $tcp_reqtime{$reqtime};
  }
  $avg_tcp_reqtime = ($reqtime_num) ? $reqtime_sum / $reqtime_num : '0';
  $skipped_percent = (($skipped+$reqtime_num) ? round(100*$reqtime_num/($skipped+$reqtime_num),2) : 100 ) . '%';
  outline( $report_index, '', "TCP response time of $skipped_percent requests$time_skipped:", 'msec',
	kilomegagigatera( $avg_tcp_reqtime, $format[2] ) ) if ($avg_tcp_reqtime);
}
outseperator($report_index);

outheader( $report_index, 'Cache statistics', '', '');
outseperator($report_index);
outline( $report_index, '', 'Total amount cached:', 'requests',
	$tcp_hit + $udp_hit );
@format = ( 53, 14, '10%' );
outline( $report_index, '', 'Request hit rate:', '%',
	($tcp + $udp) ? 100 * ($tcp_hit + $udp_hit) / ($tcp + $udp) : 0 );
@format = ( 53, 14, 10 );
outline( $report_index, '', 'Bandwidth savings:', 'Byte',
	kilomegagigatera( $tcp_hit_size + $udp_hit_size, $format[2] ) );
@format = ( 53, 14, '10%' );
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 );
@format = ( 53, 14, 10 );
if ($opt_D) {
  # Object sizes
  $cached_obj_size = $cached_obj_num = $obj_size = $obj_num =
    $avg_cached_obj_size = $avg_direct_obj_size = $avg_all_obj_size = 0;
  foreach $distribution ( sort { $a <=> $b } keys(%tcp_distribution) ) {
    $cached_obj_size += $tcp_hit_distribution_size{$distribution};
    $cached_obj_num += $tcp_hit_distribution{$distribution};
    $obj_size += $tcp_distribution_size{$distribution};
    $obj_num += $tcp_distribution{$distribution};
  }
  $avg_cached_obj_size = $cached_obj_size / $cached_obj_num if $cached_obj_num;
  $avg_direct_obj_size = ($obj_size - $cached_obj_size) / ($obj_num - $cached_obj_num) if $obj_num - $cached_obj_num;
  $avg_all_obj_size = $obj_size / $obj_num if $obj_num;

  outline( $report_index, '', 'Average cached object size:', 'Byte',
	kilomegagigatera( $avg_cached_obj_size, $format[2] ) );
  outline( $report_index, '', 'Average direct object size:', 'Byte',
	kilomegagigatera( $avg_direct_obj_size, $format[2] ) );
  outline( $report_index, '', 'Average object size:', 'Byte',
	kilomegagigatera( $avg_all_obj_size, $format[2] ) );
}

$max_value[$report_index] = '-
    </td>
    <td>
     -
    </td>
    <td>
     -';
outseperator($report_index);
outstop($report_index);

# Incoming request peak per protocol
$report_index = 1;
@format = ( 3, 4, 18, 5, 18, 7, 18 );
if ( not defined $opt_p ) {
} elsif ( $opt_p eq 'old' ) {
  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>
    <td>
     -
    </td>
    <td>
     -';
  outstop($report_index);
} elsif ( $opt_p eq 'new' ) {
  $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>
    <td>
     -
    </td>
    <td>
     -';
  outstop($report_index);

  # Incoming transfer volume per protocol
  $report_index = 2;
  @format = ( 5, 8, 18 );
  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>
    <td>
     -
    </td>
    <td>
     -';
  outstop($report_index);
}

# Incoming requests by method
$report_index = 3;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 30, 9, '%', 'spr', 8, '%', 'kbps' );
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, 'toggle', $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, '2', '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>
    <td>
     ' . htmlescape($xaxis[0]) . '
    </td>
    <td>
     ' . ($opt_O
    ? kilomegagigatera($yaxis2[0], 6) . ' Byte' : $yaxis1[0] . ' Requests' );
}
outstop($report_index);

# Incoming UDP-requests by status
$report_index = 4;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 30, 9, '%', 'mspr', 8, '%', 'kbps' );
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, '2', '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]);
  # find max value
  my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
  if (defined($maxi) and defined($xaxis[$maxi])) {
    $max_value[$report_index] = 'most incoming request by status to
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($opt_O ? kilomegagigatera($yaxis2[$maxi], 6) . ' Byte' :
	$yaxis1[$maxi] . ' Requests');
  }
}
outstop($report_index);

# Incoming TCP-requests by status
$report_index = 5;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 30, 9, '%', 'spr', 8, '%', 'kbps' );
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, 'toggle', '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, 'toggle', ' ' . $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, 'toggle', '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, 'toggle', ' ' . $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, 'toggle', '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, 'toggle', ' ' .  $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, '2', '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]);
  # find max value
  my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
  if (defined($maxi) and defined($xaxis[$maxi])) {
    $max_value[$report_index] = 'most incoming request by status to
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($opt_O
      ? kilomegagigatera($yaxis2[$maxi], 6) . " Byte" : $yaxis1[$maxi]. " Requests" );
  }
}
outstop($report_index);

# Outgoing requests by status
$report_index = 6;
@format = ref($formats[$report_index])
  ? @{$formats[$report_index]} : ( 30, 9, '%', 'spr', 8, '%', 'kbps' );
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, 'toggle', '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, 'toggle', ' ' . $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, 'toggle', '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, 'toggle', ' ' . $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, 'toggle', '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, 'toggle', ' ' . $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, '2', '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]);
  # find max value
  my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
  if (defined($maxi) and defined($xaxis[$maxi])) {
    $max_value[$report_index] = 'most outgoing request to
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($opt_O
      ? kilomegagigatera($yaxis2[$maxi], 6) . ' Byte' : $yaxis1[$maxi]. ' Requests' );
  }
}
outstop($report_index);

# Outgoing requests by destination
$report_index = 7;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 30, 9, '%', 'spr', 8, '%', 'kbps' );
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, 'toggle', '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, 'toggle', $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, 'toggle', ' ' . $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, '2', '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]);
  # find max value
  my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
  if (defined($maxi) and defined($xaxis[$maxi])) {
    $max_value[$report_index] = 'most requested destination
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($opt_O
	  ? kilomegagigatera($yaxis2[$maxi], 6) . ' Byte' : $yaxis1[$maxi] . ' Requests');
  }
}
outstop($report_index);

# Request-destinations by ${N}-level-domain
$report_index = 8;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off' );
if ($opt_d) {
  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, 'toggle', $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 );
      $other_urlhost = '' unless $show_other_tcp_urlhost;
      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, '2', '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>
    <td>
     ' . ($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
    $report_index = 9;
    @format = ref($formats[$report_index]) ?
      @{$formats[$report_index]} : ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off' );
    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, 'toggle', $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 );
      $other_tld = '' unless $show_other_tcp_urltld;
      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, '2', '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>
    <td>
     ' . ($opt_O
      ? kilomegagigatera($yaxis2[0], 6) . " Byte" : $yaxis1[0]. " Requests");
  }
}

# TCP-Request-protocol
$report_index = 10;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off' );
if ($opt_t) {
  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, 'toggle', $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, '2', '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_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;
    # find max value
    my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
    if (defined($maxi) and defined($xaxis[$maxi])) {
      $max_value[$report_index] = 'most requested protocol
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($opt_O
        ? kilomegagigatera($yaxis2[$maxi], 6) . ' Byte' : $yaxis1[0] . ' Requests');
    }
  }
  outstop($report_index);

# Requested content-type
  $report_index = 11;
  @format = ref($formats[$report_index]) ?
    @{$formats[$report_index]} : ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off' );
  if ( %tcp_content ) {
    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, 'toggle', $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 );
        $other_content = '' unless $show_other_tcp_content;
	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, '2', '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>
    <td>
     ' . ($opt_O
	? kilomegagigatera($yaxis2[0], 6) . ' Byte' : $yaxis1[0] . ' Requests');
    }
    outstop($report_index);
  }

# Requested extensions
  $report_index = 12;
  @format = ref($formats[$report_index]) ?
    @{$formats[$report_index]} : ( 32, 9, '%', '%', 'off', 8, '%', '%', 'off', 'off', 'off' );
  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, 'toggle', $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 );
      $other_urlext = '' unless $show_other_tcp_urlext;
      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, '2', '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>
    <td>
     ' . htmlescape($xaxis[0]) . '
    </td>
    <td>
     ' . ($opt_O
      ? kilomegagigatera($yaxis2[0], 6) . ' Byte' : $yaxis1[0]. ' Requests');
  }
  outstop($report_index);
}

if ($opt_r) {
# Incoming UDP-requests by host
  $report_index = 13;
  @format = ref($formats[$report_index]) ?
    @{$formats[$report_index]} : ( 38, 9, 'off', '%', 'off', 8, 'off', '%', 'kbps' );
  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, 'toggle', ' ' .  $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 );
      $other_requester = '' unless $show_other_udp_requester;
      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, '2', '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>
    <td>
     ' . htmlescape($xaxis[0]) . '
    </td>
    <td>
     ' . ($opt_O
      ? kilomegagigatera($yaxis2[0], 6) . ' Byte' : $yaxis1[0] . ' Requests');
  }
  outstop($report_index);

# Incoming TCP-requests by host
  $report_index = 14;
  @format = ref($formats[$report_index]) ?
    @{$formats[$report_index]} : ( 30, 9, 'off', '%', 'spr', 8, 'off', '%', 'kbps' );
  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, 'toggle', 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, 'toggle', ' ' .  $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 );
      $other_requester = '' unless $show_other_tcp_requester;
      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, '2', '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>
    <td>
     ' . htmlescape($xaxis[0]) . '
    </td>
    <td>
     ' . ($opt_O
      ? kilomegagigatera($yaxis2[0], 6) . ' Byte' : $yaxis1[0] . ' Requests');
  }
  outstop($report_index);
}

# Size Distribution Diagram
$report_index = 15;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 30, 9, 'off', '%', 'spr', 8, 'off', '%', 'kbps' );
if ($opt_D) {
  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, '2', '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]);
    # find max value
    my ($max, $maxi) = ($opt_O) ? maxi(@yaxis2) : maxi(@yaxis1);
    if (defined($maxi) and defined($xaxis[$maxi])) {
      $max_value[$report_index] = 'most requested object_size
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($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
$report_index = 16;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 15, 9, 'off', 5, 'off', 'off', 'kbps', 'kbps', 'kbps', 'kbps', 'kbps', 'kbps' );
if ($opt_P) {
  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, $perf_size{$perf_date} 
	? $perf_tcp_hit_size{$perf_date} / $perf_size{$perf_date} : 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, 'toggle', 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] ),
	      $perf_size{$perf_date} ? 100 * $perf_tcp_hit_size{$perf_date} / $perf_size{$perf_date} : 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, '2', '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 (defined($maxi) and defined($xaxis[$maxi])) {
      $max_value[$report_index] = 'most active day
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($opt_O
        ? kilomegagigatera($max, 6) . ' Byte' : $max . ' Requests');
    }
  }
  outstop($report_index);
}

# UDP-Request duration distribution in msec
$report_index = 17;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 16, 9, '%', '%', 'spr', 8, '%', '%', 'kbps' );
if ($opt_response_time) {
  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, 'toggle', $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, '2', '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>
    <td>
     ' . htmlescape($max_interval) . '
    </td>
    <td>
     ' . ($opt_O
      ? kilomegagigatera($max, 6) . ' Byte' : $max . ' Requests');
  }
  outstop($report_index);

# TCP-Request duration distribution in msec
  $report_index = 18;
  @format = ref($formats[$report_index]) ?
    @{$formats[$report_index]} : ( 16, 9, '%', '%', 'spr', 8, '%', '%', 'kbps' );
  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, 'toggle', $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, '2', '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>
    <td>
     ' . ($opt_O
      ? kilomegagigatera($ordered_tcp_req_time_max, 6) . ' Byte'
      : $ordered_tcp_req_time_max . ' Requests');
  }
  outstop($report_index);
}

# UDP Response code distribution
$report_index = 19;
@format = ref($formats[$report_index]) ?
  @{$formats[$report_index]} : ( 46, 9, '%', 'off', 'off', 8, '%', 'off', 'off' );
if ($opt_errorcode_distribution) {
  %err_code = geterrcode();
  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, 'toggle',
	      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, '2', '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 (defined($maxi) and defined($xaxis[$maxi])) {
      $max_value[$report_index] = 'most frequent response code
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($opt_O
        ? kilomegagigatera($max, 6) . ' Byte' : $max . ' Requests' );
    }
  }
  outstop($report_index);

# TCP Response code distribution
  $report_index = 20;
  @format = ref($formats[$report_index]) ?
    @{$formats[$report_index]} : ( 46, 9, '%', 'off', 'off', 8, '%', 'off', 'off' );
  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, 'toggle', 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, '2', '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 (defined($maxi) and defined($xaxis[$maxi])) {
      $max_value[$report_index] = 'most frequent response code
    </td>
    <td>
     ' . htmlescape($xaxis[$maxi]) . '
    </td>
    <td>
     ' . ($opt_O
        ? kilomegagigatera($max, 6) . ' Byte' : $max . ' Requests');
    }
  }
  outstop($report_index);
}
close(CACHE);

##################################################
# now print it out.

$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 ? "$host_name " : '') . "Proxy Report ($loginterval)\n\n"
  if ($outtype_mail);
if ( $outtype_html or $outtype_htmlembed or $outtype_htmlframe ) {
  $out_head .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>
   ' . ( $host_name ? "$host_name " : '') . 'Proxy Report (' . $loginterval . ')
  </title>
  <meta name="generator" content="Calamaris/' . $VERSION . '" />
 ' . ( $opt_M ? "  $opt_M\n" : '' ) . '
 </head>
 <body>' if ($outtype_html or $outtype_htmlframe);
  $out_index_head .= '
  <table border="0" summary="Page Intro">' if ($outtype_html or $outtype_htmlembed or $outtype_htmlframe);
  if ($opt_l) {
    $out_index_head .= '
   <tr>
    <td colspan="2">
     ' . $opt_l . '
    </td>
   </tr>';
  }
  $out_index_head .= '
   <tr>
    <th>
     <h1 class="headline">
      <a name="top" id="top">Proxy Report</a>
     </h1>
    </th>
    <th>
     ' . $host_name . '
    </th>
   </tr>
   <tr>
    <th>
     Report period:
    </th>
    <th>
     ' . $loginterval . '
    </th>
   </tr>
   <tr>
    <th>
     Generated at:
    </th>
    <td>
     ' . $generated . '
    </td>
   </tr>
  </table>
  <hr />'
    if ($outtype_html or $outtype_htmlembed or $outtype_htmlframe);
} else {
  $out_index_head .= "\n${host_name} Proxy-Report\n
Report period: $loginterval\nGenerated at: $generated\n";
}

@format = (60);
if ( ( $invalid / $counter ) > .05 and $counter > 1000 ) {
  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) ) {
  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) ) {
  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 ) {
  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) {
  outstart('E');
  outline( 'E', '', '' );
  outline( 'E', '', 'The following IP addresses have been ignored:' );
  foreach ( split ( ':', $opt_ipfilter_exclude ) ) {
    outline( 'E', '', "    $_" );
  }
  outstop('E');
}

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

if ( $outtype_html or $outtype_htmlembed or $outtype_htmlframe ) {
  $out_index_body .= '
  <table border="1" summary="Table of Content">
   <tr>
    <td colspan="4" class="head">
     Table of Content / Overview
    </td>
   </tr>';
  @reportlist = ($opt_S) ? ( split ( ",", $opt_S ) ) : (0 .. $#reports);
  foreach ( @reportlist ) {
    outref($_) if $outref{$_};
  }
  $out_index_body .= '
  </table>';
}

if ( $outtype_html or $outtype_htmlembed or $outtype_htmlframe ) {
  my $year = (localtime)[5] + 1900;
  $out_tail = "
  <hr />
  $HTMLCOPYRIGHT";
  $out_tail .= '
 </body>
</html>
' if ($outtype_html or $outtype_htmlframe);
} else {
  $out_tail .= "\n\n";
  $out_tail .= "-- \n" if ($outtype_mail);
  $out_tail .= "Calamaris $VERSION\n$COPYRIGHT\n$LICENSE\n$HOMEPAGE\n";
}

my $fh = *STDOUT;
if ($opt_output_path or $opt_output_file or $opt_output_file_prefix) {
  print STDERR "writing output to $path/$file_prefix$filename\n"
    if $opt_v;
  if ($outtype_htmlframe) {
    open( OUT, ">$path/${file_prefix}overview.html" )
      or die ("$0: can't open $path/${file_prefix}overview.html for writing: $!\n");
  } else {
    open( OUT, ">$path/$file_prefix$filename" )
      or die ("$0: can't open $path/$file_prefix$filename for writing: $!\n");
  }
  $fh = *OUT;
}
print $fh $out_head;
print $fh $out_index_head if $out_index_head;
print $fh $out_index_body if $out_index_body;
print $fh $out_tail if $outtype_htmlframe;

if ($opt_output_path and $outtype_htmlframe) {
# print frameset
  open( FRAME, ">$path/${file_prefix}framehead.html" )
    or die ("$0: can't open $path/${file_prefix}framehead.html for writing: $!\n");
  $frame = *FRAME;
  print $frame $out_head;
  print $frame $out_index_head;
  print $frame '
 </body>
</html>
';
  close($frame);

  open( FRAME, ">$path/$file_prefix$filename" )
    or die ("$0: can't open $path/$file_prefix$filename for writing: $!\n");
  $frame = *FRAME;
  print $frame '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>
   ' . ($host_name ? "$host_name " : '') . 'Proxy Report (' . $loginterval . ')
  </title>
  <meta name="generator" content="Calamaris/' . $VERSION . '" />' .
( $opt_M ? "  $opt_M\n" : '' ) . '
 </head>
 <frameset rows="90,*">
  <frame src="' . $file_prefix . 'framehead.html" name="FrameHead" />
  <frameset cols="300,*">
   <frame src="' . $file_prefix . 'toc.html" name="TableOfContent" />
   <frame src="' . $file_prefix . '0.html" name="Data" />
  </frameset>
  <noframes>
   <body>
    <p>
     Your browser is not able to view frames!
     Please follow this <a href="' . $file_prefix . 'toc.html">link</a>
     to get noframes.
    </p>
    <hr />
    ' . $HTMLCOPYRIGHT . '
   </body>
  </noframes>
 </frameset>
</html>
';
  close($frame);

# print table of content
  open( TOC, ">$path/${file_prefix}toc.html" )
    or die ("$0: can't open $path/${file_prefix}toc.html for writing: $!\n");
  my $toc = *TOC;

  print $toc $out_head;
  outtoc();
  print $toc $out_toc;
  print $toc '
 </body>
</html>
';
  close($toc);
}

@reportlist = ($opt_S) ? ( split ( ",", $opt_S ) ) : (0 .. $#reports);
foreach $index ( 'E', @reportlist ) {
  if ( $outtype_htmlframe ) {
    open( OUT, ">$path/${file_prefix}$index.html" )
      or die ("$0: can't open $path/${file_prefix}$index.html for writing: $!\n");
    $fh = *OUT;
    print $fh $out_head;
  }
  if ($outref{$index}) {
    if ( not defined( $out_body{$index} ) and $index ne 'E' ) {
      outstart($index);
      outline( $index, 'no matching requests' );
      outstop($index);
    }
    print $fh $out_body{$index} if defined( $out_body{$index} );
  }
  if ($outtype_htmlframe) {
    print $fh $out_tail;
    close($fh);
  }
}
print $fh $out_tail unless $outtype_htmlframe;
close($fh);

print "$test_string\n" if $test;

if ( $opt_generate_index and $opt_output_path and
      $opt_output_file_prefix) {
  opendir DIR, $opt_output_path or
    die("$0: can't opendir $opt_output_path: $!\n");
  $pattern = $opt_output_file_prefix . $filename;
  $pattern =~ s#%h#(\\S+)#;
  $pattern =~ s#%t#(\\d{14}-\\d{14})#;
  $pattern =~ s#%%#%#;
  @files = grep { m#^$pattern# && -f "$opt_output_path/$_" } readdir(DIR);
  closedir DIR;
  $filename = 'index.html' unless $opt_output_file;
open (INDEX, ">$opt_output_path/$filename") or
  die ("$0: can't open $opt_output_path/$filename for writing: $!\n");
print INDEX '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>
   Proxy Report Index
  </title>
  <meta name="generator" content="Calamaris/' . $VERSION . '" />
  ' . ( $opt_M ? "  $opt_M\n" : '' ) . ' </head>
 <body>
  <h1 class="headline">
   Proxy Report Index
  </h1>
  <table summary="list of Proxy Reports">
   <tr>
    <th class="TableDefinition">
     Hostname
    </th>
    <th class="TableDefinition">
     Startdate
    </th>
    <th class="TableDefinition">
     Enddate
    </th>
   </tr>
';
  foreach (@files) {
    m#^$pattern#;
    if ($1 =~ m#\d{14}-\d{14}#) {
      $timestamp = $1;
      $host = $2;
    } else {
      $host = $1;
      $timestamp = $2;
    }
    ($startdate, $enddate) = split ('-', $timestamp) if $timestamp;
    print INDEX "   <tr>
    <td class=\"line_1\">
     <a href=\"$_\">$host</a>
    </td>
    <td class=\"line_1\">
     <a href=\"$_\">$startdate</a>
    </td>
    <td class=\"line_1\">
     <a href=\"$_\">$enddate</a>
    </td>
   </tr>\n";
  }
  print INDEX '  </table>
  <hr />
' . $HTMLCOPYRIGHT . '
 </body>
</html>
';
  close(INDEX);
}

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) = @_;
  unless ($hostcache{$host}) {
    my $sockaddr;
    if ( $host =~ m#^(::ffff:)?(([0-9][0-9]{0,2}\.){3}[0-9][0-9]{0,2})$#io ) {
      $sockaddr = sockaddr_in(0 , inet_pton(AF_INET, $2));
    } elsif ( $host =~ m#^([0-9a-f:]+)$#io ) {
      $sockaddr = sockaddr_in6(0, inet_pton(AF_INET6, $1));
    }
    if ( $sockaddr ) {
      $hostcache{$host} = (getnameinfo($sockaddr, 0, 0))[1];
      $hostcache{$host} = $host unless $hostcache{$host};
    } else {
      $hostcache{$host} = $host;
    }
  }
  return $hostcache{$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 (@_);
  my $type = 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 = $type ?
      sprintf( "%04d%02d%02d%02d%02d%02d\n", $year, $mon+1, $mday,
	      $hour, $min, $sec ) :
      sprintf( "%02d.%s %02d %02d:%02d:%02d\n", $mday, $month,
	      substr( $year, -2 ), $hour, $min, $sec );
    chomp($retdate);
    return $retdate;
  } else {
    return '                  ';
  }
}

sub outtoc {
  $out_toc .= "
  <table summary=\"Table of Content\">
   <tr>
    <td colspan=\"2\">
     <h2>
      Table of Content
     </h2>
    </td>
   </tr>
   <tr>
    <td>
     <a href=\"${file_prefix}overview.html\" target=\"Data\">Overview</a>
    </td>
   </tr>
   <tr>
    <td>
     <a href=\"${file_prefix}E.html\" target=\"Data\">Warnings</a>
    </td>
   </tr>";
  @reportlist = ($opt_S) ? ( split ( ",", $opt_S ) ) : (0 .. $#reports);
  foreach ( @reportlist ) {
    $out_toc .= "
   <tr>
    <td>
     <a href=\"${file_prefix}$_.html\" target=\"Data\">$reports[$_]</a>
    </td>
   </tr>";
  }
  $out_toc .= '
  </table>';
}

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

sub outstart {
  my $index = shift (@_);
  if ( $outtype_html or $outtype_htmlembed or $outtype_htmlframe ) {
    $out_body{$index} .= '
  <hr />';
    if ( $index eq 'E' ) {
      $out_body{$index} .= '
    <table border="1" summary="Errors">';
    } else {
      $out_body{$index} .= '
  <table border="1" summary="'. $reports[$index] . '">
   <tr>
    <td class="head" colspan="' . scalar(@format) . '">
     <a name="' . $index . '">' . $reports[$index] . '</a>
    </td>
   </tr>';
    }
  } 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 or $outtype_htmlframe );
  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 or $outtype_htmlframe ) {
      $p =~ s# +# #go;
      $p =~ s#(^ | $)##go;
      $p = '&nbsp;' if ( $p eq '' );
      $out_body{$index} .= "
    <th class=\"TableDefinition\">
     $p
    </th>";
    } elsif ( $format[$no] eq '%' ) {
      $out_body{$index} .=
	' ' x ( ( (6 - length($p)) > 0 ) ? (6 - length($p)) : 0 ) . substr( $p, 0, 6 ) . ' ';
    } elsif ( $format[$no] =~ m/[bps|spb|spkb|rps|rpms|spr]$/ ) {
      $out_body{$index} .=
	substr( $p, 0, 7 ) . ' ' x ( ( (7 - length($p)) > 0) ? (7 - length($p)) : 0 ) . ' ';
    } else {
      $out_body{$index} .=
        substr( $p, 0, $format[$no] ) . ' ' x ( ( ($format[$no] - length($p)) > 0) ? ($format[$no] - length($p)) : 0 ) . ' ';
    }
    $no++;
  }
  $out_body{$index} .= "\n";
  $out_body{$index} .= '   </tr>' if ( $outtype_html or $outtype_htmlembed or $outtype_htmlframe );
}

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

  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/${file_prefix}${index}.${opt_image_type}")
    or die ("$0: can't open $path/${file_prefix}${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 @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 $linecolor = shift (@_);
  my $print;
  my $no = 0;
  $out_body{$index} .= "\n   <tr>" if ( $outtype_html or $outtype_htmlembed or $outtype_htmlframe );
  $color = 0 unless $linecolor;
  if ($linecolor eq 'toggle') {
    $color = $color ? 0 : 1;
  } elsif ($linecolor) {
    $color = $linecolor;
  }
  foreach (@_) {
    $print = $_;
    if ($format[$no] eq 'off') { # do nothing
    } elsif ($outtype_unformatted) {
      $out_body{$index} .= "$print ";
    } elsif ( $outtype_html or $outtype_htmlembed or $outtype_htmlframe ) {
      $print =~ s# +# #go;
      $print =~ s# $##go;
      $print =~ s#<#\&lt\;#go;
      $print =~ s#>#\&gt\;#go;
      if ( $no == 0 ) {
	unless ( $print =~ s#^ ##go ) {
	  $out_body{$index} .= "
    <th class=\"line_$color\" align=\"left\">
     $print
    </th>";
	} else {
	  $out_body{$index} .= "
    <td class=\"line_$color\">
     &nbsp;&nbsp;$print
    </td>";
	}
      } elsif ( $format[$no] eq '%' ) {
	if ( $print eq '' or $print eq '-' ) {
	  $out_body{$index} .= "
    <td class=\"line_$color\">
     &nbsp;
    </td>";
	} else {
	  $out_body{$index} .=
	    sprintf( '
    <td class="line_%s" align="right">
     %.2f
    </td>', $color, $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} .= "
    <td class=\"line_$color\">
     &nbsp;
    </td>";
# kByte/sec
	} elsif ($format[$no] =~ m/bps/) {
	  $out_body{$index} .= sprintf( '
    <td class="line_%s" align="right">
     %.2f
    </td>', $color, $print * $factor );
# sec/kByte
	} elsif ($format[$no] =~ m/spkb/ or $format[$no] =~ m/spb/) {
	  $out_body{$index} .= sprintf( '
    <td class="line_%s" align="right">
     %.2f
    </td>', $color, 1 / ($factor * $print) );
# req/[m]sec
	} elsif ($format[$no] =~ m/rps/ or $format[$no] =~ m/rpms/) {
	  $out_body{$index} .= sprintf( '
    <td class="line_%s" align="right">
     %.2f
    </td>', $color, 1 / ($print * $factor) );
# [m]sec/req
	} elsif ($format[$no] =~ m/spr/) {
	  $out_body{$index} .= sprintf( '
    <td class="line_%s" align="right">
     %.2f
    </td>', $color, $print * $factor );
# %
	} else {
	  $out_body{$index} .= sprintf( '
    <td class="line_%s" align="right">
     %.2f
    </td>', $color, $print );
	}
      } elsif ( $no == 1 and  $print !~ m#^[\d\.e\-\+]+$#o ) {
	$out_body{$index} .= sprintf( '
    <td class="line_%s" align="right">
     %s
    </td>', $color, $print );
      } elsif ( $no == 1 or $print =~ m#^[\d\.e\-\+]+$#o ) {
	$out_body{$index} .= sprintf( '
    <td class="line_%s" align="right">
     %d
    </td>', $color, $print );
      } else {
	if ($print) {
	  $out_body{$index} .= "
    <td class=\"line_$color\" align=\"right\">
     $print
    </td>";
	} else {
	  $out_body{$index} .= "
    <td class=\"line_$color\" align=\"right\">
     &nbsp;
    </td>";
	}
      }
    } else {
      if ( $no == 0 ) {
	if ( length($print) > $format[$no] ) {
	  $out_body{$index} .=
	    $print . "\n" . ' ' x ( ($format[$no] > 0) ? $format[$no] : 0) . ' ';
	} else {
	  $out_body{$index} .=
	    $print . ' ' x ( ( ($format[$no] - length($print)) > 0) ? ($format[$no] - length($print)) : 0 ) . ' ';
	}
      } elsif ( $format[$no] eq '10%' ) {
	if ( $print eq '' or $print eq '-' ) {
	  $out_body{$index} .= ' ' x 11;
	}
	else {
	  $out_body{$index} .= sprintf( '%10.2f ', $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} .=
	  ' ' x ( ( ($format[$no] - length($print)) > 0) ? ($format[$no] - length($print)) : 0 )
		    . substr( $print, 0, $format[$no] ) . ' ';
      }
    }
    $no++;
  }
  $out_body{$index} .= "\n";
  $out_body{$index} .= '   </tr>' if ( $outtype_html or $outtype_htmlembed or $outtype_htmlframe);
}

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

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

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{'208'} = 'Already Reported';
  $err_code{'226'} = 'IM Used';
  $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{'306'} = 'Switch Proxy';
  $err_code{'307'} = 'Temporary Redirect';
  $err_code{'307'} = 'Permanent Redirect';
  $err_code{'308'} = 'Resume Incomplete';
  $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'} = 'Payload Too Large';
  $err_code{'414'} = 'Request URI Too Large';
  $err_code{'415'} = 'Unsupported Media Type';
  $err_code{'416'} = 'Request Range Not Satisfiable';
  $err_code{'417'} = 'Expectation Failed';
  $err_code{'418'} = 'I\'m a teapot';
  $err_code{'419'} = 'Authentication Timeout';
  $err_code{'421'} = 'Misdirected Request';
  $err_code{'422'} = 'Unprocessable Entity';
  $err_code{'423'} = 'Locked';
  $err_code{'424'} = 'Failed Dependency';
  $err_code{'428'} = 'Precondition Required';
  $err_code{'429'} = 'Too Many Requests';
  $err_code{'431'} = 'Request Header Fields Too Large';
  $err_code{'433'} = 'Unprocessable Entity';
  $err_code{'451'} = 'Unavailable For Legal Reasons';
  $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{'506'} = 'Variant Also Negotiates';
  $err_code{'507'} = 'Insufficient Storage';
  $err_code{'508'} = 'Loop Detected';
  $err_code{'510'} = 'Not Extended';
  $err_code{'511'} = 'Network Authentication Required';
  $err_code{'522'} = 'Connection timed out (unofficial)';
  $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($generate_index);
  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_file_prefix);
  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 );
  $response_time_limit = $response_time_report_interval[$#response_time_report_interval];
  # show <other>
  $show_other_tcp_urlhost = 1;
  $show_other_tcp_urltld = 1;
  $show_other_tcp_content = 1;
  $show_other_tcp_urlext = 1;
  $show_other_udp_requester = 1;
  $show_other_tcp_requester = 1;

  # 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_generate_index = $generate_index unless $opt_generate_index;
  $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_output_file_prefix = $output_file_prefix unless $opt_output_file_prefix;
  $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' ) {
      use Sys::Hostname;
      $host_name = hostname();
    } else {
      $host_name = $opt_H;
    }
  } else {
    $host_name = '';
  }

  if ($opt_ipfilter_exclude or $opt_ipfilter_include) {
    unless (eval "require NetAddr::IP") {
      print STDERR "$0: You need package NetAddr::IP for ipfilter-*-Option,
  I searched it in @INC: $!\n\n";
      $usage_err = 1;
    }
  }

  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 = timelocal( $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 = timelocal( $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;
    }
  }

  $path = $opt_output_path ? $opt_output_path : '.';
  if (-d "$path") {
    unless (-w "$path") {
      print STDERR "$0: directory $path not writable: $!\n\n";
      $usage_err = 1; 
    }
  } else {
    use File::Path qw(make_path);
    make_path( "$path", {error => \my $err} );
    if (@err) {
      print STDERR "$0: can't create directory $path: @err\n\n";
      $usage_err = 1;
    }
  }
  $filename = $opt_output_file ? $opt_output_file : 'calamaris.txt';
  $file_prefix = $opt_output_file_prefix ? $opt_output_file_prefix : '';
  if ($opt_F) {
    $outtype_htmlembed = $outtype_htmlframe = $outtype_html = 0;
    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 'html-frame') {
        $outtype_htmlframe = 1;
      } elsif ( $output eq 'graph' ) {
        unless (eval "require GD::Graph") {
      	  print STDERR "$0: You need package GD::Graph for -F 'graph'-Option,
  I searched it in @INC: $!\n\n";
	  $usage_err = 1;
        }
        use FindBin;
	use lib "$FindBin::Bin/..";
        unless (eval "require calamaris::calBars3d") {
      	  print STDERR "$0: You need package calamaris::calBars3d for -F 'graph'-Option,
  I searched it in @INC: $!\n\n";
	  $usage_err = 1;
        }
        $outtype_graph = 1;
      } elsif ( $output eq 'unformatted' ) {
        $outtype_unformatted = 1;
      } else {
        print STDERR "$0: unknown output-format: $output\n\n";
        $usage_err = 1;
      }
    }

    if ( ( $outtype_htmlembed + $outtype_htmlframe + $outtype_html ) > 1 ) {
      print STDERR "$0: only one of 'html', 'html-embed' or 'html-frame' can be used: $opt_F\n\n";
      $usage_err = 1;
    }
    if ( $outtype_mail and ($outtype_htmlembed or $outtype_htmlframe or $outtype_graph) ) {
      print STDERR "$0: 'mail' together with 'html-embed', 'html-frame' or 'graph' won't work: $opt_F\n\n";
      $usage_err = 1;
    }
    print STDERR "$0: please use --output-path /path, when using -F 'graph'\n\n" if ( $outtype_graph and not $opt_output_path );
    if ($outtype_graph and not ($outtype_html or $outtype_htmlembed or $outtype_htmlframe) ) {
      print STDERR "$0: -F 'graph' is kinda useless without 'html', 'html-embed' or 'html-frame'.\n\n";
    }
  }

  if ( $opt_generate_index and not $opt_output_path and
      not $opt_output_file_prefix) {
    print STDERR "$0: --generate-index needs --output-path and --output-file-prefix\n";
  }

  if ($outtype_htmlembed or $outtype_html or $outtype_htmlframe) {
    $filename = $opt_output_file ? $opt_output_file : 'index.html';
  }

  $sortorder = $opt_O ? '_size' : '';
  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';
  }

  $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;

  @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' );

  $LICENSE = 'Calamaris comes with ABSOLUTELY NO WARRANTY. It is free software, and you are
welcome to redistribute it under certain conditions. See source for details.';
  $COPYRIGHT =
'Copyright (C) 1997-2006, 2013, 2015, 2017, 2021, 2024
Cord Beermann. Authors: Cord Beermann and Michael Pophal.';
  $HOMEPAGE = 'https://Calamaris.Cord.de/';
  $HTMLCOPYRIGHT = '
  <address>
   <a href="' . $HOMEPAGE . '">Calamaris</a> ' . $VERSION . ',
   Copyright &copy; 1997-2006, 2013, 2015, 2017, 2021, 2024
   <a href="https://Cord.de/">Cord Beermann</a>. Authors: Cord Beermann and Michael Pophal.
  </address>
  <p>
   ' . $LICENSE . '
  </p>';
  $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
	process 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, BlueCoat)
	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 (localtime). 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 or html-frame
--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 or html-frame
--output-format|-F type
	output format (comma-seperated list)
	mail	    = mail format, disables graph output.
	html	    = HTML format
	html-frame  = HTML frames, disables mail output.
	html-embed  = HTML format without HTML-headers.
	graph	    = enable graphics, needs GD::Graph, only useful with html
		      or html-embed, see --output-path. Disables mail output.
		      This switch needs the perl Module GD::Graph.
	unformatted = plain unformatted output
--output-path /path
--output-file-prefix string
--output-file filename
	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. %h will be expanded to the hostname,
	%t to a timerange, %% to a single %.
--generate-index
	Generates an index-file of all matching reports if output-path and
	output-file-prefix is set
--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 '1' 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};
	$show_other_tcp_urlhost = 0 if defined($tcp_urlhost{'<other>'});
	$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};
	$show_other_tcp_urltld = 0 if defined($tcp_urltld{'<other>'});
	$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};
	$show_other_tcp_content = 0 if defined($tcp_content{'<other>'});
	$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};
	$show_other_tcp_urlext = 0 if defined($tcp_urlext{'<other>'});
	$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};
	$show_other_udp_requester = 0 if defined($udp_requester{'<other>'});
	$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};
	$show_other_tcp_requester = 0 if defined($tcp_requester{'<other>'});
	$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 (round($time,2) == round(${"${code}_time"},2)) {
      $test_string .= "Time:  OK\n";
    } else {
      $test_string .= "Time:  not OK\t$time neq " . ${"${code}_time"} . "\n";
    }
  }
  $test_string .= "\n";
}

sub round {
  $num = shift;
  $power = shift;
  return int($num*10**$power+.5)/10**$power;
}

# 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;
}
