unstow_valid_link: rename $existing_path
[gnu-stow.git] / lib / Stow.pm.in
blobd1fe69d4947e5a08d08f13d06d7d8372cd2b10d7
1 #!/usr/bin/perl
3 # This file is part of GNU Stow.
5 # GNU Stow is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # GNU Stow is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see https://www.gnu.org/licenses/.
18 package Stow;
20 =head1 NAME
22 Stow - manage farms of symbolic links
24 =head1 SYNOPSIS
26 my $stow = new Stow(%$options);
28 $stow->plan_unstow(@pkgs_to_unstow);
29 $stow->plan_stow (@pkgs_to_stow);
31 my %conflicts = $stow->get_conflicts;
32 $stow->process_tasks() unless %conflicts;
34 =head1 DESCRIPTION
36 This is the backend Perl module for GNU Stow, a program for managing
37 the installation of software packages, keeping them separate
38 (C</usr/local/stow/emacs> vs. C</usr/local/stow/perl>, for example)
39 while making them appear to be installed in the same place
40 (C</usr/local>).
42 Stow doesn't store an extra state between runs, so there's no danger
43 of mangling directories when file hierarchies don't match the
44 database. Also, stow will never delete any files, directories, or
45 links that appear in a stow directory, so it is always possible to
46 rebuild the target tree.
48 =cut
50 use strict;
51 use warnings;
53 use Carp qw(carp cluck croak confess longmess);
54 use File::Copy qw(move);
55 use File::Spec;
56 use POSIX qw(getcwd);
58 use Stow::Util qw(set_debug_level debug error set_test_mode
59 join_paths restore_cwd canon_path parent adjust_dotfile);
61 our $ProgramName = 'stow';
62 our $VERSION = '@VERSION@';
64 our $LOCAL_IGNORE_FILE = '.stow-local-ignore';
65 our $GLOBAL_IGNORE_FILE = '.stow-global-ignore';
67 our @default_global_ignore_regexps =
68 __PACKAGE__->get_default_global_ignore_regexps();
70 # These are the default options for each Stow instance.
71 our %DEFAULT_OPTIONS = (
72 conflicts => 0,
73 simulate => 0,
74 verbose => 0,
75 paranoid => 0,
76 compat => 0,
77 test_mode => 0,
78 dotfiles => 0,
79 adopt => 0,
80 'no-folding' => 0,
81 ignore => [],
82 override => [],
83 defer => [],
86 =head1 CONSTRUCTORS
88 =head2 new(%options)
90 =head3 Required options
92 =over 4
94 =item * dir - the stow directory
96 =item * target - the target directory
98 =back
100 =head3 Non-mandatory options
102 See the documentation for the F<stow> CLI front-end for information on these.
104 =over 4
106 =item * conflicts
108 =item * simulate
110 =item * verbose
112 =item * paranoid
114 =item * compat
116 =item * test_mode
118 =item * adopt
120 =item * no-folding
122 =item * ignore
124 =item * override
126 =item * defer
128 =back
130 N.B. This sets the current working directory to the target directory.
132 =cut
134 sub new {
135 my $self = shift;
136 my $class = ref($self) || $self;
137 my %opts = @_;
139 my $new = bless { }, $class;
141 $new->{action_count} = 0;
143 for my $required_arg (qw(dir target)) {
144 croak "$class->new() called without '$required_arg' parameter\n"
145 unless exists $opts{$required_arg};
146 $new->{$required_arg} = delete $opts{$required_arg};
149 for my $opt (keys %DEFAULT_OPTIONS) {
150 $new->{$opt} = exists $opts{$opt} ? delete $opts{$opt}
151 : $DEFAULT_OPTIONS{$opt};
154 if (%opts) {
155 croak "$class->new() called with unrecognised parameter(s): ",
156 join(", ", keys %opts), "\n";
159 set_debug_level($new->get_verbosity());
160 set_test_mode($new->{test_mode});
161 $new->set_stow_dir();
162 $new->init_state();
164 return $new;
167 sub get_verbosity {
168 my $self = shift;
170 return $self->{verbose} unless $self->{test_mode};
172 return 0 unless exists $ENV{TEST_VERBOSE};
173 return 0 unless length $ENV{TEST_VERBOSE};
175 # Convert TEST_VERBOSE=y into numeric value
176 $ENV{TEST_VERBOSE} = 3 if $ENV{TEST_VERBOSE} !~ /^\d+$/;
178 return $ENV{TEST_VERBOSE};
181 =head2 set_stow_dir([$dir])
183 Sets a new stow directory. This allows the use of multiple stow
184 directories within one Stow instance, e.g.
186 $stow->plan_stow('foo');
187 $stow->set_stow_dir('/different/stow/dir');
188 $stow->plan_stow('bar');
189 $stow->process_tasks;
191 If C<$dir> is omitted, uses the value of the C<dir> parameter passed
192 to the L<new()> constructor.
194 =cut
196 sub set_stow_dir {
197 my $self = shift;
198 my ($dir) = @_;
199 if (defined $dir) {
200 $self->{dir} = $dir;
203 my $stow_dir = canon_path($self->{dir});
204 my $target = canon_path($self->{target});
206 # Calculate relative path from target directory to stow directory.
207 # This will be commonly used as a prefix for constructing and
208 # recognising symlinks "installed" in the target directory which
209 # point to package files under the stow directory.
210 $self->{stow_path} = File::Spec->abs2rel($stow_dir, $target);
212 debug(2, 0, "stow dir is $stow_dir");
213 debug(2, 0, "stow dir path relative to target $target is $self->{stow_path}");
216 sub init_state {
217 my $self = shift;
219 # Store conflicts during pre-processing
220 $self->{conflicts} = {};
221 $self->{conflict_count} = 0;
223 # Store command line packages to stow (-S and -R)
224 $self->{pkgs_to_stow} = [];
226 # Store command line packages to unstow (-D and -R)
227 $self->{pkgs_to_delete} = [];
229 # The following structures are used by the abstractions that allow us to
230 # defer operating on the filesystem until after all potential conflicts have
231 # been assessed.
233 # $self->{tasks}: list of operations to be performed (in order)
234 # each element is a hash ref of the form
236 # action => ... ('create' or 'remove' or 'move')
237 # type => ... ('link' or 'dir' or 'file')
238 # path => ... (unique)
239 # source => ... (only for links)
240 # dest => ... (only for moving files)
242 $self->{tasks} = [];
244 # $self->{dir_task_for}: map a path to the corresponding directory task reference
245 # This structure allows us to quickly determine if a path has an existing
246 # directory task associated with it.
247 $self->{dir_task_for} = {};
249 # $self->{link_task_for}: map a path to the corresponding directory task reference
250 # This structure allows us to quickly determine if a path has an existing
251 # directory task associated with it.
252 $self->{link_task_for} = {};
254 # N.B.: directory tasks and link tasks are NOT mutually exclusive due
255 # to tree splitting (which involves a remove link task followed by
256 # a create directory task).
259 =head1 METHODS
261 =head2 plan_unstow(@packages)
263 Plan which symlink/directory creation/removal tasks need to be executed
264 in order to unstow the given packages. Any potential conflicts are then
265 accessible via L<get_conflicts()>.
267 =cut
269 sub plan_unstow {
270 my $self = shift;
271 my @packages = @_;
273 return unless @packages;
275 debug(2, 0, "Planning unstow of: @packages ...");
277 $self->within_target_do(sub {
278 for my $package (@packages) {
279 my $pkg_path = join_paths($self->{stow_path}, $package);
280 if (not -d $pkg_path) {
281 error("The stow directory $self->{stow_path} does not contain package $package");
283 debug(2, 0, "Planning unstow of package $package...");
284 $self->unstow_contents(
285 $package,
286 '.',
287 $pkg_path,
289 debug(2, 0, "Planning unstow of package $package... done");
290 $self->{action_count}++;
295 =head2 plan_stow(@packages)
297 Plan which symlink/directory creation/removal tasks need to be executed
298 in order to stow the given packages. Any potential conflicts are then
299 accessible via L<get_conflicts()>.
301 =cut
303 sub plan_stow {
304 my $self = shift;
305 my @packages = @_;
307 return unless @packages;
309 debug(2, 0, "Planning stow of: @packages ...");
311 $self->within_target_do(sub {
312 for my $package (@packages) {
313 my $pkg_path = join_paths($self->{stow_path}, $package);
314 if (not -d $pkg_path) {
315 error("The stow directory $self->{stow_path} does not contain package $package");
317 debug(2, 0, "Planning stow of package $package...");
318 $self->stow_contents(
319 $self->{stow_path},
320 $package,
321 '.',
322 $pkg_path, # source from target
325 debug(2, 0, "Planning stow of package $package... done");
326 $self->{action_count}++;
331 =head2 within_target_do($code)
333 Execute code within target directory, preserving cwd.
335 =over 4
337 =item $code
339 Anonymous subroutine to execute within target dir.
341 =back
343 This is done to ensure that the consumer of the Stow interface doesn't
344 have to worry about (a) what their cwd is, and (b) that their cwd
345 might change.
347 =cut
349 sub within_target_do {
350 my $self = shift;
351 my ($code) = @_;
353 my $cwd = getcwd();
354 chdir($self->{target})
355 or error("Cannot chdir to target tree: $self->{target} ($!)");
356 debug(3, 0, "cwd now $self->{target}");
358 $self->$code();
360 restore_cwd($cwd);
361 debug(3, 0, "cwd restored to $cwd");
364 =head2 stow_contents($stow_path, $package, $target_subdir, $source)
366 Stow the contents of the given directory.
368 =over 4
370 =item $stow_path
372 Relative path from current (i.e. target) directory to the stow dir
373 containing the package to be stowed. This can differ from
374 C<$self->{stow_path}> when unfolding a (sub)tree which is already
375 stowed from a package in a different stow directory (see the "Multiple
376 Stow Directories" section of the manual).
378 =item $package
380 The package whose contents are being stowed.
382 =item $target_subdir
384 Subpath relative to package directory which needs stowing as a symlink
385 at subpath relative to target directory.
387 =item $source
389 Relative path from the (sub)dir of target to symlink source.
391 =back
393 C<stow_node()> and C<stow_contents()> are mutually recursive.
394 C<$source> and C<$target_subdir> are used for creating the symlink.
396 =cut
398 sub stow_contents {
399 my $self = shift;
400 my ($stow_path, $package, $target_subdir, $source, $level) = @_;
402 # Calculate the path to the package directory or sub-directory
403 # whose contents need to be stowed, relative to the current
404 # (target directory). This is needed so that we can check it's a
405 # valid directory, and can read its contents to iterate over them.
407 # Note that $source refers to the same package (sub-)directory,
408 # but instead it's relative to the target directory or
409 # sub-directory where the symlink will be installed when the plans
410 # are executed.
412 # Remove leading $level times .. from $source
413 my $n = 0;
414 my $path = join '/', map { (++$n <= $level) ? ( ) : $_ } (split m{/+}, $source);
416 return if $self->should_skip_target($target_subdir);
418 my $cwd = getcwd();
419 my $msg = "Stowing contents of $path (cwd=$cwd)";
420 $msg =~ s!$ENV{HOME}(/|$)!~$1!g;
421 debug(3, 0, $msg);
422 debug(4, 1, "=> $source");
424 error("stow_contents() called with non-directory package path: $path")
425 unless -d $path;
426 error("stow_contents() called with non-directory target: $target_subdir")
427 unless $self->is_a_node($target_subdir);
429 opendir my $DIR, $path
430 or error("cannot read directory: $path ($!)");
431 my @listing = readdir $DIR;
432 closedir $DIR;
434 NODE:
435 for my $node (@listing) {
436 next NODE if $node eq '.';
437 next NODE if $node eq '..';
438 my $node_target = join_paths($target_subdir, $node);
439 next NODE if $self->ignore($stow_path, $package, $node_target);
441 if ($self->{dotfiles}) {
442 my $adj_node_target = adjust_dotfile($node_target);
443 debug(4, 1, "Adjusting: $node_target => $adj_node_target");
444 $node_target = $adj_node_target;
447 $self->stow_node(
448 $stow_path,
449 $package,
450 $node_target, # target, potentially adjusted for dot- prefix
451 join_paths($source, $node), # source
452 $level
457 =head2 stow_node($stow_path, $package, $target_subpath, $source)
459 Stow the given node
461 =over 4
463 =item $stow_path
465 Relative path from current (i.e. target) directory to the stow dir
466 containing the node to be stowed. This can differ from
467 C<$self->{stow_path}> when unfolding a (sub)tree which is already
468 stowed from a package in a different stow directory (see the "Multiple
469 Stow Directories" section of the manual).
471 =item $package
473 The package containing the node being stowed
475 =item $target_subpath
477 Subpath relative to package directory of node which needs stowing as a
478 symlink at subpath relative to target directory.
480 =item $source
482 Relative path to symlink source from the dir of target.
484 =back
486 C<stow_node()> and C<stow_contents()> are mutually recursive.
488 =cut
490 sub stow_node {
491 my $self = shift;
492 my ($stow_path, $package, $target_subpath, $source, $level) = @_;
494 my $path = join_paths($stow_path, $package, $target_subpath);
496 debug(3, 0, "Stowing entry $stow_path / $package / $target_subpath");
497 debug(4, 1, "=> $source");
499 # Don't try to stow absolute symlinks (they can't be unstowed)
500 if (-l $source) {
501 my $link_dest = $self->read_a_link($source);
502 if ($link_dest =~ m{\A/}) {
503 $self->conflict(
504 'stow',
505 $package,
506 "source is an absolute symlink $source => $link_dest"
508 debug(3, 0, "Absolute symlinks cannot be unstowed");
509 return;
513 # Does the target already exist?
514 if ($self->is_a_link($target_subpath)) {
515 # Where is the link pointing?
516 my $existing_link_dest = $self->read_a_link($target_subpath);
517 if (not $existing_link_dest) {
518 error("Could not read link: $target_subpath");
520 debug(4, 1, "Evaluate existing link: $target_subpath => $existing_link_dest");
522 # Does it point to a node under any stow directory?
523 my ($existing_pkg_path_from_cwd, $existing_stow_path, $existing_package) =
524 $self->find_stowed_path($target_subpath, $existing_link_dest);
525 if (not $existing_pkg_path_from_cwd) {
526 $self->conflict(
527 'stow',
528 $package,
529 "existing target is not owned by stow: $target_subpath"
531 return;
534 # Does the existing $target_subpath actually point to anything?
535 if ($self->is_a_node($existing_pkg_path_from_cwd)) {
536 if ($existing_link_dest eq $source) {
537 debug(2, 0, "--- Skipping $target_subpath as it already points to $source");
539 elsif ($self->defer($target_subpath)) {
540 debug(2, 0, "--- Deferring installation of: $target_subpath");
542 elsif ($self->override($target_subpath)) {
543 debug(2, 0, "--- Overriding installation of: $target_subpath");
544 $self->do_unlink($target_subpath);
545 $self->do_link($source, $target_subpath);
547 elsif ($self->is_a_dir(join_paths(parent($target_subpath), $existing_link_dest)) &&
548 $self->is_a_dir(join_paths(parent($target_subpath), $source)))
551 # If the existing link points to a directory,
552 # and the proposed new link points to a directory,
553 # then we can unfold (split open) the tree at that point
555 debug(2, 0, "--- Unfolding $target_subpath which was already owned by $existing_package");
556 $self->do_unlink($target_subpath);
557 $self->do_mkdir($target_subpath);
558 $self->stow_contents(
559 $existing_stow_path,
560 $existing_package,
561 $target_subpath,
562 join_paths('..', $existing_link_dest),
563 $level + 1,
565 $self->stow_contents(
566 $self->{stow_path},
567 $package,
568 $target_subpath,
569 join_paths('..', $source),
570 $level + 1,
573 else {
574 $self->conflict(
575 'stow',
576 $package,
577 "existing target is stowed to a different package: "
578 . "$target_subpath => $existing_link_dest"
582 else {
583 # The existing link is invalid, so replace it with a good link
584 debug(2, 0, "--- replacing invalid link: $path");
585 $self->do_unlink($target_subpath);
586 $self->do_link($source, $target_subpath);
589 elsif ($self->is_a_node($target_subpath)) {
590 debug(4, 1, "Evaluate existing node: $target_subpath");
591 if ($self->is_a_dir($target_subpath)) {
592 $self->stow_contents(
593 $self->{stow_path},
594 $package,
595 $target_subpath,
596 join_paths('..', $source),
597 $level + 1,
600 else {
601 if ($self->{adopt}) {
602 $self->do_mv($target_subpath, $path);
603 $self->do_link($source, $target_subpath);
605 else {
606 $self->conflict(
607 'stow',
608 $package,
609 "existing target is neither a link nor a directory: $target_subpath"
614 elsif ($self->{'no-folding'} && -d $path && ! -l $path) {
615 $self->do_mkdir($target_subpath);
616 $self->stow_contents(
617 $self->{stow_path},
618 $package,
619 $target_subpath,
620 join_paths('..', $source),
621 $level + 1,
624 else {
625 $self->do_link($source, $target_subpath);
627 return;
630 =head2 should_skip_target($target_subdir)
632 Determine whether C<$target_subdir> is a stow directory which should
633 not be stowed to or unstowed from. This mechanism protects stow
634 directories from being altered by stow, and is a necessary safety
635 check because the stow directory could live beneath the target
636 directory.
638 =over 4
640 =item $target_subdir => relative path to symlink target from the current directory
642 =back
644 Returns true iff target is a stow directory
646 cwd must be the top-level target directory, otherwise
647 C<marked_stow_dir()> won't work.
649 =cut
651 sub should_skip_target {
652 my $self = shift;
653 my ($target) = @_;
655 # Don't try to remove anything under a stow directory
656 if ($target eq $self->{stow_path}) {
657 warn "WARNING: skipping target which was current stow directory $target\n";
658 return 1;
661 if ($self->marked_stow_dir($target)) {
662 warn "WARNING: skipping marked Stow directory $target\n";
663 return 1;
666 if (-e join_paths($target, ".nonstow")) {
667 warn "WARNING: skipping protected directory $target\n";
668 return 1;
671 debug(4, 1, "$target not protected; shouldn't skip");
672 return 0;
675 # cwd must be the top-level target directory, otherwise
676 # marked_stow_dir() won't work.
677 sub marked_stow_dir {
678 my $self = shift;
679 my ($dir) = @_;
680 if (-e join_paths($dir, ".stow")) {
681 debug(5, 5, "> $dir contained .stow");
682 return 1;
684 return 0;
687 =head2 unstow_contents($package, $target)
689 Unstow the contents of the given directory
691 =over 4
693 =item $package
695 The package whose contents are being unstowed.
697 =item $target
699 Relative path to symlink target from the current directory.
701 =back
703 C<unstow_node()> and C<unstow_contents()> are mutually recursive.
704 Here we traverse the package tree, rather than the target tree.
706 =cut
708 sub unstow_contents {
709 my $self = shift;
710 my ($package, $target_subdir, $path) = @_;
712 return if $self->should_skip_target($target_subdir);
714 my $cwd = getcwd();
715 my $msg = "Unstowing from $target_subdir (cwd=$cwd, stow dir=$self->{stow_path})";
716 $msg =~ s!$ENV{HOME}/!~/!g;
717 debug(3, 0, $msg);
718 debug(4, 1, "source path is $path");
720 if ($self->{compat}) {
721 # In compat mode we traverse the target tree not the source tree,
722 # so we're unstowing the contents of /target/foo, there's no
723 # guarantee that the corresponding /stow/mypkg/foo exists.
724 error("unstow_contents() in compat mode called with non-directory target: $target_subdir")
725 unless -d $target_subdir;
727 else {
728 # We traverse the source tree not the target tree, so $path must exist.
729 error("unstow_contents() called with non-directory path: $path")
730 unless -d $path;
732 # When called at the top level, $target_subdir should exist. And
733 # unstow_node() should only call this via mutual recursion if
734 # $target_subdir exists.
735 error("unstow_contents() called with invalid target: $target_subdir")
736 unless $self->is_a_node($target_subdir);
739 my $dir = $self->{compat} ? $target_subdir : $path;
740 opendir my $DIR, $dir
741 or error("cannot read directory: $dir ($!)");
742 my @listing = readdir $DIR;
743 closedir $DIR;
745 NODE:
746 for my $node (@listing) {
747 next NODE if $node eq '.';
748 next NODE if $node eq '..';
749 my $node_target = join_paths($target_subdir, $node);
750 next NODE if $self->ignore($self->{stow_path}, $package, $node_target);
752 if ($self->{dotfiles}) {
753 my $adj_node_target = adjust_dotfile($node_target);
754 debug(4, 1, "Adjusting: $node_target => $adj_node_target");
755 $node_target = $adj_node_target;
758 $self->unstow_node($package, $node_target, join_paths($path, $node));
761 if (! $self->{compat} && -d $target_subdir) {
762 $self->cleanup_invalid_links($target_subdir);
766 =head2 unstow_node($package, $target_subpath)
768 Unstow the given node.
770 =over 4
772 =item $package
774 The package containing the node being unstowed.
776 =item $target_subpath
778 Relative path to symlink target from the current directory.
780 =back
782 C<unstow_node()> and C<unstow_contents()> are mutually recursive.
784 =cut
786 sub unstow_node {
787 my $self = shift;
788 my ($package, $target_subpath, $source) = @_;
790 my $pkg_path_from_cwd = join_paths($self->{stow_path}, $package, $target_subpath);
792 debug(3, 1, "Unstowing $pkg_path_from_cwd");
793 debug(4, 2, "target is $target_subpath");
795 # Does the target exist?
796 if ($self->is_a_link($target_subpath)) {
797 $self->unstow_link_node($package, $target_subpath, $pkg_path_from_cwd);
799 elsif ($self->{compat} && -d $target_subpath) {
800 $self->unstow_contents($package, $target_subpath, $pkg_path_from_cwd);
802 # This action may have made the parent directory foldable
803 if (my $parent = $self->foldable($target_subpath)) {
804 $self->fold_tree($target_subpath, $parent);
807 elsif (-e $target_subpath) {
808 if ($self->{compat}) {
809 $self->conflict(
810 'unstow',
811 $package,
812 "existing target is neither a link nor a directory: $target_subpath",
815 else {
816 $self->unstow_existing_node($package, $target_subpath, $source);
819 else {
820 debug(2, 1, "$target_subpath did not exist to be unstowed");
824 sub unstow_link_node {
825 my $self = shift;
826 my ($package, $target_subpath, $pkg_path_from_cwd) = @_;
827 debug(4, 2, "Evaluate existing link: $target_subpath");
829 # Where is the link pointing?
830 my $link_dest = $self->read_a_link($target_subpath);
831 if (not $link_dest) {
832 error("Could not read link: $target_subpath");
835 if ($link_dest =~ m{\A/}) {
836 warn "Ignoring an absolute symlink: $target_subpath => $link_dest\n";
837 return;
840 # Does it point to a node under any stow directory?
841 my ($existing_pkg_path_from_cwd, $existing_stow_path, $existing_package) =
842 $self->find_stowed_path($target_subpath, $link_dest);
843 if (not $existing_pkg_path_from_cwd) {
844 if ($self->{compat}) {
845 # We're traversing the target tree not the package tree,
846 # so we definitely expect to find stuff not owned by stow.
847 # Therefore we can't flag a conflict.
848 return;
850 else {
851 $self->conflict(
852 'unstow',
853 $package,
854 "existing target is not owned by stow: $target_subpath => $link_dest"
857 return;
860 # Does the existing $target_subpath actually point to anything?
861 if (-e $existing_pkg_path_from_cwd) {
862 $self->unstow_valid_link($pkg_path_from_cwd, $target_subpath, $existing_pkg_path_from_cwd);
864 else {
865 debug(2, 0, "--- removing invalid link into a stow directory: $pkg_path_from_cwd");
866 $self->do_unlink($target_subpath);
870 sub unstow_valid_link {
871 my $self = shift;
872 my ($pkg_path_from_cwd, $target_subpath, $existing_pkg_path_from_cwd) = @_;
873 # Does link points to the right place?
875 # Adjust for dotfile if necessary.
876 if ($self->{dotfiles}) {
877 $existing_pkg_path_from_cwd = adjust_dotfile($existing_pkg_path_from_cwd);
880 if ($existing_pkg_path_from_cwd eq $pkg_path_from_cwd) {
881 $self->do_unlink($target_subpath);
884 # FIXME: we quietly ignore links that are stowed to a different
885 # package.
887 #elsif (defer($target_subpath)) {
888 # debug(2, 0, "--- deferring to installation of: $target_subpath");
890 #elsif ($self->override($target_subpath)) {
891 # debug(2, 0, "--- overriding installation of: $target_subpath");
892 # $self->do_unlink($target_subpath);
894 #else {
895 # $self->conflict(
896 # 'unstow',
897 # $package,
898 # "existing target is stowed to a different package: "
899 # . "$target_subpath => $existing_source"
900 # );
904 sub unstow_existing_node {
905 my $self = shift;
906 my ($package, $target_subpath, $source) = @_;
907 debug(4, 2, "Evaluate existing node: $target_subpath");
908 if (-d $target_subpath) {
909 $self->unstow_contents($package, $target_subpath, $source);
911 # This action may have made the parent directory foldable
912 if (my $parent = $self->foldable($target_subpath)) {
913 $self->fold_tree($target_subpath, $parent);
916 else {
917 $self->conflict(
918 'unstow',
919 $package,
920 "existing target is neither a link nor a directory: $target_subpath",
925 =head2 link_owned_by_package($target_subpath, $link_dest)
927 Determine whether the given link points to a member of a stowed
928 package.
930 =over 4
932 =item $target_subpath
934 Path to a symbolic link under current directory.
936 =item $link_dest
938 Where that link points to.
940 =back
942 Lossy wrapper around find_stowed_path().
944 Returns the package iff link is owned by stow, otherwise ''.
946 =cut
948 sub link_owned_by_package {
949 my $self = shift;
950 my ($target_subpath, $link_dest) = @_;
952 my ($pkg_path_from_cwd, $stow_path, $package) =
953 $self->find_stowed_path($target_subpath, $link_dest);
954 return $package;
957 =head2 find_stowed_path($target_subpath, $link_dest)
959 Determine whether the given symlink within the target directory is a
960 stowed path pointing to a member of a package under the stow dir, and
961 if so, obtain a breakdown of information about this stowed path.
963 =over 4
965 =item $target_subpath
967 Path to a symbolic link somewhere under the target directory, relative
968 to the top-level target directory (which is also expected to be the
969 current directory).
971 =item $link_dest
973 Where that link points to (needed because link might not exist yet due
974 to two-phase approach, so we can't just call C<readlink()>). If this
975 is owned by Stow, it will be expressed relative to (the directory
976 containing) C<$target_subpath>. However if it's not, it could of course be
977 relative or absolute, point absolutely anywhere, and could even be
978 dangling.
980 =back
982 Returns C<($pkg_path_from_cwd, $stow_path, $package)> where
983 C<$pkg_path_from_cwd> and C<$stow_path> are relative from the
984 top-level target directory. C<$pkg_path_from_cwd> is the full
985 relative path to the member of the package pointed to by
986 C<$link_dest>; C<$stow_path> is the relative path to the stow
987 directory; and C<$package> is the name of the package; or C<('', '',
988 '')> if link is not owned by stow.
990 cwd must be the top-level target directory, otherwise
991 C<find_containing_marked_stow_dir()> won't work. Allow for stow dir
992 not being under target dir.
994 =cut
996 sub find_stowed_path {
997 my $self = shift;
998 my ($target_subpath, $link_dest) = @_;
1000 if (substr($link_dest, 0, 1) eq '/') {
1001 # Symlink points to an absolute path, therefore it cannot be
1002 # owned by Stow.
1003 return ('', '', '');
1006 # Evaluate softlink relative to its target, without relying on
1007 # what's actually on the filesystem, since the link might not
1008 # exist yet.
1009 debug(4, 2, "find_stowed_path(target=$target_subpath; source=$link_dest)");
1010 my $pkg_path_from_cwd = join_paths(parent($target_subpath), $link_dest);
1011 debug(4, 3, "is symlink destination $pkg_path_from_cwd owned by stow?");
1013 # First check whether the link is owned by the current stow
1014 # directory, in which case $pkg_path_from_cwd will be a prefix of
1015 # $self->{stow_path}.
1016 my ($package, $pkg_subpath) = $self->link_dest_within_stow_dir($pkg_path_from_cwd);
1017 if (length $package) {
1018 debug(4, 3, "yes - package $package in $self->{stow_path} may contain $pkg_subpath");
1019 return ($pkg_path_from_cwd, $self->{stow_path}, $package);
1022 # If no .stow file was found, we need to find out whether it's
1023 # owned by the current stow directory, in which case
1024 # $pkg_path_from_cwd will be a prefix of $self->{stow_path}.
1025 my ($stow_path, $ext_package) = $self->find_containing_marked_stow_dir($pkg_path_from_cwd);
1026 if (length $stow_path) {
1027 debug(5, 5, "yes - $stow_path in $pkg_path_from_cwd was marked as a stow dir; package=$ext_package");
1028 return ($pkg_path_from_cwd, $stow_path, $ext_package);
1031 return ('', '', '');
1034 =head2 link_dest_within_stow_dir($link_dest)
1036 Detect whether symlink destination is within current stow dir
1038 =over 4
1040 =item $link_dest - destination of the symlink relative
1042 =back
1044 Returns C<($package, $pkg_subpath)> - package within the current stow
1045 dir and subpath within that package which the symlink points to.
1047 =cut
1049 sub link_dest_within_stow_dir {
1050 my $self = shift;
1051 my ($link_dest) = @_;
1053 debug(4, 4, "common prefix? link_dest=$link_dest; stow_path=$self->{stow_path}");
1055 my $removed = $link_dest =~ s,^\Q$self->{stow_path}/,,;
1056 if (! $removed) {
1057 debug(4, 3, "no - $link_dest not under $self->{stow_path}");
1058 return ('', '');
1061 debug(4, 4, "remaining after removing $self->{stow_path}: $link_dest");
1062 my @dirs = File::Spec->splitdir($link_dest);
1063 my $package = shift @dirs;
1064 my $pkg_subpath = File::Spec->catdir(@dirs);
1065 return ($package, $pkg_subpath);
1068 =head2 find_containing_marked_stow_dir($pkg_path_from_cwd)
1070 Detect whether path is within a marked stow directory
1072 =over 4
1074 =item $pkg_path_from_cwd => path to directory to check
1076 =back
1078 Returns C<($stow_path, $package)> where C<$stow_path> is the highest
1079 directory (relative from the top-level target directory) which is
1080 marked as a Stow directory, and C<$package> is the containing package;
1081 or C<('', '')> if no containing directory is marked as a stow
1082 directory.
1084 cwd must be the top-level target directory, otherwise
1085 C<marked_stow_dir()> won't work.
1087 =cut
1089 sub find_containing_marked_stow_dir {
1090 my $self = shift;
1091 my ($pkg_path_from_cwd) = @_;
1093 # Search for .stow files - this allows us to detect links
1094 # owned by stow directories other than the current one.
1095 my @segments = File::Spec->splitdir($pkg_path_from_cwd);
1096 for my $last_segment (0 .. $#segments) {
1097 my $pkg_path_from_cwd = join_paths(@segments[0 .. $last_segment]);
1098 debug(5, 5, "is $pkg_path_from_cwd marked stow dir?");
1099 if ($self->marked_stow_dir($pkg_path_from_cwd)) {
1100 if ($last_segment == $#segments) {
1101 # This should probably never happen. Even if it did,
1102 # there would be no way of calculating $package.
1103 internal_error("find_stowed_path() called directly on stow dir");
1106 my $package = $segments[$last_segment + 1];
1107 return ($pkg_path_from_cwd, $package);
1110 return ('', '');
1113 =head2 cleanup_invalid_links($dir)
1115 Clean up orphaned links that may block folding
1117 =over 4
1119 =item $dir
1121 Path to directory to check
1123 =back
1125 This is invoked by C<unstow_contents()>. We only clean up links which
1126 are both orphaned and owned by Stow, i.e. they point to a non-existent
1127 location within a Stow package. These can block tree folding, and
1128 they can easily occur when a file in Stow package is renamed or
1129 removed, so the benefit should outweigh the low risk of actually
1130 someone wanting to keep an orphaned link to within a Stow package.
1132 =cut
1134 sub cleanup_invalid_links {
1135 my $self = shift;
1136 my ($dir) = @_;
1138 my $cwd = getcwd();
1139 debug(2, 0, "Cleaning up any invalid links in $dir (pwd=$cwd)");
1141 if (not -d $dir) {
1142 internal_error("cleanup_invalid_links() called with a non-directory: $dir");
1145 opendir my $DIR, $dir
1146 or error("cannot read directory: $dir ($!)");
1147 my @listing = readdir $DIR;
1148 closedir $DIR;
1150 NODE:
1151 for my $node (@listing) {
1152 next NODE if $node eq '.';
1153 next NODE if $node eq '..';
1155 my $node_path = join_paths($dir, $node);
1157 next unless -l $node_path;
1159 debug(4, 1, "Checking validity of link $node_path");
1161 if (exists $self->{link_task_for}{$node_path}) {
1162 my $action = $self->{link_task_for}{$node_path}{action};
1163 if ($action ne 'remove') {
1164 warn "Unexpected action $action scheduled for $node_path; skipping clean-up\n";
1166 else {
1167 debug(4, 2, "$node_path scheduled for removal; skipping clean-up");
1169 next;
1172 # Where is the link pointing?
1173 # (don't use read_a_link() here)
1174 my $link_dest = readlink($node_path);
1175 if (not $link_dest) {
1176 error("Could not read link $node_path");
1179 my $target_subpath = join_paths($dir, $link_dest);
1180 debug(4, 2, "join $dir $link_dest");
1181 if (-e $target_subpath) {
1182 debug(4, 2, "Link target $link_dest exists at $target_subpath; skipping clean up");
1183 next;
1185 else {
1186 debug(4, 2, "Link target $link_dest doesn't exist at $target_subpath");
1189 debug(3, 1,
1190 "Checking whether valid link $node_path -> $link_dest is " .
1191 "owned by stow");
1193 my $owner = $self->link_owned_by_package($node_path, $link_dest);
1194 if ($owner) {
1195 # owned by stow
1196 debug(2, 0, "--- removing link owned by $owner: $node_path => " .
1197 join_paths($dir, $link_dest));
1198 $self->do_unlink($node_path);
1201 return;
1205 =head2 foldable($target_subdir)
1207 Determine whether a tree can be folded
1209 =over 4
1211 =item $target_subdir
1213 Path to the target sub-directory to check for foldability, relative to
1214 the current directory (the top-level target directory).
1216 =back
1218 Returns path to the parent dir iff the tree can be safely folded. The
1219 path returned is relative to the parent of C<$target_subdir>, i.e. it
1220 can be used as the source for a replacement symlink.
1222 =cut
1224 sub foldable {
1225 my $self = shift;
1226 my ($target_subdir) = @_;
1228 debug(3, 2, "Is $target_subdir foldable?");
1229 if ($self->{'no-folding'}) {
1230 debug(3, 3, "Not foldable because --no-folding enabled");
1231 return '';
1234 opendir my $DIR, $target_subdir
1235 or error(qq{Cannot read directory "$target_subdir" ($!)\n});
1236 my @listing = readdir $DIR;
1237 closedir $DIR;
1239 # We want to see if all the symlinks in $target_subdir point to
1240 # files under the same parent subdirectory in the package
1241 # (e.g. ../../stow/pkg1/common_dir/file1). So remember which
1242 # parent subdirectory we've already seen, and if we come across a
1243 # second one which is different,
1244 # (e.g. ../../stow/pkg2/common_dir/file2), then $target_subdir
1245 # common_dir which contains file{1,2} cannot be folded to be
1246 # a symlink to (say) ../../stow/pkg1/common_dir.
1247 my $parent_in_pkg = '';
1249 NODE:
1250 for my $node (@listing) {
1251 next NODE if $node eq '.';
1252 next NODE if $node eq '..';
1254 my $target_node_path = join_paths($target_subdir, $node);
1256 # Skip nodes scheduled for removal
1257 next NODE if not $self->is_a_node($target_node_path);
1259 # If it's not a link then we can't fold its parent
1260 if (not $self->is_a_link($target_node_path)) {
1261 debug(3, 3, "Not foldable because $target_node_path not a link");
1262 return '';
1265 # Where is the link pointing?
1266 my $link_dest = $self->read_a_link($target_node_path);
1267 if (not $link_dest) {
1268 error("Could not read link $target_node_path");
1270 my $new_parent = parent($link_dest);
1271 if ($parent_in_pkg eq '') {
1272 $parent_in_pkg = $new_parent;
1274 elsif ($parent_in_pkg ne $new_parent) {
1275 debug(3, 3, "Not foldable because $target_subdir contains links to entries in both $parent_in_pkg and $new_parent");
1276 return '';
1279 if (not $parent_in_pkg) {
1280 debug(3, 3, "Not foldable because $target_subdir contains no links");
1281 return '';
1284 # If we get here then all nodes inside $target_subdir are links,
1285 # and those links point to nodes inside the same directory.
1287 # chop of leading '..' to get the path to the common parent directory
1288 # relative to the parent of our $target_subdir
1289 $parent_in_pkg =~ s{\A\.\./}{};
1291 # If the resulting path is owned by stow, we can fold it
1292 if ($self->link_owned_by_package($target_subdir, $parent_in_pkg)) {
1293 debug(3, 3, "$target_subdir is foldable");
1294 return $parent_in_pkg;
1296 else {
1297 debug(3, 3, "$target_subdir is not foldable");
1298 return '';
1302 =head2 fold_tree($target, source)
1304 Fold the given tree
1306 =over 4
1308 =item $target
1310 directory that we will replace with a link to $source
1312 =item $source
1314 link to the folded tree source
1316 =back
1318 Only called iff foldable() is true so we can remove some checks.
1320 =cut
1322 sub fold_tree {
1323 my $self = shift;
1324 my ($target, $source) = @_;
1326 debug(3, 0, "--- Folding tree: $target => $source");
1328 opendir my $DIR, $target
1329 or error(qq{Cannot read directory "$target" ($!)\n});
1330 my @listing = readdir $DIR;
1331 closedir $DIR;
1333 NODE:
1334 for my $node (@listing) {
1335 next NODE if $node eq '.';
1336 next NODE if $node eq '..';
1337 next NODE if not $self->is_a_node(join_paths($target, $node));
1338 $self->do_unlink(join_paths($target, $node));
1340 $self->do_rmdir($target);
1341 $self->do_link($source, $target);
1342 return;
1346 =head2 conflict($package, $message)
1348 Handle conflicts in stow operations
1350 =over 4
1352 =item $package
1354 the package involved with the conflicting operation
1356 =item $message
1358 a description of the conflict
1360 =back
1362 =cut
1364 sub conflict {
1365 my $self = shift;
1366 my ($action, $package, $message) = @_;
1368 debug(2, 0, "CONFLICT when ${action}ing $package: $message");
1369 $self->{conflicts}{$action}{$package} ||= [];
1370 push @{ $self->{conflicts}{$action}{$package} }, $message;
1371 $self->{conflict_count}++;
1373 return;
1376 =head2 get_conflicts()
1378 Returns a nested hash of all potential conflicts discovered: the keys
1379 are actions ('stow' or 'unstow'), and the values are hashrefs whose
1380 keys are stow package names and whose values are conflict
1381 descriptions, e.g.:
1384 stow => {
1385 perl => [
1386 "existing target is not owned by stow: bin/a2p"
1387 "existing target is neither a link nor a directory: bin/perl"
1392 =cut
1394 sub get_conflicts {
1395 my $self = shift;
1396 return %{ $self->{conflicts} };
1399 =head2 get_conflict_count()
1401 Returns the number of conflicts found.
1403 =cut
1405 sub get_conflict_count {
1406 my $self = shift;
1407 return $self->{conflict_count};
1410 =head2 get_tasks()
1412 Returns a list of all symlink/directory creation/removal tasks.
1414 =cut
1416 sub get_tasks {
1417 my $self = shift;
1418 return @{ $self->{tasks} };
1421 =head2 get_action_count()
1423 Returns the number of actions planned for this Stow instance.
1425 =cut
1427 sub get_action_count {
1428 my $self = shift;
1429 return $self->{action_count};
1432 =head2 ignore($stow_path, $package, $target)
1434 Determine if the given path matches a regex in our ignore list.
1436 =over 4
1438 =item $stow_path
1440 the stow directory containing the package
1442 =item $package
1444 the package containing the path
1446 =item $target
1448 the path to check against the ignore list relative to its package
1449 directory
1451 =back
1453 Returns true iff the path should be ignored.
1455 =cut
1457 sub ignore {
1458 my $self = shift;
1459 my ($stow_path, $package, $target) = @_;
1461 internal_error(__PACKAGE__ . "::ignore() called with empty target")
1462 unless length $target;
1464 for my $suffix (@{ $self->{ignore} }) {
1465 if ($target =~ m/$suffix/) {
1466 debug(4, 1, "Ignoring path $target due to --ignore=$suffix");
1467 return 1;
1471 my $package_dir = join_paths($stow_path, $package);
1472 my ($path_regexp, $segment_regexp) =
1473 $self->get_ignore_regexps($package_dir);
1474 debug(5, 2, "Ignore list regexp for paths: " .
1475 (defined $path_regexp ? "/$path_regexp/" : "none"));
1476 debug(5, 2, "Ignore list regexp for segments: " .
1477 (defined $segment_regexp ? "/$segment_regexp/" : "none"));
1479 if (defined $path_regexp and "/$target" =~ $path_regexp) {
1480 debug(4, 1, "Ignoring path /$target");
1481 return 1;
1484 (my $basename = $target) =~ s!.+/!!;
1485 if (defined $segment_regexp and $basename =~ $segment_regexp) {
1486 debug(4, 1, "Ignoring path segment $basename");
1487 return 1;
1490 debug(5, 1, "Not ignoring $target");
1491 return 0;
1494 sub get_ignore_regexps {
1495 my $self = shift;
1496 my ($dir) = @_;
1498 # N.B. the local and global stow ignore files have to have different
1499 # names so that:
1500 # 1. the global one can be a symlink to within a stow
1501 # package, managed by stow itself, and
1502 # 2. the local ones can be ignored via hardcoded logic in
1503 # GlobsToRegexp(), so that they always stay within their stow packages.
1505 my $local_stow_ignore = join_paths($dir, $LOCAL_IGNORE_FILE);
1506 my $global_stow_ignore = join_paths($ENV{HOME}, $GLOBAL_IGNORE_FILE);
1508 for my $file ($local_stow_ignore, $global_stow_ignore) {
1509 if (-e $file) {
1510 debug(5, 1, "Using ignore file: $file");
1511 return $self->get_ignore_regexps_from_file($file);
1513 else {
1514 debug(5, 1, "$file didn't exist");
1518 debug(4, 1, "Using built-in ignore list");
1519 return @default_global_ignore_regexps;
1522 my %ignore_file_regexps;
1524 sub get_ignore_regexps_from_file {
1525 my $self = shift;
1526 my ($file) = @_;
1528 if (exists $ignore_file_regexps{$file}) {
1529 debug(4, 2, "Using memoized regexps from $file");
1530 return @{ $ignore_file_regexps{$file} };
1533 if (! open(REGEXPS, $file)) {
1534 debug(4, 2, "Failed to open $file: $!");
1535 return undef;
1538 my @regexps = $self->get_ignore_regexps_from_fh(\*REGEXPS);
1539 close(REGEXPS);
1541 $ignore_file_regexps{$file} = [ @regexps ];
1542 return @regexps;
1545 =head2 invalidate_memoized_regexp($file)
1547 For efficiency of performance, regular expressions are compiled from
1548 each ignore list file the first time it is used by the Stow process,
1549 and then memoized for future use. If you expect the contents of these
1550 files to change during a single run, you will need to invalidate the
1551 memoized value from this cache. This method allows you to do that.
1553 =cut
1555 sub invalidate_memoized_regexp {
1556 my $self = shift;
1557 my ($file) = @_;
1558 if (exists $ignore_file_regexps{$file}) {
1559 debug(4, 2, "Invalidated memoized regexp for $file");
1560 delete $ignore_file_regexps{$file};
1562 else {
1563 debug(2, 1, "WARNING: no memoized regexp for $file to invalidate");
1567 sub get_ignore_regexps_from_fh {
1568 my $self = shift;
1569 my ($fh) = @_;
1570 my %regexps;
1571 while (<$fh>) {
1572 chomp;
1573 s/^\s+//;
1574 s/\s+$//;
1575 next if /^#/ or length($_) == 0;
1576 s/\s+#.+//; # strip comments to right of pattern
1577 s/\\#/#/g;
1578 $regexps{$_}++;
1581 # Local ignore lists should *always* stay within the stow directory,
1582 # because this is the only place stow looks for them.
1583 $regexps{"^/\Q$LOCAL_IGNORE_FILE\E\$"}++;
1585 return $self->compile_ignore_regexps(%regexps);
1588 sub compile_ignore_regexps {
1589 my $self = shift;
1590 my (%regexps) = @_;
1592 my @segment_regexps;
1593 my @path_regexps;
1594 for my $regexp (keys %regexps) {
1595 if (index($regexp, '/') < 0) {
1596 # No / found in regexp, so use it for matching against basename
1597 push @segment_regexps, $regexp;
1599 else {
1600 # / found in regexp, so use it for matching against full path
1601 push @path_regexps, $regexp;
1605 my $segment_regexp = join '|', @segment_regexps;
1606 my $path_regexp = join '|', @path_regexps;
1607 $segment_regexp = @segment_regexps ?
1608 $self->compile_regexp("^($segment_regexp)\$") : undef;
1609 $path_regexp = @path_regexps ?
1610 $self->compile_regexp("(^|/)($path_regexp)(/|\$)") : undef;
1612 return ($path_regexp, $segment_regexp);
1615 sub compile_regexp {
1616 my $self = shift;
1617 my ($regexp) = @_;
1618 my $compiled = eval { qr/$regexp/ };
1619 die "Failed to compile regexp: $@\n" if $@;
1620 return $compiled;
1623 sub get_default_global_ignore_regexps {
1624 my $class = shift;
1625 # Bootstrap issue - first time we stow, we will be stowing
1626 # .cvsignore so it might not exist in ~ yet, or if it does, it could
1627 # be an old version missing the entries we need. So we make sure
1628 # they are there by hardcoding some crucial entries.
1629 return $class->get_ignore_regexps_from_fh(\*DATA);
1632 =head2 defer($path)
1634 Determine if the given path matches a regex in our C<defer> list
1636 =over 4
1638 =item $path
1640 =back
1642 Returns boolean.
1644 =cut
1646 sub defer {
1647 my $self = shift;
1648 my ($path) = @_;
1650 for my $prefix (@{ $self->{defer} }) {
1651 return 1 if $path =~ m/$prefix/;
1653 return 0;
1656 =head2 override($path)
1658 Determine if the given path matches a regex in our C<override> list
1660 =over 4
1662 =item $path
1664 =back
1666 Returns boolean
1668 =cut
1670 sub override {
1671 my $self = shift;
1672 my ($path) = @_;
1674 for my $regex (@{ $self->{override} }) {
1675 return 1 if $path =~ m/$regex/;
1677 return 0;
1680 ##############################################################################
1682 # The following code provides the abstractions that allow us to defer operating
1683 # on the filesystem until after all potential conflcits have been assessed.
1685 ##############################################################################
1687 =head2 process_tasks()
1689 Process each task in the tasks list
1691 =over 4
1693 =item none
1695 =back
1697 Returns : n/a
1698 Throws : fatal error if tasks list is corrupted or a task fails
1700 =cut
1702 sub process_tasks {
1703 my $self = shift;
1705 debug(2, 0, "Processing tasks...");
1707 # Strip out all tasks with a skip action
1708 $self->{tasks} = [ grep { $_->{action} ne 'skip' } @{ $self->{tasks} } ];
1710 if (not @{ $self->{tasks} }) {
1711 return;
1714 $self->within_target_do(sub {
1715 for my $task (@{ $self->{tasks} }) {
1716 $self->process_task($task);
1720 debug(2, 0, "Processing tasks... done");
1723 =head2 process_task($task)
1725 Process a single task.
1727 =over 4
1729 =item $task => the task to process
1731 =back
1733 Returns : n/a
1734 Throws : fatal error if task fails
1736 Must run from within target directory. Task involve either creating
1737 or deleting dirs and symlinks an action is set to 'skip' if it is
1738 found to be redundant
1740 =cut
1742 sub process_task {
1743 my $self = shift;
1744 my ($task) = @_;
1746 if ($task->{action} eq 'create') {
1747 if ($task->{type} eq 'dir') {
1748 mkdir($task->{path}, 0777)
1749 or error("Could not create directory: $task->{path} ($!)");
1750 return;
1752 elsif ($task->{type} eq 'link') {
1753 symlink $task->{source}, $task->{path}
1754 or error(
1755 "Could not create symlink: %s => %s ($!)",
1756 $task->{path},
1757 $task->{source}
1759 return;
1762 elsif ($task->{action} eq 'remove') {
1763 if ($task->{type} eq 'dir') {
1764 rmdir $task->{path}
1765 or error("Could not remove directory: $task->{path} ($!)");
1766 return;
1768 elsif ($task->{type} eq 'link') {
1769 unlink $task->{path}
1770 or error("Could not remove link: $task->{path} ($!)");
1771 return;
1774 elsif ($task->{action} eq 'move') {
1775 if ($task->{type} eq 'file') {
1776 # rename() not good enough, since the stow directory
1777 # might be on a different filesystem to the target.
1778 move $task->{path}, $task->{dest}
1779 or error("Could not move $task->{path} -> $task->{dest} ($!)");
1780 return;
1784 # Should never happen.
1785 internal_error("bad task action: $task->{action}");
1788 =head2 link_task_action($path)
1790 Finds the link task action for the given path, if there is one
1792 =over 4
1794 =item $path
1796 =back
1798 Returns C<'remove'>, C<'create'>, or C<''> if there is no action.
1799 Throws a fatal exception if an invalid action is found.
1801 =cut
1803 sub link_task_action {
1804 my $self = shift;
1805 my ($path) = @_;
1807 if (! exists $self->{link_task_for}{$path}) {
1808 debug(4, 4, "| link_task_action($path): no task");
1809 return '';
1812 my $action = $self->{link_task_for}{$path}->{action};
1813 internal_error("bad task action: $action")
1814 unless $action eq 'remove' or $action eq 'create';
1816 debug(4, 1, "link_task_action($path): link task exists with action $action");
1817 return $action;
1820 =head2 dir_task_action($path)
1822 Finds the dir task action for the given path, if there is one.
1824 =over 4
1826 =item $path
1828 =back
1830 Returns C<'remove'>, C<'create'>, or C<''> if there is no action.
1831 Throws a fatal exception if an invalid action is found.
1833 =cut
1835 sub dir_task_action {
1836 my $self = shift;
1837 my ($path) = @_;
1839 if (! exists $self->{dir_task_for}{$path}) {
1840 debug(4, 4, "| dir_task_action($path): no task");
1841 return '';
1844 my $action = $self->{dir_task_for}{$path}->{action};
1845 internal_error("bad task action: $action")
1846 unless $action eq 'remove' or $action eq 'create';
1848 debug(4, 4, "| dir_task_action($path): dir task exists with action $action");
1849 return $action;
1852 =head2 parent_link_scheduled_for_removal($path)
1854 Determine whether the given path or any parent thereof is a link
1855 scheduled for removal
1857 =over 4
1859 =item $path
1861 =back
1863 Returns boolean
1865 =cut
1867 sub parent_link_scheduled_for_removal {
1868 my $self = shift;
1869 my ($path) = @_;
1871 my $prefix = '';
1872 for my $part (split m{/+}, $path) {
1873 $prefix = join_paths($prefix, $part);
1874 debug(5, 4, "| parent_link_scheduled_for_removal($path): prefix $prefix");
1875 if (exists $self->{link_task_for}{$prefix} and
1876 $self->{link_task_for}{$prefix}->{action} eq 'remove') {
1877 debug(4, 4, "| parent_link_scheduled_for_removal($path): link scheduled for removal");
1878 return 1;
1882 debug(4, 4, "| parent_link_scheduled_for_removal($path): returning false");
1883 return 0;
1886 =head2 is_a_link($path)
1888 Determine if the given path is a current or planned link.
1890 =over 4
1892 =item $path
1894 =back
1896 Returns false if an existing link is scheduled for removal and true if
1897 a non-existent link is scheduled for creation.
1899 =cut
1901 sub is_a_link {
1902 my $self = shift;
1903 my ($path) = @_;
1904 debug(4, 2, "is_a_link($path)");
1906 if (my $action = $self->link_task_action($path)) {
1907 if ($action eq 'remove') {
1908 debug(4, 2, "is_a_link($path): returning 0 (remove action found)");
1909 return 0;
1911 elsif ($action eq 'create') {
1912 debug(4, 2, "is_a_link($path): returning 1 (create action found)");
1913 return 1;
1917 if (-l $path) {
1918 # Check if any of its parent are links scheduled for removal
1919 # (need this for edge case during unfolding)
1920 debug(4, 2, "is_a_link($path): is a real link");
1921 return $self->parent_link_scheduled_for_removal($path) ? 0 : 1;
1924 debug(4, 2, "is_a_link($path): returning 0");
1925 return 0;
1928 =head2 is_a_dir($path)
1930 Determine if the given path is a current or planned directory
1932 =over 4
1934 =item $path
1936 =back
1938 Returns false if an existing directory is scheduled for removal and
1939 true if a non-existent directory is scheduled for creation. We also
1940 need to be sure we are not just following a link.
1942 =cut
1944 sub is_a_dir {
1945 my $self = shift;
1946 my ($path) = @_;
1947 debug(4, 1, "is_a_dir($path)");
1949 if (my $action = $self->dir_task_action($path)) {
1950 if ($action eq 'remove') {
1951 return 0;
1953 elsif ($action eq 'create') {
1954 return 1;
1958 return 0 if $self->parent_link_scheduled_for_removal($path);
1960 if (-d $path) {
1961 debug(4, 1, "is_a_dir($path): real dir");
1962 return 1;
1965 debug(4, 1, "is_a_dir($path): returning false");
1966 return 0;
1969 =head2 is_a_node($path)
1971 Determine whether the given path is a current or planned node.
1973 =over 4
1975 =item $path
1977 =back
1979 Returns false if an existing node is scheduled for removal, or true if
1980 a non-existent node is scheduled for creation. We also need to be
1981 sure we are not just following a link.
1983 =cut
1985 sub is_a_node {
1986 my $self = shift;
1987 my ($path) = @_;
1988 debug(4, 4, "| Checking whether $path is a current/planned node");
1990 my $laction = $self->link_task_action($path);
1991 my $daction = $self->dir_task_action($path);
1993 if ($laction eq 'remove') {
1994 if ($daction eq 'remove') {
1995 internal_error("removing link and dir: $path");
1996 return 0;
1998 elsif ($daction eq 'create') {
1999 # Assume that we're unfolding $path, and that the link
2000 # removal action is earlier than the dir creation action
2001 # in the task queue. FIXME: is this a safe assumption?
2002 return 1;
2004 else { # no dir action
2005 return 0;
2008 elsif ($laction eq 'create') {
2009 if ($daction eq 'remove') {
2010 # Assume that we're folding $path, and that the dir
2011 # removal action is earlier than the link creation action
2012 # in the task queue. FIXME: is this a safe assumption?
2013 return 1;
2015 elsif ($daction eq 'create') {
2016 internal_error("creating link and dir: $path");
2017 return 1;
2019 else { # no dir action
2020 return 1;
2023 else {
2024 # No link action
2025 if ($daction eq 'remove') {
2026 return 0;
2028 elsif ($daction eq 'create') {
2029 return 1;
2031 else { # no dir action
2032 # fall through to below
2036 return 0 if $self->parent_link_scheduled_for_removal($path);
2038 if (-e $path) {
2039 debug(4, 3, "| is_a_node($path): really exists");
2040 return 1;
2043 debug(4, 3, "| is_a_node($path): returning false");
2044 return 0;
2047 =head2 read_a_link($link)
2049 Return the destination of a current or planned link.
2051 =over 4
2053 =item $link
2055 Path to the link target.
2057 =back
2059 Returns the destination of the given link. Throws a fatal exception
2060 if the given path is not a current or planned link.
2062 =cut
2064 sub read_a_link {
2065 my $self = shift;
2066 my ($link) = @_;
2068 if (my $action = $self->link_task_action($link)) {
2069 debug(4, 2, "read_a_link($link): task exists with action $action");
2071 if ($action eq 'create') {
2072 return $self->{link_task_for}{$link}->{source};
2074 elsif ($action eq 'remove') {
2075 internal_error(
2076 "read_a_link() passed a path that is scheduled for removal: $link"
2080 elsif (-l $link) {
2081 debug(4, 2, "read_a_link($link): real link");
2082 my $link_dest = readlink $link or error("Could not read link: $link ($!)");
2083 return $link_dest;
2085 internal_error("read_a_link() passed a non-link path: $link\n");
2088 =head2 do_link($link_dest, $link_src)
2090 Wrap 'link' operation for later processing
2092 =over 4
2094 =item $link_dest
2096 the existing file to link to
2098 =item $link_src
2100 the file to link
2102 =back
2104 Throws an error if this clashes with an existing planned operation.
2105 Cleans up operations that undo previous operations.
2107 =cut
2109 sub do_link {
2110 my $self = shift;
2111 my ($link_dest, $link_src) = @_;
2113 if (exists $self->{dir_task_for}{$link_src}) {
2114 my $task_ref = $self->{dir_task_for}{$link_src};
2116 if ($task_ref->{action} eq 'create') {
2117 if ($task_ref->{type} eq 'dir') {
2118 internal_error(
2119 "new link (%s => %s) clashes with planned new directory",
2120 $link_src,
2121 $link_dest,
2125 elsif ($task_ref->{action} eq 'remove') {
2126 # We may need to remove a directory before creating a link so continue.
2128 else {
2129 internal_error("bad task action: $task_ref->{action}");
2133 if (exists $self->{link_task_for}{$link_src}) {
2134 my $task_ref = $self->{link_task_for}{$link_src};
2136 if ($task_ref->{action} eq 'create') {
2137 if ($task_ref->{source} ne $link_dest) {
2138 internal_error(
2139 "new link clashes with planned new link: %s => %s",
2140 $task_ref->{path},
2141 $task_ref->{source},
2144 else {
2145 debug(1, 0, "LINK: $link_src => $link_dest (duplicates previous action)");
2146 return;
2149 elsif ($task_ref->{action} eq 'remove') {
2150 if ($task_ref->{source} eq $link_dest) {
2151 # No need to remove a link we are going to recreate
2152 debug(1, 0, "LINK: $link_src => $link_dest (reverts previous action)");
2153 $self->{link_task_for}{$link_src}->{action} = 'skip';
2154 delete $self->{link_task_for}{$link_src};
2155 return;
2157 # We may need to remove a link to replace it so continue
2159 else {
2160 internal_error("bad task action: $task_ref->{action}");
2164 # Creating a new link
2165 debug(1, 0, "LINK: $link_src => $link_dest");
2166 my $task = {
2167 action => 'create',
2168 type => 'link',
2169 path => $link_src,
2170 source => $link_dest,
2172 push @{ $self->{tasks} }, $task;
2173 $self->{link_task_for}{$link_src} = $task;
2175 return;
2178 =head2 do_unlink($file)
2180 Wrap 'unlink' operation for later processing
2182 =over 4
2184 =item $file
2186 the file to unlink
2188 =back
2190 Throws an error if this clashes with an existing planned operation.
2191 Will remove an existing planned link.
2193 =cut
2195 sub do_unlink {
2196 my $self = shift;
2197 my ($file) = @_;
2199 if (exists $self->{link_task_for}{$file}) {
2200 my $task_ref = $self->{link_task_for}{$file};
2201 if ($task_ref->{action} eq 'remove') {
2202 debug(1, 0, "UNLINK: $file (duplicates previous action)");
2203 return;
2205 elsif ($task_ref->{action} eq 'create') {
2206 # Do need to create a link then remove it
2207 debug(1, 0, "UNLINK: $file (reverts previous action)");
2208 $self->{link_task_for}{$file}->{action} = 'skip';
2209 delete $self->{link_task_for}{$file};
2210 return;
2212 else {
2213 internal_error("bad task action: $task_ref->{action}");
2217 if (exists $self->{dir_task_for}{$file} and $self->{dir_task_for}{$file} eq 'create') {
2218 internal_error(
2219 "new unlink operation clashes with planned operation: %s dir %s",
2220 $self->{dir_task_for}{$file}->{action},
2221 $file
2225 # Remove the link
2226 debug(1, 0, "UNLINK: $file");
2228 my $source = readlink $file or error("could not readlink $file ($!)");
2230 my $task = {
2231 action => 'remove',
2232 type => 'link',
2233 path => $file,
2234 source => $source,
2236 push @{ $self->{tasks} }, $task;
2237 $self->{link_task_for}{$file} = $task;
2239 return;
2242 =head2 do_mkdir($dir)
2244 Wrap 'mkdir' operation
2246 =over 4
2248 =item $dir
2250 the directory to remove
2252 =back
2254 Throws a fatal exception if operation fails. Outputs a message if
2255 'verbose' option is set. Does not perform operation if 'simulate'
2256 option is set. Cleans up operations that undo previous operations.
2258 =cut
2260 sub do_mkdir {
2261 my $self = shift;
2262 my ($dir) = @_;
2264 if (exists $self->{link_task_for}{$dir}) {
2265 my $task_ref = $self->{link_task_for}{$dir};
2267 if ($task_ref->{action} eq 'create') {
2268 internal_error(
2269 "new dir clashes with planned new link (%s => %s)",
2270 $task_ref->{path},
2271 $task_ref->{source},
2274 elsif ($task_ref->{action} eq 'remove') {
2275 # May need to remove a link before creating a directory so continue
2277 else {
2278 internal_error("bad task action: $task_ref->{action}");
2282 if (exists $self->{dir_task_for}{$dir}) {
2283 my $task_ref = $self->{dir_task_for}{$dir};
2285 if ($task_ref->{action} eq 'create') {
2286 debug(1, 0, "MKDIR: $dir (duplicates previous action)");
2287 return;
2289 elsif ($task_ref->{action} eq 'remove') {
2290 debug(1, 0, "MKDIR: $dir (reverts previous action)");
2291 $self->{dir_task_for}{$dir}->{action} = 'skip';
2292 delete $self->{dir_task_for}{$dir};
2293 return;
2295 else {
2296 internal_error("bad task action: $task_ref->{action}");
2300 debug(1, 0, "MKDIR: $dir");
2301 my $task = {
2302 action => 'create',
2303 type => 'dir',
2304 path => $dir,
2305 source => undef,
2307 push @{ $self->{tasks} }, $task;
2308 $self->{dir_task_for}{$dir} = $task;
2310 return;
2313 =head2 do_rmdir($dir)
2315 Wrap 'rmdir' operation
2317 =over 4
2319 =item $dir
2321 the directory to remove
2323 =back
2325 Throws a fatal exception if operation fails. Outputs a message if
2326 'verbose' option is set. Does not perform operation if 'simulate'
2327 option is set.
2329 =cut
2331 sub do_rmdir {
2332 my $self = shift;
2333 my ($dir) = @_;
2335 if (exists $self->{link_task_for}{$dir}) {
2336 my $task_ref = $self->{link_task_for}{$dir};
2337 internal_error(
2338 "rmdir clashes with planned operation: %s link %s => %s",
2339 $task_ref->{action},
2340 $task_ref->{path},
2341 $task_ref->{source}
2345 if (exists $self->{dir_task_for}{$dir}) {
2346 my $task_ref = $self->{link_task_for}{$dir};
2348 if ($task_ref->{action} eq 'remove') {
2349 debug(1, 0, "RMDIR $dir (duplicates previous action)");
2350 return;
2352 elsif ($task_ref->{action} eq 'create') {
2353 debug(1, 0, "MKDIR $dir (reverts previous action)");
2354 $self->{link_task_for}{$dir}->{action} = 'skip';
2355 delete $self->{link_task_for}{$dir};
2356 return;
2358 else {
2359 internal_error("bad task action: $task_ref->{action}");
2363 debug(1, 0, "RMDIR $dir");
2364 my $task = {
2365 action => 'remove',
2366 type => 'dir',
2367 path => $dir,
2368 source => '',
2370 push @{ $self->{tasks} }, $task;
2371 $self->{dir_task_for}{$dir} = $task;
2373 return;
2376 =head2 do_mv($src, $dst)
2378 Wrap 'move' operation for later processing.
2380 =over 4
2382 =item $src
2384 the file to move
2386 =item $dst
2388 the path to move it to
2390 =back
2392 Throws an error if this clashes with an existing planned operation.
2393 Alters contents of package installation image in stow dir.
2395 =cut
2397 sub do_mv {
2398 my $self = shift;
2399 my ($src, $dst) = @_;
2401 if (exists $self->{link_task_for}{$src}) {
2402 # I don't *think* this should ever happen, but I'm not
2403 # 100% sure.
2404 my $task_ref = $self->{link_task_for}{$src};
2405 internal_error(
2406 "do_mv: pre-existing link task for $src; action: %s, source: %s",
2407 $task_ref->{action}, $task_ref->{source}
2410 elsif (exists $self->{dir_task_for}{$src}) {
2411 my $task_ref = $self->{dir_task_for}{$src};
2412 internal_error(
2413 "do_mv: pre-existing dir task for %s?! action: %s",
2414 $src, $task_ref->{action}
2418 # Remove the link
2419 debug(1, 0, "MV: $src -> $dst");
2421 my $task = {
2422 action => 'move',
2423 type => 'file',
2424 path => $src,
2425 dest => $dst,
2427 push @{ $self->{tasks} }, $task;
2429 # FIXME: do we need this for anything?
2430 #$self->{mv_task_for}{$file} = $task;
2432 return;
2436 #############################################################################
2438 # End of methods; subroutines follow.
2439 # FIXME: Ideally these should be in a separate module.
2442 # ===== PRIVATE SUBROUTINE ===================================================
2443 # Name : internal_error()
2444 # Purpose : output internal error message in a consistent form and die
2445 =over 4
2447 =item $message => error message to output
2449 =back
2451 Returns : n/a
2452 Throws : n/a
2454 =cut
2456 sub internal_error {
2457 my ($format, @args) = @_;
2458 my $error = sprintf($format, @args);
2459 my $stacktrace = Carp::longmess();
2460 die <<EOF;
2462 $ProgramName: INTERNAL ERROR: $error$stacktrace
2464 This _is_ a bug. Please submit a bug report so we can fix it! :-)
2465 See http://www.gnu.org/software/stow/ for how to do this.
2469 =head1 BUGS
2471 =head1 SEE ALSO
2473 =cut
2477 # Local variables:
2478 # mode: perl
2479 # end:
2480 # vim: ft=perl
2482 #############################################################################
2483 # Default global list of ignore regexps follows
2484 # (automatically appended by the Makefile)
2486 __DATA__