#/usr/local/bin/perl

# process command line var settings
$idle = -1;
$dot = 60;
$colon = 10;
($me = $0) =~ s%.*/%%;
sub another { print stderr "Interrupted!\n"; exit 1;}
$SIG{'INT'} = 'another';

eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;

# parse switches
while ($ARGV[0] =~ /^-/) {
   $ARGV[0] =~ /^-a/ && ($all++,shift,next);
   $ARGV[0] =~ /^-d/ && ($debug++,shift,next);
   $ARGV[0] =~ /^-n/ && ($idle = 0,shift,next);
   $ARGV[0] =~ /^-w/ && ($window++,shift,next);
   $ARGV[0] =~ /^-l/ && ($long++,shift,next);
   $ARGV[0] =~ /^-b/ && ($broadcast++,shift,next);
   $ARGV[0] =~ /^-m/ && ($multicast++,shift,next);
   $ARGV[0] =~ /^-s/ && ($slumber++,shift,next);
   $ARGV[0] =~ /^-i/ && (shift,$idle=$ARGV[0],shift,next);
   last;
}

$hosts && $multicast++;

if ($idle =~ /[:h]/) {
       $hours = $minutes = 0;
       ($hours = $idle)   =~ s/[:h].*$//;
       ($minutes = $idle) =~ s/.*[:h]//;
       $idle = $hours * 60 + $minutes;
       if ($debug) { print "you said $idle m idle\n"; }
} elsif ($idle < 0) {
   $idle = $dot;
}

$sleep = 60 unless $sleep;
$idle = 999E10 if $all;

if ( $ARGV[0] =~ /^-/ ) {
   select (stderr);
   printf "usage: %s [vars] [-b | -m] [-a] [-n] [-l] [-w] [-s] [target ...]\n",
                       $me;
   printf "\t-b to broadcast to the ruserd\n";
   printf "\t-m to multicast to the ruserd\n";
   printf "\t-a for ALL, even very idle people\n";
   printf "\t-n for people active NOW short idles\n";
   printf "\t-l for LONG listing\n";
   printf "\t-w to dynamically compute WINDOW size\n";
   printf "\t-s to sleep and repeat until all targets found\n";
   printf "\tvars \$idle ($idle) and \$sleep ($sleep) can be set with var=value\n";
   printf "\t     as can \$hosts, \$dot ($dot), and \$colon ($colon)\n";
   exit 1;
}

# compute width according to window size

$ENV{"TERMCAP"} =~ /:co#(\d+):/ && ($cols = int($1/25));

if ( $window ) {
   open(win,"(stty all > /dev/tty ) 2>&1 |") || die "can't run stty";
   while (<win>) {
       chop; split;
       $cols = int($_[7]/ 25);
       last;
   }
   close win;
}

$cols = 3 unless $cols;

if ($multicast && !$hosts) {
   $machines = '/usr/adm/MACHINES';
   open machines || die "$me: can't open $machines: $!\n";
   open(saveout, ">&stdout"); # much cheaper than invoking sh
   close(stdout);

   while (<machines>) {
       next if /norpc/;
       chop;
       s/\s.*//;
       if (do ping($_)) { # no sh here!
           #print stderr $_, " ok\n";
           push(@hosts,$_);
       } else {
           #printf stderr "%s is down!\n", $_;
       }
   }
   open(stdout, ">&saveout");
   close machines;
   close saveout;
   $debug && print "hosts are: ", join(' ',@hosts),"\n";
}

restart:

$count = $#ARGV + 1;

$PIPE = "/usr/ucb/rwho";
if ($broadcast || $multicast) {
   $PIPE = "/usr/ucb/rusers -l ";
   $multicast && $PIPE .= $hosts ? $hosts : join(' ',@hosts);
}
$PIPE .= "|sort|";
$debug && printf "PIPE is %s", $PIPE;
open PIPE || die "$me: can't popen \"$PIPE\": $!\n";

PIPEline: while (<PIPE>) {
   s/-ex0//;
   $matched = 0;
   if ( $count ) {
       matchcheck: for ( $i = 0; $i < $count; $i++ ) {
           $str = $ARGV[$i];
           $matched = /$str/;
           if ( $matched ) {
               if ($debug) { printf "YES: `%s' MATCH <%s>\n", $str, $_ ; }
               last matchcheck; # break
           } else {
               if ($debug) { printf "NO: `%s' DIDN'T <%s>\n", $str, $_ ; }
           }
       }
       if ( ! $matched ) {
           next PIPEline;
       }
   }

   $_[5] = "";
   split;

   $myidle = 0;
   if ( $_[5] =~ /^[:0-9]*$/  ) {
       $hours = $minutes = 0;
       if ( $_[5] =~ /[0-9]:/ ) {
           $hours = $_[5];
           $hours =~ s/:.*$//;
       }
       $minutes = $_[5];
       $minutes =~ s/^.*://;
       $myidle = $hours * 60 + $minutes;
   }

   next PIPEline if $myidle > $idle;

   $found{$str}++ if ($slumber);

   if ( $long ) {
       print;
   } else {
       if ($debug) { printf "%s@%s is %d\n", $_[0],$_[1],$myidle; }
       if ($myidle < $dot && $myidle > $colon) {
           $_[1] =~ s/:/./;
       }  elsif ($myidle > $dot) {
           $_[1] =~ s/:/ /;
       }
       push(users,sprintf("%-9s%15s", $_[0], $_[1]));
   }
}
exit if $long && ! $slumber;

$rows = int(($#users+$cols) / $cols);

# { printf "found %d matches\n", $#users+1; }
# { printf "rows are %s, cols are %s\n", $rows, $cols; }

if ($slumber && $restarted && $#users >= $[) {
       printf "\n%s: bingo! at %s%c\n", $me, `date`, 7;
}

for ($elt = 0; $elt < $rows * $cols; $elt++) {
   $targ =  ($elt%$cols) * $rows + int(($elt/$cols));
   printf "%s ", $targ < ($#users+1) ? $users[$targ] : "";
   #if ($debug) { #printf "%d ", $targ < ($#users+1) ? $targ : -1; }

   print "\n" if (($elt+1) % $cols) == 0;
}

print "\n" if ($elt+1) % $cols == 0;

close PIPE;

if ($slumber) {
       @nargv = ();
       @users = ();
       for ($i = 0; $i < $count; $i++) {
               ! $found{$ARGV[$i]} && push(@nargv,$ARGV[$i]);
       }
       @ARGV = @nargv;

       if ($#ARGV >= $[) {
           exit if ($forked++ == 0 && fork);
           sleep $sleep;
           $restarted = 1;
           ($whoami = `who am i`) =~ s/.*!([^ ]*).*/$1/;
           exit if $whoami != $ENV{"USER"};
           goto restart;
       }
}


sub ping {
   local($host) = @_;

   if (fork) {
       wait;
       exit if $? & 0xff; # interrupted
       return $? == 0;
   }  else {
       exec 'pong', $host, 1;
   }
}