#!/usr/bin/perl
# Read alternatives info and print a shell script which updates the filter
# files.
use strict;
use warnings;

# alternatives work like this (examples in parens):
# each alternative has a name (editor)
# each alternative has one master link (/usr/bin/editor)
# each alternative has zero or more slave links (/usr/share/man/man1/editor.1.gz), of each has one name (editor.1.gz)
# 
# for each alternative there is one or more providers
# each provider has one master path (/usr/bin/vim)
# each provider has zero or more slave paths (/usr/share/man/man1/vim.1.gz). the number of slave paths may be lower than the number of slave links (in case a provider does not provide a particular slave)
#
# moreover, each path may be provided by one or more packages (vim, vim-gnome). more than one if diversions are involved


# Get a list of packages which provide a regular file
# this is slow :-/
sub packages
{
	my $file = shift;
	open DPKG, "LC_ALL=C LANG=C dpkg -S $file |" or return ();
	my @ret;
	while (<DPKG>) {
		chomp;
		next if /^diversion by/;
		my ($pp) = split /:\s*/;
		my @p = split /,\s*/, $pp;
		foreach my $p (@p) {
			push @ret, $p;
		}
	}
	close DPKG;
	return @ret;
}

opendir ALTS, "/var/lib/dpkg/alternatives" or die "alternatives dir: $!\n";

# some mappings
my %alternative; # alternative_name -> [ link, ... ]
my %path; # link name -> [ path, ... ]
my %provider; # path -> [ package, ... ]

foreach my $alternative (readdir(ALTS))
{
	next if $alternative eq '.' or $alternative eq '..';
	open FILE, '/var/lib/dpkg/alternatives/'.$alternative or die "Couldn't open $alternative: $!\n";

	$_ = <FILE>;  # auto 
	
	# read the part describing links
	my @provides = ();
	my $cnt = 0;
	do {
		$_ = <FILE>; chomp($_);
		push @{$alternative{$alternative}}, $_;
		
		$cnt++;
		
		$_ = <FILE>; chomp($_);
	} while( $_ ne "" );	

	# read the part describing paths
	while( my $main = <FILE> ) {
		chomp($main);
		unless ( $main eq "" ) {
			push @{$path{$alternative{$alternative}->[0]}}, $main;
			my $pri = <FILE>; chomp($pri);
			push @{$provider{$main}}, &packages($main);
			for ( my $i = 1; $i < $cnt; $i++ ) { 
				my $alt = <FILE>; chomp($alt);
				next if $alt eq '';
				push @{$path{$alternative{$alternative}->[$i]}}, $alt;
				push @{$provider{$alt}}, &packages($alt);
			}
		}
	}    

	close FILE;
}
closedir ALTS or die "alternatives dir: $!\n";

# spit out an updating script
open OUT, "| sort -u | tee update_filter" or die "output: $!\n";
foreach my $alt (keys %alternative) 
{
	foreach my $link (@{$alternative{$alt}})
	{
		foreach my $path (@{$path{$link}})
		{
			foreach my $package (@{$provider{$path}})
			{
				printf OUT "grep -q '%s\$' %s 2>/dev/null || echo >>%s %s\n", $link, $package, $package, $link;
			}
		}
	}
}
close OUT or die "output: $!\n";

