#!/usr/pkg/bin/perl
# $Id: mergelog,v 1.5 2007/01/31 17:07:02 abs Exp $
# Free software - no warranty

use Cwd;
use Getopt::Long;
use strict;
use warnings;

my $help_and_usage;
my $dry_run;

my $result = GetOptions(
    'help'      => \$help_and_usage,
    'dry-run|n' => \$dry_run,
);

if ( !$result || $help_and_usage || !@ARGV ) {
    print qq{Usage: mergelog [opts] file [files..]

options:
 --dry-run        Show details but do not modify any files
 --help           This help

mergelog merges multiple log files into per month .xz compressed files

};
    exit;
}

my $tmpfile = "TMP.mergelog.$$.xz";

sub mergedir {
    my ( $dir, @files ) = @_;
    my %target;
    my $olddir = getcwd;
    chdir($dir);
    if ( !@files ) {
        opendir( DIR, '.' ) || die("Unable to opendir '$dir': $!");
        push( @files, grep( $_ ne '.' && $_ ne '..', readdir(DIR) ) );
        closedir(DIR);
    }
    foreach my $file (@files) {
        my ( $base, $month, $extra, $ext );
        if ( $file =~ /^(.+\D\d{4}-?)(\d\d)(.*)(\.bz2|\.gz|\.7z|\.xz)$/ ) {
            ( $base, $month, $extra, $ext ) = ( $1, $2, $3, $4 );
        }
        elsif ( $file =~ /^(.+\D\d{4}-?)(\d\d)-?(\d+)$/ ) {
            ( $base, $month, $extra, $ext ) = ( $1, $2, $3, '' );
        }
        else {
            die("Unparsable filename '$file'");
        }
        my $targetfile = "$base$month";
        if ( $file ne "$targetfile.xz" ) {
            push( @{ $target{$targetfile}{$ext} }, $file );
        }
    }
    foreach my $targetfile ( sort keys %target ) {
        print "$targetfile:";
        print "\n" if $dry_run;
        if ( -f "$targetfile.xz" ) {
            run_command("xz -d $targetfile.xz");
        } elsif ( -f "$targetfile.7z" ) {
            run_command("7za x $targetfile.7z");
        }
        elsif ( -f $targetfile ) {
            safe_unlink($targetfile);
        }

        foreach my $ext ( keys %{ $target{$targetfile} } ) {
            my @files = @{ $target{$targetfile}{$ext} };
            my $cmd;
            if    ( $ext eq '.xz' ) { $cmd = 'xzcat'; }
            elsif ( $ext eq '.bz2' ) { $cmd = 'bzcat'; }
            elsif ( $ext eq '.gz' )  { $cmd = 'zcat'; }
            elsif ( $ext eq '.7z' )  { $cmd = '7zcat'; }
            elsif ( $ext eq '' )     { $cmd = 'cat'; }
            else                     { die "Do not know how to extract '$ext'" }
            $cmd .= " @files >> $targetfile";
            run_command($cmd);
        }

        run_command("xz -9v -c $targetfile > $tmpfile");
        die("$tmpfile too small") if !$dry_run && -s $tmpfile < 100;
        safe_rename( $tmpfile, "$targetfile.xz" );
        foreach my $ext ( keys %{ $target{$targetfile} } ) {
            safe_unlink( $targetfile, @{ $target{$targetfile}{$ext} } );
        }
        print "\n";
    }
    chdir($olddir);
}

my @files;
foreach my $arg (@ARGV) {
    if   ( -d $arg ) { mergedir($arg); }
    else             { push( @files, $arg ); }
}
@files && mergedir( '.', @files );

sub run_command {
    my @cmd = @_;
    if ($dry_run) {
        print "  @cmd\n";
    }
    elsif ( system(@cmd) ) {
        die "@cmd failed: $!";
    }
}

sub safe_rename {
    my ( $to, $from ) = @_;
    if ($dry_run) {
        print "  rename( $to, $from )\n";
    }
    else {
        rename( $to, $from ) || die("rename($to, $from) failed: $!");
    }
}

sub safe_unlink {
    my (@files) = @_;

    if ($dry_run) {
        print "  unlink(@files)\n";
    }
    else {
        unlink(@files) || die("unlink(@files) failed: $!");
    }
}
