File Coverage

blib/lib/File/Spec/Link.pm
Criterion Covered Total %
statement 112 126 88.8
branch 42 68 61.7
condition 5 18 27.7
subroutine 28 30 93.3
pod 25 25 100.0
total 212 267 79.4


line stmt bran cond sub pod time code
1             package File::Spec::Link;
2              
3 4     4   78924 use strict;
  4         10  
  4         159  
4 4     4   21 use warnings;
  4         7  
  4         131  
5              
6 4     4   22 use File::Spec ();
  4         12  
  4         92  
7 4     4   21 use base q(File::Spec);
  4         9  
  4         7786  
8              
9             our $VERSION = 0.073;
10              
11             # over-ridden class method - just a debugging wrapper
12             #
13             sub canonpath {
14 152     152 1 251 my($spec, $path) = @_;
15 152 50       8354 return $spec->SUPER::canonpath($path) if $path;
16 0         0 require Carp;
17 0 0       0 Carp::cluck( "canonpath: ",
18             defined $path ? "empty path" : "path undefined"
19             );
20 0         0 return $path;
21             }
22 126 100   126 1 178 sub catdir { my $spec = shift; return @_ ? $spec->SUPER::catdir(@_) : $spec->curdir }
  126         1017  
23              
24             # new class methods - implemented via objects
25             #
26             sub linked {
27 6     6 1 4430 my $self = shift -> new(@_);
28 6 50       21 return unless $self -> follow;
29 5         38 return $self -> path;
30             }
31             sub resolve {
32 5     5 1 1280 my $self = shift -> new(@_);
33 5 100       17 return unless $self -> resolved;
34 4         11 return $self -> path;
35             }
36             sub resolve_all {
37 4     4 1 19 my $self = shift -> new(@_);
38 4 50       14 return unless $self -> resolvedir;
39 4         11 return $self -> path;
40             }
41             sub relative_to_file {
42 2     2 1 47 my($spec, $path) = splice @_, 0, 2;
43 2         8 my $self = $spec -> new(@_);
44 2 50       7 return unless $self -> relative($path);
45 2         6 return $self -> path;
46             }
47             sub chopfile {
48 2     2 1 38 my $self = shift -> new(@_);
49 2 50       8 return $self -> path if length($self -> chop);
50             return
51 0         0 }
52              
53             # other new class methods - implemented via Cwd
54             #
55             sub full_resolve {
56 2     2 1 3109 my($spec, $file) = @_;
57 2         10 my $path = $spec->resolve_path($file);
58 2 50       31 return defined $path ? $path : $spec->resolve_all($file);
59             }
60              
61             sub resolve_path {
62 4     4 1 3783 my($spec, $file) = @_;
63 4         6 my $path = do {
64             local $SIG{__WARN__} = sub {
65 0 0 0 0   0 if ($_[0] =~ /^opendir\b/ and
      0        
      0        
66             $_[0] =~ /\bNot\s+a\s+directory\b/ and
67             $Cwd::VERSION < 2.18 and
68             not -d $file)
69             {
70 0         0 warn <
71             Cwd::abs_path() only works on directories, not: $file
72             Use Cwd v2.18 or later
73             WARN
74             }
75             else {
76 0         0 warn $_[0]
77             }
78 4         43 };
79 4 50       10 eval { require Cwd } && Cwd::abs_path($file)
  4         2008  
80             };
81 4 50       13 return unless $path;
82 4 50       108 return $spec->file_name_is_absolute($file)
83             ? $path : $spec->abs2rel($path);
84             }
85              
86             # old class method - not needed
87             #
88             sub splitlast {
89 0     0 1 0 my $self = shift -> new(@_);
90 0         0 my $last_path = $self -> chop;
91 0         0 return ($self -> path, $last_path);
92             }
93              
94             # object methods:
95             # constructor methods new
96             # access methods path, canonical, vol, dir
97             # updating methods add, pop, push, split, chop
98             # relative, follow, resolved, resolvedir
99              
100             sub new {
101 19     19 1 86 my $self = bless { }, shift;
102 19 50       107 $self -> split(shift) if @_;
103 19         37 return $self;
104             }
105             sub path {
106 107     107 1 336 my $self = shift;
107 107         239 return $self -> catpath( $self->vol, $self->dir, q{} );
108             }
109 10     10 1 16 sub canonical { my $self = shift; return $self -> canonpath( $self -> path ); }
  10         18  
110 107 50   107 1 426 sub vol { my $vol = shift->{vol}; return defined $vol ? $vol : q{} }
  107         411  
111 107     107 1 116 sub dir { my $self = shift; return $self -> catdir( $self -> dirs ); }
  107         1349  
112 108 50   108 1 229 sub dirs { my $dirs = shift->{dirs}; return $dirs ? @{$dirs} : () }
  108         1315  
  108         586  
113            
114             sub add {
115 15     15 1 22 my($self, $file) = @_;
116 15 50       88 if( $file eq $self -> curdir ) { }
    100          
117 1         3 elsif( $file eq $self -> updir ) { $self -> pop }
118 14         27 else { $self -> push($file); }
119 15         108 return;
120             }
121             sub pop {
122 1     1 1 2 my $self = shift;
123 1         3 my @dirs = $self -> dirs;
124 1 50 33     20 if( not @dirs or $dirs[-1] eq $self -> updir ) {
    50 33        
125 0         0 push @{$self->{dirs}}, $self -> updir;
  0         0  
126             }
127             elsif( length $dirs[-1] and $dirs[-1] ne $self -> curdir) {
128 1         2 CORE::pop @{$self->{dirs}}
  1         3  
129             }
130             else {
131 0         0 require Carp;
132 0 0       0 Carp::cluck( "Can't go up from ",
133             length $dirs[-1] ? $dirs[-1]: "empty dir"
134             );
135             }
136 1         3 return;
137             }
138              
139             sub push {
140 49     49 1 628 my $self = shift;
141 49         12098 my $file = shift;
142 49 100       130 CORE::push @{$self->{dirs}}, $file if length $file;
  14         31  
143 49         87 return;
144             }
145             sub split {
146 35     35 1 62 my($self, $path) = @_;
147 35         481 my($vol, $dir, $file) = $self->splitpath($path, 1);
148 35         116 $self->{vol} = $vol;
149 35         682 $self->{dirs} = [ $self->splitdir($dir) ];
150 35         118 $self->push($file);
151 35         534 return;
152             }
153             sub chop {
154 36     36 1 54 my $self = shift;
155 36         62 my $dirs = $self->{dirs};
156 36         48 my $file = '';
157 36         80 while( @$dirs ) {
158 35 100 100     464 last if @$dirs == 1 and not length $dirs->[0]; # path = '/'
159 34 100       138 last if length($file = CORE::pop @$dirs);
160             }
161 36         105 return $file;
162             }
163            
164             sub follow {
165 15     15 1 25 my $self = shift;
166 15         34 my $path = $self -> path;
167 15         566 my $link = readlink $self->path;
168 15 100       76 return $self->relative($link) if defined $link;
169 1         296 require Carp;
170 1 50       4 Carp::confess(
171             "Can't readlink ", $self->path,
172             " : ",
173             (-l $self->path ? "but it is" : "not"),
174             " a link"
175             );
176             }
177            
178             sub relative {
179 16     16 1 31 my($self, $path) = @_;
180 16 100       431 unless( $self->file_name_is_absolute($path) ) {
181 15 50       40 return unless length($self->chop);
182 15         110 $path = $self->catdir($self->path, $path);
183             }
184             # what we want to do here is just set $self->{path}
185             # to be read by $self->path; but would need to
186             # unset $self->{path} whenever it becomes invalid
187 16         61 $self->split($path);
188 16         67 return 1;
189             }
190              
191             sub resolved {
192 24     24 1 30 my $self = shift;
193 24 100       55 my $seen = @_ ? shift : {};
194 24         317 while( -l $self->path ) {
195 10 100       41 return if $seen->{$self->canonical}++;
196 9 50       34 return unless $self->follow;
197             }
198 23         104 return 1;
199             }
200              
201             sub resolvedir {
202 4     4 1 7 my $self = shift;
203 4 50       12 my $seen = @_ ? shift : {};
204 4         10 my @path;
205 4         5 while( 1 ) {
206 19 50       37 return unless $self->resolved($seen);
207 19         46 my $last = $self->chop;
208 19 100       41 last unless length $last;
209 15         34 unshift @path, $last;
210             }
211 4         17 $self->add($_) for @path;
212 4         20 return 1;
213             }
214              
215             1;
216              
217             __END__