1 package File::Spec::Win32;
5 use vars qw(@ISA $VERSION);
6 require File::Spec::Unix;
10 @ISA = qw(File::Spec::Unix);
14 File::Spec::Win32 - methods for Win32 file specs
18 require File::Spec::Win32; # Done internally by File::Spec if needed
22 See File::Spec::Unix for a documentation of the methods provided
23 there. This package overrides the implementation of these methods, not
30 Returns a string representation of the null device.
40 Returns a string representation of the first existing directory
41 from the following list:
51 The SYS:/temp is preferred in Novell NetWare.
53 Since Perl 5.8.0, if running under taint mode, and if the environment
54 variables are tainted, they are not used.
60 return $tmpdir if defined $tmpdir;
62 my @dirlist = (@ENV{qw(TMPDIR TEMP TMP)}, qw(C:/temp /tmp /));
65 if (${"\cTAINT"}) { # Check for taint mode on perl >= 5.8.0
67 @dirlist = grep { ! Scalar::Util::tainted $_ } @dirlist;
71 next unless defined && -d;
75 $tmpdir = '' unless defined $tmpdir;
76 $tmpdir = $self->canonpath($tmpdir);
84 sub file_name_is_absolute {
85 my ($self,$file) = @_;
86 return scalar($file =~ m{^([a-z]:)?[\\/]}is);
91 Concatenate one or more directory names and a filename to form a
92 complete path ending with a filename
98 my $file = $self->canonpath(pop @_);
99 return $file unless @_;
100 my $dir = $self->catdir(@_);
101 $dir .= "\\" unless substr($dir,-1) eq "\\";
106 my $path = $ENV{'PATH'} || $ENV{'Path'} || $ENV{'path'};
107 my @path = split(';',$path);
108 foreach (@path) { $_ = '.' if $_ eq '' }
114 No physical check on the filesystem, but a logical cleanup of a
115 path. On UNIX eliminated successive slashes and successive "/.".
118 dir1\dir2\dir3\..\..\dir4 -> \dir\dir4 and even
119 dir1\dir2\dir3\...\dir4 -> \dir\dir4
124 my ($self,$path) = @_;
125 my $orig_path = $path;
126 $path =~ s/^([a-z]:)/\u$1/s;
128 $path =~ s|([^\\])\\+|$1\\|g; # xx\\\\xx -> xx\xx
129 $path =~ s|(\\\.)+\\|\\|g; # xx\.\.\xx -> xx\xx
130 $path =~ s|^(\.\\)+||s unless $path eq ".\\"; # .\xx -> xx
131 $path =~ s|\\\Z(?!\n)||
132 unless $path =~ m#^([A-Z]:)?\\\Z(?!\n)#s; # xx\ -> xx
133 # xx1/xx2/xx3/../../xx -> xx1/xx
134 $path =~ s|\\\.\.\.\\|\\\.\.\\\.\.\\|g; # \...\ is 2 levels up
135 $path =~ s|^\.\.\.\\|\.\.\\\.\.\\|g; # ...\ is 2 levels up
136 return $path if $path =~ m|^\.\.|; # skip relative paths
137 return $path unless $path =~ /\.\./; # too few .'s to cleanup
138 return $path if $path =~ /\.\.\.\./; # too many .'s to cleanup
139 return $path if $orig_path =~ m|^\Q/../\E|
140 and $orig_path =~ m|\/$|; # don't do /../dirs/
141 # when called from rel2abs()
143 my ($vol,$dirs,$file) = $self->splitpath($path);
144 my @dirs = $self->splitdir($dirs);
145 my (@base_dirs, @path_dirs);
146 my $dest = \@base_dirs;
148 $dest = \@path_dirs if $dir eq $self->updir;
151 # for each .. in @path_dirs pop one item from
153 while (my $dir = shift @path_dirs){
154 unless ($dir eq $self->updir){
155 unshift @path_dirs, $dir;
160 $path = $self->catpath(
162 $self->catdir(@base_dirs, @path_dirs),
170 ($volume,$directories,$file) = File::Spec->splitpath( $path );
171 ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
173 Splits a path in to volume, directory, and filename portions. Assumes that
174 the last file is a path unless the path ends in '\\', '\\.', '\\..'
175 or $no_file is true. On Win32 this means that $no_file true makes this return
176 ( $volume, $path, undef ).
178 Separators accepted are \ and /.
180 Volumes can be drive letters or UNC sharenames (\\server\share).
182 The results can be passed to L</catpath> to get back a path equivalent to
183 (usually identical to) the original path.
188 my ($self,$path, $nofile) = @_;
189 my ($volume,$directory,$file) = ('','','');
192 m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? )
200 m{^ ( (?: [a-zA-Z]: |
201 (?:\\\\|//)[^\\/]+[\\/][^\\/]+
204 ( (?:.*[\\\\/](?:\.\.?\Z(?!\n))?)? )
212 return ($volume,$directory,$file);
218 The opposite of L<catdir()|File::Spec/catdir()>.
220 @dirs = File::Spec->splitdir( $directories );
222 $directories must be only the directory portion of the path on systems
223 that have the concept of a volume or that have path syntax that differentiates
224 files from directories.
226 Unlike just splitting the directories on the separator, leading empty and
227 trailing directory entries can be returned, because these are significant
230 File::Spec->splitdir( "/a/b/c" );
234 ( '', 'a', 'b', '', 'c', '' )
239 my ($self,$directories) = @_ ;
241 # split() likes to forget about trailing null fields, so here we
242 # check to be sure that there will not be any before handling the
245 if ( $directories !~ m|[\\/]\Z(?!\n)| ) {
246 return split( m|[\\/]|, $directories );
250 # since there was a trailing separator, add a file name to the end,
251 # then do the split, then replace it with ''.
253 my( @directories )= split( m|[\\/]|, "${directories}dummy" ) ;
254 $directories[ $#directories ]= '' ;
255 return @directories ;
262 Takes volume, directory and file portions and returns an entire path. Under
263 Unix, $volume is ignored, and this is just like catfile(). On other OSs,
264 the $volume become significant.
269 my ($self,$volume,$directory,$file) = @_;
271 # If it's UNC, make sure the glue separator is there, reusing
272 # whatever separator is first in the $volume
274 if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\Z(?!\n)@s &&
275 $directory =~ m@^[^\\/]@s
278 $volume .= $directory ;
280 # If the volume is not just A:, make sure the glue separator is
281 # there, reusing whatever separator is first in the $volume if possible.
282 if ( $volume !~ m@^[a-zA-Z]:\Z(?!\n)@s &&
283 $volume =~ m@[^\\/]\Z(?!\n)@ &&
286 $volume =~ m@([\\/])@ ;
287 my $sep = $1 ? $1 : '\\' ;
298 my($self,$path,$base) = @_;
301 if ( ! $self->file_name_is_absolute( $path ) ) {
302 $path = $self->rel2abs( $path ) ;
305 $path = $self->canonpath( $path ) ;
308 # Figure out the effective $base and clean it up.
309 if ( !defined( $base ) || $base eq '' ) {
312 elsif ( ! $self->file_name_is_absolute( $base ) ) {
313 $base = $self->rel2abs( $base ) ;
316 $base = $self->canonpath( $base ) ;
320 my ( undef, $path_directories, $path_file ) =
321 $self->splitpath( $path, 1 ) ;
323 my $base_directories = ($self->splitpath( $base, 1 ))[1] ;
325 # Now, remove all leading components that are the same
326 my @pathchunks = $self->splitdir( $path_directories );
327 my @basechunks = $self->splitdir( $base_directories );
329 while ( @pathchunks &&
331 lc( $pathchunks[0] ) eq lc( $basechunks[0] )
337 # No need to catdir, we know these are well formed.
338 $path_directories = CORE::join( '\\', @pathchunks );
339 $base_directories = CORE::join( '\\', @basechunks );
341 # $base_directories now contains the directories the resulting relative
342 # path must ascend out of before it can descend to $path_directory. So,
343 # replace all names with $parentDir
345 #FA Need to replace between backslashes...
346 $base_directories =~ s|[^\\]+|..|g ;
348 # Glue the two together, using a separator if necessary, and preventing an
351 #FA Must check that new directories are not empty.
352 if ( $path_directories ne '' && $base_directories ne '' ) {
353 $path_directories = "$base_directories\\$path_directories" ;
355 $path_directories = "$base_directories$path_directories" ;
358 return $self->canonpath(
359 $self->catpath( "", $path_directories, $path_file )
365 my ($self,$path,$base ) = @_;
367 if ( ! $self->file_name_is_absolute( $path ) ) {
369 if ( !defined( $base ) || $base eq '' ) {
372 elsif ( ! $self->file_name_is_absolute( $base ) ) {
373 $base = $self->rel2abs( $base ) ;
376 $base = $self->canonpath( $base ) ;
379 my ( $path_directories, $path_file ) =
380 ($self->splitpath( $path, 1 ))[1,2] ;
382 my ( $base_volume, $base_directories ) =
383 $self->splitpath( $base, 1 ) ;
385 $path = $self->catpath(
387 $self->catdir( $base_directories, $path_directories ),
392 return $self->canonpath( $path ) ;
397 =head2 Note For File::Spec::Win32 Maintainers
399 Novell NetWare inherits its File::Spec behaviour from File::Spec::Win32.