xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/File/Spec/Win32.pm (revision 0:68f95e015346)
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