#!/pro/bin/perl

use Tk;
use Time::localtime;

$width	= 80;

to_background ();
InitTop ();

MainLoop;

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

sub to_background
{
    setpriority $pid, $$, 20;
    my $pid = fork;
    if ($pid < 0) {
	print STDERR "Unable to run in the background, cannot fork: $!\n";
	exit $?;
	}
    $pid and exit 0;
    } # to_background

sub InitTop
{
    my $f;

    chomp ($HOST = `hostname`);

    $top = MainWindow->new (-cursor => "top_left_arrow", -name => "Top");
    $top->title ("XTOP $HOST");
    $top->bind ("<Key>q" => sub {$top->destroy});

    $f = $top->Frame (-relief => "flat")->pack (-fill => "both", -side => "top");
    $f->Label (-text => "System: $HOST")->pack (-fill => "both", -side => "left");
    $sysdate = ctime (time);
    $f->Label (-textvariable => \$sysdate)->pack (-fill => "both", -side => "right");

    $f = $top->Frame (-relief => "flat")->pack (-fill => "both", -side => "top");
    $sysload = "Load avarages: ";
    $f->Label (-textvariable => \$sysload)->pack (-fill => "both", -side => "left");

    $f = $top->Frame (-relief => "flat")->pack (-fill => "both", -side => "top");
    $sysproc = "0 processes: 0 sleeping, 0 running, 0 zombies";
    $f->Label (-textvariable => \$sysproc)->pack (-fill => "both", -side => "left");

    %state = (
	"0" => "nonexistent",
	"A" => "active     ",
	"S" => "sleeping   ",
	"W" => "waiting    ",
	"R" => "running    ",
	"I" => "idle       ",
	"Z" => "terminated ",
	"T" => "stopped    ",
	"X" => "growing    ");
    $ENV{UNIX95} = 1;
    $pslist = $top->Scrolled ("ROText",
	-scrollbars => "e",
	-wrap       => "none",
	-height     => 20,
	-width      => $width)->pack (
	    -expand => 1,
	    -fill   => "both",
	    -side   => "left");
    $pslist->insert ("end", "PROcess list\n");
    #$pslist->bind ("<Key>q" => sub {$top->destroy});

    UpdateTop ();
    $top->repeat (2000, \&UpdateTop);
    } # InitTop

sub pslist
{
    my $pcpu_ok = 1;	# Got patched ps from hp (02 Mar 1999)
    my @ps = $pcpu_ok
	? `ps -e -otty,pid,user,pri,nice,state,vsz,time,pcpu,args`
	: `ps -e -otty,pid,user,pri,nice,state,vsz,time,cpu,args`;
    
    shift @ps;		# Skip header, creating my own
    			# Not all unix flavours use the same -o syntax
    my $hdr = "   TTY    PID USERNAME PRI NI STATE      SIZE   TIME \%CPU COMMAND\n";

    my %cntproc = ();
    # Positions are based on reformatted output
    my @s = ( [ 52, 5],	# %CPU
	      [ 39, 6],	# SIZE
	      [ 45, 7],	# TIME
	      [ 27, 2],	# NICE
	      );
    @ps = sort {
	substr ($b, $s[0][0],$s[0][1]) <=> substr ($a, $s[0][0],$s[0][1]) ||
	substr ($b, $s[1][0],$s[1][1]) <=> substr ($a, $s[1][0],$s[1][1]) ||
	substr ($b, $s[2][0],$s[2][1]) cmp substr ($a, $s[2][0],$s[2][1]) ||
	substr ($a, $s[3][0],$s[3][1]) <=> substr ($b, $s[3][0],$s[3][1]) ||
	$a cmp $b;
	} map {
	    s/^\s+//;
	    my @x = split m/\s+/, $_, 10;
	    $x[0] =~ s/\?/-/;
	    $cntproc{$x[5]}++;
	    $x[5] =~ s/(.)/$state{$1}/;
	    my @t = ((reverse split m/:/, $x[7]), 0, 0, 0);
	    $x[7] = sprintf ("%4d:%02d", $t[2] * 60 + $t[1], $t[0]);
	    $pcpu_ok and $x[8] *= 100.;
	    sprintf "%6s %6d %-8s %3d %2d %-.8s %6d%6s%5.1f %s", @x;
	    } @ps;
    $sysproc = sprintf ("%d processes: %d sleeping, %d running, %d zombies",
	scalar @ps, $cntproc{S}, $cntproc{R}, $cntproc{Z});
#   unshift @ps, "0----+----1----+----2----+----3----+----4----+----5----+----6----+----7\n";
    join "", $hdr, @ps;
    } # pslist

sub ruptime
{
    my $r;

    ($r = `rup localhost`) =~ s/.*l(oad.*\d)/L$1/;
#   ($r = (grep m/^$HOST\b/, `ruptime`)[0]) =~
#	s/.*load\s+(.*\d)\s*/Load avarage: $1/;
    chomp $r;
    $r;
    } # ruptime

sub UpdateTop
{
    $sysdate = ctime (time);
    $sysload = ruptime;

    $pslist->delete ("1.0", "end");
    $pslist->insert ("end", &pslist);
    } # UpdateTop