#!/usr/bin/env perl
# watch.pl  Perl implementation of Linux 'watch' utility, suitable for Solaris,
#           AIX and other operating systems. Revised: 2012-07-25
#
# Copyright (c) 2012 Graham Jenkins <grahjenk@cpan.org>. All rights reserved.
# This program is free software; you can redistribute it and/or modify it under
# the same terms as Perl itself.

use lib "/home/grahjenk/perl5/lib"; # Adjust as appropriate
use strict;
use warnings;
use File::Basename;
use Getopt::Std;
use Term::Screen;
use vars qw($VERSION);
$VERSION=1.05;
$Getopt::Std::STANDARD_HELP_VERSION=1;

# Format-time subroutine
sub format_time { # Usage: format_time(integer)
  my ($s,$m,$h,$D,$M,$Y) = localtime($_[0]);
  return ($Y+1900).substr("00".($M+1),-2).substr("00".$D,-2)."-".
    substr("00".$h,-2).":".substr("00".$m,-2).":".substr("00".$s,-2)
}

# Set default delay, check usage, assemble command
my %opts=('n'=>2);
getopts('n:',\%opts);
die "Usage: ".basename($0)." [-n secs] Command\n".
    " e.g.: ".basename($0)." -n 1 \"ls -lt | head -19\"\n"
  if ( ($#ARGV<0) or ($opts{n}!~m/^\d+$/) );
my $command;
for my $a (@ARGV) {
  if( defined($command) ) {$command.=" ".$a}
  else                    {$command=$a     }
}

# Get initial screen size, loop until key is pressed
my $scr=new Term::Screen;
my ($oldcols,$oldrows)=(-1,-1);
my $heading=basename($0).": Hit any key to quit!"; # First-pass heading
my (@oldline);
until ( $scr->key_pressed() ) {
  # Get current screen size; if it changed, clear each line
  $scr->resize();
  my ($cols,$rows)=($scr->cols(),$scr->rows());
  if ( ($cols!=$oldcols) or ($rows!=$oldrows) ) {
    $scr->clrscr();
    for (my $j=1;$j<$rows;$j++) { $oldline[$j]="" }
    ($oldcols,$oldrows)=($cols,$rows)
  }
  # Display heading and time, adjust heading for subsequent passes
  $scr->at(0,0)->clreol()->puts(substr($heading,0,$cols-17));
  my $t=format_time(time());
  $scr->at(0,$cols-length($t))->puts($t);
  $heading="Every ".$opts{n}."s: ".$command;
  # Execute the command and display resultant lines
  my @result=`$command`;
  for (my $j=1;$j<$rows;$j++) {
    my $line="";
    if (defined($result[$j-1])) {
      $line=substr($result[$j-1],0,$cols);
      chomp($line)
    }
    # Only display lines which changed
    if ( defined($oldline[$j]) and ($oldline[$j] ne $line) ) {
      $scr->at($j,0)->clreol()->puts($line);
      $oldline[$j]=$line
    }
  }
  sleep($opts{n})
}

# Clean-up and exit
$scr->flush_input()->clrscr();
exit(0);

=head1 NAME

watch - Runs a designated command repeatedly 

=head1 DESCRIPTION

watch is a perl implementation of the equivalent command found on many
Linux systems. It can be executed on Solaris, AIX and other systems.

=head1 README

A perl implementation of the watch command found on many Linux systems.

=head1 PREREQUISITES

This script requires C<Term::Screen>.

=head1 OSNAMES

linux, unix

=head1 SCRIPT CATEGORIES

UNIX/System_administration

=cut 
