#!/usr/bin/perl -w use strict; # $Id: tracked-packages,v 1.9 2001-03-09 09:02:02-05 roderick Exp $ # # Roderick Schertler # This program checks the Debian available and status files to see what # packages are installed which are newer than what are available (and # therefore were manually installed from a newer release) and then it # reads some given packages files (presumably from the newer release) # and reports what newer versions of the tracked packages are available. # Copyright (C) 1998 Roderick Schertler # # 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 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # For a copy of the GNU General Public License write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use Proc::WaitStat qw(close_die waitstat); (my $Me = $0) =~ s-.*/--; my $Available = '/var/lib/dpkg/available'; my $Debug = 0; my $Exit = 0; my $Status = '/var/lib/dpkg/status'; my $Verbose = 1; sub xmess { push @_, " $!\n" if $_[$#_] =~ /:$/; "$Me: ", @_; } sub xwarn { warn xmess @_; $Exit ||= 1; } sub xdie { die xmess @_; } # It would be good if dpkg-perl provided a version comparison function, # and if dpkg --compare-versions provided a <=>-like comparison. sub version_cmp_dpkg { my ($v1, $op, $v2) = @_; system 'dpkg', '--compare-versions', $v1, $op, $v2; return 1 if $? == 0; xdie "invalid return from dpkg (", waitstat $?, ") comparing $v1 $op $v2\n" if $? != 256; return 0; } sub version_cmp { my ($v1, $v2) = @_; # Save a fork/exec/wait for the common case. return 0 if $v1 eq $v2; return -1 if version_cmp_dpkg $v1, 'lt', $v2; return 0 if version_cmp_dpkg $v1, 'eq', $v2; return 1; } sub parse { local $_ = shift; my ($pkg, $version); chomp; /^Package:\s*(\S+)\s*$/im or xdie "no valid package field:\n$_"; $pkg = $1; /^Version:\s*(\S+)\s*$/im or xdie "no valid version field:\n$_"; $version = $1; return $pkg, $version; } sub main { my (%have, $pkg, $version, %tracking, $file); # Read in paragraph mode. $/ = ''; @ARGV or ! -t or xdie "no packages files specified\n"; open STATUS, $Status or xdie "can't read $Status:"; open AVAIL, $Available or xdie "can't read $Available:"; # Load %have with installed packages. while () { if (/^Status:.*\sinstalled\s/im) { ($pkg, $version) = parse $_; $have{$pkg} = $version; } } close_die *STATUS, $Status; # For packages both installed and in AVAIL, move packages which are # newer installed into %tracking. while () { ($pkg, $version) = parse $_; if (exists $have{$pkg}) { $tracking{$pkg} = $have{$pkg} if version_cmp($have{$pkg}, $version) > 0; delete $have{$pkg}; } } close_die *AVAIL, $Available; # Move packages installed but not in AVAIL to %tracking. @tracking{keys %have} = values %have; @ARGV = qw(-) unless @ARGV; for $file (@ARGV) { unless (open PACKAGES, $file) { xwarn "can't read $file:"; next; } while () { ($pkg, $version) = parse $_; if (exists $tracking{$pkg}) { my $cmp = version_cmp($tracking{$pkg}, $version); printf "%-8s %-20s have %-20s available %s\n", $cmp < 0 ? 'updated' : $cmp == 0 ? 'current' : 'newer', $pkg, $tracking{$pkg}, $version if $Verbose || $cmp; delete $tracking{$pkg}; } } close_die *PACKAGES, $file; } for $pkg (keys %tracking) { printf "%-8s %-20s have %-20s available nil\n", 'gone', $pkg, $tracking{$pkg}; } return 0; } $Exit = main || $Exit; $Exit = 1 if $Exit and not $Exit % 256; exit $Exit;