Cleanup the File::Spec tmpdir() implementations:
[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)||
ecf68df6 123 unless $path =~ m#^([A-Z]:)?\\\Z(?!\n)#s; # xx\ -> xx
cc23144f 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/
132 # when called from rel2abs()
133 # for ../dirs/
134 my ($vol,$dirs,$file) = $self->splitpath($path);
135 my @dirs = $self->splitdir($dirs);
136 my (@base_dirs, @path_dirs);
137 my $dest = \@base_dirs;
138 for my $dir (@dirs){
139 $dest = \@path_dirs if $dir eq $self->updir;
140 push @$dest, $dir;
141 }
142 # for each .. in @path_dirs pop one item from
143 # @base_dirs
144 while (my $dir = shift @path_dirs){
145 unless ($dir eq $self->updir){
146 unshift @path_dirs, $dir;
147 last;
148 }
149 pop @base_dirs;
150 }
151 $path = $self->catpath(
152 $vol,
153 $self->catdir(@base_dirs, @path_dirs),
154 $file
155 );
cbc7acb0 156 return $path;
270d1e39 157}
158
c27914c9 159=item splitpath
160
161 ($volume,$directories,$file) = File::Spec->splitpath( $path );
162 ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
163
40d020d9 164Splits a path into volume, directory, and filename portions. Assumes that
c27914c9 165the last file is a path unless the path ends in '\\', '\\.', '\\..'
166or $no_file is true. On Win32 this means that $no_file true makes this return
40d020d9 167( $volume, $path, '' ).
c27914c9 168
169Separators accepted are \ and /.
170
171Volumes can be drive letters or UNC sharenames (\\server\share).
172
0994714a 173The results can be passed to L</catpath> to get back a path equivalent to
c27914c9 174(usually identical to) the original path.
175
176=cut
177
178sub splitpath {
179 my ($self,$path, $nofile) = @_;
180 my ($volume,$directory,$file) = ('','','');
181 if ( $nofile ) {
182 $path =~
0994714a 183 m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? )
c27914c9 184 (.*)
1b1e14d3 185 }xs;
c27914c9 186 $volume = $1;
187 $directory = $2;
188 }
189 else {
190 $path =~
0994714a 191 m{^ ( (?: [a-zA-Z]: |
192 (?:\\\\|//)[^\\/]+[\\/][^\\/]+
c27914c9 193 )?
194 )
9c045eb2 195 ( (?:.*[\\\\/](?:\.\.?\Z(?!\n))?)? )
c27914c9 196 (.*)
1b1e14d3 197 }xs;
c27914c9 198 $volume = $1;
199 $directory = $2;
200 $file = $3;
201 }
202
203 return ($volume,$directory,$file);
204}
205
206
207=item splitdir
208
59605c55 209The opposite of L<catdir()|File::Spec/catdir()>.
c27914c9 210
211 @dirs = File::Spec->splitdir( $directories );
212
213$directories must be only the directory portion of the path on systems
214that have the concept of a volume or that have path syntax that differentiates
215files from directories.
216
217Unlike just splitting the directories on the separator, leading empty and
218trailing directory entries can be returned, because these are significant
219on some OSs. So,
220
221 File::Spec->splitdir( "/a/b/c" );
222
223Yields:
224
225 ( '', 'a', 'b', '', 'c', '' )
226
227=cut
228
229sub splitdir {
230 my ($self,$directories) = @_ ;
231 #
232 # split() likes to forget about trailing null fields, so here we
233 # check to be sure that there will not be any before handling the
234 # simple case.
235 #
9c045eb2 236 if ( $directories !~ m|[\\/]\Z(?!\n)| ) {
c27914c9 237 return split( m|[\\/]|, $directories );
238 }
239 else {
240 #
241 # since there was a trailing separator, add a file name to the end,
242 # then do the split, then replace it with ''.
243 #
244 my( @directories )= split( m|[\\/]|, "${directories}dummy" ) ;
245 $directories[ $#directories ]= '' ;
246 return @directories ;
247 }
248}
249
250
251=item catpath
252
253Takes volume, directory and file portions and returns an entire path. Under
254Unix, $volume is ignored, and this is just like catfile(). On other OSs,
255the $volume become significant.
256
257=cut
258
259sub catpath {
260 my ($self,$volume,$directory,$file) = @_;
261
262 # If it's UNC, make sure the glue separator is there, reusing
263 # whatever separator is first in the $volume
264 $volume .= $1
9c045eb2 265 if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\Z(?!\n)@s &&
1b1e14d3 266 $directory =~ m@^[^\\/]@s
c27914c9 267 ) ;
268
269 $volume .= $directory ;
270
271 # If the volume is not just A:, make sure the glue separator is
272 # there, reusing whatever separator is first in the $volume if possible.
9c045eb2 273 if ( $volume !~ m@^[a-zA-Z]:\Z(?!\n)@s &&
274 $volume =~ m@[^\\/]\Z(?!\n)@ &&
0994714a 275 $file =~ m@[^\\/]@
c27914c9 276 ) {
277 $volume =~ m@([\\/])@ ;
278 my $sep = $1 ? $1 : '\\' ;
279 $volume .= $sep ;
280 }
281
282 $volume .= $file ;
283
284 return $volume ;
285}
286
287
c27914c9 288sub abs2rel {
289 my($self,$path,$base) = @_;
290
291 # Clean up $path
292 if ( ! $self->file_name_is_absolute( $path ) ) {
293 $path = $self->rel2abs( $path ) ;
294 }
295 else {
296 $path = $self->canonpath( $path ) ;
297 }
298
299 # Figure out the effective $base and clean it up.
9c1370fb 300 if ( !defined( $base ) || $base eq '' ) {
c27914c9 301 $base = cwd() ;
302 }
9c1370fb 303 elsif ( ! $self->file_name_is_absolute( $base ) ) {
304 $base = $self->rel2abs( $base ) ;
305 }
c27914c9 306 else {
307 $base = $self->canonpath( $base ) ;
308 }
309
310 # Split up paths
9b1c7707 311 my ( undef, $path_directories, $path_file ) =
c27914c9 312 $self->splitpath( $path, 1 ) ;
313
9c045eb2 314 my $base_directories = ($self->splitpath( $base, 1 ))[1] ;
c27914c9 315
316 # Now, remove all leading components that are the same
317 my @pathchunks = $self->splitdir( $path_directories );
318 my @basechunks = $self->splitdir( $base_directories );
319
320 while ( @pathchunks &&
321 @basechunks &&
322 lc( $pathchunks[0] ) eq lc( $basechunks[0] )
323 ) {
324 shift @pathchunks ;
325 shift @basechunks ;
326 }
327
328 # No need to catdir, we know these are well formed.
329 $path_directories = CORE::join( '\\', @pathchunks );
330 $base_directories = CORE::join( '\\', @basechunks );
331
5cefc38b 332 # $base_directories now contains the directories the resulting relative
333 # path must ascend out of before it can descend to $path_directory. So,
c27914c9 334 # replace all names with $parentDir
5cefc38b 335
336 #FA Need to replace between backslashes...
337 $base_directories =~ s|[^\\]+|..|g ;
c27914c9 338
339 # Glue the two together, using a separator if necessary, and preventing an
340 # empty result.
5cefc38b 341
342 #FA Must check that new directories are not empty.
343 if ( $path_directories ne '' && $base_directories ne '' ) {
c27914c9 344 $path_directories = "$base_directories\\$path_directories" ;
345 } else {
346 $path_directories = "$base_directories$path_directories" ;
347 }
348
349 return $self->canonpath(
9b1c7707 350 $self->catpath( "", $path_directories, $path_file )
c27914c9 351 ) ;
352}
353
c27914c9 354
786b702f 355sub rel2abs {
c27914c9 356 my ($self,$path,$base ) = @_;
357
c27914c9 358 if ( ! $self->file_name_is_absolute( $path ) ) {
359
1d7cb664 360 if ( !defined( $base ) || $base eq '' ) {
c27914c9 361 $base = cwd() ;
362 }
1d7cb664 363 elsif ( ! $self->file_name_is_absolute( $base ) ) {
364 $base = $self->rel2abs( $base ) ;
365 }
c27914c9 366 else {
367 $base = $self->canonpath( $base ) ;
368 }
369
9c045eb2 370 my ( $path_directories, $path_file ) =
371 ($self->splitpath( $path, 1 ))[1,2] ;
c27914c9 372
9c045eb2 373 my ( $base_volume, $base_directories ) =
c27914c9 374 $self->splitpath( $base, 1 ) ;
375
376 $path = $self->catpath(
377 $base_volume,
378 $self->catdir( $base_directories, $path_directories ),
379 $path_file
380 ) ;
381 }
382
383 return $self->canonpath( $path ) ;
384}
385
270d1e39 386=back
387
dd9bbc5b 388=head2 Note For File::Spec::Win32 Maintainers
389
390Novell NetWare inherits its File::Spec behaviour from File::Spec::Win32.
391
cbc7acb0 392=head1 SEE ALSO
393
394L<File::Spec>
270d1e39 395
cbc7acb0 396=cut
397
3981;