#! /bin/perl --    # -*-Perl-*-
#
# inflow-stat - display summaries of inflow.sum data files as provided
#               by the inflow-collect script  
#
# History: The inflow-package is based on the ideas of counter.pl, a perl
#          script written by Juan Garcia, Rediris, 1995-96.
#
# 960806 V1.0 released
# 960826 V1.0.1 fixed altgroups-table length (Juan Garcia)
# 960917 V1.0.2 added debugging stuff to track down sorting problems
#               which show up on certain servers only. 
# 960918 V1.0.3 more debugging, system perl info included
# 960919 V1.0.4 sorting problem fixed (Gerhard Winkler)
# 960930 V1.0.5 fixed bug concerning output to STDOUT
# 961216 V1.0.6 fixed bug in resultfile header parsing
# 970113 V1.2   modified some default paths, renumbered version
# 970129 V1.2.1 easier portability: made system commands configurable
#               fixed unproper file closings
# 970421 V1.2.2 more flexible outfile naming 
# 971018 V1.2.3 improved handling of search path and html-includes 
# 971025 V1.2.4 added opt. external config file
# 971109 V1.3.3 article delay stats support
# 971112 V1.5.0 version number aligned
# 971118 V1.5.1 minor bug fix html-table without avg_delay column
# 980408 V1.6.0 incorporated URLs to neighbour servers with monitoring
#
$Copy="(c) 1997 Felix.Kugler\@switch.ch";
$RELDATE = "Wed Apr  8 12:00:24 MET DST 1998";
$RELEASE = "V1.6.0";
#
# local settings ----------------------------------------------------
#
$INFLOWCONF = "/home/news/config/inflow.conf"; # ext. local configs (optional)
#
# the following settings may be modified by an external config file
#
$SUMDIR    = "/home/news/inflow";        # source data (summaryfile)
$SUMFILE   = "inflow.sum";
$HTMLDIR   = "/opt/www/docs/news/stat";  # where to put HTML output 
$TEXTDIR   = "/home/news/status";        # where to put ASCII output
$URLLOOKUP = "/home/news/config/olm-lookup"; # lookup table for URLs
$NODETABLE = "pernodetraf";	         # These names may be completed with  
$HIERTABLE = "perhiertraf";	         #  local hostname + ".html" extension.
$ALTHTABLE = "peralthtraf";	         #  Effective only if -F is selected.
$WANTHOSTNAME = 0;                       # add host to outfile name ( -host) ?
$GENERICTITLE = "Status of incoming feeds";
#
$LOCALTZ   = "MET DST";
#
$MINLEN    = 20;		# default length of node name field
$MAXLEN    = 32;		# max length of node name field
$TABLESIZE = 20;		# default table length
$WANTDELAY = 1;                 # show delay stats in separate column 
                                # set to 0 if you dislike approximative data
                                # (many user agents seem to run with 
                                # unaccurate clocks or wrong timezones)
#
# local HTML stuff: you can put custom header & footer information into 
#                   local files; optional...
#
$HTMLHEADER = "/home/news/config/inflowhtmlheader";
$HTMLFOOTER = "/home/news/config/inflowhtmlfooter";
# 
$PLOTSCRIPT = "inflow-plot -w"; # script to plot data
#
# external programs & config files 
@ADDTOPATH = ( '/opt/local/bin','/opt/gnu/bin' );
#
$GREP      = "grep";
# 
# end local settings ------------------------------------------------
#
# initialisation ----------------------------------------------------
#

require "getopts.pl";

($path,$0) = ($0 =~ /^(.*)\/([^\/]+)$/);  # strip path...
umask 022;			          # file mask

&Getopts('df:hpwa:g:n:AFGNV');

&modify_config;
&update_PATH;
&gethostandfqdn;		# construct fully qualified domain name...
&get_urllookup;

$Usage="$0    -  $Copy

Release $RELEASE  of $RELDATE

Process information from inflow summary file $SUMDIR/$SUMFILE
and show articles received by node and hierarchy in either ASCII or
HTML format.

Usage: 	$0 [-dhpwAFGLNV][-a<tablelen>][-f<configfile>]
        [-g<tablelen>][-n<tablelen>] [<sum_file>]

Parameters:

   -d:            turn on verbosity; for debugging only
   -h:            This help.
   -p:            plot collected data.
   -w:            WWW support: show results in HTML table format
   -a<tablelen>:  Length of alt hierarchy table. Default is $TABLESIZE,
                  tablelen = 0 means unlimited.
   -A:            Do not create the alt hierarchy table, even if the required
                  data is present in <sum_file>.
   -f<configfile>:load external configuration file (default: $configfile)
   -F:            Write to files defined in script header. Default is STDOUT. 
   -g<tablelen>:  Length of hierarchy (group) table. Default is $TABLESIZE,
                  tablelen = 0 means unlimited.
   -G:            Do not create hierarchy (group) table.
   -N:            Do not create node table.
   -V:            Sort tables on traffic volume. Default is sorting based  
                  on number of successfully transmitted articles.
   -n<tablelen>:  Length of node table. Default is $TABLESIZE.
                  tablelen = 0 means unlimited.
   <sum_file>:    Read data file <sum_file>.
                  Default is $SUMDIR/$SUMFILE

external config file: $inflowconfinfo

Local HTML headers and footers may optionally be loaded:

header file: $HTMLHEADER
footer file: $HTMLFOOTER

Lookup table for URLs pointing to neighbouring server's monitoring pages 
(this feature is automatically disabled if no valid file can be found):

$URLLOOKUP

Actual tables can be downloaded from 
ftp://ftp.switch.ch/info_service/netnews/wg/config/olm-lookup 
\n";


# process command line

$summaryfile = $#ARGV>=0 ? $ARGV[0] : "$SUMDIR/$SUMFILE";
$nodetablelen = defined($opt_n) ? $opt_n : $TABLESIZE;
$grouptablelen = defined($opt_g) ? $opt_g : $TABLESIZE;
$altgrouptablelen = defined($opt_a) ? $opt_a : $TABLESIZE;
die "silly command line options !\n\n$usage\n" if ($opt_G && $opt_N && $opt_A);

if ($opt_F) {
    $suffix = $WANTHOSTNAME ? "-$hostname" : "";
    if ($opt_w) {
	$nodetable = "$HTMLDIR/$NODETABLE$suffix.html";
	$hiertable = "$HTMLDIR/$HIERTABLE$suffix.html";
	$althtable = "$HTMLDIR/$ALTHTABLE$suffix.html";
    } else {
	$nodetable = "$TEXTDIR/$NODETABLE$suffix";
	$hiertable = "$TEXTDIR/$HIERTABLE$suffix";
	$althtable = "$TEXTDIR/$ALTHTABLE$suffix";
    }
}
$titleline = "$GENERICTITLE on $fqdn";

if ($opt_h) { print "$Usage"; exit 0; }

## Current date. Needed for HTML headers.
&Getdate();

#  check for custom header & footer files
if ($opt_w) {
    if (open(IN,$HTMLHEADER)) {  
	while (<IN>) {
	    $line = &interpret;
	    $htmlheader .= $line;
	}
	close(IN); 
    } else {
	$htmlheader = "<html><head>
          <!--Content-type: text/html\nRefresh: 60-->\n
          <title>$titleline</title></head><body><h2>$titleline</h2>\n";
    }
    if (open(IN,$HTMLFOOTER)) { 
	while (<IN>) {
	    $line = &interpret; 
	    $htmlfooter .= $line; 
	}
	close(IN);
    } else {
	$htmlfooter = "</body></html>\n";
    }
}

# check for non-empty summaryfile...
unless (-f "$summaryfile") {
    die "couldn't open $summaryfile - aborting...\n";
}

&readresults;

#
# prepare data
#
$begin = &Convdate($firstart);
$last = &Convdate($lastart);
$duration = $lastart - $firstart;
$total = $Tvolume = $Tarticles = $articles = 0;	

# per node
while (($rsite,$articles) = each(%arts)) {
    push(@persite,"$rsite:$articles:$bytes{$rsite}:$delay{$rsite}");
    $Tvolume += $bytes{$rsite};
    $Tarticles += $articles;
    $Tdelay += $delay{$rsite};
    $rsitekeylen = &getstrlength($rsitestrlen,$rsite);
}

# per toplevel hierarchy
while (($group,$articles)=each(%artsph)) {
    push(@perhier,"$group:$articles:$bytesph{$group}:$delayph{$group}");
    $groupkeylen = &getstrlength($groupkeylen,$group);
}
# optional detailed table for alt.* groups
if (!$opt_A && defined(%artspah)) {   
    while (($group,$articles)=each(%artspah)) {
	push(@peralthier,"$group:$articles:$bytespah{$group}:$delayph{$group}");
	$altgroupkeylen = &getstrlength($altgroupkeylen,$group);
    }
}

# produce output...

unless ($opt_F) {
    open(OUT, ">-") || die "unable to open STDOUT: $!\n";
    &initpage;
}
&printnodetable unless $opt_N;
&printgrouptable unless $opt_G;
&printaltgrouptable unless ($opt_A || ! defined(%artspah));

unless ($opt_F) {
    &closepage;
    close(OUT) || die "unable to close STDOUT: $!\n";
}

if ($opt_p && $PLOTSCRIPT ne "") { exec("$path/$PLOTSCRIPT"); }


# initpage
# -----------------------------------------------------------------------------
# Print Page Headers 
#
sub initpage {
    if ($opt_w) { 
	print OUT "$htmlheader\n";
    } else {
        print OUT "$titleline\n\n";
        print OUT "calculated at: $date\n\n";
    }
}


# closepage 
# -------------------------------------------------------------
# close page, write footers...   
#
sub closepage {
    if ($opt_w) {
        print OUT "</table></center><P>

<FONT SIZE=\"-1\">Statistics created by $0 $RELEASE.
<A HREF=\"$INFLOWURL\">inflow</A> is a project of the
<A HREF=\"http://www.switch.ch/netnews/wg/netnews-wg.html\">
RIPE netnews-wg</A>.</FONT><P><P>
$htmlfooter\n";
    } else {
        print OUT "\n\n";
    }
}


# setup_table
# -----------------------------------------------------------------------
# set table header, data row format and TOTAL string
#
sub setup_table {
    local($title,$firstcol,$keylen) = @_;

    $TotalStr="TOTALS";
    if ($opt_w ) {                   # html-formatted output
#warn "setup_table: avgdelay_valid=$avgdelay_valid  WANTDELAY=$WANTDELAY\n";
	if ($avgdelay_valid && $WANTDELAY) {
#warn "delay ok: avgdelay_valid=$avgdelay_valid...\n";
	    print OUT "<center><TABLE BORDER>
                <caption><font size='+1'><b>$title</b></font><p>
                <b>calculated at:</b> $date </caption>
            <TR><TD></TD>
                <TD align=center colspan=2><b>Articles</b></TD>
                <td align=center colspan=3><b>Volume</b></TD>
                <td align=right><b>avg_size</b></TD>
                <td align=right><b>avg_del</b></TD></TR>
            <TR><TD><b>$firstcol</b></TD>
                <TD align=right><b>count</b></TD>
                <td align=right><b>%</b></TD>
                <td align=right><b>Mbytes</b></TD>
                <td align=right><b>%</b></TD>
                <td align=right><b>kbps</b></TD>
                <td align=right><b>kB</b></TD>
                <td align=right><b>hours</b></TD></TR> \n";
    	    $out_form="<tr><b>
                <td align=left>%${keylen}s</TD>
                <td align=right>%7d</TD>
                <td align=right>%6.2f</TD>
                <td align=right>%8.3f</TD>
                <td align=right>%6.2f</TD>
                <td align=right>%6.2f</TD>
                <td align=right>%7.3f</b></TD>
                <td align=right>%6s</b></TD></TR> ";
	    $diff_out_form="<tr></TR> <tr>
                <td align=left>%${keylen}s</TD>
                <td align=right>%7d</TD>
                <td align=right>%6s</TD>
                <td align=right>%8.3f</TD>
                <td align=right>%6s</TD>
                <td align=right>%6.2f</TD>
                <td align=right>%7.3f</TD>
                <td align=right>%6s</TD></TR> ";
	    $bold_out_form="<tr></TR> <tr>
                <td align=left><b>%${keylen}s</b></TD>
                <td align=right><b>%7d</b></TD>
                <td align=right><b>%6.2f</b></TD>
                <td align=right><b>%8.3f</b></TD>
                <td align=right><b>%6.2f</b></TD>
                <td align=right><b>%6.2f</b></TD>
                <td align=right><b>%7.3f</b></TD>
                <td align=right><b>%6s</b></TD></TR>";
	} else {               
#warn "delay notok: avgdelay_valid=$avgdelay_valid...\n";
	    print OUT "<center><TABLE BORDER>
                <caption><font size='+1'><b>$title</b></font><p>
                <b>calculated at:</b> $date </caption>
            <TR><TD></TD>
                <TD align=center colspan=2><b>Articles</b></TD>
                <td align=center colspan=3><b>Volume</b></TD>
                <td align=right><b>avg_size</b></TD></TR> 
            <TR><TD><b>$firstcol</b></TD>
                <TD align=right><b>count</b></TD>
                <td align=right><b>%</b></TD>
                <td align=right><b>Mbytes</b></TD>
                <td align=right><b>%</b></TD>
                <td align=right><b>kbps</b></TD>
                <td align=right><b>kB</b></TD></TR> \n";
    	    $out_form="<tr><b>
                <td align=left>%${keylen}s</TD>
                <td align=right>%7d</TD>
                <td align=right>%6.2f</TD>
                <td align=right>%8.3f</TD>
                <td align=right>%6.2f</TD>
                <td align=right>%6.2f</TD>
                <td align=right>%7.3f</b></TD></TR> ";
	    $diff_out_form="<tr></TR> <tr>
                <td align=left>%${keylen}s</TD>
                <td align=right>%7d</TD>
                <td align=right>%6s</TD>
                <td align=right>%8.3f</TD>
                <td align=right>%6s</TD>
                <td align=right>%6.2f</TD>
                <td align=right>%7.3f</TD></TR> ";
	    $bold_out_form="<tr></TR> <tr>
                <td align=left><b>%${keylen}s</b></TD>
                <td align=right><b>%7d</b></TD>
                <td align=right><b>%6.2f</b></TD>
                <td align=right><b>%8.3f</b></TD>
                <td align=right><b>%6.2f</b></TD>
                <td align=right><b>%6.2f</b></TD>
                <td align=right><b>%7.3f</b></TD></TR>"; 
	}
    } else {                           # plain text output
	if ($avgdelay_valid && $WANTDELAY) {
	    $dashes = 87 - length($title);
	    printf(OUT "%s %s\n\n", $title, "-" x $dashes);
	    printf(OUT "%${keylen}s %16s %23s %7s %9s %6s\n\n",
		   " ", "received_art's", "received_volume", "kbps", 
		   "avg_size", "   delay");
	    $out_form = $bold_out_form = 
		"%${keylen}.${keylen}s %7d (%5.1f%%) %12.6f MB (%5.1f%%) %6.1f %6.2f kB %6s h";
	    $diff_out_form = 
		"%${keylen}.${keylen}s %7d (%5.1f%%) %12.6f MB (%5.1f%%) %6.1f %6.2f kB %6s ";
	} else {
	    $dashes = 79 - length($title);
	    printf(OUT "%s %s\n\n", $title, "-" x $dashes);
	    printf(OUT "%${keylen}.${keylen}s %16s %23s %7s %9s\n\n",
		   " ", "received_art's", "received_volume", "kbps", "avg_size");
	    $out_form = $bold_out_form = $diff_out_form =
		"%${keylen}.${keylen}s %7d (%5.1f%%) %12.6f MB (%5.1f%%) %6.1f %6.2f kB";
	}
    }
}

# size
# --------------------------------------------------------------------------
# sort an array of strings of format "field1:field2:field3" according to 
# the (numeric) field2 or field3.
#
sub size {
	local($x1,$x2,$x3,$y1,$y2,$y3,$res);
	($x1,$x2,$x3)=split(/:/,$a);
	($y1,$y2,$y3)=split(/:/,$b);
	$res = $opt_V ? $y3<=>$x3 : $y2<=>$x2; # by volume : by no of articles
}

# Getdate
# --------------------------------------------------------------------------
# get date from localtime
#
sub Getdate {    
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);

    $month=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
    $day=(Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$wday];
    $cyear="19".$year;
    $cmin=sprintf("%02d",$min);
    $date="$day $month $mday $hour:$min:$sec $LOCALTZ $cyear";
}


# Convdate
# --------------------------------------------------------------------------
# get date & time from ctime style data string
#
sub Convdate {    
    local($ctime) = @_;
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($ctime);

    $month=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
    $day=(Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$wday];
    $cyear="19".$year;
    $cmin=sprintf("%02d",$min);
    $date=sprintf("%s %s %2d %02d:%02d:%02d $LOCALTZ %s",
	 $day, $month, $mday, $hour, $min, $sec, $cyear);
    return $date;
}



# readresults
# ----------------------------------------------------------------------
# read existing resultfile
#
sub readresults {
    local($par,$val,$startday,$thisday,$ho,$mi);
    unless (open(SUM,"$summaryfile")) {
	warn "missing $summaryfile\n";
	return;
    }
    warn "reading results from $summaryfile...\n" if $opt_d;
    $firstart = $lastart = '';
    while(<SUM>) {		#  read header
	chop;
	unless (/^\#/) { seek(SUM,0,0); last; }
	next if (/^\#\s*$/);
        ($par,$val)  = /^\# ([^:]+): +(\S*)/;
        warn "resultfile header: par=$par  val=$val\n" if $opt_d;
        if ($par eq 'host') { $fqdn = $val; }
        elsif ($par eq 'firstart') { $firstart = $val; }
        elsif ($par eq 'lastart') { $lastart = $val; }
        elsif ($par eq 'lastperiod') { $lastperiod = $val; }
        elsif ($par eq 'lastarts') { $lastarts = $val; }
        elsif ($par eq 'lastbytes') { $lastbytes = $val; }
        elsif ($par eq 'delay_valid') { $avgdelay_valid = $val; }
    }
    while (<SUM>) {		# read summary data
	($par,$key,$cnt,$vol,$del) = split;
	if ($par eq 'pn') { 
	    $arts{$key} += $cnt; 
	    $bytes{$key} += $vol; 
	    $delay{$key} += $del;
	}
        elsif ($par eq 'ph') {
            $artsph{$key} += $cnt; 
	    $bytesph{$key} += $vol;
	    $delayph{$key} += $del;
        }
        elsif ($par eq 'pah') {
            $artspah{$key} += $cnt;
	    $bytespah{$key} += $vol;
	    $delaypah{$key} += $del;
        }
    }
    close(SUM);
}

# getstrlength
# ----------------------------------------------------------------------
# return actual lenght of a string field within $MINSIZE and $MAXSIZE 
# boundaries
#
sub getstrlength {
    local($oldlen,$str) = @_;
    local($newlen) = length($str);
    if ($len > $oldlen) { 
	$len = $oldlen;		# 
	if ($len > $MAXLEN) { $len = $MAXLEN; }	
    }
    if ($len < $MINLEN) { $len = $MINLEN; }	
    return $len;
}


# printnodetable
# --------------------------------------------------------------------------
# Print counters by node
sub printnodetable {
    my $truncated;

    if ($opt_F) {
	open(OUT,">$nodetable") || die "unable to open $nodetable: $!\n";
	&initpage;
    }

    $addtotitle = $opt_V ? ", sorted by volume" : "";
    $addtotitle .= $nodetablelen>0 ? " - Top $nodetablelen" : "";
    &setup_table("NetNews received by node$addtotitle","Node",$rsitekeylen);

    $i=0;

    if ($opt_d) {		# print @persite
	local($item,$x1,$x2,$x3,$sysinfo);
	$sysinfo = `uname -a`; 
	print STDERR "Debugging output from $0 on $fqdn\n\n";
	print STDERR "platform info: $sysinfo\n"; 
	print STDERR "perl info: $]";
	print STDERR "\n\@persite unsorted:\n\n";
	printf(STDERR "%8s %12s  %s\n", "articles", "Bytes", "site");
	foreach $item (@persite) {
	    ($x1,$x2,$x3,$x4)=split(/:/,$item);
	    printf(STDERR "%8d %12d %16d %s\n", $x2, $x3, $x4, $x1);
	}
	if ($opt_V) {
	    print STDERR "\n\@persite sorted by volume:\n\n";
	} else {
	    print STDERR "\n\@persite sorted by articles:\n\n";
	}
	printf(STDERR "%8s %12s %16s %s\n", 
	       "articles", "Bytes", "delaysum", "site");
    }

    foreach (sort size @persite) {
       	($rsite,$articles,$bytes,$delay)=split(/:/);
	if ($opt_d) {		# print @persite sorted
	    printf(STDERR "%8d %12d %16d %s\n",
		   $articles, $bytes, $delay, $rsite);
	}
	if ($use_links && $opt_w) {
	    if ($srvrname{$rsite}) {
		$rsite = "<A HREF=\"$moni_url{$srvrname{$rsite}}\">$rsite</A>";
		warn "URL ($rsite): $moni_url{$srvrname{$rsite}}\n" if $opt_d;
	    } else {
		($truncated) = ($rsite =~ /^[^.]+\.([^.]+\..*)$/);
		if ($truncated ne "" && $srvrname{$truncated}) {
		    warn "URL (truncated: $truncated): $moni_url{$srvrname{$truncated}}\n" if $opt_d;
		    $siteentry = "<A HREF=\"$moni_url{$srvrname{$truncated}}\">$rsite</A>";
		} else { warn "no URL available for $rsite...\n" if $opt_d; }
	    }
	}
	$perc_bytes = $Tvolume > 0 ? $bytes/$Tvolume*100 : 0;
	$perc_art = $Tarticles > 0 ? $articles/$Tarticles*100 : 0;
	$avg_size = $articles > 0 ? $bytes/($articles*1000) : "";
	$rate = $duration > 0 ? $bytes/(125*$duration) : 0;
	if ($avgdelay_valid eq "yes" && $WANTDELAY) {
	    $avg_delay = $articles>0 ?
		sprintf("%6.2f", $delay/($articles * 3600)) :
		sprintf("%6.2f", 0);
	} else { $avg_delay = ""; }
	printf(OUT "$out_form\n",
	       $rsite,$articles,$perc_art,$bytes/1000000,
	       $perc_bytes,$rate,$avg_size,$avg_delay);
	last if ($nodetablelen>0 && ++$i >= $nodetablelen);
    }
    # summary line...
    $avg_size = $Tarticles > 0 ? $Tvolume/($Tarticles*1000) : "";
    $rate = $duration > 0 ? $Tvolume/(125*$duration) : 0;
    if ($avgdelay_valid eq "yes" && $WANTDELAY) {
	$avg_delay = $Tarticles>0 ?
	    sprintf("%6.2f", $Tdelay/($Tarticles * 3600)) :
                sprintf("%6.2f", 0);
    } else { $avg_delay = ""; }
    printf(OUT "\n$bold_out_form\n\n",
	   $TotalStr,$Tarticles,100,$Tvolume/1000000,100,$rate,
	   $avg_size,$avg_delay);

    $avg_size = $lastarts > 0 ? $lastbytes/($lastarts*1000) : "";
    $rate = $lastperiod > 0 ? $lastbytes/(125*$lastperiod) : 0;
    printf(OUT "\n$diff_out_form\n\n",
	   "Last $lastperiod seconds",$lastarts,"",$lastbytes/1000000,
	   "",$rate,$avg_size);

    if ($opt_F) { &closepage; close(OUT); }
}


# printgrouptable
# --------------------------------------------------------------------------
# Print counters by hierarchy
sub printgrouptable {
    if ($opt_F) {
	open(OUT,">$hiertable") || die "unable to open $hiertable: $!\n";
	&initpage;
    }

    $addtotitle = $opt_V ? ", sorted by volume" : "";
    $addtotitle .= $grouptablelen>0 ? " - Top $grouptablelen" : "";
    &setup_table("NetNews received by hierarchy$addtotitle",
		 "Hierarchy",$groupkeylen);
    
    $i=0;

    foreach (sort size @perhier) {
	($group,$articles,$bytes,$delay) = split(/:/);
	if ($opt_d) {		# print @persite sorted
	    printf(STDERR "%8d %12d %16d %s\n", 
		   $articles, $bytes, $delay, $group);
	}
	$perc_bytes = $Tvolume > 0 ? $bytes/$Tvolume*100 : 0;
	$perc_art = $Tarticles > 0 ? $articles/$Tarticles*100 : 0;
	$avg_size = $articles > 0 ? $bytes/($articles*1000) : "";
	$rate = $duration > 0 ? $bytes/(125*$duration) : 0;
        if ($avgdelay_valid eq "yes" && $WANTDELAY) {
            $avg_delay = $articles>0 ?
                sprintf("%6.2f", $delay/($articles * 3600)) :
                sprintf("%6.2f", 0);
        } else { $avg_delay = ""; }
	printf(OUT "$out_form\n",$group,$articles,$perc_art,
	       $bytes/1000000,$perc_bytes,$rate,$avg_size,$avg_delay);
	last if ($grouptablelen>0 && ++$i >= $grouptablelen);
    }
    # close table
    if ($opt_w) { print OUT "</table></center><P><P>\n"; }
    if ($opt_F) { &closepage; close(OUT); }
}


# printaltgrouptable
# --------------------------------------------------------------------------
# Print counters for alt.* hierarchy
sub printaltgrouptable {
    if ($opt_F) {
	open(OUT,">$althtable") || die "unable to open $althtable: $!\n";
	&initpage;
    }

    $addtotitle = $opt_V ? ", sorted by volume" : "";
    $addtotitle .= $altgrouptablelen>0 ? " - Top $altgrouptablelen" : "";
    &setup_table("Details for alt.* hierarchy$addtotitle",
		 "Hierarchy",$altgroupkeylen);
    
    $i=0;
    foreach (sort size @peralthier) {
	($group,$articles,$bytes) = split(/:/);
	$perc_bytes = $Tvolume > 0 ? $bytes/$Tvolume*100 : 0;
	$perc_art = $Tarticles > 0 ? $articles/$Tarticles*100 : 0;
	$avg_size = $articles > 0 ? $bytes/($articles*1000) : "";
	$rate = $duration > 0 ? $bytes/(125*$duration) : 0;
        if ($avgdelay_valid eq "yes" && $WANTDELAY) {
            $avg_delay = $articles>0 ?
                sprintf("%6.2f", $delay/($articles * 3600)) :
                sprintf("%6.2f", 0);
        } else { $avg_delay = ""; }
	printf(OUT "$out_form\n",$group,$articles,$perc_art,
	       $bytes/1000000,$perc_bytes,$rate,$avg_size,$avg_delay);
	last if ($altgrouptablelen>0 && ++$i >= $altgrouptablelen);
    }
    # close table
    if ($opt_w) { print OUT "</table></center>\n"; }
    if ($opt_F) { &closepage; close(OUT); }
}


# gethostandfqdn
#---------------------------------------------------------------------- 
# construct fully qualified domain name...
sub gethostandfqdn {
    chop($str=`uname -n`);
    if ($str =~ /\./) {             # str is fqdn
	$fqdn = $str;
	($hostname) = ($str =~ /^([^.]+)\./);
    } else {                        # str is simple hostname
	$hostname = $str;
	$str = `$GREP domain /etc/resolv.conf`;
	$str =~ /domain\s*(\S+)$/;
	$fqdn = $hostname . "." . $1;
    }
}

# get_urllookup
#----------------------------------------------------------------------
# read URL info from config file
#
# file format:
# <name> <srvrname> <operator_code> <monitoring-URL>
sub get_urllookup {
    if ($URLLOOKUP eq "") {
	warn "no lookup table specified\n";
	$use_links = 0;
	return;
    }
    unless (open(LOOKUP,"$URLLOOKUP")) {
        warn "unable to open lookup table $URLLOOKUP\n";
        $use_links = 0;
        return;
    }
    $use_links = 1;
    while (<LOOKUP>) {
	chop;
	next if (/^\#|^$/);
	($name,$srvr,$code,$url) = split;
	$srvrname{$name} = $srvr;
	$operatedby{$srvr} = $code;
	$moni_url{$srvr} = $url;
    }
}


# update_PATH
# -----------------------------------------------------------------------------
# enhance PATH to support scripts run from cron
#
sub update_PATH {
    warn "OLD PATH=$ENV{'PATH'}\n" if $opt_d;
    @ENVPATH = split(/:/,$ENV{'PATH'});
    push(@ENVPATH,@ADDTOPATH);
    
    $ENV{'PATH'}='';
    foreach $p (@ENVPATH) {
	next if $envpathseen{$p}; # skip duplicates
	$envpathseen{$p} = 1;
	$ENV{'PATH'} .= $p . ":";
    }
    chop($ENV{'PATH'});           # cut last ":"
    warn "NEW PATH=$ENV{'PATH'}\n" if $opt_d;
}



# interpret
# -----------------------------------------------------------------------------
# interpret variable read from file; variables have to be separated by
# white space
#
sub interpret {
    local($line) = ""; 
    @words = split;
    foreach $w (@words) {
	if ($w =~ /^\$/) { eval "\$w=$w"; }
	$line .= "$w ";
    }
    return $line;
}


# modify_config
# -----------------------------------------------------------------------------
#
sub modify_config {
    local($p,$n);
    $inflowconf = $opt_f ? $opt_f : $INFLOWCONF;
    if ($inflowconf =~ /^(.*)\/([^\/]+)$/) {
	$p = $1; 
	$n = $2;
	push(@INC,$p);
    } else { 
	$n = $inflowconf; 
    }
    warn "p=$p  n=$n  inflowconf=$inflowconf\n" if $opt_d;
    if (-e $inflowconf) {
	warn "loading local configs from $inflowconf...\n" if $opt_d;
	require $n;
	$inflowconfinfo = "$inflowconf $CFRELEASE\n";
	$inflowconfinfo .= " " x 16 . "from: $CFRELDATE";
    } else {
	warn "didn't find any local configs $inflowconf on INC=@INC\n" if $opt_d;
	$inflowconfinfo = "- none -";
    }
}

