source: trunk/third/perl/lib/File/Spec/Win32.pm @ 18450

Revision 18450, 8.9 KB checked in by zacheiss, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18449, which included commits to RCS files with non-trunk default branches.
Line 
1package File::Spec::Win32;
2
3use strict;
4use Cwd;
5use vars qw(@ISA $VERSION);
6require File::Spec::Unix;
7
8$VERSION = '1.3';
9
10@ISA = qw(File::Spec::Unix);
11
12=head1 NAME
13
14File::Spec::Win32 - methods for Win32 file specs
15
16=head1 SYNOPSIS
17
18 require File::Spec::Win32; # Done internally by File::Spec if needed
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
26=over 4
27
28=item devnull
29
30Returns a string representation of the null device.
31
32=cut
33
34sub devnull {
35    return "nul";
36}
37
38=item tmpdir
39
40Returns a string representation of the first existing directory
41from the following list:
42
43    $ENV{TMPDIR}
44    $ENV{TEMP}
45    $ENV{TMP}
46    SYS:/temp
47    C:/temp
48    /tmp
49    /
50
51The SYS:/temp is preferred in Novell NetWare.
52
53Since Perl 5.8.0, if running under taint mode, and if the environment
54variables are tainted, they are not used.
55
56=cut
57
58my $tmpdir;
59sub tmpdir {
60    return $tmpdir if defined $tmpdir;
61    my $self = shift;
62    my @dirlist = (@ENV{qw(TMPDIR TEMP TMP)}, qw(C:/temp /tmp /));
63    {
64        no strict 'refs';
65        if (${"\cTAINT"}) { # Check for taint mode on perl >= 5.8.0
66            require Scalar::Util;
67            @dirlist = grep { ! Scalar::Util::tainted $_ } @dirlist;
68        }
69    }
70    foreach (@dirlist) {
71        next unless defined && -d;
72        $tmpdir = $_;
73        last;
74    }
75    $tmpdir = '' unless defined $tmpdir;
76    $tmpdir = $self->canonpath($tmpdir);
77    return $tmpdir;
78}
79
80sub case_tolerant {
81    return 1;
82}
83
84sub file_name_is_absolute {
85    my ($self,$file) = @_;
86    return scalar($file =~ m{^([a-z]:)?[\\/]}is);
87}
88
89=item catfile
90
91Concatenate one or more directory names and a filename to form a
92complete path ending with a filename
93
94=cut
95
96sub catfile {
97    my $self = shift;
98    my $file = pop @_;
99    return $file unless @_;
100    my $dir = $self->catdir(@_);
101    $dir .= "\\" unless substr($dir,-1) eq "\\";
102    return $dir.$file;
103}
104
105sub path {
106    my $path = $ENV{'PATH'} || $ENV{'Path'} || $ENV{'path'};
107    my @path = split(';',$path);
108    foreach (@path) { $_ = '.' if $_ eq '' }
109    return @path;
110}
111
112=item canonpath
113
114No physical check on the filesystem, but a logical cleanup of a
115path. On UNIX eliminated successive slashes and successive "/.".
116
117=cut
118
119sub canonpath {
120    my ($self,$path) = @_;
121    $path =~ s/^([a-z]:)/\u$1/s;
122    $path =~ s|/|\\|g;
123    $path =~ s|([^\\])\\+|$1\\|g;                  # xx\\\\xx  -> xx\xx
124    $path =~ s|(\\\.)+\\|\\|g;                     # xx\.\.\xx -> xx\xx
125    $path =~ s|^(\.\\)+||s unless $path eq ".\\";  # .\xx      -> xx
126    $path =~ s|\\\Z(?!\n)||
127             unless $path =~ m#^([A-Z]:)?\\\Z(?!\n)#s;   # xx\       -> xx
128    return $path;
129}
130
131=item splitpath
132
133    ($volume,$directories,$file) = File::Spec->splitpath( $path );
134    ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
135
136Splits a path in to volume, directory, and filename portions. Assumes that
137the last file is a path unless the path ends in '\\', '\\.', '\\..'
138or $no_file is true.  On Win32 this means that $no_file true makes this return
139( $volume, $path, undef ).
140
141Separators accepted are \ and /.
142
143Volumes can be drive letters or UNC sharenames (\\server\share).
144
145The results can be passed to L</catpath> to get back a path equivalent to
146(usually identical to) the original path.
147
148=cut
149
150sub splitpath {
151    my ($self,$path, $nofile) = @_;
152    my ($volume,$directory,$file) = ('','','');
153    if ( $nofile ) {
154        $path =~
155            m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? )
156                 (.*)
157             }xs;
158        $volume    = $1;
159        $directory = $2;
160    }
161    else {
162        $path =~
163            m{^ ( (?: [a-zA-Z]: |
164                      (?:\\\\|//)[^\\/]+[\\/][^\\/]+
165                  )?
166                )
167                ( (?:.*[\\\\/](?:\.\.?\Z(?!\n))?)? )
168                (.*)
169             }xs;
170        $volume    = $1;
171        $directory = $2;
172        $file      = $3;
173    }
174
175    return ($volume,$directory,$file);
176}
177
178
179=item splitdir
180
181The opposite of L<catdir()|File::Spec/catdir()>.
182
183    @dirs = File::Spec->splitdir( $directories );
184
185$directories must be only the directory portion of the path on systems
186that have the concept of a volume or that have path syntax that differentiates
187files from directories.
188
189Unlike just splitting the directories on the separator, leading empty and
190trailing directory entries can be returned, because these are significant
191on some OSs. So,
192
193    File::Spec->splitdir( "/a/b/c" );
194
195Yields:
196
197    ( '', 'a', 'b', '', 'c', '' )
198
199=cut
200
201sub splitdir {
202    my ($self,$directories) = @_ ;
203    #
204    # split() likes to forget about trailing null fields, so here we
205    # check to be sure that there will not be any before handling the
206    # simple case.
207    #
208    if ( $directories !~ m|[\\/]\Z(?!\n)| ) {
209        return split( m|[\\/]|, $directories );
210    }
211    else {
212        #
213        # since there was a trailing separator, add a file name to the end,
214        # then do the split, then replace it with ''.
215        #
216        my( @directories )= split( m|[\\/]|, "${directories}dummy" ) ;
217        $directories[ $#directories ]= '' ;
218        return @directories ;
219    }
220}
221
222
223=item catpath
224
225Takes volume, directory and file portions and returns an entire path. Under
226Unix, $volume is ignored, and this is just like catfile(). On other OSs,
227the $volume become significant.
228
229=cut
230
231sub catpath {
232    my ($self,$volume,$directory,$file) = @_;
233
234    # If it's UNC, make sure the glue separator is there, reusing
235    # whatever separator is first in the $volume
236    $volume .= $1
237        if ( $volume =~ m@^([\\/])[\\/][^\\/]+[\\/][^\\/]+\Z(?!\n)@s &&
238             $directory =~ m@^[^\\/]@s
239           ) ;
240
241    $volume .= $directory ;
242
243    # If the volume is not just A:, make sure the glue separator is
244    # there, reusing whatever separator is first in the $volume if possible.
245    if ( $volume !~ m@^[a-zA-Z]:\Z(?!\n)@s &&
246         $volume =~ m@[^\\/]\Z(?!\n)@      &&
247         $file   =~ m@[^\\/]@
248       ) {
249        $volume =~ m@([\\/])@ ;
250        my $sep = $1 ? $1 : '\\' ;
251        $volume .= $sep ;
252    }
253
254    $volume .= $file ;
255
256    return $volume ;
257}
258
259
260sub abs2rel {
261    my($self,$path,$base) = @_;
262
263    # Clean up $path
264    if ( ! $self->file_name_is_absolute( $path ) ) {
265        $path = $self->rel2abs( $path ) ;
266    }
267    else {
268        $path = $self->canonpath( $path ) ;
269    }
270
271    # Figure out the effective $base and clean it up.
272    if ( !defined( $base ) || $base eq '' ) {
273        $base = cwd() ;
274    }
275    elsif ( ! $self->file_name_is_absolute( $base ) ) {
276        $base = $self->rel2abs( $base ) ;
277    }
278    else {
279        $base = $self->canonpath( $base ) ;
280    }
281
282    # Split up paths
283    my ( undef, $path_directories, $path_file ) =
284        $self->splitpath( $path, 1 ) ;
285
286    my $base_directories = ($self->splitpath( $base, 1 ))[1] ;
287
288    # Now, remove all leading components that are the same
289    my @pathchunks = $self->splitdir( $path_directories );
290    my @basechunks = $self->splitdir( $base_directories );
291
292    while ( @pathchunks &&
293            @basechunks &&
294            lc( $pathchunks[0] ) eq lc( $basechunks[0] )
295          ) {
296        shift @pathchunks ;
297        shift @basechunks ;
298    }
299
300    # No need to catdir, we know these are well formed.
301    $path_directories = CORE::join( '\\', @pathchunks );
302    $base_directories = CORE::join( '\\', @basechunks );
303
304    # $base_directories now contains the directories the resulting relative
305    # path must ascend out of before it can descend to $path_directory.  So,
306    # replace all names with $parentDir
307
308    #FA Need to replace between backslashes...
309    $base_directories =~ s|[^\\]+|..|g ;
310
311    # Glue the two together, using a separator if necessary, and preventing an
312    # empty result.
313
314    #FA Must check that new directories are not empty.
315    if ( $path_directories ne '' && $base_directories ne '' ) {
316        $path_directories = "$base_directories\\$path_directories" ;
317    } else {
318        $path_directories = "$base_directories$path_directories" ;
319    }
320
321    return $self->canonpath(
322        $self->catpath( "", $path_directories, $path_file )
323    ) ;
324}
325
326
327sub rel2abs {
328    my ($self,$path,$base ) = @_;
329
330    if ( ! $self->file_name_is_absolute( $path ) ) {
331
332        if ( !defined( $base ) || $base eq '' ) {
333            $base = cwd() ;
334        }
335        elsif ( ! $self->file_name_is_absolute( $base ) ) {
336            $base = $self->rel2abs( $base ) ;
337        }
338        else {
339            $base = $self->canonpath( $base ) ;
340        }
341
342        my ( $path_directories, $path_file ) =
343            ($self->splitpath( $path, 1 ))[1,2] ;
344
345        my ( $base_volume, $base_directories ) =
346            $self->splitpath( $base, 1 ) ;
347
348        $path = $self->catpath(
349            $base_volume,
350            $self->catdir( $base_directories, $path_directories ),
351            $path_file
352        ) ;
353    }
354
355    return $self->canonpath( $path ) ;
356}
357
358=back
359
360=head2 Note For File::Spec::Win32 Maintainers
361
362Novell NetWare inherits its File::Spec behaviour from File::Spec::Win32.
363
364=head1 SEE ALSO
365
366L<File::Spec>
367
368=cut
369
3701;
Note: See TracBrowser for help on using the repository browser.