patch@27373 VMS build fix + more long pathname stuff
[p5sagit/p5-mst-13.2.git] / vms / vms.c
index 6c8208a..62092c5 100644 (file)
--- a/vms/vms.c
+++ b/vms/vms.c
@@ -167,6 +167,18 @@ struct itmlst_3 {
   void *bufadr;
   unsigned short int *retlen;
 };
+
+struct filescan_itmlst_2 {
+    unsigned short length;
+    unsigned short itmcode;
+    char * component;
+};
+
+struct vs_str_st {
+    unsigned short length;
+    char str[65536];
+};
+
 #ifdef __DECC
 #pragma message restore
 #pragma member_alignment restore
@@ -246,10 +258,12 @@ static int vms_process_case_tolerant = 1;
 
 /* bug workarounds if needed */
 int decc_bug_readdir_efs1 = 0;
-int decc_bug_devnull = 0;
+int decc_bug_devnull = 1;
 int decc_bug_fgetname = 0;
 int decc_dir_barename = 0;
 
+static int vms_debug_on_exception = 0;
+
 /* Is this a UNIX file specification?
  *   No longer a simple check with EFS file specs
  *   For now, not a full check, but need to
@@ -258,7 +272,7 @@ int decc_dir_barename = 0;
  *   changes to many other conversion routines.
  */
 
-static is_unix_filespec(const char *path)
+static int is_unix_filespec(const char *path)
 {
 int ret_val;
 const char * pch1;
@@ -280,6 +294,254 @@ const char * pch1;
     return ret_val;
 }
 
+/* This handles the expansion of a '^' prefix to the proper character
+ * in a UNIX file specification.
+ *
+ * The output count variable contains the number of characters added
+ * to the output string.
+ *
+ * The return value is the number of characters read from the input
+ * string
+ */
+static int copy_expand_vms_filename_escape
+  (char *outspec, const char *inspec, int *output_cnt)
+{
+int count;
+int scnt;
+
+    count = 0;
+    *output_cnt = 0;
+    if (*inspec == '^') {
+       inspec++;
+       switch (*inspec) {
+       case '.':
+           /* Non trailing dots should just be passed through */
+           *outspec = *inspec;
+           count++;
+           (*output_cnt)++;
+           break;
+       case '_': /* space */
+           *outspec = ' ';
+           inspec++;
+           count++;
+           (*output_cnt)++;
+           break;
+       case 'U': /* Unicode */
+           inspec++;
+           count++;
+           scnt = strspn(inspec, "0123456789ABCDEFabcdef");
+           if (scnt == 4) {
+               unsigned int c1, c2;
+               scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
+               outspec[0] == c1 & 0xff;
+               outspec[1] == c2 & 0xff;
+               if (scnt > 1) {
+                   (*output_cnt) += 2;
+                   count += 4;
+               }
+           }
+           else {
+               /* Error - do best we can to continue */
+               *outspec = 'U';
+               outspec++;
+               (*output_cnt++);
+               *outspec = *inspec;
+               count++;
+               (*output_cnt++);
+           }
+           break;
+       default:
+           scnt = strspn(inspec, "0123456789ABCDEFabcdef");
+           if (scnt == 2) {
+               /* Hex encoded */
+               unsigned int c1;
+               scnt = sscanf(inspec, "%2x", &c1);
+               outspec[0] = c1 & 0xff;
+               if (scnt > 0) {
+                   (*output_cnt++);
+                   count += 2;
+               }
+           }
+           else {
+               *outspec = *inspec;
+               count++;
+               (*output_cnt++);
+           }
+       }
+    }
+    else {
+       *outspec = *inspec;
+       count++;
+       (*output_cnt)++;
+    }
+    return count;
+}
+
+
+int SYS$FILESCAN
+   (const struct dsc$descriptor_s * srcstr,
+    struct filescan_itmlst_2 * valuelist,
+    unsigned long * fldflags,
+    struct dsc$descriptor_s *auxout,
+    unsigned short * retlen);
+
+/* vms_split_path - Verify that the input file specification is a
+ * VMS format file specification, and provide pointers to the components of
+ * it.  With EFS format filenames, this is virtually the only way to
+ * parse a VMS path specification into components.
+ *
+ * If the sum of the components do not add up to the length of the
+ * string, then the passed file specification is probably a UNIX style
+ * path.
+ */
+static int vms_split_path
+   (const char * path,
+    char * * volume,
+    int * vol_len,
+    char * * root,
+    int * root_len,
+    char * * dir,
+    int * dir_len,
+    char * * name,
+    int * name_len,
+    char * * ext,
+    int * ext_len,
+    char * * version,
+    int * ver_len)
+{
+struct dsc$descriptor path_desc;
+int status;
+unsigned long flags;
+int ret_stat;
+struct filescan_itmlst_2 item_list[9];
+const int filespec = 0;
+const int nodespec = 1;
+const int devspec = 2;
+const int rootspec = 3;
+const int dirspec = 4;
+const int namespec = 5;
+const int typespec = 6;
+const int verspec = 7;
+
+    /* Assume the worst for an easy exit */
+    ret_stat = -1;
+    *volume = NULL;
+    *vol_len = 0;
+    *root = NULL;
+    *root_len = 0;
+    *dir = NULL;
+    *dir_len;
+    *name = NULL;
+    *name_len = 0;
+    *ext = NULL;
+    *ext_len = 0;
+    *version = NULL;
+    *ver_len = 0;
+
+    path_desc.dsc$a_pointer = (char *)path; /* cast ok */
+    path_desc.dsc$w_length = strlen(path);
+    path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+    path_desc.dsc$b_class = DSC$K_CLASS_S;
+
+    /* Get the total length, if it is shorter than the string passed
+     * then this was probably not a VMS formatted file specification
+     */
+    item_list[filespec].itmcode = FSCN$_FILESPEC;
+    item_list[filespec].length = 0;
+    item_list[filespec].component = NULL;
+
+    /* If the node is present, then it gets considered as part of the
+     * volume name to hopefully make things simple.
+     */
+    item_list[nodespec].itmcode = FSCN$_NODE;
+    item_list[nodespec].length = 0;
+    item_list[nodespec].component = NULL;
+
+    item_list[devspec].itmcode = FSCN$_DEVICE;
+    item_list[devspec].length = 0;
+    item_list[devspec].component = NULL;
+
+    /* root is a special case,  adding it to either the directory or
+     * the device components will probalby complicate things for the
+     * callers of this routine, so leave it separate.
+     */
+    item_list[rootspec].itmcode = FSCN$_ROOT;
+    item_list[rootspec].length = 0;
+    item_list[rootspec].component = NULL;
+
+    item_list[dirspec].itmcode = FSCN$_DIRECTORY;
+    item_list[dirspec].length = 0;
+    item_list[dirspec].component = NULL;
+
+    item_list[namespec].itmcode = FSCN$_NAME;
+    item_list[namespec].length = 0;
+    item_list[namespec].component = NULL;
+
+    item_list[typespec].itmcode = FSCN$_TYPE;
+    item_list[typespec].length = 0;
+    item_list[typespec].component = NULL;
+
+    item_list[verspec].itmcode = FSCN$_VERSION;
+    item_list[verspec].length = 0;
+    item_list[verspec].component = NULL;
+
+    item_list[8].itmcode = 0;
+    item_list[8].length = 0;
+    item_list[8].component = NULL;
+
+    status = SYS$FILESCAN
+       ((const struct dsc$descriptor_s *)&path_desc, item_list,
+       &flags, NULL, NULL);
+    _ckvmssts(status); /* All failure status values indicate a coding error */
+
+    /* If we parsed it successfully these two lengths should be the same */
+    if (path_desc.dsc$w_length != item_list[filespec].length)
+       return ret_stat;
+
+    /* If we got here, then it is a VMS file specification */
+    ret_stat = 0;
+
+    /* set the volume name */
+    if (item_list[nodespec].length > 0) {
+       *volume = item_list[nodespec].component;
+       *vol_len = item_list[nodespec].length + item_list[devspec].length;
+    }
+    else {
+       *volume = item_list[devspec].component;
+       *vol_len = item_list[devspec].length;
+    }
+
+    *root = item_list[rootspec].component;
+    *root_len = item_list[rootspec].length;
+
+    *dir = item_list[dirspec].component;
+    *dir_len = item_list[dirspec].length;
+
+    /* Now fun with versions and EFS file specifications
+     * The parser can not tell the difference when a "." is a version
+     * delimiter or a part of the file specification.
+     */
+    if ((decc_efs_charset) && 
+       (item_list[verspec].length > 0) &&
+       (item_list[verspec].component[0] == '.')) {
+       *name = item_list[namespec].component;
+       *name_len = item_list[namespec].length + item_list[typespec].length;
+       *ext = item_list[verspec].component;
+       *ext_len = item_list[verspec].length;
+       *version = NULL;
+       *ver_len = 0;
+    }
+    else {
+       *name = item_list[namespec].component;
+       *name_len = item_list[namespec].length;
+       *ext = item_list[typespec].component;
+       *ext_len = item_list[typespec].length;
+       *version = item_list[verspec].component;
+       *ver_len = item_list[verspec].length;
+    }
+    return ret_stat;
+}
+
 
 /* my_maxidx
  * Routine to retrieve the maximum equivalence index for an input
@@ -1256,7 +1518,7 @@ mp_do_kill_file(pTHX_ const char *name, int dirflag)
      * system services won't do this by themselves, so we may miss
      * a file "hiding" behind a logical name or search list. */
     Newx(vmsname, NAM$C_MAXRSS+1, char);
-    if (do_tovmsspec(name,vmsname,0) == NULL) {
+    if (do_rmsexpand(name, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
       Safefree(vmsname);
       return -1;
     }
@@ -1268,7 +1530,7 @@ mp_do_kill_file(pTHX_ const char *name, int dirflag)
     }
     else {
       Newx(rspec, NAM$C_MAXRSS+1, char);
-      if (do_rmsexpand(vmsname, rspec, 1, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
+      if (do_rmsexpand(vmsname, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
        Safefree(rspec);
         Safefree(vmsname);
        return -1;
@@ -1422,7 +1684,8 @@ Perl_do_rmdir(pTHX_ const char *name)
 int
 Perl_kill_file(pTHX_ const char *name)
 {
-    char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
+    char rspec[NAM$C_MAXRSS+1];
+    char *tspec;
     unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
     unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
     struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
@@ -1446,8 +1709,8 @@ Perl_kill_file(pTHX_ const char *name)
     /* Expand the input spec using RMS, since the CRTL remove() and
      * system services won't do this by themselves, so we may miss
      * a file "hiding" behind a logical name or search list. */
-    if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
-    if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
+    tspec = do_rmsexpand(name, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS);
+    if (tspec == NULL) return -1;
     if (!remove(rspec)) return 0;   /* Can we just get rid of it? */
     /* If not, can changing protections help? */
     if (vaxc$errno != RMS$_PRV) return -1;
@@ -1570,12 +1833,12 @@ Perl_my_chdir(pTHX_ const char *dir)
    * - Preview- '/' will be valid soon on VMS
    */
   if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
-    char *newdir = savepvn(dir,dirlen-1);
+    char *newdir = savepvn(dir1,dirlen-1);
     int ret = chdir(newdir);
     Safefree(newdir);
     return ret;
   }
-  else return chdir(dir);
+  else return chdir(dir1);
 }  /* end of my_chdir */
 /*}}}*/
 
@@ -1660,8 +1923,8 @@ Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
 
 #define _MY_SIG_MAX 17
 
-unsigned int
-Perl_sig_to_vmscondition(int sig)
+static unsigned int
+Perl_sig_to_vmscondition_int(int sig)
 {
     static unsigned int sig_code[_MY_SIG_MAX+1] = 
     {
@@ -1703,6 +1966,17 @@ Perl_sig_to_vmscondition(int sig)
     return sig_code[sig];
 }
 
+unsigned int
+Perl_sig_to_vmscondition(int sig)
+{
+#ifdef SS$_DEBUG
+    if (vms_debug_on_exception != 0)
+       lib$signal(SS$_DEBUG);
+#endif
+    return Perl_sig_to_vmscondition_int(sig);
+}
+
+
 int
 Perl_my_kill(int pid, int sig)
 {
@@ -1738,7 +2012,7 @@ Perl_my_kill(int pid, int sig)
        return -1;
     }
 
-    code = Perl_sig_to_vmscondition(sig);
+    code = Perl_sig_to_vmscondition_int(sig);
 
     if (!code) {
        SETERRNO(EINVAL, SS$_BADPARAM);
@@ -3025,12 +3299,15 @@ find_vmspipe(pTHX)
         pPLOC  p = head_PLOC;
 
         while (p) {
+           char * exp_res;
             strcpy(file, p->dir);
             strncat(file, "vmspipe.com",NAM$C_MAXRSS);
             file[NAM$C_MAXRSS] = '\0';
             p = p->next;
 
-            if (!do_tovmsspec(file,vmspipe_file,0)) continue;
+            exp_res = do_rmsexpand
+               (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS);
+            if (!exp_res) continue;
 
             if (cando_by_name(S_IRUSR, 0, vmspipe_file)
              && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
@@ -3127,11 +3404,7 @@ vmspipe_tempfile(pTHX)
     if (!fp) return 0;
     fstat(fileno(fp), (struct stat *)&s1);
 
-    #if defined(_USE_STD_STAT)
-      cmp_result = s0.crtl_stat.st_ino != s1.crtl_stat.st_ino;
-    #else
-      cmp_result = memcmp(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino, 6);
-    #endif
+    cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
     if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime))  {
         fclose(fp);
         return 0;
@@ -4475,6 +4748,7 @@ static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
         for (cp2 = cp1; cp2 > trndir; cp2--) {
          if (*cp2 == '.') {
            if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
+/* fix-me, can not scan EFS file specs backward like this */
               *cp2 = *cp1; *cp1 = '\0';
               hasfilename = 1;
              break;
@@ -4739,6 +5013,7 @@ static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
       retlen = strlen(esa);
       cp1 = strrchr(esa,'.');
       /* ODS-5 directory specifications can have extra "." in them. */
+      /* Fix-me, can not scan EFS file specifications backwards */
       while (cp1 != NULL) {
         if ((cp1-1 == esa) || (*(cp1-1) != '^'))
          break;
@@ -4786,7 +5061,7 @@ static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
           memcpy(retspec,esa,dirlen);
           if (!strncmp(cp1+2,"000000]",7)) {
             retspec[dirlen-1] = '\0';
-           /* Not full ODS-5, just extra dots in directories for now */
+           /* fix-me Not full ODS-5, just extra dots in directories for now */
            cp1 = retspec + dirlen - 1;
            while (cp1 > retspec)
            {
@@ -5110,7 +5385,7 @@ char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
 {
   static char __tounixspec_retbuf[VMS_MAXRSS];
-  char *dirend, *rslt, *cp1, *cp3, tmp[VMS_MAXRSS];
+  char *dirend, *rslt, *cp1, *cp3, *tmp;
   const char *cp2;
   int devlen, dirlen, retlen = VMS_MAXRSS;
   int expand = 1; /* guarantee room for leading and trailing slashes */
@@ -5257,6 +5532,7 @@ static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
 #else
   cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
 #endif
+  Newx(tmp, VMS_MAXRSS, char);
   if (cmp_rslt == 0) {
   int islnm;
 
@@ -5280,11 +5556,13 @@ static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
     cp2++;
     if (*cp2 == ']' || *cp2 == '>') {
       *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
+      Safefree(tmp);
       return rslt;
     }
     else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
-      if (getcwd(tmp,sizeof tmp,1) == NULL) {
+      if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
         if (ts) Safefree(rslt);
+       Safefree(tmp);
         return NULL;
       }
       trnlnm_iter_count = 0;
@@ -5306,7 +5584,10 @@ static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
       *(cp1++) = '/';
       while (*cp3) {
         *(cp1++) = *(cp3++);
-        if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
+        if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
+           Safefree(tmp);
+           return NULL; /* No room */
+       }
       }
       *(cp1++) = '/';
     }
@@ -5323,6 +5604,7 @@ static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
       else cp2++;
     }
   }
+  Safefree(tmp);
   for (; cp2 <= dirend; cp2++) {
     if ((*cp2 == '^')) {
        /* EFS file escape, pass the next character as is */
@@ -6363,7 +6645,7 @@ static char *mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts) {
        break;
     case ';':
        /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
-        * which is wrong.  UNIX notation should be ".dir. unless
+        * which is wrong.  UNIX notation should be ".dir." unless
         * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
         * changing this behavior could break more things at this time.
         * efs character set effectively does not allow "." to be a version
@@ -6813,6 +7095,7 @@ $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
 $DESCRIPTOR(resultspec, "");
 unsigned long int lff_flags = 0;
 int sts;
+int rms_sts;
 
 #ifdef VMS_LONGNAME_SUPPORT
     lff_flags = LIB$M_FIL_LONG_NAMES;
@@ -6862,7 +7145,7 @@ int sts;
     
     while ($VMS_STATUS_SUCCESS(sts = lib$find_file
                                 (&filespec, &resultspec, &context,
-                                 &defaultspec, 0, 0, &lff_flags)))
+                                 &defaultspec, 0, &rms_sts, &lff_flags)))
        {
        char *string;
        char *c;
@@ -7495,10 +7778,18 @@ DIR *
 Perl_opendir(pTHX_ const char *name)
 {
     DIR *dd;
-    char dir[NAM$C_MAXRSS+1];
+    char *dir;
     Stat_t sb;
+    int unix_flag;
 
+    unix_flag = 0;
+    if (decc_efs_charset) {
+        unix_flag = is_unix_filespec(name);
+    }
+
+    Newx(dir, VMS_MAXRSS, char);
     if (do_tovmspath(name,dir,0) == NULL) {
+      Safefree(dir);
       return NULL;
     }
     /* Check access before stat; otherwise stat does not
@@ -7506,10 +7797,12 @@ Perl_opendir(pTHX_ const char *name)
      */
     if (!cando_by_name(S_IRUSR,0,dir)) {
       /* cando_by_name has already set errno */
+      Safefree(dir);
       return NULL;
     }
     if (flex_stat(dir,&sb) == -1) return NULL;
     if (!S_ISDIR(sb.st_mode)) {
+      Safefree(dir);
       set_errno(ENOTDIR);  set_vaxc_errno(RMS$_DIR);
       return NULL;
     }
@@ -7519,9 +7812,12 @@ Perl_opendir(pTHX_ const char *name)
 
     /* Fill in the fields; mainly playing with the descriptor. */
     sprintf(dd->pattern, "%s*.*",dir);
+    Safefree(dir);
     dd->context = 0;
     dd->count = 0;
-    dd->vms_wantversions = 0;
+    dd->flags = 0;
+    if (unix_flag)
+       dd->flags = PERL_VMSDIR_M_UNIXSPECS;
     dd->pat.dsc$a_pointer = dd->pattern;
     dd->pat.dsc$w_length = strlen(dd->pattern);
     dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
@@ -7544,7 +7840,10 @@ Perl_opendir(pTHX_ const char *name)
 void
 vmsreaddirversions(DIR *dd, int flag)
 {
-    dd->vms_wantversions = flag;
+    if (flag)
+       dd->flags |= PERL_VMSDIR_M_VERSIONS;
+    else
+       dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
 }
 /*}}}*/
 
@@ -7576,7 +7875,7 @@ collectversions(pTHX_ DIR *dd)
     struct dsc$descriptor_s    pat;
     struct dsc$descriptor_s    res;
     struct dirent *e;
-    char *p, *text, buff[sizeof dd->entry.d_name];
+    char *p, *text, *buff;
     int i;
     unsigned long context, tmpsts;
 
@@ -7596,8 +7895,9 @@ collectversions(pTHX_ DIR *dd)
     pat.dsc$b_class = DSC$K_CLASS_S;
 
     /* Set up result descriptor. */
+    Newx(buff, VMS_MAXRSS, char);
     res.dsc$a_pointer = buff;
-    res.dsc$w_length = sizeof buff - 2;
+    res.dsc$w_length = VMS_MAXRSS - 1;
     res.dsc$b_dtype = DSC$K_DTYPE_T;
     res.dsc$b_class = DSC$K_CLASS_S;
 
@@ -7605,10 +7905,16 @@ collectversions(pTHX_ DIR *dd)
     for (context = 0, e->vms_verscount = 0;
          e->vms_verscount < VERSIZE(e);
          e->vms_verscount++) {
-       tmpsts = lib$find_file(&pat, &res, &context);
+       unsigned long rsts;
+       unsigned long flags = 0;
+
+#ifdef VMS_LONGNAME_SUPPORT
+       flags = LIB$M_FIL_LONG_NAMES
+#endif
+       tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
        if (tmpsts == RMS$_NMF || context == 0) break;
        _ckvmssts(tmpsts);
-       buff[sizeof buff - 1] = '\0';
+       buff[VMS_MAXRSS - 1] = '\0';
        if ((p = strchr(buff, ';')))
            e->vms_versions[e->vms_verscount] = atoi(p + 1);
        else
@@ -7617,6 +7923,7 @@ collectversions(pTHX_ DIR *dd)
 
     _ckvmssts(lib$find_file_end(&context));
     Safefree(text);
+    Safefree(buff);
 
 }  /* end of collectversions() */
 
@@ -7628,15 +7935,26 @@ struct dirent *
 Perl_readdir(pTHX_ DIR *dd)
 {
     struct dsc$descriptor_s    res;
-    char *p, buff[sizeof dd->entry.d_name];
+    char *p, *buff;
     unsigned long int tmpsts;
+    unsigned long rsts;
+    unsigned long flags = 0;
+    char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
+    int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
 
     /* Set up result descriptor, and get next file. */
+    Newx(buff, VMS_MAXRSS, char);
     res.dsc$a_pointer = buff;
-    res.dsc$w_length = sizeof buff - 2;
+    res.dsc$w_length = VMS_MAXRSS - 1;
     res.dsc$b_dtype = DSC$K_DTYPE_T;
     res.dsc$b_class = DSC$K_CLASS_S;
-    tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
+
+#ifdef VMS_LONGNAME_SUPPORT
+    flags = LIB$M_FIL_LONG_NAMES
+#endif
+
+    tmpsts = lib$find_file
+       (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
     if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL;  /* None left. */
     if (!(tmpsts & 1)) {
       set_vaxc_errno(tmpsts);
@@ -7652,34 +7970,78 @@ Perl_readdir(pTHX_ DIR *dd)
         default:
           set_errno(EVMSERR);
       }
+      Safefree(buff);
       return NULL;
     }
     dd->count++;
     /* Force the buffer to end with a NUL, and downcase name to match C convention. */
     if (!decc_efs_case_preserve) {
-      buff[sizeof buff - 1] = '\0';
+      buff[VMS_MAXRSS - 1] = '\0';
       for (p = buff; *p; p++) *p = _tolower(*p);
-      while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
-      *p = '\0';
     }
     else {
       /* we don't want to force to lowercase, just null terminate */
       buff[res.dsc$w_length] = '\0';
     }
-    for (p = buff; *p; p++) *p = _tolower(*p);
     while (--p >= buff) if (!isspace(*p)) break;  /* Do we really need this? */
     *p = '\0';
 
     /* Skip any directory component and just copy the name. */
-    if ((p = strchr(buff, ']'))) strcpy(dd->entry.d_name, p + 1);
-    else strcpy(dd->entry.d_name, buff);
+    sts = vms_split_path
+       (buff,
+       &v_spec,
+       &v_len,
+       &r_spec,
+       &r_len,
+       &d_spec,
+       &d_len,
+       &n_spec,
+       &n_len,
+       &e_spec,
+       &e_len,
+       &vs_spec,
+       &vs_len);
+
+    /* Drop NULL extensions on UNIX file specification */
+    if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
+       (e_len == 1) && decc_readdir_dropdotnotype)) {
+       e_len = 0;
+       e_spec[0] = '\0';
+    }
+
+    strncpy(dd->entry.d_name, n_spec, n_len + e_len);
+    dd->entry.d_name[n_len + e_len] = '\0';
+    dd->entry.d_namlen = strlen(dd->entry.d_name);
 
-    /* Clobber the version. */
-    if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
+    /* Convert the filename to UNIX format if needed */
+    if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
+
+       /* Translate the encoded characters. */
+       /* Fixme: unicode handling could result in embedded 0 characters */
+       if (strchr(dd->entry.d_name, '^') != NULL) {
+           char new_name[256];
+           char * q;
+           int cnt;
+           p = dd->entry.d_name;
+           q = new_name;
+           while (*p != 0) {
+               int x, y;
+               x = copy_expand_vms_filename_escape(q, p, &y);
+               p += x;
+               q += y;
+               /* fix-me */
+               /* if y > 1, then this is a wide file specification */
+               /* Wide file specifications need to be passed in Perl */
+               /* counted strings apparently with a unicode flag */
+           }
+           *q = 0;
+           strcpy(dd->entry.d_name, new_name);
+       }
+    }
 
-    dd->entry.d_namlen = strlen(dd->entry.d_name);
     dd->entry.vms_verscount = 0;
-    if (dd->vms_wantversions) collectversions(aTHX_ dd);
+    if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
+    Safefree(buff);
     return &dd->entry;
 
 }  /* end of readdir() */
@@ -7725,15 +8087,15 @@ Perl_telldir(DIR *dd)
 void
 Perl_seekdir(pTHX_ DIR *dd, long count)
 {
-    int vms_wantversions;
+    int old_flags;
 
     /* If we haven't done anything yet... */
     if (dd->count == 0)
        return;
 
     /* Remember some state, and clear it. */
-    vms_wantversions = dd->vms_wantversions;
-    dd->vms_wantversions = 0;
+    old_flags = dd->flags;
+    dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
     _ckvmssts(lib$find_file_end(&dd->context));
     dd->context = 0;
 
@@ -7741,7 +8103,7 @@ Perl_seekdir(pTHX_ DIR *dd, long count)
     for (dd->count = 0; dd->count < count; )
        readdir(dd);
 
-    dd->vms_wantversions = vms_wantversions;
+    dd->flags = old_flags;
 
 }  /* end of seekdir() */
 /*}}}*/
@@ -7929,19 +8291,20 @@ setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
   }
 
   if (!isdcl) {
+    int rsts;
     imgdsc.dsc$a_pointer = s;
     imgdsc.dsc$w_length = wordbreak - s;
-    retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
+    retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
     if (!(retsts&1)) {
         _ckvmssts(lib$find_file_end(&cxt));
-        retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
+        retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
       if (!(retsts & 1) && *s == '$') {
         _ckvmssts(lib$find_file_end(&cxt));
        imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
-       retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
+       retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
        if (!(retsts&1)) {
          _ckvmssts(lib$find_file_end(&cxt));
-          retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
+          retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
         }
       }
     }
@@ -9504,7 +9867,7 @@ is_null_device(name)
     const char *name;
 {
   if (decc_bug_devnull != 0) {
-    if (strcmp("/dev/null", name) == 0) /* temp hack */
+    if (strncmp("/dev/null", name, 9) == 0)
       return 1;
   }
     /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
@@ -9586,7 +9949,8 @@ Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
   static char usrname[L_cuserid];
   static struct dsc$descriptor_s usrdsc =
          {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
-  char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
+  char vmsname[NAM$C_MAXRSS+1];
+  char *fileified;
   unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
   unsigned short int retlen, trnlnm_iter_count;
   struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
@@ -9602,6 +9966,7 @@ Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
 
   if (!fname || !*fname) return FALSE;
   /* Make sure we expand logical names, since sys$check_access doesn't */
+  Newx(fileified, VMS_MAXRSS, char);
   if (!strpbrk(fname,"/]>:")) {
     strcpy(fileified,fname);
     trnlnm_iter_count = 0;
@@ -9611,7 +9976,10 @@ Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
     }
     fname = fileified;
   }
-  if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
+  if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS)) {
+    Safefree(fileified);
+    return FALSE;
+  }
   retlen = namdsc.dsc$w_length = strlen(vmsname);
   namdsc.dsc$a_pointer = vmsname;
   if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
@@ -9631,6 +9999,7 @@ Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
     case S_IDUSR: case S_IDGRP: case S_IDOTH:
       access = ARM$M_DELETE; break;
     default:
+      Safefree(fileified);
       return FALSE;
   }
 
@@ -9674,13 +10043,16 @@ Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
     if (retsts == SS$_NOPRIV) set_errno(EACCES);
     else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
     else set_errno(ENOENT);
+    Safefree(fileified);
     return FALSE;
   }
   if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
+    Safefree(fileified);
     return TRUE;
   }
   _ckvmssts(retsts);
 
+  Safefree(fileified);
   return FALSE;  /* Should never get here */
 
 }  /* end of cando_by_name() */
@@ -9702,11 +10074,8 @@ Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
        if (cptr == NULL)
           namecache[0] = '\0';
     }
-#ifdef _USE_STD_STAT
-    memcpy(&statbufp->st_ino, &statbufp->crtl_stat.st_ino, 8);
-#else
-    memcpy(&statbufp->st_ino, statbufp->crtl_stat.st_ino, 8);
-#endif
+
+    VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
 #ifndef _USE_STD_STAT
     strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
     statbufp->st_devnam[63] = 0;
@@ -9822,11 +10191,7 @@ Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
   }
 #endif
     if (!retval) {
-#ifdef _USE_STD_STAT
-      memcpy(&statbufp->st_ino, &statbufp->crtl_stat.st_ino, 8);
-#else
-      memcpy(&statbufp->st_ino, statbufp->crtl_stat.st_ino, 8);
-#endif
+      VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
 #ifndef _USE_STD_STAT
       strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
       statbufp->st_devnam[63] = 0;
@@ -10635,6 +11000,170 @@ hushexit_fromperl(pTHX_ CV *cv)
     XSRETURN(1);
 }
 
+
+PerlIO * 
+Perl_vms_start_glob
+   (pTHX_ SV *tmpglob,
+    IO *io)
+{
+    PerlIO *fp;
+    struct vs_str_st *rslt;
+    char *vmsspec;
+    char *rstr;
+    char *begin, *cp;
+    $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
+    PerlIO *tmpfp;
+    STRLEN i;
+    struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
+    struct dsc$descriptor_vs rsdsc;
+    unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
+    unsigned long hasver = 0, isunix = 0;
+    unsigned long int lff_flags = 0;
+    int rms_sts;
+
+#ifdef VMS_LONGNAME_SUPPORT
+    lff_flags = LIB$M_FIL_LONG_NAMES;
+#endif
+    /* The Newx macro will not allow me to assign a smaller array
+     * to the rslt pointer, so we will assign it to the begin char pointer
+     * and then copy the value into the rslt pointer.
+     */
+    Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
+    rslt = (struct vs_str_st *)begin;
+    rslt->length = 0;
+    rstr = &rslt->str[0];
+    rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
+    rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
+    rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
+    rsdsc.dsc$b_class = DSC$K_CLASS_VS;
+
+    Newx(vmsspec, VMS_MAXRSS, char);
+
+       /* We could find out if there's an explicit dev/dir or version
+          by peeking into lib$find_file's internal context at
+          ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
+          but that's unsupported, so I don't want to do it now and
+          have it bite someone in the future. */
+       /* Fix-me: vms_split_path() is the only way to do this, the
+          existing method will fail with many legal EFS or UNIX specifications
+        */
+
+    cp = SvPV(tmpglob,i);
+
+    for (; i; i--) {
+       if (cp[i] == ';') hasver = 1;
+       if (cp[i] == '.') {
+           if (sts) hasver = 1;
+           else sts = 1;
+       }
+       if (cp[i] == '/') {
+           hasdir = isunix = 1;
+           break;
+       }
+       if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
+           hasdir = 1;
+           break;
+       }
+    }
+    if ((tmpfp = PerlIO_tmpfile()) != NULL) {
+       Stat_t st;
+       int stat_sts;
+       stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
+       if (!stat_sts && S_ISDIR(st.st_mode)) {
+           wilddsc.dsc$a_pointer = tovmspath(SvPVX(tmpglob),vmsspec);
+           ok = (wilddsc.dsc$a_pointer != NULL);
+       }
+       else {
+           wilddsc.dsc$a_pointer = tovmsspec(SvPVX(tmpglob),vmsspec);
+           ok = (wilddsc.dsc$a_pointer != NULL);
+       }
+       if (ok)
+           wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
+
+       /* If not extended character set, replace ? with % */
+       /* With extended character set, ? is a wildcard single character */
+       if (!decc_efs_case_preserve) {
+           for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
+               if (*cp == '?') *cp = '%';
+       }
+       sts = SS$_NORMAL;
+       while (ok && $VMS_STATUS_SUCCESS(sts)) {
+        char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
+        int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
+
+           sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
+                               &dfltdsc,NULL,&rms_sts,&lff_flags);
+           if (!$VMS_STATUS_SUCCESS(sts))
+               break;
+
+           /* with varying string, 1st word of buffer contains result length */
+           rstr[rslt->length] = '\0';
+
+            /* Find where all the components are */
+            v_sts = vms_split_path
+                      (rstr,
+                       &v_spec,
+                       &v_len,
+                       &r_spec,
+                       &r_len,
+                       &d_spec,
+                       &d_len,
+                       &n_spec,
+                       &n_len,
+                       &e_spec,
+                       &e_len,
+                       &vs_spec,
+                       &vs_len);
+
+           /* If no version on input, truncate the version on output */
+           if (!hasver && (vs_len > 0)) {
+               *vs_spec = '\0';
+               vs_len = 0;
+
+               /* No version & a null extension on UNIX handling */
+               if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
+                   e_len = 0;
+                   *e_spec = '\0';
+               }
+           }
+
+           if (!decc_efs_case_preserve) {
+               for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
+           }
+
+           if (hasdir) {
+               if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
+               begin = rstr;
+           }
+           else {
+               /* Start with the name */
+               begin = n_spec;
+           }
+           strcat(begin,"\n");
+           ok = (PerlIO_puts(tmpfp,begin) != EOF);
+       }
+       if (cxt) (void)lib$find_file_end(&cxt);
+       if (ok && sts != RMS$_NMF &&
+           sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
+       if (!ok) {
+           if (!(sts & 1)) {
+               SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
+           }
+           PerlIO_close(tmpfp);
+           fp = NULL;
+       }
+       else {
+           PerlIO_rewind(tmpfp);
+           IoTYPE(io) = IoTYPE_RDONLY;
+           IoIFP(io) = fp = tmpfp;
+           IoFLAGS(io) &= ~IOf_UNTAINT;  /* maybe redundant */
+       }
+    }
+    Safefree(vmsspec);
+    Safefree(rslt);
+    return fp;
+}
+
 #ifdef HAS_SYMLINK
 static char *
 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec);
@@ -10877,6 +11406,17 @@ static int set_features
     unsigned long case_image;
 #endif
 
+    /* Allow an exception to bring Perl into the VMS debugger */
+    vms_debug_on_exception = 0;
+    status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
+    if ($VMS_STATUS_SUCCESS(status)) {
+       if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
+        vms_debug_on_exception = 1;
+       else
+        vms_debug_on_exception = 0;
+    }
+
+
     /* hacks to see if known bugs are still present for testing */
 
     /* Readdir is returning filenames in VMS syntax always */
@@ -10890,11 +11430,13 @@ static int set_features
     }
 
     /* PCP mode requires creating /dev/null special device file */
-    decc_bug_devnull = 0;
+    decc_bug_devnull = 1;
     status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
     if ($VMS_STATUS_SUCCESS(status)) {
        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
           decc_bug_devnull = 1;
+       else
+         decc_bug_devnull = 0;
     }
 
     /* fgetname returning a VMS name in UNIX mode */