Manual integration error in #12235.
[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}
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
9c045eb2 111 $path =~ s|\\\Z(?!\n)||
112 unless $path =~ m#^([A-Z]:)?\\\Z(?!\n)#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 )
9c045eb2 152 ( (?:.*[\\\\/](?:\.\.?\Z(?!\n))?)? )
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 #
9c045eb2 193 if ( $directories !~ m|[\\/]\Z(?!\n)| ) {
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
9c045eb2 222 if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\Z(?!\n)@s &&
1b1e14d3 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.
9c045eb2 230 if ( $volume !~ m@^[a-zA-Z]:\Z(?!\n)@s &&
231 $volume =~ m@[^\\/]\Z(?!\n)@ &&
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
c27914c9 245sub abs2rel {
246 my($self,$path,$base) = @_;
247
248 # Clean up $path
249 if ( ! $self->file_name_is_absolute( $path ) ) {
250 $path = $self->rel2abs( $path ) ;
251 }
252 else {
253 $path = $self->canonpath( $path ) ;
254 }
255
256 # Figure out the effective $base and clean it up.
257 if ( ! $self->file_name_is_absolute( $base ) ) {
258 $base = $self->rel2abs( $base ) ;
259 }
260 elsif ( !defined( $base ) || $base eq '' ) {
261 $base = cwd() ;
262 }
263 else {
264 $base = $self->canonpath( $base ) ;
265 }
266
267 # Split up paths
268 my ( $path_volume, $path_directories, $path_file ) =
269 $self->splitpath( $path, 1 ) ;
270
9c045eb2 271 my $base_directories = ($self->splitpath( $base, 1 ))[1] ;
c27914c9 272
273 # Now, remove all leading components that are the same
274 my @pathchunks = $self->splitdir( $path_directories );
275 my @basechunks = $self->splitdir( $base_directories );
276
277 while ( @pathchunks &&
278 @basechunks &&
279 lc( $pathchunks[0] ) eq lc( $basechunks[0] )
280 ) {
281 shift @pathchunks ;
282 shift @basechunks ;
283 }
284
285 # No need to catdir, we know these are well formed.
286 $path_directories = CORE::join( '\\', @pathchunks );
287 $base_directories = CORE::join( '\\', @basechunks );
288
5cefc38b 289 # $base_directories now contains the directories the resulting relative
290 # path must ascend out of before it can descend to $path_directory. So,
c27914c9 291 # replace all names with $parentDir
5cefc38b 292
293 #FA Need to replace between backslashes...
294 $base_directories =~ s|[^\\]+|..|g ;
c27914c9 295
296 # Glue the two together, using a separator if necessary, and preventing an
297 # empty result.
5cefc38b 298
299 #FA Must check that new directories are not empty.
300 if ( $path_directories ne '' && $base_directories ne '' ) {
c27914c9 301 $path_directories = "$base_directories\\$path_directories" ;
302 } else {
303 $path_directories = "$base_directories$path_directories" ;
304 }
305
0994714a 306 # It makes no sense to add a relative path to a UNC volume
1b1e14d3 307 $path_volume = '' unless $path_volume =~ m{^[A-Z]:}is ;
0994714a 308
c27914c9 309 return $self->canonpath(
0994714a 310 $self->catpath($path_volume, $path_directories, $path_file )
c27914c9 311 ) ;
312}
313
c27914c9 314
786b702f 315sub rel2abs {
c27914c9 316 my ($self,$path,$base ) = @_;
317
c27914c9 318 if ( ! $self->file_name_is_absolute( $path ) ) {
319
1d7cb664 320 if ( !defined( $base ) || $base eq '' ) {
c27914c9 321 $base = cwd() ;
322 }
1d7cb664 323 elsif ( ! $self->file_name_is_absolute( $base ) ) {
324 $base = $self->rel2abs( $base ) ;
325 }
c27914c9 326 else {
327 $base = $self->canonpath( $base ) ;
328 }
329
9c045eb2 330 my ( $path_directories, $path_file ) =
331 ($self->splitpath( $path, 1 ))[1,2] ;
c27914c9 332
9c045eb2 333 my ( $base_volume, $base_directories ) =
c27914c9 334 $self->splitpath( $base, 1 ) ;
335
336 $path = $self->catpath(
337 $base_volume,
338 $self->catdir( $base_directories, $path_directories ),
339 $path_file
340 ) ;
341 }
342
343 return $self->canonpath( $path ) ;
344}
345
270d1e39 346=back
347
cbc7acb0 348=head1 SEE ALSO
349
350L<File::Spec>
270d1e39 351
cbc7acb0 352=cut
353
3541;