#!/usr/bin/perl # (c) 2011 by Immo 'FaUl' Wehrenberg # be most verbose use strict; use warnings; use diagnostics; use IO::Uncompress::Bunzip2; use IO::Uncompress::Gunzip; # use IO::Uncompress::UnLzma; #does not exist on debian use Storable; use Env qw($HOME); use vars qw($cache $dists $arch @sources @distributions); # directory with mirror from $arch = "i386"; $dists = "dists/"; #distris in cronological order @distributions = ("potato", "woody", "sarge", "etch", "lenny", "squeeze"); $cache = "$HOME/.apt-sec/cache/"; eval { mkdir "$HOME/.apt-sec"; mkdir $cache; }; sub recursor($$$); sub getarrayofpackeges($); sub getfilehandle($) { my $name = pop; my $fd; if ($name =~ /.bz2$/) { $fd = new IO::Uncompress::Bunzip2 $name or die "$name: $!"; } elsif ($name =~ /.gz$/) { $fd = new IO::Uncompress::Gunzip $name or die "$name: $!"; } # elsif ($name =~ /.lzma$/) { # $fd new IO::Uncompress::UnLzma $name or die "$name: $!"; else { open $fd, $name or die "$name: $!"; } return $fd; } sub cachename($;$) { my $f = shift; my $suffix = shift; $suffix = "cache" if ! defined $suffix; my @a = stat $f; $f =~ s/\/\//\//g; $f =~ s/\//_/g; my $x = $cache."/".$f."_".$a[9].".".$suffix; return $x; } sub getcached ($;$) { my %file; my $cachename = cachename shift(), shift(); eval { my $file = retrieve $cachename; %file = %{$file}; }; return %file; } # parsefile($filehandle) # returns hash of the package file given with $filename, key is package name, # value is a reference to a hash with key: value sub parsefile($) { my $filename = pop; # do we have stuff cached? my %file = getcached($filename, "parsefile.cache"); return %file if %file; my $fd = getfilehandle $filename; # this is the parser my $savedsl = $/; $/ = "\n\n"; while (<$fd>) { my %dict; $_ = "\n".$_; ($_, %dict) = split /\n(\w+): /; # first is crap $file{$dict{"Package"}} = \%dict; } $/ = $savedsl; close $fd; # cache result store \%file, cachename ($filename, "parsefile.cache"); return %file; } # getarrayofpackeges($field) # Returns an array of all packages given in @fields (as returned by getfield() # strips versions sub getarrayofpackages($) { $_ = pop or return; s/(\(.*?\)|\s)//g; # remove everything inside parathesis and whitespaces return split /,/ or (); } # parsesource($filehandle) # parses the sourcefile represented by the open file in $filehandle # returns a dict with each binary package name reference to a # source package hash sub parsesource($) { my $filename = pop; # do we have stuff cached? my %file = getcached($filename, "parsesource.cache"); return %file if %file; # if not, parse my $savedsl = $/; $/ = "\n\n"; my $fd = getfilehandle $filename; while (<$fd>) { my %dict; $_ = "\n".$_; ($_, %dict) = split /\n(\w+): /; # first is crap foreach my $bin (getarrayofpackages ($dict{"Binary"})) { $file{$bin} = \%dict; } } $/ = $savedsl; close $fd; # cache store \%file, cachename ($filename, "parsesource.cache"); return %file; } # intersection (\@array1, \@array2) # returns intersection of both arrays sub intersection($$) { my @arr1 = uniq(pop); my @arr2 = uniq(pop); my @intersection; my %c; foreach (@arr1, @arr2){ $c{$_}++; } foreach (keys %c) { if ($c{$_} > 1) { push @intersection, $_; } } return @intersection; } # uniq(\@array) # returns an array which all unique elements of @array sub uniq($) { my %c = map {$_, 1} @{pop()}; return keys %c; } # old_names_in_file($name, $filehandle) # returns array of alternative names to $name in the opened Package # file ($filehandle) sub old_names_in_file($) { my %pkg = %{pop()}; my @replaces = getarrayofpackages($pkg{"Replaces"}); my @conflicts = getarrayofpackages($pkg{"Conflicts"}); return intersection(\@replaces, \@conflicts); } # pkg2src(%sources, $packagename) # return the source package to a given binary package sub pkg2src($$){ my $p = pop; my %f = %{pop()}; if (!$p || !$f{$p}) { return (); } return $f{$p}->{"Package"}; } # recursor (\@distris, \@names, $start) # recursively works through all @dists (filenames of uncompressed Package files # from debian) from last to first. Tries to find alternative names to the names # given in @names), if $start = 1, dies if name is not found in the current dist sub recursor($$$) { my $start = pop; my @names = @{pop()}; my @distris = @{pop()}; my $dist = pop(@distris); my %pkg; my @list = @names; my %binfile = parsefile "$dists/$dist/binary-$arch/Packages.bz2"; my %sourcefile = parsesource "$dists/$dist/source/Sources.bz2"; foreach my $p (@names) { eval { push @sources, pkg2src(\%sourcefile, $p); @sources = uniq(\@sources); %pkg = %{$binfile{$p}}; @list = old_names_in_file(\%pkg); @names = (@names, @list); @names = uniq(\@names); } or do { die "Could not find any package named '$p'" if grep ($p, @ARGV) && $start; }; } @names = uniq(\@names); if (@distris){ return recursor(\@distris, \@names, 0); } else { return (@names); } } sub main() { $\ = "\n"; $, = "\n"; my @dists = @distributions; my $F; my @binpackages = recursor(\@dists, \@ARGV, 1); #print @l; #print "---"; print @sources; } main();