File Coverage

blib/lib/Stow/Util.pm
Criterion Covered Total %
statement 63 70 90.0
branch 13 20 65.0
condition 3 3 100.0
subroutine 14 15 93.3
pod 4 10 40.0
total 97 118 82.2


line stmt bran cond sub pod time code
1             # This file is part of GNU Stow.
2             #
3             # GNU Stow is free software: you can redistribute it and/or modify it
4             # under the terms of the GNU General Public License as published by
5             # the Free Software Foundation, either version 3 of the License, or
6             # (at your option) any later version.
7             #
8             # GNU Stow is distributed in the hope that it will be useful, but
9             # WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11             # General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program. If not, see https://www.gnu.org/licenses/.
15              
16             package Stow::Util;
17              
18             =head1 NAME
19              
20             Stow::Util - general utilities
21              
22             =head1 SYNOPSIS
23              
24             use Stow::Util qw(debug set_debug_level error ...);
25              
26             =head1 DESCRIPTION
27              
28             Supporting utility routines for L.
29              
30             =cut
31              
32 18     18   263989 use strict;
  18         50  
  18         685  
33 18     18   101 use warnings;
  18         67  
  18         1107  
34              
35 18     18   106 use File::Spec;
  18         29  
  18         473  
36 18     18   2361 use POSIX qw(getcwd);
  18         36894  
  18         98  
37              
38 18     18   10994 use base qw(Exporter);
  18         32  
  18         25340  
39             our @EXPORT_OK = qw(
40             error debug set_debug_level set_test_mode
41             join_paths parent canon_path restore_cwd
42             adjust_dotfile unadjust_dotfile
43             );
44              
45             our $ProgramName = 'stow';
46             our $VERSION = '2.4.1';
47              
48             #############################################################################
49             #
50             # General Utilities: nothing stow specific here.
51             #
52             #############################################################################
53              
54             =head1 IMPORTABLE SUBROUTINES
55              
56             =head2 error($format, @args)
57              
58             Outputs an error message in a consistent form and then dies.
59              
60             =cut
61              
62             sub error {
63 0     0 1 0 my ($format, @args) = @_;
64 0         0 die "$ProgramName: ERROR: " . sprintf($format, @args) . "\n";
65             }
66              
67             =head2 set_debug_level($level)
68              
69             Sets verbosity level for C.
70              
71             =cut
72              
73             our $debug_level = 0;
74              
75             sub set_debug_level {
76 90     90 1 239 my ($level) = @_;
77 90         194 $debug_level = $level;
78             }
79              
80             =head2 set_test_mode($on_or_off)
81              
82             Sets testmode on or off.
83              
84             =cut
85              
86             our $test_mode = 0;
87              
88             sub set_test_mode {
89 90     90 1 219 my ($on_or_off) = @_;
90 90 50       224 if ($on_or_off) {
91 90         177 $test_mode = 1;
92             }
93             else {
94 0         0 $test_mode = 0;
95             }
96             }
97              
98             =head2 debug($level[, $indent_level], $msg)
99              
100             Logs to STDERR based on C<$debug_level> setting. C<$level> is the
101             minimum verbosity level required to output C<$msg>. All output is to
102             STDERR to preserve backward compatibility, except for in test mode,
103             when STDOUT is used instead. In test mode, the verbosity can be
104             overridden via the C environment variable.
105              
106             Verbosity rules:
107              
108             =over 4
109              
110             =item 0: errors only
111              
112             =item >= 1: print operations: LINK/UNLINK/MKDIR/RMDIR/MV
113              
114             =item >= 2: print operation exceptions
115              
116             e.g. "_this_ already points to _that_", skipping, deferring,
117             overriding, fixing invalid links
118              
119             =item >= 3: print trace detail: trace: stow/unstow/package/contents/node
120              
121             =item >= 4: debug helper routines
122              
123             =item >= 5: debug ignore lists
124              
125             =back
126              
127             =cut
128              
129             sub debug {
130 90877     90877 1 95134 my $level = shift;
131 90877         88679 my $indent_level;
132             # Maintain backwards-compatibility in case anyone's relying on this.
133 90877 50       157190 $indent_level = $_[0] =~ /^\d+$/ ? shift : 0;
134 90877         97155 my $msg = shift;
135 90877 50       147750 if ($debug_level >= $level) {
136 0         0 my $indent = ' ' x $indent_level;
137 0 0       0 if ($test_mode) {
138 0         0 print "# $indent$msg\n";
139             }
140             else {
141 0         0 warn "$indent$msg\n";
142             }
143             }
144             }
145              
146             #===== METHOD ===============================================================
147             # Name : join_paths()
148             # Purpose : concatenates given paths
149             # Parameters: path1, path2, ... => paths
150             # Returns : concatenation of given paths
151             # Throws : n/a
152             # Comments : Factors out some redundant path elements:
153             # : '//' => '/', and 'a/b/../c' => 'a/c'. We need this function
154             # : with this behaviour, even though b could be a symlink to
155             # : elsewhere, as noted in the perldoc for File::Spec->canonpath().
156             # : This behaviour is deliberately different to
157             # : Stow::Util::canon_path(), because the way join_paths() is used
158             # : relies on this. Firstly, there is no guarantee that the paths
159             # : exist, so a filesystem check is inappropriate.
160             # :
161             # : For example, it's used to determine the path from the target
162             # : directory to a symlink destination. So if a symlink
163             # : path/to/target/a/b/c points to ../../../stow/pkg/a/b/c,
164             # : then joining path/to/target/a/b with ../../../stow/pkg/a/b/c
165             # : yields path/to/stow/pkg/a/b/c, and it's crucial that the
166             # : path/to/stow prefix matches a recognisable stow directory.
167             #============================================================================
168             sub join_paths {
169 11004     11004 0 350020 my @paths = @_;
170              
171 11004         29523 debug(5, 5, "| Joining: @paths");
172 11004         14134 my $result = '';
173 11004         14899 for my $part (@paths) {
174 22648 100       33867 next if ! length $part; # probably shouldn't happen?
175 21716         44654 $part = File::Spec->canonpath($part);
176              
177 21716 100       33974 if (substr($part, 0, 1) eq '/') {
178 1474         1973 $result = $part; # absolute path, so ignore all previous parts
179             }
180             else {
181 20242 100 100     45100 $result .= '/' if length $result && $result ne '/';
182 20242         27718 $result .= $part;
183             }
184 21716         33851 debug(7, 6, "| Join now: $result");
185             }
186 11004         20690 debug(6, 5, "| Joined: $result");
187              
188             # Need this to remove any initial ./
189 11004         21323 $result = File::Spec->canonpath($result);
190              
191             # remove foo/..
192 11004         24237 1 while $result =~ s,(^|/)(?!\.\.)[^/]+/\.\.(/|$),$1,;
193 11004         22481 debug(6, 5, "| After .. removal: $result");
194              
195 11004         19938 $result = File::Spec->canonpath($result);
196 11004         22881 debug(5, 5, "| Final join: $result");
197              
198 11004         38196 return $result;
199             }
200              
201             #===== METHOD ===============================================================
202             # Name : parent
203             # Purpose : find the parent of the given path
204             # Parameters: @path => components of the path
205             # Returns : returns a path string
206             # Throws : n/a
207             # Comments : allows you to send multiple chunks of the path
208             # : (this feature is currently not used)
209             #============================================================================
210             sub parent {
211 197     197 0 368146 my @path = @_;
212 197         473 my $path = join '/', @_;
213 197         889 my @elts = split m{/+}, $path;
214 197         353 pop @elts;
215 197         725 return join '/', @elts;
216             }
217              
218             #===== METHOD ===============================================================
219             # Name : canon_path
220             # Purpose : find absolute canonical path of given path
221             # Parameters: $path
222             # Returns : absolute canonical path
223             # Throws : n/a
224             # Comments : is this significantly different from File::Spec->rel2abs?
225             #============================================================================
226             sub canon_path {
227 193     193 0 34239 my ($path) = @_;
228              
229 193         1561 my $cwd = getcwd();
230 193 50       1713 chdir($path) or error("canon_path: cannot chdir to $path from $cwd");
231 193         927 my $canon_path = getcwd();
232 193         555 restore_cwd($cwd);
233              
234 193         518 return $canon_path;
235             }
236              
237             sub restore_cwd {
238 329     329 0 642 my ($prev) = @_;
239 329 50       3485 chdir($prev) or error("Your current directory $prev seems to have vanished");
240             }
241              
242             sub adjust_dotfile {
243 81     81 0 165026 my ($pkg_node) = @_;
244 81         415 (my $adjusted = $pkg_node) =~ s/^dot-([^.])/.$1/;
245 81         243 return $adjusted;
246             }
247              
248             # Needed when unstowing with --compat and --dotfiles
249             sub unadjust_dotfile {
250 9     9 0 6303 my ($target_node) = @_;
251 9 100       44 return $target_node if $target_node =~ /^\.\.?$/;
252 7         46 (my $adjusted = $target_node) =~ s/^\./dot-/;
253 7         18 return $adjusted;
254             }
255              
256             =head1 BUGS
257              
258             =head1 SEE ALSO
259              
260             =cut
261              
262             1;
263              
264             # Local variables:
265             # mode: perl
266             # end:
267             # vim: ft=perl