#!/usr/bin/perl
#
# $Id: dshbak 862 2004-05-28 17:55:04Z grondo $
# $Source$
# 
require 5.003;

use Getopt::Std;

getopts('c') or usage();

#
# Stdin consists of lines of the form "hostname: output...".
# Store these in a hash, keyed by hostname, of lists of lines.
#
while (<>) {
	($tag, $data) = m/^\s*(\S+)\s*:(.*\n)$/;
	push(@{$lines{$tag}}, $data);
}

#
# If -c was specified, hosts with identical output are displayed as a list
# of hosts followed by one copy of the output.
#
if ($opt_c) {
	foreach $tag (keys %lines) {			# look thru each host
		next if (!defined($lines{$tag}));	# skip deleted keys
		@identical = ();			# init list of matches
		foreach $tag2 (keys %lines) {	
			next if ($tag2 eq $tag);	# skip over myself
			if (cmp_list(\@{$lines{$tag}}, \@{$lines{$tag2}})) {
				push(@identical, $tag2);# equal?  stash match
				delete($lines{$tag2});	# delete data from hash
			}
		}
		print("----------------\n");		# header: list of hosts
		printf("%s\n", 				# plus myself
		    join(",", compress(sort(@identical, $tag)))); 
		print("----------------\n");
		foreach $data (@{$lines{$tag}}) {	# lines of data (once)
			print($data);
		}
	}
#
# If no -c, all hosts appear individually with their output.
#
} else {
	foreach $tag (keys %lines) {
		print("----------------\n");		# header: one host
		print("$tag\n");
		print("----------------\n");
		foreach $data (@{$lines{$tag}}) {	# lines of data
			print($data);
		}
	}
}

#
# Compare two lists-o-strings
#	\@l1 (IN)	list1
#	\@l2 (IN)	list2
#	RETURN		1 if match, 0 if not
#
sub cmp_list
{
	my ($l1, $l2) = @_;
	my ($i, $retval);

	$retval = 1;

	if ($#{$l1} != $#{$l2}) {
		return 0;
	}
	for ($i = 0; $i <= $#{$l1} && $retval == 1; $i++) {
		if (!defined(${$l2}[$i]) || ${$l1}[$i] ne ${$l2}[$i]) {
			$retval = 0;
		}
	}

	return $retval;
}

sub usage
{
	printf STDERR ("Usage: dshbak [-c]\n");
}


sub compress 
{
	my %rng = comp(@_);
	my @list = ();

	local $"=",";

	@list = map {  $_ .
		      (@{$rng{$_}}>1 || ${$rng{$_}}[0] =~ /-/ ?
		                "[@{$rng{$_}}]" :
				 "@{$rng{$_}}"
		      )
	            } sort keys %rng;

	return wantarray ? @list : "@list";
}

sub comp
{
        my (%i) = ();
        my (%s) = ();

        # turn off warnings here to avoid perl complaints about 
        # uninitialized values for members of %i and %s
        local ($^W) = 0;
        push(@{
               $s{$$_[0]}[ (
                            $s{ $$_[0] }[ $i{$$_[0]} ]
                              [$#{$s{$$_[0]}[$i{$$_[0]}]}] == ($$_[1]-1)
                           ) ? $i{$$_[0]} : ++$i{$$_[0]}
                         ]
              }, ($$_[1])
        ) for map { [/(.*?)(\d*)$/] } sortn(@_);

        for my $key (keys %s) {
                @{$s{$key}} =
                    map { $#$_>0 ? "$$_[0]-$$_[$#$_]" : @{$_} }  @{$s{$key}};
        }

	return %s;
}

# sortn:
#
# sort a group of alphanumeric strings by the last group of digits on
# those strings, if such exists (good for numerically suffixed host lists)
#
sub sortn
{
	map {$$_[0]} sort {($$a[1]||0)<=>($$b[1]||0)} map {[$_,/(\d*)$/]} @_;
}


