File Coverage

blib/lib/File/Spec/Link.pm
Criterion Covered Total %
statement 106 120 88.3
branch 42 68 61.7
condition 5 18 27.7
subroutine 26 28 92.8
pod 25 25 100.0
total 204 259 78.7


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