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