| 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__
|