3 # GNU Stow - manage the installation of multiple software packages
4 # Copyright (C) 1993, 1994, 1995, 1996 by Bob Glickstein
5 # Copyright (C) 2000,2001 Guillaume Morin
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 $ProgramName =~ s,.*/,,;
32 $Version = '@VERSION@';
44 # FIXME: use Getopt::Long
45 while (@ARGV && ($_ = $ARGV[0]) && /^-/) {
52 if ($opt =~ /^no?$/i) {
54 } elsif ($opt =~ /^c(o(n(f(l(i(c(ts?)?)?)?)?)?)?)?$/i) {
57 } elsif ($opt =~ /^dir?/i) {
59 if ($remainder =~ /^=/) {
60 $Stow = $'; # the stuff after the =
64 } elsif ($opt =~ /^t(a(r(g(et?)?)?)?)?/i) {
66 if ($remainder =~ /^=/) {
67 $Target = $'; # the stuff after the =
71 } elsif ($opt =~ /^verb(o(se?)?)?/i) {
73 if ($remainder =~ /^=(\d+)/) {
78 } elsif ($opt =~ /^de(l(e(te?)?)?)?$/i) {
80 } elsif ($opt =~ /^r(e(s(t(o(w?)?)?)?)?)?$/i) {
82 } elsif ($opt =~ /^vers(i(on?)?)?$/i) {
85 &usage(($opt =~ /^h(e(lp?)?)?$/) ? undef :
86 "unknown or ambiguous option: $opt");
89 @opts = split(//, $opt);
90 while ($_ = shift(@opts)) {
97 $Stow = (join('', @opts) || shift);
100 $Target = (join('', @opts) || shift);
102 } elsif ($_ eq 'v') {
104 } elsif ($_ eq 'D') {
106 } elsif ($_ eq 'R') {
108 } elsif ($_ eq 'V') {
111 &usage(($_ eq 'h') ? undef : "unknown option: $_");
117 &usage("No packages named") unless @ARGV;
119 # Changing dirs helps a lot when soft links are used
120 $current_dir = &getcwd;
122 chdir($Stow) || die "Cannot chdir to target tree $Stow ($!)\n";
125 # This prevents problems if $Target was supplied as a relative path
128 chdir($current_dir) || die "Your directory does not seem to exist anymore ($!)\n";
130 $Target = &parent($Stow) unless $Target;
132 chdir($Target) || die "Cannot chdir to target tree $Target ($!)\n";
135 foreach $package (@ARGV) {
136 $package =~ s,/+$,,; # delete trailing slashes
137 if ($package =~ m,/,) {
138 die "$ProgramName: slashes not permitted in package names\n";
142 if ($Delete || $Restow) {
143 @Collections = @ARGV;
144 &Unstow('', &RelativePath($Target, $Stow));
147 if (!$Delete || $Restow) {
148 foreach $Collection (@ARGV) {
149 warn "Stowing package $Collection...\n" if $Verbose;
150 &StowContents($Collection, &RelativePath($Target, $Stow));
155 local($dir1, $dir2) = @_;
157 local(@d1) = split(/\/+/, $dir1);
158 local(@d2) = split(/\/+/, $dir2);
160 while (@d1 && @d2 && (($x = shift(@d1)) eq shift(@d2))) {
167 # Find the relative patch between
168 # two paths given as arguments.
172 local($c) = &CommonParent($a, $b);
173 local(@a) = split(/\/+/, $a);
174 local(@b) = split(/\/+/, $b);
175 local(@c) = split(/\/+/, $c);
177 # if $c == "/something", scalar(@c) >= 2
178 # but if $c == "/", scalar(@c) == 0
180 my $length = scalar(@c) ? scalar(@c) : 1;
181 splice(@a, 0, $length);
182 splice(@b, 0, $length);
184 unshift(@b, (('..') x (@a + 0)));
188 # Basically concatenates the paths given
192 local(@paths, @parts);
196 $result = '/' if ($_[0] =~ /^\//);
198 @parts = split(/\/+/, $x);
199 foreach $y (@parts) {
200 push(@paths, $y) if ($y ne "");
203 $result .= join('/', @paths);
207 local($targetdir, $stow) = @_;
210 local($linktarget, $stowmember, $collection);
212 local($pure, $othercollection) = (1, '');
213 local($subpure, $subother);
217 return (0, '') if (&JoinPaths($Target, $targetdir) eq $Stow);
218 return (0, '') if (-e &JoinPaths($Target, $targetdir, '.stow'));
219 warn sprintf("Unstowing in %s\n", &JoinPaths($Target, $targetdir))
221 if (!opendir(DIR, &JoinPaths($Target, $targetdir))) {
222 warn "Warning: $ProgramName: Cannot read directory \"$dir\" ($!). Stow might leave some links. If you think, it does. Rerun Stow with appropriate rights.\n";
224 @contents = readdir(DIR);
226 foreach $content (@contents) {
227 next if (($content eq '.') || ($content eq '..'));
229 if (-l &JoinPaths($Target, $targetdir, $content)) {
230 ($linktarget = readlink(&JoinPaths($Target,
233 || die sprintf("%s: Cannot read link %s (%s)\n",
235 &JoinPaths($Target, $targetdir, $content),
237 if ($stowmember = &FindStowMember(&JoinPaths($Target,
240 @stowmember = split(/\/+/, $stowmember);
241 $collection = shift(@stowmember);
242 if (grep(($collection eq $_), @Collections)) {
243 &DoUnlink(&JoinPaths($Target, $targetdir, $content));
245 if ($othercollection) {
246 $pure = 0 if ($collection ne $othercollection);
248 $othercollection = $collection;
254 } elsif (-d &JoinPaths($Target, $targetdir, $content)) {
255 ($subpure, $subother) = &Unstow(&JoinPaths($targetdir, $content),
256 &JoinPaths('..', $stow));
258 push(@puresubdirs, "$content/$subother");
262 if ($othercollection) {
264 if ($othercollection ne $subother) {
268 } elsif ($subother) {
269 $othercollection = $subother;
279 # This directory was an initially empty directory therefore
280 # We do not remove it.
282 if ((!$pure || !$targetdir) && @puresubdirs) {
283 &CoalesceTrees($targetdir, $stow, @puresubdirs);
285 ($pure, $othercollection);
289 local($parent, $stow, @trees) = @_;
290 local($tree, $collection, $x);
292 foreach $x (@trees) {
293 ($tree, $collection) = ($x =~ /^(.*)\/(.*)/);
294 &EmptyTree(&JoinPaths($Target, $parent, $tree));
295 &DoRmdir(&JoinPaths($Target, $parent, $tree));
297 &DoLink(&JoinPaths($stow, $collection, $parent, $tree),
298 &JoinPaths($Target, $parent, $tree));
309 || die "$ProgramName: Cannot read directory \"$dir\" ($!)\n";
310 @contents = readdir(DIR);
312 foreach $content (@contents) {
313 next if (($content eq '.') || ($content eq '..'));
314 if (-l &JoinPaths($dir, $content)) {
315 &DoUnlink(&JoinPaths($dir, $content));
316 } elsif (-d &JoinPaths($dir, $content)) {
317 &EmptyTree(&JoinPaths($dir, $content));
318 &DoRmdir(&JoinPaths($dir, $content));
320 &DoUnlink(&JoinPaths($dir, $content));
326 local($dir, $stow) = @_;
330 warn "Stowing contents of $dir\n" if ($Verbose > 1);
331 opendir(DIR, &JoinPaths($Stow, $dir))
332 || die "$ProgramName: Cannot read directory \"$dir\" ($!)\n";
333 @contents = readdir(DIR);
335 foreach $content (@contents) {
336 next if (($content eq '.') || ($content eq '..'));
337 if (-d &JoinPaths($Stow, $dir, $content)) {
338 &StowDir(&JoinPaths($dir, $content), $stow);
340 &StowNondir(&JoinPaths($dir, $content), $stow);
346 local($dir, $stow) = @_;
347 local(@dir) = split(/\/+/, $dir);
348 local($collection) = shift(@dir);
349 local($subdir) = join('/', @dir);
350 local($linktarget, $stowsubdir);
352 warn "Stowing directory $dir\n" if ($Verbose > 1);
353 if (-l &JoinPaths($Target, $subdir)) {
354 ($linktarget = readlink(&JoinPaths($Target, $subdir)))
355 || die sprintf("%s: Could not read link %s (%s)\n",
357 &JoinPaths($Target, $subdir),
360 &FindStowMember(sprintf('%s/%s', $Target,
361 join('/', @dir[0..($#dir - 1)])),
363 || (&Conflict($dir, $subdir), return);
364 if (-e &JoinPaths($Stow, $stowsubdir)) {
365 if ($stowsubdir eq $dir) {
366 warn sprintf("%s already points to %s\n",
367 &JoinPaths($Target, $subdir),
368 &JoinPaths($Stow, $dir))
372 if (-d &JoinPaths($Stow, $stowsubdir)) {
373 &DoUnlink(&JoinPaths($Target, $subdir));
374 &DoMkdir(&JoinPaths($Target, $subdir));
375 &StowContents($stowsubdir, &JoinPaths('..', $stow));
376 &StowContents($dir, &JoinPaths('..', $stow));
378 (&Conflict($dir, $subdir), return);
381 &DoUnlink(&JoinPaths($Target, $subdir));
382 &DoLink(&JoinPaths($stow, $dir),
383 &JoinPaths($Target, $subdir));
385 } elsif (-e &JoinPaths($Target, $subdir)) {
386 if (-d &JoinPaths($Target, $subdir)) {
387 &StowContents($dir, &JoinPaths('..', $stow));
389 &Conflict($dir, $subdir);
392 &DoLink(&JoinPaths($stow, $dir),
393 &JoinPaths($Target, $subdir));
398 local($file, $stow) = @_;
399 local(@file) = split(/\/+/, $file);
400 local($collection) = shift(@file);
401 local($subfile) = join('/', @file);
402 local($linktarget, $stowsubfile);
404 if (-l &JoinPaths($Target, $subfile)) {
405 ($linktarget = readlink(&JoinPaths($Target, $subfile)))
406 || die sprintf("%s: Could not read link %s (%s)\n",
408 &JoinPaths($Target, $subfile),
411 &FindStowMember(sprintf('%s/%s', $Target,
412 join('/', @file[0..($#file - 1)])),
414 || (&Conflict($file, $subfile), return);
415 if (-e &JoinPaths($Stow, $stowsubfile)) {
416 (&Conflict($file, $subfile), return)
417 unless ($stowsubfile eq $file);
418 warn sprintf("%s already points to %s\n",
419 &JoinPaths($Target, $subfile),
420 &JoinPaths($Stow, $file))
423 &DoUnlink(&JoinPaths($Target, $subfile));
424 &DoLink(&JoinPaths($stow, $file),
425 &JoinPaths($Target, $subfile));
427 } elsif (-e &JoinPaths($Target, $subfile)) {
428 &Conflict($file, $subfile);
430 &DoLink(&JoinPaths($stow, $file),
431 &JoinPaths($Target, $subfile));
438 warn "UNLINK $file\n" if $Verbose;
439 (unlink($file) || die "$ProgramName: Could not unlink $file ($!)\n")
446 warn "RMDIR $dir\n" if $Verbose;
447 (rmdir($dir) || die "$ProgramName: Could not rmdir $dir ($!)\n")
452 local($target, $name) = @_;
454 warn "LINK $name to $target\n" if $Verbose;
455 (symlink($target, $name) ||
456 die "$ProgramName: Could not symlink $name to $target ($!)\n")
463 warn "MKDIR $dir\n" if $Verbose;
465 || die "$ProgramName: Could not make directory $dir ($!)\n")
473 warn sprintf("CONFLICT: %s vs. %s\n", &JoinPaths($Stow, $a),
474 &JoinPaths($Target, $b));
476 die sprintf("%s: CONFLICT: %s vs. %s\n",
478 &JoinPaths($Stow, $a),
479 &JoinPaths($Target, $b));
484 local($start, $path) = @_;
485 local(@x) = split(/\/+/, $start);
486 local(@path) = split(/\/+/, $path);
488 local(@d) = split(/\/+/, $Stow);
500 if (($x = shift(@x)) ne shift(@d)) {
509 local($path) = join('/', @_);
510 local(@elts) = split(/\/+/, $path);
519 print "$ProgramName: $msg\n";
521 print "$ProgramName (GNU Stow) version $Version\n\n";
522 print "Usage: $ProgramName [OPTION ...] PACKAGE ...\n";
524 -n, --no Do not actually make changes
525 -c, --conflicts Scan for conflicts, implies -n
526 -d DIR, --dir=DIR Set stow dir to DIR (default is current dir)
527 -t DIR, --target=DIR Set target to DIR (default is parent of stow dir)
528 -v, --verbose[=N] Increase verboseness (levels are 0,1,2,3;
529 -v or --verbose adds 1; --verbose=N sets level)
530 -D, --delete Unstow instead of stow
531 -R, --restow Restow (like stow -D followed by stow)
532 -V, --version Show Stow version number
533 -h, --help Show this help
539 print "$ProgramName (GNU Stow) version $Version\n";