line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Path::Resolve;
|
2
|
4
|
|
|
4
|
|
8094
|
use warnings;
|
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
232
|
|
3
|
4
|
|
|
4
|
|
27
|
use strict;
|
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
137
|
|
4
|
4
|
|
|
4
|
|
19
|
use Carp;
|
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
375
|
|
5
|
4
|
|
|
4
|
|
3403
|
use Data::Dumper;
|
|
4
|
|
|
|
|
42774
|
|
|
4
|
|
|
|
|
1876
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.0.2';
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
my $isWindows = $^O eq 'MSWin32';
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
if ($isWindows) {
|
12
|
|
|
|
|
|
|
eval "use base 'Path::Resolve::Win'";
|
13
|
|
|
|
|
|
|
*splitPath = *Path::Resolve::Win::splitPath;
|
14
|
|
|
|
|
|
|
} else {
|
15
|
4
|
|
|
4
|
|
35
|
eval "use base 'Path::Resolve::POSIX'";
|
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
1873
|
|
16
|
|
|
|
|
|
|
*splitPath = *Path::Resolve::POSIX::splitPath;
|
17
|
|
|
|
|
|
|
}
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub new {
|
20
|
3
|
|
|
3
|
0
|
26491
|
bless {}, __PACKAGE__;
|
21
|
|
|
|
|
|
|
}
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub dirname {
|
24
|
12
|
|
|
12
|
1
|
29
|
my ($self,$path) = @_;
|
25
|
12
|
|
|
|
|
33
|
my @result = splitPath($path);
|
26
|
12
|
|
|
|
|
26
|
my $root = $result[0],
|
27
|
|
|
|
|
|
|
my $dir = $result[1];
|
28
|
12
|
100
|
66
|
|
|
42
|
if (!$root && !$dir) {
|
29
|
|
|
|
|
|
|
#No dirname whatsoever
|
30
|
1
|
|
|
|
|
5
|
return '.';
|
31
|
|
|
|
|
|
|
}
|
32
|
11
|
100
|
|
|
|
24
|
if ($dir) {
|
33
|
|
|
|
|
|
|
#It has a dirname, strip trailing slash
|
34
|
8
|
|
|
|
|
24
|
$dir = substr $dir,0, length($dir) - 1;
|
35
|
|
|
|
|
|
|
}
|
36
|
11
|
|
|
|
|
62
|
return $root . $dir;
|
37
|
|
|
|
|
|
|
}
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub extname {
|
40
|
50
|
|
|
50
|
1
|
96
|
my ($self,$path) = @_;
|
41
|
50
|
|
|
|
|
109
|
return (splitPath($path))[3];
|
42
|
|
|
|
|
|
|
}
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub basename {
|
45
|
21
|
|
|
21
|
1
|
73
|
my ($self, $path, $ext) = @_;
|
46
|
21
|
|
|
|
|
73
|
my $f = (splitPath($path))[2];
|
47
|
|
|
|
|
|
|
#TODO: make this comparison case-insensitive on windows?
|
48
|
21
|
50
|
33
|
|
|
86
|
if ($ext && (substr $f, -1 * length $ext) eq $ext) {
|
49
|
0
|
|
|
|
|
0
|
$f = substr $f, 0, length($f) - length($ext);
|
50
|
|
|
|
|
|
|
}
|
51
|
21
|
|
|
|
|
109
|
return $f;
|
52
|
|
|
|
|
|
|
}
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
package
|
55
|
|
|
|
|
|
|
Path::Resolve::Win; {
|
56
|
4
|
|
|
4
|
|
41
|
use Carp;
|
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
320
|
|
57
|
4
|
|
|
4
|
|
28
|
use strict;
|
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
227
|
|
58
|
4
|
|
|
4
|
|
25
|
use warnings;
|
|
4
|
|
|
|
|
15
|
|
|
4
|
|
|
|
|
227
|
|
59
|
4
|
|
|
4
|
|
25
|
use Cwd();
|
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
85
|
|
60
|
4
|
|
|
4
|
|
24
|
use Data::Dumper;
|
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
9248
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $splitDeviceRe = qr/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
|
63
|
|
|
|
|
|
|
my $splitTailRe = qr/^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
|
64
|
|
|
|
|
|
|
my $CWD = Cwd::cwd();
|
65
|
|
|
|
|
|
|
|
66
|
0
|
|
|
0
|
|
0
|
sub sep {'\\'};
|
67
|
0
|
|
|
0
|
|
0
|
sub delimiter {';'};
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub splitPath {
|
70
|
0
|
|
|
0
|
|
0
|
my ($filename) = @_;
|
71
|
0
|
|
|
|
|
0
|
my @result = Path::Resolve::Utils::exec($splitDeviceRe,$filename);
|
72
|
0
|
|
0
|
|
|
0
|
my $device = ($result[1] || '') . ($result[2] || '');
|
|
|
|
0
|
|
|
|
|
73
|
0
|
|
0
|
|
|
0
|
my $tail = $result[3] || '';
|
74
|
|
|
|
|
|
|
#Split the tail into dir, basename and extension
|
75
|
0
|
|
|
|
|
0
|
my @result2 = Path::Resolve::Utils::exec($splitTailRe,$tail);
|
76
|
0
|
|
|
|
|
0
|
my $dir = $result2[1];
|
77
|
0
|
|
|
|
|
0
|
my $basename = $result2[2];
|
78
|
0
|
|
|
|
|
0
|
my $ext = $result2[3];
|
79
|
0
|
|
|
|
|
0
|
return ($device, $dir, $basename, $ext);
|
80
|
|
|
|
|
|
|
}
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub parse {
|
83
|
0
|
|
|
0
|
|
0
|
my ($self, $path) = @_;
|
84
|
0
|
0
|
0
|
|
|
0
|
if (!$path || ref $path){
|
85
|
0
|
|
|
|
|
0
|
croak("Parameter 'pathString' must be a string");
|
86
|
|
|
|
|
|
|
}
|
87
|
|
|
|
|
|
|
|
88
|
0
|
|
|
|
|
0
|
my @allParts = splitPath($path);
|
89
|
|
|
|
|
|
|
|
90
|
0
|
0
|
0
|
|
|
0
|
if (!@allParts || scalar @allParts != 4) {
|
91
|
0
|
|
|
|
|
0
|
croak("Invalid path '" . $path . "'");
|
92
|
|
|
|
|
|
|
}
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
return {
|
95
|
0
|
|
|
|
|
0
|
root => $allParts[0],
|
96
|
|
|
|
|
|
|
dir => $allParts[0] . substr ($allParts[1], 0, -1),
|
97
|
|
|
|
|
|
|
base => $allParts[2],
|
98
|
|
|
|
|
|
|
ext => $allParts[3],
|
99
|
|
|
|
|
|
|
name => substr ($allParts[2], 0, length($allParts[2]) - length($allParts[3]))
|
100
|
|
|
|
|
|
|
};
|
101
|
|
|
|
|
|
|
}
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub format {
|
104
|
0
|
|
|
0
|
|
0
|
my ($self, $pathObject) = @_;
|
105
|
0
|
0
|
|
|
|
0
|
if ( ref $pathObject ne 'HASH' ) {
|
106
|
0
|
|
|
|
|
0
|
croak "Parameter 'pathObject' must be an Hash Ref";
|
107
|
|
|
|
|
|
|
}
|
108
|
|
|
|
|
|
|
|
109
|
0
|
|
0
|
|
|
0
|
my $root = $pathObject->{root} || '';
|
110
|
0
|
0
|
0
|
|
|
0
|
if (!defined $root || ref $root) {
|
111
|
0
|
|
|
|
|
0
|
croak 'pathObject->{root} must be a string or undefined';
|
112
|
|
|
|
|
|
|
}
|
113
|
|
|
|
|
|
|
|
114
|
0
|
|
|
|
|
0
|
my $dir = $pathObject->{dir};
|
115
|
0
|
|
0
|
|
|
0
|
my $base = $pathObject->{base} || '';
|
116
|
0
|
0
|
|
|
|
0
|
if (!$dir) {
|
117
|
0
|
|
|
|
|
0
|
return $base;
|
118
|
|
|
|
|
|
|
}
|
119
|
|
|
|
|
|
|
|
120
|
0
|
|
|
|
|
0
|
my $t = substr $dir, length($dir) - 1, 2;
|
121
|
0
|
0
|
|
|
|
0
|
if ($t eq $self->sep) {
|
122
|
0
|
|
|
|
|
0
|
return $dir . $base;
|
123
|
|
|
|
|
|
|
}
|
124
|
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
0
|
return $dir . $self->sep . $base;
|
126
|
|
|
|
|
|
|
}
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub isAbsolute {
|
129
|
0
|
|
|
0
|
|
0
|
my ($self, $path) = @_;
|
130
|
0
|
|
|
|
|
0
|
my @result = Path::Resolve::Utils::exec($splitDeviceRe,$path);
|
131
|
0
|
|
0
|
|
|
0
|
my $device = $result[1] || '';
|
132
|
0
|
|
0
|
|
|
0
|
my $isUnc = $device && $device !~ /^.:/;
|
133
|
|
|
|
|
|
|
##UNC paths are always absolute
|
134
|
0
|
|
0
|
|
|
0
|
return !!$result[2] || $isUnc;
|
135
|
|
|
|
|
|
|
}
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub normalizeUNCRoot {
|
138
|
0
|
|
|
0
|
|
0
|
my $device = shift;
|
139
|
0
|
|
|
|
|
0
|
$device =~ s/^[\\\/]+//;
|
140
|
0
|
|
|
|
|
0
|
$device =~ s/[\\\/]+/\\/g;
|
141
|
0
|
|
|
|
|
0
|
return '\\\\' . $device;
|
142
|
|
|
|
|
|
|
}
|
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub resolve {
|
145
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
146
|
0
|
|
|
|
|
0
|
my $resolvedDevice = '';
|
147
|
0
|
|
|
|
|
0
|
my $resolvedTail = '';
|
148
|
0
|
|
|
|
|
0
|
my $resolvedAbsolute = 0;
|
149
|
0
|
|
|
|
|
0
|
my $isUnc;
|
150
|
0
|
|
|
|
|
0
|
my @args = @_;
|
151
|
0
|
|
|
|
|
0
|
for (my $i = (scalar @args) - 1; $i >= -1; $i--) {
|
152
|
0
|
|
|
|
|
0
|
my $path;
|
153
|
0
|
0
|
|
|
|
0
|
if ($i >= 0) {
|
|
|
0
|
|
|
|
|
|
154
|
0
|
|
|
|
|
0
|
$path = $_[$i];
|
155
|
|
|
|
|
|
|
} elsif (!$resolvedDevice) {
|
156
|
0
|
|
|
|
|
0
|
$path = $CWD;
|
157
|
|
|
|
|
|
|
} else {
|
158
|
|
|
|
|
|
|
##TODO
|
159
|
|
|
|
|
|
|
}
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
## skip empty paths
|
162
|
0
|
0
|
|
|
|
0
|
if (!$path) {
|
163
|
0
|
|
|
|
|
0
|
next;
|
164
|
|
|
|
|
|
|
}
|
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
0
|
my @result = Path::Resolve::Utils::exec($splitDeviceRe,$path);
|
167
|
0
|
|
0
|
|
|
0
|
my $device = $result[1] || '';
|
168
|
0
|
|
0
|
|
|
0
|
$isUnc = $device && $device !~ /^.:/;
|
169
|
0
|
|
|
|
|
0
|
my $isAbsolute = $self->isAbsolute($path);
|
170
|
0
|
|
|
|
|
0
|
my $tail = $result[3];
|
171
|
0
|
0
|
0
|
|
|
0
|
if ($device &&
|
|
|
|
0
|
|
|
|
|
172
|
|
|
|
|
|
|
$resolvedDevice &&
|
173
|
|
|
|
|
|
|
( lc $device ne lc $resolvedDevice ) ) {
|
174
|
|
|
|
|
|
|
#This path points to another device so it is not applicable
|
175
|
0
|
|
|
|
|
0
|
next;
|
176
|
|
|
|
|
|
|
}
|
177
|
|
|
|
|
|
|
|
178
|
0
|
0
|
|
|
|
0
|
if (!$resolvedDevice) {
|
179
|
0
|
|
|
|
|
0
|
$resolvedDevice = $device;
|
180
|
|
|
|
|
|
|
}
|
181
|
0
|
0
|
|
|
|
0
|
if (!$resolvedAbsolute) {
|
182
|
0
|
|
|
|
|
0
|
$resolvedTail = $tail . '\\' . $resolvedTail;
|
183
|
0
|
|
|
|
|
0
|
$resolvedAbsolute = $isAbsolute;
|
184
|
|
|
|
|
|
|
}
|
185
|
0
|
0
|
0
|
|
|
0
|
if ($resolvedDevice && $resolvedAbsolute) {
|
186
|
0
|
|
|
|
|
0
|
last;
|
187
|
|
|
|
|
|
|
}
|
188
|
|
|
|
|
|
|
}
|
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
#Convert slashes to backslashes when `resolvedDevice` points to an UNC
|
191
|
|
|
|
|
|
|
#root. Also squash multiple slashes into a single one where appropriate.
|
192
|
0
|
0
|
|
|
|
0
|
if ($isUnc) {
|
193
|
0
|
|
|
|
|
0
|
$resolvedDevice = normalizeUNCRoot($resolvedDevice);
|
194
|
|
|
|
|
|
|
}
|
195
|
|
|
|
|
|
|
|
196
|
0
|
0
|
|
|
|
0
|
my @resolvedTail = grep {$_ if $_} split /[\\\/]+/, $resolvedTail;
|
|
0
|
|
|
|
|
0
|
|
197
|
0
|
|
|
|
|
0
|
@resolvedTail = Path::Resolve::Utils::normalizeArray(\@resolvedTail,!$resolvedAbsolute);
|
198
|
0
|
|
|
|
|
0
|
$resolvedTail = join '\\',@resolvedTail;
|
199
|
0
|
|
0
|
|
|
0
|
my $ret = ($resolvedDevice . ($resolvedAbsolute ? '\\' : '') . $resolvedTail) || '.';
|
200
|
0
|
|
|
|
|
0
|
return $ret;
|
201
|
|
|
|
|
|
|
}
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub normalize {
|
204
|
0
|
|
|
0
|
|
0
|
my ($self, $path) = @_;
|
205
|
0
|
|
|
|
|
0
|
my @result = Path::Resolve::Utils::exec($splitDeviceRe,$path);
|
206
|
0
|
|
0
|
|
|
0
|
my $device = $result[1] || '';
|
207
|
0
|
|
|
|
|
0
|
my @device = split '',$device;
|
208
|
0
|
|
0
|
|
|
0
|
my $isUnc = @device && $device[1] ne ':';
|
209
|
0
|
|
0
|
|
|
0
|
my $isAbsolute = !!$result[2] || $isUnc;
|
210
|
0
|
|
|
|
|
0
|
my $tail = $result[3];
|
211
|
0
|
|
|
|
|
0
|
my $trailingSlash = $tail =~ /[\\\/]$/;
|
212
|
|
|
|
|
|
|
#If device is a drive letter, we'll normalize to lower case.
|
213
|
0
|
0
|
0
|
|
|
0
|
if (@device && $device[1] eq ':') {
|
214
|
0
|
|
|
|
|
0
|
$device = lc $device;
|
215
|
|
|
|
|
|
|
}
|
216
|
|
|
|
|
|
|
|
217
|
0
|
0
|
|
|
|
0
|
my @tail = grep {$_ if $_} split /[\\\/]+/, $tail;
|
|
0
|
|
|
|
|
0
|
|
218
|
0
|
|
|
|
|
0
|
@tail = Path::Resolve::Utils::normalizeArray(\@tail,!$isAbsolute);
|
219
|
0
|
|
|
|
|
0
|
$tail = join '\\', @tail;
|
220
|
0
|
0
|
0
|
|
|
0
|
if (!$tail && !$isAbsolute) {
|
221
|
0
|
|
|
|
|
0
|
$tail = '.';
|
222
|
|
|
|
|
|
|
}
|
223
|
0
|
0
|
0
|
|
|
0
|
if ($tail && $trailingSlash) {
|
224
|
0
|
|
|
|
|
0
|
$tail .= '\\';
|
225
|
|
|
|
|
|
|
}
|
226
|
|
|
|
|
|
|
#Convert slashes to backslashes when `device` points to an UNC root.
|
227
|
|
|
|
|
|
|
#Also squash multiple slashes into a single one where appropriate.
|
228
|
0
|
0
|
|
|
|
0
|
if ($isUnc) {
|
229
|
0
|
|
|
|
|
0
|
$device = normalizeUNCRoot($device);
|
230
|
|
|
|
|
|
|
}
|
231
|
|
|
|
|
|
|
|
232
|
0
|
0
|
|
|
|
0
|
return $device . ($isAbsolute ? '\\' : '') . $tail;
|
233
|
|
|
|
|
|
|
}
|
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub join {
|
236
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
237
|
0
|
0
|
|
|
|
0
|
my @paths = grep {$_ if $_} @_;
|
|
0
|
|
|
|
|
0
|
|
238
|
0
|
|
|
|
|
0
|
my $joined = join '\\', @paths;
|
239
|
0
|
0
|
0
|
|
|
0
|
if ( @paths && $paths[0] !~ /^[\\\/]{2}[^\\\/]/ ) {
|
240
|
0
|
|
|
|
|
0
|
$joined =~ s/^[\\\/]{2,}/\\/;
|
241
|
|
|
|
|
|
|
}
|
242
|
0
|
|
|
|
|
0
|
return $self->normalize($joined);
|
243
|
|
|
|
|
|
|
}
|
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub _trim {
|
246
|
0
|
|
|
0
|
|
0
|
my @arr = @_;
|
247
|
0
|
|
|
|
|
0
|
my $start = 0;
|
248
|
0
|
|
|
|
|
0
|
foreach my $a (@arr) {
|
249
|
0
|
0
|
|
|
|
0
|
last if ($a ne '');
|
250
|
0
|
|
|
|
|
0
|
$start++;
|
251
|
|
|
|
|
|
|
}
|
252
|
0
|
|
|
|
|
0
|
my $end = scalar @arr - 1;
|
253
|
0
|
|
|
|
|
0
|
while ($end >= 0) {
|
254
|
0
|
0
|
|
|
|
0
|
last if ($arr[$end] ne '');
|
255
|
0
|
|
|
|
|
0
|
$end--;
|
256
|
|
|
|
|
|
|
}
|
257
|
0
|
0
|
|
|
|
0
|
return () if ($start > $end);
|
258
|
0
|
|
|
|
|
0
|
return splice @arr,$start, $end - $start + 1;
|
259
|
|
|
|
|
|
|
}
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub relative {
|
262
|
0
|
|
|
0
|
|
0
|
my ($self, $from, $to) = @_;
|
263
|
0
|
|
|
|
|
0
|
$from = $self->resolve($from);
|
264
|
0
|
|
|
|
|
0
|
$to = $self->resolve($to);
|
265
|
|
|
|
|
|
|
#windows is not case sensitive
|
266
|
0
|
|
|
|
|
0
|
my $lowerFrom = lc $from;
|
267
|
0
|
|
|
|
|
0
|
my $lowerTo = lc $to;
|
268
|
0
|
|
|
|
|
0
|
my @toParts = _trim(split(/\\/, $to));
|
269
|
0
|
|
|
|
|
0
|
my @lowerFromParts = _trim(split(/\\/,$lowerFrom));
|
270
|
0
|
|
|
|
|
0
|
my @lowerToParts = _trim(split(/\\/,$lowerTo));
|
271
|
0
|
|
|
|
|
0
|
my $length = do {
|
272
|
0
|
|
|
|
|
0
|
my $len = scalar @lowerFromParts;
|
273
|
0
|
|
|
|
|
0
|
my $len2 = scalar @lowerToParts;
|
274
|
0
|
0
|
|
|
|
0
|
$len < $len2 ? $len : $len2;
|
275
|
|
|
|
|
|
|
};
|
276
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
0
|
my $samePartsLength = $length;
|
278
|
0
|
|
|
|
|
0
|
for (my $i = 0; $i < $length; $i++) {
|
279
|
0
|
0
|
|
|
|
0
|
if ($lowerFromParts[$i] ne $lowerToParts[$i]) {
|
280
|
0
|
|
|
|
|
0
|
$samePartsLength = $i;
|
281
|
0
|
|
|
|
|
0
|
last;
|
282
|
|
|
|
|
|
|
}
|
283
|
|
|
|
|
|
|
}
|
284
|
0
|
0
|
|
|
|
0
|
if ($samePartsLength == 0) {
|
285
|
0
|
|
|
|
|
0
|
return $to;
|
286
|
|
|
|
|
|
|
}
|
287
|
|
|
|
|
|
|
|
288
|
0
|
|
|
|
|
0
|
my @outputParts = ();
|
289
|
0
|
|
|
|
|
0
|
for (my $i = $samePartsLength; $i < scalar @lowerFromParts; $i++) {
|
290
|
0
|
|
|
|
|
0
|
push @outputParts, ('..');
|
291
|
|
|
|
|
|
|
}
|
292
|
|
|
|
|
|
|
|
293
|
0
|
|
|
|
|
0
|
push @outputParts, ( splice(@toParts,$samePartsLength) );
|
294
|
0
|
|
|
|
|
0
|
return CORE::join '\\',@outputParts;
|
295
|
|
|
|
|
|
|
}
|
296
|
|
|
|
|
|
|
} #end Path::Resolve::Win
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
package
|
300
|
|
|
|
|
|
|
Path::Resolve::POSIX; {
|
301
|
4
|
|
|
4
|
|
72
|
use strict;
|
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
160
|
|
302
|
4
|
|
|
4
|
|
20
|
use warnings;
|
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
154
|
|
303
|
4
|
|
|
4
|
|
20
|
use Carp;
|
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
312
|
|
304
|
4
|
|
|
4
|
|
22
|
use Cwd();
|
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
5491
|
|
305
|
|
|
|
|
|
|
my $CWD = Cwd::cwd();
|
306
|
|
|
|
|
|
|
|
307
|
8
|
|
|
8
|
|
21
|
sub sep {'/'};
|
308
|
1
|
|
|
1
|
|
298
|
sub delimiter {':'};
|
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
#'root' is just a slash, or nothing.
|
311
|
|
|
|
|
|
|
my $splitPathRe = qr/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
|
312
|
|
|
|
|
|
|
sub splitPath {
|
313
|
92
|
|
|
92
|
|
113
|
my ($filename) = @_;
|
314
|
92
|
|
|
|
|
187
|
my @res = Path::Resolve::Utils::exec($splitPathRe,$filename);
|
315
|
92
|
|
|
|
|
413
|
return splice @res, 1;
|
316
|
|
|
|
|
|
|
}
|
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub parse {
|
319
|
9
|
|
|
9
|
|
3165
|
my ($self, $path) = @_;
|
320
|
9
|
50
|
33
|
|
|
64
|
if (!$path || ref $path){
|
321
|
0
|
|
|
|
|
0
|
croak("Parameter 'pathString' must be a string");
|
322
|
|
|
|
|
|
|
}
|
323
|
|
|
|
|
|
|
|
324
|
9
|
|
|
|
|
22
|
my @allParts = splitPath($path);
|
325
|
|
|
|
|
|
|
|
326
|
9
|
50
|
33
|
|
|
52
|
if (!@allParts || scalar @allParts != 4) {
|
327
|
0
|
|
|
|
|
0
|
croak("Invalid path '" . $path . "'");
|
328
|
|
|
|
|
|
|
}
|
329
|
|
|
|
|
|
|
|
330
|
9
|
|
100
|
|
|
27
|
$allParts[1] ||= '';
|
331
|
9
|
|
50
|
|
|
21
|
$allParts[2] ||= '';
|
332
|
9
|
|
100
|
|
|
24
|
$allParts[3] ||= '';
|
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
return {
|
335
|
9
|
|
|
|
|
83
|
root => $allParts[0],
|
336
|
|
|
|
|
|
|
dir => $allParts[0] . substr ($allParts[1], 0, -1),
|
337
|
|
|
|
|
|
|
base => $allParts[2],
|
338
|
|
|
|
|
|
|
ext => $allParts[3],
|
339
|
|
|
|
|
|
|
name => substr ($allParts[2], 0, length($allParts[2]) - length($allParts[3]))
|
340
|
|
|
|
|
|
|
};
|
341
|
|
|
|
|
|
|
}
|
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
sub format {
|
344
|
12
|
|
|
12
|
|
1390
|
my ($self, $pathObject) = @_;
|
345
|
12
|
50
|
|
|
|
40
|
if ( ref $pathObject ne 'HASH' ) {
|
346
|
0
|
|
|
|
|
0
|
croak "Parameter 'pathObject' must be an Hash Ref";
|
347
|
|
|
|
|
|
|
}
|
348
|
|
|
|
|
|
|
|
349
|
12
|
|
100
|
|
|
56
|
my $root = $pathObject->{root} || '';
|
350
|
12
|
50
|
33
|
|
|
56
|
if (!defined $root || ref $root) {
|
351
|
0
|
|
|
|
|
0
|
croak 'pathObject->{root} must be a string or undefined';
|
352
|
|
|
|
|
|
|
}
|
353
|
|
|
|
|
|
|
|
354
|
12
|
100
|
|
|
|
54
|
my $dir = $pathObject->{dir} ? $pathObject->{dir} . $self->sep : '';
|
355
|
12
|
|
100
|
|
|
35
|
my $base = $pathObject->{base} || '';
|
356
|
12
|
|
|
|
|
55
|
return $dir . $base;
|
357
|
|
|
|
|
|
|
}
|
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
sub resolve {
|
360
|
18
|
|
|
18
|
|
1946
|
my $self = shift;
|
361
|
18
|
|
|
|
|
27
|
my $resolvedPath = '';
|
362
|
18
|
|
|
|
|
17
|
my $resolvedAbsolute = 0;
|
363
|
18
|
|
|
|
|
33
|
my @args = @_;
|
364
|
18
|
|
100
|
|
|
101
|
for (my $i = (scalar @args) - 1; $i >= -1 && !$resolvedAbsolute; $i--) {
|
365
|
24
|
100
|
|
|
|
45
|
my $path = ($i >= 0) ? $args[$i] : $CWD;
|
366
|
|
|
|
|
|
|
#Skip empty and invalid entries
|
367
|
24
|
50
|
|
|
|
44
|
if (!$path) {
|
368
|
0
|
|
|
|
|
0
|
next;
|
369
|
|
|
|
|
|
|
}
|
370
|
24
|
|
|
|
|
44
|
$resolvedPath = $path . '/' . $resolvedPath;
|
371
|
24
|
|
|
|
|
142
|
$resolvedAbsolute = $path =~ m/^\//;
|
372
|
|
|
|
|
|
|
}
|
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
#At this point the path should be resolved to a full absolute path, but
|
375
|
|
|
|
|
|
|
#handle relative paths to be safe (might happen when process.cwd() fails)
|
376
|
|
|
|
|
|
|
#Normalize the path
|
377
|
18
|
100
|
|
|
|
55
|
my @resolved = grep { $_ if $_ } split '/', $resolvedPath;
|
|
61
|
|
|
|
|
171
|
|
378
|
18
|
|
|
|
|
52
|
$resolvedPath = join '/', Path::Resolve::Utils::normalizeArray(\@resolved, !$resolvedAbsolute);
|
379
|
18
|
|
50
|
|
|
88
|
return (($resolvedAbsolute ? '/' : '') . $resolvedPath) || '.';
|
380
|
|
|
|
|
|
|
}
|
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub isAbsolute {
|
383
|
101
|
|
|
101
|
|
111
|
my ($self, $path) = @_;
|
384
|
101
|
|
|
|
|
325
|
return $path =~ /^\//;
|
385
|
|
|
|
|
|
|
}
|
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub normalize {
|
388
|
97
|
|
|
97
|
|
773
|
my ($self, $path) = @_;
|
389
|
97
|
|
|
|
|
180
|
my $isAbsolute = $self->isAbsolute($path);
|
390
|
97
|
|
|
|
|
198
|
my $trailingSlash = (substr $path, -1) eq '/';
|
391
|
|
|
|
|
|
|
#Normalize the path
|
392
|
97
|
100
|
|
|
|
264
|
my @resolved = grep { $_ if $_ } split '/', $path;
|
|
337
|
|
|
|
|
886
|
|
393
|
97
|
|
|
|
|
272
|
$path = join '/', Path::Resolve::Utils::normalizeArray(\@resolved, !$isAbsolute);
|
394
|
97
|
100
|
66
|
|
|
267
|
if (!$path && !$isAbsolute) {
|
395
|
20
|
|
|
|
|
26
|
$path = '.';
|
396
|
|
|
|
|
|
|
}
|
397
|
97
|
100
|
100
|
|
|
414
|
if ($path && $trailingSlash) {
|
398
|
8
|
|
|
|
|
14
|
$path .= '/';
|
399
|
|
|
|
|
|
|
}
|
400
|
97
|
100
|
|
|
|
402
|
return ($isAbsolute ? '/' : '') . $path;
|
401
|
|
|
|
|
|
|
}
|
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub join {
|
404
|
92
|
|
|
92
|
|
38054
|
my $self = shift;
|
405
|
92
|
|
|
|
|
207
|
my @paths = @_;
|
406
|
92
|
100
|
|
|
|
141
|
my @norm = grep {$_ if $_} @paths;
|
|
220
|
|
|
|
|
652
|
|
407
|
92
|
|
|
|
|
299
|
return $self->normalize( join('/', @norm) );
|
408
|
|
|
|
|
|
|
}
|
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
sub _trim {
|
411
|
12
|
|
|
12
|
|
14
|
my @arr = @_;
|
412
|
12
|
|
|
|
|
14
|
my $start = 0;
|
413
|
12
|
|
|
|
|
13
|
foreach my $a (@arr) {
|
414
|
11
|
50
|
|
|
|
27
|
last if ($a ne '');
|
415
|
0
|
|
|
|
|
0
|
$start++;
|
416
|
|
|
|
|
|
|
}
|
417
|
12
|
|
|
|
|
14
|
my $end = scalar @arr - 1;
|
418
|
12
|
|
|
|
|
17
|
while ($end >= 0) {
|
419
|
11
|
50
|
|
|
|
22
|
last if ($arr[$end] ne '');
|
420
|
0
|
|
|
|
|
0
|
$end--;
|
421
|
|
|
|
|
|
|
}
|
422
|
12
|
100
|
|
|
|
19
|
return () if ($start > $end);
|
423
|
11
|
|
|
|
|
27
|
return splice @arr,$start, $end - $start + 1;
|
424
|
|
|
|
|
|
|
}
|
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub relative {
|
427
|
6
|
|
|
6
|
|
2072
|
my ($self, $from, $to) = @_;
|
428
|
6
|
|
|
|
|
13
|
$from = substr resolve($self,$from),1;
|
429
|
6
|
|
|
|
|
11
|
$to = substr resolve($self,$to),1;
|
430
|
6
|
|
|
|
|
18
|
my @fromParts = _trim( split('/',$from) );
|
431
|
6
|
|
|
|
|
13
|
my @toParts = _trim( split('/',$to) );
|
432
|
6
|
|
|
|
|
7
|
my $length = do {
|
433
|
6
|
|
|
|
|
6
|
my $len = scalar @fromParts;
|
434
|
6
|
|
|
|
|
5
|
my $len2 = scalar @toParts;
|
435
|
6
|
100
|
|
|
|
13
|
$len < $len2 ? $len : $len2;
|
436
|
|
|
|
|
|
|
};
|
437
|
|
|
|
|
|
|
|
438
|
6
|
|
|
|
|
5
|
my $samePartsLength = $length;
|
439
|
6
|
|
|
|
|
14
|
for (my $i = 0; $i < $length; $i++) {
|
440
|
7
|
100
|
|
|
|
19
|
if ($fromParts[$i] ne $toParts[$i]) {
|
441
|
2
|
|
|
|
|
2
|
$samePartsLength = $i;
|
442
|
2
|
|
|
|
|
2
|
last;
|
443
|
|
|
|
|
|
|
}
|
444
|
|
|
|
|
|
|
}
|
445
|
|
|
|
|
|
|
|
446
|
6
|
|
|
|
|
7
|
my @outputParts = ();
|
447
|
6
|
|
|
|
|
12
|
for (my $i = $samePartsLength; $i < scalar @fromParts; $i++) {
|
448
|
4
|
|
|
|
|
16
|
push @outputParts, ('..');
|
449
|
|
|
|
|
|
|
}
|
450
|
|
|
|
|
|
|
|
451
|
6
|
|
|
|
|
8
|
push @outputParts, ( splice(@toParts,$samePartsLength) );
|
452
|
6
|
|
|
|
|
19
|
return CORE::join '/',@outputParts;
|
453
|
|
|
|
|
|
|
}
|
454
|
|
|
|
|
|
|
} #end Path::Resolve::Posix
|
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
package
|
457
|
|
|
|
|
|
|
Path::Resolve::Utils; {
|
458
|
4
|
|
|
4
|
|
48
|
use strict;
|
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
218
|
|
459
|
4
|
|
|
4
|
|
24
|
use warnings;
|
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
1333
|
|
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
#==========================================================================
|
462
|
|
|
|
|
|
|
# javascript like exec function
|
463
|
|
|
|
|
|
|
# we can do it the perl way but for fun I want to emulate node path module
|
464
|
|
|
|
|
|
|
# the way it is :)
|
465
|
|
|
|
|
|
|
#==========================================================================
|
466
|
|
|
|
|
|
|
sub exec {
|
467
|
92
|
|
|
92
|
|
116
|
my ($expr,$string) = @_;
|
468
|
92
|
|
|
|
|
1688
|
my @m = $string =~ $expr;
|
469
|
92
|
50
|
|
|
|
280
|
if (@m){
|
470
|
92
|
|
|
|
|
445
|
unshift @m, substr $string,$-[0],$+[0];
|
471
|
|
|
|
|
|
|
}
|
472
|
92
|
|
|
|
|
404
|
return @m;
|
473
|
|
|
|
|
|
|
}
|
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
sub normalizeArray {
|
476
|
115
|
|
|
115
|
|
150
|
my ($parts, $allowAboveRoot) = @_;
|
477
|
|
|
|
|
|
|
#if the path tries to go above the root, `up` ends up > 0
|
478
|
115
|
|
|
|
|
105
|
my @parts = @{$parts};
|
|
115
|
|
|
|
|
222
|
|
479
|
115
|
|
|
|
|
117
|
my $up = 0;
|
480
|
115
|
|
|
|
|
323
|
for (my $i = (scalar @parts) - 1; $i >= 0; $i--) {
|
481
|
271
|
|
|
|
|
835
|
my $last = $parts->[$i];
|
482
|
271
|
100
|
|
|
|
845
|
if ($last eq '.') {
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
483
|
58
|
|
|
|
|
152
|
splice @parts, $i, 1;
|
484
|
|
|
|
|
|
|
} elsif ($last eq '..') {
|
485
|
58
|
|
|
|
|
87
|
splice @parts, $i, 1;
|
486
|
58
|
|
|
|
|
132
|
$up++;
|
487
|
|
|
|
|
|
|
} elsif ($up) {
|
488
|
21
|
|
|
|
|
25
|
splice @parts, $i, 1;
|
489
|
21
|
|
|
|
|
51
|
$up--;
|
490
|
|
|
|
|
|
|
}
|
491
|
|
|
|
|
|
|
}
|
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
#if the path is allowed to go above the root, restore leading ..s
|
494
|
115
|
100
|
|
|
|
191
|
if ($allowAboveRoot) {
|
495
|
68
|
|
|
|
|
150
|
while ($up--) {
|
496
|
24
|
|
|
|
|
62
|
unshift @parts, ('..');
|
497
|
|
|
|
|
|
|
}
|
498
|
|
|
|
|
|
|
}
|
499
|
115
|
|
|
|
|
321
|
return @parts;
|
500
|
|
|
|
|
|
|
}
|
501
|
|
|
|
|
|
|
} # end Path::Resolve::Utils
|
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
1;
|
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
__END__
|