xref: /openbsd-src/gnu/usr.bin/perl/dist/PathTools/lib/File/Spec/VMS.pm (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1package File::Spec::VMS;
2
3use strict;
4use vars qw(@ISA $VERSION);
5require File::Spec::Unix;
6
7$VERSION = '3.48_03';
8$VERSION =~ tr/_//;
9
10@ISA = qw(File::Spec::Unix);
11
12use File::Basename;
13use VMS::Filespec;
14
15=head1 NAME
16
17File::Spec::VMS - methods for VMS file specs
18
19=head1 SYNOPSIS
20
21 require File::Spec::VMS; # Done internally by File::Spec if needed
22
23=head1 DESCRIPTION
24
25See File::Spec::Unix for a documentation of the methods provided
26there. This package overrides the implementation of these methods, not
27the semantics.
28
29The default behavior is to allow either VMS or Unix syntax on input and to
30return VMS syntax on output unless Unix syntax has been explicitly requested
31via the C<DECC$FILENAME_UNIX_REPORT> CRTL feature.
32
33=over 4
34
35=cut
36
37# Need to look up the feature settings.  The preferred way is to use the
38# VMS::Feature module, but that may not be available to dual life modules.
39
40my $use_feature;
41BEGIN {
42    if (eval { local $SIG{__DIE__};
43               local @INC = @INC;
44               pop @INC if $INC[-1] eq '.';
45               require VMS::Feature; }) {
46        $use_feature = 1;
47    }
48}
49
50# Need to look up the UNIX report mode.  This may become a dynamic mode
51# in the future.
52sub _unix_rpt {
53    my $unix_rpt;
54    if ($use_feature) {
55        $unix_rpt = VMS::Feature::current("filename_unix_report");
56    } else {
57        my $env_unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
58        $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
59    }
60    return $unix_rpt;
61}
62
63=item canonpath (override)
64
65Removes redundant portions of file specifications and returns results
66in native syntax unless Unix filename reporting has been enabled.
67
68=cut
69
70
71sub canonpath {
72    my($self,$path) = @_;
73
74    return undef unless defined $path;
75
76    my $unix_rpt = $self->_unix_rpt;
77
78    if ($path =~ m|/|) {
79      my $pathify = $path =~ m|/\Z(?!\n)|;
80      $path = $self->SUPER::canonpath($path);
81
82      return $path if $unix_rpt;
83      $path = $pathify ? vmspath($path) : vmsify($path);
84    }
85
86    $path =~ s/(?<!\^)</[/;			# < and >       ==> [ and ]
87    $path =~ s/(?<!\^)>/]/;
88    $path =~ s/(?<!\^)\]\[\./\.\]\[/g;		# ][.		==> .][
89    $path =~ s/(?<!\^)\[000000\.\]\[/\[/g;	# [000000.][	==> [
90    $path =~ s/(?<!\^)\[000000\./\[/g;		# [000000.	==> [
91    $path =~ s/(?<!\^)\.\]\[000000\]/\]/g;	# .][000000]	==> ]
92    $path =~ s/(?<!\^)\.\]\[/\./g;		# foo.][bar     ==> foo.bar
93    1 while ($path =~ s/(?<!\^)([\[\.])(-+)\.(-+)([\.\]])/$1$2$3$4/);
94						# That loop does the following
95						# with any amount of dashes:
96						# .-.-.		==> .--.
97						# [-.-.		==> [--.
98						# .-.-]		==> .--]
99						# [-.-]		==> [--]
100    1 while ($path =~ s/(?<!\^)([\[\.])[^\]\.]+\.-(-+)([\]\.])/$1$2$3/);
101						# That loop does the following
102						# with any amount (minimum 2)
103						# of dashes:
104						# .foo.--.	==> .-.
105						# .foo.--]	==> .-]
106						# [foo.--.	==> [-.
107						# [foo.--]	==> [-]
108						#
109						# And then, the remaining cases
110    $path =~ s/(?<!\^)\[\.-/[-/;		# [.-		==> [-
111    $path =~ s/(?<!\^)\.[^\]\.]+\.-\./\./g;	# .foo.-.	==> .
112    $path =~ s/(?<!\^)\[[^\]\.]+\.-\./\[/g;	# [foo.-.	==> [
113    $path =~ s/(?<!\^)\.[^\]\.]+\.-\]/\]/g;	# .foo.-]	==> ]
114						# [foo.-]       ==> [000000]
115    $path =~ s/(?<!\^)\[[^\]\.]+\.-\]/\[000000\]/g;
116						# []		==>
117    $path =~ s/(?<!\^)\[\]// unless $path eq '[]';
118    return $unix_rpt ? unixify($path) : $path;
119}
120
121=item catdir (override)
122
123Concatenates a list of file specifications, and returns the result as a
124native directory specification unless the Unix filename reporting feature
125has been enabled.  No check is made for "impossible" cases (e.g. elements
126other than the first being absolute filespecs).
127
128=cut
129
130sub catdir {
131    my $self = shift;
132    my $dir = pop;
133
134    my $unix_rpt = $self->_unix_rpt;
135
136    my @dirs = grep {defined() && length()} @_;
137
138    my $rslt;
139    if (@dirs) {
140	my $path = (@dirs == 1 ? $dirs[0] : $self->catdir(@dirs));
141	my ($spath,$sdir) = ($path,$dir);
142	$spath =~ s/\.dir\Z(?!\n)//i; $sdir =~ s/\.dir\Z(?!\n)//i;
143
144	if ($unix_rpt) {
145	    $spath = unixify($spath) unless $spath =~ m#/#;
146	    $sdir= unixify($sdir) unless $sdir =~ m#/#;
147            return $self->SUPER::catdir($spath, $sdir)
148	}
149
150	$sdir = $self->eliminate_macros($sdir) unless $sdir =~ /^[\w\-]+\Z(?!\n)/s;
151	$rslt = $self->fixpath($self->eliminate_macros($spath)."/$sdir",1);
152
153	# Special case for VMS absolute directory specs: these will have
154	# had device prepended during trip through Unix syntax in
155	# eliminate_macros(), since Unix syntax has no way to express
156	# "absolute from the top of this device's directory tree".
157	if ($spath =~ /^[\[<][^.\-]/s) { $rslt =~ s/^[^\[<]+//s; }
158
159    } else {
160	# Single directory. Return an empty string on null input; otherwise
161	# just return a canonical path.
162
163	if    (not defined $dir or not length $dir) {
164	    $rslt = '';
165	} else {
166	    $rslt = $unix_rpt ? $dir : vmspath($dir);
167	}
168    }
169    return $self->canonpath($rslt);
170}
171
172=item catfile (override)
173
174Concatenates a list of directory specifications with a filename specification
175to build a path.
176
177=cut
178
179sub catfile {
180    my $self = shift;
181    my $tfile = pop();
182    my $file = $self->canonpath($tfile);
183    my @files = grep {defined() && length()} @_;
184
185    my $unix_rpt = $self->_unix_rpt;
186
187    my $rslt;
188    if (@files) {
189	my $path = (@files == 1 ? $files[0] : $self->catdir(@files));
190	my $spath = $path;
191
192        # Something building a VMS path in pieces may try to pass a
193        # directory name in filename format, so normalize it.
194	$spath =~ s/\.dir\Z(?!\n)//i;
195
196        # If the spath ends with a directory delimiter and the file is bare,
197        # then just concatenate them.
198	if ($spath =~ /^(?<!\^)[^\)\]\/:>]+\)\Z(?!\n)/s && basename($file) eq $file) {
199	    $rslt = "$spath$file";
200	} else {
201           $rslt = $self->eliminate_macros($spath);
202           $rslt .= (defined($rslt) && length($rslt) ? '/' : '') . unixify($file);
203           $rslt = vmsify($rslt) unless $unix_rpt;
204	}
205    }
206    else {
207        # Only passed a single file?
208        my $xfile = (defined($file) && length($file)) ? $file : '';
209
210        $rslt = $unix_rpt ? $file : vmsify($file);
211    }
212    return $self->canonpath($rslt) unless $unix_rpt;
213
214    # In Unix report mode, do not strip off redundant path information.
215    return $rslt;
216}
217
218
219=item curdir (override)
220
221Returns a string representation of the current directory: '[]' or '.'
222
223=cut
224
225sub curdir {
226    my $self = shift @_;
227    return '.' if ($self->_unix_rpt);
228    return '[]';
229}
230
231=item devnull (override)
232
233Returns a string representation of the null device: '_NLA0:' or '/dev/null'
234
235=cut
236
237sub devnull {
238    my $self = shift @_;
239    return '/dev/null' if ($self->_unix_rpt);
240    return "_NLA0:";
241}
242
243=item rootdir (override)
244
245Returns a string representation of the root directory: 'SYS$DISK:[000000]'
246or '/'
247
248=cut
249
250sub rootdir {
251    my $self = shift @_;
252    if ($self->_unix_rpt) {
253       # Root may exist, try it first.
254       my $try = '/';
255       my ($dev1, $ino1) = stat('/');
256       my ($dev2, $ino2) = stat('.');
257
258       # Perl falls back to '.' if it can not determine '/'
259       if (($dev1 != $dev2) || ($ino1 != $ino2)) {
260           return $try;
261       }
262       # Fall back to UNIX format sys$disk.
263       return '/sys$disk/';
264    }
265    return 'SYS$DISK:[000000]';
266}
267
268=item tmpdir (override)
269
270Returns a string representation of the first writable directory
271from the following list or '' if none are writable:
272
273    /tmp if C<DECC$FILENAME_UNIX_REPORT> is enabled.
274    sys$scratch:
275    $ENV{TMPDIR}
276
277If running under taint mode, and if $ENV{TMPDIR}
278is tainted, it is not used.
279
280=cut
281
282sub tmpdir {
283    my $self = shift @_;
284    my $tmpdir = $self->_cached_tmpdir('TMPDIR');
285    return $tmpdir if defined $tmpdir;
286    if ($self->_unix_rpt) {
287        $tmpdir = $self->_tmpdir('/tmp', '/sys$scratch', $ENV{TMPDIR});
288    }
289    else {
290        $tmpdir = $self->_tmpdir( 'sys$scratch:', $ENV{TMPDIR} );
291    }
292    $self->_cache_tmpdir($tmpdir, 'TMPDIR');
293}
294
295=item updir (override)
296
297Returns a string representation of the parent directory: '[-]' or '..'
298
299=cut
300
301sub updir {
302    my $self = shift @_;
303    return '..' if ($self->_unix_rpt);
304    return '[-]';
305}
306
307=item case_tolerant (override)
308
309VMS file specification syntax is case-tolerant.
310
311=cut
312
313sub case_tolerant {
314    return 1;
315}
316
317=item path (override)
318
319Translate logical name DCL$PATH as a searchlist, rather than trying
320to C<split> string value of C<$ENV{'PATH'}>.
321
322=cut
323
324sub path {
325    my (@dirs,$dir,$i);
326    while ($dir = $ENV{'DCL$PATH;' . $i++}) { push(@dirs,$dir); }
327    return @dirs;
328}
329
330=item file_name_is_absolute (override)
331
332Checks for VMS directory spec as well as Unix separators.
333
334=cut
335
336sub file_name_is_absolute {
337    my ($self,$file) = @_;
338    # If it's a logical name, expand it.
339    $file = $ENV{$file} while $file =~ /^[\w\$\-]+\Z(?!\n)/s && $ENV{$file};
340    return scalar($file =~ m!^/!s             ||
341		  $file =~ m![<\[][^.\-\]>]!  ||
342		  $file =~ /^[A-Za-z0-9_\$\-\~]+(?<!\^):/);
343}
344
345=item splitpath (override)
346
347   ($volume,$directories,$file) = File::Spec->splitpath( $path );
348   ($volume,$directories,$file) = File::Spec->splitpath( $path,
349                                                         $no_file );
350
351Passing a true value for C<$no_file> indicates that the path being
352split only contains directory components, even on systems where you
353can usually (when not supporting a foreign syntax) tell the difference
354between directories and files at a glance.
355
356=cut
357
358sub splitpath {
359    my($self,$path, $nofile) = @_;
360    my($dev,$dir,$file)      = ('','','');
361    my $vmsify_path = vmsify($path);
362
363    if ( $nofile ) {
364        #vmsify('d1/d2/d3') returns '[.d1.d2]d3'
365        #vmsify('/d1/d2/d3') returns 'd1:[d2]d3'
366        if( $vmsify_path =~ /(.*)\](.+)/ ){
367            $vmsify_path = $1.'.'.$2.']';
368        }
369        $vmsify_path =~ /(.+:)?(.*)/s;
370        $dir = defined $2 ? $2 : ''; # dir can be '0'
371        return ($1 || '',$dir,$file);
372    }
373    else {
374        $vmsify_path =~ /(.+:)?([\[<].*[\]>])?(.*)/s;
375        return ($1 || '',$2 || '',$3);
376    }
377}
378
379=item splitdir (override)
380
381Split a directory specification into the components.
382
383=cut
384
385sub splitdir {
386    my($self,$dirspec) = @_;
387    my @dirs = ();
388    return @dirs if ( (!defined $dirspec) || ('' eq $dirspec) );
389
390    $dirspec =~ s/(?<!\^)</[/;                  # < and >	==> [ and ]
391    $dirspec =~ s/(?<!\^)>/]/;
392    $dirspec =~ s/(?<!\^)\]\[\./\.\]\[/g;	# ][.		==> .][
393    $dirspec =~ s/(?<!\^)\[000000\.\]\[/\[/g;	# [000000.][	==> [
394    $dirspec =~ s/(?<!\^)\[000000\./\[/g;	# [000000.	==> [
395    $dirspec =~ s/(?<!\^)\.\]\[000000\]/\]/g;	# .][000000]	==> ]
396    $dirspec =~ s/(?<!\^)\.\]\[/\./g;		# foo.][bar	==> foo.bar
397    while ($dirspec =~ s/(^|[\[\<\.])\-(\-+)($|[\]\>\.])/$1-.$2$3/g) {}
398						# That loop does the following
399						# with any amount of dashes:
400						# .--.		==> .-.-.
401						# [--.		==> [-.-.
402						# .--]		==> .-.-]
403						# [--]		==> [-.-]
404    $dirspec = "[$dirspec]" unless $dirspec =~ /(?<!\^)[\[<]/; # make legal
405    $dirspec =~ s/^(\[|<)\./$1/;
406    @dirs = split /(?<!\^)\./, vmspath($dirspec);
407    $dirs[0] =~ s/^[\[<]//s;  $dirs[-1] =~ s/[\]>]\Z(?!\n)//s;
408    @dirs;
409}
410
411
412=item catpath (override)
413
414Construct a complete filespec.
415
416=cut
417
418sub catpath {
419    my($self,$dev,$dir,$file) = @_;
420
421    # We look for a volume in $dev, then in $dir, but not both
422    my ($dir_volume, $dir_dir, $dir_file) = $self->splitpath($dir);
423    $dev = $dir_volume unless length $dev;
424    $dir = length $dir_file ? $self->catfile($dir_dir, $dir_file) : $dir_dir;
425
426    if ($dev =~ m|^(?<!\^)/+([^/]+)|) { $dev = "$1:"; }
427    else { $dev .= ':' unless $dev eq '' or $dev =~ /:\Z(?!\n)/; }
428    if (length($dev) or length($dir)) {
429        $dir = "[$dir]" unless $dir =~ /(?<!\^)[\[<\/]/;
430        $dir = vmspath($dir);
431    }
432    $dir = '' if length($dev) && ($dir eq '[]' || $dir eq '<>');
433    "$dev$dir$file";
434}
435
436=item abs2rel (override)
437
438Attempt to convert an absolute file specification to a relative specification.
439
440=cut
441
442sub abs2rel {
443    my $self = shift;
444    return vmspath(File::Spec::Unix::abs2rel( $self, @_ ))
445        if grep m{/}, @_;
446
447    my($path,$base) = @_;
448    $base = $self->_cwd() unless defined $base and length $base;
449
450    for ($path, $base) { $_ = $self->canonpath($_) }
451
452    # Are we even starting $path on the same (node::)device as $base?  Note that
453    # logical paths or nodename differences may be on the "same device"
454    # but the comparison that ignores device differences so as to concatenate
455    # [---] up directory specs is not even a good idea in cases where there is
456    # a logical path difference between $path and $base nodename and/or device.
457    # Hence we fall back to returning the absolute $path spec
458    # if there is a case blind device (or node) difference of any sort
459    # and we do not even try to call $parse() or consult %ENV for $trnlnm()
460    # (this module needs to run on non VMS platforms after all).
461
462    my ($path_volume, $path_directories, $path_file) = $self->splitpath($path);
463    my ($base_volume, $base_directories, $base_file) = $self->splitpath($base);
464    return $path unless lc($path_volume) eq lc($base_volume);
465
466    for ($path, $base) { $_ = $self->rel2abs($_) }
467
468    # Now, remove all leading components that are the same
469    my @pathchunks = $self->splitdir( $path_directories );
470    my $pathchunks = @pathchunks;
471    unshift(@pathchunks,'000000') unless $pathchunks[0] eq '000000';
472    my @basechunks = $self->splitdir( $base_directories );
473    my $basechunks = @basechunks;
474    unshift(@basechunks,'000000') unless $basechunks[0] eq '000000';
475
476    while ( @pathchunks &&
477            @basechunks &&
478            lc( $pathchunks[0] ) eq lc( $basechunks[0] )
479          ) {
480        shift @pathchunks ;
481        shift @basechunks ;
482    }
483
484    # @basechunks now contains the directories to climb out of,
485    # @pathchunks now has the directories to descend in to.
486    if ((@basechunks > 0) || ($basechunks != $pathchunks)) {
487      $path_directories = join '.', ('-' x @basechunks, @pathchunks) ;
488    }
489    else {
490      $path_directories = join '.', @pathchunks;
491    }
492    $path_directories = '['.$path_directories.']';
493    return $self->canonpath( $self->catpath( '', $path_directories, $path_file ) ) ;
494}
495
496
497=item rel2abs (override)
498
499Return an absolute file specification from a relative one.
500
501=cut
502
503sub rel2abs {
504    my $self = shift ;
505    my ($path,$base ) = @_;
506    return undef unless defined $path;
507    if ($path =~ m/\//) {
508       $path = ( -d $path || $path =~ m/\/\z/  # educated guessing about
509                  ? vmspath($path)             # whether it's a directory
510                  : vmsify($path) );
511    }
512    $base = vmspath($base) if defined $base && $base =~ m/\//;
513
514    # Clean up and split up $path
515    if ( ! $self->file_name_is_absolute( $path ) ) {
516        # Figure out the effective $base and clean it up.
517        if ( !defined( $base ) || $base eq '' ) {
518            $base = $self->_cwd;
519        }
520        elsif ( ! $self->file_name_is_absolute( $base ) ) {
521            $base = $self->rel2abs( $base ) ;
522        }
523        else {
524            $base = $self->canonpath( $base ) ;
525        }
526
527        # Split up paths
528        my ( $path_directories, $path_file ) =
529            ($self->splitpath( $path ))[1,2] ;
530
531        my ( $base_volume, $base_directories ) =
532            $self->splitpath( $base ) ;
533
534        $path_directories = '' if $path_directories eq '[]' ||
535                                  $path_directories eq '<>';
536        my $sep = '' ;
537        $sep = '.'
538            if ( $base_directories =~ m{[^.\]>]\Z(?!\n)} &&
539                 $path_directories =~ m{^[^.\[<]}s
540            ) ;
541        $base_directories = "$base_directories$sep$path_directories";
542        $base_directories =~ s{\.?[\]>][\[<]\.?}{.};
543
544        $path = $self->catpath( $base_volume, $base_directories, $path_file );
545   }
546
547    return $self->canonpath( $path ) ;
548}
549
550
551# eliminate_macros() and fixpath() are MakeMaker-specific methods
552# which are used inside catfile() and catdir().  MakeMaker has its own
553# copies as of 6.06_03 which are the canonical ones.  We leave these
554# here, in peace, so that File::Spec continues to work with MakeMakers
555# prior to 6.06_03.
556#
557# Please consider these two methods deprecated.  Do not patch them,
558# patch the ones in ExtUtils::MM_VMS instead.
559#
560# Update:  MakeMaker 6.48 is still using these routines on VMS.
561# so they need to be kept up to date with ExtUtils::MM_VMS.
562
563sub eliminate_macros {
564    my($self,$path) = @_;
565    return '' unless (defined $path) && ($path ne '');
566    $self = {} unless ref $self;
567
568    if ($path =~ /\s/) {
569      return join ' ', map { $self->eliminate_macros($_) } split /\s+/, $path;
570    }
571
572    my $npath = unixify($path);
573    # sometimes unixify will return a string with an off-by-one trailing null
574    $npath =~ s{\0$}{};
575
576    my($complex) = 0;
577    my($head,$macro,$tail);
578
579    # perform m##g in scalar context so it acts as an iterator
580    while ($npath =~ m#(.*?)\$\((\S+?)\)(.*)#gs) {
581        if (defined $self->{$2}) {
582            ($head,$macro,$tail) = ($1,$2,$3);
583            if (ref $self->{$macro}) {
584                if (ref $self->{$macro} eq 'ARRAY') {
585                    $macro = join ' ', @{$self->{$macro}};
586                }
587                else {
588                    print "Note: can't expand macro \$($macro) containing ",ref($self->{$macro}),
589                          "\n\t(using MMK-specific deferred substitutuon; MMS will break)\n";
590                    $macro = "\cB$macro\cB";
591                    $complex = 1;
592                }
593            }
594            else { ($macro = unixify($self->{$macro})) =~ s#/\Z(?!\n)##; }
595            $npath = "$head$macro$tail";
596        }
597    }
598    if ($complex) { $npath =~ s#\cB(.*?)\cB#\${$1}#gs; }
599    $npath;
600}
601
602# Deprecated.  See the note above for eliminate_macros().
603
604# Catchall routine to clean up problem MM[SK]/Make macros.  Expands macros
605# in any directory specification, in order to avoid juxtaposing two
606# VMS-syntax directories when MM[SK] is run.  Also expands expressions which
607# are all macro, so that we can tell how long the expansion is, and avoid
608# overrunning DCL's command buffer when MM[KS] is running.
609
610# fixpath() checks to see whether the result matches the name of a
611# directory in the current default directory and returns a directory or
612# file specification accordingly.  C<$is_dir> can be set to true to
613# force fixpath() to consider the path to be a directory or false to force
614# it to be a file.
615
616sub fixpath {
617    my($self,$path,$force_path) = @_;
618    return '' unless $path;
619    $self = bless {}, $self unless ref $self;
620    my($fixedpath,$prefix,$name);
621
622    if ($path =~ /\s/) {
623      return join ' ',
624             map { $self->fixpath($_,$force_path) }
625	     split /\s+/, $path;
626    }
627
628    if ($path =~ m#^\$\([^\)]+\)\Z(?!\n)#s || $path =~ m#[/:>\]]#) {
629        if ($force_path or $path =~ /(?:DIR\)|\])\Z(?!\n)/) {
630            $fixedpath = vmspath($self->eliminate_macros($path));
631        }
632        else {
633            $fixedpath = vmsify($self->eliminate_macros($path));
634        }
635    }
636    elsif ((($prefix,$name) = ($path =~ m#^\$\(([^\)]+)\)(.+)#s)) && $self->{$prefix}) {
637        my($vmspre) = $self->eliminate_macros("\$($prefix)");
638        # is it a dir or just a name?
639        $vmspre = ($vmspre =~ m|/| or $prefix =~ /DIR\Z(?!\n)/) ? vmspath($vmspre) : '';
640        $fixedpath = ($vmspre ? $vmspre : $self->{$prefix}) . $name;
641        $fixedpath = vmspath($fixedpath) if $force_path;
642    }
643    else {
644        $fixedpath = $path;
645        $fixedpath = vmspath($fixedpath) if $force_path;
646    }
647    # No hints, so we try to guess
648    if (!defined($force_path) and $fixedpath !~ /[:>(.\]]/) {
649        $fixedpath = vmspath($fixedpath) if -d $fixedpath;
650    }
651
652    # Trim off root dirname if it's had other dirs inserted in front of it.
653    $fixedpath =~ s/\.000000([\]>])/$1/;
654    # Special case for VMS absolute directory specs: these will have had device
655    # prepended during trip through Unix syntax in eliminate_macros(), since
656    # Unix syntax has no way to express "absolute from the top of this device's
657    # directory tree".
658    if ($path =~ /^[\[>][^.\-]/) { $fixedpath =~ s/^[^\[<]+//; }
659    $fixedpath;
660}
661
662
663=back
664
665=head1 COPYRIGHT
666
667Copyright (c) 2004 by the Perl 5 Porters.  All rights reserved.
668
669This program is free software; you can redistribute it and/or modify
670it under the same terms as Perl itself.
671
672=head1 SEE ALSO
673
674See L<File::Spec> and L<File::Spec::Unix>.  This package overrides the
675implementation of these methods, not the semantics.
676
677An explanation of VMS file specs can be found at
678L<http://h71000.www7.hp.com/doc/731FINAL/4506/4506pro_014.html#apps_locating_naming_files>.
679
680=cut
681
6821;
683