#!/usr/bin/perl
#
# Log monitor - to watch ongoing activites in the io.log file
#
#
# Options:
# -x <call>             e(x)clude packets to/from this call
# -f <call>             (f)ocus this call exclusively
# -w <chars>		maximum (w)idth of lines monitored (default: no limit)
#
# If you want to scan a different log file you may specify the
# filename as an argument.
#
# DL6RAI -- Sun Dec  5 23:51:46 GMT 1999

require "getopts.pl";

use Time::Local;
use integer;

$| = 1;						# No buffering
($home) = (getpwnam('clx_us'))[7];
$prog_name = 'CLX log monitor v1.1';

%month = ("Jan", 1,"Feb", 2,"Mar", 3,
          "Apr", 4,"May", 5,"Jun", 6,
          "Jul", 7,"Aug", 8,"Sep", 9,
          "Oct",10,"Nov",11,"Dec",12);

$help = <<"NNNN";

$prog_name

Options:
        -x <call>       e(x)clude packets to/from this call
        -f <call>       (f)ocus this call exclusively
        -w <chars>      maximum (w)idth of lines monitored (default: no limit)
        -s <start>      start time to watch in log file
NNNN

$options = <<"NNNN";
Commands:
        q,x             E(x)it or (Q)uit Monitor
        h               Print (h)elp
        p               (P)rint status
        w <n>           Adjust line (w)idth <n> columns
        f <call>        (f)ocus on <call>
NNNN

# Defaults
$focus = "-";					# matches any call
$x_focus = "no-filtering";			# arbitrary string
$logfile = "$home/log/io.log";			# default log file
$ll = 0;					# unlimited
$starttime = -1;				# no start time

&Getopts('f:s:x:w:h');

if (length($opt_f) > 0) {
    $focus = $opt_f;
}

if (length($opt_x) > 0) {
    $x_focus = $opt_x;
}

if (length($opt_w) > 0) {
    $ll = $opt_w;
}

if ($opt_h) {
    print "$help\n\n$options\n\n";
    exit(1);
}

if (defined ($ARGV[0])) {
	$logfile = $ARGV[0];
	open(LOG,$logfile) || die "$0: cannot open $logfile\n";
	$line = <LOG>;
	$timestring = join(' ',(split(' ',$line))[0..2]);
	$starttime = &convert_time($timestring);
	close(LOG);
}

open(LOG,$logfile) || die "$0: cannot open $logfile\n";

if (length($opt_s) > 0) {
	$starttime = &convert_time($opt_s);
	$file_size = (stat($logfile))[7];
	$filepos = $file_size / 2;
	$interval = $file_size / 4;
	while($interval > 1) {
		seek(LOG,$filepos,0);
		$_ = <LOG>;
		exit if eof(LOG);
		$_ = <LOG>;
		$datetime = &convert_time(join(' ',(split)[0..2]));
		if ( $starttime > $datetime ) {
			$filepos = $filepos + $interval;
		} else {
			$filepos = $filepos - $interval;
		}
		end if ( $filepos >= $file_size || $filepos < 0 );
		$interval = $interval / 2;
		# print "INT: $interval\n";
		# print "POS: $filepos\n";
	}
}

&print_status;

if ( $starttime < 0 ) {
	seek(LOG,0,2);
} else {
	$now = time;
	$timediff = $now - $starttime;
	seek(LOG,$filepos,0);
	$t = <LOG>;	# dump the trash 
}

for (;;) {

	if ( $starttime >= 0 ) {
		$_ = <LOG>;
		$t = join(' ',(split)[0..2]);
	        $datetime = &convert_time($t);
		$now = time;
		$next = $datetime + $timediff - $now;
		&process_line($_);
		# print "SLEEP: $next\n";
		if ( $next > 0 ) {
			print "[$t]\n";
			sleep($next);
		}
	} else { 
		while (<LOG>) {
			&process_line($_);
		}
	}

	if ( $starttime < 0 ) {
		sleep 1;
		seek(LOG,0,1);			# check if file has changed
						# online-log
	} else {
		exit if eof(LOG);		# offline-log
	}

	# Check for command input from console
	if (&key_ready) {
		chop($cmd = <STDIN>);
		print "clx-mon => $cmd\n";	# echo the input

		if ( $cmd =~ /^[qx]/ ) {
			print "$prog_name finished\n";
			exit(0);	
		}
		elsif ( $cmd =~ /^h/ ) {
			print $options;
		}
		elsif ( $cmd =~ /^w/ ) {
			($ll) = (split(/ /,$cmd))[1];
			&print_status
		}
		elsif ( $cmd =~ /^f/ ) {
			($focus) = (split(/ /,$cmd))[1];
			&print_status
		}
		elsif ( $cmd =~ /^p/ ) {
			&print_status
		}
		elsif ( $cmd =~ /^$/ ) {
			# do nothing when null string
		}
		else { 
			print "*** no such command \"$cmd\"\n";
		}
	}

}


sub print_status {

	printf("$prog_name");
	printf(" -- Focus: \"%s\"",$focus) if $focus;
	printf(" -- X-Focus: \"%s\"",$x_focus) if ($x_focus ne "no-filtering");
	printf(" -- Width: %d",$ll) if $ll;
	printf("\n");
}

#
# Check for keyboard input - without waiting for it 
# tnx: comp.lang.perl.faq
#
sub key_ready {
	local($rin, $nfd);
	vec($rin, fileno(STDIN), 1) = 1;
	return $nfd = select($rin,undef,undef,0);
}

#
# Converts time into Unix format
#
sub convert_time() {
#	$_[0] =~ s/ /:/g;
	# print "CONV: $_[0]\n";
	($lit_month,$mday,$hour,$min,$sec) = split(/[: ]/,$_[0]);
	$mon = $month{$lit_month};
	$time = timegm($sec,$min,$hour,$mday,$mon-1,$year);
	return $time;
}

sub process_line() {

	if ($_[0] =~ /->/ || $_[0] =~ /<-/) {
		$_[0] =~ s/^.*: //;
		($call,$rest) = split(/:/,$_[0],2);
	
		$_ = $call;
		next if ! /$focus/;
		next if /$x_focus/;
	
		# Filter out any packets of other log_monitors 
		# running - otherwise we will have a monitor loop
		#
		next if ( $rest =~ "<-:" || $rest =~ "->:" );
	
		$call =~ s/ //g;
	
		chop($rest);
	
		# This is meant to break multi-line log lines
		# into single ones. A little problem ocurred with
		# M0AZM's callsign, so this is a fix to it.
		#
		if ( $rest =~ /^PC/ ) {
			@a = $rest;
		} else { 
			@a = split(/\^M/,$rest); 
		}
	
		foreach $i (@a) { 
			if ($ll && (length($i) > $ll) ) {
				$i = substr($i,0,$ll) . "|";
			}
			printf("%11s: %s\n",$call,$i); 
		}
	}
}
