1*0Sstevel@tonic-gatepackage File::Spec::Win32; 2*0Sstevel@tonic-gate 3*0Sstevel@tonic-gateuse strict; 4*0Sstevel@tonic-gate 5*0Sstevel@tonic-gateuse vars qw(@ISA $VERSION); 6*0Sstevel@tonic-gaterequire File::Spec::Unix; 7*0Sstevel@tonic-gate 8*0Sstevel@tonic-gate$VERSION = '1.4'; 9*0Sstevel@tonic-gate 10*0Sstevel@tonic-gate@ISA = qw(File::Spec::Unix); 11*0Sstevel@tonic-gate 12*0Sstevel@tonic-gate=head1 NAME 13*0Sstevel@tonic-gate 14*0Sstevel@tonic-gateFile::Spec::Win32 - methods for Win32 file specs 15*0Sstevel@tonic-gate 16*0Sstevel@tonic-gate=head1 SYNOPSIS 17*0Sstevel@tonic-gate 18*0Sstevel@tonic-gate require File::Spec::Win32; # Done internally by File::Spec if needed 19*0Sstevel@tonic-gate 20*0Sstevel@tonic-gate=head1 DESCRIPTION 21*0Sstevel@tonic-gate 22*0Sstevel@tonic-gateSee File::Spec::Unix for a documentation of the methods provided 23*0Sstevel@tonic-gatethere. This package overrides the implementation of these methods, not 24*0Sstevel@tonic-gatethe semantics. 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate=over 4 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate=item devnull 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gateReturns a string representation of the null device. 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate=cut 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gatesub devnull { 35*0Sstevel@tonic-gate return "nul"; 36*0Sstevel@tonic-gate} 37*0Sstevel@tonic-gate 38*0Sstevel@tonic-gate=item tmpdir 39*0Sstevel@tonic-gate 40*0Sstevel@tonic-gateReturns a string representation of the first existing directory 41*0Sstevel@tonic-gatefrom the following list: 42*0Sstevel@tonic-gate 43*0Sstevel@tonic-gate $ENV{TMPDIR} 44*0Sstevel@tonic-gate $ENV{TEMP} 45*0Sstevel@tonic-gate $ENV{TMP} 46*0Sstevel@tonic-gate SYS:/temp 47*0Sstevel@tonic-gate C:/temp 48*0Sstevel@tonic-gate /tmp 49*0Sstevel@tonic-gate / 50*0Sstevel@tonic-gate 51*0Sstevel@tonic-gateThe SYS:/temp is preferred in Novell NetWare (the File::Spec::Win32 52*0Sstevel@tonic-gateis used also for NetWare). 53*0Sstevel@tonic-gate 54*0Sstevel@tonic-gateSince Perl 5.8.0, if running under taint mode, and if the environment 55*0Sstevel@tonic-gatevariables are tainted, they are not used. 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate=cut 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gatemy $tmpdir; 60*0Sstevel@tonic-gatesub tmpdir { 61*0Sstevel@tonic-gate return $tmpdir if defined $tmpdir; 62*0Sstevel@tonic-gate my $self = shift; 63*0Sstevel@tonic-gate $tmpdir = $self->_tmpdir( @ENV{qw(TMPDIR TEMP TMP)}, 64*0Sstevel@tonic-gate 'SYS:/temp', 65*0Sstevel@tonic-gate 'C:/temp', 66*0Sstevel@tonic-gate '/tmp', 67*0Sstevel@tonic-gate '/' ); 68*0Sstevel@tonic-gate} 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gatesub case_tolerant { 71*0Sstevel@tonic-gate return 1; 72*0Sstevel@tonic-gate} 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gatesub file_name_is_absolute { 75*0Sstevel@tonic-gate my ($self,$file) = @_; 76*0Sstevel@tonic-gate return scalar($file =~ m{^([a-z]:)?[\\/]}is); 77*0Sstevel@tonic-gate} 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate=item catfile 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gateConcatenate one or more directory names and a filename to form a 82*0Sstevel@tonic-gatecomplete path ending with a filename 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate=cut 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gatesub catfile { 87*0Sstevel@tonic-gate my $self = shift; 88*0Sstevel@tonic-gate my $file = $self->canonpath(pop @_); 89*0Sstevel@tonic-gate return $file unless @_; 90*0Sstevel@tonic-gate my $dir = $self->catdir(@_); 91*0Sstevel@tonic-gate $dir .= "\\" unless substr($dir,-1) eq "\\"; 92*0Sstevel@tonic-gate return $dir.$file; 93*0Sstevel@tonic-gate} 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gatesub catdir { 96*0Sstevel@tonic-gate my $self = shift; 97*0Sstevel@tonic-gate my @args = @_; 98*0Sstevel@tonic-gate foreach (@args) { 99*0Sstevel@tonic-gate tr[/][\\]; 100*0Sstevel@tonic-gate # append a backslash to each argument unless it has one there 101*0Sstevel@tonic-gate $_ .= "\\" unless m{\\$}; 102*0Sstevel@tonic-gate } 103*0Sstevel@tonic-gate return $self->canonpath(join('', @args)); 104*0Sstevel@tonic-gate} 105*0Sstevel@tonic-gate 106*0Sstevel@tonic-gatesub path { 107*0Sstevel@tonic-gate my $path = $ENV{'PATH'} || $ENV{'Path'} || $ENV{'path'}; 108*0Sstevel@tonic-gate my @path = split(';',$path); 109*0Sstevel@tonic-gate foreach (@path) { $_ = '.' if $_ eq '' } 110*0Sstevel@tonic-gate return @path; 111*0Sstevel@tonic-gate} 112*0Sstevel@tonic-gate 113*0Sstevel@tonic-gate=item canonpath 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gateNo physical check on the filesystem, but a logical cleanup of a 116*0Sstevel@tonic-gatepath. On UNIX eliminated successive slashes and successive "/.". 117*0Sstevel@tonic-gateOn Win32 makes 118*0Sstevel@tonic-gate 119*0Sstevel@tonic-gate dir1\dir2\dir3\..\..\dir4 -> \dir\dir4 and even 120*0Sstevel@tonic-gate dir1\dir2\dir3\...\dir4 -> \dir\dir4 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate=cut 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gatesub canonpath { 125*0Sstevel@tonic-gate my ($self,$path) = @_; 126*0Sstevel@tonic-gate my $orig_path = $path; 127*0Sstevel@tonic-gate $path =~ s/^([a-z]:)/\u$1/s; 128*0Sstevel@tonic-gate $path =~ s|/|\\|g; 129*0Sstevel@tonic-gate $path =~ s|([^\\])\\+|$1\\|g; # xx\\\\xx -> xx\xx 130*0Sstevel@tonic-gate $path =~ s|(\\\.)+\\|\\|g; # xx\.\.\xx -> xx\xx 131*0Sstevel@tonic-gate $path =~ s|^(\.\\)+||s unless $path eq ".\\"; # .\xx -> xx 132*0Sstevel@tonic-gate $path =~ s|\\\Z(?!\n)|| 133*0Sstevel@tonic-gate unless $path =~ m{^([A-Z]:)?\\\Z(?!\n)}s; # xx\ -> xx 134*0Sstevel@tonic-gate # xx1/xx2/xx3/../../xx -> xx1/xx 135*0Sstevel@tonic-gate $path =~ s|\\\.\.\.\\|\\\.\.\\\.\.\\|g; # \...\ is 2 levels up 136*0Sstevel@tonic-gate $path =~ s|^\.\.\.\\|\.\.\\\.\.\\|g; # ...\ is 2 levels up 137*0Sstevel@tonic-gate return $path if $path =~ m|^\.\.|; # skip relative paths 138*0Sstevel@tonic-gate return $path unless $path =~ /\.\./; # too few .'s to cleanup 139*0Sstevel@tonic-gate return $path if $path =~ /\.\.\.\./; # too many .'s to cleanup 140*0Sstevel@tonic-gate $path =~ s{^\\\.\.$}{\\}; # \.. -> \ 141*0Sstevel@tonic-gate 1 while $path =~ s{^\\\.\.}{}; # \..\xx -> \xx 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate my ($vol,$dirs,$file) = $self->splitpath($path); 144*0Sstevel@tonic-gate my @dirs = $self->splitdir($dirs); 145*0Sstevel@tonic-gate my (@base_dirs, @path_dirs); 146*0Sstevel@tonic-gate my $dest = \@base_dirs; 147*0Sstevel@tonic-gate for my $dir (@dirs){ 148*0Sstevel@tonic-gate $dest = \@path_dirs if $dir eq $self->updir; 149*0Sstevel@tonic-gate push @$dest, $dir; 150*0Sstevel@tonic-gate } 151*0Sstevel@tonic-gate # for each .. in @path_dirs pop one item from 152*0Sstevel@tonic-gate # @base_dirs 153*0Sstevel@tonic-gate while (my $dir = shift @path_dirs){ 154*0Sstevel@tonic-gate unless ($dir eq $self->updir){ 155*0Sstevel@tonic-gate unshift @path_dirs, $dir; 156*0Sstevel@tonic-gate last; 157*0Sstevel@tonic-gate } 158*0Sstevel@tonic-gate pop @base_dirs; 159*0Sstevel@tonic-gate } 160*0Sstevel@tonic-gate $path = $self->catpath( 161*0Sstevel@tonic-gate $vol, 162*0Sstevel@tonic-gate $self->catdir(@base_dirs, @path_dirs), 163*0Sstevel@tonic-gate $file 164*0Sstevel@tonic-gate ); 165*0Sstevel@tonic-gate return $path; 166*0Sstevel@tonic-gate} 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate=item splitpath 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate ($volume,$directories,$file) = File::Spec->splitpath( $path ); 171*0Sstevel@tonic-gate ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file ); 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gateSplits a path into volume, directory, and filename portions. Assumes that 174*0Sstevel@tonic-gatethe last file is a path unless the path ends in '\\', '\\.', '\\..' 175*0Sstevel@tonic-gateor $no_file is true. On Win32 this means that $no_file true makes this return 176*0Sstevel@tonic-gate( $volume, $path, '' ). 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gateSeparators accepted are \ and /. 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gateVolumes can be drive letters or UNC sharenames (\\server\share). 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gateThe results can be passed to L</catpath> to get back a path equivalent to 183*0Sstevel@tonic-gate(usually identical to) the original path. 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gate=cut 186*0Sstevel@tonic-gate 187*0Sstevel@tonic-gatesub splitpath { 188*0Sstevel@tonic-gate my ($self,$path, $nofile) = @_; 189*0Sstevel@tonic-gate my ($volume,$directory,$file) = ('','',''); 190*0Sstevel@tonic-gate if ( $nofile ) { 191*0Sstevel@tonic-gate $path =~ 192*0Sstevel@tonic-gate m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? ) 193*0Sstevel@tonic-gate (.*) 194*0Sstevel@tonic-gate }xs; 195*0Sstevel@tonic-gate $volume = $1; 196*0Sstevel@tonic-gate $directory = $2; 197*0Sstevel@tonic-gate } 198*0Sstevel@tonic-gate else { 199*0Sstevel@tonic-gate $path =~ 200*0Sstevel@tonic-gate m{^ ( (?: [a-zA-Z]: | 201*0Sstevel@tonic-gate (?:\\\\|//)[^\\/]+[\\/][^\\/]+ 202*0Sstevel@tonic-gate )? 203*0Sstevel@tonic-gate ) 204*0Sstevel@tonic-gate ( (?:.*[\\\\/](?:\.\.?\Z(?!\n))?)? ) 205*0Sstevel@tonic-gate (.*) 206*0Sstevel@tonic-gate }xs; 207*0Sstevel@tonic-gate $volume = $1; 208*0Sstevel@tonic-gate $directory = $2; 209*0Sstevel@tonic-gate $file = $3; 210*0Sstevel@tonic-gate } 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate return ($volume,$directory,$file); 213*0Sstevel@tonic-gate} 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate=item splitdir 217*0Sstevel@tonic-gate 218*0Sstevel@tonic-gateThe opposite of L<catdir()|File::Spec/catdir()>. 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate @dirs = File::Spec->splitdir( $directories ); 221*0Sstevel@tonic-gate 222*0Sstevel@tonic-gate$directories must be only the directory portion of the path on systems 223*0Sstevel@tonic-gatethat have the concept of a volume or that have path syntax that differentiates 224*0Sstevel@tonic-gatefiles from directories. 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gateUnlike just splitting the directories on the separator, leading empty and 227*0Sstevel@tonic-gatetrailing directory entries can be returned, because these are significant 228*0Sstevel@tonic-gateon some OSs. So, 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate File::Spec->splitdir( "/a/b/c" ); 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gateYields: 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate ( '', 'a', 'b', '', 'c', '' ) 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate=cut 237*0Sstevel@tonic-gate 238*0Sstevel@tonic-gatesub splitdir { 239*0Sstevel@tonic-gate my ($self,$directories) = @_ ; 240*0Sstevel@tonic-gate # 241*0Sstevel@tonic-gate # split() likes to forget about trailing null fields, so here we 242*0Sstevel@tonic-gate # check to be sure that there will not be any before handling the 243*0Sstevel@tonic-gate # simple case. 244*0Sstevel@tonic-gate # 245*0Sstevel@tonic-gate if ( $directories !~ m|[\\/]\Z(?!\n)| ) { 246*0Sstevel@tonic-gate return split( m|[\\/]|, $directories ); 247*0Sstevel@tonic-gate } 248*0Sstevel@tonic-gate else { 249*0Sstevel@tonic-gate # 250*0Sstevel@tonic-gate # since there was a trailing separator, add a file name to the end, 251*0Sstevel@tonic-gate # then do the split, then replace it with ''. 252*0Sstevel@tonic-gate # 253*0Sstevel@tonic-gate my( @directories )= split( m|[\\/]|, "${directories}dummy" ) ; 254*0Sstevel@tonic-gate $directories[ $#directories ]= '' ; 255*0Sstevel@tonic-gate return @directories ; 256*0Sstevel@tonic-gate } 257*0Sstevel@tonic-gate} 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate 260*0Sstevel@tonic-gate=item catpath 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gateTakes volume, directory and file portions and returns an entire path. Under 263*0Sstevel@tonic-gateUnix, $volume is ignored, and this is just like catfile(). On other OSs, 264*0Sstevel@tonic-gatethe $volume become significant. 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate=cut 267*0Sstevel@tonic-gate 268*0Sstevel@tonic-gatesub catpath { 269*0Sstevel@tonic-gate my ($self,$volume,$directory,$file) = @_; 270*0Sstevel@tonic-gate 271*0Sstevel@tonic-gate # If it's UNC, make sure the glue separator is there, reusing 272*0Sstevel@tonic-gate # whatever separator is first in the $volume 273*0Sstevel@tonic-gate $volume .= $1 274*0Sstevel@tonic-gate if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\Z(?!\n)@s && 275*0Sstevel@tonic-gate $directory =~ m@^[^\\/]@s 276*0Sstevel@tonic-gate ) ; 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate $volume .= $directory ; 279*0Sstevel@tonic-gate 280*0Sstevel@tonic-gate # If the volume is not just A:, make sure the glue separator is 281*0Sstevel@tonic-gate # there, reusing whatever separator is first in the $volume if possible. 282*0Sstevel@tonic-gate if ( $volume !~ m@^[a-zA-Z]:\Z(?!\n)@s && 283*0Sstevel@tonic-gate $volume =~ m@[^\\/]\Z(?!\n)@ && 284*0Sstevel@tonic-gate $file =~ m@[^\\/]@ 285*0Sstevel@tonic-gate ) { 286*0Sstevel@tonic-gate $volume =~ m@([\\/])@ ; 287*0Sstevel@tonic-gate my $sep = $1 ? $1 : '\\' ; 288*0Sstevel@tonic-gate $volume .= $sep ; 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate 291*0Sstevel@tonic-gate $volume .= $file ; 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate return $volume ; 294*0Sstevel@tonic-gate} 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gatesub abs2rel { 298*0Sstevel@tonic-gate my($self,$path,$base) = @_; 299*0Sstevel@tonic-gate $base = $self->_cwd() unless defined $base and length $base; 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate for ($path, $base) { $_ = $self->canonpath($_) } 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate my ($path_volume) = $self->splitpath($path, 1); 304*0Sstevel@tonic-gate my ($base_volume) = $self->splitpath($base, 1); 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate # Can't relativize across volumes 307*0Sstevel@tonic-gate return $path unless $path_volume eq $base_volume; 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate for ($path, $base) { $_ = $self->rel2abs($_) } 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate my $path_directories = ($self->splitpath($path, 1))[1]; 312*0Sstevel@tonic-gate my $base_directories = ($self->splitpath($base, 1))[1]; 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate # Now, remove all leading components that are the same 315*0Sstevel@tonic-gate my @pathchunks = $self->splitdir( $path_directories ); 316*0Sstevel@tonic-gate my @basechunks = $self->splitdir( $base_directories ); 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate while ( @pathchunks && 319*0Sstevel@tonic-gate @basechunks && 320*0Sstevel@tonic-gate lc( $pathchunks[0] ) eq lc( $basechunks[0] ) 321*0Sstevel@tonic-gate ) { 322*0Sstevel@tonic-gate shift @pathchunks ; 323*0Sstevel@tonic-gate shift @basechunks ; 324*0Sstevel@tonic-gate } 325*0Sstevel@tonic-gate 326*0Sstevel@tonic-gate my $result_dirs = $self->catdir( ($self->updir) x @basechunks, @pathchunks ); 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate return $self->canonpath( $self->catpath('', $result_dirs, '') ); 329*0Sstevel@tonic-gate} 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate 332*0Sstevel@tonic-gatesub rel2abs { 333*0Sstevel@tonic-gate my ($self,$path,$base ) = @_; 334*0Sstevel@tonic-gate 335*0Sstevel@tonic-gate if ( ! $self->file_name_is_absolute( $path ) ) { 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate if ( !defined( $base ) || $base eq '' ) { 338*0Sstevel@tonic-gate $base = $self->_cwd() ; 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate elsif ( ! $self->file_name_is_absolute( $base ) ) { 341*0Sstevel@tonic-gate $base = $self->rel2abs( $base ) ; 342*0Sstevel@tonic-gate } 343*0Sstevel@tonic-gate else { 344*0Sstevel@tonic-gate $base = $self->canonpath( $base ) ; 345*0Sstevel@tonic-gate } 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate my ( $path_directories, $path_file ) = 348*0Sstevel@tonic-gate ($self->splitpath( $path, 1 ))[1,2] ; 349*0Sstevel@tonic-gate 350*0Sstevel@tonic-gate my ( $base_volume, $base_directories ) = 351*0Sstevel@tonic-gate $self->splitpath( $base, 1 ) ; 352*0Sstevel@tonic-gate 353*0Sstevel@tonic-gate $path = $self->catpath( 354*0Sstevel@tonic-gate $base_volume, 355*0Sstevel@tonic-gate $self->catdir( $base_directories, $path_directories ), 356*0Sstevel@tonic-gate $path_file 357*0Sstevel@tonic-gate ) ; 358*0Sstevel@tonic-gate } 359*0Sstevel@tonic-gate 360*0Sstevel@tonic-gate return $self->canonpath( $path ) ; 361*0Sstevel@tonic-gate} 362*0Sstevel@tonic-gate 363*0Sstevel@tonic-gate=back 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate=head2 Note For File::Spec::Win32 Maintainers 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gateNovell NetWare inherits its File::Spec behaviour from File::Spec::Win32. 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate=head1 SEE ALSO 370*0Sstevel@tonic-gate 371*0Sstevel@tonic-gateSee L<File::Spec> and L<File::Spec::Unix>. This package overrides the 372*0Sstevel@tonic-gateimplementation of these methods, not the semantics. 373*0Sstevel@tonic-gate 374*0Sstevel@tonic-gate=cut 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate1; 377