I have not tested this or anything on 5.8.7, I am in the process of moving my changes over to 5.8.7.
Applying this to the 5.8.7 vms.pm results in the vms.pm that I am using on 5.8.6.
I included Michael Schwern on this since I had to make a similar change in the mm_vms.pm routines, along with several others which I will post later, as I merge in my changes into my local copy of the 5.8.7 source.
I am sure that there is probably a better way to do many of the changes I made to this model, particularly where I have to look up the DECC feature logicals every time a routine is called.
When my yet to be posted patch to VMS.C is installed, it replaces the "sub case_tolerant()" routine with one that returns the current process state.
This also means that on UNIX, this module is now providing what could be a wrong value, if something wants to know if VMS is case sensitive, unless you interpret it as actually being that the default on VMS is to be case insensitive.
Also I have a lot of commented out diagnostics in this, as this was the only way I found to find out why some test scripts were not working, particularly in the MakeMaker area.
I was not able to get the canonpath() routine to work correctly with ODS-5 Extended file specifications in directory name.
In particular, '[.foo^.-.bar]' is a legal directory name on an ODS-5 file, and would correspond to the UNIX './foo.-/bar/' path name. I could not find a simple change to the Perl rules to stop it from incorrectly compressing that specification to '[.bar]'. So I put in a hack to force the directory name into UNIX mode and run the UNIX canonpath() routine instead when a '^' is found in a VMS directory specification.
-John [EMAIL PROTECTED] Personal Opinion Only
--- lib/File/Spec/VMS.pm_5_8_7 Thu Jul 14 10:36:30 2005 +++ lib/File/Spec/VMS.pm Thu Jul 14 10:45:57 2005 @@ -38,6 +38,7 @@ sub eliminate_macros { my($self,$path) = @_; return '' unless $path; +#print STDERR "VMS.pm eliminate_macros - path = $path\n"; $self = {} unless ref $self; if ($path =~ /\s/) { @@ -68,6 +69,7 @@ } } if ($complex) { $npath =~ s#\cB(.*?)\cB#\${$1}#gs; } +#print STDERR "VMS eliminate_macros - npath = $npath\n"; $npath; } @@ -92,6 +94,7 @@ return '' unless $path; $self = bless {} unless ref $self; my($fixedpath,$prefix,$name); + my $vms_mode = 1; if ($path =~ /\s/) { return join ' ', @@ -99,28 +102,100 @@ split /\s+/, $path; } + my $unix_report; + my $unix_only; + $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } + else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + if (defined $unix_report) { + $vms_mode = 0; + } + if (!defined ($unix_only)) { + $vms_mode = 1 if $path =~ m|[\]>:]|; + } +#if ($force_path) { +#print STDERR "VMS fixpath path = $path, force_path = $force_path \n"; +#} else { +#print STDERR "VMS fixpath path = $path, force_path = undef \n"; +#} + if ($path =~ m#^\$\([^\)]+\)\Z(?!\n)#s || $path =~ m#[/:>\]]#) { if ($force_path or $path =~ /(?:DIR\)|\])\Z(?!\n)/) { +#my $path_n2 = $self->eliminate_macros($path); +#print "fixpath (1.0) path_n2 = $path_n2, path = $path \n"; + if ($vms_mode == 1) { $fixedpath = vmspath($self->eliminate_macros($path)); } else { - $fixedpath = vmsify($self->eliminate_macros($path)); + $fixedpath = $self->eliminate_macros($path); + } +#print "fixpath (1) path = $path, fixedpath = $fixedpath \n"; + } + else { + $fixedpath = $self->eliminate_macros($path); + $fixedpath = vmsify($fixedpath) if ($vms_mode == 1); +#print "fixpath (2) path = $path, fixedpath = $fixedpath \n"; } } elsif ((($prefix,$name) = ($path =~ m#^\$\(([^\)]+)\)(.+)#s)) && $self->{$prefix}) { my($vmspre) = $self->eliminate_macros("\$($prefix)"); # is it a dir or just a name? + if ($vms_mode == 1) { $vmspre = ($vmspre =~ m|/| or $prefix =~ /DIR\Z(?!\n)/) ? vmspath($vmspre) : ''; + } + else { + $vmspre = ($vmspre =~ m|/| or $prefix =~ /DIR\Z(?!\n)/) ? unixpath($vmspre) : ''; + } $fixedpath = ($vmspre ? $vmspre : $self->{$prefix}) . $name; - $fixedpath = vmspath($fixedpath) if $force_path; + if ($force_path) { + if ($vms_mode == 1) { + $fixedpath = vmspath($fixedpath); + } +# else { +# $fixedpath = unixpath($fixedpath); +# } + } +#print "fixpath (3) path = $path, fixedpath = $fixedpath \n"; } else { $fixedpath = $path; - $fixedpath = vmspath($fixedpath) if $force_path; + if ($force_path) { + if ($vms_mode == 1) { + $fixedpath = vmspath($fixedpath); + } +# else { +# $fixedpath = unixpath($fixedpath); +# } + } +#print STDERR "fixpath (4) path = $path, fixedpath = $fixedpath \n"; } # No hints, so we try to guess if (!defined($force_path) and $fixedpath !~ /[:>(.\]]/) { - $fixedpath = vmspath($fixedpath) if -d $fixedpath; + if (-d $fixedpath) { + if ($vms_mode == 1) { + $fixedpath = vmspath($fixedpath); + } +# else { +# $fixedpath = unixpath($fixedpath); +# } + } +#print STDERR "fixpath (5) path = $path, fixedpath = $fixedpath \n"; } # Trim off root dirname if it's had other dirs inserted in front of it. @@ -130,6 +205,7 @@ # Unix syntax has no way to express "absolute from the top of this device's # directory tree". if ($path =~ /^[\[>][^.\-]/) { $fixedpath =~ s/^[^\[<]+//; } +#print STDERR "VMS fixpath (6) path = $path, fixedpath = $fixedpath \n"; $fixedpath; } @@ -148,13 +224,54 @@ sub canonpath { my($self,$path) = @_; - if ($path =~ m|/|) { # Fake Unix +#print STDERR "VMS canonpath (0) path = $path\n"; + my $vms_mode; + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + # VMS/UNIX mixed handling. + $vms_mode = 1; + $vms_mode = 0 if ($path =~ m|/|) || (defined $unix_only); + +# temp hack to force UNIX mode if EFS characters seen. + + if (($vms_mode == 0) || ($path =~ m|\^|)) { # Fake Unix +#print STDERR "#vms.pm cannonpath (UNIX mode) path = $path, vms_mode = $vms_mode\n"; + + # Convert to UNIX if need be, VMS cannonpath can not handle EFS charset. + $path = unixify($path) if ($vms_mode == 1); my $pathify = $path =~ m|/\Z(?!\n)|; +#print STDERR "VMS canonpath (1) path = $path\n"; + $path = $self->SUPER::canonpath($path); +#print STDERR "VMS canonpath (2) path = $path\n"; + if ($vms_mode == 0) { + return $path; + } + else { if ($pathify) { return vmspath($path); } else { return vmsify($path); } } + } else { +#print STDERR "#vms.pm cannonpath (VMS mode) path = $path\n"; + $path =~ tr/<>/[]/; # < and > ==> [ and ] $path =~ s/\]\[\./\.\]\[/g; # ][. ==> .][ $path =~ s/\[000000\.\]\[/\[/g; # [000000.][ ==> [ @@ -168,7 +285,11 @@ # [-.-. ==> [--. # .-.-] ==> .--] # [-.-] ==> [--] +#print STDERR "VMS canonpath (1) path = $path\n"; 1 while ($path =~ s/([\[\.])[^\]\.]+\.-(-+)([\]\.])/$1$2$3/); +# 1 while ($path =~ s/([\[]*.+(?:(?<!\^)\.)+).+(?:(?!<!\^)\.)-(-+)([\]\.])/$1$2$3/); not working. +# '^.' must not be treated as '.' - Only seen on ODS-5 EFS specifications +#print STDERR "VMS canonpath (2) path = $path\n"; # That loop does the following # with any amount (minimum 2) # of dashes: @@ -180,9 +301,17 @@ # And then, the remaining cases $path =~ s/\[\.-/[-/; # [.- ==> [- $path =~ s/\.[^\]\.]+\.-\./\./g; # .foo.-. ==> . +# $path =~ s/(?<!\^)\..+(?:(?!<!\^)\.)-\./\./g; # .foo^..-. ==> . not working. +#print STDERR "VMS canonpath (3) path = $path\n"; $path =~ s/\[[^\]\.]+\.-\./\[/g; # [foo.-. ==> [ +# $path =~ s/(?<!\^)\[.+(?:(?!<!\^)\.)-\./\[/g; # [foo^..-. ==-> [ +#print STDERR "VMS canonpath (4) path = $path\n"; $path =~ s/\.[^\]\.]+\.-\]/\]/g; # .foo.-] ==> ] +# $path =~ s/(?<!\^)\..+(?:(?!<!\^)\.)-\]/\]/g; # .foo^..-] == > ] not working +#print STDERR "VMS canonpath (5) path = $path\n"; $path =~ s/\[[^\]\.]+\.-\]/\[000000\]/g;# [foo.-] ==> [000000] +# $path =~ s/(?<!\^)\].+(?:(?!<!\^)\.)-\]/\[000000\]/g; # [foo^..-] => [000000] not working +#print STDERR "VMS canonpath (6) path = $path\n"; $path =~ s/\[\]//; # [] ==> return $path; } @@ -197,16 +326,75 @@ =cut sub catdir { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + my $case_preserved = 1; + $case_preserved = $ENV{'DECC$EFS_CASE_PRESERVE'}; + if (defined $case_preserved) { + if (($case_preserved lt '1') && ($case_preserved ne 'ENABLE')) { + $case_preserved = undef; + } + } + + # VMS mode my ($self,@dirs) = @_; +#print STDERR "VMS catdir files = @dirs .\n"; + + my $vms_mode = 1; + $vms_mode = 0 if (defined $unix_report); my $dir = pop @dirs; + $vms_mode = 1 if $dir =~ m|[\]>:]|; +#print STDERR "catdir vms_mode = $vms_mode .\n"; @dirs = grep($_,@dirs); my $rslt; if (@dirs) { - my $path = (@dirs == 1 ? $dirs[0] : $self->catdir(@dirs)); + my $path; + if (@dirs == 1) { + $path = $dirs[0]; + $vms_mode = 1 if $path =~ m|[\]>:]|; +#print STDERR "catdir (1a) dirs[0] = $dirs[0], vms_mode = $vms_mode .\n"; + } + else { + $path = $self->catdir(@dirs); + $vms_mode = 1 if $path =~ m|[\]>:]|; +#print STDERR "catdir (1b) path = $path, dirs = @dirs, vms_mode = $vms_mode .\n"; + } my ($spath,$sdir) = ($path,$dir); + if (defined $case_preserved) { + $spath =~ s/\.DIR\Z(?!\n)//; $sdir =~ s/\.DIR\Z(?!\n)//; + } $spath =~ s/\.dir\Z(?!\n)//; $sdir =~ s/\.dir\Z(?!\n)//; + if ($vms_mode == 0) { + $vms_mode = 1 if (($spath ne $path) || ($sdir ne $dir)); + } $sdir = $self->eliminate_macros($sdir) unless $sdir =~ /^[\w\-]+\Z(?!\n)/s; + if ($vms_mode) { +#my $spath_n = $self->eliminate_macros($spath); +#print STDERR "catdir (3a.0) spath_n = $spath_n, spath = $spath, sdir = $sdir, vms_mode = $vms_mode .\n"; $rslt = $self->fixpath($self->eliminate_macros($spath)."/$sdir",1); + $rslt = vmspath($rslt); +#print STDERR "catdir (3a) rslt = $rslt, sdir = $sdir, spath = $spath, vms_mode = $vms_mode .\n"; + } + else { + $rslt = unixify($self->fixpath($self->eliminate_macros($spath)."/$sdir",0)); +#print STDERR "catdir (3b) rslt = $rslt, sdir = $sdir, spath = $spath, vms_mode = $vms_mode .\n"; + } # Special case for VMS absolute directory specs: these will have had device # prepended during trip through Unix syntax in eliminate_macros(), since @@ -216,9 +404,22 @@ } else { if (not defined $dir or not length $dir) { $rslt = ''; } - elsif ($dir =~ /^\$\([^\)]+\)\Z(?!\n)/s) { $rslt = $dir; } - else { $rslt = vmspath($dir); } + elsif ($dir =~ /^\$\([^\)]+\)\Z(?!\n)/s) { + $rslt = $dir; +#print STDERR "catdir (4) rslt = $rslt, vms_mode = $vms_mode .\n"; } + else { + if ($vms_mode) { + $rslt = vmspath($dir); +#print STDERR "catdir (5a) rslt = $rslt, dir = $dir, vms_mode = $vms_mode .\n"; + } + else { + $rslt = unixpath($dir); +#print STDERR "catdir (5b) rslt = $rslt, dir = $dir, vms_mode = $vms_mode .\n"; + } + } + } +#print STDERR "catdir (6) rslt = $rslt, vms_mode = $vms_mode .\n"; return $self->canonpath($rslt); } @@ -230,24 +431,86 @@ =cut sub catfile { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + if (defined $unix_only) { + my $self = shift; + my $file = $self->canonpath(pop @_); + return $file unless @_; + my $dir = $self->catdir(@_); + $dir .= "/" unless substr($dir,-1) eq "/"; + return $dir.$file; + + } else { + # vms / unix mode my ($self,@files) = @_; - my $file = $self->canonpath(pop @files); +#print STDERR "VMS catfile files = @files .\n"; + my $vms_mode = 1; + $vms_mode = 0 if (defined $unix_report); + my $file = pop @files; + $vms_mode = 1 if $file =~ m|[\]>:]|; + $file = $self->canonpath($file); @files = grep($_,@files); my $rslt; if (@files) { my $path = (@files == 1 ? $files[0] : $self->catdir(@files)); + $vms_mode = 1 if $path =~ m|[\]>:]|; my $spath = $path; + if (defined $unix_report) { + $spath =~ s/\.DIR\Z(?!\n)//; + } + else { $spath =~ s/\.dir\Z(?!\n)//; + } if ($spath =~ /^[^\)\]\/:>]+\)\Z(?!\n)/s && basename($file) eq $file) { $rslt = "$spath$file"; +#print STDERR "catfile 1 spath = $spath, file = $file, rslt = $rslt .\n"; } else { $rslt = $self->eliminate_macros($spath); +#print STDERR "catfile 2a spath = $spath, file = $file, rslt = $rslt .\n"; + if ($vms_mode) { $rslt = vmsify($rslt.($rslt ? '/' : '').unixify($file)); +#print STDERR "catfile 2b spath = $spath, file = $file, rslt = $rslt .\n"; } + else { + $rslt = $rslt.($rslt ? '/' : '').unixify($file); +#print STDERR "catfile 2c spath = $spath, file = $file, rslt = $rslt .\n"; + } + } + } + else { + if (defined($file) && (length($file) != 0)) { + $rslt = $vms_mode ? vmsify($file) : $file; +#print STDERR "catfile 3 vms_mode = $vms_mode, file = $file, rslt = $rslt .\n"; + } + else { + $rslt = ''; + } + } +#print STDERR "catfile result = $rslt .\n"; + return $self->canonpath($rslt) unless defined $unix_report; + + # In unix_report mode, do not strip off the redundant path here. + return $rslt; } - else { $rslt = (defined($file) && length($file)) ? vmsify($file) : ''; } - return $self->canonpath($rslt); } @@ -258,7 +521,30 @@ =cut sub curdir { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + if (defined $unix_report) { + return '.'; + } else { return '[]'; + } } =item devnull (override) @@ -268,7 +554,30 @@ =cut sub devnull { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + if (defined $unix_report) { + return '/dev/null'; + } else { return "_NLA0:"; + } } =item rootdir (override) @@ -278,7 +587,30 @@ =cut sub rootdir { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + if (defined $unix_report) { + return '/'; + } else { return 'SYS$DISK:[000000]'; + } } =item tmpdir (override) @@ -298,7 +630,57 @@ sub tmpdir { return $tmpdir if defined $tmpdir; my $self = shift; + + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + my $posix_compliant = $ENV{'DECC$POSIX_COMPLIANT_PATHNAMES'}; + if (defined $posix_compliant) { + if (($posix_compliant lt '1') && ($posix_compliant ne 'ENABLE')) { + $posix_compliant = undef; + } + } + + if (defined $posix_compliant) { + my @dirlist = @_; + { + no strict 'refs'; + if (${"\cTAINT"}) { # Check for taint mode on perl >= 5.8.0 + require Scalar::Util; + @dirlist = grep { ! Scalar::Util::tainted($_) } @dirlist; + } + } + foreach (@dirlist) { + next unless defined && -d && -w _; + $tmpdir = $_; + last; + } + $tmpdir = $self->curdir unless defined $tmpdir; + $tmpdir = defined $tmpdir && $self->canonpath($tmpdir); + return $tmpdir; + } else { + if (defined $unix_report) { + $tmpdir = $self->_tmpdir( 'sys$scratch/', $ENV{TMPDIR} ); + } else { $tmpdir = $self->_tmpdir( 'sys$scratch:', $ENV{TMPDIR} ); + } + } } =item updir (override) @@ -308,7 +690,30 @@ =cut sub updir { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + if (defined $unix_report) { + return '../'; + } else { return '[-]'; + } } =item case_tolerant (override) @@ -317,6 +722,8 @@ =cut +#fix me - ODS-5 can be case-intolerant. + sub case_tolerant { return 1; } @@ -329,9 +736,36 @@ =cut sub path { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + if (defined $unix_report) { + return () unless exists $ENV{PATH}; + my @path = split(':', $ENV{PATH}); + foreach (@path) { $_ = '.' if $_ eq '' } + return @path; + } else { + # vms mode my (@dirs,$dir,$i); while ($dir = $ENV{'DCL$PATH;' . $i++}) { push(@dirs,$dir); } return @dirs; + } } =item file_name_is_absolute (override) @@ -343,7 +777,9 @@ sub file_name_is_absolute { my ($self,$file) = @_; # If it's a logical name, expand it. - $file = $ENV{$file} while $file =~ /^[\w\$\-]+\Z(?!\n)/s && $ENV{$file}; + # but do not expand it forever and ever... + my $j = 0; + $file = $ENV{$file} while (($file =~ /^[\w\$\-]+\Z(?!\n)/s && $ENV{$file}) && ($j++ < 10)); return scalar($file =~ m!^/!s || $file =~ m![<\[][^.\-\]>]! || $file =~ /:[^<\[]/); @@ -356,11 +792,55 @@ =cut sub splitpath { - my($self,$path) = @_; + my $vms_mode = 1; + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + + my ($self,$path, $nofile) = @_; + if (defined $unix_report) { + $vms_mode = 0; + $vms_mode = 1 if (defined $path) && ($path =~ m|[\]>:]|); + } + + if ($vms_mode == 0) { + + my ($volume,$directory,$file) = ('','',''); + + if ( $nofile ) { + $directory = $path; + } + else { +#print STDERR "#vms.pm splitpath path = undef \n" unless $path; + $path =~ m|^ ( (?: .* / (?: \.\.?\Z(?!\n) )? )? ) ([^/]*) |xs; + $directory = $1; + $file = $2; + } + + return ($volume,$directory,$file); + } else { + #vms mode my($dev,$dir,$file) = ('','',''); vmsify($path) =~ /(.+:)?([\[<].*[\]>])?(.*)/s; return ($1 || '',$2 || '',$3); + } } =item splitdir (override) @@ -370,7 +850,35 @@ =cut sub splitdir { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + my($self,$dirspec) = @_; + my $vms_mode = 1; + if (defined $unix_report) { + $vms_mode = 0; + $vms_mode = 1 if $dirspec =~ m|[\]>:]|; + } + + if ($vms_mode == 0) { + return split m|/|, $_[1], -1; # Preserve trailing fields + } else { $dirspec =~ tr/<>/[]/; # < and > ==> [ and ] $dirspec =~ s/\]\[\./\.\]\[/g; # ][. ==> .][ $dirspec =~ s/\[000000\.\]\[/\[/g; # [000000.][ ==> [ @@ -388,6 +896,7 @@ my(@dirs) = split('\.', vmspath($dirspec)); $dirs[0] =~ s/^[\[<]//s; $dirs[-1] =~ s/[\]>]\Z(?!\n)//s; @dirs; + } } @@ -400,7 +909,58 @@ sub catpath { my($self,$dev,$dir,$file) = @_; + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + + my $vms_mode = 1; + if (defined $unix_report) { + $vms_mode = 0; + $vms_mode = 1 if $dir =~ m|[\]>:;]|; + $vms_mode = 1 if $dev =~ m|[\]>:;]|; + $vms_mode = 1 if (@_ == 4) && $dev ne ''; + } + if ($vms_mode == 0) { + if ( $dir ne '' && + $file ne '' && + substr( $dir, -1 ) ne '/' && + substr( $file, 0, 1 ) ne '/' + ) { + $dir .= "/$file" ; + } + else { + $dir .= $file ; + } + + return $dir ; + } else { # We look for a volume in $dev, then in $dir, but not both + + # Make sure $dir is in VMS format if UNIX emulation is on or you may + # get a UNIX format result that you are not expecting. + if (defined $unix_report) { + if ($dir eq '') { + $dir = '[]'; + } + else { + $dir = vmspath($dir); + } + } my ($dir_volume, $dir_dir, $dir_file) = $self->splitpath($dir); $dev = $dir_volume unless length $dev; $dir = length $dir_file ? $self->catfile($dir_dir, $dir_file) : $dir_dir; @@ -411,7 +971,9 @@ $dir = "[$dir]" unless $dir =~ /[\[<\/]/; $dir = vmspath($dir); } + "$dev$dir$file"; + } } =item abs2rel (override) @@ -421,11 +983,132 @@ =cut sub abs2rel { + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + my $self = shift; + my($path,$base) = @_; + my $vms_mode = 1; + if (defined $unix_report) { + $vms_mode = 0; + if (defined $path) { + $vms_mode = 1 if $path =~ m|[\]>:]|; + } + if (defined $base) { + if ($vms_mode == 1) { + $base = vmsify($base); + } + else { + if ($base =~ m|[\]>:]|) { + if (($vms_mode == 0) && defined $path) { + $path = vmsify($path); + } + $vms_mode = 1 + } + } + } + } + + if ($vms_mode == 0) { + + my $path_abs = 0; + my $base_abs = 0; + + # Clean up $path + if ( ! $self->file_name_is_absolute( $path ) ) { + $path = $self->rel2abs( $path ) ; + } + else { + $path_abs = 1; + $path = $self->canonpath( $path ) ; + } + + # Figure out the effective $base and clean it up. + if ( !defined( $base ) || $base eq '' ) { + $base = $self->_cwd(); + $base_abs = 1; + } + elsif ( ! $self->file_name_is_absolute( $base ) ) { + $base = $self->rel2abs( $base ) ; + } + else { + $base = $self->canonpath( $base ) ; + $base_abs = 1; + } + + # Now, remove all leading components that are the same + my @pathchunks = $self->splitdir( $path); + my @basechunks = $self->splitdir( $base); + + if ($base_abs == 1 || ($path_abs == 1)) { + # Fix me: No real root may be common between the path and base + # It would take comparing the physical device name of the absolute + # paths of both components to find out a relative path could + # be crafted. + + # Different algorithms are also needed when in traditional + # UNIX emulation mode, SYS$POSIX_ROOT mode and the new + # Posix Compliant mode. + + # So if the path is not absolute or the both the path and base + # are not absolute, then returning path is the only safe + # thing to do in UNIX emulation mode. + if (($path_abs == 0) || ($base_abs != $path_abs)) { + return $path; + } + + # If both are absolute, then they need the same base directory + if (!defined $pathchunks[1] || !defined $basechunks[1] || + ($pathchunks[1] ne $basechunks[1])) { + return $path; + } + + } + + while (@pathchunks && @basechunks && $pathchunks[0] eq $basechunks[0]) { + shift @pathchunks ; + shift @basechunks ; + } + + $path = CORE::join( '/', @pathchunks ); + $base = CORE::join( '/', @basechunks ); + + # $base now contains the directories the resulting relative path + # must ascend out of before it can descend to $path_directory. So, + # replace all names with $parentDir + $base =~ s|[^/]+|..|g ; + + # Glue the two together, using a separator if necessary, and preventing an + # empty result. + if ( $path ne '' && $base ne '' ) { + $path = "$base/$path" ; + } else { + $path = "$base$path" ; + } + + return $self->canonpath( $path ) ; + } else { + # vms mode return vmspath(File::Spec::Unix::abs2rel( $self, @_ )) - if grep m{/}, @_; + if (grep m{/}, @_) && !(defined $unix_report); - my($path,$base) = @_; + ($path,$base) = @_; $base = $self->_cwd() unless defined $base and length $base; for ($path, $base) { $_ = $self->canonpath($_) } @@ -440,7 +1123,9 @@ # and we do not even try to call $parse() or consult %ENV for $trnlnm() # (this module needs to run on non VMS platforms after all). +#print STDERR "#vms.pm splitpath call (2) path = $path\n"; my ($path_volume, $path_directories, $path_file) = $self->splitpath($path); +#print STDERR "#vms.pm splitpath call (3) base = $base\n"; my ($base_volume, $base_directories, $base_file) = $self->splitpath($base); return $path unless lc($path_volume) eq lc($base_volume); @@ -448,10 +1133,13 @@ # Now, remove all leading components that are the same my @pathchunks = $self->splitdir( $path_directories ); + my $pathchunks = @pathchunks; unshift(@pathchunks,'000000') unless $pathchunks[0] eq '000000'; my @basechunks = $self->splitdir( $base_directories ); + my $basechunks = @basechunks; unshift(@basechunks,'000000') unless $basechunks[0] eq '000000'; - +#print STDERR "#vms.pm rel2abs basechunks = $basechunks, pathchunks = $pathchunks\n"; + if (case_tolerant) { while ( @pathchunks && @basechunks && lc( $pathchunks[0] ) eq lc( $basechunks[0] ) @@ -459,11 +1147,30 @@ shift @pathchunks ; shift @basechunks ; } - + } + else { + while ( @pathchunks && + @basechunks && + $pathchunks[0] eq $basechunks[0] + ) { + shift @pathchunks ; + shift @basechunks ; + } + } # @basechunks now contains the directories to climb out of, # @pathchunks now has the directories to descend in to. + if ((@basechunks > 0) || ($basechunks != $pathchunks)) { +#print STDERR "#vms.pm rel2abs basechunks > 0\n"; $path_directories = join '.', ('-' x @basechunks, @pathchunks) ; - return $self->canonpath( $self->catpath( '', $path_directories, $path_file ) ) ; + } + else { +#print STDERR "#vms.pm rel2abs basechunks <= 0\n"; + $path_directories = join '.', @pathchunks; + } + $path_directories = '['.$path_directories.']'; +#print STDERR "#vms.pm rel2abs catpath call (3a) p_dir = $path_directories, p_file = $path_file\n"; + return vmsify($self->canonpath( $self->catpath( '', $path_directories, $path_file ) ) ); + } } @@ -474,8 +1181,69 @@ =cut sub rel2abs { + + my $posix_compliant = $ENV{'DECC$POSIX_COMPLIANT_PATHNAMES'}; + if (defined $posix_compliant) { + if (($posix_compliant lt '1') && ($posix_compliant ne 'ENABLE')) { + $posix_compliant = undef; + } + } + + my $unix_report; + my $unix_only = $ENV{'DECC$FILENAME_UNIX_ONLY'}; + if (defined $unix_only) { + if (($unix_only ne '1') && ($unix_only ne 'ENABLE')) { + $unix_only = undef; + } + } + + if (defined $unix_only) { + $unix_report = 1; + } else { + $unix_report = $ENV{'DECC$FILENAME_UNIX_REPORT'}; + if (defined $unix_report) { + if (($unix_report ne '1') && ($unix_report ne 'ENABLE')) { + $unix_report = undef; + } + } + } + my $self = shift ; my ($path,$base ) = @_; + my $vms_mode = 1; + if (defined $unix_report) { + $vms_mode = 0; + if (defined $path) { + $vms_mode = 1 if $path =~ m|[\]>:]|; + } + if (defined $base) { + $vms_mode = 1 if $base =~ m|[\]>:]|; + } + } + + if ($vms_mode == 0) { + + # Clean up $path + if ( ! $self->file_name_is_absolute( $path ) ) { + # Figure out the effective $base and clean it up. + if ( !defined( $base ) || $base eq '' ) { + $base = $self->_cwd(); + } + elsif ( ! $self->file_name_is_absolute( $base ) ) { + $base = $self->rel2abs( $base ) ; + } + else { + $base = $self->canonpath( $base ) ; + } + + # Glom them together + $path = $self->catdir( $base, $path ) ; + } + + return $self->canonpath( $path ) ; + + } else { + # VMS mode return undef unless defined $path; if ($path =~ m/\//) { $path = ( -d $path || $path =~ m/\/\z/ # educated guessing about @@ -488,18 +1256,30 @@ # Figure out the effective $base and clean it up. if ( !defined( $base ) || $base eq '' ) { $base = $self->_cwd; + + $base = vmspath($base) if $vms_mode == 1; } elsif ( ! $self->file_name_is_absolute( $base ) ) { $base = $self->rel2abs( $base ) ; + + $base = vmspath($base) if $vms_mode == 1; } else { $base = $self->canonpath( $base ) ; } + # are we forced to $vms_mode == 0? + if ($vms_mode == 0) { + $base = unixpath($base); # Must have trailing '/' + $path = unixify($path); + } + # Split up paths +#print STDERR "#vms.pm splitpath call (4) path = $path\n"; my ( $path_directories, $path_file ) = ($self->splitpath( $path ))[1,2] ; +#print STDERR "#vms.pm splitpath call (5) base = $base\n"; my ( $base_volume, $base_directories ) = $self->splitpath( $base ) ; @@ -508,15 +1288,18 @@ my $sep = '' ; $sep = '.' if ( $base_directories =~ m{[^.\]>]\Z(?!\n)} && - $path_directories =~ m{^[^.\[<]}s + $path_directories =~ m{^[^.\[<]}s && + $vms_mode == 1 ) ; $base_directories = "$base_directories$sep$path_directories"; $base_directories =~ s{\.?[\]>][\[<]\.?}{.}; +#print STDERR "#vms.pm splitpath call cat_path (5.1) $base_volume, $base_directories, $path_file \n"; $path = $self->catpath( $base_volume, $base_directories, $path_file ); } return $self->canonpath( $path ) ; + } }