xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/File/Spec/Unix.pm (revision 0:68f95e015346)
1package File::Spec::Unix;
2
3use strict;
4use vars qw($VERSION);
5
6$VERSION = '1.5';
7
8=head1 NAME
9
10File::Spec::Unix - File::Spec for Unix, base for other File::Spec modules
11
12=head1 SYNOPSIS
13
14 require File::Spec::Unix; # Done automatically by File::Spec
15
16=head1 DESCRIPTION
17
18Methods for manipulating file specifications.  Other File::Spec
19modules, such as File::Spec::Mac, inherit from File::Spec::Unix and
20override specific methods.
21
22=head1 METHODS
23
24=over 2
25
26=item canonpath()
27
28No physical check on the filesystem, but a logical cleanup of a
29path. On UNIX eliminates successive slashes and successive "/.".
30
31    $cpath = File::Spec->canonpath( $path ) ;
32
33=cut
34
35sub canonpath {
36    my ($self,$path) = @_;
37
38    # Handle POSIX-style node names beginning with double slash (qnx, nto)
39    # Handle network path names beginning with double slash (cygwin)
40    # (POSIX says: "a pathname that begins with two successive slashes
41    # may be interpreted in an implementation-defined manner, although
42    # more than two leading slashes shall be treated as a single slash.")
43    my $node = '';
44    if ( $^O =~ m/^(?:qnx|nto|cygwin)$/ && $path =~ s:^(//[^/]+)(/|\z):/:s ) {
45      $node = $1;
46    }
47    # This used to be
48    # $path =~ s|/+|/|g unless($^O eq 'cygwin');
49    # but that made tests 29, 30, 35, 46, and 213 (as of #13272) to fail
50    # (Mainly because trailing "" directories didn't get stripped).
51    # Why would cygwin avoid collapsing multiple slashes into one? --jhi
52    $path =~ s|/+|/|g;                             # xx////xx  -> xx/xx
53    $path =~ s@(/\.)+(/|\Z(?!\n))@/@g;             # xx/././xx -> xx/xx
54    $path =~ s|^(\./)+||s unless $path eq "./";    # ./xx      -> xx
55    $path =~ s|^/(\.\./)+|/|s;                     # /../../xx -> xx
56    $path =~ s|/\Z(?!\n)|| unless $path eq "/";          # xx/       -> xx
57    return "$node$path";
58}
59
60=item catdir()
61
62Concatenate two or more directory names to form a complete path ending
63with a directory. But remove the trailing slash from the resulting
64string, because it doesn't look good, isn't necessary and confuses
65OS2. Of course, if this is the root directory, don't cut off the
66trailing slash :-)
67
68=cut
69
70sub catdir {
71    my $self = shift;
72
73    $self->canonpath(join('/', @_, '')); # '' because need a trailing '/'
74}
75
76=item catfile
77
78Concatenate one or more directory names and a filename to form a
79complete path ending with a filename
80
81=cut
82
83sub catfile {
84    my $self = shift;
85    my $file = $self->canonpath(pop @_);
86    return $file unless @_;
87    my $dir = $self->catdir(@_);
88    $dir .= "/" unless substr($dir,-1) eq "/";
89    return $dir.$file;
90}
91
92=item curdir
93
94Returns a string representation of the current directory.  "." on UNIX.
95
96=cut
97
98sub curdir () { '.' }
99
100=item devnull
101
102Returns a string representation of the null device. "/dev/null" on UNIX.
103
104=cut
105
106sub devnull () { '/dev/null' }
107
108=item rootdir
109
110Returns a string representation of the root directory.  "/" on UNIX.
111
112=cut
113
114sub rootdir () { '/' }
115
116=item tmpdir
117
118Returns a string representation of the first writable directory from
119the following list or the current directory if none from the list are
120writable:
121
122    $ENV{TMPDIR}
123    /tmp
124
125Since perl 5.8.0, if running under taint mode, and if $ENV{TMPDIR}
126is tainted, it is not used.
127
128=cut
129
130my $tmpdir;
131sub _tmpdir {
132    return $tmpdir if defined $tmpdir;
133    my $self = shift;
134    my @dirlist = @_;
135    {
136	no strict 'refs';
137	if (${"\cTAINT"}) { # Check for taint mode on perl >= 5.8.0
138            require Scalar::Util;
139	    @dirlist = grep { ! Scalar::Util::tainted($_) } @dirlist;
140	}
141    }
142    foreach (@dirlist) {
143	next unless defined && -d && -w _;
144	$tmpdir = $_;
145	last;
146    }
147    $tmpdir = $self->curdir unless defined $tmpdir;
148    $tmpdir = defined $tmpdir && $self->canonpath($tmpdir);
149    return $tmpdir;
150}
151
152sub tmpdir {
153    return $tmpdir if defined $tmpdir;
154    my $self = shift;
155    $tmpdir = $self->_tmpdir( $ENV{TMPDIR}, "/tmp" );
156}
157
158=item updir
159
160Returns a string representation of the parent directory.  ".." on UNIX.
161
162=cut
163
164sub updir () { '..' }
165
166=item no_upwards
167
168Given a list of file names, strip out those that refer to a parent
169directory. (Does not strip symlinks, only '.', '..', and equivalents.)
170
171=cut
172
173sub no_upwards {
174    my $self = shift;
175    return grep(!/^\.{1,2}\Z(?!\n)/s, @_);
176}
177
178=item case_tolerant
179
180Returns a true or false value indicating, respectively, that alphabetic
181is not or is significant when comparing file specifications.
182
183=cut
184
185sub case_tolerant () { 0 }
186
187=item file_name_is_absolute
188
189Takes as argument a path and returns true if it is an absolute path.
190
191This does not consult the local filesystem on Unix, Win32, OS/2 or Mac
192OS (Classic).  It does consult the working environment for VMS (see
193L<File::Spec::VMS/file_name_is_absolute>).
194
195=cut
196
197sub file_name_is_absolute {
198    my ($self,$file) = @_;
199    return scalar($file =~ m:^/:s);
200}
201
202=item path
203
204Takes no argument, returns the environment variable PATH as an array.
205
206=cut
207
208sub path {
209    return () unless exists $ENV{PATH};
210    my @path = split(':', $ENV{PATH});
211    foreach (@path) { $_ = '.' if $_ eq '' }
212    return @path;
213}
214
215=item join
216
217join is the same as catfile.
218
219=cut
220
221sub join {
222    my $self = shift;
223    return $self->catfile(@_);
224}
225
226=item splitpath
227
228    ($volume,$directories,$file) = File::Spec->splitpath( $path );
229    ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
230
231Splits a path into volume, directory, and filename portions. On systems
232with no concept of volume, returns '' for volume.
233
234For systems with no syntax differentiating filenames from directories,
235assumes that the last file is a path unless $no_file is true or a
236trailing separator or /. or /.. is present. On Unix this means that $no_file
237true makes this return ( '', $path, '' ).
238
239The directory portion may or may not be returned with a trailing '/'.
240
241The results can be passed to L</catpath()> to get back a path equivalent to
242(usually identical to) the original path.
243
244=cut
245
246sub splitpath {
247    my ($self,$path, $nofile) = @_;
248
249    my ($volume,$directory,$file) = ('','','');
250
251    if ( $nofile ) {
252        $directory = $path;
253    }
254    else {
255        $path =~ m|^ ( (?: .* / (?: \.\.?\Z(?!\n) )? )? ) ([^/]*) |xs;
256        $directory = $1;
257        $file      = $2;
258    }
259
260    return ($volume,$directory,$file);
261}
262
263
264=item splitdir
265
266The opposite of L</catdir()>.
267
268    @dirs = File::Spec->splitdir( $directories );
269
270$directories must be only the directory portion of the path on systems
271that have the concept of a volume or that have path syntax that differentiates
272files from directories.
273
274Unlike just splitting the directories on the separator, empty
275directory names (C<''>) can be returned, because these are significant
276on some OSs.
277
278On Unix,
279
280    File::Spec->splitdir( "/a/b//c/" );
281
282Yields:
283
284    ( '', 'a', 'b', '', 'c', '' )
285
286=cut
287
288sub splitdir {
289    return split m|/|, $_[1], -1;  # Preserve trailing fields
290}
291
292
293=item catpath()
294
295Takes volume, directory and file portions and returns an entire path. Under
296Unix, $volume is ignored, and directory and file are concatenated.  A '/' is
297inserted if needed (though if the directory portion doesn't start with
298'/' it is not added).  On other OSs, $volume is significant.
299
300=cut
301
302sub catpath {
303    my ($self,$volume,$directory,$file) = @_;
304
305    if ( $directory ne ''                &&
306         $file ne ''                     &&
307         substr( $directory, -1 ) ne '/' &&
308         substr( $file, 0, 1 ) ne '/'
309    ) {
310        $directory .= "/$file" ;
311    }
312    else {
313        $directory .= $file ;
314    }
315
316    return $directory ;
317}
318
319=item abs2rel
320
321Takes a destination path and an optional base path returns a relative path
322from the base path to the destination path:
323
324    $rel_path = File::Spec->abs2rel( $path ) ;
325    $rel_path = File::Spec->abs2rel( $path, $base ) ;
326
327If $base is not present or '', then L<cwd()|Cwd> is used. If $base is
328relative, then it is converted to absolute form using
329L</rel2abs()>. This means that it is taken to be relative to
330L<cwd()|Cwd>.
331
332On systems that have a grammar that indicates filenames, this ignores the
333$base filename. Otherwise all path components are assumed to be
334directories.
335
336If $path is relative, it is converted to absolute form using L</rel2abs()>.
337This means that it is taken to be relative to L<cwd()|Cwd>.
338
339No checks against the filesystem are made.  On VMS, there is
340interaction with the working environment, as logicals and
341macros are expanded.
342
343Based on code written by Shigio Yamaguchi.
344
345=cut
346
347sub abs2rel {
348    my($self,$path,$base) = @_;
349
350    # Clean up $path
351    if ( ! $self->file_name_is_absolute( $path ) ) {
352        $path = $self->rel2abs( $path ) ;
353    }
354    else {
355        $path = $self->canonpath( $path ) ;
356    }
357
358    # Figure out the effective $base and clean it up.
359    if ( !defined( $base ) || $base eq '' ) {
360        $base = $self->_cwd();
361    }
362    elsif ( ! $self->file_name_is_absolute( $base ) ) {
363        $base = $self->rel2abs( $base ) ;
364    }
365    else {
366        $base = $self->canonpath( $base ) ;
367    }
368
369    # Now, remove all leading components that are the same
370    my @pathchunks = $self->splitdir( $path);
371    my @basechunks = $self->splitdir( $base);
372
373    while (@pathchunks && @basechunks && $pathchunks[0] eq $basechunks[0]) {
374        shift @pathchunks ;
375        shift @basechunks ;
376    }
377
378    $path = CORE::join( '/', @pathchunks );
379    $base = CORE::join( '/', @basechunks );
380
381    # $base now contains the directories the resulting relative path
382    # must ascend out of before it can descend to $path_directory.  So,
383    # replace all names with $parentDir
384    $base =~ s|[^/]+|..|g ;
385
386    # Glue the two together, using a separator if necessary, and preventing an
387    # empty result.
388    if ( $path ne '' && $base ne '' ) {
389        $path = "$base/$path" ;
390    } else {
391        $path = "$base$path" ;
392    }
393
394    return $self->canonpath( $path ) ;
395}
396
397=item rel2abs()
398
399Converts a relative path to an absolute path.
400
401    $abs_path = File::Spec->rel2abs( $path ) ;
402    $abs_path = File::Spec->rel2abs( $path, $base ) ;
403
404If $base is not present or '', then L<cwd()|Cwd> is used. If $base is
405relative, then it is converted to absolute form using
406L</rel2abs()>. This means that it is taken to be relative to
407L<cwd()|Cwd>.
408
409On systems that have a grammar that indicates filenames, this ignores
410the $base filename. Otherwise all path components are assumed to be
411directories.
412
413If $path is absolute, it is cleaned up and returned using L</canonpath()>.
414
415No checks against the filesystem are made.  On VMS, there is
416interaction with the working environment, as logicals and
417macros are expanded.
418
419Based on code written by Shigio Yamaguchi.
420
421=cut
422
423sub rel2abs {
424    my ($self,$path,$base ) = @_;
425
426    # Clean up $path
427    if ( ! $self->file_name_is_absolute( $path ) ) {
428        # Figure out the effective $base and clean it up.
429        if ( !defined( $base ) || $base eq '' ) {
430	    $base = $self->_cwd();
431        }
432        elsif ( ! $self->file_name_is_absolute( $base ) ) {
433            $base = $self->rel2abs( $base ) ;
434        }
435        else {
436            $base = $self->canonpath( $base ) ;
437        }
438
439        # Glom them together
440        $path = $self->catdir( $base, $path ) ;
441    }
442
443    return $self->canonpath( $path ) ;
444}
445
446=back
447
448=head1 SEE ALSO
449
450L<File::Spec>
451
452=cut
453
454# Internal routine to File::Spec, no point in making this public since
455# it is the standard Cwd interface.  Most of the platform-specific
456# File::Spec subclasses use this.
457sub _cwd {
458    require Cwd;
459    Cwd::cwd();
460}
461
4621;
463