#! /usr/bin/env perl

#
#   Copyright (C) Heinz-Josef Claes (2000-2012)
#                 hjclaes@web.de
#   
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.

#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


push @VERSION, '$Id: multitail.pl 362 2012-01-28 22:11:13Z hjc $ ';

sub libPath
{
    my $file = shift;

    my $dir;

    # Falls Datei selbst ein symlink ist, solange folgen, bis aufgelöst
    if (-f $file)
    {
	while (-l $file)
	{
	    my $link = readlink($file);

	    if (substr($link, 0, 1) ne "/")
	    {
		$file =~ s/[^\/]+$/$link/;
	    }
	    else
	    {
		$file = $link;
	    }
	}

	($dir, $file) = &splitFileDir($file);
	$file = "/$file";
    }
    else
    {
	print STDERR "<$file> does not exist!\n";
	exit 1;
    }

    $dir .= "/../lib";           # Pfad zu den Bibliotheken
    my $oldDir = `/bin/pwd`;
    chomp $oldDir;
    if (chdir $dir)
    {
	my $absDir = `/bin/pwd`;
	chop $absDir;
	chdir $oldDir;

	return (&splitFileDir("$absDir$file"));
    }
    else
    {
	print STDERR "<$dir> does not exist, exiting\n";
    }
}
sub splitFileDir
{
    my $name = shift;

    return ('.', $name) unless ($name =~/\//);    # nur einfacher Dateiname

    my ($dir, $file) = $name =~ /^(.*)\/(.*)$/s;
    $dir = '/' if ($dir eq '');                   # gilt, falls z.B. /filename
    return ($dir, $file);
}
my ($req, $prog) = &libPath($0);
push @INC, "$req";

require "checkParam2.pl";
require "tail.pl";
require "prLog.pl";
require 'version.pl';

=head1 NAME

multitail.pl - Read multiple log files. The log files can be written round.

=head1 SYNOPSIS

	multitail.pl [-a] [-d delay] [-p begin|end] [-t]
                [-o outFile [-m max] [-P]
		 [[-n noFiles] | [-s [-c compressprog]] ]
		] files...

=head1 OPTIONS

=over 8

=item B<-a>, B<--addName>

    add filename to the output at the beginning of each line

=item B<-d>, B<--delay>

    delay in sec. between checking the files (default 5 sec)

=item B<-p>, B<--position>

    read from begin or end of file (default = begin)

=item B<-t>, B<--withTime>

    with current time and date in the output

=item B<-o>, B<--out>

    write output to file

=item B<-m>, B<--maxFilelen>

    maximal len of file written round (default = 1e6)

=item B<-n>, B<--noOldFiles>

    number of old files to store

=item B<-P>, B<--withPID>

    write pid to log file (default is not)

=item B<-H>, B<--withHostname>

    write hostname to log file (default is not)

=item B<-l>, B<--maxlines>

    maximal number of lines to read per --delay in one chunk
    from a log file (default = 100)
    this value multiplied with [-d] is the number read at once

=item B<-s>, B<--saveLogs>

    save log files with date and time instead of deleting the
    old (with [-noOldFiles])

=item B<-c>, B<--compressWith>

    compress saved log files (e.g. with -c 'gzip -9')

=item B<-V>

    print version(s)

=back

=head1 COPYRIGHT

Copyright (c) 2001-2008 by Heinz-Josef Claes (see README)
Published under the GNU General Public License or any later version

=cut

my $Help = join('', grep(!/^\s*$/, `pod2text $0`));
$Help = "cannot find pod2text, see documentation for details\n"
    unless $Help;

&printVersions(\@ARGV, '-V');

$CheckPar = CheckParam->new('-allowLists' => 'yes',
			    '-list' => [
					Option->new('-name' => 'addName',
						    '-cl_option' => '-a',
						    '-cl_alias' => '--addName'),
					Option->new('-name' => 'delay',
						    '-cl_option' => '-d',
						    '-cl_alias' => '--delay',
						    '-default' => 5),
					Option->new('-name' => 'position',
						    '-cl_option' => '-p',
						    '-cl_alias' => '--position',
						    '-default' => 'begin',
						    '-pattern' =>
						    '^begin$|^end$' # '
						    ),
					Option->new('-name' => 'withTime',
						    '-cl_option' => '-t',
						    '-cl_alias' => '--withTime'),
					Option->new('-name' => 'out',
						    '-cl_option' => '-o',
						    '-cl_alias' => '--out',
						    '-param' => 'yes'),
					Option->new('-name' => 'maxFilelen',
						    '-cl_option' => '-m',
						    '-cl_alias' => '--maxFilelen',
						    '-default' => 1e6),
					Option->new('-name' => 'noOldFiles',
						    '-cl_option' => '-n',
						    '-cl_alias' => '--noOldFiles',
						    '-param' => 'yes',
						    '-only_if' =>
				'[pit] and not ( [saveLogs] or [compressWith])'),
					Option->new('-name' => 'withPID',
						    '-cl_option' => '-P',
						    '-cl_alias' => '--withPID'),
					Option->new('-name' => 'withHostname',
						    '-cl_option' => '-H',
						    '-cl_alias' => '--withHostname'),
					Option->new('-name' => 'maxlines',
						    '-cl_option' => '-l',
						    '-cl_alias' => '--maxlines',
						    '-default' => 100,
						    '-pattern' => '^\d+$' # '
						    ),
					Option->new('-name' => 'saveLogs',
						    '-cl_option' => '-s',
						    '-cl_alias' => '--saveLogs',
						    '-only_if' =>
						    '[out] and not [noOldFiles]'),
					Option->new('-name' => 'compressWith',
						    '-cl_option' => '-c',
						    '-cl_alias' => '--compressWith',
						    '-param' => 'yes',
						    '-only_if' =>
						    '[out] and not [noOldFiles]')
					]
			    );

$CheckPar->check('-argv' => \@ARGV,
                 '-help' => $Help
                 );

$delay = $CheckPar->getOptWithPar('delay');
$addName = 1 if ($CheckPar->getOptWithoutPar('addName'));
$position = $CheckPar->getOptWithPar('position');
$withTime = ($CheckPar->getOptWithoutPar('withTime')) ? 'yes' : 'no';
$out = $CheckPar->getOptWithPar('out');
$maxFilelen = $CheckPar->getOptWithPar('maxFilelen');
$noOldFiles = $CheckPar->getOptWithPar('noOldFiles');
$withPID = $CheckPar->getOptWithoutPar('withPID') ? 'yes' : 'no';
$hostname = $CheckPar->getOptWithoutPar('withHostname') ? `hostname -f` : '';
chomp $hostname;
$maxlines = $CheckPar->getOptWithPar('maxlines');
$saveLogs = $CheckPar->getOptWithoutPar('saveLogs');
$compressWith = $CheckPar->getOptWithPar('compressWith');

unless ($CheckPar->getNoListPar())
{
    print "$Help";
    exit 1;
}

# Initialisierung
my @files;
my $file;
my $iter = Iter_ParList->new($CheckPar);
while ($file = $iter->next())
{
    my $f = ($addName) ? "$file " : '';
    push @files, tailOneFile->new('-filename' => $file,
				  '-position' => $position,
				  '-prefix' => $f,
				  '-maxlines' => $maxlines * $d)
}


# Ausgabeobjekt erzeugen
@fileout = ('-file' => $out) if ($out);
$prLog = printLog->new(@fileout,
		       '-withTime' => $withTime,
		       '-maxFilelen' => $maxFilelen,
		       '-noOfOldFiles' => $noOldFiles,
		       '-saveLogs' => $saveLogs,
		       '-compressWith' => $compressWith,
		       '-withPID' => $withPID,
		       '-hostname' => $hostname
		       );


my $i = 0;
while (42)
{
    my ($l, $e) = $files[$i]->read();
    chop @$l;
#    print "@$l\n";
    $prLog->pr(@$l);
    $prLog->pr("*** $e ***") if $e;

    ++$i;
    $i %= @files;
    select(undef, undef, undef, $delay / @files); # sleep in seconds
}

