sync version numbers in File::Spec with the ones on CPAN
[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
8$VERSION = '1.1';
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
26=over
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}
46 /tmp
47 /
48
49=cut
270d1e39 50
cbc7acb0 51my $tmpdir;
52sub tmpdir {
53 return $tmpdir if defined $tmpdir;
270d1e39 54 my $self = shift;
cbc7acb0 55 foreach (@ENV{qw(TMPDIR TEMP TMP)}, qw(/tmp /)) {
56 next unless defined && -d;
57 $tmpdir = $_;
58 last;
270d1e39 59 }
cbc7acb0 60 $tmpdir = '' unless defined $tmpdir;
61 $tmpdir = $self->canonpath($tmpdir);
62 return $tmpdir;
63}
64
46726cbe 65sub case_tolerant {
66 return 1;
67}
68
cbc7acb0 69sub file_name_is_absolute {
70 my ($self,$file) = @_;
1b1e14d3 71 return scalar($file =~ m{^([a-z]:)?[\\/]}is);
270d1e39 72}
73
74=item catfile
75
76Concatenate one or more directory names and a filename to form a
77complete path ending with a filename
78
79=cut
80
81sub catfile {
cbc7acb0 82 my $self = shift;
270d1e39 83 my $file = pop @_;
84 return $file unless @_;
85 my $dir = $self->catdir(@_);
cbc7acb0 86 $dir .= "\\" unless substr($dir,-1) eq "\\";
270d1e39 87 return $dir.$file;
88}
89
90sub path {
270d1e39 91 my $path = $ENV{'PATH'} || $ENV{'Path'} || $ENV{'path'};
92 my @path = split(';',$path);
cbc7acb0 93 foreach (@path) { $_ = '.' if $_ eq '' }
94 return @path;
270d1e39 95}
96
97=item canonpath
98
99No physical check on the filesystem, but a logical cleanup of a
100path. On UNIX eliminated successive slashes and successive "/.".
101
102=cut
103
104sub canonpath {
0994714a 105 my ($self,$path) = @_;
1b1e14d3 106 $path =~ s/^([a-z]:)/\u$1/s;
270d1e39 107 $path =~ s|/|\\|g;
f505c983 108 $path =~ s|([^\\])\\+|$1\\|g; # xx////xx -> xx/xx
cbc7acb0 109 $path =~ s|(\\\.)+\\|\\|g; # xx/././xx -> xx/xx
1b1e14d3 110 $path =~ s|^(\.\\)+||s unless $path eq ".\\"; # ./xx -> xx
111 $path =~ s|\\\z||
112 unless $path =~ m#^([A-Z]:)?\\\z#s; # xx/ -> xx
cbc7acb0 113 return $path;
270d1e39 114}
115
c27914c9 116=item splitpath
117
118 ($volume,$directories,$file) = File::Spec->splitpath( $path );
119 ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
120
121Splits a path in to volume, directory, and filename portions. Assumes that
122the last file is a path unless the path ends in '\\', '\\.', '\\..'
123or $no_file is true. On Win32 this means that $no_file true makes this return
124( $volume, $path, undef ).
125
126Separators accepted are \ and /.
127
128Volumes can be drive letters or UNC sharenames (\\server\share).
129
0994714a 130The results can be passed to L</catpath> to get back a path equivalent to
c27914c9 131(usually identical to) the original path.
132
133=cut
134
135sub splitpath {
136 my ($self,$path, $nofile) = @_;
137 my ($volume,$directory,$file) = ('','','');
138 if ( $nofile ) {
139 $path =~
0994714a 140 m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? )
c27914c9 141 (.*)
1b1e14d3 142 }xs;
c27914c9 143 $volume = $1;
144 $directory = $2;
145 }
146 else {
147 $path =~
0994714a 148 m{^ ( (?: [a-zA-Z]: |
149 (?:\\\\|//)[^\\/]+[\\/][^\\/]+
c27914c9 150 )?
151 )
1b1e14d3 152 ( (?:.*[\\\\/](?:\.\.?\z)?)? )
c27914c9 153 (.*)
1b1e14d3 154 }xs;
c27914c9 155 $volume = $1;
156 $directory = $2;
157 $file = $3;
158 }
159
160 return ($volume,$directory,$file);
161}
162
163
164=item splitdir
165
166The opposite of L</catdir()>.
167
168 @dirs = File::Spec->splitdir( $directories );
169
170$directories must be only the directory portion of the path on systems
171that have the concept of a volume or that have path syntax that differentiates
172files from directories.
173
174Unlike just splitting the directories on the separator, leading empty and
175trailing directory entries can be returned, because these are significant
176on some OSs. So,
177
178 File::Spec->splitdir( "/a/b/c" );
179
180Yields:
181
182 ( '', 'a', 'b', '', 'c', '' )
183
184=cut
185
186sub splitdir {
187 my ($self,$directories) = @_ ;
188 #
189 # split() likes to forget about trailing null fields, so here we
190 # check to be sure that there will not be any before handling the
191 # simple case.
192 #
1b1e14d3 193 if ( $directories !~ m|[\\/]\z| ) {
c27914c9 194 return split( m|[\\/]|, $directories );
195 }
196 else {
197 #
198 # since there was a trailing separator, add a file name to the end,
199 # then do the split, then replace it with ''.
200 #
201 my( @directories )= split( m|[\\/]|, "${directories}dummy" ) ;
202 $directories[ $#directories ]= '' ;
203 return @directories ;
204 }
205}
206
207
208=item catpath
209
210Takes volume, directory and file portions and returns an entire path. Under
211Unix, $volume is ignored, and this is just like catfile(). On other OSs,
212the $volume become significant.
213
214=cut
215
216sub catpath {
217 my ($self,$volume,$directory,$file) = @_;
218
219 # If it's UNC, make sure the glue separator is there, reusing
220 # whatever separator is first in the $volume
221 $volume .= $1
1b1e14d3 222 if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\z@s &&
223 $directory =~ m@^[^\\/]@s
c27914c9 224 ) ;
225
226 $volume .= $directory ;
227
228 # If the volume is not just A:, make sure the glue separator is
229 # there, reusing whatever separator is first in the $volume if possible.
1b1e14d3 230 if ( $volume !~ m@^[a-zA-Z]:\z@s &&
231 $volume =~ m@[^\\/]\z@ &&
0994714a 232 $file =~ m@[^\\/]@
c27914c9 233 ) {
234 $volume =~ m@([\\/])@ ;
235 my $sep = $1 ? $1 : '\\' ;
236 $volume .= $sep ;
237 }
238
239 $volume .= $file ;
240
241 return $volume ;
242}
243
244
245=item abs2rel
246
247Takes a destination path and an optional base path returns a relative path
248from the base path to the destination path:
249
250 $rel_path = File::Spec->abs2rel( $destination ) ;
251 $rel_path = File::Spec->abs2rel( $destination, $base ) ;
252
253If $base is not present or '', then L</cwd()> is used. If $base is relative,
254then it is converted to absolute form using L</rel2abs()>. This means that it
255is taken to be relative to L<cwd()>.
256
257On systems with the concept of a volume, this assumes that both paths
0994714a 258are on the $destination volume, and ignores the $base volume.
c27914c9 259
260On systems that have a grammar that indicates filenames, this ignores the
261$base filename as well. Otherwise all path components are assumed to be
262directories.
263
264If $path is relative, it is converted to absolute form using L</rel2abs()>.
265This means that it is taken to be relative to L</cwd()>.
266
267Based on code written by Shigio Yamaguchi.
268
269No checks against the filesystem are made.
270
271=cut
272
273sub abs2rel {
274 my($self,$path,$base) = @_;
275
276 # Clean up $path
277 if ( ! $self->file_name_is_absolute( $path ) ) {
278 $path = $self->rel2abs( $path ) ;
279 }
280 else {
281 $path = $self->canonpath( $path ) ;
282 }
283
284 # Figure out the effective $base and clean it up.
285 if ( ! $self->file_name_is_absolute( $base ) ) {
286 $base = $self->rel2abs( $base ) ;
287 }
288 elsif ( !defined( $base ) || $base eq '' ) {
289 $base = cwd() ;
290 }
291 else {
292 $base = $self->canonpath( $base ) ;
293 }
294
295 # Split up paths
296 my ( $path_volume, $path_directories, $path_file ) =
297 $self->splitpath( $path, 1 ) ;
298
299 my ( undef, $base_directories, undef ) =
300 $self->splitpath( $base, 1 ) ;
301
302 # Now, remove all leading components that are the same
303 my @pathchunks = $self->splitdir( $path_directories );
304 my @basechunks = $self->splitdir( $base_directories );
305
306 while ( @pathchunks &&
307 @basechunks &&
308 lc( $pathchunks[0] ) eq lc( $basechunks[0] )
309 ) {
310 shift @pathchunks ;
311 shift @basechunks ;
312 }
313
314 # No need to catdir, we know these are well formed.
315 $path_directories = CORE::join( '\\', @pathchunks );
316 $base_directories = CORE::join( '\\', @basechunks );
317
5cefc38b 318 # $base_directories now contains the directories the resulting relative
319 # path must ascend out of before it can descend to $path_directory. So,
c27914c9 320 # replace all names with $parentDir
5cefc38b 321
322 #FA Need to replace between backslashes...
323 $base_directories =~ s|[^\\]+|..|g ;
c27914c9 324
325 # Glue the two together, using a separator if necessary, and preventing an
326 # empty result.
5cefc38b 327
328 #FA Must check that new directories are not empty.
329 if ( $path_directories ne '' && $base_directories ne '' ) {
c27914c9 330 $path_directories = "$base_directories\\$path_directories" ;
331 } else {
332 $path_directories = "$base_directories$path_directories" ;
333 }
334
0994714a 335 # It makes no sense to add a relative path to a UNC volume
1b1e14d3 336 $path_volume = '' unless $path_volume =~ m{^[A-Z]:}is ;
0994714a 337
c27914c9 338 return $self->canonpath(
0994714a 339 $self->catpath($path_volume, $path_directories, $path_file )
c27914c9 340 ) ;
341}
342
343=item rel2abs
344
345Converts a relative path to an absolute path.
346
1d7cb664 347 $abs_path = File::Spec->rel2abs( $destination ) ;
348 $abs_path = File::Spec->rel2abs( $destination, $base ) ;
c27914c9 349
350If $base is not present or '', then L<cwd()> is used. If $base is relative,
351then it is converted to absolute form using L</rel2abs()>. This means that it
352is taken to be relative to L</cwd()>.
353
354Assumes that both paths are on the $base volume, and ignores the
355$destination volume.
356
357On systems that have a grammar that indicates filenames, this ignores the
358$base filename as well. Otherwise all path components are assumed to be
359directories.
360
361If $path is absolute, it is cleaned up and returned using L</canonpath()>.
362
363Based on code written by Shigio Yamaguchi.
364
365No checks against the filesystem are made.
366
367=cut
368
369sub rel2abs($;$;) {
370 my ($self,$path,$base ) = @_;
371
c27914c9 372 if ( ! $self->file_name_is_absolute( $path ) ) {
373
1d7cb664 374 if ( !defined( $base ) || $base eq '' ) {
c27914c9 375 $base = cwd() ;
376 }
1d7cb664 377 elsif ( ! $self->file_name_is_absolute( $base ) ) {
378 $base = $self->rel2abs( $base ) ;
379 }
c27914c9 380 else {
381 $base = $self->canonpath( $base ) ;
382 }
383
c27914c9 384 my ( undef, $path_directories, $path_file ) =
385 $self->splitpath( $path, 1 ) ;
386
387 my ( $base_volume, $base_directories, undef ) =
388 $self->splitpath( $base, 1 ) ;
389
390 $path = $self->catpath(
391 $base_volume,
392 $self->catdir( $base_directories, $path_directories ),
393 $path_file
394 ) ;
395 }
396
397 return $self->canonpath( $path ) ;
398}
399
270d1e39 400=back
401
cbc7acb0 402=head1 SEE ALSO
403
404L<File::Spec>
270d1e39 405
cbc7acb0 406=cut
407
4081;