#!/usr/bin/env perl
# watch.pl  Perl implementation of Linux 'watch' utility, suitable for Solaris,
#           AIX and other operating systems.
#
# 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. Revised: 2020-03-29

use strict;
use warnings;
use File::Basename;
use Getopt::Std;
use Term::Screen;
use vars qw($VERSION);
$VERSION=1.06;
$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);
my $BadOpt;
getopts('dn:',\%opts) or $BadOpt="Y";
die "Usage: ".basename($0)." [-d] [-n secs] Command\n".
    " e.g.: ".basename($0)." -n 1 \"ls -lt | head -19\"\n\n".
    "Options: -n     interval between updates (default=2)\n".
    "         -d     highlight changes between updates\n"
  if ( defined($BadOpt) or ($#ARGV<0) or ($opts{n}!~m/^\d+$/) );
my $command;
for my $a (@ARGV) {
  if( defined($command) ) {$command.=" ".$a}
  else                    {$command=$a     }
}

# Force screen-size initialisation, define heading, set exit trap
my $scr=new Term::Screen;
my ($oldcols,$oldrows)=(-1,-1);
my $heading="Every ".$opts{n}."s: ".$command;
my (@oldline);
$SIG{INT} = sub { done() };
sub done { $scr->flush_input()->clrscr(); exit(0) };

# Loop until trap is sprung
while (1) {
  # 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 current time
  $scr->at(0,0)->clreol()->puts(substr($heading,0,$cols-17));
  my $t=format_time(time());
  $scr->at(0,$cols-length($t))->puts($t);
  # Execute the command and place resultant lines in array
  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)
    }
    # Optionally highlight the lines which changed
    if ( defined($oldline[$j]) and ($oldline[$j] ne $line)
                               and defined($opts{'d'})     ) {
      $scr->at($j,0)->clreol()->bold()->puts($line)->normal()
    } else {
      $scr->at($j,0)->clreol()->puts($line)
    }
    $oldline[$j]=$line
  }
  sleep($opts{n})
}

=head1 NAME

watch - Runs a designated command repeatedly 

=head1 DESCRIPTION

C<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 
