Stow.pm.in 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149
  1. #!/usr/bin/perl
  2. #
  3. # This file is part of GNU Stow.
  4. #
  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.
  9. #
  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.
  14. #
  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/.
  17. package Stow;
  18. =head1 NAME
  19. Stow - manage farms of symbolic links
  20. =head1 SYNOPSIS
  21. my $stow = new Stow(%$options);
  22. $stow->plan_unstow(@pkgs_to_unstow);
  23. $stow->plan_stow (@pkgs_to_stow);
  24. my %conflicts = $stow->get_conflicts;
  25. $stow->process_tasks() unless %conflicts;
  26. =head1 DESCRIPTION
  27. This is the backend Perl module for GNU Stow, a program for managing
  28. the installation of software packages, keeping them separate
  29. (C</usr/local/stow/emacs> vs. C</usr/local/stow/perl>, for example)
  30. while making them appear to be installed in the same place
  31. (C</usr/local>).
  32. Stow doesn't store an extra state between runs, so there's no danger
  33. of mangling directories when file hierarchies don't match the
  34. database. Also, stow will never delete any files, directories, or
  35. links that appear in a stow directory, so it is always possible to
  36. rebuild the target tree.
  37. =cut
  38. use strict;
  39. use warnings;
  40. use Carp qw(carp cluck croak confess longmess);
  41. use File::Copy qw(move);
  42. use File::Spec;
  43. use POSIX qw(getcwd);
  44. use Stow::Util qw(set_debug_level debug error set_test_mode
  45. join_paths restore_cwd canon_path parent adjust_dotfile);
  46. our $ProgramName = 'stow';
  47. our $VERSION = '@VERSION@';
  48. our $LOCAL_IGNORE_FILE = '.stow-local-ignore';
  49. our $GLOBAL_IGNORE_FILE = '.stow-global-ignore';
  50. our @default_global_ignore_regexps =
  51. __PACKAGE__->get_default_global_ignore_regexps();
  52. # These are the default options for each Stow instance.
  53. our %DEFAULT_OPTIONS = (
  54. conflicts => 0,
  55. simulate => 0,
  56. verbose => 0,
  57. paranoid => 0,
  58. compat => 0,
  59. test_mode => 0,
  60. dotfiles => 0,
  61. adopt => 0,
  62. 'no-folding' => 0,
  63. absolute => 0,
  64. ignore => [],
  65. override => [],
  66. defer => [],
  67. );
  68. =head1 CONSTRUCTORS
  69. =head2 new(%options)
  70. =head3 Required options
  71. =over 4
  72. =item * dir - the stow directory
  73. =item * target - the target directory
  74. =back
  75. =head3 Non-mandatory options
  76. See the documentation for the F<stow> CLI front-end for information on these.
  77. =over 4
  78. =item * conflicts
  79. =item * simulate
  80. =item * verbose
  81. =item * paranoid
  82. =item * compat
  83. =item * test_mode
  84. =item * adopt
  85. =item * no-folding
  86. =item * absolute
  87. =item * ignore
  88. =item * override
  89. =item * defer
  90. =back
  91. N.B. This sets the current working directory to the target directory.
  92. =cut
  93. sub new {
  94. my $self = shift;
  95. my $class = ref($self) || $self;
  96. my %opts = @_;
  97. my $new = bless { }, $class;
  98. $new->{action_count} = 0;
  99. for my $required_arg (qw(dir target)) {
  100. croak "$class->new() called without '$required_arg' parameter\n"
  101. unless exists $opts{$required_arg};
  102. $new->{$required_arg} = delete $opts{$required_arg};
  103. }
  104. for my $opt (keys %DEFAULT_OPTIONS) {
  105. $new->{$opt} = exists $opts{$opt} ? delete $opts{$opt}
  106. : $DEFAULT_OPTIONS{$opt};
  107. }
  108. if (%opts) {
  109. croak "$class->new() called with unrecognised parameter(s): ",
  110. join(", ", keys %opts), "\n";
  111. }
  112. set_debug_level($new->get_verbosity());
  113. set_test_mode($new->{test_mode});
  114. $new->set_stow_dir();
  115. $new->init_state();
  116. return $new;
  117. }
  118. sub get_verbosity {
  119. my $self = shift;
  120. return $self->{verbose} unless $self->{test_mode};
  121. return 0 unless exists $ENV{TEST_VERBOSE};
  122. return 0 unless length $ENV{TEST_VERBOSE};
  123. # Convert TEST_VERBOSE=y into numeric value
  124. $ENV{TEST_VERBOSE} = 3 if $ENV{TEST_VERBOSE} !~ /^\d+$/;
  125. return $ENV{TEST_VERBOSE};
  126. }
  127. =head2 set_stow_dir([$dir])
  128. Sets a new stow directory. This allows the use of multiple stow
  129. directories within one Stow instance, e.g.
  130. $stow->plan_stow('foo');
  131. $stow->set_stow_dir('/different/stow/dir');
  132. $stow->plan_stow('bar');
  133. $stow->process_tasks;
  134. If C<$dir> is omitted, uses the value of the C<dir> parameter passed
  135. to the L<new()> constructor.
  136. =cut
  137. sub set_stow_dir {
  138. my $self = shift;
  139. my ($dir) = @_;
  140. if (defined $dir) {
  141. $self->{dir} = $dir;
  142. }
  143. my $stow_dir = canon_path($self->{dir});
  144. my $target = canon_path($self->{target});
  145. $self->{stow_path} = $self->{absolute} ? $stow_dir :
  146. File::Spec->abs2rel($stow_dir, $target);
  147. debug(2, "stow dir is $stow_dir");
  148. if (!$self->{absolute}) {
  149. debug(2, "stow dir path relative to target $target is $self->{stow_path}");
  150. } else {
  151. debug(2, "stow dir path for target $target is $self->{stow_path}");
  152. }
  153. }
  154. sub init_state {
  155. my $self = shift;
  156. # Store conflicts during pre-processing
  157. $self->{conflicts} = {};
  158. $self->{conflict_count} = 0;
  159. # Store command line packages to stow (-S and -R)
  160. $self->{pkgs_to_stow} = [];
  161. # Store command line packages to unstow (-D and -R)
  162. $self->{pkgs_to_delete} = [];
  163. # The following structures are used by the abstractions that allow us to
  164. # defer operating on the filesystem until after all potential conflicts have
  165. # been assessed.
  166. # $self->{tasks}: list of operations to be performed (in order)
  167. # each element is a hash ref of the form
  168. # {
  169. # action => ... ('create' or 'remove' or 'move')
  170. # type => ... ('link' or 'dir' or 'file')
  171. # path => ... (unique)
  172. # source => ... (only for links)
  173. # dest => ... (only for moving files)
  174. # }
  175. $self->{tasks} = [];
  176. # $self->{dir_task_for}: map a path to the corresponding directory task reference
  177. # This structure allows us to quickly determine if a path has an existing
  178. # directory task associated with it.
  179. $self->{dir_task_for} = {};
  180. # $self->{link_task_for}: map a path to the corresponding directory task reference
  181. # This structure allows us to quickly determine if a path has an existing
  182. # directory task associated with it.
  183. $self->{link_task_for} = {};
  184. # N.B.: directory tasks and link tasks are NOT mutually exclusive due
  185. # to tree splitting (which involves a remove link task followed by
  186. # a create directory task).
  187. }
  188. =head1 METHODS
  189. =head2 plan_unstow(@packages)
  190. Plan which symlink/directory creation/removal tasks need to be executed
  191. in order to unstow the given packages. Any potential conflicts are then
  192. accessible via L<get_conflicts()>.
  193. =cut
  194. sub plan_unstow {
  195. my $self = shift;
  196. my @packages = @_;
  197. $self->within_target_do(sub {
  198. for my $package (@packages) {
  199. my $path = join_paths($self->{stow_path}, $package);
  200. if (not -d $path) {
  201. error("The stow directory $self->{stow_path} does not contain package $package");
  202. }
  203. debug(2, "Planning unstow of package $package...");
  204. if ($self->{compat}) {
  205. $self->unstow_contents_orig(
  206. $self->{stow_path},
  207. $package,
  208. '.',
  209. );
  210. }
  211. else {
  212. $self->unstow_contents(
  213. $self->{stow_path},
  214. $package,
  215. '.',
  216. );
  217. }
  218. debug(2, "Planning unstow of package $package... done");
  219. $self->{action_count}++;
  220. }
  221. });
  222. }
  223. =head2 plan_stow(@packages)
  224. Plan which symlink/directory creation/removal tasks need to be executed
  225. in order to stow the given packages. Any potential conflicts are then
  226. accessible via L<get_conflicts()>.
  227. =cut
  228. sub plan_stow {
  229. my $self = shift;
  230. my @packages = @_;
  231. $self->within_target_do(sub {
  232. for my $package (@packages) {
  233. my $path = join_paths($self->{stow_path}, $package);
  234. if (not -d $path) {
  235. error("The stow directory $self->{stow_path} does not contain package $package");
  236. }
  237. debug(2, "Planning stow of package $package...");
  238. $self->stow_contents(
  239. $self->{stow_path},
  240. $package,
  241. '.',
  242. $path, # source from target
  243. );
  244. debug(2, "Planning stow of package $package... done");
  245. $self->{action_count}++;
  246. }
  247. });
  248. }
  249. #===== METHOD ===============================================================
  250. # Name : within_target_do()
  251. # Purpose : execute code within target directory, preserving cwd
  252. # Parameters: $code => anonymous subroutine to execute within target dir
  253. # Returns : n/a
  254. # Throws : n/a
  255. # Comments : This is done to ensure that the consumer of the Stow interface
  256. # : doesn't have to worry about (a) what their cwd is, and
  257. # : (b) that their cwd might change.
  258. #============================================================================
  259. sub within_target_do {
  260. my $self = shift;
  261. my ($code) = @_;
  262. my $cwd = getcwd();
  263. chdir($self->{target})
  264. or error("Cannot chdir to target tree: $self->{target} ($!)");
  265. debug(3, "cwd now $self->{target}");
  266. $self->$code();
  267. restore_cwd($cwd);
  268. debug(3, "cwd restored to $cwd");
  269. }
  270. #===== METHOD ===============================================================
  271. # Name : stow_contents()
  272. # Purpose : stow the contents of the given directory
  273. # Parameters: $stow_path => relative path from current (i.e. target) directory
  274. # : to the stow dir containing the package to be stowed
  275. # : $package => the package whose contents are being stowed
  276. # : $target => subpath relative to package directory which needs
  277. # : stowing as a symlink at subpath relative to target
  278. # : directory.
  279. # : $source => relative path from the (sub)dir of target
  280. # : to symlink source
  281. # Returns : n/a
  282. # Throws : a fatal error if directory cannot be read
  283. # Comments : stow_node() and stow_contents() are mutually recursive.
  284. # : $source and $target are used for creating the symlink
  285. # : $path is used for folding/unfolding trees as necessary
  286. #============================================================================
  287. sub stow_contents {
  288. my $self = shift;
  289. my ($stow_path, $package, $target, $source) = @_;
  290. my $path = join_paths($stow_path, $package, $target);
  291. return if $self->should_skip_target_which_is_stow_dir($target);
  292. my $cwd = getcwd();
  293. my $msg = "Stowing contents of $path (cwd=$cwd)";
  294. $msg =~ s!$ENV{HOME}(/|$)!~$1!g;
  295. debug(3, $msg);
  296. debug(4, " => $source");
  297. error("stow_contents() called with non-directory path: $path")
  298. unless -d $path;
  299. error("stow_contents() called with non-directory target: $target")
  300. unless $self->is_a_node($target);
  301. opendir my $DIR, $path
  302. or error("cannot read directory: $path ($!)");
  303. my @listing = readdir $DIR;
  304. closedir $DIR;
  305. NODE:
  306. for my $node (@listing) {
  307. next NODE if $node eq '.';
  308. next NODE if $node eq '..';
  309. my $node_target = join_paths($target, $node);
  310. next NODE if $self->ignore($stow_path, $package, $node_target);
  311. if ($self->{dotfiles}) {
  312. my $adj_node_target = adjust_dotfile($node_target);
  313. debug(4, " Adjusting: $node_target => $adj_node_target");
  314. $node_target = $adj_node_target;
  315. }
  316. $self->stow_node(
  317. $stow_path,
  318. $package,
  319. $node_target, # target
  320. join_paths($source, $node), # source
  321. );
  322. }
  323. }
  324. #===== METHOD ===============================================================
  325. # Name : stow_node()
  326. # Purpose : stow the given node
  327. # Parameters: $stow_path => relative path from current (i.e. target) directory
  328. # : to the stow dir containing the node to be stowed
  329. # : $package => the package containing the node being stowed
  330. # : $target => subpath relative to package directory of node which
  331. # : needs stowing as a symlink at subpath relative to
  332. # : target directory.
  333. # : $source => relative path to symlink source from the dir of target
  334. # Returns : n/a
  335. # Throws : fatal exception if a conflict arises
  336. # Comments : stow_node() and stow_contents() are mutually recursive
  337. # : $source and $target are used for creating the symlink
  338. # : $path is used for folding/unfolding trees as necessary
  339. #============================================================================
  340. sub stow_node {
  341. my $self = shift;
  342. my ($stow_path, $package, $target, $source) = @_;
  343. my $path = join_paths($stow_path, $package, $target);
  344. debug(3, "Stowing $stow_path / $package / $target");
  345. debug(4, " => $source");
  346. # Don't try to stow absolute symlinks (they can't be unstowed)
  347. if ((not $self->{absolute}) && -l $source) {
  348. my $second_source = $self->read_a_link($source);
  349. if ($second_source =~ m{\A/}) {
  350. $self->conflict(
  351. 'stow',
  352. $package,
  353. "source is an absolute symlink $source => $second_source"
  354. );
  355. debug(3, "Absolute symlinks cannot be unstowed");
  356. return;
  357. }
  358. }
  359. if ($self->{absolute}) {
  360. debug(3, "Absolute symlinks cannot be unstowed, proceeding due to command-line option");
  361. }
  362. # Does the target already exist?
  363. if ($self->is_a_link($target)) {
  364. # Where is the link pointing?
  365. my $existing_source = $self->read_a_link($target);
  366. if (not $existing_source) {
  367. error("Could not read link: $target");
  368. }
  369. debug(4, " Evaluate existing link: $target => $existing_source");
  370. # Does it point to a node under any stow directory?
  371. my ($existing_path, $existing_stow_path, $existing_package) =
  372. $self->find_stowed_path($target, $existing_source);
  373. if (not $existing_path) {
  374. $self->conflict(
  375. 'stow',
  376. $package,
  377. "existing target is not owned by stow: $target"
  378. );
  379. return; # XXX #
  380. }
  381. # Does the existing $target actually point to anything?
  382. if ($self->is_a_node($existing_path)) {
  383. if ($existing_source eq $source) {
  384. debug(2, "--- Skipping $target as it already points to $source");
  385. }
  386. elsif ($self->defer($target)) {
  387. debug(2, "--- Deferring installation of: $target");
  388. }
  389. elsif ($self->override($target)) {
  390. debug(2, "--- Overriding installation of: $target");
  391. $self->do_unlink($target);
  392. $self->do_link($source, $target);
  393. }
  394. elsif ($self->is_a_dir(join_paths(parent($target), $existing_source)) &&
  395. $self->is_a_dir(join_paths(parent($target), $source)) ) {
  396. # If the existing link points to a directory,
  397. # and the proposed new link points to a directory,
  398. # then we can unfold (split open) the tree at that point
  399. debug(2, "--- Unfolding $target which was already owned by $existing_package");
  400. $self->do_unlink($target);
  401. $self->do_mkdir($target);
  402. $self->stow_contents(
  403. $existing_stow_path,
  404. $existing_package,
  405. $target,
  406. join_paths('..', $existing_source),
  407. );
  408. $self->stow_contents(
  409. $self->{stow_path},
  410. $package,
  411. $target,
  412. join_paths('..', $source),
  413. );
  414. }
  415. else {
  416. $self->conflict(
  417. 'stow',
  418. $package,
  419. "existing target is stowed to a different package: "
  420. . "$target => $existing_source"
  421. );
  422. }
  423. }
  424. else {
  425. # The existing link is invalid, so replace it with a good link
  426. debug(2, "--- replacing invalid link: $path");
  427. $self->do_unlink($target);
  428. $self->do_link($source, $target);
  429. }
  430. }
  431. elsif ($self->is_a_node($target)) {
  432. debug(4, " Evaluate existing node: $target");
  433. if ($self->is_a_dir($target)) {
  434. $self->stow_contents(
  435. $self->{stow_path},
  436. $package,
  437. $target,
  438. join_paths('..', $source),
  439. );
  440. }
  441. else {
  442. if ($self->{adopt}) {
  443. $self->do_mv($target, $path);
  444. $self->do_link($source, $target);
  445. }
  446. else {
  447. $self->conflict(
  448. 'stow',
  449. $package,
  450. "existing target is neither a link nor a directory: $target"
  451. );
  452. }
  453. }
  454. }
  455. elsif ($self->{'no-folding'} && -d $path && ! -l $path) {
  456. $self->do_mkdir($target);
  457. $self->stow_contents(
  458. $self->{stow_path},
  459. $package,
  460. $target,
  461. join_paths('..', $source),
  462. );
  463. }
  464. else {
  465. $self->do_link($source, $target);
  466. }
  467. return;
  468. }
  469. #===== METHOD ===============================================================
  470. # Name : should_skip_target_which_is_stow_dir()
  471. # Purpose : determine whether target is a stow directory which should
  472. # : not be stowed to or unstowed from
  473. # Parameters: $target => relative path to symlink target from the current directory
  474. # Returns : true iff target is a stow directory
  475. # Throws : n/a
  476. # Comments : none
  477. #============================================================================
  478. sub should_skip_target_which_is_stow_dir {
  479. my $self = shift;
  480. my ($target) = @_;
  481. # Don't try to remove anything under a stow directory
  482. if ($target eq $self->{stow_path}) {
  483. warn "WARNING: skipping target which was current stow directory $target\n";
  484. return 1;
  485. }
  486. if ($self->marked_stow_dir($target)) {
  487. warn "WARNING: skipping protected directory $target\n";
  488. return 1;
  489. }
  490. debug(4, "$target not protected");
  491. return 0;
  492. }
  493. sub marked_stow_dir {
  494. my $self = shift;
  495. my ($target) = @_;
  496. for my $f (".stow", ".nonstow") {
  497. if (-e join_paths($target, $f)) {
  498. debug(4, "$target contained $f");
  499. return 1;
  500. }
  501. }
  502. return 0;
  503. }
  504. #===== METHOD ===============================================================
  505. # Name : unstow_contents_orig()
  506. # Purpose : unstow the contents of the given directory
  507. # Parameters: $stow_path => relative path from current (i.e. target) directory
  508. # : to the stow dir containing the package to be unstowed
  509. # : $package => the package whose contents are being unstowed
  510. # : $target => relative path to symlink target from the current directory
  511. # Returns : n/a
  512. # Throws : a fatal error if directory cannot be read
  513. # Comments : unstow_node_orig() and unstow_contents_orig() are mutually recursive
  514. # : Here we traverse the target tree, rather than the source tree.
  515. #============================================================================
  516. sub unstow_contents_orig {
  517. my $self = shift;
  518. my ($stow_path, $package, $target) = @_;
  519. my $path = join_paths($stow_path, $package, $target);
  520. return if $self->should_skip_target_which_is_stow_dir($target);
  521. my $cwd = getcwd();
  522. my $msg = "Unstowing from $target (compat mode, cwd=$cwd, stow dir=$self->{stow_path})";
  523. $msg =~ s!$ENV{HOME}(/|$)!~$1!g;
  524. debug(3, $msg);
  525. debug(4, " source path is $path");
  526. # In compat mode we traverse the target tree not the source tree,
  527. # so we're unstowing the contents of /target/foo, there's no
  528. # guarantee that the corresponding /stow/mypkg/foo exists.
  529. error("unstow_contents_orig() called with non-directory target: $target")
  530. unless -d $target;
  531. opendir my $DIR, $target
  532. or error("cannot read directory: $target ($!)");
  533. my @listing = readdir $DIR;
  534. closedir $DIR;
  535. NODE:
  536. for my $node (@listing) {
  537. next NODE if $node eq '.';
  538. next NODE if $node eq '..';
  539. my $node_target = join_paths($target, $node);
  540. next NODE if $self->ignore($stow_path, $package, $node_target);
  541. $self->unstow_node_orig($stow_path, $package, $node_target);
  542. }
  543. }
  544. #===== METHOD ===============================================================
  545. # Name : unstow_node_orig()
  546. # Purpose : unstow the given node
  547. # Parameters: $stow_path => relative path from current (i.e. target) directory
  548. # : to the stow dir containing the node to be stowed
  549. # : $package => the package containing the node being stowed
  550. # : $target => relative path to symlink target from the current directory
  551. # Returns : n/a
  552. # Throws : fatal error if a conflict arises
  553. # Comments : unstow_node() and unstow_contents() are mutually recursive
  554. #============================================================================
  555. sub unstow_node_orig {
  556. my $self = shift;
  557. my ($stow_path, $package, $target) = @_;
  558. my $path = join_paths($stow_path, $package, $target);
  559. debug(3, "Unstowing $target (compat mode)");
  560. debug(4, " source path is $path");
  561. # Does the target exist?
  562. if ($self->is_a_link($target)) {
  563. debug(4, " Evaluate existing link: $target");
  564. # Where is the link pointing?
  565. my $existing_source = $self->read_a_link($target);
  566. if (not $existing_source) {
  567. error("Could not read link: $target");
  568. }
  569. # Does it point to a node under any stow directory?
  570. my ($existing_path, $existing_stow_path, $existing_package) =
  571. $self->find_stowed_path($target, $existing_source);
  572. if (not $existing_path) {
  573. # We're traversing the target tree not the package tree,
  574. # so we definitely expect to find stuff not owned by stow.
  575. # Therefore we can't flag a conflict.
  576. return; # XXX #
  577. }
  578. # Does the existing $target actually point to anything?
  579. if (-e $existing_path) {
  580. # Does link point to the right place?
  581. if ($existing_path eq $path) {
  582. $self->do_unlink($target);
  583. }
  584. elsif ($self->override($target)) {
  585. debug(2, "--- overriding installation of: $target");
  586. $self->do_unlink($target);
  587. }
  588. # else leave it alone
  589. }
  590. else {
  591. debug(2, "--- removing invalid link into a stow directory: $path");
  592. $self->do_unlink($target);
  593. }
  594. }
  595. elsif (-d $target) {
  596. $self->unstow_contents_orig($stow_path, $package, $target);
  597. # This action may have made the parent directory foldable
  598. if (my $parent = $self->foldable($target)) {
  599. $self->fold_tree($target, $parent);
  600. }
  601. }
  602. elsif (-e $target) {
  603. $self->conflict(
  604. 'unstow',
  605. $package,
  606. "existing target is neither a link nor a directory: $target",
  607. );
  608. }
  609. else {
  610. debug(2, "$target did not exist to be unstowed");
  611. }
  612. return;
  613. }
  614. #===== METHOD ===============================================================
  615. # Name : unstow_contents()
  616. # Purpose : unstow the contents of the given directory
  617. # Parameters: $stow_path => relative path from current (i.e. target) directory
  618. # : to the stow dir containing the package to be unstowed
  619. # : $package => the package whose contents are being unstowed
  620. # : $target => relative path to symlink target from the current directory
  621. # Returns : n/a
  622. # Throws : a fatal error if directory cannot be read
  623. # Comments : unstow_node() and unstow_contents() are mutually recursive
  624. # : Here we traverse the source tree, rather than the target tree.
  625. #============================================================================
  626. sub unstow_contents {
  627. my $self = shift;
  628. my ($stow_path, $package, $target) = @_;
  629. my $path = join_paths($stow_path, $package, $target);
  630. return if $self->should_skip_target_which_is_stow_dir($target);
  631. my $cwd = getcwd();
  632. my $msg = "Unstowing from $target (cwd=$cwd, stow dir=$self->{stow_path})";
  633. $msg =~ s!$ENV{HOME}/!~/!g;
  634. debug(3, $msg);
  635. debug(4, " source path is $path");
  636. # We traverse the source tree not the target tree, so $path must exist.
  637. error("unstow_contents() called with non-directory path: $path")
  638. unless -d $path;
  639. # When called at the top level, $target should exist. And
  640. # unstow_node() should only call this via mutual recursion if
  641. # $target exists.
  642. error("unstow_contents() called with invalid target: $target")
  643. unless $self->is_a_node($target);
  644. opendir my $DIR, $path
  645. or error("cannot read directory: $path ($!)");
  646. my @listing = readdir $DIR;
  647. closedir $DIR;
  648. NODE:
  649. for my $node (@listing) {
  650. next NODE if $node eq '.';
  651. next NODE if $node eq '..';
  652. my $node_target = join_paths($target, $node);
  653. next NODE if $self->ignore($stow_path, $package, $node_target);
  654. if ($self->{dotfiles}) {
  655. my $adj_node_target = adjust_dotfile($node_target);
  656. debug(4, " Adjusting: $node_target => $adj_node_target");
  657. $node_target = $adj_node_target;
  658. }
  659. $self->unstow_node($stow_path, $package, $node_target);
  660. }
  661. if (-d $target) {
  662. $self->cleanup_invalid_links($target);
  663. }
  664. }
  665. #===== METHOD ===============================================================
  666. # Name : unstow_node()
  667. # Purpose : unstow the given node
  668. # Parameters: $stow_path => relative path from current (i.e. target) directory
  669. # : to the stow dir containing the node to be stowed
  670. # : $package => the package containing the node being unstowed
  671. # : $target => relative path to symlink target from the current directory
  672. # Returns : n/a
  673. # Throws : fatal error if a conflict arises
  674. # Comments : unstow_node() and unstow_contents() are mutually recursive
  675. #============================================================================
  676. sub unstow_node {
  677. my $self = shift;
  678. my ($stow_path, $package, $target) = @_;
  679. my $path = join_paths($stow_path, $package, $target);
  680. debug(3, "Unstowing $path");
  681. debug(4, " target is $target");
  682. # Does the target exist?
  683. if ($self->is_a_link($target)) {
  684. debug(4, " Evaluate existing link: $target");
  685. # Where is the link pointing?
  686. my $existing_source = $self->read_a_link($target);
  687. if (not $existing_source) {
  688. error("Could not read link: $target");
  689. }
  690. if (not $self->{absolute} and $existing_source =~ m{\A/}) {
  691. warn "Ignoring an absolute symlink: $target => $existing_source\n";
  692. return; # XXX #
  693. }
  694. # Does it point to a node under any stow directory?
  695. my ($existing_path, $existing_stow_path, $existing_package) =
  696. $self->find_stowed_path($target, $existing_source);
  697. if (not $existing_path) {
  698. $self->conflict(
  699. 'unstow',
  700. $package,
  701. "existing target is not owned by stow: $target => $existing_source"
  702. );
  703. return; # XXX #
  704. }
  705. # Does the existing $target actually point to anything?
  706. if (-e $existing_path) {
  707. # Does link points to the right place?
  708. # Adjust for dotfile if necessary.
  709. if ($self->{dotfiles}) {
  710. $existing_path = adjust_dotfile($existing_path);
  711. }
  712. if ($existing_path eq $path) {
  713. $self->do_unlink($target);
  714. }
  715. # XXX we quietly ignore links that are stowed to a different
  716. # package.
  717. #elsif (defer($target)) {
  718. # debug(2, "--- deferring to installation of: $target");
  719. #}
  720. #elsif ($self->override($target)) {
  721. # debug(2, "--- overriding installation of: $target");
  722. # $self->do_unlink($target);
  723. #}
  724. #else {
  725. # $self->conflict(
  726. # 'unstow',
  727. # $package,
  728. # "existing target is stowed to a different package: "
  729. # . "$target => $existing_source"
  730. # );
  731. #}
  732. }
  733. else {
  734. debug(2, "--- removing invalid link into a stow directory: $path");
  735. $self->do_unlink($target);
  736. }
  737. }
  738. elsif (-e $target) {
  739. debug(4, " Evaluate existing node: $target");
  740. if (-d $target) {
  741. $self->unstow_contents($stow_path, $package, $target);
  742. # This action may have made the parent directory foldable
  743. if (my $parent = $self->foldable($target)) {
  744. $self->fold_tree($target, $parent);
  745. }
  746. }
  747. else {
  748. $self->conflict(
  749. 'unstow',
  750. $package,
  751. "existing target is neither a link nor a directory: $target",
  752. );
  753. }
  754. }
  755. else {
  756. debug(2, "$target did not exist to be unstowed");
  757. }
  758. return;
  759. }
  760. #===== METHOD ===============================================================
  761. # Name : path_owned_by_package()
  762. # Purpose : determine whether the given link points to a member of a
  763. # : stowed package
  764. # Parameters: $target => path to a symbolic link under current directory
  765. # : $source => where that link points to
  766. # Returns : the package iff link is owned by stow, otherwise ''
  767. # Throws : n/a
  768. # Comments : lossy wrapper around find_stowed_path()
  769. #============================================================================
  770. sub path_owned_by_package {
  771. my $self = shift;
  772. my ($target, $source) = @_;
  773. my ($path, $stow_path, $package) =
  774. $self->find_stowed_path($target, $source);
  775. return $package;
  776. }
  777. #===== METHOD ===============================================================
  778. # Name : find_stowed_path()
  779. # Purpose : determine whether the given link points to a member of a
  780. # : stowed package
  781. # Parameters: $target => path to a symbolic link under current directory.
  782. # : Must share a common prefix with $self->{stow_path}
  783. # : $source => where that link points to (needed because link
  784. # : might not exist yet due to two-phase approach,
  785. # : so we can't just call readlink()). This must be
  786. # : expressed relative to (the directory containing)
  787. # : $target.
  788. # Returns : ($path, $stow_path, $package) where $path and $stow_path are
  789. # : relative from the current (i.e. target) directory. $path
  790. # : is the full relative path, $stow_path is the relative path
  791. # : to the stow directory, and $package is the name of the package.
  792. # : or ('', '', '') if link is not owned by stow
  793. # Throws : n/a
  794. # Comments : Allow for stow dir not being under target dir.
  795. # : We could put more logic under here for multiple stow dirs.
  796. #============================================================================
  797. sub find_stowed_path {
  798. my $self = shift;
  799. my ($target, $source) = @_;
  800. # Evaluate softlink relative to its target
  801. my $path = join_paths(parent($target), $source);
  802. debug(4, " is path $path owned by stow?");
  803. # Search for .stow files - this allows us to detect links
  804. # owned by stow directories other than the current one.
  805. my $dir = '';
  806. my @path = split m{/+}, $path;
  807. for my $i (0 .. $#path) {
  808. my $part = $path[$i];
  809. $dir = join_paths($dir, $part);
  810. if ($self->marked_stow_dir($dir)) {
  811. # FIXME - not sure if this can ever happen
  812. internal_error("find_stowed_path() called directly on stow dir")
  813. if $i == $#path;
  814. debug(4, " yes - $dir was marked as a stow dir");
  815. my $package = $path[$i + 1];
  816. return ($path, $dir, $package);
  817. }
  818. }
  819. # If no .stow file was found, we need to find out whether it's
  820. # owned by the current stow directory, in which case $path will be
  821. # a prefix of $self->{stow_path}.
  822. if (substr($path, 0, 1) eq '/' xor substr($self->{stow_path}, 0, 1) eq '/')
  823. {
  824. warn "BUG in find_stowed_path? Absolute/relative mismatch between " .
  825. "Stow dir $self->{stow_path} and path $path";
  826. }
  827. my @stow_path = split m{/+}, $self->{stow_path};
  828. # Strip off common prefixes until one is empty
  829. while (@path && @stow_path) {
  830. if ((shift @path) ne (shift @stow_path)) {
  831. debug(4, " no - either $path not under $self->{stow_path} or vice-versa");
  832. return ('', '', '');
  833. }
  834. }
  835. if (@stow_path) { # @path must be empty
  836. debug(4, " no - $path is not under $self->{stow_path}");
  837. return ('', '', '');
  838. }
  839. my $package = shift @path;
  840. debug(4, " yes - by $package in " . join_paths(@path));
  841. return ($path, $self->{stow_path}, $package);
  842. }
  843. #===== METHOD ================================================================
  844. # Name : cleanup_invalid_links()
  845. # Purpose : clean up invalid links that may block folding
  846. # Parameters: $dir => path to directory to check
  847. # Returns : n/a
  848. # Throws : no exceptions
  849. # Comments : removing files from a stowed package is probably a bad practice
  850. # : so this kind of clean up is not _really_ stow's responsibility;
  851. # : however, failing to clean up can block tree folding, so we'll do
  852. # : it anyway
  853. #=============================================================================
  854. sub cleanup_invalid_links {
  855. my $self = shift;
  856. my ($dir) = @_;
  857. if (not -d $dir) {
  858. error("cleanup_invalid_links() called with a non-directory: $dir");
  859. }
  860. opendir my $DIR, $dir
  861. or error("cannot read directory: $dir ($!)");
  862. my @listing = readdir $DIR;
  863. closedir $DIR;
  864. NODE:
  865. for my $node (@listing) {
  866. next NODE if $node eq '.';
  867. next NODE if $node eq '..';
  868. my $node_path = join_paths($dir, $node);
  869. if (-l $node_path and not exists $self->{link_task_for}{$node_path}) {
  870. # Where is the link pointing?
  871. # (don't use read_a_link() here)
  872. my $source = readlink($node_path);
  873. if (not $source) {
  874. error("Could not read link $node_path");
  875. }
  876. if (
  877. not -e join_paths($dir, $source) and # bad link
  878. $self->path_owned_by_package($node_path, $source) # owned by stow
  879. ){
  880. debug(2, "--- removing stale link: $node_path => " .
  881. join_paths($dir, $source));
  882. $self->do_unlink($node_path);
  883. }
  884. }
  885. }
  886. return;
  887. }
  888. #===== METHOD ===============================================================
  889. # Name : foldable()
  890. # Purpose : determine whether a tree can be folded
  891. # Parameters: $target => path to a directory
  892. # Returns : path to the parent dir iff the tree can be safely folded
  893. # Throws : n/a
  894. # Comments : the path returned is relative to the parent of $target,
  895. # : that is, it can be used as the source for a replacement symlink
  896. #============================================================================
  897. sub foldable {
  898. my $self = shift;
  899. my ($target) = @_;
  900. debug(3, "--- Is $target foldable?");
  901. if ($self->{'no-folding'}) {
  902. debug(3, "--- no because --no-folding enabled");
  903. return '';
  904. }
  905. opendir my $DIR, $target
  906. or error(qq{Cannot read directory "$target" ($!)\n});
  907. my @listing = readdir $DIR;
  908. closedir $DIR;
  909. my $parent = '';
  910. NODE:
  911. for my $node (@listing) {
  912. next NODE if $node eq '.';
  913. next NODE if $node eq '..';
  914. my $path = join_paths($target, $node);
  915. # Skip nodes scheduled for removal
  916. next NODE if not $self->is_a_node($path);
  917. # If it's not a link then we can't fold its parent
  918. return '' if not $self->is_a_link($path);
  919. # Where is the link pointing?
  920. my $source = $self->read_a_link($path);
  921. if (not $source) {
  922. error("Could not read link $path");
  923. }
  924. if ($parent eq '') {
  925. $parent = parent($source)
  926. }
  927. elsif ($parent ne parent($source)) {
  928. return '';
  929. }
  930. }
  931. return '' if not $parent;
  932. # If we get here then all nodes inside $target are links, and those links
  933. # point to nodes inside the same directory.
  934. # chop of leading '..' to get the path to the common parent directory
  935. # relative to the parent of our $target
  936. $parent =~ s{\A\.\./}{};
  937. # If the resulting path is owned by stow, we can fold it
  938. if ($self->path_owned_by_package($target, $parent)) {
  939. debug(3, "--- $target is foldable");
  940. return $parent;
  941. }
  942. else {
  943. return '';
  944. }
  945. }
  946. #===== METHOD ===============================================================
  947. # Name : fold_tree()
  948. # Purpose : fold the given tree
  949. # Parameters: $source => link to the folded tree source
  950. # : $target => directory that we will replace with a link to $source
  951. # Returns : n/a
  952. # Throws : none
  953. # Comments : only called iff foldable() is true so we can remove some checks
  954. #============================================================================
  955. sub fold_tree {
  956. my $self = shift;
  957. my ($target, $source) = @_;
  958. debug(3, "--- Folding tree: $target => $source");
  959. opendir my $DIR, $target
  960. or error(qq{Cannot read directory "$target" ($!)\n});
  961. my @listing = readdir $DIR;
  962. closedir $DIR;
  963. NODE:
  964. for my $node (@listing) {
  965. next NODE if $node eq '.';
  966. next NODE if $node eq '..';
  967. next NODE if not $self->is_a_node(join_paths($target, $node));
  968. $self->do_unlink(join_paths($target, $node));
  969. }
  970. $self->do_rmdir($target);
  971. $self->do_link($source, $target);
  972. return;
  973. }
  974. #===== METHOD ===============================================================
  975. # Name : conflict()
  976. # Purpose : handle conflicts in stow operations
  977. # Parameters: $package => the package involved with the conflicting operation
  978. # : $message => a description of the conflict
  979. # Returns : n/a
  980. # Throws : none
  981. # Comments : none
  982. #============================================================================
  983. sub conflict {
  984. my $self = shift;
  985. my ($action, $package, $message) = @_;
  986. debug(2, "CONFLICT when ${action}ing $package: $message");
  987. $self->{conflicts}{$action}{$package} ||= [];
  988. push @{ $self->{conflicts}{$action}{$package} }, $message;
  989. $self->{conflict_count}++;
  990. return;
  991. }
  992. =head2 get_conflicts()
  993. Returns a nested hash of all potential conflicts discovered: the keys
  994. are actions ('stow' or 'unstow'), and the values are hashrefs whose
  995. keys are stow package names and whose values are conflict
  996. descriptions, e.g.:
  997. (
  998. stow => {
  999. perl => [
  1000. "existing target is not owned by stow: bin/a2p"
  1001. "existing target is neither a link nor a directory: bin/perl"
  1002. ]
  1003. }
  1004. )
  1005. =cut
  1006. sub get_conflicts {
  1007. my $self = shift;
  1008. return %{ $self->{conflicts} };
  1009. }
  1010. =head2 get_conflict_count()
  1011. Returns the number of conflicts found.
  1012. =cut
  1013. sub get_conflict_count {
  1014. my $self = shift;
  1015. return $self->{conflict_count};
  1016. }
  1017. =head2 get_tasks()
  1018. Returns a list of all symlink/directory creation/removal tasks.
  1019. =cut
  1020. sub get_tasks {
  1021. my $self = shift;
  1022. return @{ $self->{tasks} };
  1023. }
  1024. =head2 get_action_count()
  1025. Returns the number of actions planned for this Stow instance.
  1026. =cut
  1027. sub get_action_count {
  1028. my $self = shift;
  1029. return $self->{action_count};
  1030. }
  1031. #===== METHOD ================================================================
  1032. # Name : ignore
  1033. # Purpose : determine if the given path matches a regex in our ignore list
  1034. # Parameters: $stow_path => the stow directory containing the package
  1035. # : $package => the package containing the path
  1036. # : $target => the path to check against the ignore list
  1037. # : relative to its package directory
  1038. # Returns : true iff the path should be ignored
  1039. # Throws : no exceptions
  1040. # Comments : none
  1041. #=============================================================================
  1042. sub ignore {
  1043. my $self = shift;
  1044. my ($stow_path, $package, $target) = @_;
  1045. internal_error(__PACKAGE__ . "::ignore() called with empty target")
  1046. unless length $target;
  1047. for my $suffix (@{ $self->{ignore} }) {
  1048. if ($target =~ m/$suffix/) {
  1049. debug(4, " Ignoring path $target due to --ignore=$suffix");
  1050. return 1;
  1051. }
  1052. }
  1053. my $package_dir = join_paths($stow_path, $package);
  1054. my ($path_regexp, $segment_regexp) =
  1055. $self->get_ignore_regexps($package_dir);
  1056. debug(5, " Ignore list regexp for paths: " .
  1057. (defined $path_regexp ? "/$path_regexp/" : "none"));
  1058. debug(5, " Ignore list regexp for segments: " .
  1059. (defined $segment_regexp ? "/$segment_regexp/" : "none"));
  1060. if (defined $path_regexp and "/$target" =~ $path_regexp) {
  1061. debug(4, " Ignoring path /$target");
  1062. return 1;
  1063. }
  1064. (my $basename = $target) =~ s!.+/!!;
  1065. if (defined $segment_regexp and $basename =~ $segment_regexp) {
  1066. debug(4, " Ignoring path segment $basename");
  1067. return 1;
  1068. }
  1069. debug(5, " Not ignoring $target");
  1070. return 0;
  1071. }
  1072. sub get_ignore_regexps {
  1073. my $self = shift;
  1074. my ($dir) = @_;
  1075. # N.B. the local and global stow ignore files have to have different
  1076. # names so that:
  1077. # 1. the global one can be a symlink to within a stow
  1078. # package, managed by stow itself, and
  1079. # 2. the local ones can be ignored via hardcoded logic in
  1080. # GlobsToRegexp(), so that they always stay within their stow packages.
  1081. my $local_stow_ignore = join_paths($dir, $LOCAL_IGNORE_FILE);
  1082. my $global_stow_ignore = join_paths($ENV{HOME}, $GLOBAL_IGNORE_FILE);
  1083. for my $file ($local_stow_ignore, $global_stow_ignore) {
  1084. if (-e $file) {
  1085. debug(5, " Using ignore file: $file");
  1086. return $self->get_ignore_regexps_from_file($file);
  1087. }
  1088. else {
  1089. debug(5, " $file didn't exist");
  1090. }
  1091. }
  1092. debug(4, " Using built-in ignore list");
  1093. return @default_global_ignore_regexps;
  1094. }
  1095. my %ignore_file_regexps;
  1096. sub get_ignore_regexps_from_file {
  1097. my $self = shift;
  1098. my ($file) = @_;
  1099. if (exists $ignore_file_regexps{$file}) {
  1100. debug(4, " Using memoized regexps from $file");
  1101. return @{ $ignore_file_regexps{$file} };
  1102. }
  1103. if (! open(REGEXPS, $file)) {
  1104. debug(4, " Failed to open $file: $!");
  1105. return undef;
  1106. }
  1107. my @regexps = $self->get_ignore_regexps_from_fh(\*REGEXPS);
  1108. close(REGEXPS);
  1109. $ignore_file_regexps{$file} = [ @regexps ];
  1110. return @regexps;
  1111. }
  1112. =head2 invalidate_memoized_regexp($file)
  1113. For efficiency of performance, regular expressions are compiled from
  1114. each ignore list file the first time it is used by the Stow process,
  1115. and then memoized for future use. If you expect the contents of these
  1116. files to change during a single run, you will need to invalidate the
  1117. memoized value from this cache. This method allows you to do that.
  1118. =cut
  1119. sub invalidate_memoized_regexp {
  1120. my $self = shift;
  1121. my ($file) = @_;
  1122. if (exists $ignore_file_regexps{$file}) {
  1123. debug(4, " Invalidated memoized regexp for $file");
  1124. delete $ignore_file_regexps{$file};
  1125. }
  1126. else {
  1127. debug(2, " WARNING: no memoized regexp for $file to invalidate");
  1128. }
  1129. }
  1130. sub get_ignore_regexps_from_fh {
  1131. my $self = shift;
  1132. my ($fh) = @_;
  1133. my %regexps;
  1134. while (<$fh>) {
  1135. chomp;
  1136. s/^\s+//;
  1137. s/\s+$//;
  1138. next if /^#/ or length($_) == 0;
  1139. s/\s+#.+//; # strip comments to right of pattern
  1140. s/\\#/#/g;
  1141. $regexps{$_}++;
  1142. }
  1143. # Local ignore lists should *always* stay within the stow directory,
  1144. # because this is the only place stow looks for them.
  1145. $regexps{"^/\Q$LOCAL_IGNORE_FILE\E\$"}++;
  1146. return $self->compile_ignore_regexps(%regexps);
  1147. }
  1148. sub compile_ignore_regexps {
  1149. my $self = shift;
  1150. my (%regexps) = @_;
  1151. my @segment_regexps;
  1152. my @path_regexps;
  1153. for my $regexp (keys %regexps) {
  1154. if (index($regexp, '/') < 0) {
  1155. # No / found in regexp, so use it for matching against basename
  1156. push @segment_regexps, $regexp;
  1157. }
  1158. else {
  1159. # / found in regexp, so use it for matching against full path
  1160. push @path_regexps, $regexp;
  1161. }
  1162. }
  1163. my $segment_regexp = join '|', @segment_regexps;
  1164. my $path_regexp = join '|', @path_regexps;
  1165. $segment_regexp = @segment_regexps ?
  1166. $self->compile_regexp("^($segment_regexp)\$") : undef;
  1167. $path_regexp = @path_regexps ?
  1168. $self->compile_regexp("(^|/)($path_regexp)(/|\$)") : undef;
  1169. return ($path_regexp, $segment_regexp);
  1170. }
  1171. sub compile_regexp {
  1172. my $self = shift;
  1173. my ($regexp) = @_;
  1174. my $compiled = eval { qr/$regexp/ };
  1175. die "Failed to compile regexp: $@\n" if $@;
  1176. return $compiled;
  1177. }
  1178. sub get_default_global_ignore_regexps {
  1179. my $class = shift;
  1180. # Bootstrap issue - first time we stow, we will be stowing
  1181. # .cvsignore so it might not exist in ~ yet, or if it does, it could
  1182. # be an old version missing the entries we need. So we make sure
  1183. # they are there by hardcoding some crucial entries.
  1184. return $class->get_ignore_regexps_from_fh(\*DATA);
  1185. }
  1186. #===== METHOD ================================================================
  1187. # Name : defer
  1188. # Purpose : determine if the given path matches a regex in our defer list
  1189. # Parameters: $path
  1190. # Returns : Boolean
  1191. # Throws : no exceptions
  1192. # Comments : none
  1193. #=============================================================================
  1194. sub defer {
  1195. my $self = shift;
  1196. my ($path) = @_;
  1197. for my $prefix (@{ $self->{defer} }) {
  1198. return 1 if $path =~ m/$prefix/;
  1199. }
  1200. return 0;
  1201. }
  1202. #===== METHOD ================================================================
  1203. # Name : override
  1204. # Purpose : determine if the given path matches a regex in our override list
  1205. # Parameters: $path
  1206. # Returns : Boolean
  1207. # Throws : no exceptions
  1208. # Comments : none
  1209. #=============================================================================
  1210. sub override {
  1211. my $self = shift;
  1212. my ($path) = @_;
  1213. for my $regex (@{ $self->{override} }) {
  1214. return 1 if $path =~ m/$regex/;
  1215. }
  1216. return 0;
  1217. }
  1218. ##############################################################################
  1219. #
  1220. # The following code provides the abstractions that allow us to defer operating
  1221. # on the filesystem until after all potential conflcits have been assessed.
  1222. #
  1223. ##############################################################################
  1224. #===== METHOD ===============================================================
  1225. # Name : process_tasks()
  1226. # Purpose : process each task in the tasks list
  1227. # Parameters: none
  1228. # Returns : n/a
  1229. # Throws : fatal error if tasks list is corrupted or a task fails
  1230. # Comments : none
  1231. #============================================================================
  1232. sub process_tasks {
  1233. my $self = shift;
  1234. debug(2, "Processing tasks...");
  1235. # Strip out all tasks with a skip action
  1236. $self->{tasks} = [ grep { $_->{action} ne 'skip' } @{ $self->{tasks} } ];
  1237. if (not @{ $self->{tasks} }) {
  1238. return;
  1239. }
  1240. $self->within_target_do(sub {
  1241. for my $task (@{ $self->{tasks} }) {
  1242. $self->process_task($task);
  1243. }
  1244. });
  1245. debug(2, "Processing tasks... done");
  1246. }
  1247. #===== METHOD ===============================================================
  1248. # Name : process_task()
  1249. # Purpose : process a single task
  1250. # Parameters: $task => the task to process
  1251. # Returns : n/a
  1252. # Throws : fatal error if task fails
  1253. # Comments : Must run from within target directory.
  1254. # : Task involve either creating or deleting dirs and symlinks
  1255. # : an action is set to 'skip' if it is found to be redundant
  1256. #============================================================================
  1257. sub process_task {
  1258. my $self = shift;
  1259. my ($task) = @_;
  1260. if ($task->{action} eq 'create') {
  1261. if ($task->{type} eq 'dir') {
  1262. mkdir($task->{path}, 0777)
  1263. or error("Could not create directory: $task->{path} ($!)");
  1264. return;
  1265. }
  1266. elsif ($task->{type} eq 'link') {
  1267. symlink $task->{source}, $task->{path}
  1268. or error(
  1269. "Could not create symlink: %s => %s ($!)",
  1270. $task->{path},
  1271. $task->{source}
  1272. );
  1273. return;
  1274. }
  1275. }
  1276. elsif ($task->{action} eq 'remove') {
  1277. if ($task->{type} eq 'dir') {
  1278. rmdir $task->{path}
  1279. or error("Could not remove directory: $task->{path} ($!)");
  1280. return;
  1281. }
  1282. elsif ($task->{type} eq 'link') {
  1283. unlink $task->{path}
  1284. or error("Could not remove link: $task->{path} ($!)");
  1285. return;
  1286. }
  1287. }
  1288. elsif ($task->{action} eq 'move') {
  1289. if ($task->{type} eq 'file') {
  1290. # rename() not good enough, since the stow directory
  1291. # might be on a different filesystem to the target.
  1292. move $task->{path}, $task->{dest}
  1293. or error("Could not move $task->{path} -> $task->{dest} ($!)");
  1294. return;
  1295. }
  1296. }
  1297. # Should never happen.
  1298. internal_error("bad task action: $task->{action}");
  1299. }
  1300. #===== METHOD ===============================================================
  1301. # Name : link_task_action()
  1302. # Purpose : finds the link task action for the given path, if there is one
  1303. # Parameters: $path
  1304. # Returns : 'remove', 'create', or '' if there is no action
  1305. # Throws : a fatal exception if an invalid action is found
  1306. # Comments : none
  1307. #============================================================================
  1308. sub link_task_action {
  1309. my $self = shift;
  1310. my ($path) = @_;
  1311. if (! exists $self->{link_task_for}{$path}) {
  1312. debug(4, " link_task_action($path): no task");
  1313. return '';
  1314. }
  1315. my $action = $self->{link_task_for}{$path}->{action};
  1316. internal_error("bad task action: $action")
  1317. unless $action eq 'remove' or $action eq 'create';
  1318. debug(4, " link_task_action($path): link task exists with action $action");
  1319. return $action;
  1320. }
  1321. #===== METHOD ===============================================================
  1322. # Name : dir_task_action()
  1323. # Purpose : finds the dir task action for the given path, if there is one
  1324. # Parameters: $path
  1325. # Returns : 'remove', 'create', or '' if there is no action
  1326. # Throws : a fatal exception if an invalid action is found
  1327. # Comments : none
  1328. #============================================================================
  1329. sub dir_task_action {
  1330. my $self = shift;
  1331. my ($path) = @_;
  1332. if (! exists $self->{dir_task_for}{$path}) {
  1333. debug(4, " dir_task_action($path): no task");
  1334. return '';
  1335. }
  1336. my $action = $self->{dir_task_for}{$path}->{action};
  1337. internal_error("bad task action: $action")
  1338. unless $action eq 'remove' or $action eq 'create';
  1339. debug(4, " dir_task_action($path): dir task exists with action $action");
  1340. return $action;
  1341. }
  1342. #===== METHOD ===============================================================
  1343. # Name : parent_link_scheduled_for_removal()
  1344. # Purpose : determine whether the given path or any parent thereof
  1345. # : is a link scheduled for removal
  1346. # Parameters: $path
  1347. # Returns : Boolean
  1348. # Throws : none
  1349. # Comments : none
  1350. #============================================================================
  1351. sub parent_link_scheduled_for_removal {
  1352. my $self = shift;
  1353. my ($path) = @_;
  1354. my $prefix = '';
  1355. for my $part (split m{/+}, $path) {
  1356. $prefix = join_paths($prefix, $part);
  1357. debug(4, " parent_link_scheduled_for_removal($path): prefix $prefix");
  1358. if (exists $self->{link_task_for}{$prefix} and
  1359. $self->{link_task_for}{$prefix}->{action} eq 'remove') {
  1360. debug(4, " parent_link_scheduled_for_removal($path): link scheduled for removal");
  1361. return 1;
  1362. }
  1363. }
  1364. debug(4, " parent_link_scheduled_for_removal($path): returning false");
  1365. return 0;
  1366. }
  1367. #===== METHOD ===============================================================
  1368. # Name : is_a_link()
  1369. # Purpose : determine if the given path is a current or planned link
  1370. # Parameters: $path
  1371. # Returns : Boolean
  1372. # Throws : none
  1373. # Comments : returns false if an existing link is scheduled for removal
  1374. # : and true if a non-existent link is scheduled for creation
  1375. #============================================================================
  1376. sub is_a_link {
  1377. my $self = shift;
  1378. my ($path) = @_;
  1379. debug(4, " is_a_link($path)");
  1380. if (my $action = $self->link_task_action($path)) {
  1381. if ($action eq 'remove') {
  1382. debug(4, " is_a_link($path): returning 0 (remove action found)");
  1383. return 0;
  1384. }
  1385. elsif ($action eq 'create') {
  1386. debug(4, " is_a_link($path): returning 1 (create action found)");
  1387. return 1;
  1388. }
  1389. }
  1390. if (-l $path) {
  1391. # Check if any of its parent are links scheduled for removal
  1392. # (need this for edge case during unfolding)
  1393. debug(4, " is_a_link($path): is a real link");
  1394. return $self->parent_link_scheduled_for_removal($path) ? 0 : 1;
  1395. }
  1396. debug(4, " is_a_link($path): returning 0");
  1397. return 0;
  1398. }
  1399. #===== METHOD ===============================================================
  1400. # Name : is_a_dir()
  1401. # Purpose : determine if the given path is a current or planned directory
  1402. # Parameters: $path
  1403. # Returns : Boolean
  1404. # Throws : none
  1405. # Comments : returns false if an existing directory is scheduled for removal
  1406. # : and true if a non-existent directory is scheduled for creation
  1407. # : we also need to be sure we are not just following a link
  1408. #============================================================================
  1409. sub is_a_dir {
  1410. my $self = shift;
  1411. my ($path) = @_;
  1412. debug(4, " is_a_dir($path)");
  1413. if (my $action = $self->dir_task_action($path)) {
  1414. if ($action eq 'remove') {
  1415. return 0;
  1416. }
  1417. elsif ($action eq 'create') {
  1418. return 1;
  1419. }
  1420. }
  1421. return 0 if $self->parent_link_scheduled_for_removal($path);
  1422. if (-d $path) {
  1423. debug(4, " is_a_dir($path): real dir");
  1424. return 1;
  1425. }
  1426. debug(4, " is_a_dir($path): returning false");
  1427. return 0;
  1428. }
  1429. #===== METHOD ===============================================================
  1430. # Name : is_a_node()
  1431. # Purpose : determine whether the given path is a current or planned node
  1432. # Parameters: $path
  1433. # Returns : Boolean
  1434. # Throws : none
  1435. # Comments : returns false if an existing node is scheduled for removal
  1436. # : true if a non-existent node is scheduled for creation
  1437. # : we also need to be sure we are not just following a link
  1438. #============================================================================
  1439. sub is_a_node {
  1440. my $self = shift;
  1441. my ($path) = @_;
  1442. debug(4, " is_a_node($path)");
  1443. my $laction = $self->link_task_action($path);
  1444. my $daction = $self->dir_task_action($path);
  1445. if ($laction eq 'remove') {
  1446. if ($daction eq 'remove') {
  1447. internal_error("removing link and dir: $path");
  1448. return 0;
  1449. }
  1450. elsif ($daction eq 'create') {
  1451. # Assume that we're unfolding $path, and that the link
  1452. # removal action is earlier than the dir creation action
  1453. # in the task queue. FIXME: is this a safe assumption?
  1454. return 1;
  1455. }
  1456. else { # no dir action
  1457. return 0;
  1458. }
  1459. }
  1460. elsif ($laction eq 'create') {
  1461. if ($daction eq 'remove') {
  1462. # Assume that we're folding $path, and that the dir
  1463. # removal action is earlier than the link creation action
  1464. # in the task queue. FIXME: is this a safe assumption?
  1465. return 1;
  1466. }
  1467. elsif ($daction eq 'create') {
  1468. internal_error("creating link and dir: $path");
  1469. return 1;
  1470. }
  1471. else { # no dir action
  1472. return 1;
  1473. }
  1474. }
  1475. else {
  1476. # No link action
  1477. if ($daction eq 'remove') {
  1478. return 0;
  1479. }
  1480. elsif ($daction eq 'create') {
  1481. return 1;
  1482. }
  1483. else { # no dir action
  1484. # fall through to below
  1485. }
  1486. }
  1487. return 0 if $self->parent_link_scheduled_for_removal($path);
  1488. if (-e $path) {
  1489. debug(4, " is_a_node($path): really exists");
  1490. return 1;
  1491. }
  1492. debug(4, " is_a_node($path): returning false");
  1493. return 0;
  1494. }
  1495. #===== METHOD ===============================================================
  1496. # Name : read_a_link()
  1497. # Purpose : return the source of a current or planned link
  1498. # Parameters: $path => path to the link target
  1499. # Returns : a string
  1500. # Throws : fatal exception if the given path is not a current or planned
  1501. # : link
  1502. # Comments : none
  1503. #============================================================================
  1504. sub read_a_link {
  1505. my $self = shift;
  1506. my ($path) = @_;
  1507. if (my $action = $self->link_task_action($path)) {
  1508. debug(4, " read_a_link($path): task exists with action $action");
  1509. if ($action eq 'create') {
  1510. return $self->{link_task_for}{$path}->{source};
  1511. }
  1512. elsif ($action eq 'remove') {
  1513. internal_error(
  1514. "read_a_link() passed a path that is scheduled for removal: $path"
  1515. );
  1516. }
  1517. }
  1518. elsif (-l $path) {
  1519. debug(4, " read_a_link($path): real link");
  1520. my $target = readlink $path or error("Could not read link: $path ($!)");
  1521. return $target;
  1522. }
  1523. internal_error("read_a_link() passed a non link path: $path\n");
  1524. }
  1525. #===== METHOD ===============================================================
  1526. # Name : do_link()
  1527. # Purpose : wrap 'link' operation for later processing
  1528. # Parameters: $oldfile => the existing file to link to
  1529. # : $newfile => the file to link
  1530. # Returns : n/a
  1531. # Throws : error if this clashes with an existing planned operation
  1532. # Comments : cleans up operations that undo previous operations
  1533. #============================================================================
  1534. sub do_link {
  1535. my $self = shift;
  1536. my ($oldfile, $newfile) = @_;
  1537. if (exists $self->{dir_task_for}{$newfile}) {
  1538. my $task_ref = $self->{dir_task_for}{$newfile};
  1539. if ($task_ref->{action} eq 'create') {
  1540. if ($task_ref->{type} eq 'dir') {
  1541. internal_error(
  1542. "new link (%s => %s) clashes with planned new directory",
  1543. $newfile,
  1544. $oldfile,
  1545. );
  1546. }
  1547. }
  1548. elsif ($task_ref->{action} eq 'remove') {
  1549. # We may need to remove a directory before creating a link so continue.
  1550. }
  1551. else {
  1552. internal_error("bad task action: $task_ref->{action}");
  1553. }
  1554. }
  1555. if (exists $self->{link_task_for}{$newfile}) {
  1556. my $task_ref = $self->{link_task_for}{$newfile};
  1557. if ($task_ref->{action} eq 'create') {
  1558. if ($task_ref->{source} ne $oldfile) {
  1559. internal_error(
  1560. "new link clashes with planned new link: %s => %s",
  1561. $task_ref->{path},
  1562. $task_ref->{source},
  1563. )
  1564. }
  1565. else {
  1566. debug(1, "LINK: $newfile => $oldfile (duplicates previous action)");
  1567. return;
  1568. }
  1569. }
  1570. elsif ($task_ref->{action} eq 'remove') {
  1571. if ($task_ref->{source} eq $oldfile) {
  1572. # No need to remove a link we are going to recreate
  1573. debug(1, "LINK: $newfile => $oldfile (reverts previous action)");
  1574. $self->{link_task_for}{$newfile}->{action} = 'skip';
  1575. delete $self->{link_task_for}{$newfile};
  1576. return;
  1577. }
  1578. # We may need to remove a link to replace it so continue
  1579. }
  1580. else {
  1581. internal_error("bad task action: $task_ref->{action}");
  1582. }
  1583. }
  1584. # Creating a new link
  1585. debug(1, "LINK: $newfile => $oldfile");
  1586. my $task = {
  1587. action => 'create',
  1588. type => 'link',
  1589. path => $newfile,
  1590. source => $oldfile,
  1591. };
  1592. push @{ $self->{tasks} }, $task;
  1593. $self->{link_task_for}{$newfile} = $task;
  1594. return;
  1595. }
  1596. #===== METHOD ===============================================================
  1597. # Name : do_unlink()
  1598. # Purpose : wrap 'unlink' operation for later processing
  1599. # Parameters: $file => the file to unlink
  1600. # Returns : n/a
  1601. # Throws : error if this clashes with an existing planned operation
  1602. # Comments : will remove an existing planned link
  1603. #============================================================================
  1604. sub do_unlink {
  1605. my $self = shift;
  1606. my ($file) = @_;
  1607. if (exists $self->{link_task_for}{$file}) {
  1608. my $task_ref = $self->{link_task_for}{$file};
  1609. if ($task_ref->{action} eq 'remove') {
  1610. debug(1, "UNLINK: $file (duplicates previous action)");
  1611. return;
  1612. }
  1613. elsif ($task_ref->{action} eq 'create') {
  1614. # Do need to create a link then remove it
  1615. debug(1, "UNLINK: $file (reverts previous action)");
  1616. $self->{link_task_for}{$file}->{action} = 'skip';
  1617. delete $self->{link_task_for}{$file};
  1618. return;
  1619. }
  1620. else {
  1621. internal_error("bad task action: $task_ref->{action}");
  1622. }
  1623. }
  1624. if (exists $self->{dir_task_for}{$file} and $self->{dir_task_for}{$file} eq 'create') {
  1625. internal_error(
  1626. "new unlink operation clashes with planned operation: %s dir %s",
  1627. $self->{dir_task_for}{$file}->{action},
  1628. $file
  1629. );
  1630. }
  1631. # Remove the link
  1632. debug(1, "UNLINK: $file");
  1633. my $source = readlink $file or error("could not readlink $file ($!)");
  1634. my $task = {
  1635. action => 'remove',
  1636. type => 'link',
  1637. path => $file,
  1638. source => $source,
  1639. };
  1640. push @{ $self->{tasks} }, $task;
  1641. $self->{link_task_for}{$file} = $task;
  1642. return;
  1643. }
  1644. #===== METHOD ===============================================================
  1645. # Name : do_mkdir()
  1646. # Purpose : wrap 'mkdir' operation
  1647. # Parameters: $dir => the directory to remove
  1648. # Returns : n/a
  1649. # Throws : fatal exception if operation fails
  1650. # Comments : outputs a message if 'verbose' option is set
  1651. # : does not perform operation if 'simulate' option is set
  1652. # Comments : cleans up operations that undo previous operations
  1653. #============================================================================
  1654. sub do_mkdir {
  1655. my $self = shift;
  1656. my ($dir) = @_;
  1657. if (exists $self->{link_task_for}{$dir}) {
  1658. my $task_ref = $self->{link_task_for}{$dir};
  1659. if ($task_ref->{action} eq 'create') {
  1660. internal_error(
  1661. "new dir clashes with planned new link (%s => %s)",
  1662. $task_ref->{path},
  1663. $task_ref->{source},
  1664. );
  1665. }
  1666. elsif ($task_ref->{action} eq 'remove') {
  1667. # May need to remove a link before creating a directory so continue
  1668. }
  1669. else {
  1670. internal_error("bad task action: $task_ref->{action}");
  1671. }
  1672. }
  1673. if (exists $self->{dir_task_for}{$dir}) {
  1674. my $task_ref = $self->{dir_task_for}{$dir};
  1675. if ($task_ref->{action} eq 'create') {
  1676. debug(1, "MKDIR: $dir (duplicates previous action)");
  1677. return;
  1678. }
  1679. elsif ($task_ref->{action} eq 'remove') {
  1680. debug(1, "MKDIR: $dir (reverts previous action)");
  1681. $self->{dir_task_for}{$dir}->{action} = 'skip';
  1682. delete $self->{dir_task_for}{$dir};
  1683. return;
  1684. }
  1685. else {
  1686. internal_error("bad task action: $task_ref->{action}");
  1687. }
  1688. }
  1689. debug(1, "MKDIR: $dir");
  1690. my $task = {
  1691. action => 'create',
  1692. type => 'dir',
  1693. path => $dir,
  1694. source => undef,
  1695. };
  1696. push @{ $self->{tasks} }, $task;
  1697. $self->{dir_task_for}{$dir} = $task;
  1698. return;
  1699. }
  1700. #===== METHOD ===============================================================
  1701. # Name : do_rmdir()
  1702. # Purpose : wrap 'rmdir' operation
  1703. # Parameters: $dir => the directory to remove
  1704. # Returns : n/a
  1705. # Throws : fatal exception if operation fails
  1706. # Comments : outputs a message if 'verbose' option is set
  1707. # : does not perform operation if 'simulate' option is set
  1708. #============================================================================
  1709. sub do_rmdir {
  1710. my $self = shift;
  1711. my ($dir) = @_;
  1712. if (exists $self->{link_task_for}{$dir}) {
  1713. my $task_ref = $self->{link_task_for}{$dir};
  1714. internal_error(
  1715. "rmdir clashes with planned operation: %s link %s => %s",
  1716. $task_ref->{action},
  1717. $task_ref->{path},
  1718. $task_ref->{source}
  1719. );
  1720. }
  1721. if (exists $self->{dir_task_for}{$dir}) {
  1722. my $task_ref = $self->{link_task_for}{$dir};
  1723. if ($task_ref->{action} eq 'remove') {
  1724. debug(1, "RMDIR $dir (duplicates previous action)");
  1725. return;
  1726. }
  1727. elsif ($task_ref->{action} eq 'create') {
  1728. debug(1, "MKDIR $dir (reverts previous action)");
  1729. $self->{link_task_for}{$dir}->{action} = 'skip';
  1730. delete $self->{link_task_for}{$dir};
  1731. return;
  1732. }
  1733. else {
  1734. internal_error("bad task action: $task_ref->{action}");
  1735. }
  1736. }
  1737. debug(1, "RMDIR $dir");
  1738. my $task = {
  1739. action => 'remove',
  1740. type => 'dir',
  1741. path => $dir,
  1742. source => '',
  1743. };
  1744. push @{ $self->{tasks} }, $task;
  1745. $self->{dir_task_for}{$dir} = $task;
  1746. return;
  1747. }
  1748. #===== METHOD ===============================================================
  1749. # Name : do_mv()
  1750. # Purpose : wrap 'move' operation for later processing
  1751. # Parameters: $src => the file to move
  1752. # : $dst => the path to move it to
  1753. # Returns : n/a
  1754. # Throws : error if this clashes with an existing planned operation
  1755. # Comments : alters contents of package installation image in stow dir
  1756. #============================================================================
  1757. sub do_mv {
  1758. my $self = shift;
  1759. my ($src, $dst) = @_;
  1760. if (exists $self->{link_task_for}{$src}) {
  1761. # I don't *think* this should ever happen, but I'm not
  1762. # 100% sure.
  1763. my $task_ref = $self->{link_task_for}{$src};
  1764. internal_error(
  1765. "do_mv: pre-existing link task for $src; action: %s, source: %s",
  1766. $task_ref->{action}, $task_ref->{source}
  1767. );
  1768. }
  1769. elsif (exists $self->{dir_task_for}{$src}) {
  1770. my $task_ref = $self->{dir_task_for}{$src};
  1771. internal_error(
  1772. "do_mv: pre-existing dir task for %s?! action: %s",
  1773. $src, $task_ref->{action}
  1774. );
  1775. }
  1776. # Remove the link
  1777. debug(1, "MV: $src -> $dst");
  1778. my $task = {
  1779. action => 'move',
  1780. type => 'file',
  1781. path => $src,
  1782. dest => $dst,
  1783. };
  1784. push @{ $self->{tasks} }, $task;
  1785. # FIXME: do we need this for anything?
  1786. #$self->{mv_task_for}{$file} = $task;
  1787. return;
  1788. }
  1789. #############################################################################
  1790. #
  1791. # End of methods; subroutines follow.
  1792. # FIXME: Ideally these should be in a separate module.
  1793. #===== PRIVATE SUBROUTINE ===================================================
  1794. # Name : internal_error()
  1795. # Purpose : output internal error message in a consistent form and die
  1796. # Parameters: $message => error message to output
  1797. # Returns : n/a
  1798. # Throws : n/a
  1799. # Comments : none
  1800. #============================================================================
  1801. sub internal_error {
  1802. my ($format, @args) = @_;
  1803. my $error = sprintf($format, @args);
  1804. my $stacktrace = Carp::longmess();
  1805. die <<EOF;
  1806. $ProgramName: INTERNAL ERROR: $error$stacktrace
  1807. This _is_ a bug. Please submit a bug report so we can fix it! :-)
  1808. See http://www.gnu.org/software/stow/ for how to do this.
  1809. EOF
  1810. }
  1811. =head1 BUGS
  1812. =head1 SEE ALSO
  1813. =cut
  1814. 1;
  1815. # Local variables:
  1816. # mode: perl
  1817. # cperl-indent-level: 4
  1818. # end:
  1819. # vim: ft=perl
  1820. #############################################################################
  1821. # Default global list of ignore regexps follows
  1822. # (automatically appended by the Makefile)
  1823. __DATA__