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 ) ;
+  }
 }
 
 

Reply via email to