#!/usr/bin/perl
#
# avg-pkg-build-time: display average build times of packages
# Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# $Id: avg-pkg-build-time,v 1.1.1.1 2004/09/17 13:17:41 frankie Exp $
#
# $Log: avg-pkg-build-time,v $
# Revision 1.1.1.1  2004/09/17 13:17:41  frankie
# Imported sources
#
# Revision 1.13  2000/10/19 08:48:11  rnhodek
# Check if /etc/sbuild.conf exists before including it.
#
# Revision 1.12  1999/07/13 07:27:39  rnhodek
# Use GDBM for time/space databases, as perl-5.004 seems not to contain
# DB_File anymore.
#
# Revision 1.11  1999/05/04 14:25:38  rnhodek
# Also can dump avg-space-db now.
#
# Revision 1.10  1999/02/01 09:29:35  rnhodek
# New option -f (--dbfile) to select DB on the command line.
#
# Revision 1.9  1998/11/26 07:04:28  james
# Handle 1 entry/n entries properly.
#
# Revision 1.8  1998/10/01 10:15:32  rnhodek
# Read sbuild.conf instead of wanna-build.conf; time db now defined
# there.
#
# Revision 1.7  1998/09/24 10:00:26  rnhodek
# Changed argument handling.
# Added --add to add a build time manually.
# New option --top to list ordered by build time.
#
# Revision 1.6  1998/09/17 11:10:23  rnhodek
# Added --dump mode for displaying raw contents.
# Align package names to 3 tab stops.
# Display also standard derivation (sigma).
#
# Revision 1.5  1998/09/17 09:28:18  rnhodek
# Fix typo.
#
# Revision 1.4  1998/09/17 09:27:07  rnhodek
# Need to open DB in read+write mode for --delete.
#
# Revision 1.3  1998/09/17 09:26:04  rnhodek
# Added --delete mode.
#
# Revision 1.2  1998/09/15 13:03:49  rnhodek
# Added cvs headers.
#

($main::HOME = $ENV{'HOME'})
	or die "HOME not defined in environment!\n";
package conf;
$HOME = $main::HOME;
require "/etc/sbuild.conf" if -r "/etc/sbuild.conf";
require "$HOME/.sbuildrc" if -r "$HOME/.sbuildrc";
package main;

die "No avg-time database defined\n" if !$conf::avg_time_db;

use strict;
use GDBM_File;
use File::Basename;

my $do_space = 0;
my $delmode = 0;
my $dumpmode = 0;
my $addmode = 0;
my $topmode = 0;
my $open_mode = GDBM_READER;
my $db_file = $conf::avg_time_db;

if (basename($0) =~ /space$/) {
	$do_space = 1;
	$db_file = $conf::avg_space_db;
}

while( @ARGV && $ARGV[0] =~ /^-/ ) {
	$_ = shift @ARGV;
	if (/^(-s|--space)$/) {
		$do_space = 1;
		$db_file = $conf::avg_space_db;
	}
	elsif (/^--delete$/) {
		$delmode = 1;
		$open_mode = GDBM_WRCREAT;
	}
	elsif (/^--dump$/) {
		$dumpmode = 1;
	}
	elsif (/^(-a|--add)$/) {
		$addmode = 1;
		$open_mode = GDBM_WRCREAT;
	}
	elsif (/^(-t|--top)$/) {
		$topmode = 1;
	}
	elsif (/^(-f|--dbfile)$/) {
		die "-f need argument\n" if !@ARGV;
		$db_file = shift @ARGV;
	}
}

my %db;
tie %db, 'GDBM_File', $db_file, $open_mode, 0664
	or die "Can't open db\n";

die "No packages given for --delete.\n" if $delmode && !@ARGV;

if ($addmode) {
	die "Args for --add must be package and a build time\n"
		if @ARGV != 2 || $ARGV[1] !~ /^[\d.:]+$/;
	my($pkg,$t) = @ARGV;

	if (!$do_space) {
		if ($t =~ /:/) {
			my @a = split( ':', $t );
			my $x;
			for( $t = 0; $x = shift @a; ) {
				$t = ($t * 60) + $x;
			}
		}
	}
	
	if (exists $db{$pkg}) {
		if (!$do_space) {
			my @times = split( /\s+/, $db{$pkg} );
			push( @times, $t );
			my $sum = 0;
			foreach (@times[1..$#times]) { $sum += $_; }
			$times[0] = $sum / (@times-1);
			$db{$pkg} = join( ' ', @times );
		}
		else {
			my $keepvals = 4;
			my @values = split( /\s+/, $db{$pkg} );
			shift @values;
			unshift( @values, $t );
			pop @values if @values > $keepvals;
			my ($sum, $n, $weight, $i) = (0, 0, scalar(@values));
			for( $i = 0; $i < @values; ++$i) {
				$sum += $values[$i] * $weight;
				$n += $weight;
			}
			unshift( @values, $sum/$n );
			$db{$pkg} = join( ' ', @values );
		}
	}
	else {
		$db{$pkg} = "$t $t";
	}
}
else {
	my $pkg;
	my @pkgs = sort sortfunc (@ARGV ? @ARGV : keys %db);
	foreach $pkg (@pkgs) {
		if (exists $db{$pkg}) {
			if ($delmode) {
				delete $db{$pkg};
				print "$pkg: deleted\n";
			}
			elsif ($dumpmode) {
				print "$pkg: $db{$pkg}\n";
			}
			else {
				if (!$do_space) {
					my @times = split( /\s+/, $db{$pkg} );
					my $t = $times[0];
					my($sum, $sumq) = (0, 0);
					foreach (@times[1..$#times]) {
						$sum += $_;
						$sumq += $_*$_;
					}
					my $sigma;
					$sigma = (@times <= 2) ? 0 :
						sqrt( ($sumq - $sum*$sum/(@times-1))/(@times-2) );
					printf "%s%02d:%02d:%02d (%d %s, sigma %02d:%02d:%02d)\n",
						   align($pkg), int($t/3600), int(($t%3600)/60),
						   int($t%60), @times-1, (@times == 2) ? "entry" : "entries", 
				           int($sigma/3600), int(($sigma%3600)/60), int($sigma%60);
				}
				else {
					my @values = split( /\s+/, $db{$pkg} );
					printf "%s%6dk (%dk latest)\n",
						   align($pkg), $values[0], $values[1];
				}
			}
		}
		else {
			print "$pkg: unknown\n";
		}
	}
}

untie %db;
exit 0;

sub align {
	my $str = shift;
	
	$str .= ":";
	my $l = length($str);
	$str .= "\t" if $l < 24;
	$str .= "\t" if $l < 16;
	$str .= "\t" if $l < 8;
	return $str;
}

sub sortfunc {
	if ($topmode) {
		my $tima = (split( /\s+/, $db{$a} ))[0];
		my $timb = (split( /\s+/, $db{$b} ))[0];
		return $timb <=> $tima;
	}
	else {
		return $a cmp $b;
	}
}
