Extra noise from File::Spec.
[p5sagit/p5-mst-13.2.git] / lib / File / Spec / Win32.pm
CommitLineData
270d1e39 1package File::Spec::Win32;
2
cbc7acb0 3use strict;
c27914c9 4use Cwd;
07824bd1 5
b4296952 6use vars qw(@ISA $VERSION);
cbc7acb0 7require File::Spec::Unix;
b4296952 8
07824bd1 9$VERSION = '1.4';
b4296952 10
cbc7acb0 11@ISA = qw(File::Spec::Unix);
12
270d1e39 13=head1 NAME
14
15File::Spec::Win32 - methods for Win32 file specs
16
17=head1 SYNOPSIS
18
cbc7acb0 19 require File::Spec::Win32; # Done internally by File::Spec if needed
270d1e39 20
21=head1 DESCRIPTION
22
23See File::Spec::Unix for a documentation of the methods provided
24there. This package overrides the implementation of these methods, not
25the semantics.
26
bbc7dcd2 27=over 4
270d1e39 28
cbc7acb0 29=item devnull
270d1e39 30
cbc7acb0 31Returns a string representation of the null device.
270d1e39 32
cbc7acb0 33=cut
270d1e39 34
cbc7acb0 35sub devnull {
36 return "nul";
37}
270d1e39 38
cbc7acb0 39=item tmpdir
270d1e39 40
cbc7acb0 41Returns a string representation of the first existing directory
42from the following list:
270d1e39 43
cbc7acb0 44 $ENV{TMPDIR}
45 $ENV{TEMP}
46 $ENV{TMP}
dd9bbc5b 47 SYS:/temp
28747828 48 C:/temp
cbc7acb0 49 /tmp
50 /
51
07824bd1 52The SYS:/temp is preferred in Novell NetWare (the File::Spec::Win32
53is used also for NetWare).
dd9bbc5b 54
55Since Perl 5.8.0, if running under taint mode, and if the environment
a384e9e1 56variables are tainted, they are not used.
57
cbc7acb0 58=cut
270d1e39 59
cbc7acb0 60my $tmpdir;
61sub tmpdir {
62 return $tmpdir if defined $tmpdir;
270d1e39 63 my $self = shift;
07824bd1 64 $tmpdir = $self->_tmpdir( @ENV{qw(TMPDIR TEMP TMP)},
65 'SYS:/temp',
66 'C:/temp',
67 '/tmp',
68 '/' );
cbc7acb0 69}
70
46726cbe 71sub case_tolerant {
72 return 1;
73}
74
cbc7acb0 75sub file_name_is_absolute {
76 my ($self,$file) = @_;
1b1e14d3 77 return scalar($file =~ m{^([a-z]:)?[\\/]}is);
270d1e39 78}
79
80=item catfile
81
82Concatenate one or more directory names and a filename to form a
83complete path ending with a filename
84
85=cut
86
87sub catfile {
cbc7acb0 88 my $self = shift;
02961b52 89 my $file = $self->canonpath(pop @_);
270d1e39 90 return $file unless @_;
91 my $dir = $self->catdir(@_);
cbc7acb0 92 $dir .= "\\" unless substr($dir,-1) eq "\\";
270d1e39 93 return $dir.$file;
94}
95
96sub path {
270d1e39 97 my $path = $ENV{'PATH'} || $ENV{'Path'} || $ENV{'path'};
98 my @path = split(';',$path);
cbc7acb0 99 foreach (@path) { $_ = '.' if $_ eq '' }
100 return @path;
270d1e39 101}
102
103=item canonpath
104
105No physical check on the filesystem, but a logical cleanup of a
106path. On UNIX eliminated successive slashes and successive "/.".
cc23144f 107On Win32 makes
108
109 dir1\dir2\dir3\..\..\dir4 -> \dir\dir4 and even
110 dir1\dir2\dir3\...\dir4 -> \dir\dir4
270d1e39 111
112=cut
113
114sub canonpath {
0994714a 115 my ($self,$path) = @_;
cc23144f 116 my $orig_path = $path;
1b1e14d3 117 $path =~ s/^([a-z]:)/\u$1/s;
270d1e39 118 $path =~ s|/|\\|g;
ecf68df6 119 $path =~ s|([^\\])\\+|$1\\|g; # xx\\\\xx -> xx\xx
120 $path =~ s|(\\\.)+\\|\\|g; # xx\.\.\xx -> xx\xx
121 $path =~ s|^(\.\\)+||s unless $path eq ".\\"; # .\xx -> xx
9c045eb2 122 $path =~ s|\\\Z(?!\n)||
e021ab8e 123 unless $path =~ m{^([A-Z]:)?\\\Z(?!\n)}s; # xx\ -> xx
124 # xx1/xx2/xx3/../../xx -> xx1/xx
125 $path =~ s|\\\.\.\.\\|\\\.\.\\\.\.\\|g; # \...\ is 2 levels up
126 $path =~ s|^\.\.\.\\|\.\.\\\.\.\\|g; # ...\ is 2 levels up
127 return $path if $path =~ m|^\.\.|; # skip relative paths
128 return $path unless $path =~ /\.\./; # too few .'s to cleanup
129 return $path if $path =~ /\.\.\.\./; # too many .'s to cleanup
130 return $path if $orig_path =~ m|^\Q/../\E|
131 and $orig_path =~ m|\/$|; # don't do /../dirs/ when called
132 # from rel2abs() for ../dirs/
133 1 while $path =~ s{^\\\.\.}{}; # \..\xx -> \xx
134
135 my ($vol,$dirs,$file) = $self->splitpath($path);
136 my @dirs = $self->splitdir($dirs);
137 my (@base_dirs, @path_dirs);
138 my $dest = \@base_dirs;
139 for my $dir (@dirs){
140 $dest = \@path_dirs if $dir eq $self->updir;
141 push @$dest, $dir;
142 }
143 # for each .. in @path_dirs pop one item from
144 # @base_dirs
145 while (my $dir = shift @path_dirs){
146 unless ($dir eq $self->updir){
147 unshift @path_dirs, $dir;
148 last;
cc23144f 149 }
e021ab8e 150 pop @base_dirs;
151 }
152 $path = $self->catpath(
153 $vol,
154 $self->catdir(@base_dirs, @path_dirs),
155 $file
156 );
cbc7acb0 157 return $path;
270d1e39 158}
159
c27914c9 160=item splitpath
161
162 ($volume,$directories,$file) = File::Spec->splitpath( $path );
163 ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
164
40d020d9 165Splits a path into volume, directory, and filename portions. Assumes that
c27914c9 166the last file is a path unless the path ends in '\\', '\\.', '\\..'
167or $no_file is true. On Win32 this means that $no_file true makes this return
40d020d9 168( $volume, $path, '' ).
c27914c9 169
170Separators accepted are \ and /.
171
172Volumes can be drive letters or UNC sharenames (\\server\share).
173
0994714a 174The results can be passed to L</catpath> to get back a path equivalent to
c27914c9 175(usually identical to) the original path.
176
177=cut
178
179sub splitpath {
180 my ($self,$path, $nofile) = @_;
181 my ($volume,$directory,$file) = ('','','');
182 if ( $nofile ) {
183 $path =~
0994714a 184 m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? )
c27914c9 185 (.*)
1b1e14d3 186 }xs;
c27914c9 187 $volume = $1;
188 $directory = $2;
189 }
190 else {
191 $path =~
0994714a 192 m{^ ( (?: [a-zA-Z]: |
193 (?:\\\\|//)[^\\/]+[\\/][^\\/]+
c27914c9 194 )?
195 )
9c045eb2 196 ( (?:.*[\\\\/](?:\.\.?\Z(?!\n))?)? )
c27914c9 197 (.*)
1b1e14d3 198 }xs;
c27914c9 199 $volume = $1;
200 $directory = $2;
201 $file = $3;
202 }
203
204 return ($volume,$directory,$file);
205}
206
207
208=item splitdir
209
59605c55 210The opposite of L<catdir()|File::Spec/catdir()>.
c27914c9 211
212 @dirs = File::Spec->splitdir( $directories );
213
214$directories must be only the directory portion of the path on systems
215that have the concept of a volume or that have path syntax that differentiates
216files from directories.
217
218Unlike just splitting the directories on the separator, leading empty and
219trailing directory entries can be returned, because these are significant
220on some OSs. So,
221
222 File::Spec->splitdir( "/a/b/c" );
223
224Yields:
225
226 ( '', 'a', 'b', '', 'c', '' )
227
228=cut
229
230sub splitdir {
231 my ($self,$directories) = @_ ;
232 #
233 # split() likes to forget about trailing null fields, so here we
234 # check to be sure that there will not be any before handling the
235 # simple case.
236 #
9c045eb2 237 if ( $directories !~ m|[\\/]\Z(?!\n)| ) {
c27914c9 238 return split( m|[\\/]|, $directories );
239 }
240 else {
241 #
242 # since there was a trailing separator, add a file name to the end,
243 # then do the split, then replace it with ''.
244 #
245 my( @directories )= split( m|[\\/]|, "${directories}dummy" ) ;
246 $directories[ $#directories ]= '' ;
247 return @directories ;
248 }
249}
250
251
252=item catpath
253
254Takes volume, directory and file portions and returns an entire path. Under
255Unix, $volume is ignored, and this is just like catfile(). On other OSs,
256the $volume become significant.
257
258=cut
259
260sub catpath {
261 my ($self,$volume,$directory,$file) = @_;
262
263 # If it's UNC, make sure the glue separator is there, reusing
264 # whatever separator is first in the $volume
265 $volume .= $1
9c045eb2 266 if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\Z(?!\n)@s &&
1b1e14d3 267 $directory =~ m@^[^\\/]@s
c27914c9 268 ) ;
269
270 $volume .= $directory ;
271
272 # If the volume is not just A:, make sure the glue separator is
273 # there, reusing whatever separator is first in the $volume if possible.
9c045eb2 274 if ( $volume !~ m@^[a-zA-Z]:\Z(?!\n)@s &&
275 $volume =~ m@[^\\/]\Z(?!\n)@ &&
0994714a 276 $file =~ m@[^\\/]@
c27914c9 277 ) {
278 $volume =~ m@([\\/])@ ;
279 my $sep = $1 ? $1 : '\\' ;
280 $volume .= $sep ;
281 }
282
283 $volume .= $file ;
284
285 return $volume ;
286}
287
288
c27914c9 289sub abs2rel {
290 my($self,$path,$base) = @_;
e021ab8e 291 $base = $self->cwd() unless defined $base and length $base;
c27914c9 292
e021ab8e 293 for ($path, $base) {
294 $_ = $self->canonpath($self->rel2abs($_));
c27914c9 295 }
e021ab8e 296 my ($path_volume, $path_directories) = $self->splitpath($path, 1) ;
297 my ($base_volume, $base_directories) = $self->splitpath($base, 1);
c27914c9 298
e021ab8e 299 if ($path_volume and not $base_volume) {
300 ($base_volume) = $self->splitpath($self->cwd);
c27914c9 301 }
c27914c9 302
e021ab8e 303 # Can't relativize across volumes
304 return $path unless $path_volume eq $base_volume;
c27914c9 305
306 # Now, remove all leading components that are the same
307 my @pathchunks = $self->splitdir( $path_directories );
308 my @basechunks = $self->splitdir( $base_directories );
309
310 while ( @pathchunks &&
311 @basechunks &&
312 lc( $pathchunks[0] ) eq lc( $basechunks[0] )
313 ) {
314 shift @pathchunks ;
315 shift @basechunks ;
316 }
317
e021ab8e 318 my $result_dirs = $self->catdir( ($self->updir) x @basechunks, @pathchunks );
c27914c9 319
e021ab8e 320 return $self->canonpath( $self->catpath('', $result_dirs, '') );
c27914c9 321}
322
c27914c9 323
786b702f 324sub rel2abs {
c27914c9 325 my ($self,$path,$base ) = @_;
326
c27914c9 327 if ( ! $self->file_name_is_absolute( $path ) ) {
328
1d7cb664 329 if ( !defined( $base ) || $base eq '' ) {
e021ab8e 330 $base = $self->cwd() ;
c27914c9 331 }
1d7cb664 332 elsif ( ! $self->file_name_is_absolute( $base ) ) {
333 $base = $self->rel2abs( $base ) ;
334 }
c27914c9 335 else {
336 $base = $self->canonpath( $base ) ;
337 }
338
9c045eb2 339 my ( $path_directories, $path_file ) =
340 ($self->splitpath( $path, 1 ))[1,2] ;
c27914c9 341
9c045eb2 342 my ( $base_volume, $base_directories ) =
c27914c9 343 $self->splitpath( $base, 1 ) ;
344
345 $path = $self->catpath(
346 $base_volume,
347 $self->catdir( $base_directories, $path_directories ),
348 $path_file
349 ) ;
350 }
351
352 return $self->canonpath( $path ) ;
353}
354
270d1e39 355=back
356
dd9bbc5b 357=head2 Note For File::Spec::Win32 Maintainers
358
359Novell NetWare inherits its File::Spec behaviour from File::Spec::Win32.
360
cbc7acb0 361=head1 SEE ALSO
362
363L<File::Spec>
270d1e39 364
cbc7acb0 365=cut
366
3671;