summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile.in79
-rw-r--r--scripts/dpkg-divert.pl220
-rw-r--r--scripts/dpkg-name.160
-rwxr-xr-xscripts/dpkg-name.sh94
-rwxr-xr-xscripts/dpkg-scanpackages.pl178
-rw-r--r--scripts/install-info.8245
-rwxr-xr-xscripts/install-info.pl342
-rw-r--r--scripts/lib.pl565
-rwxr-xr-xscripts/perl-dpkg.pl1482
-rw-r--r--scripts/start-stop-daemon.819
-rwxr-xr-xscripts/start-stop-daemon.pl160
-rw-r--r--scripts/update-alternatives.819
-rwxr-xr-xscripts/update-alternatives.pl429
-rw-r--r--scripts/update-rc.d.879
-rwxr-xr-xscripts/update-rc.d.sh104
15 files changed, 4075 insertions, 0 deletions
diff --git a/scripts/Makefile.in b/scripts/Makefile.in
new file mode 100644
index 000000000..e3fe56dda
--- /dev/null
+++ b/scripts/Makefile.in
@@ -0,0 +1,79 @@
+# Copyright (C) 1994 Ian Murdock <imurdock@debian.org>
+# Copyright (C) 1994,1995 Ian Jackson <ijackson@nyx.cs.du.edu>
+#
+# This 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,
+# or (at your option) any later version.
+#
+# This 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.
+#
+# You should have received a copy of the GNU General Public
+# License along with dpkg; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+bindir = $(prefix)/bin
+sbindir = $(prefix)/sbin
+datadir = /var/lib/dpkg
+altsdatadir = $(datadir)/alternatives
+mandir = $(prefix)/man
+man1dir = $(mandir)/man1
+man8dir = $(mandir)/man8
+man1 = 1
+man8 = 8
+libdir = $(prefix)/lib
+dpkglibdir = $(libdir)/dpkg
+etcdir= /etc
+altsetcdir = $(etcdir)/alternatives
+perlpath = @perlpath@
+
+MAN1 = dpkg-name
+EXC = dpkg-name
+MAN8 = update-rc.d start-stop-daemon update-alternatives install-info
+SBIN = update-rc.d start-stop-daemon update-alternatives install-info \
+ dpkg-scanpackages dpkg-divert
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+.SUFFIXES: .pl .sh .gzuue
+
+.pl:
+ sed <$@.pl 's:^#!/usr/bin/perl:#!$(perlpath):' \
+ | ../insert-version.pl >$@.new
+ chmod +x $@.new
+ mv $@.new $@
+
+.sh:
+ sed <$@.sh 's:^dpkglibdir=/usr/lib/dpkg$$:dpkglibdir=$(dpkglibdir):' \
+ | ../insert-version.pl >$@.new
+ mv $@.new $@
+
+.gzuue:
+ uudecode <$@.gzuue
+ gunzip <$@.gz >$@.new
+ test ! -x $@.gz || chmod +x $@.new
+ rm $@.gz
+ mv $@.new $@
+
+all: $(EXC) $(SBIN)
+
+clean:
+ rm -f $(EXC) $(SBIN) core *.new
+
+distclean: clean
+ rm -f Makefile *.orig *~ *.~* ./#*# i386elf-hello-world.gz
+
+install: all
+ for f in $(EXC) ; do $(INSTALL_PROGRAM) $$f $(bindir)/$$f ; done
+ for f in $(MAN1) ; do $(INSTALL_DATA) $$f.1 $(man1dir)/$$f.$(man1) ; done
+ for f in $(SBIN) ; do $(INSTALL_PROGRAM) $$f $(sbindir)/$$f ; done
+ for f in $(MAN8) ; do $(INSTALL_DATA) $$f.8 $(man8dir)/$$f.$(man8) ; done
diff --git a/scripts/dpkg-divert.pl b/scripts/dpkg-divert.pl
new file mode 100644
index 000000000..dff1854ca
--- /dev/null
+++ b/scripts/dpkg-divert.pl
@@ -0,0 +1,220 @@
+#!/usr/bin/perl --
+
+#use POSIX; &ENOENT;
+sub ENOENT { 2; }
+# Sorry about this, but POSIX.pm isn't necessarily available
+
+$version= '1.0.11'; # This line modified by Makefile
+sub usageversion {
+ print(STDERR <<END)
+Debian GNU/Linux dpkg-divert $version. Copyright (C) 1995
+Ian Jackson. This is free software; see the GNU General Public Licence
+version 2 or later for copying conditions. There is NO warranty.
+
+Usage:
+ dpkg-divert [options] [--add] <file>
+ dpkg-divert [options] --remove <file>
+ dpkg-divert [options] --list [<glob-pattern>]
+
+Options: --package <package> | --local --divert <divert-to> --rename
+ --quiet --test --help|--version --admindir <directory>
+
+<package> is the name of a package whose copy of <file> will not be diverted.
+<divert-to> is the name used by other packages' versions.
+--local specifies that all packages' versions are diverted.
+--rename causes dpkg-divert to actually move the file aside (or back).
+
+When adding, default is --local and --divert <original>.distrib.
+When removing, --package or --local and --divert must match if specified.
+Package preinst/postrm scripts should always specify --package and --divert.
+END
+ || &quit("failed to write usage: $!");
+}
+
+$admindir= '/var/lib/dpkg';
+$testmode= 0;
+$dorename= 0;
+$verbose= 1;
+$mode='';
+$|=1;
+
+sub checkmanymodes {
+ return unless $mode;
+ &badusage("two modes specified: $_ and --$mode");
+}
+
+while (@ARGV) {
+ $_= shift(@ARGV);
+ last if m/^--$/;
+ if (!m/^-/) {
+ unshift(@ARGV,$_); last;
+ } elsif (m/^--(help|version)$/) {
+ &usageversion; exit(0);
+ } elsif (m/^--test$/) {
+ $testmode= 1;
+ } elsif (m/^--rename$/) {
+ $dorename= 1;
+ } elsif (m/^--quiet$/) {
+ $verbose= 0;
+ } elsif (m/^--local$/) {
+ $package= ':';
+ } elsif (m/^--add$/) {
+ &checkmanymodes;
+ $mode= 'add';
+ } elsif (m/^--remove$/) {
+ &checkmanymodes;
+ $mode= 'remove';
+ } elsif (m/^--list$/) {
+ &checkmanymodes;
+ $mode= 'list';
+ } elsif (m/^--divert$/) {
+ @ARGV || &badusage("--divert needs a divert-to argument");
+ $divertto= shift(@ARGV);
+ $divertto =~ m/\n/ && &badusage("divert-to may not contain newlines");
+ } elsif (m/^--package$/) {
+ @ARGV || &badusage("--package needs a package argument");
+ $package= shift(@ARGV);
+ $divertto =~ m/\n/ && &badusage("package may not contain newlines");
+ } elsif (m/^--admindir$/) {
+ @ARGV || &badusage("--admindir needs a directory argument");
+ $admindir= shift(@ARGV);
+ } else {
+ &badusage("unknown option \`$_'");
+ }
+}
+
+$mode='add' unless $mode;
+
+open(O,"$admindir/diversions") || &quit("cannot open diversions: $!");
+while(<O>) {
+ s/\n$//; push(@contest,$_);
+ $_=<O>; s/\n$// || &badfmt("missing altname");
+ push(@altname,$_);
+ $_=<O>; s/\n$// || &badfmt("missing package");
+ push(@package,$_);
+}
+close(O);
+
+if ($mode eq 'add') {
+ @ARGV == 1 || &badusage("--add needs a single argument");
+ $file= $ARGV[0];
+ $file =~ m/\n/ && &badusage("file may not contain newlines");
+ $divertto= "$file.distrib" unless defined($divertto);
+ $package= ':' unless defined($package);
+ for ($i=0; $i<=$#contest; $i++) {
+ if ($contest[$i] eq $file || $altname[$i] eq $file ||
+ $contest[$i] eq $divertto || $altname[$i] eq $divertto) {
+ if ($contest[$i] eq $file && $altname[$i] eq $divertto &&
+ $package[$i] eq $package) {
+ print "Leaving \`",&infon($i),"'\n" if $verbose > 0;
+ exit(0);
+ }
+ &quit("\`".&infoa."' clashes with \`".&infon($i)."'");
+ }
+ }
+ push(@contest,$file);
+ push(@altname,$divertto);
+ push(@package,$package);
+ print "Adding \`",&infon($#contest),"'\n" if $verbose > 0;
+ &checkrename($file,$divertto);
+ &save;
+ &dorename($file,$divertto);
+ exit(0);
+} elsif ($mode eq 'remove') {
+ @ARGV == 1 || &badusage("--remove needs a single argument");
+ $file= $ARGV[0];
+ for ($i=0; $i<=$#contest; $i++) {
+ next unless $file eq $contest[$i];
+ &quit("mismatch on divert-to\n when removing \`".&infoa."'\n found \`".
+ &infon($i)."'") if defined($divertto) && $altname[$i] ne $divertto;
+ &quit("mismatch on package\n when removing \`".&infoa."'\n found \`".
+ &infon($i)."'") if defined($package) && $package[$i] ne $package;
+ print "Removing \`",&infon($i),"'\n" if $verbose > 0;
+ $orgfile= $contest[$i];
+ $orgdivertto= $altname[$i];
+ @contest= (($i > 0 ? @contest[0..$i-1] : ()),
+ ($i < $#contest ? @contest[$i+1,$#contest] : ()));
+ @altname= (($i > 0 ? @altname[0..$i-1] : ()),
+ ($i < $#altname ? @altname[$i+1,$#altname] : ()));
+ @package= (($i > 0 ? @package[0..$i-1] : ()),
+ ($i < $#package ? @package[$i+1,$#package] : ()));
+ &checkrename($orgdivertto,$orgfile);
+ &dorename($orgdivertto,$orgfile);
+ &save;
+ exit(0);
+ }
+ print "No diversion \`",&infoa,"', none removed\n" if $verbose > 0;
+ exit(0);
+} elsif ($mode eq 'list') {
+ @ilist= @ARGV ? @ARGV : ('*');
+ while (defined($_=shift(@ilist))) {
+ s/\W/\\$&/g;
+ s/\\\?/./g;
+ s/\\\*/.*/g;
+ push(@list,"^$_\$");
+ }
+ $pat= join('$|^',@list);
+ for ($i=0; $i<=$#contest; $i++) {
+ next unless ($contest[$i] =~ m/$pat/o ||
+ $altname[$i] =~ m/$pat/o ||
+ $package[$i] =~ m/$pat/o);
+ print &infon($i),"\n";
+ }
+ exit(0);
+} else {
+ &quit("internal error - bad mode \`$mode'");
+}
+
+sub infol {
+ return (($_[2] eq ':' ? "<local>" : length($_[2]) ? "$_[2]" : "<any>").
+ ": $_[0]".
+ (length($_[1]) ? " -> $_[1]" : ""));
+}
+
+sub checkrename {
+ return unless $dorename;
+ ($rsrc,$rdest) = @_;
+ (@ssrc= lstat($rsrc)) || $! == &ENOENT ||
+ &quit("cannot stat old name \`$rsrc': $!");
+ (@sdest= lstat($rdest)) || $! == &ENOENT ||
+ &quit("cannot stat new name \`$rdest': $!");
+ if (@ssrc && @sdest &&
+ !($ssrc[0] == $sdest[0] && $ssrc[1] == $sdest[1])) {
+ &quit("rename involves overwriting \`$rdest' with\n".
+ " different file \`$rsrc', not allowed");
+ }
+}
+
+sub dorename {
+ return unless $dorename;
+ if (@ssrc) {
+ if (@sdest) {
+ unlink($rsrc) || &quit("rename: remove duplicate old link \`$rsrc': $!");
+ } else {
+ rename($rsrc,$rdest) || &quit("rename: rename \`$rsrc' to \`$rdest': $!");
+ }
+ }
+}
+
+sub save {
+ return if $testmode;
+ open(N,"> $admindir/diversions-new") || &quit("create diversions-new: $!");
+ for ($i=0; $i<=$#contest; $i++) {
+ print(N "$contest[$i]\n$altname[$i]\n$package[$i]\n")
+ || &quit("write diversions-new: $!");
+ }
+ close(N) || &quit("close diversions-new: $!");
+ unlink("$admindir/diversions-old") ||
+ $! == &ENOENT || &quit("remove old diversions-old: $!");
+ link("$admindir/diversions","$admindir/diversions-old") ||
+ $! == &ENOENT || &quit("create new diversions-old: $!");
+ rename("$admindir/diversions-new","$admindir/diversions")
+ || &quit("install new diversions: $!");
+}
+
+sub infoa { &infol($file,$divertto,$package); }
+sub infon { &infol($contest[$i],$altname[$i],$package[$i]); }
+
+sub quit { print STDERR "dpkg-divert: @_\n"; exit(2); }
+sub badusage { print STDERR "dpkg-divert: @_\n\n"; &usageversion; exit(2); }
+sub badfmt { &quit("internal error: $admindir/diversions corrupt: $_[0]"); }
diff --git a/scripts/dpkg-name.1 b/scripts/dpkg-name.1
new file mode 100644
index 000000000..b474feb8d
--- /dev/null
+++ b/scripts/dpkg-name.1
@@ -0,0 +1,60 @@
+.\" This is an -*- nroff -*- source file.
+.\" dpkg-name and this manpage are Copyright 1995,1996 by Erick Branderhorst.
+.\"
+.\" This is free software; see the GNU General Public Licence version 2
+.\" or later for copying conditions. There is NO warranty.
+.TH dpkg-name 1 "January 1996" "Debian Project" "Debian GNU/Linux"
+.SH NAME
+dpkg\-name \- rename Debian packages to full package names
+.SH SYNOPSIS
+.B dpkg\-name [\-h|\-\-help] [\-V|\-\-version] [\-L|\-\-license] [--] [files]
+.SH DESCRIPTION
+.PP
+This manual page documents the
+.B dpkg\-name
+sh script which provides an easy way to rename
+.B Debian
+packages into their full package names. A full package name consists
+of <package>-<version>[-<revision>].deb as specified in the control
+file of the package.
+.SH EXAMPLES
+.TP
+.B dpkg-name toedeledokie
+The file `toedeledokie' will be renamed to emacs-19.29-4.deb or
+something similar (depending on whatever information is in the control
+part of `toedeledokie').
+.TP
+.B find /root/debian/ \-name '*.deb' | xargs dpkg\-name
+All files with the extension `deb' in the directory /root/debian and
+its subdirectory's will be renamed by dpkg\-name if required.
+.SS OPTIONS
+.TP
+.B "\-h, \-\-help"
+Print a usage message and exit successfully.
+.TP
+.B "\-v, \-\-version"
+Print version information and exit successfully.
+.TP
+.B "\-l, \-\-license"
+Print copyright information and (a reference to GNU) license
+information and exit successfully.
+.SH BUGS?
+Successfully tested on
+.B Debian GNU/Linux
+systems only. Some packages don't follow the name structure
+<package>-<version>[-<revision>].deb. Packages renamed by dpkg-name
+will follow this structure. Generally this will have no impact on how
+packages are installed by dselect/dpkg.
+.SH SEE ALSO
+.BR deb (5),
+.BR deb-control (5),
+.BR dpkg (5),
+.BR dpkg (8),
+.BR dpkg-deb (8).
+.SH COPYRIGHT
+Copyright 1995,1996 Erick Branderhorst.
+.B dpkg-name
+is free software; see the GNU General Public Licence version 2 or
+later for copying conditions. There is
+.B no
+warranty.
diff --git a/scripts/dpkg-name.sh b/scripts/dpkg-name.sh
new file mode 100755
index 000000000..294010aa9
--- /dev/null
+++ b/scripts/dpkg-name.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+set -e
+
+prog="`basename \"${0}\"`"
+version="0.11"; # This line modified by Makefile
+purpose="rename Debian packages to full package names"
+
+license () {
+echo "# ${prog} ${version} -- ${purpose}
+# Copyright (C) 1995,1996 Erick Branderhorst <branderhorst@heel.fgg.eur.nl>.
+
+# This 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, or (at your option) any
+# later version.
+
+# This 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 file
+# /usr/doc/copyright/GPL for more details."
+}
+
+stderr () {
+ echo "${prog}: $@" >/dev/stderr;
+}
+
+show_version () {
+ echo "${prog} version ${version} -- ${purpose}";
+}
+
+usage () {
+ echo "Usage: ${prog} file[s]
+ ${purpose}
+ file.deb changes to <package>-<version>[-<revision>].deb
+ -h|--help|-v|--version|-l|--license Show help/version/license"
+}
+
+rename () {
+ if [ -f "$1" ];
+ then
+ if p=`dpkg-deb -f -- "$1" package`;
+ then
+ p="$p-"`dpkg-deb -f -- "$1" version`;
+ r=`dpkg-deb -f -- "$1" revision`;
+ if [ -z "$r" ];
+ then
+ r=`dpkg-deb -f -- "$1" package_revision`;
+ fi
+ if [ -n "$r" ];
+ then
+ p=$p-$r;
+ fi
+ p=`echo $p|sed 's/ //g'`
+ p=`dirname "$1"`"/"$p.deb
+ if [ $p -ef "$1" ]; # same device and inode numbers
+ then
+ stderr "skipping \`"$1"'";
+ elif [ -f $p ];
+ then
+ stderr "can't move \`"$1"' to existing file";
+ elif `mv -- "$1" $p`;
+ then
+ echo "moved \``basename "$1"`' to \`${p}'";
+ else
+ stderr "hmm how did this happen?";
+ fi
+ fi
+ else
+ stderr "can't deal with \`"$1"'";
+ fi
+}
+
+if [ $# = 0 ]; then usage; exit 0; fi
+for arg
+do
+ case "$arg" in
+ --version|-v) show_version; exit 0;;
+ --help|-[h?]) usage; exit 0;;
+ --licen[cs]e|-l) license; exit 0;;
+ --) shift;
+ for arg
+ do
+ rename "$arg";
+ done; exit 0;;
+ *) rename "$arg";;
+ esac
+done
+exit 0;
+
+# Local variables:
+# tab-width: 2
+# End:
+
diff --git a/scripts/dpkg-scanpackages.pl b/scripts/dpkg-scanpackages.pl
new file mode 100755
index 000000000..efb6a6e29
--- /dev/null
+++ b/scripts/dpkg-scanpackages.pl
@@ -0,0 +1,178 @@
+#!/usr/bin/perl --
+# usage:
+# dpkg-scanpackages .../binary .../noverride pathprefix >.../Packages.new
+# mv .../Packages.new .../Packages
+#
+# This is the core script that generates Packages files (as found
+# on the Debian FTP site and CD-ROMs).
+#
+# The first argument should preferably be a relative filename, so that
+# the Filename field has good information.
+#
+# Any desired string can be prepended to each Filename value by
+# passing it as the third argument.
+#
+# The noverride file is a series of lines of the form
+# <package> <priority> <section> <maintainer>
+# where the <maintainer> field is optional. Fields are separated by
+# whitespace.
+
+$version= '1.0.10'; # This line modified by Makefile
+
+%kmap= ('optional','suggests',
+ 'recommended','recommends',
+ 'class','priority',
+ 'package_revision','revision');
+
+%pri= ('priority',300,
+ 'section',290,
+ 'maintainer',280,
+ 'version',270,
+ 'depends',250,
+ 'recommends',240,
+ 'suggests',230,
+ 'conflicts',220,
+ 'provides',210,
+ 'filename',200,
+ 'size',180,
+ 'md5sum',170,
+ 'description',160);
+
+@ARGV==3 || die;
+
+$binarydir= shift(@ARGV);
+-d $binarydir || die $!;
+
+$override= shift(@ARGV);
+-e $override || die $!;
+
+$pathprefix= shift(@ARGV);
+
+open(F,"find $binarydir -name '*.deb' -print |") || die $!;
+while (<F>) {
+ chop($fn=$_);
+ substr($fn,0,length($binarydir)) eq $binarydir || die $fn;
+ open(C,"dpkg-deb -I $fn control |") || die "$fn $!";
+ $t=''; while (<C>) { $t.=$_; }
+ $!=0; close(C); $? && die "$fn $? $!";
+ undef %tv;
+ $o= $t;
+ while ($t =~ s/^\n*(\S+):[ \t]*(.*(\n[ \t].*)*)\n//) {
+ $k= $1; $v= $2;
+ $k =~ y/A-Z/a-z/;
+ if (defined($kmap{$k})) { $k= $kmap{$k}; }
+ $v =~ s/\s+$//;
+ $tv{$k}= $v;
+#print STDERR "K>$k V>$v<\n";
+ }
+ $t =~ m/^\n*$/ || die "$fn $o / $t ?";
+ defined($tv{'package'}) || die "$fn $o ?";
+ $p= $tv{'package'}; delete $tv{'package'};
+ defined($p1{$p}) && die "$fn $p repeat";
+ if (defined($tv{'filename'})) {
+ print(STDERR " ! Package $p (filename $fn) has Filename field !\n") || die $!;
+ }
+ $tv{'filename'}= "$pathprefix$fn";
+ open(C,"md5sum <$fn |") || die "$fn $!";
+ chop($_=<C>); m/^[0-9a-f]{32}$/ || die "$fn \`$_' $!";
+ $!=0; close(C); $? && die "$fn $? $!";
+ $tv{'md5sum'}= $_;
+ defined(@stat= stat($fn)) || die "$fn $!";
+ $stat[7] || die "$fn $stat[7]";
+ $tv{'size'}= $stat[7];
+ if (length($tv{'revision'})) {
+ $tv{'version'}.= '-'.$tv{'revision'};
+ delete $tv{'revision'};
+ }
+ for $k (keys %tv) {
+ $pv{$p,$k}= $tv{$k};
+ $k1{$k}= 1;
+ $p1{$p}= 1;
+ }
+ $_= substr($fn,length($binarydir));
+ s#/[^/]+$##; s#^/*##;
+ $psubdir{$p}= $_;
+ $pfilename{$p}= $fn;
+}
+$!=0; close(F); $? && die "$? $!";
+
+select(STDERR); $= = 1000; select(STDOUT);
+
+format STDERR =
+ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+$packages
+.
+
+sub writelist {
+ $title= shift(@_);
+ return unless @_;
+ print(STDERR " $title\n") || die $!;
+ $packages= join(' ',sort @_);
+ while (length($packages)) { write(STDERR) || die $!; }
+ print(STDERR "\n") || die $!;
+}
+
+@inover=();
+@samemaint=();
+
+open(O,"<$override") || die $!;
+while(<O>) {
+ s/\s+$//;
+ ($p,$priority,$section,$maintainer)= split(/\s+/,$_,4);
+ if (!defined($p1{$p})) {
+ push(@inover,$p);
+ next;
+ }
+ if (length($maintainer)) {
+ if ($pv{$p,'maintainer'} eq $maintainer) {
+ push(@samemaint," $p ($maintainer)\n");
+ } else {
+ $pv{$p,'maintainer'}= $maintainer;
+ }
+ }
+ $pv{$p,'priority'}= $priority;
+ $pv{$p,'section'}= $section;
+ if (length($psubdir{$p}) && $section ne $psubdir{$p}) {
+ print(STDERR " !! Package $p has \`Section: $section',".
+ " but file is in \`$psubdir{$p}' !!\n") || die $!;
+ $ouches++;
+ }
+ $o1{$p}= 1;
+}
+close(O);
+
+if ($ouches) { print(STDERR "\n") || die $!; }
+
+$k1{'maintainer'}= 1;
+$k1{'priority'}= 1;
+$k1{'section'}= 1;
+
+@missingover=();
+
+for $p (sort keys %p1) {
+ if (!defined($o1{$p})) {
+ push(@missingover,$p);
+ }
+ $r= "Package: $p\n";
+ for $k (sort { $pri{$b} <=> $pri{$a} } keys %k1) {
+ next unless length($pv{$p,$k});
+ $r.= "$k: $pv{$p,$k}\n";
+ }
+ $r.= "\n";
+ $written++;
+ print(STDOUT $r) || die $!;
+}
+close(STDOUT) || die $!;
+
+&writelist("** Packages in archive but missing from override file: **",
+ @missingover);
+&writelist("++ Packages appearing in override file but not in archive: ++",
+ @inover);
+if (@samemaint) {
+ print(STDERR
+ " -- Packages specifying same maintainer as override file: --\n",
+ @samemaint,
+ "\n") || die $!;
+}
+
+print(STDERR " Wrote $written entries to output Packages file.\n") || die $!;
diff --git a/scripts/install-info.8 b/scripts/install-info.8
new file mode 100644
index 000000000..2d8817982
--- /dev/null
+++ b/scripts/install-info.8
@@ -0,0 +1,245 @@
+.\" Hey, Emacs! This is an -*- nroff -*- source file.
+.\" Install-info and this manpage are Copyright 1994 by Ian Jackson.
+.\"
+.\" This is free software; see the GNU General Public Licence version 2
+.\" or later for copying conditions. There is NO warranty.
+.TH INSTALL-INFO 8 "29th November 1995" "Debian Project" "Debian GNU/Linux"
+.SH NAME
+install\-info \- create or update entry in Info directory
+.SH SYNOPSIS
+.B install\-info
+[\-\-version] [\-\-help] [\-\-debug] [\-\-maxwidth=nnn]
+[\-\-section regexp title] [\-\-infodir=xxx] [\-\-align=nnn]
+[\-\-quiet] [\-\-menuentry=xxx] [\-\-description=xxx] [\-\-remove]
+[\-\-] filename
+.SH DESCRIPTION
+.PP
+.B install-info
+makes, updates or removes entries in the Info directory, the
+.B dir
+file. When updating or creating entries, if no description is
+specified on the command line or in the Info file it attempts to guess
+a description from the contents of the file.
+
+See the description of the
+.B \-\-section
+option for details of where the entry will be placed and a description
+of the expected format of the
+.B dir
+file.
+.SS OPTIONS
+.TP
+.BI "[\-\-] " filename
+Gives the filename of the Info file whose menu entry is to be created,
+updated or removed. The basename of this filename is used as the
+referent of the menu entry which is created. This file must therefore
+exist (or be about to be installed, or have previously existed when
+removing an entry) in the same directory as the
+.B dir
+file (see the
+.B \-\-infodir
+option).
+
+If
+.I filename
+ends in
+.B .gz
+it is taken to refer to a file compressed with GNU gzip; if it doesn't
+exist, but a corresponding
+.IB filename .gz
+does, the latter is used instead.
+
+When adding or updating entries the file must exist at the path
+specified (possibly with an additional
+.B .gz
+extension).
+.TP
+.B \-\-remove
+Specifies that the entry for the file
+.I filename
+is to be removed; by default entries are created or updated.
+
+If the removal results in a section becoming empty the section heading
+(and the spare blank line) will be removed as well, unless this is the
+last section in the file or
+.B \-\-keep\-old
+is specified. See the
+.B \-\-section
+option for details about the expected format of the
+.B dir
+file.
+
+If there are several suitable entries in the
+.B dir
+file only those in the first matching contiguous group will be removed
+and the others silently ignored.
+
+It is not an error for no suitable entry to be found, though
+.B install\-info
+will issue a warning unless the
+.B \-\-quiet
+option was specified.
+
+When
+.B \-\-remove
+is specified the
+.BR \-\-maxwidth ", " \-\-align " and " \-\-calign
+formatting options are silently ignored.
+.TP
+.BI "\-\-section " "regexp title"
+Specifies that, if a new entry is to be made, it should be placed in a
+section of the
+.B dir
+file whose title matches
+.IR regexp .
+If no such section exists one will be created as the second last
+section in the file (see below), with title
+.IR title .
+A section is a part of the
+.B dir
+menu delimited by blank lines; the first line is assumed to be the
+title.
+
+If a new entry is to be created
+.B install-info
+will attempt to insert it within the section according in alphabetic
+order; if the entries in the section aren't already sorted the new
+location within the section will be unpredictable. The order of
+existing entries will not be changed.
+
+The default is to append new entries to the end of the file. The last
+section (even if it only consists of the title line) should always
+exist, to ensure that new sections are created in the right place.
+The final section should be titled to reflect the fact that Info files
+with no more well specified location are appended to it.
+
+If there is already an entry for the Info file being installed it is
+replaced in situ with the new entry.
+
+If a section is specified when removing an entry the section is
+ignored and a warning is issued.
+.TP
+.BI \-\-infodir= infodir
+Specifies that the
+.B dir
+file is, and the installed copy of the new Info file was, is or will
+be located in
+.IR infodir .
+The default is
+.BR /usr/info .
+.TP
+.BI \-\-align= nnn
+Specifies that the first line of the description should be indented at
+least
+.I nnn
+characters; extra spaces will be added as required. If necessary
+because of the length of the
+.B dir
+menu entry details it may be offset more. The default is 27.
+.TP
+.BI \-\-calign= nnn
+Specifies that the second and subsequent lines of the description
+should be indented at least
+.I nnn
+characters. The default is 29.
+.TP
+.BI \-\-maxwidth= nnn
+Specifies that the maximum width for the Info file is
+.IR nnn .
+This is used when wordwrapping the descriptive text.
+The default is 79.
+.TP
+.B \-\-quiet
+Prevents the usual display of the new menu entry just before it is
+inserted, and of the messages announcing the replacement and removal
+of existing entries and the creation and deletion of sections.
+.TP
+.B \-\-help
+Causes
+.B install-info
+to display its usage information and exit.
+.TP
+.B \-\-version
+Causes
+.B install-info
+to display its version and copyright information and exit.
+.TP
+.BI \-\-description= xxx
+Specifies that the description to use after the menu entry in new or
+updated entries be
+.IR xxx .
+The default is to use the the value specified in the Info file itself;
+this is found by searching for a section of the form
+.br
+.B START\-INFO\-DIR\-ENTRY
+.br
+.B * Auto-PGP: (auto-pgp). PGP under GNU Emacs.
+.br
+.B END\-INFO\-DIR\-ENTRY
+
+If the entry found in the Info file itself extends across several
+lines, each giving a menu entry, the text found in the file is used
+verbatim. In this case the alphabetic ordering scheme is turned off,
+and the entries are inserted at the top of section in question. In
+this case the
+.BR \-\-menuentry ", " \-\-maxwidth ", " \-\-align ", " \-\-calign
+.RB " and " \-\-menuentry
+options are ignored.
+
+If there is no
+.B dir
+entry in the file the program will try to find a paragraph early in
+the file starting
+.BR "this file documents" .
+It will capitalise the first character of the remainder, and use that.
+
+It is an error for none of these methods to yield a description.
+
+If a description argument is given when
+.B \-\-remove
+is specified it is ignored and a warning is issued.
+.TP
+.BI \-\-menuentry= xxx
+Specifies that the entry in the menu should be
+.IR xxx .
+The default is to use the the value specified in the Info file itself.
+If this is not present the basename of the Info file is used
+.RB "(any " ".info " "is deleted, and the entry is made mixed case)."
+See above for details of the format expected for the menu entry in the
+Info file.
+
+When removing entries the value of the
+.B \-\-menuentry
+option must match the actual menu entry field in the menu item to be
+removed (case not significant). If
+.B \-\-menuentry
+is omitted no check on the menu entry is done.
+.TP
+.B \-\-keep\-old
+Inhibits the replacement of existing entries and the removal of empty
+sections.
+
+If the file being installed alreay has an entry in the directory the
+old entry will be left alone instead of being replaced; the default is
+to overwrite any old entry found with the newly generated one.
+
+If
+.BR \-\-remove " is specified " \-\-keep\-old
+will prevent the removal of the section heading which would otherwise
+happen if the section is made empty by the removal.
+.TP
+.B \-\-test
+Enables test mode, which inhibits the update of the directory file.
+.TP
+.B \-\-debug
+Enables debugging mode, in which the results of some internal
+processing steps are shown.
+.SH "SEE ALSO"
+emacs(1), info(1), gzip(1)
+.SH COPYRIGHT
+Copyright 1994, Ian Jackson.
+.B install\-info
+is free software; see the GNU General Public Licence version 2 or
+later for copying conditions. There is
+.I no
+warranty.
diff --git a/scripts/install-info.pl b/scripts/install-info.pl
new file mode 100755
index 000000000..0455ab1a3
--- /dev/null
+++ b/scripts/install-info.pl
@@ -0,0 +1,342 @@
+#!/usr/bin/perl --
+
+# fixme: --dirfile option
+# fixme: sort entries
+# fixme: send to FSF ?
+
+$version= '0.93.42.2'; # This line modified by Makefile
+sub version {
+ print STDERR <<END;
+Debian GNU/Linux install-info $version. Copyright (C) 1994,1995
+Ian Jackson. This is free software; see the GNU General Public Licence
+version 2 or later for copying conditions. There is NO warranty.
+END
+}
+
+sub usage {
+ print STDERR <<END;
+usage: install-info [--version] [--help] [--debug] [--maxwidth=nnn]
+ [--section regexp title] [--infodir=xxx] [--align=nnn]
+ [--calign=nnn] [--quiet] [--menuentry=xxx] [--keep-old]
+ [--description=xxx] [--test] [--remove] [--] filename
+END
+}
+
+$infodir='/usr/info';
+$maxwidth=79;
+$align=27;
+$calign=29;
+
+undef $menuentry;
+undef $quiet;
+undef $nowrite;
+undef $keepold;
+undef $description;
+undef $sectionre;
+undef $sectiontitle;
+$0 =~ m|[^/]+$|; $name= $&;
+
+while ($ARGV[0] =~ m/^--/) {
+ $_= shift(@ARGV);
+ last if $eq eq '--';
+ if ($_ eq '--version') {
+ &version; exit 0;
+ } elsif ($_ eq '--quiet') {
+ $quiet=1;
+ } elsif ($_ eq '--test') {
+ $nowrite=1;
+ } elsif ($_ eq '--keep-old') {
+ $keepold=1;
+ } elsif ($_ eq '--remove') {
+ $remove=1;
+ } elsif ($_ eq '--help') {
+ &usage; exit 0;
+ } elsif ($_ eq '--debug') {
+ open(DEBUG,">&STDERR") || exit 1;
+ } elsif ($_ eq '--section') {
+ if (@ARGV < 2) {
+ print STDERR "$name: --section needs two more args\n";
+ &usage; exit 1;
+ }
+ $sectionre= shift(@ARGV);
+ $sectiontitle= shift(@ARGV);
+ } elsif (m/^--maxwidth=([0-9]+)$/) {
+ $maxwidth= $1;
+ } elsif (m/^--align=([0-9]+)$/) {
+ $align= $1;
+ } elsif (m/^--calign=([0-9]+)$/) {
+ $calign= $1;
+ } elsif (m/^--infodir=/) {
+ $infodir=$';
+ } elsif (m/^--menuentry=/) {
+ $menuentry=$';
+ } elsif (m/^--description=/) {
+ $description=$';
+ } else {
+ print STDERR "$name: unknown option \`$_'\n"; &usage; exit 1;
+ }
+}
+
+if (!@ARGV) { &version; print STDERR "\n"; &usage; exit 1; }
+
+$filename= shift(@ARGV);
+if (@ARGV) { print STDERR "$name: too many arguments\n"; &usage; exit 1; }
+
+if ($remove) {
+ print STDERR "$name: --section ignored with --remove\n" if length($sectiontitle);
+ print STDERR "$name: --description ignored with --remove\n" if length($description);
+}
+
+print STDERR "$name: test mode - dir file will not be updated\n"
+ if $nowrite && !$quiet;
+
+umask(umask(0777) & ~0444);
+
+$filename =~ m|[^/]+$|; $basename= $&; $basename =~ s/(\.info)?(\.gz)?$//;
+print DEBUG <<END;
+ infodir=\`$infodir' filename=\`$filename' maxwidth=\`$maxwidth'
+ menuentry=\`$menuentry' basename=\`$basename'
+ description=\`$description' remove=$remove
+END
+
+if (!$remove) {
+
+ if (!-f $filename && -f "$filename.gz" || $filename =~ s/\.gz$//) {
+ $filename= "gzip -d <$filename.gz |"; $pipeit= 1;
+ } else {
+ $filename= "< $filename";
+ }
+
+ if (!length($description)) {
+
+ open(IF,"$filename") || die "$name: read $filename: $!\n";
+ $asread='';
+ while(<IF>) { last if m/^START-INFO-DIR-ENTRY$/; }
+ while(<IF>) { last if m/^END-INFO-DIR-ENTRY$/; $asread.= $_; }
+ close(IF); &checkpipe;
+ if ($asread =~ m/(\* *[^:]+: *\([^\)]+\).*\. *.*\n){2,}/) {
+ $infoentry= $asread; $multiline= 1;
+ print DEBUG <<END;
+ multiline \`$asread'
+END
+ } elsif ($asread =~ m/^\* *([^:]+):( *\([^\)]+\)\.|:)\s*/) {
+ $menuentry= $1; $description= $';
+ print DEBUG <<END;
+ infile menuentry \`$menuentry' description \`$description'
+END
+ } elsif (length($asread)) {
+ print STDERR <<END;
+$name: warning, ignoring confusing INFO-DIR-ENTRY in file.
+END
+ }
+ }
+
+ if (length($infoentry)) {
+
+ $infoentry =~ m/\n/;
+ print "$`\n" unless $quiet;
+ $infoentry =~ m/^\* *([^:]+): *\(([^\)]+)\)/ || die; # internal error
+ $sortby= $1; $fileinentry= $2;
+
+ } else {
+
+ if (!length($description)) {
+ open(IF,"$filename") || die "$name: read $filename: $!\n";
+ $asread='';
+ while(<IF>) {
+ if (m/^\s*[Tt]his file documents/) {
+ $asread=$';
+ last;
+ }
+ }
+ if (length($asread)) {
+ while(<IF>) { last if m/^\s*$/; $asread.= $_; }
+ $description= $asread;
+ }
+ close(IF); &checkpipe;
+ }
+
+ if (!length($description)) {
+ print STDERR <<END;
+No \`START-INFO-DIR-ENTRY' and no \`This file documents'.
+$name: unable to determine description for \`dir' entry - giving up
+END
+ exit 1;
+ }
+
+ $description =~ s/^\s*(.)//; $_=$1; y/a-z/A-Z/;
+ $description= $_ . $description;
+
+ if (!length($menuentry)) {
+ $menuentry= $basename; $menuentry =~ s/\Winfo$//;
+ $menuentry =~ s/^.//; $_=$&; y/a-z/A-Z/;
+ $menuentry= $_ . $menuentry;
+ }
+
+ print DEBUG <<END;
+ menuentry=\`$menuentry' description=\`$description'
+END
+
+ $cprefix= sprintf("* %s: (%s).", $menuentry, $basename);
+ $align--; $calign--;
+ $lprefix= length($cprefix);
+ if ($lprefix < $align) {
+ $cprefix .= ' ' x ($align - $lprefix);
+ $lprefix= $align;
+ }
+ $prefix= "\n". (' 'x $calign);
+ $cwidth= $maxwidth+1;
+
+ for $_ (split(/\s+/,$description)) {
+ $l= length($_);
+ $cwidth++; $cwidth += $l;
+ if ($cwidth > $maxwidth) {
+ $infoentry .= $cprefix;
+ $cwidth= $lprefix+1+$l;
+ $cprefix= $prefix; $lprefix= $calign;
+ }
+ $infoentry.= ' '; $infoentry .= $_;
+ }
+
+ $infoentry.= "\n";
+ print $infoentry unless $quiet;
+ $sortby= $menuentry; $sortby =~ y/A-Z/a-z/;
+
+ }
+}
+
+if (!link("$infodir/dir","$infodir/dir.lock")) {
+ die "$name: failed to lock dir for editing! $!\n".
+ ($! =~ m/exists/i ? "try deleting $infodir/dir.lock ?\n" : '');
+}
+
+open(OLD,"$infodir/dir") || &ulquit("$name: open $infodir/dir: $!\n");
+@work= <OLD>;
+eof(OLD) || &ulquit("$name: read $infodir/dir: $!\n");
+close(OLD) || &ulquit("$name: close $infodir/dir after read: $!\n");
+while ($work[$#work] !~ m/\S/) { $#work--; }
+
+if (!$remove) {
+
+ for ($i=0; $i<=$#work; $i++) {
+ next unless $work[$i] =~ m/^\* *[^:]+: *\(([^\)]+)\).*\.\s/;
+ last if $1 eq $basename || $1 eq "$basename.info";
+ }
+ for ($j=$i; $j<=$#work+1; $j++) {
+ next if $work[$j] =~ m/^\s+\S/;
+ last unless $work[$j] =~ m/^\* *[^:]+: *\(([^\)]+)\).*\.\s/;
+ last unless $1 eq $basename || $1 eq "$basename.info";
+ }
+
+ if ($i < $j) {
+ if ($keepold) {
+ print "$name: existing entry for \`$basename' not replaced\n" unless $quiet;
+ $nowrite=1;
+ } else {
+ print "$name: replacing existing dir entry for \`$basename'\n" unless $quiet;
+ }
+ $mss= $i;
+ @work= (@work[0..$i-1], @work[$j..$#work]);
+ } elsif (length($sectionre)) {
+ for ($i=0; $i<=$#work && $work[$i] !~ m/^\* *menu/i; $i++) { }
+ $mss= -1;
+ for (; $i<=$#work; $i++) {
+ $_= $work[$i];
+ next if m/^\*/;
+ next unless m/$sectionre/io;
+ $mss= $i+1; last;
+ }
+ if ($mss < 0) {
+ print "$name: creating new section \`$sectiontitle'\n" unless $quiet;
+ for ($i= $#work; $i>=0 && $work[$i] =~ m/\S/; $i--) { }
+ $i >= 0 || &ulquit("$name: nowhere to create new section - giving up\n");
+ @work= (@work[0..$i], "$sectiontitle\n", "\n", @work[$i+1..$#work]);
+ $mss= $i+1;
+ }
+ while ($mss <= $#work) {
+ $work[$mss] =~ m/\S/ || last;
+ $work[$mss] =~ m/^\* *([^:]+):/ || ($mss++, next);
+ last if $multiline;
+ $_=$1; y/A-Z/a-z/;
+ last if $_ gt $sortby;
+ $mss++;
+ }
+ } else {
+ print "$name: no section specified for new entry, placing at end\n"
+ unless $quiet;
+ $mss= $#work+1;
+ }
+
+ @work= (@work[0..$mss-1], $infoentry, @work[$mss..$#work]);
+
+} else {
+
+ for ($i=0; $i<=$#work; $i++) {
+ next unless $work[$i] =~ m/^\* *([^:]+): *\((\w[^\)]*)\)/;
+ $tme= $1; $tfile= $2; $match= $&;
+ next unless $tfile eq $basename;
+ last if !length($menuentry);
+ $tme =~ y/A-Z/a-z/;
+ last if $tme eq $menuentry;
+ }
+ for ($j=$i; $j<=$#work+1; $j++) {
+ next if $work[$j] =~ m/^\s+\S/;
+ last unless $work[$j] =~ m/^\* *([^:]+): *\((\w[^\)]*)\)/;
+ $tme= $1; $tfile= $2;
+ last unless $tfile eq $basename;
+ next if !length($menuentry);
+ $tme =~ y/A-Z/a-z/;
+ last unless $tme eq $menuentry;
+ }
+ print DEBUG <<END;
+ i=$i \$work[\$i]=\`$work[$i]' j=$j \$work[\$j]=\`$work[$j]'
+END
+
+ if ($i < $j) {
+ print "$name: deleting entry \`$match ...'\n" unless $quiet;
+ $_= $work[$i-1];
+ unless (m/^\s/ || m/^\*/ || m/^$/ ||
+ $j > $#work || $work[$j] !~ m/^\s*$/) {
+ s/:?\s+$//;
+ if ($keepold) {
+ print "$name: empty section \`$_' not removed\n" unless $quiet;
+ } else {
+ $i--; $j++;
+ print "$name: deleting empty section \`$_'\n" unless $quiet;
+ }
+ }
+ @work= (@work[0..$i-1], @work[$j..$#work]);
+ } else {
+ print "$name: no entry for file \`$basename'".
+ (length($menuentry) ? " and menu entry \`$menuentry'": '').
+ ".\n"
+ unless $quiet;
+ }
+}
+
+if (!$nowrite) {
+ open(NEW,"> $infodir/dir.new") || &ulquit("$name: create $infodir/dir.new: $!\n");
+ print(NEW @work) || &ulquit("$name: write $infodir/dir.new: $!\n");
+ close(NEW) || &ulquit("$name: close $infodir/dir.new: $!\n");
+
+ unlink("$infodir/dir.old");
+ link("$infodir/dir","$infodir/dir.old") ||
+ &ulquit("$name: cannot backup old $infodir/dir, giving up: $!\n");
+ rename("$infodir/dir.new","$infodir/dir") ||
+ &ulquit("$name: install new $infodir/dir: $!\n");
+}
+
+unlink("$infodir/dir.lock") || die "$name: unlock $infodir/dir: $!\n";
+
+sub ulquit {
+ unlink("$infodir/dir.lock") ||
+ warn "$name: warning - unable to unlock $infodir/dir: $!\n";
+ die $_[0];
+}
+
+sub checkpipe {
+ return if !$pipeit || !$? || $?==0x8D00;
+ die "$name: read $filename: $?\n";
+}
+
+exit 0;
diff --git a/scripts/lib.pl b/scripts/lib.pl
new file mode 100644
index 000000000..ba9c12765
--- /dev/null
+++ b/scripts/lib.pl
@@ -0,0 +1,565 @@
+# -*- perl -*-
+#
+# dpkg library: Debian GNU/Linux package maintenance utility,
+# useful library functions.
+#
+# Copyright (C) 1994 Matt Welsh <mdw@sunsite.unc.edu>
+# Copyright (C) 1994 Carl Streeter <streeter@cae.wisc.edu>
+# Copyright (C) 1994 Ian Murdock <imurdock@debian.org>
+# Copyright (C) 1994 Ian Jackson <iwj10@cus.cam.ac.uk>
+#
+# dpkg 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,
+# or (at your option) any later version.
+#
+# dpkg 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.
+#
+# You should have received a copy of the GNU General Public
+# License along with dpkg; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# /var/lib/dpkg/ +---- status
+# |---- updates/ +---- <id>
+# | |---- tmp.i
+# | \---- <id>.new
+# |---- available
+# |---- lock
+# |---- info/ |---- <package>.{post,pre}{inst,rm}
+# |---- tmp.$$
+# \---- tmp.ci/ +---- control
+# |---- conffiles
+# |---- {post,pre}{inst,rm}
+# |---- list
+# \---- conffiles
+
+$backend = "dpkg-deb";
+$fpextract = "dpkg-deb";
+$md5sum = "md5sum";
+$dselect = "dselect";
+$dpkg = "dpkg";
+
+$status_mergeevery = 20;
+$tmp = "/tmp";
+$visiblecontroldir = "DEBIAN";
+
+sub setadmindir {
+ $dd = $_[0];
+ $statusdb = "$dd/status";
+ $updatesdir = "$dd/updates";
+ $availabledb = "$dd/available";
+ $scriptsdir = "$dd/info";
+ $listsdir = "$dd/info";
+ $lockfile = "$dd/lock";
+ $lockmine = "$dd/tmp.$$";
+ $controli = "$dd/tmp.ci";
+ $importantspace = "$updatesdir/tmp.i";
+}
+$orgadmindir= "/var/lib/dpkg";
+&setadmindir($orgadmindir);
+
+@nokeepfields= ('package','version','package_revision',
+ 'depends','recommended','optional','conflicts','part');
+# Don't keep these fields in the Available database if a new record is
+# merged which is missing values for any of them.
+
+$packagere = '\w[-_a-zA-Z0-9+.@:=%]+';
+$packageversionre= $packagere.'(\s*\([^()]+\))?';
+$singledependencyre= "$packageversionre(\\s*\\|\\s*$packageversionre)*";
+
+# Abbreviations for dpkg-deb options common to dpkg & dpkg-deb.
+%debabbrevact= ('b','build', 'c','contents', 'e','control', 'i','info',
+ 'f','field', 'x','extract', 'X','vextract');
+
+@keysortorder= ('package', 'status', 'version', 'package_revision',
+ 'maintainer', 'description',
+ 'depends', 'recommended', 'optional', 'conflicts',
+ 'list', 'conffiles');
+
+#*** replacements for things in headers ***#
+
+#require 'sys/errno.ph';
+sub ENOENT { 2; } # No such file or directory
+sub EEXIST { 17; } # File exists
+sub EISDIR { 21; } # Is a directory
+sub ENOTEMPTY { 39; } # Directory not empty
+
+#require 'sys/stat.ph';
+sub S_IFMT { 00170000; }
+sub S_IFREG { 0100000; }
+sub S_IFLNK { 0120000; }
+sub S_ISREG { ($_[0] & &S_IFMT) == &S_IFREG; }
+sub S_ISLNK { ($_[0] & &S_IFMT) == &S_IFLNK; }
+
+#require 'sys/wait.ph';
+sub WIFEXITED { ($_[0] & 0x0ff) == 0; }
+sub WIFSTOPPED { ($_[0] & 0x0ff) == 0x07f; }
+sub WIFSIGNALED { !&WIFEXITED && !&WIFSTOPPED; }
+sub WCOREDUMP { ($_[0] & 0x080) != 0; }
+sub WEXITSTATUS { ($_[0] & 0x0ff00) >> 8; }
+sub WSTOPSIG { ($_[0] & 0x0ff00) >> 8; }
+sub WTERMSIG { $_[0] & 0x07f; }
+
+#require 'sys/signal.ph';
+sub SIGPIPE { 13; }
+
+#require 'sys/syscall.ph';
+sub SYS_lseek { 19; }
+
+
+#*** /var/lib/dpkg database management - `exported' routines ***#
+
+sub database_start {
+ # Lock the package management databases, amalgamate any
+ # changes files, and leave the results in:
+ # From /var/lib/dpkg/status:
+ # %st_pk2v{ package_name, field_name } = field_value
+ # %st_p21{ package_name } = 1
+ # From /var/lib/dpkg/available:
+ # %av_pk2v{ package_name, field_name } = field_value
+ # %av_p21{ package_name } = 1
+ # From both:
+ # %all_k21{ field_name } = 1
+ &lock_database;
+ &read_status_mainfile;
+ &read_status_extrafiles;
+ &write_status_mainfile;
+ &delete_status_extrafiles;
+ &read_available_file;
+ &prepare_important_database;
+ &invent_status_availableonly_packages;
+}
+
+sub database_finish {
+ # Tidy up and unlock the package management databases.
+ &release_important_database;
+ &write_available_file;
+ &write_status_mainfile;
+ &delete_status_extrafiles;
+ &unlock_database;
+}
+
+sub amended_status {
+ # Record amended status of package (in an `extra' file).
+ local (@packages) = @_;
+ local ($p);
+ &debug("amended @packages");
+ for $p (@packages) {
+ $st_pk2v{$p,'status'}= "$st_p2w{$p} $st_p2h{$p} $st_p2s{$p}";
+ $st_p21{$p}= 1;
+ }
+ $all_k21{'status'}= 1;
+ local ($ef) = sprintf("%03d",$next_extrafile++);
+ &write_database_file("$updatesdir/$ef",*st_pk2v,*st_p21,1,@packages);
+ push(@status_extrafiles_done,$ef); &sync;
+ if ($next_extrafile >= $status_mergeevery) {
+ &write_status_mainfile;
+ &delete_status_extrafiles;
+ }
+ $status_modified= 1;
+ for $p (@packages) { delete $st_pk2v{$p,'status'}; }
+ &prepare_important_database;
+}
+
+sub note_amended_status {
+ # Note the fact that the status has been modified, but don't
+ # commit yet.
+ $status_modified= 1;
+}
+
+sub amended_available {
+ # Record amended available information (in core for the moment -
+ # noncritical, so we defer writing it out).
+ $available_modified++;
+ &invent_status_availableonly_packages(@_);
+}
+
+#*** internal routines ***#
+
+sub invent_status_availableonly_packages {
+ local ($p);
+ for $p (@_ ? @_ : keys %av_p21) {
+ next if defined($st_p2w{$p});
+ $st_p2w{$p}= 'unknown';
+ $st_p2h{$p}= 'ok';
+ $st_p2s{$p}= 'not-installed';
+ }
+}
+
+sub read_status_mainfile {
+ local ($p, @p);
+ &read_status_database_file($statusdb);
+}
+
+sub read_status_extrafiles {
+ local ($fn);
+ opendir(UPD,$updatesdir) || &bombout("cannot opendir updates $updatesdir: $!");
+ for $_ (sort readdir(UPD)) {
+ next if $_ eq '.' || $_ eq '..';
+ if (m/\.new$/ || m/\.old$/ || $_ eq 'tmp.i') {
+ unlink("$updatesdir/$_") ||
+ &bombout("cannot unlink old update temp file $updatesdir/$_: $!");
+ } elsif (m/^\d+$/) {
+ $fn= $_;
+ &read_status_database_file("$updatesdir/$fn");
+ $status_modified= 1; push(@status_extrafiles_done, $fn);
+ } else {
+ warn("$name: ignoring unexpected file in $updatesdir named \`$_'\n");
+ }
+ }
+ closedir(UPD);
+}
+
+sub read_status_database_file {
+ local ($filename) = @_;
+ @p= &read_database_file($filename,*st_pk2v,*st_p21);
+ for $p (@p) {
+ if (defined($st_pk2v{$p,'status'})) {
+ $v= $st_pk2v{$p,'status'};
+ $v =~ y/A-Z/a-z/;
+ $v =~
+ m/^(unknown|install|deinstall|purge)\s+(ok|hold)\s+(not-installed|unpacked|postinst-failed|installed|removal-failed|config-files)$/
+ || &bombout("package \`$p' has bad status in $statusdb (\`$v')");
+ $st_p2w{$p}= $1;
+ $st_p2h{$p}= $2;
+ $st_p2s{$p}= $3;
+ }
+ delete($st_pk2v{$p,'status'});
+ }
+ $status_modified= 0; @status_extrafiles_done= ();
+}
+
+sub write_status_mainfile {
+ return unless $status_modified;
+ local ($p);
+ for $p (keys %st_p21) {
+ $st_pk2v{$p,'status'}= "$st_p2w{$p} $st_p2h{$p} $st_p2s{$p}";
+ }
+ $all_k21{'status'}= 1;
+ unlink("$statusdb.old") || $!==&ENOENT ||
+ &bombout("unable to remove $statusdb.old: $!");
+ link("$statusdb","$statusdb.old") ||
+ &bombout("unable to back up $statusdb: $!");
+ &write_database_file($statusdb,*st_pk2v,*st_p21,0);
+ $status_modified= 0;
+ &sync;
+ for $p (keys %st_p21) { delete $st_pk2v{$p,'status'}; }
+}
+
+sub delete_status_extrafiles {
+#print STDERR "delete @status_extrafiles_done> "; <STDIN>;
+ for $_ (@status_extrafiles_done) {
+ unlink("$updatesdir/$_") ||
+ &bombout("cannot remove already-done update file $updatesdir/$_: $!");
+ }
+ $next_extrafile= 0;
+ @status_extrafiles_done= ();
+}
+
+sub read_available_file {
+ &read_database_file($availabledb,*av_pk2v,*av_p21);
+ $available_modified= 0;
+}
+
+sub write_available_file {
+ return unless $available_modified;
+ &write_database_file($availabledb,*av_pk2v,*av_p21,0);
+ $available_modified= 0;
+}
+
+#*** bottom level of read routines ***#
+
+sub read_database_file {
+ local ($filename, *xx_pk2v, *xx_p21) = @_;
+ local ($quick,$cf,@cf,%cf_k2v,@cwarnings,@cerrors,$p,@p)= 1;
+ &debug("reading database file $filename");
+ open(DB,"<$filename") || &bombout("unable to open $filename for reading: $!");
+ $/="";
+ @p=();
+ while (defined($cf=<DB>)) {
+ chop($cf);
+# $cf =~ s/\n+$/\n/;
+ $p= &parse_control_entry;
+# if (@cwarnings) {
+# warn("$name: warning, packaging database file $filename\n".
+# " contains oddities in entry for package \`$p':\n ".
+# join(";\n ",@cwarnings).
+# ".\n This is probably a symptom of a bug.\n");
+# }
+ if (@cerrors) {
+ &bombout("packaging database corruption - please report:\n".
+ " file $filename has error(s) in entry for \`$p':\n ".
+ join(";\n ",@cerrors). ".");
+ }
+ $xx_p21{$p}= 1;
+ for $k (keys %all_k21) { $xx_pk2v{$p,$k}= $cf_k2v{$k}; }
+ push(@p,$p);
+ }
+ &debug("database file $filename read");
+ $/="\n"; close(DB);
+ return @p;
+}
+
+sub parse_control_entry {
+ # Expects $cf to be a sequence of lines,
+ # representing exactly one package's information.
+ # Results are put in cf_k2v.
+ # @warnings and @errors are made to contain warning and error
+ # messages, respectively.
+ local ($ln,$k,$v,$p,$l);
+ @cwarnings= @cerrors= ();
+
+ undef %cf_k2v;
+# &debug(">>>$cf<<<#\n");
+ if (!$quick) {
+ if ($cf =~ s/\n\n+/\n/g) { push(@cwarnings, "blank line(s) found and ignored"); }
+ if ($cf =~ s/^\n+//) { push(@cwarnings, "blank line(s) at start ignored"); }
+ if ($cf !~ m/\n$/) {
+ $cf.= "\n"; push(@cwarnings, "missing newline after last line assumed");
+ }
+ if ($cf =~ s/\0//g) {
+ push(@cwarnings, "nul characters discarded");
+ }
+ }
+ $cf =~ s/\n([ \t])/\0$1/g; # join lines
+# &debug(">>>$cf<<<*\n");
+ $ln = 0;
+ for $_ (split(/\n/,$cf)) {
+ $ln++; s/\s+$//;
+ next if m/^#/;
+ m/^(\S+):[ \t]*/ || (push(@cerrors, "garbage at line $ln, \`$_'"), next);
+ $k= $1; $v= $'; $k =~ y/A-Z/a-z/; $k='package_revision' if $k eq 'revision';
+# &debug("key=\`$k' value=\`$v' line=\`$_'\n");
+ $ln += ($v =~ s/\0/\n/g);
+ $cf_k2v{$k}= $v;
+ $all_k21{$k}= 1;
+# while ($cf =~ s/^(\S+):[ \t]*(.*)\n//) {
+ }
+ return unless keys %cf_k2v;
+ $p= $cf_k2v{'package'}; delete $cf_k2v{'package'}; delete $all_k21{'package'};
+ $cf_k2v{'class'} =~ y/A-Z/a-z/ if defined($cf_k2v{'class'});
+ $cf_k2v{'section'} =~ y/A-Z/a-z/ if defined($cf_k2v{'section'});
+# length($cf) &&
+# push(@cerrors, "garbage at line $ln, \`".($cf =~ m/\n/ ? $` : $cf)."'");
+ if (!$quick) {
+ defined($p) || push(@cerrors, "no \`package' line");
+ $p =~ m/^$packagere$/o || &bad_control_field('package');
+ defined($cf_k2v{'version'}) || push(@cerrors, "no Version field");
+ for $f ('depends','recommended','optional','conflicts') {
+ next unless defined($cf_k2v{$f}) && length($cf_k2v{$f});
+ $cf_k2v{$f} =~ m/^$singledependencyre(\s*,\s*$singledependencyre)*$/o
+ || &bad_control_field("$f");
+ }
+ }
+ return $p;
+}
+
+sub bad_control_field {
+ push(@cerrors, "bad \`$_[0]' line, contains \`$cf_k2v{$_[0]}'");
+}
+
+#*** bottom level of database writing code ***#
+
+sub write_database_file {
+ local ($filename, *xx_pk2v, *xx_p21, $important, @packages) = @_;
+ local ($p,$tl,$k,$v);
+ if (!@packages) { @packages= keys(%xx_p21); }
+
+ &debug("called write_database_file $filename, important=$important, for @packages");
+ if (!$important) {
+ open(DB,">$filename.new") || &bombout("unable to create $filename.new: $!");
+ }
+ $tl= 0;
+ for $p (@packages) {
+ &write_database_string("\n") if $tl;
+ &write_database_string("Package: $p\n");
+ for $k (keys %all_k21) {
+ next unless defined($xx_pk2v{$p,$k});
+ $v= $xx_pk2v{$p,$k};
+ $v =~ s/\n(\S)/\n $1/g;
+ &write_database_string("$k: $v\n");
+ }
+ }
+ if ($important) {
+ if (!truncate(IMP,$tl)) {
+ if (print(IMP "#")) {
+ warn("$name: warning - unable to truncate $importantspace: $!;".
+ "\n commenting the rest out instead seems to have worked.\n");
+ } else {
+ &database_corrupted("unable to truncate $importantspace: $!");
+ }
+ }
+ close(IMP) || &database_corrupted("unable to close $importantspace: $!");
+ rename($importantspace,$filename) ||
+ &database_corrupted("unable to install $importantspace as $filename: $!");
+ } else {
+ close(DB) || &bombout("unable to close $filename.new: $!");
+ rename("$filename.new",$filename) ||
+ &bombout("unable to install $filename.new as $filename: $!");
+ }
+}
+
+sub write_database_string {
+ $tl += length($_[0]);
+ if ($important) {
+ print(IMP $_[0]) ||
+ &database_corrupted("failed write to update file $importantspace: $!");
+ } else {
+ print(DB $_[0]) ||
+ &bombout("failed to write to $filename.new: $!");
+ }
+}
+
+sub database_corrupted {
+ &debug("corruptingstatus @_");
+ print STDERR "$name - really horrible error:\n @_\n".
+ "Package manager status data is now out of step with installed system.\n".
+ "(Last action has not been recorded. Please try re-installing \`@packages'\n".
+ "to ensure system consistency, or seek assistance from an expert if\n".
+ "problems persist.)\n";
+ &cleanup; exit(2);
+}
+
+sub prepare_important_database {
+ open(IMP,"+>$importantspace") || &bombout("unable to create $importantspace: $!");
+ select((select(IMP),$|=1)[0]);
+ print(IMP "#padding\n"x512) || &bombout("unable to pad $importantspace: $!");
+ seek(IMP,0,0) || &bombout("unable to seek (rewind) $importantspace: $!");
+ &debug("important database prepared");
+}
+
+sub release_important_database {
+ close(IMP);
+ unlink($importantspace) || &bombout("unable to delete $importantspace: $!");
+ &debug("important database released");
+}
+
+#*** database lock management ***#
+
+sub lock_database {
+ # Lock the package management databases. Stale locks will
+ # be broken, but there is no concurrency checking on the lock-
+ # breaking code.
+ push(@cleanups,'unlink($lockmine)');
+ open(PID,">$lockmine") || &bombout("failed to create new pid file $lockmine: $!");
+ printf(PID "%010d\n",$$) || &bombout("failed to add pid to $lockmine: $!");
+ close(PID) || &bombout("failed to close new pid file $lockmine: $!");
+ unless (link($lockmine,$lockfile)) {
+ $! == &EEXIST || &bombout("failed to create lock on packages database: $!");
+ if (open(PID,"<$lockfile")) {
+ undef $/; $opid= <PID>; $/="\n";
+ $opid =~ m/^\d{10}\n$/ || &lockfailed(" (pid missing)");
+ close(PID);
+ -d '/proc/self' ||
+ &bombout("/proc/self not found ($!) - /proc not mounted ?");
+ -d sprintf("/proc/%d",$opid) && &lockfailed(" (in use by pid $opid)");
+ if (open(PID,"<$lockfile")) {
+ $opid eq <PID> || &lockfailed(' (pid changed)');
+ close(PID);
+ unlink($lockfile) ||
+ &bombout("failed to break stale lock on database: $!");
+ print STDERR
+ "$name: stale lock found on packages database, lock forced\n";
+ } else {
+ $!==&ENOENT ||
+ &bombout("failed to confirm who owns lock on database: $!");
+ }
+ } else {
+ $!==&ENOENT || &bombout("failed to determine who owns lock on database: $!");
+ }
+ link($lockmine,$lockfile) ||
+ &bombout("failed to create lock on packages database: $!");
+ }
+ push(@cleanups, 'unlink($lockfile) ||
+ warn("$name: failed to unlock packages database: $!\n")');
+ unlink($lockmine);
+}
+
+sub unlock_database {
+ unlink($lockfile) || &bombout("failed to unlock packages database: $!");
+ pop(@cleanups);
+}
+
+#*** error handling ***#
+
+sub lockfailed { &bombout("unable to lock packages database@_"); }
+sub bombout { print STDERR "$name - critical error: @_\n"; &cleanup; exit(2); }
+sub badusage { print STDERR "$name: @_\n\n"; &usage; &cleanup; exit(3); }
+
+sub outerr {
+ &bombout("failed write to stdout: $!");
+}
+
+sub cleanup {
+ while (@cleanups) {
+ eval(pop(@cleanups));
+ $@ && print STDERR "error while cleaning up: $@";
+ }
+}
+
+sub debug {
+ return unless $debug;
+ print "D: @_\n";
+}
+
+sub ecode {
+ local ($w,$s) = ($?,$!);
+ &debug("ecode $w syserr $s");
+ return
+# (($w & 0x0ffff) == 0x0ff00 ? "problems running program - exit code -1" :
+# ($w & 0x0ff) == 0 ? "exit status ".(($w & 0x0ff00) >> 8) :
+# ($w & 0x0ff) == 0x07f ? "stopped by signal ".(($w & 0x0ff00) >> 8) :
+# "killed by signal ".($w & 0x07f).($w & 0x080 ? " (core dumped)" : '')).
+ (&WIFEXITED($w) ? "exit status ".&WEXITSTATUS($w) :
+ &WIFSIGNALED($w) ? "killed by signal ".&WTERMSIG($w).
+ (&WCOREDUMP($w) ? " (core dumped)" : ""):
+ &WIFSTOPPED($w) ? "stopped due to signal ".&WSTOPSIG($w) :
+ "unknown status $w").
+ ($s ? ", system error $s" : '');
+}
+
+#*** miscellaneous helpful routines ***#
+
+sub readall {
+ local ($fh) = @_;
+ local ($r,$n,$this) = '';
+ for (;;) {
+ defined($n=read($fh,$this,4096)) || return undef;
+ $n || last;
+ $r.= $this;
+ }
+ return $r;
+}
+
+#sub debug_compare_verrevs {
+# local (@i)= @_;
+# local ($i)= &x_compare_verrevs(@i);
+# &debug("compare_verrevs >@i< = >$i<");
+# return $i;
+#}
+
+sub compare_verrevs {
+ local ($av,$ar,$bv,$br,$c) = @_;
+ $c = &compare_vnumbers($av,$bv); return $c if $c;
+ return &compare_vnumbers($ar,$br);
+}
+
+sub compare_vnumbers {
+ local ($a, $b) = @_;
+ do {
+ $a =~ s/^\D*//; $ad= $&; $ad =~ s/\W/ /g;
+ $b =~ s/^\D*//; $bd= $&; $bd =~ s/\W/ /g;
+ $cm = $ad cmp $bd; return $cm if $cm;
+ $a =~ s/^\d*//; $ad= $&;
+ $b =~ s/^\d*//; $bd= $&;
+ $cm = $ad <=> $bd; return $cm if $cm;
+ } while (length ($a) && length ($b));
+ return length ($a) cmp length ($b);
+}
+
+sub sync {
+ system('sync');
+}
diff --git a/scripts/perl-dpkg.pl b/scripts/perl-dpkg.pl
new file mode 100755
index 000000000..ba70fc5f5
--- /dev/null
+++ b/scripts/perl-dpkg.pl
@@ -0,0 +1,1482 @@
+#!/usr/bin/perl --
+#
+# dpkg: Debian GNU/Linux package maintenance utility
+#
+# Copyright (C) 1994 Matt Welsh <mdw@sunsite.unc.edu>
+# Copyright (C) 1994 Carl Streeter <streeter@cae.wisc.edu>
+# Copyright (C) 1994 Ian Murdock <imurdock@debian.org>
+# Copyright (C) 1994 Ian Jackson <iwj10@cus.cam.ac.uk>
+#
+# dpkg 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,
+# or (at your option) any later version.
+#
+# dpkg 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.
+#
+# You should have received a copy of the GNU General Public
+# License along with dpkg; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+$version= '0.93.15'; # This line modified by Makefile
+
+sub version {
+ print STDERR <<END;
+Debian GNU/Linux \`dpkg\' package handling tool version $version.
+Copyright (C)1994 Matt Welsh, Carl Streeter, Ian Murdock, Ian Jackson.
+This is free software; see the GNU General Public Licence version 2
+or later for copying conditions. There is NO warranty.
+END
+}
+
+sub usage {
+ print STDERR <<END;
+Usage: dpkg -i|--install <opts> <.deb file name> ... | -a|--auto <dir> ...
+ dpkg --unpack <opts> <.deb file name> ... | -a|--auto <dir> ...
+ dpkg -A|--avail <opts> <.deb file name> ... | -a|--auto <dir> ...
+ dpkg --configure <opts> <package name> ... | -a|--auto
+ dpkg -r|--remove <opts> <package name> ... | -a|--auto
+ dpkg -l|--list <status select> [<regexp> ...]
+ dpkg -s|--status <status select> [<package-name> ...]
+ dpkg -S|--search <glob pattern> ...
+ dpkg -b|--build|-c|--contents|-e|--control|--info|-f|--field|
+ -x|--extract|-X|--vextract ... (see dpkg-deb --help)
+Options: --purge --control-quiet --control-verbose --version --help
+ -R|--root=<directory> --admindir=<directory> --instdir=<directory>
+ --no-keep-old-conf --no-keep-new-conf -N|--no-also-select
+ --ignore-depends=<package name>,...
+ --conf-(same|diff|all)-(new|old|promptnew|promptold)
+ --force-<thing>,<thing>,... --no-force-...|--refuse-...
+Status selections: --isok-[o][h] (OK, Hold; alternatives are y, n)
+ --want-[u][i][d][p] (Unknown, Install, Deinstall, Purge)
+ --stat-[nupircNO] (Not, Unpacked, Postinst-failed, Installed, Removal-failed,
+ Config-files, Not/Config-files, Not/Config-files/Installed)
+Force things: conflicts, depends, downgrade, depends-version, prermfail,
+ configure-any, hold, extractfail
+ (default is --no-force everything, except --force-downgrade)
+Use \`$dselect\' for user-friendly package management.
+END
+}
+
+$instroot= '';
+$controlwarn = 1;
+$estatus = 0;
+$filename_pattern = "*.deb";
+%force= ( 'conflicts',0, 'depends',0, 'depends-version',0, 'downgrade',1,
+ 'prermfail',0, 'postrmfail',0, 'hold',0, 'configure-any',0,
+ 'extractfail',0 );
+
+%selectmap_h= ('o','ok', 'h','hold', 'y','ok', 'n','hold');
+%selectmap_w= ('u', 'unknown', 'i', 'install', 'd', 'deinstall', 'p', 'purge');
+%selectmap_s= ('n', 'not-installed',
+ 'u', 'unpacked',
+ 'p', 'postinst-failed',
+ 'i', 'installed',
+ 'r', 'removal-failed',
+ 'c', 'config-files',
+ 'n', 'not-installed,config-files',
+ 'o', 'not-installed,config-files,installed');
+%selectthings= ('isok','h', 'want','w', 'stat','s');
+
+require 'lib.pl'; # This line modified by Makefile
+$0 =~ m|[^/]+$|; $name = $dpkg;
+$|=1;
+umask(022);
+
+$action= 'none';
+
+%myabbrevact= ('i','install', 'r','remove', 'A','avail',
+ 'S','search', 'l','list', 's','status');
+
+# $conf...[0] corresponds to `same', 1 to diff
+$confusenew[0]= 0; $confprompt[0]= 0;
+$confusenew[1]= 1; $confprompt[1]= 1;
+# Ie, default is to prompt only when hashes differ,
+# and to use new when hashes differ
+
+while ($ARGV[0] =~ m/^-/) {
+ $_= shift(@ARGV);
+ $noptsdone++;
+ if (m/^--$/) {
+ $noptsdone--; last;
+ } elsif (m/^--(install|remove|unpack|configure|avail|list|status)$/) {
+ &setaction($1);
+ } elsif (m/^--(build|contents|control|info|field|extract|vextract)$/) {
+ $noptsdone--; &backend($1);
+ } elsif (m/^--ignore-depends=($packagere(,$packagere)*)$/o) {
+ grep($ignore_depends{$_}=1, split(/,/,$1));
+ } elsif (m/^--(force|no-force|refuse)-/) {
+ $fvalue= ($1 eq 'force');
+ for $fv (split(/,/,$')) {
+ defined($force{$fv}) || &badusage("unknown --force option \`$fv'");
+ $force{$fv}= $fvalue;
+ }
+ } elsif (m/^--conf-(same|diff|all)-(new|old|promptnew|promptold)$/) {
+ $new= $2 eq 'new' || $2 eq 'promptnew';
+ $prompt= $2 eq 'promptnew' || $2 eq 'promptold';
+ if ($1 ne 'same') { $confusenew[1]= $new; $confprompt[1]= $prompt; }
+ if ($1 ne 'diff') { $confusenew[0]= $new; $confprompt[0]= $prompt; }
+ } elsif (m/^--(\w+)-(\w+)$/ && defined($selectthings{$1})) {
+ $thisname= $1;
+ $thisthing= $selectthings{$thisname};
+ $_=$2;
+ eval '%thismap= %selectmap_'.$thisthing;
+ while (s/^.//) {
+ if (!defined($thismap{$&})) {
+ &badusage("unknown status letter $& for status field $thisname");
+ }
+ $thiscodes= $thismap{$&};
+ $selectdo.= "undef \$select_$thisthing;";
+ for $v (split(m/,/, $thiscodes)) {
+ $selectdo .= "\$select_$thisthing{'$v'}=1;";
+ }
+ }
+ } elsif (m/^--root=/) {
+ $instroot=$'; &setadmindir("$instroot/$orgadmindir");
+ } elsif (m/^--admindir=/) {
+ &setadmindir("$'");
+ } elsif (m/^--instdir=/) {
+ $instroot=$';
+ } elsif (m/^--auto$/) {
+ $auto= 1;
+ } elsif (m/^--purge$/) {
+ $purge= 1;
+ } elsif (m/^--skip-same-version$/) {
+ print STDERR
+ "Warning: dpkg --skip-same-version not implemented, will process\n".
+ " process even packages the same version of which is installed.\n";
+ } elsif (m/^--no-also-select$/) {
+ $noalsoselect= 1;
+ } elsif (m/^--control-verbose$/) {
+ $controlwarn= 1;
+ } elsif (m/^--control-quiet$/) {
+ $controlwarn= 0;
+ } elsif (m/^--no-keep-old-conf$/) {
+ $nokeepold= 1;
+ } elsif (m/^--no-keep-new-conf$/) {
+ $nokeepnew= 1;
+ } elsif (m/^--succinct-prompts$/) {
+ $succinct= 1;
+ } elsif (m/^--debug$/) {
+ $debug= 1;
+ } elsif (m/^--help$/) {
+ &usage; exit(0);
+ } elsif (m/^--version$/) {
+ &version; exit(0);
+ } elsif (m/^--/) {
+ &badusage("unknown option \`$_'");
+ } else {
+ s/^-//; $noptsdone--;
+ while (s/^.//) {
+ $noptsdone++;
+ if (defined($myabbrevact{$&})) {
+ &setaction($myabbrevact{$&});
+ } elsif (defined($debabbrevact{$&})) {
+ $noptsdone--; &backend($debabbrevact{$&});
+ } elsif ($& eq 'a') {
+ $auto= 1;
+ } elsif ($& eq 'D') {
+ $debug= 1;
+ } elsif ($& eq 'N') {
+ $noautoselect= 1;
+ } elsif ($& eq 'R') {
+ s/^=// || &badusage("missing value for -R=<dir> option");
+ $instroot= $_; &setadmindir("$instroot/$orgadmindir"); $_='';
+ } else {
+ &badusage("unknown option \`-$&'");
+ }
+ }
+ }
+}
+
+$action eq 'none' && &badusage("an action must be specified");
+
+&debug("arguments parsed");
+
+#*** list, status or search - the nonactive operations ***#
+
+if ($action eq 'list' || $action eq 'status') {
+ &database_start;
+ if ($action eq 'list' || !@ARGV) {
+ &selectall(*selectmap_h,*select_h);
+ &selectall(*selectmap_w,*select_w);
+ &selectall(*selectmap_s,*select_s);
+ if (@ARGV) { $select_s{'not-installed'}=0; }
+ }
+ $ecode= 0;
+ if ($action eq 'status') {
+ for ($i=0;$i<=$#keysortorder;$i++) {
+ $keysortorder{$keysortorder[$i]}= sprintf("%6d ",$i);
+# &debug("set $i: $keysortorder[$i], sortorder ".
+# "\`$keysortorder{$keysortorder[$i]}'");
+ }
+ @ARGV= &applyselcrit(sort keys %st_p21) unless @ARGV;
+ for $p (@ARGV) {
+ if (!$st_p21{$p}) {
+ print(STDERR "$name: no information available about package $p\n")
+ || &bombout("writing to stderr: $!");
+ $ecode= 1;
+ }
+ print("Package: $p\n",
+ "Status: $st_p2w{$p} $st_p2h{$p} $st_p2s{$p}\n") || &outerr;
+ for $k (sort { $keysortorder{$a}.$a cmp $keysortorder{$b}.$b; }
+ keys %all_k21) {
+# &debug("field $k, sortorder \`$keysortorder{$k}'");
+ next unless defined($st_pk2v{$p,$k});
+ $v= $st_pk2v{$p,$k}; next unless length($v);
+ if ($k eq 'conffiles' || $k eq 'list') {
+ $v= sprintf("(%d files, not listed)",
+ scalar(grep(m/\S/, split(/\n/,$v))));
+ }
+ print("$k: $v\n") || &outerr;
+ }
+ if (defined($av_p21{$p})) {
+ print("\n\`Available' version of package $p, where different:\n")
+ || &outerr;
+ for $k (keys %all_k21) {
+ next unless defined($av_pk2v{$p,$k});
+ $v= $st_pk2v{$p,$k}; next unless length($v);
+ $u= $st_pk2v{$p,$k}; next if $u eq $v;
+ print("$k: $v\n") || &outerr;
+ }
+ print("\n") || &outerr;
+ }
+ }
+ } else { # $action eq 'list'
+ $listhead=0;
+ if (@ARGV) {
+ for $r (@ARGV) {
+ &listinfo(&applyselcrit(sort grep(m/$r/,keys %st_p21)));
+ }
+ } else {
+ undef $r;
+ &listinfo(&applyselcrit(sort keys %st_p21));
+ }
+ }
+ &database_finish;
+ exit($ecode);
+}
+
+sub listinfo {
+ if (!@_) {
+ print(STDERR
+ defined($r) ?
+ "No selected packages found matching regexp \`$r'.\n" :
+ "No packages matched selection criteria.\n") ||
+ &bombout("writing to stderr: $!");
+ return;
+ }
+
+ if (!$listhead) {
+ print <<END
+Err? Name Version Rev Description
+| Status=Not/Unpacked/Postinst-failed/Installed/Removal-failed/Config-files
+|/ Desired=Unknown/Install/Deinstall/Purge
+||/ | | | |
++++-============-==========-===-===============================================
+END
+ || &outerr;
+ $listhead= 1;
+ }
+ for $p (@_) {
+ $des= $st_pk2v{$p,'description'};
+ $des= $` if $des =~ m/\n/;
+ printf("%s%.1s%.1s %-12.12s %-10.10s %-3.3s %-47.47s\n",
+ $st_p2h{$p} eq 'hold' ? 'x' : ' ', $st_p2s{$p}, $st_p2w{$p},
+ $p, $st_pk2v{$p,'version'}, $st_pk2v{$p,'package_revision'},
+ $des);
+ }
+}
+
+sub applyselcrit {
+ &debug("sel from @_");
+ for $f (@_) { &debug("$f :$st_p2s{$f},$select_s{$st_p2s{$f}}:$st_p2w{$f},$select_w{$st_p2w{$f}}:$st_p2h{$f},$select_h{$st_p2h{$f}}:"); }
+ @ascr= grep($select_s{$st_p2s{$_}} &&
+ $select_w{$st_p2w{$_}} &&
+ $select_h{$st_p2h{$_}},
+ @_);
+ &debug("sel gave @ascr");
+ @ascr;
+}
+
+sub selectall {
+ local (*map, *sel) = @_;
+ local ($v);
+ for $v (values %map) {
+ next if m/,/;
+ $sel{$v}=1;
+ }
+}
+
+if ($action eq 'search') {
+ @ARGV || &badusage("need at least one glob pattern for --$action");
+ &database_start;
+ while (@ARGV) {
+ $orgpat= $_= shift(@ARGV);
+ s/\W/\\$&/g;
+ s|\\\*\\\*|.*|g;
+ s|\\\*|[^/]*|g;
+ s|\\\?|[^/]|g;
+ $pat= $_; $f=0;
+ for $p (sort keys %st_p21) {
+ $s= $st_p2s{$p};
+ next if $s eq 'not-installed' || $s eq 'config-files';
+ &filesinpackage($arg, $package);
+ @ilist= grep(m/^$pat$/,@ilist);
+ next unless @ilist;
+ $f=1;
+ for $_ (@ilist) { print("$p: $_\n") || &outerr; }
+ }
+ if (!$f) {
+ print(STDERR "No packages found containing \`$orgpat'.\n")
+ || &bombout("writing to stderr: $!");
+ $ecode= 1;
+ }
+ }
+ &database_finish;
+ exit($ecode);
+}
+
+#*** lock and read in databases ***#
+
+&database_start;
+&debug("databases read");
+
+#*** derive argument list for --auto ***#
+
+if ($auto) {
+ if ($action eq 'install' || $action eq 'unpack' || $action eq 'avail') {
+ @ARGV || &badusage("need at least one directory for --$action --auto");
+ pipe(RP,WP) || &bombout("create pipe for \`find': $!");
+ defined($c= fork) || &bombout("fork for \`find': $!");
+ if (!$c) {
+ close(RP); open(STDOUT,">& WP"); close(WP);
+ exec('find',@ARGV,'-name',$filename_pattern,'-type','f','-print0');
+ die "$name: could not exec \`find': $!";
+ }
+ close(WP);
+ $/="\0"; @ARGV= <RP>; $/="\n";
+ eof || &bombout("read filenames from \`find': $!");
+ close(RP);
+ $!=0; waitpid($c,0) eq $c || &bombout("wait for \`find' failed: $!");
+ $? && &bombout("\`find' process returned error code ".&ecode);
+ @ARGV || &bombout("no packages found to $action");
+ } else {
+ @ARGV && &badusage("no package names may be specified with --$action --auto");
+ if ($action eq 'remove') {
+ eval 'sub condition {
+ $wants eq "deinstall" || $wants eq "purge" || return 0;
+ $cstatus eq "not-installed" && return 0;
+ $cstatus eq "config-files" && $wants eq "deinstall" && return 0;
+ return 1;
+ } 1;' || &internalerr("sub condition: $@");
+ } elsif ($action eq 'configure') {
+ eval 'sub condition {
+ $wants eq "install" || return 0;
+ $cstatus eq "unpacked" || $cstatus eq "postinst-failed" || return 0;
+ return 1;
+ } 1;' || &internalerr("sub condition: $@");
+ } else {
+ &internalerr("unknown auto nonames action $action");
+ }
+ for $p (keys %st_p21) {
+ next if $st_p2h{$p} eq 'hold';
+ $wants= $st_p2w{$p}; $cstatus= $st_p2s{$p};
+ next unless &condition;
+ push(@ARGV,$p);
+ }
+ }
+ &debug("auto: arglist @ARGV");
+} else {
+ @ARGV || &badusage("need a list of packages or filenames");
+}
+
+if ($action eq 'install' || $action eq 'unpack') {
+ grep(s:^[^/.]:./$&:, @ARGV); # Sanitise filenames
+}
+
+&debug("action: $action; arglist @ARGV");
+
+#*** actually do things ***#
+
+for $arg (@ARGV) {
+ $package= ''; @undo=();
+ &debug("&do_$action($arg)");
+ if (!eval "&do_$action(\$arg); 1;") { &handleerror || last; }
+ &checkpointstatus;
+}
+&checkpointstatus;
+
+if (!$abort) {
+ &debug("&middle_$action($arg)");
+ if (!eval "&middle_$action; 1;") { print STDERR $@; $abort=1; }
+}
+&checkpointstatus;
+
+if (!$abort) {
+ while (@deferred) {
+ $arg= shift(@deferred); $package= ''; @undo=();
+ &debug("&deferred_$action($arg) ($dependtry: $sincenothing)");
+ if (!eval "&deferred_$action(\$arg); 1;") { &handleerror || last; }
+ &checkpointstatus;
+ }
+ &checkpointstatus;
+}
+
+if ($errors) {
+ print STDERR "$name: $errors errors occurred.\n";
+ $estatus= 1;
+}
+
+&database_finish;
+&cleanup;
+
+exit($estatus);
+
+#*** useful subroutines for main control section ***#
+
+sub handleerror {
+ print STDERR $@;
+ if (length($package) && defined($st_p21{$package})) {
+ $st_p2h{$package}='hold'; &amended_status($package);
+ }
+ $errors++;
+ if ($errors >20) { print STDERR "$name: too many errors, halting\n"; return 0; }
+ return !$abort;
+}
+
+sub checkpointstatus {
+ return unless keys %statusupdated;
+ &amended_status(keys %statusupdated);
+ undef %statusupdated;
+}
+
+sub backend {
+ &setaction('');
+ ($noptsdone) && &badusage("action \`$_[0]' must be first argument");
+ &debug("backend --$_[0]");
+ exec($backend, "--$_[0]", @ARGV);
+ &bombout("unable to run $backend: $!");
+}
+
+sub setaction {
+ $action eq 'none' || &badusage("conflicting actions \`$action' and \`$1'");
+ $action= $_[0];
+}
+
+#*** error handlers for use in actions ***#
+
+sub warn { warn "$name - warning: @_\n"; }
+sub subcriterr { warn "$name - subcritical error: @_\n"; $estatus=1; }
+sub error { &acleanup; die "$name - error: @_\n"; }
+sub internalerr { &acleanup; die "$name - internal error, please report: @_\n"; }
+sub fatalerr { &acleanup; die "$name - fatal error, halting: @_\n"; $abort=1; }
+
+sub corruptingerr {
+ local ($corruptingerr)= @_;
+ &acleanup;
+ die "$name - horrible error: $corruptingerr;\n".
+ "Package manager data is now out of step with installed system.\n".
+ "Please re-install \`$package' to ensure system consistency!\n".
+ "(Seek assistance from an expert if problems persist.)\n";
+ $abort=1;
+}
+
+sub forcibleerr {
+ local ($msg,@forces) = @_;
+ if (@forces= grep($force{$_},@forces)) {
+ &warn("$msg (proceeding due to --force-$forces[0])");
+ } else {
+ &error("$msg (skipping this package)");
+ }
+}
+
+sub acleanup {
+ while (@undo) {
+ eval(pop(@undo));
+ $@ && print STDERR "error while cleaning up: $@";
+ }
+}
+
+#*** --install ***#
+
+sub do_install {
+ &do_unpack($arg);
+ $arg= $package;
+ &do_configure($arg);
+}
+
+sub deferred_install { &deferred_configure; }
+
+sub middle_install { &middle_configure }
+
+#*** --avail ***#
+
+sub do_avail {
+ unlink($controli);
+ if ($! != &ENOENT) {
+ system('rm','-rf',$controli);
+ unlink($controli);
+ $! == &ENOENT || &fatalerr("unable to get rid of $controli: $!");
+ }
+ &debug("extract control $backend --control $arg $controli");
+ $!=0; system($backend,"--control",$arg,$controli);
+ $? && &error("$arg: could not extract control information (".&ecode.")");
+ open(CONTROL,"$controli/control") ||
+ &error("$arg: corrupt - unable to read control file: $!");
+ &parse_control("$arg");
+ for $k (keys %cf_k2v) {
+ $av_pk2v{$p,$k}= $cf_k2v{$k};
+ }
+ for $k (@nokeepfields) {
+ delete $av_pk2v{$p,$k} unless defined($cf_k2v{$k});
+ }
+ &amended_available($p);
+ $package=$p;
+}
+
+sub deferred_avail { }
+sub middle_avail { }
+
+#*** --unpack ***#
+
+sub middle_unpack { }
+
+sub do_unpack {
+ &do_avail;
+ $cstatus= $st_p2s{$package};
+ if ($st_p2w{$package} ne 'install') {
+ if (!$noalsoselect) {
+ $st_p2w{$package}= 'install'; $statusupdated{$package}= 1;
+ print STDOUT "Selecting previously deselected package $package.\n";
+ } else {
+ print STDOUT "Skipping deselected package $package.\n";
+ return;
+ }
+ }
+ for $tp (split(/,/, $av_pk2v{$package,'conflicts'})) {
+ $tp =~ s/^\s*//; $tp =~ s/\s+$//;
+ ($tps, $rightver, $inst, $want, $tpp)= &installationstatus($tp);
+ unless ($tps eq 'not-installed' || $tps eq 'config-files' || !$rightver) {
+ &forcibleerr("$arg: conflicts with package $tpp ($want),".
+ " found $inst on system",
+ 'conflicts');
+ }
+ }
+ if ($cstatus eq 'installed') {
+ if (&compare_verrevs($av_pk2v{$package,'version'},
+ $av_pk2v{$package,'package_revision'},
+ $st_k2v{'version'},$st_k2v{'package_revision'}) <0) {
+ &forcibleerr("$arg: downgrading installed $package version ".
+ "$st_k2v{'version'}, ".
+ "package revision $st_k2v{'package_revision'} ".
+ "to older version ".
+ "$av_pk2v{$package,'version'}, ".
+ "package revision $av_pk2v{$package,'package_revision'}",
+ 'downgrade');
+ }
+ }
+ if (open(CONF,"$controli/conffiles")) {
+ @configf= <CONF>;
+ eof || &error("$arg: unable to read $controli/conffiles: $!");
+ close(CONF);
+ grep((chop, m,^/, || s,^,/,), @configf);
+ } elsif ($! != &ENOENT) {
+ &error("$arg: cannot get config files list: $!");
+ } else {
+ @configf= ();
+ }
+
+ if ($cstatus eq 'installed' || $cstatus eq 'unpacked' ||
+ $cstatus eq 'postinst-failed' || $cstatus eq 'removal-failed') {
+ &filesinpackage($arg,$package);
+ print STDOUT "Preparing to replace $package ...\n";
+ }
+ if ($cstatus eq 'installed') {
+ if (!eval {
+ &run_script_ne("$scriptsdir/$package.prerm", 'old pre-removal script',
+ 'upgrade',
+ $av_pk2v{$package,'version'}.'-'.
+ $av_pk2v{$package,'package_revision'});
+ 1;
+ }) {
+ &warn("$@... trying script from new package instead.");
+ &run_script("$controli/prerm", 'new prerm script',
+ 'failed-upgrade',
+ $st_k2v{'version'}.'-'.$st_k2v{'package_revision'});
+ }
+ push(@undo,
+ '$st_p2s{$package}= "postinst-failed"; $statusupdated{$package}=1;
+ &run_script_ne("$scriptsdir/$package.postinst",
+ "old post-installation script",
+ "abort-upgrade",
+ $av_pk2v{$package,"version"}."-".
+ $av_pk2v{$package,"package_revision"});
+ $st_p2s{$package}= "installed"; $statusupdated{$package}=1;');
+ }
+ @fbackups=();
+ if ($cstatus eq 'installed' || $cstatus eq 'unpacked' ||
+ $cstatus eq 'postinst-failed' || $cstatus eq 'removal-failed') {
+ for ($i=0; $i<=$#ilist; $i++) {
+ next if grep($_ eq $ilist[$i], @configf);
+ $_= $ilist[$i];
+ unless (lstat("$instroot/$_")) {
+ $! == &ENOENT || &error("old file $_ unstattable: $!");
+ next;
+ }
+ next if -d _;
+ rename("$instroot/$_","$instroot/$_.dpkg-tmp") ||
+ &error("couldn't rename old file $_ to $_.dpkg-tmp: $!");
+ push(@undo,
+ '$_=pop(@fbackups); rename("$instroot/$_.dpkg-tmp","$instroot/$_") ||
+ die "unable to undo rename of $_ to $_.dpkg-tmp: $!"');
+ push(@fbackups, $_);
+ }
+ if (!eval {
+ &run_script_ne("$scriptsdir/$package.postrm", 'old post-removal script',
+ 'upgrade',
+ $av_pk2v{$package,'version'}.'-'.
+ $av_pk2v{$package,'package_revision'});
+ 1;
+ }) {
+ &warn("$@... trying script from new package instead.");
+ &run_script("$controli/postrm", 'new post-removal script',
+ 'failed-upgrade',
+ $st_k2v{'version'}.'-'.$st_k2v{'package_revision'});
+ }
+ push(@undo,
+ '&run_script_ne("$scriptsdir/$package.preinst",
+ "old pre-installation script",
+ "abort-upgrade",
+ $av_pk2v{$package,"version"}.
+ "-".$av_pk2v{$package,"package_revision"})');
+ }
+ $shortarg= $arg; $shortarg =~ s:.{15,}/:.../:;
+ print STDOUT "Unpacking $arg, containing $package ...\n";
+ &run_script("$controli/preinst", 'pre-installation script',
+ 'upgrade', $st_k2v{'version'}.'-'.$st_k2v{'package_revision'});
+ push(@undo,'&run_script_ne("$controli/postrm", "post-removal script",
+ "abort-upgrade",
+ $st_k2v{"version"}."-".$st_k2v{"package_revision"})');
+ if ($cstatus ne 'not-installed') {
+ for $_ (split(/\n/,$st_pk2v{$package,'conffiles'})) {
+ s/^ //; next unless length($_);
+ if (!m/^(\S+) (-|newconffile|nonexistent|[0-9a-f]{32})$/) {
+ &warn("$arg: ignoring bad stuff in old conffiles field \`$_'");
+ next;
+ }
+ $oldhash{$1}= $2;
+ }
+ }
+ for $f (@configf) {
+ $drf= &conffderef($f); if (!defined($drf)) { next; }
+ if (lstat("$instroot/$drf.dpkg-tmp")) {
+ $undo=1;
+ } else {
+ $! == &ENOENT || &error("unable to stat backup config file $_.dpkg-tmp: $!");
+ if (lstat("$instroot/$drf")) {
+ rename("$instroot/$drf","$instroot/$drf.dpkg-tmp") ||
+ &error("couldn't back up config file $f (= $drf): $!");
+ $undo=1;
+ } elsif ($! == &ENOENT) {
+ $undo=0;
+ } else {
+ &error("unable to stat config file $drf: $!");
+ }
+ }
+ if ($undo) {
+ push(@undo,
+ '$_=pop(@undof); rename("$instroot/$_.dpkg-tmp","$instroot/$_") ||
+ die "unable to undo backup of config file $_: $!"');
+ push(@undof, $drf);
+ }
+ }
+
+ open(NL,">$listsdir/$package.list.new") ||
+ &error("$package: cannot create $listsdir/$package.list.new: $!");
+ defined($c= fork) || &error("$package: cannot pipe/fork for $backend --vextract");
+ if (!$c) {
+ if (!open(STDOUT,">&NL")) {
+ print STDERR "$name: cannot redirect stdout: $!\n"; exit(1);
+ }
+ $vexroot= length($instroot) ? $instroot : '/';
+ exec($backend,"--vextract",$arg,$vexroot);
+ print STDERR "$name: cannot exec $backend --vextract $arg $vexroot: $!\n";
+ exit(1);
+ }
+ $!=0; waitpid($c,0) == $c || &error("could not wait for $backend: $!");
+ $? && &forcibleerr("$package: failed to install (".&ecode.")", 'extractfail');
+
+ rename("$listsdir/$package.list.new","$listsdir/$package.list") ||
+ &error("$package: failed to install new $listsdir/$package.list: $!");
+
+ $newconff='';
+ for $f (@configf) {
+ $h= $oldhash{$f};
+ $h='newconffile' unless length($h);
+ $newconff.= "\n $f $h";
+ &debug("new hash, after unpack, of $f, is $h");
+ }
+
+ for $k (keys %all_k21) {
+ next if $k eq 'binary' || $k eq 'source' || $k eq 'section';
+ $st_pk2v{$package,$k}= $av_pk2v{$package,$k};
+ }
+ $st_pk2v{$package,'conffiles'}= $newconff; $all_k21{'conffiles'}= 1;
+ $st_p2s{$package}= 'unpacked'; $st_p2h{$package}= 'ok'; $st_p21{$package}= 1;
+ $statusupdated{$package}= 1;
+ @undo=(); @undof=();
+
+ for $f (@fbackups) {
+ unlink("$instroot/$f.dpkg-tmp") || $! == &ENOENT ||
+ &subcriterr("$package: unable to delete saved old file $f.dpkg-tmp: $!\n");
+ }
+
+ undef %fordeletion;
+ opendir(PI,"$scriptsdir") ||
+ &corruptingerr("$package: unable to read $scriptsdir directory ($!)");
+ while(defined($_=readdir(PI))) {
+ next unless substr($_,0,length($package)+1) eq $package.'.';
+ $fordeletion{$_}= 1;
+ }
+ closedir(PI);
+ opendir(PI,"$controli") ||
+ &corruptingerr("$package: unable to read $controli".
+ " new package control information directory ($!)");
+ $fordeletion{"$package.list"}= 0;
+ while(defined($_=readdir(PI))) {
+ next if m/^\.\.?$/ || m/^conffiles$/ || m/^control$/;
+ rename("$controli/$_","$scriptsdir/$package.$_") ||
+ &corruptingerr("$package: unable to install new package control".
+ " information file $scriptsdir/$package.$_ ($!)");
+ $fordeletion{"$package.$_"}= 0;
+ }
+ closedir(PI);
+ for $_ (keys %fordeletion) {
+ next unless $fordeletion{$_};
+ unlink("$scriptsdir/$_") ||
+ &corruptingerr("$package: unable to remove old package script".
+ " $scriptsdir/$_ ($!)");
+ }
+}
+
+#*** --configure ***#
+
+sub do_configure {
+ $package=$arg; $cstatus= $st_p2s{$package};
+ if (!defined($st_p21{$package})) { $cstatus= 'not-installed'; }
+ unless ($cstatus eq 'unpacked' || $cstatus eq 'postinst-failed') {
+ if ($cstatus eq 'not-installed') {
+ &error("no package named \`$package' is installed, cannot configure");
+ } else {
+ &error("$package: is currently in state \`$cstatus', cannot configure");
+ }
+ }
+ push(@deferred,$package);
+}
+
+sub middle_configure {
+ $sincenothing=0; $dependtry=1;
+}
+
+sub deferred_configure {
+ # The algorithm for deciding what to configure first is as follows:
+ # Loop through all packages doing a `try 1' until we've been round
+ # and nothing has been done, then do `try 2' and `try 3' likewise.
+ # Try 1:
+ # Are all dependencies of this package done ? If so, do it.
+ # Are any of the dependencies missing or the wrong version ?
+ # If so, abort (unless --force-depends, in which case defer)
+ # Will we need to configure a package we weren't given as an
+ # argument ? If so, abort - except if --force-configure-any,
+ # in which case we add the package to the argument list.
+ # If none of the above, defer the package.
+ # Try 2:
+ # Find a cycle and break it (see above).
+ # Do as for try 1.
+ # Try 3 (only if --force-depends-version).
+ # Same as for try 2, but don't mind version number in dependencies.
+ # Try 4 (only if --force-depends).
+ # Do anyway.
+ $package= $arg;
+ if ($sincenothing++ > $#deferred*2+2) {
+ $dependtry++; $sincenothing=0;
+ &internalerr("$package: nothing configured, but try was already 4 !")
+ if $dependtry > 4;
+ }
+ if ($dependtry > 1) { &findbreakcycle($package); }
+ ($ok, @aemsgs) = &dependencies_ok($package,'');
+ if ($ok == 1) {
+ push(@deferred,$package); return;
+ } elsif ($ok == 0) {
+ $sincenothing= 0;
+ &error("$arg: dependency problems - not configuring this package:\n ".
+ join("\n ",@aemsgs));
+ } elsif (@aemsgs) {
+ &warn("$arg: dependency problems, configuring anyway as you request:\n ".
+ join("\n ",@aemsgs));
+ }
+ $sincenothing= 0;
+ print STDOUT "Setting up $package ...\n";
+ if ($st_p2s{$package} eq 'unpacked') {
+ &debug("conffiles updating >$st_pk2v{$package,'conffiles'}<");
+ undef %oldhash; @configf=();
+ for $_ (split(/\n/,$st_pk2v{$package,'conffiles'})) {
+ s/^ //; next unless length($_);
+ if (!m/^(\S+) (-|newconffile|nonexistent|[0-9a-f]{32})$/) {
+ &warn("$arg: ignoring bad stuff in old conffiles field \`$_'");
+ next;
+ }
+ $oldhash{$1}= $2; push(@configf,$1);
+ &debug("old hash of $1 is $2");
+ }
+ undef %newhash;
+ for $file (@configf) {
+ $drf= &conffderef($file);
+ if (!defined($drf)) { $newhash{$file}= '-'; next; }
+ $newhash{$file}= &hash("$instroot/$drf");
+ &debug("new hash of $file is $newhash{$file} (old $oldhash{$file})");
+ if ($oldhash{$file} eq 'newconffile') {
+ $usenew= 1;
+ } else {
+ if (!&files_not_identical("$instroot/$drf",
+ "$instroot/$drf.dpkg-tmp")) {
+ rename("$instroot/$drf.dpkg-tmp",$drf) || $!==&ENOENT ||
+ &error("$package: unable to reinstall ".
+ "existing conffile $drf.dpkg-tmp: $!");
+ &debug("files identical $file");
+ } else {
+ $diff= $newhash{$file} ne $oldhash{$file};
+ $usenew= $confusenew[$diff];
+ &debug("the decision - diff $diff;".
+ " usenew $usenew prompt $confpromt[$diff]");
+ if ($confprompt[$diff]) {
+ $symlinked = $drf eq $file ? '' :
+ $succinct ? " (-> $drf)" :
+ " (which is a symlink to $drf)";
+ for (;;) {
+ print
+ $succinct ? "
+Package $package, file $file$symlinked, ".($diff ? "CHANGED": "not changed")
+ : $diff ? "
+In package $package, distributed version of configuration
+file $file$symlinked has changed
+since the last time it was installed. You may:
+ * Install the new version and edit it later to reflect your wishes.
+ ". ($nokeepold ?
+ "(Your old version will not be saved.)" :
+ "(Your old version will be saved in $drf.dpkg-old.)") . "
+ * Leave your old version in place, and perhaps check later that
+ you don't want to update it to take account of new features.
+ ". ($nokeepnew ?
+ "(The new version be discarded.)" :
+ "(The new version will be placed in $drf.dpkg-new.)")
+ : "
+Package $package contains the same distributed version of
+configuration file $file$symlinked
+as the last time it was installed. You may:
+ * Install the distributed version, overwriting your changes.
+ ". ($nokeepold ?
+ "(Your changed version will not be saved.)" :
+ "(Your changed version will be saved in $drf.dpkg-old.)") . "
+ * Leave your own version in place.
+ ". ($nokeepnew ?
+ "(The distributed version be discarded.)" :
+ "(The distributed version will be placed in $drf.dpkg-new.)");
+
+ print "
+$file: install new version ? (y/n, default=". ($usenew?'y':'n'). ") ";
+
+ $!=0; defined($iread= <STDIN>) ||
+ &error("$package: prompting, EOF/error on stdin: $!");
+ $_= $iread; s/^\s*//; s/\s+$//;
+ ($usenew=0, last) if m/^n(o)?$/i;
+ ($usenew=1, last) if m/^y(es)?$/i;
+ last if m/^$/;
+ print "\nPlease answer \`y' or \`n'.\n";
+ }
+ }
+ &debug("decided, usenew $usenew");
+ if ($usenew) {
+ &copyperm("$drf.dpkg-tmp",$drf,$drf);
+ if ($nokeepold) {
+ unlink("$instroot/$drf.dpkg-tmp") || $!==&ENOENT ||
+ &error("$package: unable to delete old conffile ".
+ "$drf.dpkg-tmp: $!");
+ unlink("$instroot/$drf.dpkg-old") || $!==&ENOENT ||
+ &error("$package: unable to delete very old ".
+ "conffile $drf.dpkg-old: $!");
+ } else {
+ rename("$instroot/$drf.dpkg-tmp","$instroot/$drf.dpkg-old")
+ || $!==&ENOENT ||
+ &error("$package: unable to back up old conffile ".
+ "$drf.dpkg-tmp as $drf.dpkg-old: $!");
+ }
+ } else {
+ unlink("$instroot/$drf.dpkg-new") || $!==&ENOENT ||
+ &error("$package: unable to delete old new conffile ".
+ "$drf.dpkg-new: $!");
+ if (!$nokeepnew) {
+ link("$instroot/$drf","$instroot/$drf.dpkg-new")
+ || $!==&ENOENT ||
+ &error("$package: unable save new conffile ".
+ "$drf as $drf.dpkg-new: $!");
+ }
+ if (!rename("$instroot/$drf.dpkg-tmp","$instroot/$drf")) {
+ $!==&ENOENT || &error("$package: unable reinstall old ".
+ "conffile $drf.dpkg-tmp as $drf: $!");
+ unlink("$instroot/$drf");
+ }
+ }
+ }
+ }
+ }
+ $newconff='';
+ for $f (@configf) {
+ $h= $newhash{$f}; $newconff.= "\n $f $h";
+ }
+ $st_pk2v{$package,'conffiles'}= $newconff; $all_k21{'conffiles'}= 1;
+ }
+ $st_p2s{$package}= 'postinst-failed'; $statusupdated{$package}= 1;
+ &run_script("$scriptsdir/$package.postinst",
+ 'post-installation script', 'configure');
+ $st_p2s{$package}= 'installed';
+ $st_p2h{$package}= 'ok'; $statusupdated{$package}= 1;
+}
+
+#*** --remove ***#
+
+sub do_remove {
+ $package=$arg; $cstatus= $st_p2s{$package};
+ if (!defined($st_p21{$package}) ||
+ $cstatus eq 'not-installed' ||
+ !$purge && $cstatus eq 'config-files') {
+ &error("$package: is not installed, cannot remove");
+ }
+ push(@deferred,$package);
+ if (!$auto) {
+ $ns= $purge ? 'purge' : 'deinstall';
+ if ($st_p2w{$package} ne $ns) {
+ $st_p2w{$package}= $ns; $statusupdated{$package}= 1;
+ }
+ }
+}
+
+sub middle_remove {
+ $sincenothing=0; $dependtry=1;
+ for $p (keys %st_p2s) {
+ $cstatus= $st_p2s{$p};
+ next unless $cstatus eq 'installed';
+ for $tp (split(/[\|,]/, $st_pk2v{$p,'depends'})) {
+ $tp =~ s/\s*\(.+\)\s*$//; $tp =~ s/^\s*//; $tp =~ s/\s+$//;
+ $tp =~ m/^$packagere$/o ||
+ &internalerr("package $p dependency $tp didn't match re");
+ $depended{$tp}.= "$p ";
+ }
+ }
+}
+
+sub deferred_remove {
+ $package= $arg;
+ if ($sincenothing++ > $#deferred*2+2) {
+ $dependtry++; $sincenothing=0;
+ &internalerr("$package: nothing configured, but try was already 4 !")
+ if $dependtry > 4;
+ }
+ @raemsgs= (); $rok= 2;
+ &debug("$package may be depended on by any of >$depended{$package}<");
+ for $fp (split(/ /, $depended{$package})) {
+ next if $fp eq '' || $ignore_depends{$tp};
+ $is= $st_p2s{$fp};
+ next if $is eq 'not-installed' || $is eq 'unpacked' ||
+ $is eq 'removal-failed' || $is eq 'config-files';
+ if ($dependtry > 1) { &findbreakcycle($fp); }
+ ($ok, @aemsgs) = &dependencies_ok($fp,$package);
+ if ($rok != 1) { push(@raemsgs,@aemsgs); }
+ $rok= $ok if $ok < $rok;
+ }
+ if ($rok == 1) {
+ push(@deferred,$package); return;
+ } elsif ($rok == 0) {
+ $sincenothing= 0;
+ &error("$arg: dependency problems - not removing this package:\n ".
+ join("\n ",@raemsgs));
+ } elsif (@raemsgs) {
+ &warn("$arg: dependency problems, removing anyway as you request:\n ".
+ join("\n ",@raemsgs));
+ }
+ $sincenothing= 0;
+ &filesinpackage($arg,$package);
+
+ undef %hash; @configfr= @configf= ();
+ for $_ (split(/\n/,$st_pk2v{$package,'conffiles'})) {
+ s/^ //; next unless length($_);
+ if (!m/^(\S+) (-|newconffile|nonexistent|[0-9a-f]{32})$/) {
+ &warn("$arg: ignoring bad stuff in old conffiles field \`$_'");
+ next;
+ }
+ unshift(@configfr,$1); push(@configf,$1);
+ $hash{$1}= $2;
+ }
+
+ if ($st_p2s{$package} ne 'config-files') {
+ print STDOUT "Removing $package ...\n";
+ &run_script("$scriptsdir/$package.prerm", 'pre-removal script', 'remove');
+ $st_p2s{$package}= 'removal-failed'; $statusupdated{$package}= 1;
+ for $file (reverse @ilist) {
+ next if grep($_ eq $file, @configf);
+ unlink("$instroot/$file.dpkg-tmp") || $! == &ENOENT ||
+ &error("$arg: cannot remove supposed old temp file $file: $!");
+ next if unlink("$instroot/$file");
+ next if $! == &ENOENT;
+ &error("$arg: cannot remove file $file: $!") unless $! == &EISDIR;
+ next if rmdir("$instroot/$file");
+ &error("$arg: cannot remove directory $file: $!") unless $! == &ENOTEMPTY;
+ }
+ &run_script("$scriptsdir/$package.postrm", 'post-removal script', 'remove');
+ opendir(DSD,"$scriptsdir") ||
+ &error("$arg: cannot read directory $scriptsdir: $!");
+ for $_ (readdir(DSD)) {
+ next unless m/\.[^.]$/;
+ next if $& eq '.postrm' || $& eq '.list';
+ # need postrm for --purge, and list has to go last in case it
+ # goes wrong
+ next unless $` eq $package;
+ unlink("$scriptsdir/$_") ||
+ &error("$arg: unable to delete control information $scriptsdir/$_: $!");
+ }
+ closedir(DSD);
+ $st_p2s{$package}= 'config-files';
+ $statusupdated{$package}= 1;
+ }
+ if ($purge) {
+ print STDOUT "Purging configuration files for $package ...\n";
+ push(@undo,
+ '$newconff="";
+ for $f (@configf) { $newconff.= "\n $f $hash{$f}"; }
+ $st_pk2v{$package,"conffiles"}= $newconff; $all_k21{"conffiles"}= 1;');
+ for $file (@configfr) {
+ $drf= &conffderef($file); if (!defined($drf)) { next; }
+ unlink("$instroot/$drf") || $! == &ENOENT ||
+ &error("$arg: cannot remove old config file $file (= $drf): $!");
+ $hash{$file}= 'newconffile';
+ unlink("$instroot/$file") || $! == &ENOENT ||
+ &error("$arg: cannot remove old config file $file: $!")
+ if $file ne $drf;
+ for $ext ('.dpkg-tmp', '.dpkg-old', '.dpkg-new', '~', '.bak', '%') {
+ unlink("$instroot/$drf$ext") || $! == &ENOENT ||
+ &error("$arg: cannot remove old config file $drf$ext: $!");
+ }
+ unlink("#$instroot/$drf#") || $! == &ENOENT ||
+ &error("$arg: cannot remove old auto-save file #$drf#: $!");
+ $drf =~ m,^(.*)/, || next; $dir= $1; $base= $';
+ if (opendir(CFD,"$instroot/$dir")) {
+ for $_ (readdir(CFD)) {
+ next unless m/\.~\d+~$/;
+ next unless $` eq $base;
+ unlink("$instroot/$dir/$_") || $! == &ENOENT ||
+ &error("$arg: cannot remove old emacs backup file $dir/$_: $!");
+ }
+ closedir(CFD);
+ if (grep($_ eq $dir, @ilist)) {
+ rmdir("$instroot/$dir") || $! == &ENOTEMPTY ||
+ &error("$arg: cannot remove config file directory $dir: $!");
+ }
+ } elsif ($! != &ENOENT) {
+ &error("$arg: cannot read config file dir $dir: $!");
+ }
+ }
+ &run_script("$scriptsdir/$package.postrm", 'post-removal script for purge',
+ 'purge');
+ unlink("$scriptsdir/$package.postrm") || $! == &ENOENT ||
+ &error("$arg: cannot remove old postrm script: $!");
+ &setnotinstalled;
+ @undo= ();
+ } elsif (!@configf && !stat("$scripts/$package.postrm")) {
+ # If there are no config files and no postrm script then we
+ # go straight into `purge'. However, perhaps the stat didn't
+ # fail with ENOENT ...
+ $! == &ENOENT || &error("$package: stat failed on postrm script: $!");
+ $st_p2w{$package}= 'purge';
+ &setnotinstalled;
+ }
+ $st_p2h{$package}= 'ok'; $statusupdated{$package}= 1;
+}
+
+sub setnotinstalled {
+ unlink("$listsdir/$package.list") ||
+ &error("$arg: unable to delete old file list: $!");
+ $st_p2s{$package}= 'not-installed';
+ for $k (keys %all_k21) { delete $st_pk2v{$package,$k}; }
+}
+
+#*** dependency processing - common to --configure and --remove ***#
+
+# The algorithm for deciding what to configure or remove first is as
+# follows:
+#
+# Loop through all packages doing a `try 1' until we've been round and
+# nothing has been done, then do `try 2' and `try 3' likewise.
+#
+# When configuring, in each try we check to see whether all
+# dependencies of this package are done. If so we do it. If some of
+# the dependencies aren't done yet but will be later we defer the
+# package, otherwise it is an error.
+#
+# When removing, in each try we check to see whether there are any
+# packages that would have dependencies missing if we removed this
+# one. If not we remove it now. If some of these packages are
+# themselves scheduled for removal we defer the package until they
+# have been done.
+#
+# The criteria for satisfying a dependency vary with the various
+# tries. In try 1 we treat the dependencies as absolute. In try 2 we
+# check break any cycles in the dependency graph involving the package
+# we are trying to process before trying to process the package
+# normally. In try 3 (which should only be reached if
+# --force-depends-version is set) we ignore version number clauses in
+# Depends lines. In try 4 (only reached if --force-depends is set) we
+# say "ok" regardless.
+#
+# If we are configuring and one of the packages we depend on is
+# awaiting configuration but wasn't specified in the argument list we
+# will add it to the argument list if --configure-any is specified.
+# In this case we note this as having "done something" so that we
+# don't needlessly escalate to higher levels of dependency checking
+# and breaking.
+
+sub dependencies_ok {
+ local ($dp, $removingp) = @_;
+ local ($tpo, $however_t, $ok, $found, @aemsgs, @oemsgs);
+ local ($tp, $rightver, $inst, $want, $thisf, $matched, $tpp);
+ $ok= 2; # 2=ok, 1=defer, 0=halt
+ &debug("checking dependencies of $dp (- $removingp)");
+ for $tpo (split(/,/, $st_pk2v{$dp,'depends'})) {
+ $tpo =~ s/^\s*//; $tpo =~ s/\s+$//;
+ &debug(" checking group $dp -> $tpo");
+ $matched= 0; @oemsgs=();
+ $found=0; # 0=none, 1=defer, 2=withwarning, 3=ok
+ for $tp (split(/\|/, $tpo)) {
+ $tp =~ s/^\s*//; $tp =~ s/\s+$//;
+ &debug(" checking possibility $dp -> $tp");
+ if ($ignore_depends{$tp}) { &debug("ignoring so ok"); $found=3; last; }
+ if (defined($cyclebreak{$dp,$tp})) { &debug("break cycle"); $found=3; last; }
+ if ($tp eq $removingp) {
+ ($tps, $rightver, $inst, $want, $tpp)= ('removing-now', 1, '','', $tp);
+ $matched= 1;
+ } else {
+ ($tps, $rightver, $inst, $want, $tpp)= &installationstatus($tp);
+ &debug("installationstatus($tp) -> !$tps!$rightver!$inst!$want!$tps|");
+ }
+ if (($tps eq 'installed' || $tps eq 'unpacked' || $tps eq 'postinst-failed')
+ && !$rightver) {
+ push(@oemsgs,"version of $tpp on system is $inst (wanted $want)");
+ if ($force{'depends'}) { $thisf= $dependtry >= 3 ? 2 : 1; }
+ } elsif ($tps eq 'unpacked' || $tps eq 'postinst-failed') {
+ if (grep($_ eq $tpp, @deferred)) {
+ $thisf=1;
+ } elsif (!length($removingp) && $force{'configure-any'}) {
+ &warn("will also configure $tpp");
+ push(@deferred,$tpp); $sincenothing=0; $thisf=1;
+ } else {
+ push(@oemsgs,"package $tpp is not configured yet");
+ if ($force{'depends'}) { $thisf= $dependtry >= 4 ? 2 : 1; }
+ }
+ } elsif ($tps eq 'installed') {
+ $found=3; last;
+ } elsif ($tps eq 'removing-now') {
+ push(@oemsgs,"$tpp is to be removed");
+ if ($force{'depends'}) { $thisf= $dependtry >= 4 ? 2 : 1; }
+ } else {
+ push(@oemsgs,"$tpp ($want) is not installed");
+ if ($force{'depends'}) { $thisf= $dependtry >= 4 ? 2 : 1; }
+ }
+ &debug(" found $found");
+ $found=$thisf if $thisf>$found;
+ }
+ &debug(" found $found matched $matched");
+ next if length($removingp) && !$matched;
+ if (length($removingp) && $tpo !~ m/\|/) {
+ $however_t= '';
+ } elsif (@oemsgs > 1) {
+ $however_t= "\n However, ". join(",\n ", @oemsgs[0..($#oemsgs-1)]).
+ " and\n ". $oemsgs[$#oemsgs]. ".";
+ } else {
+ $however_t= "\n However, @oemsgs.";
+ }
+ if ($found == 0) {
+ push(@aemsgs, "$dp depends on $tpo.$however_t");
+ $ok=0;
+ } elsif ($found == 1) {
+ $ok=1 if $ok>1;
+ } elsif ($found == 2) {
+ push(@aemsgs, "$dp depends on $tpo.$however_t");
+ } elsif ($found != 3) {
+ &internalerr("found value in deferred_configure $found not known");
+ }
+ }
+ &debug("ok $ok msgs >>@aemsgs<<");
+ return ($ok, @aemsgs);
+}
+
+sub findbreakcycle {
+ # Cycle breaking works recursively down the package dependency
+ # tree. @sofar is the list of packages we've descended down
+ # already - if we encounter any of its packages again in a
+ # dependency we have found a cycle.
+ #
+ # Cycles are preferentially broken by ignoring a dependency from
+ # a package which doesn't have a postinst script. If there isn't
+ # such a dependency in the cycle we break at the `start' of the
+ # cycle from the point of view of our package.
+ #
+ local ($package,@sofar) = @_;
+ local ($tp,$tpp,$tps,$rightver,$inst,$want,$i,$dr,$de,@sf);
+ &debug("findbreakcycle($package; @sofar)");
+ push(@sofar,$package);
+ for $tp (split(/[,|]/, $st_pk2v{$package,'depends'})) {
+ $tp =~ s/^\s*//; $tp =~ s/\s+$//;
+ ($tps, $rightver, $inst, $want, $tpp)= &installationstatus($tp);
+ next unless $tps eq 'config-files' || $tps eq 'unpacked';
+ next if $cyclebreak{$package,$tpp};
+ if (grep($_ eq $tpp, @sofar)) {
+ &debug("found cycle $package, $tpp (@sofar)");
+ @sf= (@sofar,$tpp);
+ for ($i=0;
+ $i<$#sf;
+ $i++) {
+ next if stat("$scriptsdir/$sf[$i].postinst");
+ last if $! == &ENOENT;
+ &error("$arg: unable to stat $scriptsdir/$sf[$i].postinst: $!");
+ }
+ $i=0 if $i>=$#sf;
+ ($dr,$de)= @sf[$i..$i+1];
+ if (!defined($cyclebreak{$dr,$de})) {
+ $sincenothing=0; $cyclebreak{$dr,$de}= 1;
+ &debug("broken cycle $i (@sf) at $dr -> $de");
+ return 1;
+ }
+ } else {
+ return if &findbreakcycle($tpp,@sofar);
+ }
+ }
+ return 0;
+}
+
+#*** useful subroutines for actions ***#
+
+sub filesinpackage {
+ # Returns the list in @ilist.
+ # If error, calls &error("$epfx: ...");
+ local ($epfx, $package) = @_;
+ open(LIST,"$listsdir/$package.list") ||
+ &error("$epfx: database broken for $package - ".
+ "can't get installed files list: $!");
+ @ilist= <LIST>;
+ eof || &error("$epfx: cannot read $listsdir/$package.list: $!");
+ close(LIST);
+ @ilist= grep((chop,
+ s|/$||,
+ m|^/| || s|^|/|,
+ m/./),
+ @ilist);
+}
+
+sub installationstatus {
+ local ($controlstring) = @_;
+ local ($lversion,$lpackage,$lstatus,$lrevision,$cmp) = @_;
+ local ($cc);
+ $lversion= $controlstring;
+ $lversion =~ s/^($packagere)\s*// ||
+ &internalerr("needed installation status of bogus thing \`$lversion'");
+ $lpackage= $1;
+ $lstatus= defined($st_p2s{$lpackage}) ? $st_p2s{$lpackage} : 'not-installed';
+ if ($lstatus ne 'not-installed') {
+ if (length($lversion)) {
+ $lversion =~ s/^\s*\(\s*// && $lversion =~ s/\s*\)\s*$// ||
+ &internalerr("failed to strip version \`$lversion'");
+ if ($lversion =~ s/^[><=]//) { $cc= $&; } else { $cc= '='; }
+ $lrevision = ($lversion =~ s/-([^-]+)$//) ? $1 : '';
+ $wantedstring= "version $lversion";
+ $wantedstring .= ", package revision $lrevision" if length($lrevision);
+ $cmp= &compare_verrevs($st_pk2v{$lpackage,'version'},
+ $st_pk2v{$lpackage,'package_revision'},
+ $lversion,
+ $lrevision);
+ $installedstring= "version $st_pk2v{$lpackage,'version'}";
+ $installedstring .=
+ ", package revision $st_pk2v{$lpackage,'package_revision'}"
+ if length($st_pk2v{$lpackage,'package_revision'});
+ if ($cc eq '>') {
+ $rightver= $cmp>=0; $wantedstring.= ' or later';
+ } elsif ($cc eq '<') {
+ $rightver= $cmp<=0; $wantedstring.= ' or earlier';
+ } else {
+ s/^=//;
+ $rightver= !$cmp; $wantedstring= "exactly $wantedstring";
+ }
+ } else {
+ $rightver= 1;
+ $wantedstring= "any version";
+ $installedstring= $st_pk2v{$lpackage,'version'}.'-'.
+ $st_pk2v{$lpackage,'package_revision'};
+ }
+ } else {
+ $rightver= -1;
+ $installedstring= "not installed";
+ }
+ return ($lstatus,$rightver,$installedstring,$wantedstring,$lpackage);
+}
+
+sub parse_control {
+ # reads from fh CONTROL
+ local ($fn) = @_;
+ local ($cf,$ln,$l,$k,$v);
+ defined($cf= &readall('CONTROL')) || &error("read control file $fn: $!");
+ close(CONTROL);
+ $p= &parse_control_entry;
+ if (@cwarnings) {
+ &warn("$fn: control file contains oddities: ".join("; ",@cwarnings))
+ unless $controlwarn;
+ }
+ if (@cerrors) {
+ &error("$fn: control file contains errors: ".join("; ",@cerrors));
+ }
+}
+
+sub run_script_ne {
+ local ($script,$describe,@args) = @_;
+ local ($extranewlines) = $script =~ m/postinst/;
+ &debug("running $describe = $script @args");
+ if (!stat("$script")) {
+ return if $! == &ENOENT;
+ die "couldn't stat $script: $!\n";
+ }
+ if (! -x _) {
+ chmod(0755, "$script") || die "couldn't make $script executable: $!\n";
+ }
+ print "\n" if $extranewlines;
+ &debug("forking now");
+ defined($rsc= fork) || die "couldn't fork for running $script: $!\n";
+ if (!$rsc) {
+ if ($instroot !~ m|^/*$| && !chroot($instroot)) {
+ print STDERR "$name: failed to chroot to $instroot for $describe: $!\n";
+ exit(1);
+ }
+ exec($script,@args);
+ print STDERR "$name: failed to exec $script: $!\n";
+ exit(1);
+ }
+ $!=0; waitpid($rsc,0) == $rsc || die "couldn't wait for $describe: $!\n";
+ $? && die "$describe failed (".&ecode.")\n";
+ &debug("script done");
+ print "\n" if $extranewlines;
+}
+
+sub run_script {
+ return if eval { &run_script_ne; 1; };
+ $rse= $@; chop($rse); &error("$package: $rse");
+}
+
+sub hash {
+ local ($file) = @_; # NB: filename must already have $instroot here
+ local ($c);
+ if (open(HF,"<$file")) {
+ defined($c= open(MDP,"-|")) || &error("fork/pipe for hash: $!");
+ if (!$c) {
+ if (!open(STDIN,"<&HF")) {
+ print STDERR "$name: unable to redirect stdin for hash: $!\n"; exit(1);
+ }
+ exec($md5sum); print STDERR "$name: unable to exec $md5sum: $!\n"; exit(1);
+ }
+ defined($hash= &readall('MDP')) || &error("unable to read from $md5sum: $!\n");
+ $!=0; close(MDP); $? && &error("$md5sum returned error (".&ecode.")");
+ $hash =~ s/\n+$//;
+ $hash =~ m/^[0-9a-f]{32}$/i || &error("$md5sum returned bogus output \`$hash'");
+ return $hash;
+ } elsif ($! == &ENOENT) {
+ return 'nonexistent';
+ } else {
+ &warn("$arg: unable to open conffile $file for hash: $!");
+ return '-';
+ }
+}
+
+sub files_not_identical {
+ local ($file1,$file2) = @_; # NB: filenames must already have $instroot here
+ if (stat($file1)) {
+ if (stat($file2)) {
+ system("cmp","-s",$file1,$file2);
+ if (&WIFEXITED($?)) {
+ $es= &WEXITSTATUS($?);
+ return $es if $es == 0 || $es == 1;
+ }
+ &error("cmp $file1 $file2 returned error (".&ecode.")");
+ } elsif ($! == &ENOENT) {
+ return 1;
+ } else {
+ &error("failed to stat conffile $file2: $!");
+ }
+ } elsif ($! == &ENOENT) {
+ if (stat($file2)) {
+ return 1;
+ } elsif ($! == &ENOENT) {
+ return 0;
+ } else {
+ &error("failed to stat conffile $file2: $!");
+ }
+ } else {
+ &error("failed to stat conffile $file1: $!");
+ }
+}
+
+sub copyperm {
+ local ($from,$to,$name) = @_;
+ if (@statv= stat("$instroot/$from")) {
+ chown($statv[4],$statv[5],"$instroot/$to") ||
+ $!==&ENOENT ||
+ &warn("$package: unable to preserve ownership of $name");
+ chmod($statv[2],"$instroot/$to") ||
+ $!==&ENOENT ||
+ &warn("$package: unable to preserve permissions of $name");
+ } elsif ($! != &ENOENT) {
+ &warn("$package: unable to check permissions and ownership of".
+ " $name in order to preserve them");
+ }
+}
+
+sub conffderef {
+ local ($file) = @_;
+ local ($drf, $warning);
+ $drf= $file; $warning='';
+ for (;;) {
+ if (!lstat("$instroot/$drf")) {
+ last if $! == &ENOENT; $warning= "unable to lstat: $!"; last;
+ } elsif (-f _) {
+ last;
+ } elsif (-l _) {
+ if (!defined($lv= readlink("$instroot/$drf"))) {
+ $warning= "unable to readlink: $!"; last;
+ }
+ if ($lv =~ m|^/|) {
+ $drf= $lv;
+ } else {
+ $drf =~ s|/[^/]+$|/$lv|;
+ }
+ } else {
+ $warning= "not a plain file or symlink"; last;
+ }
+ }
+ &debug("conffile $file drf $drf warns \`$warning'");
+ if ($warning) {
+ &warn("$arg: possible problem with configuration file $file (= $drf):\n".
+ " $warning");
+ return undef;
+ } else {
+ return $drf;
+ }
+}
diff --git a/scripts/start-stop-daemon.8 b/scripts/start-stop-daemon.8
new file mode 100644
index 000000000..787072016
--- /dev/null
+++ b/scripts/start-stop-daemon.8
@@ -0,0 +1,19 @@
+.\" Hey, Emacs! This is an -*- nroff -*- source file.
+.TH START\-STOP\-DAEMON 8 "29th November 1995" "Debian Project" "Debian GNU/Linux"
+.SH NAME
+start\-stop\-daemon \- Debian package installation tool
+.SH DESCRIPTION
+.B start\-stop\-daemon
+does not have a useful man page. Please do not report this as a bug,
+as this has already been done many times.
+
+Instead, if you are a competent and accurate writer and are willing to
+spend the time reading the source code and writing good manpages
+please write a better man page than this one.
+
+Type
+.B start\-stop\-daemon \-\-help
+for a brief summary of how to use dpkg.
+
+.SH AUTHOR
+Ian Jackson <ijackson@gnu.ai.mit.edu>.
diff --git a/scripts/start-stop-daemon.pl b/scripts/start-stop-daemon.pl
new file mode 100755
index 000000000..ecfeb8960
--- /dev/null
+++ b/scripts/start-stop-daemon.pl
@@ -0,0 +1,160 @@
+#!/usr/bin/perl --
+
+$version= '0.93.30'; # This line modified by Makefile
+sub usageversion {
+ print(STDERR <<END)
+Debian GNU/Linux start-stop-daemon $version. Copyright (C) 1995
+Ian Jackson. This is free software; see the GNU General Public Licence
+version 2 or later for copying conditions. There is NO warranty.
+
+Usage: start-stop-daemon --start | --stop | --version|--help options ...
+Options: --test --oknodo --exec <executable> --pidfile <pid-file>
+ --quiet|--verbose --user <username>|<uid> --name <process-name>
+ --signal <signal> --startas <pathname>
+ -- <... all of the rest are arguments to daemon ...>
+ Be careful - try not to call without --exec. \`start-stop-daemon --stop'
+ would send a SIGTERM to every process, if it weren't specially prevented.
+
+Exit status: 0 = done 1 = nothing done (=> 0 if --oknodo) 2 = trouble
+END
+ || &quit("failed to write usage: $!");
+}
+sub quit { print STDERR "start-stop-daemon: @_\n"; exit(2); }
+sub badusage { print STDERR "start-stop-daemon: @_\n\n"; &usageversion; exit(2); }
+
+$exitnodo= 1;
+$quietmode= 0;
+$signal= 15;
+undef $operation;
+undef $exec;
+undef $pidfile;
+undef $user;
+undef $name;
+undef $startas;
+
+while (@ARGV) {
+ $_= shift(@ARGV);
+ last if m/^--$/;
+ if (!m/^--/) {
+ &quit("unknown argument \`$_'");
+ } elsif (m/^--(help|version)$/) {
+ &usageversion; exit(0);
+ } elsif (m/^--test$/) {
+ $testmode= 1;
+ } elsif (m/^--quiet$/) {
+ $quietmode= 1;
+ } elsif (m/^--verbose$/) {
+ $quietmode= -1;
+ } elsif (m/^--oknodo$/) {
+ $exitnodo= 0;
+ } elsif (m/^--(start|stop)$/) {
+ $operation= $1; next;
+ } elsif (m/^--signal$/) {
+ $_= shift(@ARGV); m/^\d+$/ || &badusage("--signal takes a numeric argument");
+ $signal= $_;
+ } elsif (m/^--(exec|pidfile|name|startas)$/) {
+ defined($_= shift(@ARGV)) || &badusage("--$1 takes an argument");
+ eval("\$$1= \$_");
+ } elsif (m/^--user$/) {
+ defined($_= shift(@ARGV)) || &badusage("--user takes a username argument");
+ if (m/^\d+$/) {
+ $user= $_;
+ } else {
+ (@u= getpwnam($_)) || &quit("user \`$_' not found");
+ $user= $u[2];
+ }
+ $userspec= $_;
+ } else {
+ &badusage("unknown option \`$_'");
+ }
+}
+
+defined($operation) ||
+ &badusage("need --start or --stop");
+defined($exec) || defined($pidfile) || defined($user) ||
+ &badusage("need at least one of --exec, --pidfile or --user");
+$startas= $exec if !defined($startas);
+$operation ne 'start' || defined($startas) ||
+ &badusage("--start needs --exec or --startas");
+
+if (defined($exec)) {
+ $exec =~ s,^,./, unless $exec =~ m,^[./],;
+ (@ve= stat("$exec")) || &quit("unable to stat executable \`$exec': $!");
+}
+
+@found= ();
+if (defined($pidfile)) {
+ $pidfile =~ s,^,./, unless $pidfile =~ m,^[./],;
+ if (open(PID,"< $pidfile")) {
+ $pid= <PID>;
+ &check($1) if $pid =~ m/^\s*(\d+)\s*$/;
+ close(PID);
+ }
+} else {
+ opendir(PROC,"/proc") || &quit("failed to opendir /proc: $!");
+ $foundany= 0;
+ while (defined($pid= readdir(PROC))) {
+ next unless $pid =~ m/^\d+$/;
+ $foundany++; &check($pid);
+ }
+ $foundany || &quit("nothing in /proc - not mounted ?");
+}
+
+sub check {
+ local ($p)= @_;
+ if (defined($exec)) {
+ return unless @vp= stat("/proc/$p/exe");
+ return unless $vp[0] eq $ve[0] && $vp[1] eq $ve[1];
+ }
+ open(C,"/proc/$p/stat");
+ (@vs= stat(C)) || return;
+ if (defined($user)) {
+ (close(C), return) unless $vs[4] == $user;
+ }
+ if (defined($name)) {
+ $c= <C>; close(C);
+ return unless $c =~ m/^$p \(([^\)]*)\) / && $1 eq $name;
+ }
+ close(C);
+ push(@found,$p);
+}
+
+if ($operation eq 'start') {
+ if (@found) {
+ print "$exec already running.\n" unless $quietmode>0;
+ exit($exitnodo);
+ }
+ if ($testmode) {
+ print "would start $startas @ARGV.\n";
+ exit(0);
+ }
+ print "starting $exec ...\n" if $quietmode<0;
+ exec($startas,@ARGV);
+ &quit("unable to start $exec: $!");
+}
+
+$what= defined($name) ? $name :
+ defined($exec) ? $exec :
+ defined($pidfile) ? "process in pidfile \`$pidfile'" :
+ defined($user) ? "process(es) owned by \`$userspec'" :
+ &quit("internal error, this is a bug - please report:".
+ " no name,exec,pidfile,user");
+
+if (!@found) {
+ print "no $what found; none killed.\n" unless $quietmode>0;
+ exit($exitnodo);
+}
+
+for $pid (@found) {
+ if ($testmode) {
+ print "would send signal $signal to $pid.\n";
+ } else {
+ if (kill($signal,$pid)) {
+ push(@killed,$pid);
+ } else {
+ print "start-stop-daemon: warning: failed to kill $pid: $!\n"; #
+ }
+ }
+}
+print "stopped $what (pid @killed).\n" if $quietmode<0;
+exit(0);
diff --git a/scripts/update-alternatives.8 b/scripts/update-alternatives.8
new file mode 100644
index 000000000..d6719afd9
--- /dev/null
+++ b/scripts/update-alternatives.8
@@ -0,0 +1,19 @@
+.\" Hey, Emacs! This is an -*- nroff -*- source file.
+.TH UPDATE\-ALTERNATIVES 8 "29th November 1995" "Debian Project" "Debian GNU/Linux"
+.SH NAME
+update\-alternatives \- Debian package installation tool
+.SH DESCRIPTION
+.B update\-alternatives
+does not have a useful man page. Please do not report this as a bug,
+as this has already been done many times.
+
+Instead, if you are a competent and accurate writer and are willing to
+spend the time reading the source code and writing good manpages
+please write a better man page than this one.
+
+Type
+.B update\-alternatives \-\-help
+for a brief summary of how to use dpkg.
+
+.SH AUTHOR
+Ian Jackson <ijackson@gnu.ai.mit.edu>.
diff --git a/scripts/update-alternatives.pl b/scripts/update-alternatives.pl
new file mode 100755
index 000000000..a961253dc
--- /dev/null
+++ b/scripts/update-alternatives.pl
@@ -0,0 +1,429 @@
+#!/usr/bin/perl --
+
+#use POSIX; &ENOENT;
+sub ENOENT { 2; }
+# Sorry about this, but POSIX.pm isn't necessarily available
+
+$version= '0.93.80'; # This line modified by Makefile
+sub usageversion {
+ print(STDERR <<END)
+Debian GNU/Linux update-alternatives $version. Copyright (C) 1995
+Ian Jackson. This is free software; see the GNU General Public Licence
+version 2 or later for copying conditions. There is NO warranty.
+
+Usage: update-alternatives --install <link> <name> <path> <priority>
+ [--slave <link> <name> <path>] ...
+ update-alternatives --remove <name> <path>
+ update-alternatives --auto <name>
+ update-alternatives --display <name>
+<name> is the name in /etc/alternatives.
+<path> is the name referred to.
+<link> is the link pointing to /etc/alternatives/<name>.
+<priority> is an integer; options with higher numbers are chosen.
+
+Options: --verbose|--quiet --test --help --version
+ --altdir <directory> --admindir <directory>
+END
+ || &quit("failed to write usage: $!");
+}
+sub quit { print STDERR "update-alternatives: @_\n"; exit(2); }
+sub badusage { print STDERR "update-alternatives: @_\n\n"; &usageversion; exit(2); }
+
+$altdir= '/etc/alternatives';
+$admindir= '/var/lib/dpkg/alternatives';
+$testmode= 0;
+$verbosemode= 0;
+$mode='';
+$manual= 'auto';
+$|=1;
+
+sub checkmanymodes {
+ return unless $mode;
+ &badusage("two modes specified: $_ and --$mode");
+}
+
+while (@ARGV) {
+ $_= shift(@ARGV);
+ last if m/^--$/;
+ if (!m/^--/) {
+ &quit("unknown argument \`$_'");
+ } elsif (m/^--(help|version)$/) {
+ &usageversion; exit(0);
+ } elsif (m/^--test$/) {
+ $testmode= 1;
+ } elsif (m/^--verbose$/) {
+ $verbosemode= +1;
+ } elsif (m/^--quiet$/) {
+ $verbosemode= -1;
+ } elsif (m/^--install$/) {
+ &checkmanymodes;
+ @ARGV >= 4 || &badusage("--install needs <link> <name> <path> <priority>");
+ ($alink,$name,$apath,$apriority,@ARGV) = @ARGV;
+ $apriority =~ m/^[-+]?\d+/ || &badusage("priority must be an integer");
+ $mode= 'install';
+ } elsif (m/^--remove$/) {
+ &checkmanymodes;
+ @ARGV >= 2 || &badusage("--remove needs <name> <path>");
+ ($name,$apath,@ARGV) = @ARGV;
+ $mode= 'remove';
+ } elsif (m/^--(display|auto)$/) {
+ &checkmanymodes;
+ @ARGV || &badusage("--$1 needs <name>");
+ $mode= $1;
+ $name= shift(@ARGV);
+ } elsif (m/^--slave$/) {
+ @ARGV >= 3 || &badusage("--slave needs <link> <name> <path>");
+ ($slink,$sname,$spath,@ARGV) = @ARGV;
+ defined($aslavelink{$sname}) && &badusage("slave name $sname duplicated");
+ $aslavelinkcount{$slink}++ && &badusage("slave link $slink duplicated");
+ $aslavelink{$sname}= $slink;
+ $aslavepath{$sname}= $spath;
+ } elsif (m/^--altdir$/) {
+ @ARGV && &badusage("--altdir needs a <directory> argument");
+ $altdir= shift(@ARGV);
+ } elsif (m/^--admindir$/) {
+ @ARGV && &badusage("--admindir needs a <directory> argument");
+ $admindir= shift(@ARGV);
+ } else {
+ &badusage("unknown option \`$_'");
+ }
+}
+
+defined($aslavelink{$name}) && &badusage("name $name is both primary and slave");
+$aslavelinkcount{$alink} && &badusage("link $link is both primary and slave");
+
+$mode || &badusage("need --display, --install, --remove or --auto");
+$mode eq 'install' || !%slavelink || &badusage("--slave only allowed with --install");
+
+if (open(AF,"$admindir/$name")) {
+ $manual= &gl("manflag");
+ $manual eq 'auto' || $manual eq 'manual' || &badfmt("manflag");
+ $link= &gl("link");
+ while (($sname= &gl("sname")) ne '') {
+ push(@slavenames,$sname);
+ defined($slavenum{$sname}) && &badfmt("duplicate slave $tsname");
+ $slavenum{$sname}= $#slavenames;
+ $slink= &gl("slink");
+ $slink eq $link && &badfmt("slave link same as main link $link");
+ $slavelinkcount{$slink}++ && &badfmt("duplicate slave link $slink");
+ push(@slavelinks,$slink);
+ }
+ while (($version= &gl("version")) ne '') {
+ defined($versionnum{$version}) && &badfmt("duplicate path $tver");
+ push(@versions,$version);
+ $versionnum{$version}= $i= $#versions;
+ $priority= &gl("priority");
+ $priority =~ m/^[-+]?\d+$/ || &badfmt("priority $version $priority");
+ $priorities[$i]= $priority;
+ for ($j=0; $j<=$#slavenames; $j++) {
+ $slavepath{$i,$j}= &gl("spath");
+ }
+ }
+ close(AF);
+ $dataread=1;
+} elsif ($! != &ENOENT) {
+ &quit("failed to open $admindir/$name: $!");
+}
+
+if ($mode eq 'display') {
+ if (!$dataread) {
+ &pr("No alternatives for $name.");
+ } else {
+ &pr("$name - status is $manual.");
+ if (defined($linkname= readlink("$altdir/$name"))) {
+ &pr(" link currently points to $linkname");
+ } elsif ($! == &ENOENT) {
+ &pr(" link currently absent");
+ } else {
+ &pr(" link unreadable - $!");
+ }
+ $best= '';
+ for ($i=0; $i<=$#versions; $i++) {
+ if ($best eq '' || $priorities[$i] > $bestpri) {
+ $best= $versions[$i]; $bestpri= $priorities[$i];
+ }
+ &pr("$versions[$i] - priority $priorities[$i]");
+ for ($j=0; $j<=$#slavenames; $j++) {
+ next unless length($tspath= $slavepath{$i,$j});
+ &pr(" slave $slavenames[$j]: $tspath");
+ }
+ }
+ if ($best eq '') {
+ &pr("No versions available.");
+ } else {
+ &pr("Current \`best' version is $best.");
+ }
+ }
+ exit 0;
+}
+
+$best= '';
+for ($i=0; $i<=$#versions; $i++) {
+ if ($best eq '' || $priorities[$i] > $bestpri) {
+ $best= $versions[$i]; $bestpri= $priorities[$i];
+ }
+}
+
+if (defined($linkname= readlink("$altdir/$name"))) {
+ if ($linkname eq $best) {
+ $state= 'expected';
+ } elsif (defined($linkname2= readlink("$altdir/$name.dpkg-tmp"))) {
+ $state= 'expected-inprogress';
+ } else {
+ $state= 'unexpected';
+ }
+} elsif ($! == &ENOENT) {
+ $state= 'nonexistent';
+} else {
+ $state= 'unexpected';
+}
+
+# Possible values for:
+# $manual manual, auto
+# $state expected, expected-inprogress, unexpected, nonexistent
+# $mode auto, install, remove
+# all independent
+
+if ($mode eq 'auto') {
+ &pr("Setting up automatic selection of $name.");
+ unlink("$altdir/$name.dpkg-tmp") || $! == &ENOENT ||
+ &quit("unable to remove $altdir/$name.dpkg-tmp: $!");
+ unlink("$altdir/$name") || $! == &ENOENT ||
+ &quit("unable to remove $altdir/$name.dpkg-tmp: $!");
+ $state= 'nonexistent';
+ $manual= 'auto';
+} elsif ($state eq 'nonexistent') {
+ if ($mode eq 'manual') {
+ &pr("$altdir/$name has been deleted, returning to automatic selection.");
+ $mode= 'auto';
+ }
+}
+
+# $manual manual, auto
+# $state expected, expected-inprogress, unexpected, nonexistent
+# $mode auto, install, remove
+# mode=auto <=> state=nonexistent
+
+if ($state eq 'unexpected' && $manual eq 'auto') {
+ &pr("$altdir/$name has been changed (manually or by a script).\n".
+ "Switching to manual updates only.");
+ $manual= 'manual';
+}
+
+# $manual manual, auto
+# $state expected, expected-inprogress, unexpected, nonexistent
+# $mode auto, install, remove
+# mode=auto <=> state=nonexistent
+# state=unexpected => manual=manual
+
+&pr("Checking available versions of $name, updating links in $altdir ...\n".
+ "(You may modify the symlinks there yourself if desired - see \`man ln'.)");
+
+if ($mode eq 'install') {
+ if ($link ne $alink && $link ne '') {
+ &pr("Renaming $name link from $link to $alink.");
+ rename($link,$alink) || $! == &ENOENT ||
+ &quit("unable to rename $link to $alink: $!");
+ }
+ $link= $alink;
+ if (!defined($i= $versionnum{$apath})) {
+ push(@versions,$apath);
+ $versionnum{$apath}= $i= $#versions;
+ }
+ $priorities[$i]= $apriority;
+ for $sname (keys %aslavelink) {
+ if (!defined($j= $slavenum{$sname})) {
+ push(@slavenames,$sname);
+ $slavenum{$sname}= $j= $#slavenames;
+ }
+ $oldslavelink= $slavelinks[$j];
+ $newslavelink= $aslavelink{$sname};
+ $slavelinkcount{$oldslavelink}-- if $oldslavelink ne '';
+ $slavelinkcount{$newslavelink}++ &&
+ &quit("slave link name $newslavelink duplicated");
+ if ($newslavelink ne $oldslavelink && $oldslavelink ne '') {
+ &pr("Renaming $sname slave link from $oldslavelink to $newslavelink.");
+ rename($oldslavelink,$newslavelink) || $! == &ENOENT ||
+ &quit("unable to rename $oldslavelink to $newslavelink: $!");
+ }
+ $slavelinks[$j]= $newslavelink;
+ }
+ for ($j=0; $j<=$#slavenames; $j++) {
+ $slavepath{$i,$j}= $aslavepath{$slavenames[$j]};
+ }
+}
+
+if ($mode eq 'remove') {
+ if (defined($i= $versionnum{$apath})) {
+ $k= $#versions;
+ $versionnum{$versions[$k]}= $i;
+ delete $versionnum{$versions[$i]};
+ $versions[$i]= $versions[$k]; $#versions--;
+ $priorities[$i]= $priorities[$k]; $#priorities--;
+ for ($j=0; $j<=$#slavenames; $j++) {
+ $slavepath{$i,$j}= $slavepath{$k,$j};
+ delete $slavepath{$k,$j};
+ }
+ } else {
+ &pr("Alternative $apath for $name not registered, not removing.");
+ }
+}
+
+for ($j=0; $j<=$#slavenames; $j++) {
+ for ($i=0; $i<=$#versions; $i++) {
+ last if $slavepath{$i,$j} ne '';
+ }
+ if ($i > $#versions) {
+ &pr("Discarding obsolete slave link $slavenames[$j] ($slavelinks[$j]).");
+ unlink("$altdir/$slavenames[$j]") || $! == &ENOENT ||
+ &quit("unable to remove $slavenames[$j]: $!");
+ unlink($slavelinks[$j]) || $! == &ENOENT ||
+ &quit("unable to remove $slavelinks[$j]: $!");
+ $k= $#slavenames;
+ $slavenum{$slavenames[$k]}= $j;
+ delete $slavenum{$slavenames[$j]};
+ $slavelinkcount{$slavelinks[$j]}--;
+ $slavenames[$j]= $slavenames[$k]; $#slavenames--;
+ $slavelinks[$j]= $slavelinks[$k]; $#slavelinks--;
+ for ($i=0; $i<=$#versions; $i++) {
+ $slavepath{$i,$j}= $slavepath{$i,$k};
+ delete $slavepath{$i,$k};
+ }
+ $j--;
+ }
+}
+
+if ($manual eq 'manual') {
+ &pr("Automatic updates of $altdir/$name are disabled, leaving it alone.");
+ &pr("To return to automatic updates use \`update-alternatives --auto $name'.");
+} else {
+ if ($state eq 'expected-inprogress') {
+ &pr("Recovering from previous failed update of $name ...");
+ rename("$altdir/$name.dpkg-tmp","$altdir/$name") ||
+ &quit("unable to rename $altdir/$name.dpkg-tmp to $altdir/$name: $!");
+ $state= 'expected';
+ }
+}
+
+# $manual manual, auto
+# $state expected, expected-inprogress, unexpected, nonexistent
+# $mode auto, install, remove
+# mode=auto <=> state=nonexistent
+# state=unexpected => manual=manual
+# manual=auto => state!=expected-inprogress && state!=unexpected
+
+open(AF,">$admindir/$name.dpkg-new") ||
+ &quit("unable to open $admindir/$name.dpkg-new for write: $!");
+&paf($manual);
+&paf($link);
+for ($j=0; $j<=$#slavenames; $j++) {
+ &paf($slavenames[$j]);
+ &paf($slavelinks[$j]);
+}
+&paf('');
+$best= '';
+for ($i=0; $i<=$#versions; $i++) {
+ if ($best eq '' || $priorities[$i] > $bestpri) {
+ $best= $versions[$i]; $bestpri= $priorities[$i]; $bestnum= $i;
+ }
+ &paf($versions[$i]);
+ &paf($priorities[$i]);
+ for ($j=0; $j<=$#slavenames; $j++) {
+ &paf($slavepath{$i,$j});
+ }
+}
+&paf('');
+close(AF) || &quit("unable to close $admindir/$name.dpkg-new: $!");
+
+if ($manual eq 'auto') {
+ if ($best eq '') {
+ &pr("Last package providing $name ($link) removed, deleting it.");
+ unlink("$altdir/$name") || $! == &ENOENT ||
+ &quit("unable to remove $altdir/$name: $!");
+ unlink("$link") || $! == &ENOENT ||
+ &quit("unable to remove $altdir/$name: $!");
+ unlink("$admindir/$name.dpkg-new") ||
+ &quit("unable to remove $admindir/$name.dpkg-new: $!");
+ unlink("$admindir/$name") || $! == &ENOENT ||
+ &quit("unable to remove $admindir/$name: $!");
+ exit(0);
+ } else {
+ if (!defined($linkname= readlink($link)) && $! != &ENOENT) {
+ &pr("warning: $link is supposed to be a symlink to $altdir/$name\n".
+ " (or nonexistent); however, readlink failed: $!");
+ } elsif ($linkname ne "$altdir/$name") {
+ unlink("$link.dpkg-tmp") || $! == &ENOENT ||
+ &quit("unable to ensure $link.dpkg-tmp nonexistent: $!");
+ symlink("$altdir/$name","$link.dpkg-tmp") ||
+ &quit("unable to make $link.dpkg-tmp a symlink to $altdir/$name: $!");
+ rename("$link.dpkg-tmp",$link) ||
+ &quit("unable to install $link.dpkg-tmp as $link: $!");
+ }
+ if (defined($linkname= readlink("$altdir/$name")) && $linkname eq $best) {
+ &pr("Leaving $name ($link) pointing to $best.");
+ } else {
+ &pr("Updating $name ($link) to point to $best.");
+ }
+ unlink("$altdir/$name.dpkg-tmp") || $! == &ENOENT ||
+ &quit("unable to ensure $altdir/$name.dpkg-tmp nonexistent: $!");
+ symlink($best,"$altdir/$name.dpkg-tmp");
+ }
+}
+
+rename("$admindir/$name.dpkg-new","$admindir/$name") ||
+ &quit("unable to rename $admindir/$name.dpkg-new to $admindir/$name: $!");
+
+if ($manual eq 'auto') {
+ rename("$altdir/$name.dpkg-tmp","$altdir/$name") ||
+ &quit("unable to install $altdir/$name.dpkg-tmp as $altdir/$name");
+ for ($j=0; $j<=$#slavenames; $j++) {
+ $sname= $slavenames[$j];
+ $slink= $slavelinks[$j];
+ if (!defined($linkname= readlink($slink)) && $! != &ENOENT) {
+ &pr("warning: $slink is supposed to be a slave symlink to\n".
+ " $altdir/$sname, or nonexistent; however, readlink failed: $!");
+ } elsif ($linkname ne "$altdir/$sname") {
+ unlink("$slink.dpkg-tmp") || $! == &ENOENT ||
+ &quit("unable to ensure $slink.dpkg-tmp nonexistent: $!");
+ symlink("$altdir/$sname","$slink.dpkg-tmp") ||
+ &quit("unable to make $slink.dpkg-tmp a symlink to $altdir/$sname: $!");
+ rename("$slink.dpkg-tmp",$slink) ||
+ &quit("unable to install $slink.dpkg-tmp as $slink: $!");
+ }
+ $spath= $slavepath{$bestnum,$j};
+ unlink("$altdir/$sname.dpkg-tmp") || $! == &ENOENT ||
+ &quit("unable to ensure $altdir/$sname.dpkg-tmp nonexistent: $!");
+ if ($spath eq '') {
+ &pr("Removing $sname ($slink), not appropriate with $best.");
+ unlink("$altdir/$sname") || $! == &ENOENT ||
+ &quit("unable to remove $altdir/$sname: $!");
+ } else {
+ if (defined($linkname= readlink("$altdir/$sname")) && $linkname eq $spath) {
+ &pr("Leaving $sname ($slink) pointing to $spath.");
+ } else {
+ &pr("Updating $sname ($slink) to point to $spath.");
+ }
+ symlink("$spath","$altdir/$sname.dpkg-tmp") ||
+ &quit("unable to make $altdir/$sname.dpkg-tmp a symlink to $spath: $!");
+ rename("$altdir/$sname.dpkg-tmp","$altdir/$sname") ||
+ &quit("unable to install $altdir/$sname.dpkg-tmp as $altdir/$sname: $!");
+ }
+ }
+}
+
+sub pr { print(STDOUT "@_\n") || &quit("error writing stdout: $!"); }
+sub paf {
+ $_[0] =~ m/\n/ && &quit("newlines prohibited in update-alternatives files ($_[0])");
+ print(AF "$_[0]\n") || &quit("error writing stdout: $!");
+}
+sub gl {
+ $!=0; $_= <AF>;
+ length($_) || &quit("error or eof reading $admindir/$name for $_[0] ($!)");
+ s/\n$// || &badfmt("missing newline after $_[0]");
+ $_;
+}
+sub badfmt {
+ &quit("internal error: $admindir/$name corrupt: $_[0]");
+}
+
+exit(0);
diff --git a/scripts/update-rc.d.8 b/scripts/update-rc.d.8
new file mode 100644
index 000000000..3ed3d9049
--- /dev/null
+++ b/scripts/update-rc.d.8
@@ -0,0 +1,79 @@
+.\" Hey, Emacs! This is an -*- nroff -*- source file.
+.TH UPDATE\-RC.D 8 "29th November 1995" "Debian Project" "Debian/GNU Linux"
+.SH NAME
+update\-rc.d \- install and remove System-V style init script links
+.SH SYNOPSIS
+.B update\-rc.d
+.I <basename>
+\&remove
+.LP
+.B update-rc.d
+.I <basename>
+\&defaults
+.RI [ " <codenumber> " | " <startcodenumber>" " " "<stopcodenumber> " ]
+.LP
+.B update-rc.d
+.I <basename>
+\&start | stop
+.I <codenumber> <runlevel>
+.RI [ " <runlevel> " [ " <runlevel> " [ ... ]]]
+\&.
+.SH DESCRIPTION
+This manual page explains the Debian
+.B "update-rc.d"
+System-V init script link utility. It should be used when installing and
+removing init scripts on a Debian system.
+
+.SH REMOVING SCRIPTS
+When invoked with the
+.I remove
+option, update-rc.d removes the script and links to the script for the package
+.RI "" <basename> .
+It first finds and removes the script in
+.B /etc/init.d/
+and then removes all links to the script in
+.RB "" /etc/rc[0123456].d/ .
+
+.SH INSTALLING SCRIPTS
+When run with either the
+.RI "" defaults ", " start ", or " stop
+options, update-rc.d makes links pointing to the script in
+.RB "" /etc/init.d/ .
+The script must be installed before update-rc.d is run.
+The
+.I <codenumber>
+arguments specify the order in which the script will be executed.
+When
+.B init
+changes runlevels it executes the scripts in the order of their
+.I codenumber
+from lowest to highest.
+
+The
+.I <runlevel>
+arguments specify the runlevels that the script will be run in.
+As many as seven runlevels (0-6) may be specified.
+The last runlevel must be followed by a period.
+
+When invoked with the
+.I defaults
+option the start runlevels are
+.B 2 3 4 5
+and the stop runlevels are
+.RB "" "0 1 2 3 4 5 6" .
+If neither
+.I <codenumber>
+or
+.I <startcodenumber>
+and
+.I <stopcodenumber>
+are specified, then the stop and start codenumbers default to 20.
+
+.SH FILES
+.B /etc/init.d/
+.bl
+.B /etc/rc[0123456].d/
+
+.SH "SEE ALSO"
+.BR init (1),
+.BR inittab (1),
diff --git a/scripts/update-rc.d.sh b/scripts/update-rc.d.sh
new file mode 100755
index 000000000..d471cc177
--- /dev/null
+++ b/scripts/update-rc.d.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+#
+# Usage:
+# update-rc.d <basename> remove
+# update-rc.d <basename> [options]
+# Options are:
+# start <codenumber> <runlevel> <runlevel> <runlevel> .
+# stop <codenumber> <runlevel> <runlevel> <runlevel> .
+# defaults [<codenumber> | <startcode> <stopcode>]
+# (means start <startcode> 2 3 4 5
+# as well as stop <stopcode> 0 1 2 3 4 5 6
+# <codenumber> defaults to 20)
+
+set -e
+cd /etc
+
+initd='init.d'
+
+usage () { echo >&2 "\
+update-rc.d: error: $1.
+usage: update-rc.d <basename> remove
+ update-rc.d <basename> defaults [<cn> | <scn> <kcn>]
+ update-rc.d <basename> start|stop <cn> <r> <r> . ..."; exit 1 }
+
+getinode () {
+ inode="`ls -Li1 \"$1\" | sed -e 's/^ *//; s/ .*//'`"
+}
+
+if [ $# -lt 2 ]; then usage "too few arguments"; fi
+bn="$1"; shift
+
+if [ xremove = "x$1" ]; then
+ if [ $# != 1 ]; then usage "remove must be only action"; fi
+ if [ -f "$initd/$bn" ]; then
+ echo >&2 "update-rc.d: error: /etc/$initd/$bn exists during rc.d purge."
+ exit 1
+ fi
+ echo " Removing any system startup links to /etc/$initd/$bn ..."
+ trap 'rm -f "$initd/$bn"' 0
+ touch "$initd/$bn"
+ getinode "$initd/$bn"
+ own="$inode"
+ for f in rc?.d/[SK]*; do
+ getinode "$f"
+ if [ "x$inode" = "x$own" ]; then
+ rm "$f";
+ echo " $f"
+ fi
+ done
+ exit 0
+elif [ xdefaults = "x$1" ]; then
+ if [ $# = 1 ]; then sn=20; kn=20;
+ elif [ $# = 2 ]; then sn="$2"; kn="$2";
+ elif [ $# = 3 ]; then sn="$2"; kn="$3";
+ else usage "defaults takes only one or two codenumbers"; fi
+ set start "$sn" 2 3 4 5 . stop "$kn" 0 1 6 .
+elif ! [ xstart = "x$1" -o xstop = "x$1" ]; then
+ usage "unknown mode or add action $1"
+fi
+
+if ! [ -f "$initd/$bn" ]; then
+ echo >&2 "update-rc.d: warning /etc/$initd/$bn doesn't exist during rc.d setup."
+ exit 0
+fi
+
+getinode "$initd/$bn"
+own="$inode"
+for f in rc?.d/[SK]*; do
+ getinode "$f"
+ if [ "x$inode" = "x$own" ]; then
+ echo " System startup links pointing to /etc/$initd/$bn already exist."
+ exit 0
+ fi
+done
+
+echo " Adding system startup links pointing to /etc/$initd/$bn ..."
+while [ $# -ge 3 ]; do
+ if [ xstart = "x$1" ]; then ks=S
+ elif [ xstop = "x$1" ]; then ks=K
+ else usage "unknown action $1"; fi
+ number="$2"
+ shift; shift
+ while [ $# -ge 1 ]; do
+ case "$1" in
+ .)
+ break
+ ;;
+ ?)
+ ln -s "../$initd/$bn" "rc$1.d/$ks$number$bn"
+ echo " rc$1.d/$ks$number$bn -> ../$initd/$bn"
+ shift
+ continue
+ ;;
+ esac
+ usage 'runlevel is more than one character (forgotten . ?)'
+ done
+ shift
+done
+
+if [ $# != 0 ]; then
+ usage "surplus arguments, but not enough for an add action: $*"
+fi
+
+exit 0