xref: /openbsd-src/gnu/usr.bin/perl/dist/PathTools/lib/File/Spec/VMS.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
16fb12b70Safresh1package File::Spec::VMS;
26fb12b70Safresh1
36fb12b70Safresh1use strict;
49f11ffb7Safresh1use Cwd ();
56fb12b70Safresh1require File::Spec::Unix;
66fb12b70Safresh1
7*3d61058aSafresh1our $VERSION = '3.91';
8b8851fccSafresh1$VERSION =~ tr/_//d;
96fb12b70Safresh1
109f11ffb7Safresh1our @ISA = qw(File::Spec::Unix);
116fb12b70Safresh1
126fb12b70Safresh1use File::Basename;
136fb12b70Safresh1use VMS::Filespec;
146fb12b70Safresh1
156fb12b70Safresh1=head1 NAME
166fb12b70Safresh1
176fb12b70Safresh1File::Spec::VMS - methods for VMS file specs
186fb12b70Safresh1
196fb12b70Safresh1=head1 SYNOPSIS
206fb12b70Safresh1
216fb12b70Safresh1 require File::Spec::VMS; # Done internally by File::Spec if needed
226fb12b70Safresh1
236fb12b70Safresh1=head1 DESCRIPTION
246fb12b70Safresh1
256fb12b70Safresh1See File::Spec::Unix for a documentation of the methods provided
266fb12b70Safresh1there. This package overrides the implementation of these methods, not
276fb12b70Safresh1the semantics.
286fb12b70Safresh1
296fb12b70Safresh1The default behavior is to allow either VMS or Unix syntax on input and to
306fb12b70Safresh1return VMS syntax on output unless Unix syntax has been explicitly requested
316fb12b70Safresh1via the C<DECC$FILENAME_UNIX_REPORT> CRTL feature.
326fb12b70Safresh1
336fb12b70Safresh1=over 4
346fb12b70Safresh1
356fb12b70Safresh1=cut
366fb12b70Safresh1
376fb12b70Safresh1# Need to look up the feature settings.  The preferred way is to use the
386fb12b70Safresh1# VMS::Feature module, but that may not be available to dual life modules.
396fb12b70Safresh1
406fb12b70Safresh1my $use_feature;
416fb12b70Safresh1BEGIN {
420b7734b3Safresh1    if (eval { local $SIG{__DIE__};
430b7734b3Safresh1               local @INC = @INC;
440b7734b3Safresh1               pop @INC if $INC[-1] eq '.';
450b7734b3Safresh1               require VMS::Feature; }) {
466fb12b70Safresh1        $use_feature = 1;
476fb12b70Safresh1    }
486fb12b70Safresh1}
496fb12b70Safresh1
506fb12b70Safresh1# Need to look up the UNIX report mode.  This may become a dynamic mode
516fb12b70Safresh1# in the future.
526fb12b70Safresh1sub _unix_rpt {
536fb12b70Safresh1    my $unix_rpt;
546fb12b70Safresh1    if ($use_feature) {
556fb12b70Safresh1        $unix_rpt = VMS::Feature::current("filename_unix_report");
566fb12b70Safresh1    } else {
576fb12b70Safresh1        my $env_unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
586fb12b70Safresh1        $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
596fb12b70Safresh1    }
606fb12b70Safresh1    return $unix_rpt;
616fb12b70Safresh1}
626fb12b70Safresh1
636fb12b70Safresh1=item canonpath (override)
646fb12b70Safresh1
656fb12b70Safresh1Removes redundant portions of file specifications and returns results
666fb12b70Safresh1in native syntax unless Unix filename reporting has been enabled.
676fb12b70Safresh1
686fb12b70Safresh1=cut
696fb12b70Safresh1
706fb12b70Safresh1
716fb12b70Safresh1sub canonpath {
726fb12b70Safresh1    my($self,$path) = @_;
736fb12b70Safresh1
746fb12b70Safresh1    return undef unless defined $path;
756fb12b70Safresh1
766fb12b70Safresh1    my $unix_rpt = $self->_unix_rpt;
776fb12b70Safresh1
786fb12b70Safresh1    if ($path =~ m|/|) {
796fb12b70Safresh1      my $pathify = $path =~ m|/\Z(?!\n)|;
806fb12b70Safresh1      $path = $self->SUPER::canonpath($path);
816fb12b70Safresh1
826fb12b70Safresh1      return $path if $unix_rpt;
836fb12b70Safresh1      $path = $pathify ? vmspath($path) : vmsify($path);
846fb12b70Safresh1    }
856fb12b70Safresh1
866fb12b70Safresh1    $path =~ s/(?<!\^)</[/;			# < and >       ==> [ and ]
876fb12b70Safresh1    $path =~ s/(?<!\^)>/]/;
886fb12b70Safresh1    $path =~ s/(?<!\^)\]\[\./\.\]\[/g;		# ][.		==> .][
896fb12b70Safresh1    $path =~ s/(?<!\^)\[000000\.\]\[/\[/g;	# [000000.][	==> [
906fb12b70Safresh1    $path =~ s/(?<!\^)\[000000\./\[/g;		# [000000.	==> [
916fb12b70Safresh1    $path =~ s/(?<!\^)\.\]\[000000\]/\]/g;	# .][000000]	==> ]
926fb12b70Safresh1    $path =~ s/(?<!\^)\.\]\[/\./g;		# foo.][bar     ==> foo.bar
936fb12b70Safresh1    1 while ($path =~ s/(?<!\^)([\[\.])(-+)\.(-+)([\.\]])/$1$2$3$4/);
946fb12b70Safresh1						# That loop does the following
956fb12b70Safresh1						# with any amount of dashes:
966fb12b70Safresh1						# .-.-.		==> .--.
976fb12b70Safresh1						# [-.-.		==> [--.
986fb12b70Safresh1						# .-.-]		==> .--]
996fb12b70Safresh1						# [-.-]		==> [--]
1009f11ffb7Safresh1    1 while ($path =~ s/(?<!\^)([\[\.])(?:\^.|[^\]\.])+\.-(-+)([\]\.])/$1$2$3/);
1016fb12b70Safresh1						# That loop does the following
1026fb12b70Safresh1						# with any amount (minimum 2)
1036fb12b70Safresh1						# of dashes:
1046fb12b70Safresh1						# .foo.--.	==> .-.
1056fb12b70Safresh1						# .foo.--]	==> .-]
1066fb12b70Safresh1						# [foo.--.	==> [-.
1076fb12b70Safresh1						# [foo.--]	==> [-]
1086fb12b70Safresh1						#
1096fb12b70Safresh1						# And then, the remaining cases
1106fb12b70Safresh1    $path =~ s/(?<!\^)\[\.-/[-/;		# [.-		==> [-
1119f11ffb7Safresh1    $path =~ s/(?<!\^)\.(?:\^.|[^\]\.])+\.-\./\./g;	# .foo.-.	==> .
1129f11ffb7Safresh1    $path =~ s/(?<!\^)\[(?:\^.|[^\]\.])+\.-\./\[/g;	# [foo.-.	==> [
1139f11ffb7Safresh1    $path =~ s/(?<!\^)\.(?:\^.|[^\]\.])+\.-\]/\]/g;	# .foo.-]	==> ]
1146fb12b70Safresh1						# [foo.-]       ==> [000000]
1159f11ffb7Safresh1    $path =~ s/(?<!\^)\[(?:\^.|[^\]\.])+\.-\]/\[000000\]/g;
1166fb12b70Safresh1						# []		==>
1176fb12b70Safresh1    $path =~ s/(?<!\^)\[\]// unless $path eq '[]';
1186fb12b70Safresh1    return $unix_rpt ? unixify($path) : $path;
1196fb12b70Safresh1}
1206fb12b70Safresh1
1216fb12b70Safresh1=item catdir (override)
1226fb12b70Safresh1
1236fb12b70Safresh1Concatenates a list of file specifications, and returns the result as a
1246fb12b70Safresh1native directory specification unless the Unix filename reporting feature
1256fb12b70Safresh1has been enabled.  No check is made for "impossible" cases (e.g. elements
1266fb12b70Safresh1other than the first being absolute filespecs).
1276fb12b70Safresh1
1286fb12b70Safresh1=cut
1296fb12b70Safresh1
1306fb12b70Safresh1sub catdir {
1316fb12b70Safresh1    my $self = shift;
1326fb12b70Safresh1    my $dir = pop;
1336fb12b70Safresh1
1346fb12b70Safresh1    my $unix_rpt = $self->_unix_rpt;
1356fb12b70Safresh1
1366fb12b70Safresh1    my @dirs = grep {defined() && length()} @_;
1376fb12b70Safresh1
1386fb12b70Safresh1    my $rslt;
1396fb12b70Safresh1    if (@dirs) {
1406fb12b70Safresh1	my $path = (@dirs == 1 ? $dirs[0] : $self->catdir(@dirs));
1416fb12b70Safresh1	my ($spath,$sdir) = ($path,$dir);
1426fb12b70Safresh1	$spath =~ s/\.dir\Z(?!\n)//i; $sdir =~ s/\.dir\Z(?!\n)//i;
1436fb12b70Safresh1
1446fb12b70Safresh1	if ($unix_rpt) {
1456fb12b70Safresh1	    $spath = unixify($spath) unless $spath =~ m#/#;
1466fb12b70Safresh1	    $sdir= unixify($sdir) unless $sdir =~ m#/#;
1476fb12b70Safresh1            return $self->SUPER::catdir($spath, $sdir)
1486fb12b70Safresh1	}
1496fb12b70Safresh1
150b8851fccSafresh1	$rslt = vmspath( unixify($spath) . '/' . unixify($sdir));
1516fb12b70Safresh1
1526fb12b70Safresh1	# Special case for VMS absolute directory specs: these will have
1536fb12b70Safresh1	# had device prepended during trip through Unix syntax in
1546fb12b70Safresh1	# eliminate_macros(), since Unix syntax has no way to express
1556fb12b70Safresh1	# "absolute from the top of this device's directory tree".
1566fb12b70Safresh1	if ($spath =~ /^[\[<][^.\-]/s) { $rslt =~ s/^[^\[<]+//s; }
1576fb12b70Safresh1
1586fb12b70Safresh1    } else {
1596fb12b70Safresh1	# Single directory. Return an empty string on null input; otherwise
1606fb12b70Safresh1	# just return a canonical path.
1616fb12b70Safresh1
1626fb12b70Safresh1	if    (not defined $dir or not length $dir) {
1636fb12b70Safresh1	    $rslt = '';
1646fb12b70Safresh1	} else {
1656fb12b70Safresh1	    $rslt = $unix_rpt ? $dir : vmspath($dir);
1666fb12b70Safresh1	}
1676fb12b70Safresh1    }
1686fb12b70Safresh1    return $self->canonpath($rslt);
1696fb12b70Safresh1}
1706fb12b70Safresh1
1716fb12b70Safresh1=item catfile (override)
1726fb12b70Safresh1
1736fb12b70Safresh1Concatenates a list of directory specifications with a filename specification
1746fb12b70Safresh1to build a path.
1756fb12b70Safresh1
1766fb12b70Safresh1=cut
1776fb12b70Safresh1
1786fb12b70Safresh1sub catfile {
1796fb12b70Safresh1    my $self = shift;
1806fb12b70Safresh1    my $tfile = pop();
1816fb12b70Safresh1    my $file = $self->canonpath($tfile);
1826fb12b70Safresh1    my @files = grep {defined() && length()} @_;
1836fb12b70Safresh1
1846fb12b70Safresh1    my $unix_rpt = $self->_unix_rpt;
1856fb12b70Safresh1
1866fb12b70Safresh1    my $rslt;
1876fb12b70Safresh1    if (@files) {
1886fb12b70Safresh1	my $path = (@files == 1 ? $files[0] : $self->catdir(@files));
1896fb12b70Safresh1	my $spath = $path;
1906fb12b70Safresh1
1916fb12b70Safresh1        # Something building a VMS path in pieces may try to pass a
1926fb12b70Safresh1        # directory name in filename format, so normalize it.
1936fb12b70Safresh1	$spath =~ s/\.dir\Z(?!\n)//i;
1946fb12b70Safresh1
1956fb12b70Safresh1        # If the spath ends with a directory delimiter and the file is bare,
1966fb12b70Safresh1        # then just concatenate them.
1976fb12b70Safresh1	if ($spath =~ /^(?<!\^)[^\)\]\/:>]+\)\Z(?!\n)/s && basename($file) eq $file) {
1986fb12b70Safresh1	    $rslt = "$spath$file";
1996fb12b70Safresh1	} else {
200b8851fccSafresh1           $rslt = unixify($spath);
2016fb12b70Safresh1           $rslt .= (defined($rslt) && length($rslt) ? '/' : '') . unixify($file);
2026fb12b70Safresh1           $rslt = vmsify($rslt) unless $unix_rpt;
2036fb12b70Safresh1	}
2046fb12b70Safresh1    }
2056fb12b70Safresh1    else {
2066fb12b70Safresh1        # Only passed a single file?
2076fb12b70Safresh1        my $xfile = (defined($file) && length($file)) ? $file : '';
2086fb12b70Safresh1
209b8851fccSafresh1        $rslt = $unix_rpt ? $xfile : vmsify($xfile);
2106fb12b70Safresh1    }
2116fb12b70Safresh1    return $self->canonpath($rslt) unless $unix_rpt;
2126fb12b70Safresh1
2136fb12b70Safresh1    # In Unix report mode, do not strip off redundant path information.
2146fb12b70Safresh1    return $rslt;
2156fb12b70Safresh1}
2166fb12b70Safresh1
2176fb12b70Safresh1
2186fb12b70Safresh1=item curdir (override)
2196fb12b70Safresh1
2206fb12b70Safresh1Returns a string representation of the current directory: '[]' or '.'
2216fb12b70Safresh1
2226fb12b70Safresh1=cut
2236fb12b70Safresh1
2246fb12b70Safresh1sub curdir {
2256fb12b70Safresh1    my $self = shift @_;
2266fb12b70Safresh1    return '.' if ($self->_unix_rpt);
2276fb12b70Safresh1    return '[]';
2286fb12b70Safresh1}
2296fb12b70Safresh1
2306fb12b70Safresh1=item devnull (override)
2316fb12b70Safresh1
2326fb12b70Safresh1Returns a string representation of the null device: '_NLA0:' or '/dev/null'
2336fb12b70Safresh1
2346fb12b70Safresh1=cut
2356fb12b70Safresh1
2366fb12b70Safresh1sub devnull {
2376fb12b70Safresh1    my $self = shift @_;
2386fb12b70Safresh1    return '/dev/null' if ($self->_unix_rpt);
2396fb12b70Safresh1    return "_NLA0:";
2406fb12b70Safresh1}
2416fb12b70Safresh1
2426fb12b70Safresh1=item rootdir (override)
2436fb12b70Safresh1
2446fb12b70Safresh1Returns a string representation of the root directory: 'SYS$DISK:[000000]'
2456fb12b70Safresh1or '/'
2466fb12b70Safresh1
2476fb12b70Safresh1=cut
2486fb12b70Safresh1
2496fb12b70Safresh1sub rootdir {
2506fb12b70Safresh1    my $self = shift @_;
2516fb12b70Safresh1    if ($self->_unix_rpt) {
2526fb12b70Safresh1       # Root may exist, try it first.
2536fb12b70Safresh1       my $try = '/';
2546fb12b70Safresh1       my ($dev1, $ino1) = stat('/');
2556fb12b70Safresh1       my ($dev2, $ino2) = stat('.');
2566fb12b70Safresh1
2576fb12b70Safresh1       # Perl falls back to '.' if it can not determine '/'
2586fb12b70Safresh1       if (($dev1 != $dev2) || ($ino1 != $ino2)) {
2596fb12b70Safresh1           return $try;
2606fb12b70Safresh1       }
2616fb12b70Safresh1       # Fall back to UNIX format sys$disk.
2626fb12b70Safresh1       return '/sys$disk/';
2636fb12b70Safresh1    }
2646fb12b70Safresh1    return 'SYS$DISK:[000000]';
2656fb12b70Safresh1}
2666fb12b70Safresh1
2676fb12b70Safresh1=item tmpdir (override)
2686fb12b70Safresh1
2696fb12b70Safresh1Returns a string representation of the first writable directory
2706fb12b70Safresh1from the following list or '' if none are writable:
2716fb12b70Safresh1
2726fb12b70Safresh1    /tmp if C<DECC$FILENAME_UNIX_REPORT> is enabled.
2736fb12b70Safresh1    sys$scratch:
2746fb12b70Safresh1    $ENV{TMPDIR}
2756fb12b70Safresh1
2766fb12b70Safresh1If running under taint mode, and if $ENV{TMPDIR}
2776fb12b70Safresh1is tainted, it is not used.
2786fb12b70Safresh1
2796fb12b70Safresh1=cut
2806fb12b70Safresh1
2816fb12b70Safresh1sub tmpdir {
2826fb12b70Safresh1    my $self = shift @_;
2836fb12b70Safresh1    my $tmpdir = $self->_cached_tmpdir('TMPDIR');
2846fb12b70Safresh1    return $tmpdir if defined $tmpdir;
2856fb12b70Safresh1    if ($self->_unix_rpt) {
2866fb12b70Safresh1        $tmpdir = $self->_tmpdir('/tmp', '/sys$scratch', $ENV{TMPDIR});
2876fb12b70Safresh1    }
2886fb12b70Safresh1    else {
2896fb12b70Safresh1        $tmpdir = $self->_tmpdir( 'sys$scratch:', $ENV{TMPDIR} );
2906fb12b70Safresh1    }
2916fb12b70Safresh1    $self->_cache_tmpdir($tmpdir, 'TMPDIR');
2926fb12b70Safresh1}
2936fb12b70Safresh1
2946fb12b70Safresh1=item updir (override)
2956fb12b70Safresh1
2966fb12b70Safresh1Returns a string representation of the parent directory: '[-]' or '..'
2976fb12b70Safresh1
2986fb12b70Safresh1=cut
2996fb12b70Safresh1
3006fb12b70Safresh1sub updir {
3016fb12b70Safresh1    my $self = shift @_;
3026fb12b70Safresh1    return '..' if ($self->_unix_rpt);
3036fb12b70Safresh1    return '[-]';
3046fb12b70Safresh1}
3056fb12b70Safresh1
3066fb12b70Safresh1=item case_tolerant (override)
3076fb12b70Safresh1
3086fb12b70Safresh1VMS file specification syntax is case-tolerant.
3096fb12b70Safresh1
3106fb12b70Safresh1=cut
3116fb12b70Safresh1
3126fb12b70Safresh1sub case_tolerant {
3136fb12b70Safresh1    return 1;
3146fb12b70Safresh1}
3156fb12b70Safresh1
3166fb12b70Safresh1=item path (override)
3176fb12b70Safresh1
3186fb12b70Safresh1Translate logical name DCL$PATH as a searchlist, rather than trying
3196fb12b70Safresh1to C<split> string value of C<$ENV{'PATH'}>.
3206fb12b70Safresh1
3216fb12b70Safresh1=cut
3226fb12b70Safresh1
3236fb12b70Safresh1sub path {
3246fb12b70Safresh1    my (@dirs,$dir,$i);
3256fb12b70Safresh1    while ($dir = $ENV{'DCL$PATH;' . $i++}) { push(@dirs,$dir); }
3266fb12b70Safresh1    return @dirs;
3276fb12b70Safresh1}
3286fb12b70Safresh1
3296fb12b70Safresh1=item file_name_is_absolute (override)
3306fb12b70Safresh1
3316fb12b70Safresh1Checks for VMS directory spec as well as Unix separators.
3326fb12b70Safresh1
3336fb12b70Safresh1=cut
3346fb12b70Safresh1
3356fb12b70Safresh1sub file_name_is_absolute {
3366fb12b70Safresh1    my ($self,$file) = @_;
3376fb12b70Safresh1    # If it's a logical name, expand it.
3386fb12b70Safresh1    $file = $ENV{$file} while $file =~ /^[\w\$\-]+\Z(?!\n)/s && $ENV{$file};
3396fb12b70Safresh1    return scalar($file =~ m!^/!s             ||
3406fb12b70Safresh1		  $file =~ m![<\[][^.\-\]>]!  ||
3416fb12b70Safresh1		  $file =~ /^[A-Za-z0-9_\$\-\~]+(?<!\^):/);
3426fb12b70Safresh1}
3436fb12b70Safresh1
3446fb12b70Safresh1=item splitpath (override)
3456fb12b70Safresh1
3466fb12b70Safresh1   ($volume,$directories,$file) = File::Spec->splitpath( $path );
3476fb12b70Safresh1   ($volume,$directories,$file) = File::Spec->splitpath( $path,
3486fb12b70Safresh1                                                         $no_file );
3496fb12b70Safresh1
3506fb12b70Safresh1Passing a true value for C<$no_file> indicates that the path being
3516fb12b70Safresh1split only contains directory components, even on systems where you
3526fb12b70Safresh1can usually (when not supporting a foreign syntax) tell the difference
3536fb12b70Safresh1between directories and files at a glance.
3546fb12b70Safresh1
3556fb12b70Safresh1=cut
3566fb12b70Safresh1
3576fb12b70Safresh1sub splitpath {
3586fb12b70Safresh1    my($self,$path, $nofile) = @_;
3596fb12b70Safresh1    my($dev,$dir,$file)      = ('','','');
3606fb12b70Safresh1    my $vmsify_path = vmsify($path);
3616fb12b70Safresh1
3626fb12b70Safresh1    if ( $nofile ) {
3636fb12b70Safresh1        #vmsify('d1/d2/d3') returns '[.d1.d2]d3'
3646fb12b70Safresh1        #vmsify('/d1/d2/d3') returns 'd1:[d2]d3'
3656fb12b70Safresh1        if( $vmsify_path =~ /(.*)\](.+)/ ){
3666fb12b70Safresh1            $vmsify_path = $1.'.'.$2.']';
3676fb12b70Safresh1        }
3686fb12b70Safresh1        $vmsify_path =~ /(.+:)?(.*)/s;
3696fb12b70Safresh1        $dir = defined $2 ? $2 : ''; # dir can be '0'
3706fb12b70Safresh1        return ($1 || '',$dir,$file);
3716fb12b70Safresh1    }
3726fb12b70Safresh1    else {
3736fb12b70Safresh1        $vmsify_path =~ /(.+:)?([\[<].*[\]>])?(.*)/s;
3746fb12b70Safresh1        return ($1 || '',$2 || '',$3);
3756fb12b70Safresh1    }
3766fb12b70Safresh1}
3776fb12b70Safresh1
3786fb12b70Safresh1=item splitdir (override)
3796fb12b70Safresh1
3806fb12b70Safresh1Split a directory specification into the components.
3816fb12b70Safresh1
3826fb12b70Safresh1=cut
3836fb12b70Safresh1
3846fb12b70Safresh1sub splitdir {
3856fb12b70Safresh1    my($self,$dirspec) = @_;
3866fb12b70Safresh1    my @dirs = ();
3876fb12b70Safresh1    return @dirs if ( (!defined $dirspec) || ('' eq $dirspec) );
3886fb12b70Safresh1
3896fb12b70Safresh1    $dirspec =~ s/(?<!\^)</[/;                  # < and >	==> [ and ]
3906fb12b70Safresh1    $dirspec =~ s/(?<!\^)>/]/;
3916fb12b70Safresh1    $dirspec =~ s/(?<!\^)\]\[\./\.\]\[/g;	# ][.		==> .][
3926fb12b70Safresh1    $dirspec =~ s/(?<!\^)\[000000\.\]\[/\[/g;	# [000000.][	==> [
3936fb12b70Safresh1    $dirspec =~ s/(?<!\^)\[000000\./\[/g;	# [000000.	==> [
3946fb12b70Safresh1    $dirspec =~ s/(?<!\^)\.\]\[000000\]/\]/g;	# .][000000]	==> ]
3956fb12b70Safresh1    $dirspec =~ s/(?<!\^)\.\]\[/\./g;		# foo.][bar	==> foo.bar
3966fb12b70Safresh1    while ($dirspec =~ s/(^|[\[\<\.])\-(\-+)($|[\]\>\.])/$1-.$2$3/g) {}
3976fb12b70Safresh1						# That loop does the following
3986fb12b70Safresh1						# with any amount of dashes:
3996fb12b70Safresh1						# .--.		==> .-.-.
4006fb12b70Safresh1						# [--.		==> [-.-.
4016fb12b70Safresh1						# .--]		==> .-.-]
4026fb12b70Safresh1						# [--]		==> [-.-]
4036fb12b70Safresh1    $dirspec = "[$dirspec]" unless $dirspec =~ /(?<!\^)[\[<]/; # make legal
4046fb12b70Safresh1    $dirspec =~ s/^(\[|<)\./$1/;
4056fb12b70Safresh1    @dirs = split /(?<!\^)\./, vmspath($dirspec);
4066fb12b70Safresh1    $dirs[0] =~ s/^[\[<]//s;  $dirs[-1] =~ s/[\]>]\Z(?!\n)//s;
4076fb12b70Safresh1    @dirs;
4086fb12b70Safresh1}
4096fb12b70Safresh1
4106fb12b70Safresh1
4116fb12b70Safresh1=item catpath (override)
4126fb12b70Safresh1
4136fb12b70Safresh1Construct a complete filespec.
4146fb12b70Safresh1
4156fb12b70Safresh1=cut
4166fb12b70Safresh1
4176fb12b70Safresh1sub catpath {
4186fb12b70Safresh1    my($self,$dev,$dir,$file) = @_;
4196fb12b70Safresh1
4206fb12b70Safresh1    # We look for a volume in $dev, then in $dir, but not both
4216fb12b70Safresh1    my ($dir_volume, $dir_dir, $dir_file) = $self->splitpath($dir);
4226fb12b70Safresh1    $dev = $dir_volume unless length $dev;
4236fb12b70Safresh1    $dir = length $dir_file ? $self->catfile($dir_dir, $dir_file) : $dir_dir;
4246fb12b70Safresh1
4256fb12b70Safresh1    if ($dev =~ m|^(?<!\^)/+([^/]+)|) { $dev = "$1:"; }
4266fb12b70Safresh1    else { $dev .= ':' unless $dev eq '' or $dev =~ /:\Z(?!\n)/; }
4276fb12b70Safresh1    if (length($dev) or length($dir)) {
4286fb12b70Safresh1        $dir = "[$dir]" unless $dir =~ /(?<!\^)[\[<\/]/;
4296fb12b70Safresh1        $dir = vmspath($dir);
4306fb12b70Safresh1    }
4316fb12b70Safresh1    $dir = '' if length($dev) && ($dir eq '[]' || $dir eq '<>');
4326fb12b70Safresh1    "$dev$dir$file";
4336fb12b70Safresh1}
4346fb12b70Safresh1
4356fb12b70Safresh1=item abs2rel (override)
4366fb12b70Safresh1
4376fb12b70Safresh1Attempt to convert an absolute file specification to a relative specification.
4386fb12b70Safresh1
4396fb12b70Safresh1=cut
4406fb12b70Safresh1
4416fb12b70Safresh1sub abs2rel {
4426fb12b70Safresh1    my $self = shift;
4436fb12b70Safresh1    my($path,$base) = @_;
444b8851fccSafresh1
4459f11ffb7Safresh1    $base = Cwd::getcwd() unless defined $base and length $base;
4466fb12b70Safresh1
447b8851fccSafresh1    # If there is no device or directory syntax on $base, make sure it
448b8851fccSafresh1    # is treated as a directory.
449b8851fccSafresh1    $base = vmspath($base) unless $base =~ m{(?<!\^)[\[<:]};
450b8851fccSafresh1
451b8851fccSafresh1    for ($path, $base) { $_ = $self->rel2abs($_) }
4526fb12b70Safresh1
4536fb12b70Safresh1    # Are we even starting $path on the same (node::)device as $base?  Note that
4546fb12b70Safresh1    # logical paths or nodename differences may be on the "same device"
4556fb12b70Safresh1    # but the comparison that ignores device differences so as to concatenate
4566fb12b70Safresh1    # [---] up directory specs is not even a good idea in cases where there is
4576fb12b70Safresh1    # a logical path difference between $path and $base nodename and/or device.
4586fb12b70Safresh1    # Hence we fall back to returning the absolute $path spec
4596fb12b70Safresh1    # if there is a case blind device (or node) difference of any sort
4606fb12b70Safresh1    # and we do not even try to call $parse() or consult %ENV for $trnlnm()
4616fb12b70Safresh1    # (this module needs to run on non VMS platforms after all).
4626fb12b70Safresh1
4636fb12b70Safresh1    my ($path_volume, $path_directories, $path_file) = $self->splitpath($path);
4646fb12b70Safresh1    my ($base_volume, $base_directories, $base_file) = $self->splitpath($base);
465b8851fccSafresh1    return $self->canonpath( $path ) unless lc($path_volume) eq lc($base_volume);
4666fb12b70Safresh1
4676fb12b70Safresh1    # Now, remove all leading components that are the same
4686fb12b70Safresh1    my @pathchunks = $self->splitdir( $path_directories );
4696fb12b70Safresh1    my $pathchunks = @pathchunks;
4706fb12b70Safresh1    unshift(@pathchunks,'000000') unless $pathchunks[0] eq '000000';
4716fb12b70Safresh1    my @basechunks = $self->splitdir( $base_directories );
4726fb12b70Safresh1    my $basechunks = @basechunks;
4736fb12b70Safresh1    unshift(@basechunks,'000000') unless $basechunks[0] eq '000000';
4746fb12b70Safresh1
4756fb12b70Safresh1    while ( @pathchunks &&
4766fb12b70Safresh1            @basechunks &&
4776fb12b70Safresh1            lc( $pathchunks[0] ) eq lc( $basechunks[0] )
4786fb12b70Safresh1          ) {
4796fb12b70Safresh1        shift @pathchunks ;
4806fb12b70Safresh1        shift @basechunks ;
4816fb12b70Safresh1    }
4826fb12b70Safresh1
4836fb12b70Safresh1    # @basechunks now contains the directories to climb out of,
4846fb12b70Safresh1    # @pathchunks now has the directories to descend in to.
4856fb12b70Safresh1    if ((@basechunks > 0) || ($basechunks != $pathchunks)) {
4866fb12b70Safresh1      $path_directories = join '.', ('-' x @basechunks, @pathchunks) ;
4876fb12b70Safresh1    }
4886fb12b70Safresh1    else {
4896fb12b70Safresh1      $path_directories = join '.', @pathchunks;
4906fb12b70Safresh1    }
4916fb12b70Safresh1    $path_directories = '['.$path_directories.']';
4926fb12b70Safresh1    return $self->canonpath( $self->catpath( '', $path_directories, $path_file ) ) ;
4936fb12b70Safresh1}
4946fb12b70Safresh1
4956fb12b70Safresh1
4966fb12b70Safresh1=item rel2abs (override)
4976fb12b70Safresh1
4986fb12b70Safresh1Return an absolute file specification from a relative one.
4996fb12b70Safresh1
5006fb12b70Safresh1=cut
5016fb12b70Safresh1
5026fb12b70Safresh1sub rel2abs {
5036fb12b70Safresh1    my $self = shift ;
5046fb12b70Safresh1    my ($path,$base ) = @_;
5056fb12b70Safresh1    return undef unless defined $path;
5066fb12b70Safresh1    if ($path =~ m/\//) {
5076fb12b70Safresh1       $path = ( -d $path || $path =~ m/\/\z/  # educated guessing about
5086fb12b70Safresh1                  ? vmspath($path)             # whether it's a directory
5096fb12b70Safresh1                  : vmsify($path) );
5106fb12b70Safresh1    }
5116fb12b70Safresh1    $base = vmspath($base) if defined $base && $base =~ m/\//;
5126fb12b70Safresh1
5136fb12b70Safresh1    # Clean up and split up $path
5146fb12b70Safresh1    if ( ! $self->file_name_is_absolute( $path ) ) {
5156fb12b70Safresh1        # Figure out the effective $base and clean it up.
5166fb12b70Safresh1        if ( !defined( $base ) || $base eq '' ) {
5179f11ffb7Safresh1            $base = Cwd::getcwd();
5186fb12b70Safresh1        }
5196fb12b70Safresh1        elsif ( ! $self->file_name_is_absolute( $base ) ) {
5206fb12b70Safresh1            $base = $self->rel2abs( $base ) ;
5216fb12b70Safresh1        }
5226fb12b70Safresh1        else {
5236fb12b70Safresh1            $base = $self->canonpath( $base ) ;
5246fb12b70Safresh1        }
5256fb12b70Safresh1
5266fb12b70Safresh1        # Split up paths
5276fb12b70Safresh1        my ( $path_directories, $path_file ) =
5286fb12b70Safresh1            ($self->splitpath( $path ))[1,2] ;
5296fb12b70Safresh1
5306fb12b70Safresh1        my ( $base_volume, $base_directories ) =
5316fb12b70Safresh1            $self->splitpath( $base ) ;
5326fb12b70Safresh1
5336fb12b70Safresh1        $path_directories = '' if $path_directories eq '[]' ||
5346fb12b70Safresh1                                  $path_directories eq '<>';
5356fb12b70Safresh1        my $sep = '' ;
5366fb12b70Safresh1        $sep = '.'
5376fb12b70Safresh1            if ( $base_directories =~ m{[^.\]>]\Z(?!\n)} &&
5386fb12b70Safresh1                 $path_directories =~ m{^[^.\[<]}s
5396fb12b70Safresh1            ) ;
5406fb12b70Safresh1        $base_directories = "$base_directories$sep$path_directories";
5416fb12b70Safresh1        $base_directories =~ s{\.?[\]>][\[<]\.?}{.};
5426fb12b70Safresh1
5436fb12b70Safresh1        $path = $self->catpath( $base_volume, $base_directories, $path_file );
5446fb12b70Safresh1   }
5456fb12b70Safresh1
5466fb12b70Safresh1    return $self->canonpath( $path ) ;
5476fb12b70Safresh1}
5486fb12b70Safresh1
5496fb12b70Safresh1
5506fb12b70Safresh1=back
5516fb12b70Safresh1
5526fb12b70Safresh1=head1 COPYRIGHT
5536fb12b70Safresh1
554b8851fccSafresh1Copyright (c) 2004-14 by the Perl 5 Porters.  All rights reserved.
5556fb12b70Safresh1
5566fb12b70Safresh1This program is free software; you can redistribute it and/or modify
5576fb12b70Safresh1it under the same terms as Perl itself.
5586fb12b70Safresh1
5596fb12b70Safresh1=head1 SEE ALSO
5606fb12b70Safresh1
5616fb12b70Safresh1See L<File::Spec> and L<File::Spec::Unix>.  This package overrides the
5626fb12b70Safresh1implementation of these methods, not the semantics.
5636fb12b70Safresh1
5646fb12b70Safresh1An explanation of VMS file specs can be found at
5656fb12b70Safresh1L<http://h71000.www7.hp.com/doc/731FINAL/4506/4506pro_014.html#apps_locating_naming_files>.
5666fb12b70Safresh1
5676fb12b70Safresh1=cut
5686fb12b70Safresh1
5696fb12b70Safresh11;
570