#! /usr/bin/perl -w
# $Id: updated-packages,v 1.5.2.3 1998/05/18 20:54:52 asm21 Exp $

# get a list of files to install, for whatever reason (ie because they've been
# requested, because they're already installed but have a new version out, or
# because they're marked as reinst-required). The output is a list of
# TAB-separated fields:
#	package-name filename current-version new-version
# Commandline options are the location of the status file, followed by one or
# more package files or available-package lists. If the same package occurs
# more than once in the available package file(s), it is guaranteed to only
# occur once in the output, but the filename and new version number may be any
# of those named in the available files.

# If invoked with --old, give a list in the same format but of packages which
# are unneeded from the available file, because either the same version or a
# newer version is already installed correctly on the system. Packages which
# are not marked for installation are *not* included in this list.

# (c) 1997,1998 Andy Mortimer.

# deal with arguments
my $status;
my $doavailable=0;
my $listold=0;
my @available=();
for $_ (@ARGV) {
    if ($_ eq "--available") {
	$doavailable=1;
    } elsif ($_ eq "--old") {
	$listold=1;
    } else {
	if (defined($status)) {
	    push @available,$_;
	} else {
	    $status=$_;
	}
    }
}

# read in the list of package stati
my %version;
sub procstatus {
    my $statusf=shift;
    my $pkg='';
    my $wantinst;
    my $forceinst;
    my $version;
    
    open (STATUS, $statusf) or die "Cannot open status file $statusf";
    while (<STATUS>) {
	chomp;
	
	if (/^([^ \t]+):[ \t]*(.*)[ \t]*$/) {
	    my $fld=$1;
	    my $val=$2;
	    $fld=lc($fld);
	    if ( $fld eq 'package' ) {
		$pkg=$val;
		$wantinst=0;
		$forceinst=0;
		$version='';
	    } elsif ($pkg ne '') {
		# if the version is already defined, it's set to '' from the status field
		if ($fld eq 'version') {
		    $version=$val;
		} elsif ($fld eq 'status') {
		    # if the package is marked to install, and is not installed right, then we want to do it anyway.
		    # We mark this by setting the version to ''.
		    # NB that if we don't already have it and don't want it, it never gets a version entry at all.
		    # This overrides the version number of the installed package, as we /know/ that we want it.
		    my ($selec,$flag,$stat)=split(/[ \t]+/,$val);
		    if ($selec eq "install" || $flag eq "reinstreq") {
			$wantinst=1;
			if ($flag eq "reinstreq" or $stat ne "installed" or $flag ne "ok") {
			    $forceinst=1;
			}
		    }
		}
	    }
	} elsif ( $_ eq '' and $pkg ne '' ) {
	    if ($wantinst || $doavailable) {
		if ($forceinst) {
		    $version{$pkg} = '';
		} else {
		    $version{$pkg} = $version;
		}
	    }
	    $pkg='';
	}
    }
    close(STATUS) or die "Error closing status file $statusf";
}

print STDERR "Reading current status ...\n";
procstatus($status);

sub dcmpvers {
    my($a, $p, $b) = @_;
    my ($r);
    $r = system("/usr/bin/dpkg", "--compare-versions", "$a", "$p", "$b");
    $r = $r/256;
    if( $r == 0) {
	return 1;
    } if( $r == 1) {
	return 0;
    }
    die "dpkg --compare-versions $a $p $b - failed with $r"
}

my %get;
my %fname;
my %newver;
my %md5sum;
my %priority;

sub procavail {
    my $availf=shift;
    my $fname='';
    my $getme=0;
    my $pkg='';
    my $newver;
    my $md5sum;
    my $priority;

    open(AVAIL,$availf) or die "Error: cannot open available packages file $availf: $!\n"
	. "Maybe you haven't updated the package list?\n";
  PACKAGE: while (<AVAIL>) {
	chop;
	
	if (/^([^ \t]+):[ \t]*(.*)[ \t]*$/) {
	    my $fld=$1;
	    my $val=$2;
	    $fld=lc($fld);
	    if ( $fld eq 'package' ) {
		$pkg=$val;
		if (!defined($version{$pkg})) {
		    my $inp;
		    while (<AVAIL>) {
			chop;
			if ($_ eq '') {
			    next PACKAGE;
			}
		    }
		    next PACKAGE;
		}
		if ($version{$pkg} eq '') {
		    $getme=1;
		} else {
		    $getme=0;
		}
		$fname='';
		$newver='';
		$md5sum='';
	    } elsif ($fld eq 'filename') {
		$fname=$val;
	    } elsif ($fld eq 'version') {
		$newver=$val;
	    } elsif ($fld eq 'md5sum') {
		$md5sum=$val;
	    } elsif ($fld eq 'priority') {
		$priority=$val;
	    }
	} elsif ($_ eq '') {
	    my $reallygetme;
	    if ($getme ||
		($newver ne $version{$pkg}
		 && dcmpvers($newver,"gt",$version{$pkg}))) {
		$reallygetme=1;
	    }
	    if (($reallygetme && !$listold) || (!$reallygetme and $listold)) {
		$get{$pkg}=1;
		$fname{$pkg}=$fname;
		$newver{$pkg}=$newver;
		$md5sum{$pkg}=$md5sum;
		$priority{$pkg}=$priority;
	    }
	}
    }
    close(AVAIL) or die "Error closing available packages file $availf";
}
my $f;
for $f (@available) {
    print STDERR "Processing available packages file $f ...\n";
    procavail($f);
}

%pv=(
    'required' => 1,
    'important' => 2,
    'standard' => 3,
    'optional' => 4,
    'extra' => 5,
);
my $p;
for $p (sort {
    if ($pv{$priority{$a}} == $pv{$priority{$b}}) {
	$a cmp $b;
    } else {
	$pv{$priority{$a}} cmp $pv{$priority{$b}};
    }
}  keys(%get)) {
    print "$p\t$fname{$p}\t$version{$p}\t$newver{$p}\t$md5sum{$p}\t$priority{$p}\n";
}
