#!/usr/local/bin/perl -w
# Ian Beckwith <ianb@nessie.mcc.ac.uk> 20031025
# $Id: mp3lintsum,v 1.8 2003/10/31 02:31:04 ianb Exp $

# data structure:
# $messages{dir}{startofmsg} => count
# $all{startofmsg} => count

use strict;
my $me=($0=~/(?:.*\/)?(.*)/)[0];

my %opts=();

# defaults
my $bydir = 1;
my $sum   = 1;

while( ($#ARGV>-1) && ($ARGV[0]=~/^-.+/) && ($_=shift))
{
	if    (/-d/) { $bydir = 1; }
	elsif (/-g/) { $bydir = 0; }
	elsif (/-c/) { $sum   = 1; }
	elsif (/-r/) { $sum   = 0; }
	elsif (/-h/) { usage(); }
	else { usage(); }
}

my $msg;
my %messages;
my %all;
my $lines=0;
while($msg=<>)
{
	$lines++;
	# from mp3lint or malformed line, just print
	if(($msg=~/^mp3lint/) ||
	   (scalar(my @a=split(/:/,$msg)) < 3))
	{
		print($msg);
		next;
	}
	# treat debug like normal messages.
	my($start,$optional,$file)=parsemsg($msg);
	my $key=$file;

	# strip filename off unless we are a dir
	if(-d $key)
	{
		# make sure / on end for consistency
		unless($key=~/\/$/)
		{
			$key .= "/";
		}
	}
	else
	{
		if($key=~/(.*\/).*/) { $key=$1; }
	}

	$messages{$key}{$start}++;
	$all{$start}++;
}


if($sum)
{
	if($bydir)
	{
		foreach my $k (sort keys(%messages))
		{
			my @vals=values(%{$messages{$k}});
			my $sum=add(@vals);
			my $problems=scalar(keys(%{$messages{$k}}));
			print("$k: ",stats($sum,$problems),"\n");
		}
	}
	my $totalmessages=add(map { values(%{$messages{$_}}) } keys(%messages));
	my $problems=scalar(keys(%all));
	my $dirs=scalar(keys(%messages));
	print("Total: ",stats($totalmessages,$problems,$dirs),"\n");
}
else
{
	if($bydir)
	{
		foreach my $dir (sort keys(%messages))
		{
			foreach my $msg (sort keys(%{$messages{$dir}}))
			{
				print "$dir:$msg\n";
			}
		}
	}
	else
	{
		foreach my $msg (sort keys(%all))
		{
			print "$msg\n";
		}
	}
}
   
	
sub add
{
	my @list=@_;
	my $tot=0;
	while(my $add=shift(@list))
	{
		$tot += $add;
	}
	return $tot;
}

sub parsemsg
{
	my $msg=shift;
	chomp($msg);
	my @msg=split(/:/,$msg);

	# debug is included in start if present
	my $offset=2;
	if($msg[0] eq 'debug') { $offset++ }
	my $start=join(':',@msg[0 .. $offset++]);
	my $left=$#msg-$offset;
	my $optional=undef;
	if($left>0)
	{
		# optional gets any spare fields
	    $optional=join(':',@msg[$offset .. ($#msg-1)]);
	}
	my $file=$msg[$#msg];
	return($start,$optional,$file);
		
}

sub stats
{
	# $dirs is optional
	my($total,$probs,$dirs)=@_;
	my $str = "$total message";
	if($total != 1) { $str .="s"; }
	$str .= ", $probs problem";
	if($probs != 1) { $str .="s"; }
	if(defined($dirs))
	{
		$str .=" in $dirs director";
		if($dirs != 1) { $str .="ies"; }
		else { $str .= "y"; }
	}
	return($str);
}
	
sub usage
{
	die( "Usage: $me [-d] [-g] [-c] [-r] [-h] [FILE|-]\n" .
		 "Produces a summary of mp3lint output.\n".
		 " -d\t\tBy directory (default)\n" .
		 " -g\t\tGlobal summary\n" .
		 " -c\t\tShow count of problems (default)\n" .
		 " -r\t\tRemove duplicate messages\n" .
		 " -h\t\tthis help\n".
		 "if FILE not supplied, uses STDIN.\n".
		 "e.g. mp3lint music/ | mp3lintsum -d -c\n"
		 );
}

__END__


=head1 NAME

mp3lintsum - Summarise mp3lint messages

=head1 SYNOPSIS

B<mp3lintsum> [I<-d>] [I<-g>] [I<-c>] [I<-r>] [I<-h>] [I<FILEE<verbar>->...]

=head1 DESCRIPTION

mp3lintsum is used to produce a summary of messages generated by
L<mp3lint(1)>. If there are a lot of problems, the output from mp3lint
can be overwhelming. mp3lintsum enables you to get a rough idea
of what needs fixing.

mp3lintsum can summarise either by directory (the default) or one
global summary. The summary can either consist of a count of problems
(the default) or show messages with duplicates (caused by multiple
files having the same problem) removed.

=head1 OPTIONS

Running mp3lintsum with no options is equivalent to running it with
options B<-dS< >-c>. If no filename is passed it reads from standard
input.

=over 4

=item B<-d>

Show a per-directory summary. This is the default.	

=item B<-g>

Show a global summary.

=item B<-c>

Show a summary of message count. This is the default.

=item B<-r>

Remove duplicate messages. This also removes the filename and the
"optional" section of the message (which may contain file-specific
data).

=item B<-h>

Show a short help message.

=item B<FILEE<verbar>->

Filename to read mp3lint output from. If not supplied or B<->, read
standard input.

=back

=head1 EXAMPLES

=over 4

=item B<mp3lint musicE<sol> E<verbar> mp3lintsum>

Produce a per-directory count of messages (with a total at the end).

=item B<mp3lint musicE<sol> E<verbar> mp3lintsum -g ->

Just print a final total.

=item B<mp3lint musicE<sol> E<verbar> mp3lintsum -g -r>

Display one of every message that occurred, with duplicates and
filenames stripped out.

=item B<mp3lint musicE<sol> E<verbar> mp3lintsum -d -r>	

As above, but per-directory. This means a message can be printed more
than once if it occurs in different directories.

=item B<mp3lint musicE<sol> E<verbar> tee out; mp3lintsum -g out>

As mp3lint can take a long time to run over a large collection of
music, it can be a good idea to redirect the output to a file. This
command displays the output of mp3lint, but also saves it to the file
F<out>. At the end mp3lintsum is used to provide a one-line summary.

=item B<mp3lint musicE<sol> E<verbar> tee E<sol>devE<sol>stderr E<verbar> mp3lintsum -g>

This is the same as above, only without leaving a file behind. It
requires a shell and/or OS that supports or emulates
F</dev/stderr>. Linux with F</proc> support and L<bash(1)> both work.

=back


=head1 BUGS

None known. Please report any found to ianb@nessie.mcc.ac.uk	

=head1 SEE ALSO    

L<mp3lint(1)>, L<mp3lint-tools(3)>, L<mp3lintskip(1)>,
L<mp3lintrc(5)>, L<MP3::Archive(3)>, L<mp3-archive-tools(1)>

=head1 AUTHOR

Ian Beckwith <ianb@nessie.mcc.ac.uk>

=head1 COPYRIGHT

Copyright 2003 Ian Beckwith <ianb@nessie.mcc.ac.uk>

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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

=cut


