dpkg-divert.pl 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #!/usr/bin/perl --
  2. #use POSIX; &ENOENT;
  3. sub ENOENT { 2; }
  4. # Sorry about this, but the errno-part of POSIX.pm isn't in perl-*-base
  5. $version= '1.0.11'; # This line modified by Makefile
  6. sub usageversion {
  7. print(STDERR <<END)
  8. Debian GNU/Linux dpkg-divert $version. Copyright (C) 1995
  9. Ian Jackson. This is free software; see the GNU General Public Licence
  10. version 2 or later for copying conditions. There is NO warranty.
  11. Usage:
  12. dpkg-divert [options] [--add] <file>
  13. dpkg-divert [options] --remove <file>
  14. dpkg-divert [options] --list [<glob-pattern>]
  15. Options: --package <package> | --local --divert <divert-to> --rename
  16. --quiet --test --help|--version --admindir <directory>
  17. <package> is the name of a package whose copy of <file> will not be diverted.
  18. <divert-to> is the name used by other packages' versions.
  19. --local specifies that all packages' versions are diverted.
  20. --rename causes dpkg-divert to actually move the file aside (or back).
  21. When adding, default is --local and --divert <original>.distrib.
  22. When removing, --package or --local and --divert must match if specified.
  23. Package preinst/postrm scripts should always specify --package and --divert.
  24. END
  25. || &quit("failed to write usage: $!");
  26. }
  27. $admindir= '/var/lib/dpkg';
  28. $testmode= 0;
  29. $dorename= 0;
  30. $verbose= 1;
  31. $mode='';
  32. $|=1;
  33. sub checkmanymodes {
  34. return unless $mode;
  35. &badusage("two modes specified: $_ and --$mode");
  36. }
  37. while (@ARGV) {
  38. $_= shift(@ARGV);
  39. last if m/^--$/;
  40. if (!m/^-/) {
  41. unshift(@ARGV,$_); last;
  42. } elsif (m/^--(help|version)$/) {
  43. &usageversion; exit(0);
  44. } elsif (m/^--test$/) {
  45. $testmode= 1;
  46. } elsif (m/^--rename$/) {
  47. $dorename= 1;
  48. } elsif (m/^--quiet$/) {
  49. $verbose= 0;
  50. } elsif (m/^--local$/) {
  51. $package= ':';
  52. } elsif (m/^--add$/) {
  53. &checkmanymodes;
  54. $mode= 'add';
  55. } elsif (m/^--remove$/) {
  56. &checkmanymodes;
  57. $mode= 'remove';
  58. } elsif (m/^--list$/) {
  59. &checkmanymodes;
  60. $mode= 'list';
  61. } elsif (m/^--divert$/) {
  62. @ARGV || &badusage("--divert needs a divert-to argument");
  63. $divertto= shift(@ARGV);
  64. $divertto =~ m/\n/ && &badusage("divert-to may not contain newlines");
  65. } elsif (m/^--package$/) {
  66. @ARGV || &badusage("--package needs a package argument");
  67. $package= shift(@ARGV);
  68. $divertto =~ m/\n/ && &badusage("package may not contain newlines");
  69. } elsif (m/^--admindir$/) {
  70. @ARGV || &badusage("--admindir needs a directory argument");
  71. $admindir= shift(@ARGV);
  72. } else {
  73. &badusage("unknown option \`$_'");
  74. }
  75. }
  76. $mode='add' unless $mode;
  77. open(O,"$admindir/diversions") || &quit("cannot open diversions: $!");
  78. while(<O>) {
  79. s/\n$//; push(@contest,$_);
  80. $_=<O>; s/\n$// || &badfmt("missing altname");
  81. push(@altname,$_);
  82. $_=<O>; s/\n$// || &badfmt("missing package");
  83. push(@package,$_);
  84. }
  85. close(O);
  86. if ($mode eq 'add') {
  87. @ARGV == 1 || &badusage("--add needs a single argument");
  88. $file= $ARGV[0];
  89. $file =~ m/\n/ && &badusage("file may not contain newlines");
  90. -d $file && &badusage("Cannot divert directories");
  91. $divertto= "$file.distrib" unless defined($divertto);
  92. $package= ':' unless defined($package);
  93. for ($i=0; $i<=$#contest; $i++) {
  94. if ($contest[$i] eq $file || $altname[$i] eq $file ||
  95. $contest[$i] eq $divertto || $altname[$i] eq $divertto) {
  96. if ($contest[$i] eq $file && $altname[$i] eq $divertto &&
  97. $package[$i] eq $package) {
  98. print "Leaving \`",&infon($i),"'\n" if $verbose > 0;
  99. exit(0);
  100. }
  101. &quit("\`".&infoa."' clashes with \`".&infon($i)."'");
  102. }
  103. }
  104. push(@contest,$file);
  105. push(@altname,$divertto);
  106. push(@package,$package);
  107. print "Adding \`",&infon($#contest),"'\n" if $verbose > 0;
  108. &checkrename($file,$divertto);
  109. &save;
  110. &dorename($file,$divertto);
  111. exit(0);
  112. } elsif ($mode eq 'remove') {
  113. @ARGV == 1 || &badusage("--remove needs a single argument");
  114. $file= $ARGV[0];
  115. for ($i=0; $i<=$#contest; $i++) {
  116. next unless $file eq $contest[$i];
  117. &quit("mismatch on divert-to\n when removing \`".&infoa."'\n found \`".
  118. &infon($i)."'") if defined($divertto) && $altname[$i] ne $divertto;
  119. &quit("mismatch on package\n when removing \`".&infoa."'\n found \`".
  120. &infon($i)."'") if defined($package) && $package[$i] ne $package;
  121. print "Removing \`",&infon($i),"'\n" if $verbose > 0;
  122. $orgfile= $contest[$i];
  123. $orgdivertto= $altname[$i];
  124. @contest= (($i > 0 ? @contest[0..$i-1] : ()),
  125. ($i < $#contest ? @contest[$i+1..$#contest] : ()));
  126. @altname= (($i > 0 ? @altname[0..$i-1] : ()),
  127. ($i < $#altname ? @altname[$i+1..$#altname] : ()));
  128. @package= (($i > 0 ? @package[0..$i-1] : ()),
  129. ($i < $#package ? @package[$i+1..$#package] : ()));
  130. &checkrename($orgdivertto,$orgfile);
  131. &dorename($orgdivertto,$orgfile);
  132. &save;
  133. exit(0);
  134. }
  135. print "No diversion \`",&infoa,"', none removed\n" if $verbose > 0;
  136. exit(0);
  137. } elsif ($mode eq 'list') {
  138. @ilist= @ARGV ? @ARGV : ('*');
  139. while (defined($_=shift(@ilist))) {
  140. s/\W/\\$&/g;
  141. s/\\\?/./g;
  142. s/\\\*/.*/g;
  143. push(@list,"^$_\$");
  144. }
  145. $pat= join('$|^',@list);
  146. for ($i=0; $i<=$#contest; $i++) {
  147. next unless ($contest[$i] =~ m/$pat/o ||
  148. $altname[$i] =~ m/$pat/o ||
  149. $package[$i] =~ m/$pat/o);
  150. print &infon($i),"\n";
  151. }
  152. exit(0);
  153. } else {
  154. &quit("internal error - bad mode \`$mode'");
  155. }
  156. sub infol {
  157. return (($_[2] eq ':' ? "local " : length($_[2]) ? "" : "any ").
  158. "diversion of $_[0]".
  159. (length($_[1]) ? " to $_[1]" : "").
  160. (length($_[2]) && $_[2] ne ':' ? " by $_[2]" : ""));
  161. }
  162. sub checkrename {
  163. return unless $dorename;
  164. ($rsrc,$rdest) = @_;
  165. my %exist;
  166. (@ssrc= lstat($rsrc)) || $! == &ENOENT ||
  167. &quit("cannot stat old name \`$rsrc': $!");
  168. $exist{$rsrc} = 1 unless $! != &ENOENT;
  169. (@sdest= lstat($rdest)) || $! == &ENOENT ||
  170. &quit("cannot stat new name \`$rdest': $!");
  171. $exist{$rdest} = 1 unless $! != &ENOENT;
  172. foreach $file ($rsrc,$rdest) {
  173. open (TMP, "a $file") || &quit("error checking \`$file': $!");
  174. close TMP;
  175. if ($exist{$file} == 1) {
  176. unlink ("$file");
  177. }
  178. }
  179. if (@ssrc && @sdest &&
  180. !($ssrc[0] == $sdest[0] && $ssrc[1] == $sdest[1])) {
  181. &quit("rename involves overwriting \`$rdest' with\n".
  182. " different file \`$rsrc', not allowed");
  183. }
  184. }
  185. sub dorename {
  186. return unless $dorename;
  187. return if $testmode;
  188. if (@ssrc) {
  189. if (@sdest) {
  190. unlink($rsrc) || &quit("rename: remove duplicate old link \`$rsrc': $!");
  191. } else {
  192. rename($rsrc,$rdest) || &quit("rename: rename \`$rsrc' to \`$rdest': $!");
  193. }
  194. }
  195. }
  196. sub save {
  197. return if $testmode;
  198. open(N,"> $admindir/diversions-new") || &quit("create diversions-new: $!");
  199. chmod 0644, "$admindir/diversions-new";
  200. for ($i=0; $i<=$#contest; $i++) {
  201. print(N "$contest[$i]\n$altname[$i]\n$package[$i]\n")
  202. || &quit("write diversions-new: $!");
  203. }
  204. close(N) || &quit("close diversions-new: $!");
  205. unlink("$admindir/diversions-old") ||
  206. $! == &ENOENT || &quit("remove old diversions-old: $!");
  207. link("$admindir/diversions","$admindir/diversions-old") ||
  208. $! == &ENOENT || &quit("create new diversions-old: $!");
  209. rename("$admindir/diversions-new","$admindir/diversions")
  210. || &quit("install new diversions: $!");
  211. }
  212. sub infoa { &infol($file,$divertto,$package); }
  213. sub infon { &infol($contest[$i],$altname[$i],$package[$i]); }
  214. sub quit { print STDERR "dpkg-divert: @_\n"; exit(2); }
  215. sub badusage { print STDERR "dpkg-divert: @_\n\n"; &usageversion; exit(2); }
  216. sub badfmt { &quit("internal error: $admindir/diversions corrupt: $_[0]"); }