#!/afs/athena/contrib/perl/perl
require 'timelocal.pl';

sub getone {
# ok. I know it's a crock, but in the interest of speed,
# well...
# this subroutine expects the input file to be in the
# standard .stats format, and to be opened on a stream
# called FI.  In addition, the values are passed back via
# global variables. (naughty, naughty....)

#    $foo = 0;
#    while ($foo == 0) {
#	read(FI, $foo, 1);
#	$foo = unpack("C", $foo);
#    }
#    seek(FI, -1, 1);
    read(FI, $tmstamp, 4);
    $tmstamp = unpack("N", $tmstamp);
    $auth = ($tmstamp > 0x7fffffff || $tmstamp < 0) ? 0 : 1;
    $auth || $tmstamp &= 0x7fffffff;
    read(FI, $zlen, 1);
    $zlen = unpack("C", $zlen);
    if ($zlen == 255) {
	read(FI, $zlen, 2);
	$zlen = unpack("n", $zlen);
    }
    read(FI, $zsiglen, 1);
    $zsiglen = unpack("C", $zsiglen);
    if ($zsiglen == 255) {
	read(FI, $zsiglen, 2);
	$zsiglen = unpack("n", $zsiglen);
    }
    read(FI, $zrest, 1);
    $zrest = unpack("C", $zrest);
    if ($zrest == 255) {
	read(FI, $zrest, 2);
	$zrest = unpack("n", $zrest);
    }
    $/="\0";
    $_=<FI>;
    $zsender = substr($_, 0, length($_) - 1);
    $_=<FI>;
    $zinst = substr($_, 0, length($_) - 1);
}

%msgcount=();
@sortedarray=();
@outarray=();
$fname = "/mit/bitbucket/zephyr-log/.stats";
$foo = "foo!";
$ncols = 1;
$mlimit = 0;
$title = "";
$mvalue = ".*";
$sender = 0;
$inst = 1;
$count = 1;
$since = 0;
$pairs = 0;

PARSECMD:
while ($foo ne "") {
    $foo = shift;
    if ($foo eq "-f") {
	$fname = shift;
	next PARSECMD;
    }
    if ($foo eq "-p") {
	# do sender/instance pairs
	$pairs = 1;
	$sender = 0;
	$inst = 0;
	next PARSECMD;
    }
    if ($foo eq "-s") {
	# select particular sender
	$mvalue = shift;
	$pairs = 0;
	$sender = 1;
	$inst = 0;
	next PARSECMD;
    }
    if ($foo eq "-i") {
	# match one instance
	$mvalue = shift;
	$pairs = 0;
	$sender = 0;
	$inst = 1;
	next PARSECMD;
    }
    if ($foo eq "-t") {
	# generate top instances
	$pairs = 0;
	$sender = 1;
	$inst = 0;
	next PARSECMD;
    }
    if ($foo eq "-tl") {
	# sort by total message length
	$totlen = 1;
	$count = 0;
	next PARSECMD;
    }
    if ($foo eq "-sl") {
	# sort by sig length
	$siglen = 1;
	$count = 0;
	next PARSECMD;
    }
    if ($foo eq "-ml") {
	# sort by message length
	$msglen = 1;
	$count = 0;
	next PARSECMD;
    }
    if ($foo eq "-c") {
	# use n columns
	$ncols = shift;
	next PARSECMD;
    }
    if ($foo eq "-l") {
	# limit the number of displayed ranks
	$mlimit = shift;
	next PARSECMD;
    }
    if ($foo eq "-T") {
	# change title for zwrite
	$title = shift;
	next PARSECMD;
    }
    if ($foo eq "-S") {
	$_=shift;
	($sincey,$sincem,$sinced)=/(\d{2})(\d{2})(\d{2})/;
	$sincem--;
	$since=&timelocal(0,0,0,$sinced,$sincem,$sincey,0,0,0);
	next PARSECMD;
    }
    if ($foo eq "-sf") {
	$showfirst = 1;
	next PARSECMD;
    }
    if ($foo eq "-z") {
	$zwrite = 1;
	next PARSECMD;
    }
    if ($foo eq "-zp") {
	$zwrite = 1;
	$zp = 1;
	$rx = shift;
    }
}

open(FI, $fname) || die "couldn't open $fname";
print STDERR "reading .stats ... ";
$start = (times)[0];
if ($pairs) {
    if ($totlen) {
	while (!eof(FI)) {
	    &getone;
	    ($tmstamp <= $since) ||
		$msgcount{join(" ", $zsender, $zinst)} +=
		    $zlen;
	}
    }
    if ($siglen) {
	while (!eof(FI)) {
	    &getone;
	    ($tmstamp <= $since) ||
		$msgcount{join(" ", $zsender, $zinst)} +=
		    $zsiglen;
	}
    }
    if ($msglen) {
	while (!eof(FI)) {
	    &getone;
	    ($tmstamp <= $since) ||
		$msgcount{join(" ", $zsender, $zinst)} +=
		    ($zlen - $zsiglen - $zrest);
	}
    }
    if ($count) {
	while (!eof(FI)) {
	    &getone;
	    ($tmstamp <= $since) ||
		$msgcount{join(" ", $zsender, $zinst)}++;
	}
    }
}
if ($sender) {
    if ($totlen) {
	while (!eof(FI)) {
	    &getone;
	    (($tmstamp <= $since) || !($zsender =~ /$mvalue/)) ||
		$msgcount{$zinst} += $zlen;
	}
    }
    if ($siglen) {
	while (!eof(FI)) {
	    &getone;
	    (($tmstamp <= $since) || !($zsender =~ /$mvalue/)) ||
		$msgcount{$zinst} += $zsiglen;
	}
    }
    if ($msglen) {
	while (!eof(FI)) {
	    &getone;
	    (($tmstamp < $since) || !($zsender =~ /$mvalue/)) ||
		$msgcount{$zinst} += $zlen - $zsiglen - $zrest;
	}
    }
    if ($count) {
	while (!eof(FI)) {
	    &getone;
	    (($tmstamp <= $since) || !($zsender =~ /$mvalue/)) ||
		$msgcount{$zinst}++;
	}
    }
}
if ($inst) {
    if ($totlen) {
	while (!eof(FI)) {
	    &getone;
	    (($tmstamp <= $since) || !($zinst =~ /$mvalue/)) ||
		$msgcount{$zsender} += $zlen;
	}
    }
    if ($siglen) {
	while (!eof(FI)) {
	    &getone;
	    (($tmstamp <= $since) || !($zinst =~ /$mvalue/)) ||
		$msgcount{$zsender} += $zsiglen;
	}
    }
    if ($msglen) {
	while (!eof(FI)) {
	    &getone;
	    (($tmstamp <= $since) || !($zinst =~ /$mvalue/)) ||
		$msgcount{$zsender} += $zlen - $zsiglen - $zrest;
	}
    }
    if ($count) {
	while (!eof(FI)) {
	    &getone;
	    (($tmstamp <= $since) || !($zinst =~ /$mvalue/)) ||
		$msgcount{$zsender}++;
	}
    }
}

if ($showfirst) {
    seek(FI, 0, 0);
    read(FI, $foo, 4);
    $since = unpack("N", $foo);
    $auth = ($since > 0x7fffffff || $since < 0) ? 0 : 1;
    $auth || $since &= 0x7fffffff;
}

$end = (times)[0];
printf(STDERR "(%.2f) ", $end - $start);
print STDERR "done.\nassembling array... ";
$start = (times)[0];
foreach (keys(%msgcount)) {
    push(@outarray,join(" ",$msgcount{$_},$_));
}
$end = (times)[0];
printf(STDERR "(%.2f) ", $end - $start);
print STDERR "done.\nsorting array... ";
$start = (times)[0];
@sortedarray = sort(mysort @outarray);
$end = (times)[0];
printf(STDERR "(%.2f) ", $end-$start);
print STDERR "done.\ngenerating output... ";
$start = (times)[0];
$#outarray = -1;
if ($mlimit && $mlimit < $#sortedarray) {
    $#sortedarray = $mlimit - 1;
}
$_ = $mvalue;
/^\^(.*)\$$/ && ($mvalue = $1);
if (!$title) {
    $title = "Top " . ($#sortedarray + 1);
    if ($pairs) {
	$title .= " zwriter-instance pairs";
    }
    else {
	if ($inst) {
	    $title .= " zwriters to";
	    if ($mvalue eq ".*") {
		$title .= " public instances";
	    }
	    else {
		$title .= " -i " . $mvalue;
	    }
	}
	else {
	    $title .= " instances";
	    if ($mvalue ne ".*") {
		$title .= " written to by " . $mvalue;
	    }
	}
    }
    $title .= " sorted by";
    $count && $title .= " total number of messages";
    $totlen && $title .= " total zephyrgram length (kb)";
    $siglen && $title .= " total signature length (kb)";
    $msglen && $title .= " total message-body length (kb)";
    if ($since) {
	$title .= "\nsince ";
	($day, $month, $year, $wday)=(localtime($since))[3..6];
	$title .= (join(" ", ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")[$wday],
			("Jan", "Feb", "Mar", "Apr", "May", "Jun",
			 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")[$month],
			$day, (1900 + $year)));
    }
}
$title .= "\n"; 
if ($count) {
    for ($foo = 0; $foo <= $#sortedarray; $foo++) {
	$_ = $sortedarray[$foo];
	push(@outarray, sprintf("%4d %s",/(\d+)\s+(.+)/));
	if (($foo + 1) % 10 == 0 && $foo != 0 && $foo != $#sortedarray) {
	    push(@outarray, sprintf("   ****%2d****", $foo + 1));
	}
    }
}
else {
    for ($foo = 0; $foo <= $#sortedarray; $foo++)
    {
	$_ = $sortedarray[$foo];
	/(\d+)\s+(.+)/;
	push(@outarray, sprintf("%5.1f  %s", $1/1024, $2));
	if (($foo + 1) % 10 == 0 && $foo != 0 && $foo != $#sortedarray)
	{
	    push(@outarray, sprintf("   ****%2d****", $foo + 1));
	}
    }
}
$end = (times)[0];
printf(STDERR "(%.2f) ", $end-$start);
print STDERR "done.\n";

if ($zwrite) {
    if ($zp) {
	$argstr = "|zwrite -n -s \"$title\n\" $rx";
    }
    else {
	$argstr = "|zfwrite -U \"Mr. Stats\" -H dragons-lair -n -i zstats -s \"$title\n\"";
    }
    open(ZWRITE, $argstr);
    select(ZWRITE);
    print "@courier{\n";
}
else {
    print $title;
}
for ($foo = 0; $foo <= int(($#outarray + 1) / $ncols); $foo++) {
    $cwidth = int(79 / $ncols);
    for ($foo2 = 0; $foo2 < $ncols; $foo2++) {
	($foo * $foo2 <= $#outarray) &&
	    printf("%-${cwidth}.${cwidth}s",
		   $outarray[$foo + $foo2 * (int($#outarray / $ncols) + 1)]);
    }
    print "\n";
}
if ($zwrite) {
    print "}";
    close ZWRITE;
}
sub mysort {
  @1 = split(" ", $a);
  @2 = split(" ", $b);
  $2[0] <=> $1[0];
}
