#!/usr/bin/perl

eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
    if 0; # not running under some shell

=head1 NAME

urpmdiff - shows diff between rpms

=head1 SYNOPSIS

    urpmdiff [-f] [-5] [-d] [-m] old.rpm new.rpm

=head1 DESCRIPTION

C<urpmdiff> shows the differences between two rpms. It's intended to help
packagers to know what has changed between an old and a new version of an rpm.
Its output is reminiscent of the unified diff format.

=head1 OPTIONS

=over 4

=item -f

Lists added and removed files

=item -5

Lists modified files (according to their MD5SUM)

=item -m

Lists file mode and ownership changes

=item -d

Lists dependency differences (Requires, Provides, Obsoletes)

=back

Without any option, C<-fmd> is assumed.

=head1 AUTHOR

Copyright (C) 2005 Mandrakesoft,
Rafael Garcia-Suarez E<lt>rgarciasuarez@mandrakesoft.comE<gt>

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, or (at your option)
any later version.

=cut

use strict;
use Getopt::Long;
use Algorithm::Diff 'sdiff';
use URPM;

sub usage {
    warn $_[0] if @_;
    print <<USAGE;
$0 [options] old-rpm new-rpm
Options:
  -f  list added/removed files
  -5  list modified files
  -m  list file mode/ownership differences
  -d  list dependency differences
The default is to list all differences except modified files (like -fmd)
USAGE
    exit 0;
}

my %methods;
@methods{qw(files files_md5sum files_group files_mode files_owner provides requires obsoletes)} = ();
Getopt::Long::Configure(qw(bundling gnu_compat permute));
GetOptions(
    'h|help' => sub { usage },
    'f' => sub { $methods{files} = 1 },
    '5' => sub { $methods{$_} = 1 for qw(files files_md5sum) },
    'm' => sub { $methods{$_} = 1 for qw(files files_group files_mode files_owner) },
    'd' => sub { $methods{$_} = 1 for qw(provides requires obsoletes) },
);
my @methods = grep $methods{$_}, keys %methods;
@methods or @methods = grep $_ ne 'files_md5sum', keys %methods;

usage qq(Not enough arguments\n) if @ARGV != 2;
my $urpm = new URPM;
for (@ARGV) {
    -f && -r _ or usage qq(Can't read "$_"\n);
    $urpm->parse_rpm($_, keep_all_tags => 1);
}

my @rpm;
my $i = 0;
my @filemethods = grep /^files/, @methods;
$urpm->traverse(sub {
    my ($pkg) = @_;
    $rpm[$i]{$_} = [ $pkg->$_ ] for @methods;
    if (@filemethods) {
	# create a hash of file informations to find which ones have changed
	for my $f (0 .. $#{$rpm[$i]{files}}) {
	    $rpm[$i]{filehash}[$f] = join "\0", map $rpm[$i]{$_}[$f], @filemethods;
	}
    }
    print $i ? '+++' : '---', ' ', scalar $pkg->fullname, "\n";
    ++$i;
});

if (@filemethods) {
    my $format_file;
    if (grep /mode/, @filemethods) {
	$format_file = sub {
	    my (@i) = split /\0/, $_[0];
	    my %i = map { $filemethods[$_] => $i[$_] } 0 .. $#filemethods;
	    sprintf("%s:%s %04o %s", $i{files_owner}, $i{files_group}, $i{files_mode} & 07777, $i{files});
	};
    } else {
	$format_file = sub { (split /\0/, $_[0])[0] };
    }
    print "@@ files @@\n";
    for my $diff (sdiff($rpm[0]{filehash}, $rpm[1]{filehash})) {
	$diff->[0] =~ /[-c]/ and print "- ", $format_file->($diff->[1]), "\n";
	$diff->[0] =~ /[+c]/ and print "+ ", $format_file->($diff->[2]), "\n";
    }
}

if (grep $_ eq 'provides', @methods) {
    for my $m (qw(provides requires obsoletes)) {
	print "@@ $m @@\n";
	for my $diff (sdiff($rpm[0]{$m}, $rpm[1]{$m})) {
	    $diff->[0] =~ /[-c]/ and print "- ", $diff->[1], "\n";
	    $diff->[0] =~ /[+c]/ and print "+ ", $diff->[2], "\n";
	}
    }
}
