summaryrefslogtreecommitdiff
path: root/scripts/dpkg-divert.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/dpkg-divert.pl')
-rw-r--r--scripts/dpkg-divert.pl220
1 files changed, 220 insertions, 0 deletions
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]"); }