#!/usr/bin/perl
#
# $Id: ptail.pl,v 1.8 2003/01/13 05:28:43 jmates Exp $
#
# Copyright (c) 2001, Jeremy Mates.  This script is free software;
# you can redistribute it and/or modify it under the same terms as
# Perl itself.
#
# Run perldoc(1) on this file for additional documentation.
#
######################################################################
#
# REQUIREMENTS

require 5;

#use strict;			# disabled to allow more flexible
# expression composition

######################################################################
#
# MODULES

use Carp;         # better error reporting
use Getopt::Std;  # command line option processing

use File::Tail;   # tail -f on steroids

######################################################################
#
# VARIABLES

my $VERSION;
($VERSION = '$Revision: 1.8 $ ') =~ s/[^0-9.]//g;

my (%opts, $filename, $file);

# various File::Tail object preferences.  listen much faster than default
# to emulate tail(1) at expense of more busy wait.
my $maxinterval = 2;
my $interval    = 1;
my $adjustafter = 1;
my $tail        = 0;
my $reset_tail  = -1;

# default to just printing input
my $expression = "print";

# commands to be executed before entring the loop
my $preop = "";

######################################################################
#
# MAIN

# parse command-line options
getopts('h?m:i:a:t:r:p:', \%opts);

help() if exists $opts{'h'} or exists $opts{'?'} or not @ARGV;

$maxinterval = $opts{'m'} if exists $opts{'m'} and $opts{'m'} =~ /^\d+$/;
$interval    = $opts{'i'} if exists $opts{'i'} and $opts{'i'} =~ /^\d+$/;
$adjustafter = $opts{'a'} if exists $opts{'a'} and $opts{'a'} =~ /^\d+$/;
$tail        = $opts{'t'} if exists $opts{'t'} and $opts{'t'} =~ /^-?\d+$/;
$reset_tail  = $opts{'r'} if exists $opts{'r'} and $opts{'r'} =~ /^-?\d+$/;

$preop = $opts{'p'} if exists $opts{'p'};

# filename to tail on and perl expression to apply to each line
$filename   = shift;
$expression = "@ARGV" if @ARGV;

$file = File::Tail->new(
  name        => $filename,
  maxinterval => $maxinterval,
  interval    => $interval,
  adjustafter => $adjustafter,
  tail        => $tail,
  reset_tail  => $reset_tail,
);

# don't buffer STDOUT soas to interoperate with tee(1) properly
$| = 1;

# handle anything that needs to be done first
eval $preop;
die 'Preop failed: ', $@, if $@;

# loop forever on file; apply supplied expression to each line
while (defined($_ = $file->read)) {
  eval $expression;

  die 'Expression error: ', $@ if $@;
}

exit;

######################################################################
#
# SUBROUTINES

# a generic help blarb
sub help {
  print <<"HELP";
Usage: $0 [opts] file [expression]

tails a file and applies perl expression to each line

Options for version $VERSION:
  -h/-?   Display this message

  -p exp  Code to be executed before entering loop.

  -m sec  Check file for updates this often at most.
  -i sec  Initial sleep period before checking file.
  -a sec  Times to wait until adjusting check interval.

  -t num  Read num lines from file.  Negative is whole file.
  -r num  Lines to read after file reset.

Run perldoc(1) on this script for additional documentation.

HELP
  exit;
}

######################################################################
#
# DOCUMENTATION

=head1 NAME

ptail.pl - follows a file and applies a perl expression to each line

=head1 SYNOPSIS

Follow a logfile, and print any lines mentioning ipmon:

  $ ptail.pl /var/log/messages print if m/ipmon/

See L<"EXAMPLES"> for more ideas.

=head1 DESCRIPTION

=head2 Overview

This script is the functional equivalent of the tail(1) utility
running in follow mode, with the additional ability to apply an
arbitrary perl expression to each line seen in the file being tracked.

The primary use seen for this script is to extract out and manipulate
specific lines of interest from a busy logfile.

=head2 Normal Usage

  $ ptail.pl [options] file [expression]

See L<"OPTIONS"> for details on the command line switches supported.

The file to watch is required, and the expression defaults to "print"
to emulate the behaviour of a C<tail -f>.  The expression is built
from any number of arguments following the file, for greater
flexibility.  $_ is used to store each line in turn.

=head1 OPTIONS

This script currently supports the following command line switches:

=over 4

=item B<-h>, B<-?>

Prints a brief usage note about the script.

=item B<-p> I<expression>

Perl expression to be executed before entering the loop on the file.
Use to perform any initial one-time operations one might need.

=item B<-m> I<seconds> (maxinterval)

Check file for updates this often at most.

=item B<-i> I<seconds> (interval)

Initial sleep period before checking file.

=item B<-a> I<seconds> (adjustafter)

Times to wait until adjusting check interval.

=item B<-t> I<number> (tail)

Read num lines from file.  Negative is whole file.

=item B<-r> I<number> (reset_tail)

Lines to read after file reset.

=back

See L<File::Tail> for more information on the parenthesized parameter
names, which are File::Tail parameters passed directy to the object.

=head1 EXAMPLES

Walk through all of C</var/log/messages> before following the file,
printing lines pertaining to dhcpd:

  $ ptail.pl -t -1 /var/log/messages 'print if m/ dhcpd/'

Watch for the ip address 10.0.0.1 in C</var/log/system.log>, triming
the standard syslog leading information, and saving a copy of the data
to C</tmp/10.log>:

  $ ptail.pl /var/log/system.log 's/^\w{3}\s+\d+ [\d:]+ \w+ //; \
      print if m/\Q10.0.0.1\E/' | tee /tmp/10.log

=head1 BUGS

=head2 Reporting Bugs

Newer versions of this script may be available from:

http://sial.org/code/perl/

If the bug is in the latest version, send a report to the author.
Patches that fix problems or add new features are welcome.

=head2 Known Issues

No known bugs.

=head1 SEE ALSO

File::Tail, perl(1), tee(1)

=head1 AUTHOR

Jeremy Mates, http://sial.org/contact/

=head1 COPYRIGHT

Copyright (c) 2001, Jeremy Mates.  This script is free software;
you can redistribute it and/or modify it under the same terms as Perl
itself.

=head1 VERSION

  $Id: ptail.pl,v 1.8 2003/01/13 05:28:43 jmates Exp $

=cut
