/[pkg-asv]/trunk/apt-show-versions
ViewVC logotype

Contents of /trunk/apt-show-versions

Parent Directory Parent Directory | Revision Log Revision Log


Revision 47 - (show annotations) (download)
Sat Jun 13 09:42:13 2009 UTC (3 years, 11 months ago) by ahoenen-guest
File size: 32540 byte(s)
* Fix function name in bash completion file in order to become POSIX
  compliant and to work for Bash 2.  Thanks to Emilio Pozuelo Monfort for
  reporting and the patch.  (Closes: #531608)
* Change version variables' names in a-s-v script, thus ExtUtils::MakeMaker
  succeeds in version extraction.
1 #!/usr/bin/perl -w
2
3 # apt-show-versions - Lists available package versions with distribution
4
5 # This program parses the dpkg status file and the APT lists for the
6 # installed and available package versions and distribution and shows
7 # upgrade options within the specific distribution of the selected
8 # package
9
10 # Copyright (C) 2001 Christoph Martin
11
12 # Author: Christoph Martin <martin@uni-mainz.de>
13 # Maintainer: Christoph Martin <martin@uni-mainz.de>
14 # Version: 0.16
15
16 # This file is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by the
18 # Free Software Foundation; either version 2, or (at your option) any
19 # later version.
20
21 # This file is distributed in the hope that it will be
22 # useful, but WITHOUT ANY WARRANTY; without even the implied warranty
23 # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 # General Public License for more details.
25
26 # You should have received a copy of the GNU General Public License
27 # along with this file; see the file 'copyright' respectively
28 # '/usr/share/common-licenses/GPL-2'. If not, write to the
29 # Free Software Foundation, Inc.,
30 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31
32 use strict;
33 use Getopt::Long;
34 use Storable qw(store retrieve);
35
36 my $apackagescachefile="/var/cache/apt-show-versions/apackages";
37 my $ipackagescachefile="/var/cache/apt-show-versions/ipackages";
38 my $filescachefile="/var/cache/apt-show-versions/files";
39
40 use AptPkg::Cache;
41 use AptPkg::Config '$_config';
42 use AptPkg::Policy;
43 use AptPkg::System '$_system';
44 use AptPkg::Version;
45
46 Getopt::Long::Configure('no_ignore_case'); # Distinguish options -R and -r.
47
48 $_config->init;
49 $_config->{quiet} = 2; # Suppress cache building messages.
50 $_system = $_config->system;
51 my $vs = $_system->versioning;
52 my $cache = AptPkg::Cache->new;
53 my $policy = $cache->policy;
54
55 my $VERSION;
56 # Used by ExtUtils::MakeMaker:
57 $VERSION ='0.16';
58
59 # Provide some constants (to avoid redundant literals).
60 my $ARCHIVE = 'Archive';
61 my $CODENAME = 'Codename';
62 my $NAME = 'Name';
63 my $PACKAGE = 'Package';
64 my $RELEASE = 'Release';
65 my $STATUS = 'Status';
66 my $SUITE = 'Suite';
67 my $UNKNOWN = 'unknown';
68 my $VERS = 'Version';
69
70 # process commandline parameters
71 my %opts;
72 # If more than one packages are requested by name, each one gets registered
73 # in hash %pkg_names as a key (with a true value).
74 my %pkg_names = ();
75 unless (GetOptions (\%opts,
76 'status-file|stf=s',
77 'list-dir|ld=s',
78 'package|p=s',
79 'regex|r',
80 'regex-all|R',
81 'allversions|a',
82 'upgradeable|u',
83 'brief|b',
84 'nohold|nh',
85 'initialize|i',
86 'verbose|v',
87 'help|h')) {
88 exit 1;
89 }
90 if (scalar @ARGV == 1) {
91 if (exists $opts{'package'}) {
92 &die(1, "apt-show-versions: too many arguments\n");
93 }
94 $opts{'package'} = $ARGV[0];
95 } elsif (scalar @ARGV > 1) {
96 if (exists($opts{'package'}) or exists($opts{'regex'})) {
97 &die(1, "apt-show-versions: too many arguments\n");
98 }
99 $pkg_names{$_} = 1 foreach (@ARGV);
100 }
101
102 # Consider implicit option dependencies.
103 $opts{'regex'} = 1 if ($opts{'regex-all'});
104
105 # Determine call mode.
106 my $MODE_SINGLE = 1; # Called with one package name.
107 my $MODE_MULTIPLE = 2; # Called with several packages names.
108 my $MODE_REGEX = 3; # Called with a package name regular expression.
109 my $MODE_ALL = 4; # Called without any package information: all packages.
110 my $mode;
111 if (%pkg_names) {
112 $mode = $MODE_MULTIPLE;
113 }
114 elsif ($opts{'regex'}) {
115 $mode = $MODE_REGEX;
116 }
117 elsif ($opts{'package'}) {
118 $mode = $MODE_SINGLE;
119 }
120 else {
121 $mode = $MODE_ALL;
122 }
123
124 if (exists $opts{'help'}) {
125 print <<EOF;
126 Apt-Show-Versions v.$VERSION (c) Christoph Martin
127
128 Usage:
129 apt-show-versions shows available versions of installed packages.
130
131 Options:
132 -stf|--status-file=<file> Use <file> as the dpkg status file instead
133 of /var/lib/dpkg/status
134 -ld|list-dir=<directory> Use <directory> as path to apt's list files instead
135 of /var/state/apt/lists/ or /var/lib/apt/lists/
136 -p|--package=<package> Print versions for <package>.
137 -r|--regex Read package with -p as regex
138 -R|--regex-all Like --regex, but also show not installed packages.
139 -u|--upgradeable Print only upgradeable packages
140 -a|--allversions Print all available versions.
141 -b|--brief Short output.
142 -nh|--nohold Don't treat holded packages.
143 -i|--initialize Initialize or update package cache only (as root).
144 -v|--verbose Verbose messages.
145 -h|--help Print this help.
146 EOF
147 exit;
148 }
149
150 # Path to apt's list files
151 my $list_dir;
152 if ($opts{'list-dir'}) {
153 $list_dir = $opts{'list-dir'};
154 }
155 else {
156 $list_dir = $_config->get_dir("Dir::State::lists");
157 }
158
159 # Path to dpkg status file
160 my $status_file = $opts{'status-file'} || "/var/lib/dpkg/status";
161 my @files;
162 my $filesref;
163
164 my %used_suites = ();
165 # Determine the release names currently used by this host.
166 # %releasenames structure example:
167 # ('ftp.de.debian.org_debian_dists_unstable' => {'Suite' => 'unstable',
168 # 'Name' => 'unstable',
169 # 'Codename' => 'sid'},
170 # 'debian.udsorg.ru_dists_unstable' => {'Suite' => 'unknown',
171 # 'Name' => 'unknown',
172 # 'Codename' => 'unknown'},
173 # ...)
174 my %releasenames = &determine_releasenames();
175
176 if (exists $opts{'initialize'}) {
177 unlink $apackagescachefile;
178 unlink $ipackagescachefile;
179 unlink $filescachefile;
180 }
181
182 # Get Packages-files list from cache or create new list and write
183 # cache if root
184 if (-e $filescachefile and -M $filescachefile < -M $list_dir) {
185 $filesref = retrieve($filescachefile);
186 @files = @$filesref unless !ref($filesref);
187 }
188 # Test also to be sure $filescachefile is not corrupt and returns a ref to it
189 if (!-e $filescachefile or -M $list_dir < -M $filescachefile or !ref($filesref)) {
190 opendir(DIR, $list_dir) or &die("Can't opendir $list_dir: $!\n");
191 @files = map { $list_dir . $_} grep /Packages$/, readdir(DIR);
192 ($< == 0) and (store(\@files, $filescachefile) or
193 warn "Can't write $filescachefile\n");
194
195 closedir DIR ;
196 }
197 unless (@files > 0) {
198 &die("Error: No information about packages! (Maybe no deb entries?)\n");
199 }
200
201 # Get hash with all installed packages from cache or create new hash
202 # and write cache if root
203 # $ipackages structure example:
204 # {'dblatex' => {'Version' => '0.2.8-6',
205 # 'Status' => 'install ok installed',
206 # 'Package' => 'dblatex'},
207 # ...}
208 my $ipackages;
209
210 if (-e $ipackagescachefile and -M $ipackagescachefile < -M $status_file) {
211 $ipackages = retrieve($ipackagescachefile);
212 }
213 if (!-e $ipackagescachefile or -M $status_file < -M $ipackagescachefile or !ref($ipackages)) {
214 ($ipackages, undef) = parse_file ($status_file, 1);
215 ($< == 0) and (store($ipackages, $ipackagescachefile) or
216 warn "Can't write $ipackagescachefile\n");
217 }
218
219 # Get available packages list from cache if possible
220 # $apackages structure example:
221 # {'dblatex' => {'ftp.de.debian.org_debian_dists_stable'
222 # => {'Version' => '0.2-2',
223 # 'Release' => 'ftp.de.debian.org_debian_dists_stable',
224 # 'Package' => 'dblatex'},
225 # 'ftp.de.debian.org_debian_dists_testing'
226 # => {'Version' => '0.2.8-2',
227 # 'Release' => 'ftp.de.debian.org_debian_dists_testing',
228 # 'Package' => 'dblatex'},
229 # 'ftp.de.debian.org_debian_dists_unstable'
230 # => {'Version' => '0.2.8-6',
231 # 'Release' => 'ftp.de.debian.org_debian_dists_unstable',
232 # 'Package' => 'dblatex'}},
233 # ...}
234 my $apackages;
235 my $cache_file_corrupt;
236 -e $apackagescachefile and $apackages = retrieve($apackagescachefile);
237 unless (ref($apackages)) {
238 $cache_file_corrupt = 1;
239 undef $apackages;
240 }
241
242 my $default_release = $_config->get("APT::Default-Release");
243
244 my @official_suites = qw(oldstable stable proposed-updates testing unstable);
245 # %official_suites:
246 # - Keys: Known official suite names
247 # - Values: Order index
248 my %official_suites;
249 $official_suites{$official_suites[$_]} = $_ foreach (0 .. $#official_suites);
250
251 # Get available package information out of all Packages files
252 foreach (@files) {
253 # Parse Packages file if creation time is newer than packages cache
254 if (! -e $apackagescachefile or -C $_ < -M $apackagescachefile
255 or $cache_file_corrupt) {
256 my ($href, $release) = &parse_file ($_);
257 foreach (keys %$href) {
258 $apackages->{$_}{$release} = $href->{$_};
259 }
260 }
261 }
262 # Store if we are root
263 ($< == 0) and (store($apackages, $apackagescachefile) or
264 &die("Warning: Can't write to $apackagescachefile!\n"));
265 # Exit if we are root and using the -i option
266 ($< == 0) and (exists $opts{'initialize'}) and exit;
267
268 # print info for selected package
269 if ($mode == $MODE_SINGLE) {
270 my $key = $opts{'package'};
271
272 print_package ($key);
273 }
274 elsif ($mode == $MODE_MULTIPLE) {
275 print_package($_) foreach (sort keys %pkg_names);
276 }
277 else {
278 # print info for all packages or packages matching regex
279 my $pkgs = ($opts{'regex-all'}) ? $apackages : $ipackages;
280 foreach my $key (sort keys %$pkgs) {
281 next if (exists $opts{'package'} &&
282 exists $opts{'regex'} &&
283 !($key =~ m/$opts{'package'}/));
284 print_package ($key);
285 }
286 }
287
288 ################################################################################
289 # Collect uptodate or up/downgradeable status of package depending on
290 # distribution. Return: ($version_indicator, @version_info)
291 # - $version_indicator:
292 # * 0: Version is not to be installed/kept.
293 # * 1: Version is to be kept.
294 # * 2: Version is to be up/downgraded.
295 # - @version_info:
296 # Version information to be printed (undef if no information exists)
297 ################################################################################
298 sub print_version {
299 my ($archiv, $package, $iversion, $aversion, $cand) = @_;
300
301 if (defined($aversion) and $cache->{$package}) {
302 if ($cand and $aversion eq $cand->{VerStr})
303 {
304 my $cmp_versions = $vs->compare($aversion, $iversion);
305 if ($cmp_versions != 0) {
306 my $direction = ($cmp_versions > 0) ? 'up' : 'down';
307 return(2, "$package/$archiv",
308 (defined($opts{'brief'})) ? "\n" :
309 " ${direction}gradeable from $iversion to $aversion\n");
310 }
311 else {
312 return(1, "$package/$archiv", defined($opts{'brief'}) ? "\n"
313 : " uptodate $iversion\n");
314 }
315 }
316 }
317 # Default outcome: either version is definitely not to be installed/kept
318 # or information for a decicion is lacking.
319 return(0, undef);
320 }
321
322 # print information about package
323
324 sub print_package {
325 my ($package) = @_;
326
327 # Sort all releases of package.
328 my @pkg_releases = sort sort_pkg_releases values(%{$apackages->{$package}});
329
330 # All print information of package must be buffered, as the decision
331 # whether to suppress all printing for the package can be executed only at
332 # a later stage.
333 my @print_info = ();
334 my $is_upgradeable = 0; # Intialize with: not upgradeable.
335 # To guarantee tabular printing of the package's releases some further
336 # variables are needed:
337 my $max_package_len = 0;
338 my $max_version_len = 0;
339 my $max_name_len = 0;
340
341 # print more information if required
342 if ($opts{'allversions'}) {
343 if ($ipackages->{$package}->{$PACKAGE}) {
344 push @print_info, "$ipackages->{$package}->{$PACKAGE} ";
345 unless ($ipackages->{$package}->{$STATUS} =~ /not-installed/ ||
346 $ipackages->{$package}->{$STATUS} =~ /config-files/) {
347 push @print_info, "$ipackages->{$package}->{$VERS} ";
348 }
349 push @print_info, "$ipackages->{$package}->{$STATUS}\n";
350 } else {
351 push @print_info, "Not installed\n";
352 }
353
354 # Index to @official_suites: Next official suite to mention if missing.
355 my $official_idx = 0;
356 # Print preparation loop
357 foreach my $pkg (@pkg_releases) {
358 # First handle missing official suites to be listed before current
359 # release.
360 my $cur_idx = $official_suites{&get_rel_suite($pkg->{$RELEASE})};
361 if (defined $cur_idx) {
362 # Current release is an official one:
363 # List prepending missing suites.
364 foreach ($official_idx .. $cur_idx - 1) {
365 if ($used_suites{$official_suites[$_]}) {
366 push @print_info, "No $official_suites[$_] version\n";
367 }
368 }
369 # All official suites including current one are handled.
370 $official_idx = $cur_idx + 1;
371 }
372 # Then handle current release.
373 (my $archive = $pkg->{$RELEASE}) =~ s/_.*//;
374 push @print_info, {$PACKAGE => $pkg->{$PACKAGE},
375 $VERS => $pkg->{$VERS},
376 $NAME => &get_rel_name($pkg->{$RELEASE}),
377 $ARCHIVE => $archive};
378 $max_package_len = &max(length($pkg->{$PACKAGE}), $max_package_len);
379 $max_version_len = &max(length($pkg->{$VERS}), $max_version_len);
380 $max_name_len = &max(length(&get_rel_name($pkg->{$RELEASE})),
381 $max_name_len);
382 }
383 # Finally handle missing official suites after last existing release.
384 foreach ($official_idx .. $#official_suites) {
385 if ($used_suites{$official_suites[$_]}) {
386 push @print_info, "No $official_suites[$_] version\n";
387 }
388 }
389 }
390
391 my $iversion = $ipackages->{$package}->{$VERS};
392
393 # print info about upgrade status (only if package is installed)
394
395 if (($ipackages->{$package}->{$VERS}) &&
396 (!($ipackages->{$package}->{$STATUS} =~ /config-files/))) {
397 # Reorder package version structures to prefer the default release.
398 @pkg_releases = &reorder_pkg_releases(@pkg_releases);
399 my $found = 0;
400 my $aversion = 0;
401 my $cand;
402 if ($cache->{$package}) {
403 $cand = $policy->candidate($cache->{$package});
404 }
405 foreach (@pkg_releases) {
406 my $version = $_->{$VERS};
407 if ($version) {
408 my @version_info;
409 ($found, @version_info) =
410 &print_version(&get_rel_name($_->{$RELEASE}),
411 $package, $iversion, $version, $cand);
412 push @print_info, @version_info if ($found);
413 $aversion = $version;
414 }
415 $is_upgradeable = 1 if ($found == 2);
416 last if $found;
417 }
418 if ($aversion && ($vs->compare($iversion, $aversion) > 0)) {
419 # Test whether installed version is newer
420 # than all available versions.
421 my $newer_indic = 1;
422 foreach (@pkg_releases) {
423 my $cmp_version = $_->{$VERS};
424 if ($cmp_version and
425 $vs->compare($iversion, $cmp_version) <= 0)
426 {
427 $newer_indic = 0;
428 last;
429 }
430 }
431 if ($newer_indic and not defined($opts{'brief'}))
432 {
433 push(@print_info,
434 "$package $iversion newer than version in archive\n");
435 }
436 } elsif (not $found) {
437 # Check for manual upgrade possibility:
438 # There are cases where the APT policy doesn't find a better
439 # candidate than the installed version, but which itself isn't
440 # available any longer because it has been replaced by a newer
441 # version in the archives. As the newer version isn't chosen by
442 # the policy, the upgrade can only be executed manually.
443 if ($cand and $iversion eq $cand->{VerStr}) {
444 foreach my $release (@pkg_releases) {
445 my $cmp_version = $release->{$VERS};
446 if ($cmp_version and
447 $vs->compare($iversion, $cmp_version) < 0)
448 {
449 push(@print_info,
450 $package,
451 '/',
452 &get_rel_name($release->{$RELEASE}),
453 (defined($opts{'brief'})) ? "\n" :
454 " *manually* upgradeable from $iversion to " .
455 "$aversion\n");
456 $found = 1;
457 $is_upgradeable = 1;
458 last;
459 }
460 }
461 }
462 if (not $found) {
463 push(@print_info, "$package $iversion installed: No available ",
464 "version in archive\n");
465 }
466 }
467 } else {
468 push(@print_info, "$package not installed",
469 ($mode == $MODE_SINGLE and not keys(%{$apackages->{$package}}))
470 ? " (even not available)\n" : "\n");
471 }
472
473 if ($opts{'upgradeable'}
474 and not $is_upgradeable
475 and $mode == $MODE_SINGLE) {
476 # Caller expects single given package to be upgradeable.
477 # Signal failure of this expectation with a special exit code.
478 exit 2;
479 }
480
481 # Print loop
482 unless ($opts{'upgradeable'} and not $is_upgradeable) {
483 foreach my $print_info (@print_info) {
484 if (ref $print_info) {
485 printf("%*s %*s %*s %s\n",
486 -$max_package_len, $print_info->{$PACKAGE},
487 -$max_version_len, $print_info->{$VERS},
488 -$max_name_len, $print_info->{$NAME},
489 $print_info->{$ARCHIVE});
490 }
491 else {
492 print $print_info;
493 }
494 }
495 }
496 }
497
498 # ------------------------------------------------------
499 # FUNCTION: HASHREF, RELEASE_KEY = parse_file FILE (STATUS)
500 #
501 # Parses FILE into an HASHREF of Hashes
502 # Set STATUS when the file should be parsed just for
503 # installed packages (here the dpkg status file)
504 # Returns HASHREF and key of corresponding %releasenames record.
505 # ------------------------------------------------------
506
507 sub parse_file {
508 my ($file, $status) = @_;
509 my ($key, $value, $package, $packages);
510
511 my $release = &determine_pkgfile_release($file);
512 open FILE, $file or &die("Can't open file $file: $!\n");
513 if ($opts{'verbose'}) {print "Parsing $file...";};
514 while (<FILE>) {
515 if (/^$/){
516 unless (defined $package) {next};
517
518 if ($status) { # Are we parsing the status file?
519 # if we did not specify a package or pattern
520 # only include installed packages
521 unless ($mode == $MODE_ALL and
522 ($package->{$STATUS} =~ /not-installed|config-files/ or
523 # don't print holded packages if requested
524 ($opts{'nohold'} and $package->{$STATUS} =~ /hold/))) {
525 $packages->{ $package->{$PACKAGE}} = $package;
526 }
527 }
528 else {
529 if (!defined $packages->{$package->{$PACKAGE}} or
530 $vs->compare($packages->{$package->{$PACKAGE}}{$VERS},
531 $package->{$VERS}) < 0) {
532 $package->{$RELEASE} = $release;
533 $packages->{$package->{$PACKAGE}} = $package;
534 }
535 }
536 undef $package;
537 next;
538 }
539 unless ((/^Package/) || (/^Version/) || (/^Status/) || (/^Source/)) {next};
540 ($key, $value) = split /: /, $_;
541 $value =~ s/\n//;
542 $value =~ s/\s\(.*\)$//; # Remove any Version information in ()
543 $package->{$key} = $value;
544 }
545 if ($opts{'verbose'}) {print " completed.\n"};
546 close FILE;
547 return $packages, $release;
548 }
549
550 ################################################################################
551 # Determine the release of the specified package file.
552 # If no corresponding %releasenames record exists, one gets created.
553 # Argument $pkgfile is either full package file name or name core, e.g.:
554 # - /var/lib/apt/lists/ftp.de.debian.org_debian_dists_unstable_main_binary-i386_Packages
555 # - ftp.de.debian.org_debian_dists_unstable
556 ################################################################################
557 sub determine_pkgfile_release {
558 my $pkgfile = shift;
559
560 return $pkgfile if ($releasenames{$pkgfile});
561 $pkgfile =~ s{.*/}{};
562 return undef if ($pkgfile eq 'status');
563 foreach (keys %releasenames) {
564 return $_ if ($_ eq substr($pkgfile, 0, length($_)));
565 }
566 # As package file has no release file, create a fallback %releasenames
567 # record based on the information of the package file name.
568 my $releasename;
569 foreach my $suite (@official_suites) {
570 if (index($pkgfile, "_${suite}_") != -1) {
571 # Packagefile belongs to a known suite.
572 ($releasename = $pkgfile) =~ s/(.*$suite).*/$1/;
573 $releasenames{$releasename}{$SUITE} = $suite;
574 $releasenames{$releasename}{$NAME} = $suite;
575 last;
576 }
577 }
578 unless ($releasename) {
579 # No release information available for this package file:
580 # create a dummy %releasenames record.
581 $releasename = $pkgfile;
582 $releasenames{$releasename}{$SUITE} = $UNKNOWN;
583 $releasenames{$releasename}{$NAME} = $UNKNOWN;
584 }
585 $releasenames{$releasename}{$CODENAME} = $UNKNOWN;
586 return $releasename;
587 }
588
589 ################################################################################
590 # Determine the release names currently used by this host.
591 ################################################################################
592 sub determine_releasenames {
593 my %rel_names;
594 opendir LIST_DIR, $list_dir
595 or &die("Failed to open directory $list_dir: $!\n");
596 while (defined(my $rel_file = readdir LIST_DIR)) {
597 my $file_name = "$list_dir/$rel_file";
598 if ($rel_file =~ m/(.*)_Release$/) {
599 $rel_file = $1;
600 }
601 else {
602 next;
603 }
604 open RELEASE_FILE, "< $file_name"
605 or &die("Failed to open file $file_name for reading: $!\n");
606 while (defined (my $line = <RELEASE_FILE>)) {
607 if ($line =~ m/^\s*($SUITE|$CODENAME):\s*(\S+)\s*$/o) {
608 $rel_names{$rel_file}{$1} = $2;
609 }
610 # After extracting values for Suite and Codename, do not parse
611 # rest of release file.
612 # Thus normally only the first lines of the release file must be
613 # read, whereas the much bigger rest may be skipped.
614 if (defined $rel_names{$rel_file}{$SUITE} and
615 defined $rel_names{$rel_file}{$CODENAME}) {
616 last;
617 }
618 }
619 close RELEASE_FILE
620 or &die("Failed to close file $file_name: $!\n");
621 # Register suite as used.
622 if (defined $rel_names{$rel_file}{$SUITE}) {
623 $used_suites{$rel_names{$rel_file}{$SUITE}} = 1;
624 }
625 # Provide default values for missing fields.
626 foreach ($SUITE, $CODENAME) {
627 unless (defined $rel_names{$rel_file}{$_}) {
628 $rel_names{$rel_file}{$_} = $UNKNOWN;
629 }
630 }
631 # Determine name relevant to user (as used in sources.list):
632 # either Suite or Codename.
633 if ($rel_file =~ m/_$rel_names{$rel_file}{$SUITE}/) {
634 $rel_names{$rel_file}{$NAME} = $rel_names{$rel_file}{$SUITE};
635 }
636 elsif ($rel_file =~ m/_$rel_names{$rel_file}{$CODENAME}/) {
637 $rel_names{$rel_file}{$NAME} = $rel_names{$rel_file}{$CODENAME};
638 }
639 else {
640 # Fall back to Suite.
641 $rel_names{$rel_file}{$NAME} = $rel_names{$rel_file}{$SUITE};
642 }
643 }
644 closedir LIST_DIR
645 or &die("Failed to close directory $list_dir: $!\n");
646 return %rel_names;
647 }
648
649 ################################################################################
650 # Return the numerically biger of the two specified arguments.
651 ################################################################################
652 sub max {
653 return ($_[0] > $_[1]) ? $_[0] : $_[1];
654 }
655
656 ################################################################################
657 # Reorder package releases in a way that within the releases of the same
658 # version number the default release gets placed first.
659 ################################################################################
660 sub reorder_pkg_releases {
661 my @releases = @_;
662
663 if (@releases and $default_release) {
664 # Reordering strategy:
665 # - Precondition: The releases are sorted by version already.
666 # - Iterate over the release list: from "left" to "right".
667 # - For the releases of each version:
668 # - Find the first release that is not the default release.
669 # - If right from this "move candidate" the default release is found,
670 # move it left before the move candidate.
671 my $move_idx; # Index of move candidate
672 foreach my $idx (0 .. $#releases) {
673 my $rel_key = $releases[$idx]->{$RELEASE};
674 if (defined $move_idx) {
675 # There exists a move candidate.
676 if ($releases[$idx]->{$VERS} eq $releases[$move_idx]->{$VERS}) {
677 # Current release is of same version as move candidate.
678 if (&get_rel_suite($rel_key) eq $default_release
679 or &get_rel_codename($rel_key) eq $default_release) {
680 # Move current release before move candidate in order to
681 # place default release first.
682 my $rel = splice @releases, $idx, 1;
683 splice @releases, $move_idx, 0, $rel;
684 $move_idx = $idx;
685 }
686 }
687 else {
688 # Version change
689 undef $move_idx;
690 }
691 }
692 unless (defined $move_idx) {
693 # Test whether current release is move candidate.
694 if (&get_rel_suite($rel_key) ne $default_release
695 and &get_rel_codename($rel_key) ne $default_release) {
696 $move_idx = $idx;
697 }
698 }
699 }
700 }
701 return @releases;
702 }
703
704 ################################################################################
705 # Sorting function for package releases
706 # Sorting hierarchy:
707 # 1) Release number
708 # 2) @official_suites (in array order) before other ones
709 # 3) Release name
710 ################################################################################
711 sub sort_pkg_releases {
712 my $cmp_versions = $vs->compare($a->{$VERS}, $b->{$VERS});
713 return $cmp_versions if ($cmp_versions);
714 my $cmp_suites = (&suite_idx(&get_rel_suite($a->{$RELEASE})) <=>
715 &suite_idx(&get_rel_suite($b->{$RELEASE})));
716 return $cmp_suites if ($cmp_suites);
717 return(&get_rel_name($a->{$RELEASE}) cmp &get_rel_name($b->{$RELEASE}));
718 }
719
720 ################################################################################
721 # Return the sorting index of the specified suite name.
722 # Unofficial suites are sorted last.
723 ################################################################################
724 sub suite_idx {
725 return(defined($official_suites{$_[0]}) ? $official_suites{$_[0]}
726 : $#official_suites + 1);
727 }
728
729 ################################################################################
730 # Simple die wrapper which controls the exit code.
731 # If first parameter is a number, it is used as the exit code. Otherwise it
732 # gets interpreted (like the remaining parameters) as the error message.
733 ################################################################################
734 sub die {
735 $! = ($_[0] =~ m/^\d+$/) ? shift() : 255;
736 die @_;
737 }
738
739 ################################################################################
740 # Access function for the fields of the %releasenames structure
741 # Missing fields are augmented on the fly (compare BTS report #515328).
742 ################################################################################
743 sub get_rel_codename {
744 my $rel_key = shift;
745 &determine_pkgfile_release($rel_key) unless $releasenames{$rel_key};
746 return $releasenames{$rel_key}{$CODENAME};
747 }
748 sub get_rel_name {
749 my $rel_key = shift;
750 &determine_pkgfile_release($rel_key) unless $releasenames{$rel_key};
751 return $releasenames{$rel_key}{$NAME};
752 }
753 sub get_rel_suite {
754 my $rel_key = shift;
755 &determine_pkgfile_release($rel_key) unless $releasenames{$rel_key};
756 return $releasenames{$rel_key}{$SUITE};
757 }
758
759 # script documentation (POD style)
760
761 =encoding utf8
762
763 =head1 NAME
764
765 apt-show-versions - Lists available package versions with distribution
766
767 =head1 DESCRIPTION
768
769 apt-show-versions parses the dpkg status file and the APT lists for
770 the installed and available package versions and distribution and
771 shows upgrade options within the specific distribution of the selected
772 package.
773
774 This is really useful if you have a mixed stable/testing environment
775 and want to list all packages which are from testing and can be
776 upgraded in testing.
777
778 apt-show-versions uses caching for the status information of installed
779 and available packages. If you run apt-show-versions as root the
780 cache is updated as needed. If you run as non-root uses the newest
781 available information, but can't update the cache. If you run as root
782 with the option B<-i> the cache is initialized or updated only.
783
784 =head1 SYNOPSIS
785
786 B<apt-show-versions> [B<-h>] [[B<-p>] I<package name>] [B<-a>] [B<-b>]
787
788 =head1 OPTIONS
789
790 If you don't give any options the status of all installed packages is
791 printed.
792
793 =over 4
794
795 =item B<-p> I<package>, B<--package>=I<package>
796
797 Print available and installed versions for specified I<package>. You
798 can also specify a package name without the option B<-p>. If B<-p> and
799 a package name are missing, all installed packages are displayed.
800
801 =item B<-r>, B<--regex>
802
803 interpret I<package> from option B<-p> as a regex.
804
805 =item B<-R>, B<--regex-all>
806
807 like B<--regex>, but also show matching packages which are not installed
808
809 =item B<-u>, B<--upgradeable>
810
811 Print only upgradeable packages
812
813 =item B<-a>, B<--allversions>
814
815 Print all available versions of the selected packages
816
817 =item B<-b>, B<--brief>
818
819 Print only package_name/distribution for upgradeable packages
820
821 =item B<-v>, B<--verbose>
822
823 Prints out verbose messages.
824
825 =item B<-i>, B<--initialize>
826
827 Initialize or update package cache only (as root). Do this every time
828 when the status of the installed or available packages has changed.
829
830 =item B<-stf> I<file>, B<--status-file>=I<file>
831
832 Use I<file> as the dpkg status file instead of /var/lib/dpkg/status
833
834 =item B<-ld> I<directory>, B<--list-dir>=I<directory>
835
836 Use I<directory> as path to apt's list files instead of
837 /var/state/apt/lists/ or /var/lib/apt/lists/
838
839 =item B<-h>, B<--help>
840
841 Prints out command-line help.
842
843 =back
844
845 =head1 EXIT CODES
846
847 =over 4
848
849 =item 0
850
851 No error
852
853 =item 1
854
855 Wrong usage
856
857 =item 2
858
859 apt-show-versions has been called with exactly one package and upgradeable
860 option set, but package is uptodate. As no output has been requested, this
861 case gets signaled using the exit code.
862
863 =item 255
864
865 Unspecified error
866
867 =back
868
869 =head1 EXAMPLES
870
871 If you want to know for all your installed packages whether they are
872 uptodate or upgradeable, use:
873
874 apt-show-versions
875
876 If you want to have a list of all upgradeable packages:
877
878 apt-show-versions -u
879
880 To get a list of all available versions of libc6:
881
882 apt-show-versions -a -p libc6
883
884 To get information about several packages:
885
886 apt-show-versions dpkg apt
887
888 apt-show-versions -r ^texlive
889
890 To upgrade all packages in testing:
891
892 apt-get install `apt-show-versions -u -b | fgrep testing`
893
894 =head1 AUTHOR
895
896 Christoph Martin, martin@uni-mainz.de
897
898 =head1 SEE ALSO
899
900 apt(8), dpkg(1)
901
902 =cut

Properties

Name Value
svn:executable

  ViewVC Help
Powered by ViewVC 1.1.5