1 package File::Spec::Win32;
5 use vars qw(@ISA $VERSION);
6 require File::Spec::Unix;
11 @ISA = qw(File::Spec::Unix);
13 # Some regexes we use for path splitting
14 my $DRIVE_RX = '[a-zA-Z]:';
15 my $UNC_RX = '(?:\\\\\\\\|//)[^\\\\/]+[\\\\/][^\\\\/]+';
16 my $VOL_RX = "(?:$DRIVE_RX|$UNC_RX)";
21 File::Spec::Win32 - methods for Win32 file specs
25 require File::Spec::Win32; # Done internally by File::Spec if needed
29 See File::Spec::Unix for a documentation of the methods provided
30 there. This package overrides the implementation of these methods, not
37 Returns a string representation of the null device.
50 Returns a string representation of the first existing directory
51 from the following list:
62 The SYS:/temp is preferred in Novell NetWare and the C:\system\temp
63 for Symbian (the File::Spec::Win32 is used also for those platforms).
65 If running under taint mode, and if the environment
66 variables are tainted, they are not used.
71 my $tmpdir = $_[0]->_cached_tmpdir(qw(TMPDIR TEMP TMP));
72 return $tmpdir if defined $tmpdir;
73 $tmpdir = $_[0]->_tmpdir( map( $ENV{$_}, qw(TMPDIR TEMP TMP) ),
79 $_[0]->_cache_tmpdir($tmpdir, qw(TMPDIR TEMP TMP));
84 MSWin32 case-tolerance depends on GetVolumeInformation() $ouFsFlags == FS_CASE_SENSITIVE,
85 indicating the case significance when comparing file specifications.
86 Since XP FS_CASE_SENSITIVE is effectively disabled for the NT subsubsystem.
87 See http://cygwin.com/ml/cygwin/2007-07/msg00891.html
93 eval { require Win32API::File; } or return 1;
94 my $drive = shift || "C:";
95 my $osFsType = "\0"x256;
96 my $osVolName = "\0"x256;
98 Win32API::File::GetVolumeInformation($drive, $osVolName, 256, [], [], $ouFsFlags, $osFsType, 256 );
99 if ($ouFsFlags & Win32API::File::FS_CASE_SENSITIVE()) { return 0; }
103 =item file_name_is_absolute
105 As of right now, this returns 2 if the path is absolute with a
106 volume, 1 if it's absolute with no volume, 0 otherwise.
110 sub file_name_is_absolute {
112 my ($self,$file) = @_;
114 if ($file =~ m{^($VOL_RX)}o) {
116 return ($vol =~ m{^$UNC_RX}o ? 2
117 : $file =~ m{^$DRIVE_RX[\\/]}o ? 2
120 return $file =~ m{^[\\/]} ? 1 : 0;
125 Concatenate one or more directory names and a filename to form a
126 complete path ending with a filename
133 # Legacy / compatibility support
135 shift, return _canon_cat( "/", @_ )
138 # Compatibility with File::Spec <= 3.26:
139 # catfile('A:', 'foo') should return 'A:\foo'.
140 return _canon_cat( ($_[0].'\\'), @_[1..$#_] )
141 if $_[0] =~ m{^$DRIVE_RX\z}o;
143 return _canon_cat( @_ );
149 # Legacy / compatibility support
153 shift, return _canon_cat( "/", @_ )
156 # Compatibility with File::Spec <= 3.26:
157 # catdir('A:', 'foo') should return 'A:\foo'.
158 return _canon_cat( ($_[0].'\\'), @_[1..$#_] )
159 if $_[0] =~ m{^$DRIVE_RX\z}o;
161 return _canon_cat( @_ );
165 my @path = split(';', $ENV{PATH});
167 @path = grep length, @path;
174 No physical check on the filesystem, but a logical cleanup of a
175 path. On UNIX eliminated successive slashes and successive "/.".
178 dir1\dir2\dir3\..\..\dir4 -> \dir\dir4 and even
179 dir1\dir2\dir3\...\dir4 -> \dir\dir4
184 # Legacy / compatibility support
186 return $_[1] if !defined($_[1]) or $_[1] eq '';
187 return _canon_cat( $_[1] );
192 ($volume,$directories,$file) = File::Spec->splitpath( $path );
193 ($volume,$directories,$file) = File::Spec->splitpath( $path,
196 Splits a path into volume, directory, and filename portions. Assumes that
197 the last file is a path unless the path ends in '\\', '\\.', '\\..'
198 or $no_file is true. On Win32 this means that $no_file true makes this return
199 ( $volume, $path, '' ).
201 Separators accepted are \ and /.
203 Volumes can be drive letters or UNC sharenames (\\server\share).
205 The results can be passed to L</catpath> to get back a path equivalent to
206 (usually identical to) the original path.
211 my ($self,$path, $nofile) = @_;
212 my ($volume,$directory,$file) = ('','','');
215 m{^ ( $VOL_RX ? ) (.*) }sox;
222 ( (?:.*[\\/](?:\.\.?\Z(?!\n))?)? )
230 return ($volume,$directory,$file);
236 The opposite of L<catdir()|File::Spec/catdir>.
238 @dirs = File::Spec->splitdir( $directories );
240 $directories must be only the directory portion of the path on systems
241 that have the concept of a volume or that have path syntax that differentiates
242 files from directories.
244 Unlike just splitting the directories on the separator, leading empty and
245 trailing directory entries can be returned, because these are significant
248 File::Spec->splitdir( "/a/b/c" );
252 ( '', 'a', 'b', '', 'c', '' )
257 my ($self,$directories) = @_ ;
259 # split() likes to forget about trailing null fields, so here we
260 # check to be sure that there will not be any before handling the
263 if ( $directories !~ m|[\\/]\Z(?!\n)| ) {
264 return split( m|[\\/]|, $directories );
268 # since there was a trailing separator, add a file name to the end,
269 # then do the split, then replace it with ''.
271 my( @directories )= split( m|[\\/]|, "${directories}dummy" ) ;
272 $directories[ $#directories ]= '' ;
273 return @directories ;
280 Takes volume, directory and file portions and returns an entire path. Under
281 Unix, $volume is ignored, and this is just like catfile(). On other OSs,
282 the $volume become significant.
287 my ($self,$volume,$directory,$file) = @_;
289 # If it's UNC, make sure the glue separator is there, reusing
290 # whatever separator is first in the $volume
293 if ( (($v) = $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\Z(?!\n)@s) &&
294 $directory =~ m@^[^\\/]@s
297 $volume .= $directory ;
299 # If the volume is not just A:, make sure the glue separator is
300 # there, reusing whatever separator is first in the $volume if possible.
301 if ( $volume !~ m@^[a-zA-Z]:\Z(?!\n)@s &&
302 $volume =~ m@[^\\/]\Z(?!\n)@ &&
305 $volume =~ m@([\\/])@ ;
306 my $sep = $1 ? $1 : '\\' ;
316 lc($_[1]) eq lc($_[2]);
320 my ($self,$path,$base ) = @_;
322 my $is_abs = $self->file_name_is_absolute($path);
324 # Check for volume (should probably document the '2' thing...)
325 return $self->canonpath( $path ) if $is_abs == 2;
328 # It's missing a volume, add one
329 my $vol = ($self->splitpath( $self->_cwd() ))[0];
330 return $self->canonpath( $vol . $path );
333 if ( !defined( $base ) || $base eq '' ) {
335 $base = Cwd::getdcwd( ($self->splitpath( $path ))[0] ) if defined &Cwd::getdcwd ;
336 $base = $self->_cwd() unless defined $base ;
338 elsif ( ! $self->file_name_is_absolute( $base ) ) {
339 $base = $self->rel2abs( $base ) ;
342 $base = $self->canonpath( $base ) ;
345 my ( $path_directories, $path_file ) =
346 ($self->splitpath( $path, 1 ))[1,2] ;
348 my ( $base_volume, $base_directories ) =
349 $self->splitpath( $base, 1 ) ;
351 $path = $self->catpath(
353 $self->catdir( $base_directories, $path_directories ),
357 return $self->canonpath( $path ) ;
362 =head2 Note For File::Spec::Win32 Maintainers
364 Novell NetWare inherits its File::Spec behaviour from File::Spec::Win32.
368 Copyright (c) 2004,2007 by the Perl 5 Porters. All rights reserved.
370 This program is free software; you can redistribute it and/or modify
371 it under the same terms as Perl itself.
375 See L<File::Spec> and L<File::Spec::Unix>. This package overrides the
376 implementation of these methods, not the semantics.
381 sub _canon_cat # @path -> path
383 my ($first, @rest) = @_;
385 my $volume = $first =~ s{ \A ([A-Za-z]:) ([\\/]?) }{}x # drive letter
386 ? ucfirst( $1 ).( $2 ? "\\" : "" )
387 : $first =~ s{ \A (?:\\\\|//) ([^\\/]+)
388 (?: [\\/] ([^\\/]+) )?
389 [\\/]? }{}xs # UNC volume
390 ? "\\\\$1".( defined $2 ? "\\$2" : "" )."\\"
391 : $first =~ s{ \A [\\/] }{}x # root dir
394 my $path = join "\\", $first, @rest;
396 $path =~ tr#\\/#\\\\#s; # xx/yy --> xx\yy & xx\\yy --> xx\yy
398 # xx/././yy --> xx/yy
400 (?:\A|\\) # at begin or after a slash
403 (?:\\|\z) # at end or followed by slash
404 )+ # performance boost -- I do not know why
407 # XXX I do not know whether more dots are supported by the OS supporting
408 # this ... annotation (NetWare or symbian but not MSWin32).
409 # Then .... could easily become ../../.. etc:
410 # Replace \.\.\. by (\.\.\.+) and substitute with
411 # { $1 . ".." . "\\.." x (length($2)-2) }gex
413 $path =~ s{ (\A|\\) # at begin or after a slash
415 (?=\\|\z) # at end or followed by slash
417 # xx\yy\..\zz --> xx\zz
418 while ( $path =~ s{(?:
419 (?:\A|\\) # at begin or after a slash
420 [^\\]+ # rip this 'yy' off
422 (?<!\A\.\.\\\.\.) # do *not* replace ^..\..
423 (?<!\\\.\.\\\.\.) # do *not* replace \..\..
424 (?:\\|\z) # at end or followed by slash
425 )+ # performance boost -- I do not know why
428 $path =~ s#\A\\##; # \xx --> xx NOTE: this is *not* root
429 $path =~ s#\\\z##; # xx\ --> xx
431 if ( $volume =~ m#\\\z# )
432 { # <vol>\.. --> <vol>\
433 $path =~ s{ \A # at begin
435 (?:\\\.\.)* # and more
436 (?:\\|\z) # at end or followed by slash
439 return $1 # \\HOST\SHARE\ --> \\HOST\SHARE
441 and $volume =~ m#\A(\\\\.*)\\\z#s;
443 return $path ne "" || $volume ? $volume.$path : ".";