applied suggested patch, modulo already applied parts
[p5sagit/p5-mst-13.2.git] / vms / vms.c
index 8870a0f..1212555 100644 (file)
--- a/vms/vms.c
+++ b/vms/vms.c
@@ -2,8 +2,8 @@
  *
  * VMS-specific routines for perl5
  *
- * Last revised: 27-Feb-1998 by Charles Bailey  bailey@newman.upenn.edu
- * Version: 5.4.61
+ * Last revised: 24-Apr-1999 by Charles Bailey  bailey@newman.upenn.edu
+ * Version: 5.5.58
  */
 
 #include <acedef.h>
@@ -21,6 +21,7 @@
 #include <iodef.h>
 #include <jpidef.h>
 #include <kgbdef.h>
+#include <libclidef.h>
 #include <libdef.h>
 #include <lib$routines.h>
 #include <lnmdef.h>
 #include "EXTERN.h"
 #include "perl.h"
 #include "XSUB.h"
+/* Anticipating future expansion in lexical warnings . . . */
+#ifndef WARN_INTERNAL
+#  define WARN_INTERNAL WARN_MISC
+#endif
 
 /* gcc's header files don't #define direct access macros
  * corresponding to VAXC's variant structs */
@@ -77,55 +82,141 @@ static char *__mystrtolower(char *str)
   return str;
 }
 
+static struct dsc$descriptor_s fildevdsc = 
+  { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
+static struct dsc$descriptor_s crtlenvdsc = 
+  { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
+static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
+static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
+static struct dsc$descriptor_s **env_tables = defenv;
+static bool will_taint = FALSE;  /* tainting active, but no PL_curinterp yet */
+
+/*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
 int
-my_trnlnm(char *lnm, char *eqv, unsigned long int idx)
+vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
+  struct dsc$descriptor_s **tabvec, unsigned long int flags)
 {
-    static char __my_trnlnm_eqv[LNM$C_NAMLENGTH+1];
-    unsigned short int eqvlen;
+    char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
+    unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
     unsigned long int retsts, attr = LNM$M_CASE_BLIND;
-    $DESCRIPTOR(tabdsc,"LNM$FILE_DEV");
-    struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
-    struct itmlst_3 lnmlst[3] = {{sizeof idx,      LNM$_INDEX,  &idx, 0},
-                                 {LNM$C_NAMLENGTH, LNM$_STRING, 0,    &eqvlen},
+    unsigned char acmode;
+    struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
+                            tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
+    struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
+                                 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
                                  {0, 0, 0, 0}};
+    $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
 
-    if (!lnm || idx > LNM$_MAX_INDEX) {
+    if (!lnm || !eqv || idx > LNM$_MAX_INDEX) {
       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
     }
-    if (!eqv) eqv = __my_trnlnm_eqv;
-    lnmlst[1].bufadr = (void *)eqv;
-    lnmdsc.dsc$a_pointer = lnm;
-    lnmdsc.dsc$w_length = strlen(lnm);
-    retsts = sys$trnlnm(&attr,&tabdsc,&lnmdsc,0,lnmlst);
-    if (retsts == SS$_NOLOGNAM || retsts == SS$_IVLOGNAM) {
-      set_vaxc_errno(retsts); set_errno(EINVAL); return 0;
+    for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
+      *cp2 = _toupper(*cp1);
+      if (cp1 - lnm > LNM$C_NAMLENGTH) {
+        set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
+        return 0;
+      }
     }
-    else if (retsts & 1) {
-      eqv[eqvlen] = '\0';
-      return eqvlen;
+    lnmdsc.dsc$w_length = cp1 - lnm;
+    lnmdsc.dsc$a_pointer = uplnm;
+    secure = flags & PERL__TRNENV_SECURE;
+    acmode = secure ? PSL$C_EXEC : PSL$C_USER;
+    if (!tabvec || !*tabvec) tabvec = env_tables;
+
+    for (curtab = 0; tabvec[curtab]; curtab++) {
+      if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
+        if (!ivenv && !secure) {
+          char *eq, *end;
+          int i;
+          if (!environ) {
+            ivenv = 1; 
+            warn("Can't read CRTL environ\n");
+            continue;
+          }
+          retsts = SS$_NOLOGNAM;
+          for (i = 0; environ[i]; i++) { 
+            if ((eq = strchr(environ[i],'=')) && 
+                !strncmp(environ[i],uplnm,eq - environ[i])) {
+              eq++;
+              for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
+              if (!eqvlen) continue;
+              retsts = SS$_NORMAL;
+              break;
+            }
+          }
+          if (retsts != SS$_NOLOGNAM) break;
+        }
+      }
+      else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
+               !str$case_blind_compare(&tmpdsc,&clisym)) {
+        if (!ivsym && !secure) {
+          unsigned short int deflen = LNM$C_NAMLENGTH;
+          struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
+          /* dynamic dsc to accomodate possible long value */
+          _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
+          retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
+          if (retsts & 1) { 
+            if (eqvlen > 1024) {
+              set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
+              eqvlen = 1024;
+              if (ckWARN(WARN_MISC))
+                warner(WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
+            }
+            strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
+          }
+          _ckvmssts(lib$sfree1_dd(&eqvdsc));
+          if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
+          if (retsts == LIB$_NOSUCHSYM) continue;
+          break;
+        }
+      }
+      else if (!ivlnm) {
+        retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
+        if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
+        if (retsts == SS$_NOLOGNAM) continue;
+        break;
+      }
     }
-    _ckvmssts(retsts);  /* Must be an error */
-    return 0;      /* Not reached, assuming _ckvmssts() bails out */
+    if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
+    else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
+             retsts == SS$_IVLOGNAM   || retsts == SS$_IVLOGTAB   ||
+             retsts == SS$_NOLOGNAM) {
+      set_errno(EINVAL);  set_vaxc_errno(retsts);
+    }
+    else _ckvmssts(retsts);
+    return 0;
+}  /* end of vmstrnenv */
+/*}}}*/
 
-}  /* end of my_trnlnm */
+
+/*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
+/* Define as a function so we can access statics. */
+int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)
+{
+  return vmstrnenv(lnm,eqv,idx,fildev,                                   
+#ifdef SECURE_INTERNAL_GETENV
+                   (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
+#else
+                   0
+#endif
+                                                                              );
+}
+/*}}}*/
 
 /* my_getenv
- * Translate a logical name.  Substitute for CRTL getenv() to avoid
- * memory leak, and to keep my_getenv() and my_setenv() in the same
- * domain (mostly - my_getenv() need not return a translation from
- * the process logical name table)
- *
  * Note: Uses Perl temp to store result so char * can be returned to
  * caller; this pointer will be invalidated at next Perl statement
  * transition.
+ * We define this as a function rather than a macro in terms of my_getenv_sv()
+ * so that it'll work when PL_curinterp is undefined (and we therefore can't
+ * allocate SVs).
  */
-/*{{{ char *my_getenv(const char *lnm)*/
+/*{{{ char *my_getenv(const char *lnm, bool sys)*/
 char *
-my_getenv(const char *lnm)
+my_getenv(const char *lnm, bool sys)
 {
     static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
-    char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
-    const char *cp1;
+    char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
     unsigned long int idx = 0;
     int trnsuccess;
     SV *tmpsv;
@@ -138,44 +229,66 @@ my_getenv(const char *lnm)
       eqv = SvPVX(tmpsv);
     }
     else eqv = __my_getenv_eqv;  /* Assume no interpreter ==> single thread */
-    for (cp1 = lnm, cp2= uplnm; *cp1; cp1++, cp2++) *cp2 = _toupper(*cp1);
-    *cp2 = '\0';
-    if (cp1 - lnm == 7 && !strncmp(uplnm,"DEFAULT",7)) {
+    for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
+    if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
       getcwd(eqv,LNM$C_NAMLENGTH);
       return eqv;
     }
     else {
-      if ((cp2 = strchr(uplnm,';')) != NULL) {
-        *cp2 = '\0';
+      if ((cp2 = strchr(lnm,';')) != NULL) {
+        strcpy(uplnm,lnm);
+        uplnm[cp2-lnm] = '\0';
         idx = strtoul(cp2+1,NULL,0);
+        lnm = uplnm;
       }
-      trnsuccess = my_trnlnm(uplnm,eqv,idx);
-      /* If we had a translation index, we're only interested in lnms */
-      if (!trnsuccess && cp2 != NULL) return Nullch;
-      if (trnsuccess) return eqv;
-      else {
-        unsigned long int retsts;
-        struct dsc$descriptor_s symdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
-                                valdsc = {LNM$C_NAMLENGTH,DSC$K_DTYPE_T,
-                                          DSC$K_CLASS_S, eqv};
-        symdsc.dsc$w_length = cp1 - lnm;
-        symdsc.dsc$a_pointer = uplnm;
-        retsts = lib$get_symbol(&symdsc,&valdsc,&(valdsc.dsc$w_length),0);
-        if (retsts == LIB$_INVSYMNAM) return Nullch;
-        if (retsts != LIB$_NOSUCHSYM) {
-          /* We want to return only logical names or CRTL Unix emulations */
-          if (retsts & 1) return Nullch;
-          _ckvmssts(retsts);
-        }
-        /* Try for CRTL emulation of a Unix/POSIX name */
-        else return getenv(uplnm);
-      }
+      if (vmstrnenv(lnm,eqv,idx,
+                    sys ? fildev : NULL,
+#ifdef SECURE_INTERNAL_GETENV
+                    sys ? PERL__TRNENV_SECURE : 0
+#else
+                                                0
+#endif
+                                                 )) return eqv;
+      else return Nullch;
     }
-    return Nullch;
 
 }  /* end of my_getenv() */
 /*}}}*/
 
+
+/*{{{ SV *my_getenv_sv(const char *lnm, bool sys)*/
+SV *
+my_getenv_sv(const char *lnm, bool sys)
+{
+    char buf[LNM$C_NAMLENGTH+1], *cp1, *cp2;
+    unsigned long int len, idx = 0;
+
+    for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
+    if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
+      getcwd(buf,LNM$C_NAMLENGTH);
+      return newSVpv(buf,0);
+    }
+    else {
+      if ((cp2 = strchr(lnm,';')) != NULL) {
+        strcpy(buf,lnm);
+        buf[cp2-lnm] = '\0';
+        idx = strtoul(cp2+1,NULL,0);
+        lnm = buf;
+      }
+      if ((len = vmstrnenv(lnm,buf,idx,
+                           sys ? fildev : NULL,
+#ifdef SECURE_INTERNAL_GETENV
+                           sys ? PERL__TRNENV_SECURE : 0
+#else
+                                                       0
+#endif
+                                                         ))) return newSVpv(buf,len);
+      else return &PL_sv_undef;
+    }
+
+}  /* end of my_getenv_sv() */
+/*}}}*/
+
 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
 
 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
@@ -189,140 +302,319 @@ prime_env_iter(void)
 {
   dTHR;
   static int primed = 0;
-  HV *envhv = GvHVn(PL_envgv);
-  PerlIO *sholog;
-  char eqv[LNM$C_NAMLENGTH+1],mbxnam[LNM$C_NAMLENGTH+1],*start,*end;
+  HV *seenhv = NULL, *envhv;
+  char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
   unsigned short int chan;
 #ifndef CLI$M_TRUSTED
 #  define CLI$M_TRUSTED 0x40  /* Missing from VAXC headers */
 #endif
-  unsigned long int flags = CLI$M_NOWAIT | CLI$M_NOCLISYM | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
-  unsigned long int i, retsts, substs = 0, wakect = 0;
-  STRLEN eqvlen;
-  SV *oldrs, *linesv, *eqvsv;
-  $DESCRIPTOR(cmddsc,"Show Logical *"); $DESCRIPTOR(nldsc,"_NLA0:");
-  $DESCRIPTOR(clidsc,"DCL");            $DESCRIPTOR(tabdsc,"DCLTABLES");
-  $DESCRIPTOR(mbxdsc,mbxnam); 
+  unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
+  unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
+  long int i;
+  bool have_sym = FALSE, have_lnm = FALSE;
+  struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
+  $DESCRIPTOR(cmddsc,cmd);    $DESCRIPTOR(nldsc,"_NLA0:");
+  $DESCRIPTOR(clidsc,"DCL");  $DESCRIPTOR(clitabdsc,"DCLTABLES");
+  $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
+  $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam); 
 #ifdef USE_THREADS
   static perl_mutex primenv_mutex;
   MUTEX_INIT(&primenv_mutex);
 #endif
 
-  if (primed) return;
+  if (primed || !PL_envgv) return;
   MUTEX_LOCK(&primenv_mutex);
   if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
+  envhv = GvHVn(PL_envgv);
   /* Perform a dummy fetch as an lval to insure that the hash table is
    * set up.  Otherwise, the hv_store() will turn into a nullop. */
   (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
-  /* Also, set up any "special" keys that the CRTL defines,
-   * either by itself or becasue we were called from a C program
-   * using exec[lv]e() */
-  for (i = 0; environ[i]; i++) { 
-    if (!(start = strchr(environ[i],'='))) {
-      warn("Ill-formed CRTL environ value \"%s\"\n",environ[i]);
-    }
-    else {
-      start++;
-      (void) hv_store(envhv,environ[i],start - environ[i] - 1,newSVpv(start,0),0);
-    }
-  }
 
-  /* Now, go get the logical names */
-  create_mbx(&chan,&mbxdsc);
-  if ((sholog = PerlIO_open(mbxnam,"r")) != Nullfp) {
-    if ((retsts = sys$dassgn(chan)) & 1) {
-      /* Be certain that subprocess is using the CLI and command tables we
-       * expect, and don't pass symbols through so that we insure that
-       * "Show Logical" can't be subverted.
-       */
-      do {
-        retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,0,&substs,
-                           0,&riseandshine,0,0,&clidsc,&tabdsc);
-        flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
-      } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
-    }
+  for (i = 0; env_tables[i]; i++) {
+     if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
+         !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
+     if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
   }
-  if (sholog == Nullfp || !(retsts & 1)) {
-    if (sholog != Nullfp) PerlIO_close(sholog);
-    MUTEX_UNLOCK(&primenv_mutex);
-    _ckvmssts(sholog == Nullfp ? vaxc$errno : retsts);
+  if (have_sym || have_lnm) {
+    long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
+    _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
+    _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
+    _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
   }
-  /* We use Perl's sv_gets to read from the pipe, since PerlIO_open is
-   * tied to Perl's I/O layer, so it may not return a simple FILE * */
-  oldrs = PL_rs;
-  PL_rs = newSVpv("\n",1);
-  linesv = newSVpv("",0);
-  while (1) {
-    if ((start = sv_gets(linesv,sholog,0)) == Nullch) {
-      PerlIO_close(sholog);
-      SvREFCNT_dec(linesv); SvREFCNT_dec(PL_rs); PL_rs = oldrs;
-      primed = 1;
-      /* Wait for subprocess to clean up (we know subproc won't return 0) */
-      while (substs == 0) { sys$hiber(); wakect++;}
-      if (wakect > 1) sys$wake(0,0);  /* Stole someone else's wake */
-      _ckvmssts(substs);
-      MUTEX_UNLOCK(&primenv_mutex);
-      return;
+
+  for (i--; i >= 0; i--) {
+    if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
+      char *start;
+      int j;
+      for (j = 0; environ[j]; j++) { 
+        if (!(start = strchr(environ[j],'='))) {
+          if (ckWARN(WARN_INTERNAL)) 
+            warner(WARN_INTERNAL,"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
+        }
+        else {
+          start++;
+          (void) hv_store(envhv,environ[j],start - environ[j] - 1,
+                          newSVpv(start,0),0);
+        }
+      }
+      continue;
+    }
+    else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
+             !str$case_blind_compare(&tmpdsc,&clisym)) {
+      strcpy(cmd,"Show Symbol/Global *");
+      cmddsc.dsc$w_length = 20;
+      if (env_tables[i]->dsc$w_length == 12 &&
+          (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
+          !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local  *");
+      flags = defflags | CLI$M_NOLOGNAM;
     }
-    while (*start != '"' && *start != '=' && *start) start++;
-    if (*start != '"') continue;
-    for (end = ++start; *end && *end != '"'; end++) ;
-    if (*end) *end = '\0';
-    else end = Nullch;
-    if ((eqvlen = my_trnlnm(start,eqv,0)) == 0) {
-      if (vaxc$errno == SS$_NOLOGNAM || vaxc$errno == SS$_IVLOGNAM) {
-        if (PL_dowarn)
-          warn("Ill-formed logical name |%s| in prime_env_iter",start);
+    else {
+      strcpy(cmd,"Show Logical *");
+      if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
+        strcat(cmd," /Table=");
+        strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
+        cmddsc.dsc$w_length = strlen(cmd);
+      }
+      else cmddsc.dsc$w_length = 14;  /* N.B. We test this below */
+      flags = defflags | CLI$M_NOCLISYM;
+    }
+    
+    /* Create a new subprocess to execute each command, to exclude the
+     * remote possibility that someone could subvert a mbx or file used
+     * to write multiple commands to a single subprocess.
+     */
+    do {
+      retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
+                         0,&riseandshine,0,0,&clidsc,&clitabdsc);
+      flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
+      defflags &= ~CLI$M_TRUSTED;
+    } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
+    _ckvmssts(retsts);
+    if (!buf) New(1322,buf,mbxbufsiz + 1,char);
+    if (seenhv) SvREFCNT_dec(seenhv);
+    seenhv = newHV();
+    while (1) {
+      char *cp1, *cp2, *key;
+      unsigned long int sts, iosb[2], retlen, keylen;
+      register U32 hash;
+
+      sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
+      if (sts & 1) sts = iosb[0] & 0xffff;
+      if (sts == SS$_ENDOFFILE) {
+        int wakect = 0;
+        while (substs == 0) { sys$hiber(); wakect++;}
+        if (wakect > 1) sys$wake(0,0);  /* Stole someone else's wake */
+        _ckvmssts(substs);
+        break;
+      }
+      _ckvmssts(sts);
+      retlen = iosb[0] >> 16;      
+      if (!retlen) continue;  /* blank line */
+      buf[retlen] = '\0';
+      if (iosb[1] != subpid) {
+        if (iosb[1]) {
+          croak("Unknown process %x sent message to prime_env_iter: %s",buf);
+        }
+        continue;
+      }
+      if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
+        warner(WARN_INTERNAL,"Buffer overflow in prime_env_iter: %s",buf);
+
+      for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
+      if (*cp1 == '(' || /* Logical name table name */
+          *cp1 == '='    /* Next eqv of searchlist  */) continue;
+      if (*cp1 == '"') cp1++;
+      for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
+      key = cp1;  keylen = cp2 - cp1;
+      if (keylen && hv_exists(seenhv,key,keylen)) continue;
+      while (*cp2 && *cp2 != '=') cp2++;
+      while (*cp2 && *cp2 != '"') cp2++;
+      for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
+      if ((!keylen || (cp1 - cp2 <= 0)) && ckWARN(WARN_INTERNAL)) {
+        warner(WARN_INTERNAL,"Ill-formed message in prime_env_iter: |%s|",buf);
         continue;
       }
-      else { MUTEX_UNLOCK(&primenv_mutex); _ckvmssts(vaxc$errno); }
+      /* Skip "" surrounding translation */
+      PERL_HASH(hash,key,keylen);
+      hv_store(envhv,key,keylen,newSVpv(cp2+1,cp1 - cp2 - 1),hash);
+      hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
     }
-    else {
-      eqvsv = newSVpv(eqv,eqvlen);
-      hv_store(envhv,start,(end ? end - start : strlen(start)),eqvsv,0);
+    if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
+      /* get the PPFs for this process, not the subprocess */
+      char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
+      char eqv[LNM$C_NAMLENGTH+1];
+      int trnlen, i;
+      for (i = 0; ppfs[i]; i++) {
+        trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
+        hv_store(envhv,ppfs[i],strlen(ppfs[i]),newSVpv(eqv,trnlen),0);
+      }
     }
   }
+  primed = 1;
+  if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
+  if (buf) Safefree(buf);
+  if (seenhv) SvREFCNT_dec(seenhv);
+  MUTEX_UNLOCK(&primenv_mutex);
+  return;
+
 }  /* end of prime_env_iter */
 /*}}}*/
-  
 
-/*{{{ void  my_setenv(char *lnm, char *eqv)*/
-void
-my_setenv(char *lnm,char *eqv)
-/* Define a supervisor-mode logical name in the process table.
- * In the future we'll add tables, attribs, and acmodes,
- * probably through a different call.
+
+/*{{{ int  vmssetenv(char *lnm, char *eqv)*/
+/* Define or delete an element in the same "environment" as
+ * vmstrnenv().  If an element is to be deleted, it's removed from
+ * the first place it's found.  If it's to be set, it's set in the
+ * place designated by the first element of the table vector.
+ * Like setenv() returns 0 for success, non-zero on error.
  */
+int
+vmssetenv(char *lnm, char *eqv, struct dsc$descriptor_s **tabvec)
 {
     char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
+    unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
     unsigned long int retsts, usermode = PSL$C_USER;
-    $DESCRIPTOR(tabdsc,"LNM$PROCESS");
     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
-                            eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
-
-    for(cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) *cp2 = _toupper(*cp1);
+                            eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
+                            tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
+    $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
+    $DESCRIPTOR(local,"_LOCAL");
+
+    for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
+      *cp2 = _toupper(*cp1);
+      if (cp1 - lnm > LNM$C_NAMLENGTH) {
+        set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
+        return SS$_IVLOGNAM;
+      }
+    }
     lnmdsc.dsc$w_length = cp1 - lnm;
-
-    if (!eqv || !*eqv) {  /* we're deleting a logical name */
-      retsts = sys$dellnm(&tabdsc,&lnmdsc,&usermode); /* try user mode first */
-      if (retsts == SS$_IVLOGNAM) return;
-      if (retsts != SS$_NOLOGNAM) _ckvmssts(retsts);
-      if (!(retsts & 1)) {
-        retsts = lib$delete_logical(&lnmdsc,&tabdsc); /* then supervisor mode */
-        if (retsts != SS$_NOLOGNAM) _ckvmssts(retsts);
+    if (!tabvec || !*tabvec) tabvec = env_tables;
+
+    if (!eqv) {  /* we're deleting n element */
+      for (curtab = 0; tabvec[curtab]; curtab++) {
+        if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
+        int i;
+          for (i = 0; environ[i]; i++) { /* Iff it's an environ elt, reset */
+            if ((cp1 = strchr(environ[i],'=')) && 
+                !strncmp(environ[i],lnm,cp1 - environ[i])) {
+#ifdef HAS_SETENV
+              return setenv(lnm,eqv,1) ? vaxc$errno : 0;
+            }
+          }
+          ivenv = 1; retsts = SS$_NOLOGNAM;
+#else
+              if (ckWARN(WARN_INTERNAL))
+                warner(WARN_INTERNAL,"This Perl can't reset CRTL environ elements (%s)",lnm);
+              ivenv = 1; retsts = SS$_NOSUCHPGM;
+              break;
+            }
+          }
+#endif
+        }
+        else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
+                 !str$case_blind_compare(&tmpdsc,&clisym)) {
+          unsigned int symtype;
+          if (tabvec[curtab]->dsc$w_length == 12 &&
+              (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
+              !str$case_blind_compare(&tmpdsc,&local)) 
+            symtype = LIB$K_CLI_LOCAL_SYM;
+          else symtype = LIB$K_CLI_GLOBAL_SYM;
+          retsts = lib$delete_symbol(&lnmdsc,&symtype);
+          if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
+          if (retsts == LIB$_NOSUCHSYM) continue;
+          break;
+        }
+        else if (!ivlnm) {
+          retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
+          if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
+          if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
+          retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
+          if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
+        }
       }
     }
+    else {  /* we're defining a value */
+      if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
+#ifdef HAS_SETENV
+        return setenv(lnm,eqv,1) ? vaxc$errno : 0;
+#else
+        if (ckWARN(WARN_INTERNAL))
+          warner(WARN_INTERNAL,"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
+        retsts = SS$_NOSUCHPGM;
+#endif
+      }
+      else {
+        eqvdsc.dsc$a_pointer = eqv;
+        eqvdsc.dsc$w_length  = strlen(eqv);
+        if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
+            !str$case_blind_compare(&tmpdsc,&clisym)) {
+          unsigned int symtype;
+          if (tabvec[0]->dsc$w_length == 12 &&
+              (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
+               !str$case_blind_compare(&tmpdsc,&local)) 
+            symtype = LIB$K_CLI_LOCAL_SYM;
+          else symtype = LIB$K_CLI_GLOBAL_SYM;
+          retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
+        }
+        else {
+          if (!*eqv) eqvdsc.dsc$w_length = 1;
+          retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
+        }
+      }
+    }
+    if (!(retsts & 1)) {
+      switch (retsts) {
+        case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
+        case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
+          set_errno(EVMSERR); break;
+        case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM: 
+        case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
+          set_errno(EINVAL); break;
+        case SS$_NOPRIV:
+          set_errno(EACCES);
+        default:
+          _ckvmssts(retsts);
+          set_errno(EVMSERR);
+       }
+       set_vaxc_errno(retsts);
+       return (int) retsts || 44; /* retsts should never be 0, but just in case */
+    }
     else {
-      eqvdsc.dsc$w_length = strlen(eqv);
-      eqvdsc.dsc$a_pointer = eqv;
-
-      _ckvmssts(lib$set_logical(&lnmdsc,&eqvdsc,&tabdsc,0,0));
+      /* We reset error values on success because Perl does an hv_fetch()
+       * before each hv_store(), and if the thing we're setting didn't
+       * previously exist, we've got a leftover error message.  (Of course,
+       * this fails in the face of
+       *    $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
+       * in that the error reported in $! isn't spurious, 
+       * but it's right more often than not.)
+       */
+      set_errno(0); set_vaxc_errno(retsts);
+      return 0;
     }
 
-}  /* end of my_setenv() */
+}  /* end of vmssetenv() */
+/*}}}*/
+
+/*{{{ void  my_setenv(char *lnm, char *eqv)*/
+/* This has to be a function since there's a prototype for it in proto.h */
+void
+my_setenv(char *lnm,char *eqv)
+{
+  if (lnm && *lnm && strlen(lnm) == 7) {
+    char uplnm[8];
+    int i;
+    for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
+    if (!strcmp(uplnm,"DEFAULT")) {
+      if (eqv && *eqv) chdir(eqv);
+      return;
+    }
+  }
+  (void) vmssetenv(lnm,eqv,NULL);
+}
 /*}}}*/
 
 
+
 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
 /* my_crypt - VMS password hashing
  * my_crypt() provides an interface compatible with the Unix crypt()
@@ -583,19 +875,78 @@ static struct pipe_details *open_pipes = NULL;
 static $DESCRIPTOR(nl_desc, "NL:");
 static int waitpid_asleep = 0;
 
+/* Send an EOF to a mbx.  N.B.  We don't check that fp actually points
+ * to a mbx; that's the caller's responsibility.
+ */
+static unsigned long int
+pipe_eof(FILE *fp)
+{
+  char devnam[NAM$C_MAXRSS+1], *cp;
+  unsigned long int chan, iosb[2], retsts, retsts2;
+  struct dsc$descriptor devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam};
+
+  if (fgetname(fp,devnam,1)) {
+    /* It oughta be a mailbox, so fgetname should give just the device
+     * name, but just in case . . . */
+    if ((cp = strrchr(devnam,':')) != NULL) *(cp+1) = '\0';
+    devdsc.dsc$w_length = strlen(devnam);
+    _ckvmssts(sys$assign(&devdsc,&chan,0,0));
+    retsts = sys$qiow(0,chan,IO$_WRITEOF,iosb,0,0,0,0,0,0,0,0);
+    if (retsts & 1) retsts = iosb[0];
+    retsts2 = sys$dassgn(chan);  /* Be sure to deassign the channel */
+    if (retsts & 1) retsts = retsts2;
+    _ckvmssts(retsts);
+    return retsts;
+  }
+  else _ckvmssts(vaxc$errno);  /* Should never happen */
+  return (unsigned long int) vaxc$errno;
+}
+
 static unsigned long int
 pipe_exit_routine()
 {
+    struct pipe_details *info;
     unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
-    int sts;
-
-    while (open_pipes != NULL) {
-      if (!open_pipes->done) { /* Tap them gently on the shoulder . . .*/
-        _ckvmssts(sys$forcex(&open_pipes->pid,0,&abort));
-        sleep(1);
+    int sts, did_stuff;
+
+    /* 
+     first we try sending an EOF...ignore if doesn't work, make sure we
+     don't hang
+    */
+    did_stuff = 0;
+    info = open_pipes;
+
+    while (info) {
+      if (info->mode != 'r' && !info->done) {
+        if (pipe_eof(info->fp) & 1) did_stuff = 1;
+      }
+      info = info->next;
+    }
+    if (did_stuff) sleep(1);   /* wait for EOF to have an effect */
+
+    did_stuff = 0;
+    info = open_pipes;
+    while (info) {
+      if (!info->done) { /* Tap them gently on the shoulder . . .*/
+        sts = sys$forcex(&info->pid,0,&abort);
+        if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts); 
+        did_stuff = 1;
       }
-      if (!open_pipes->done)  /* We tried to be nice . . . */
-        _ckvmssts(sys$delprc(&open_pipes->pid,0));
+      info = info->next;
+    }
+    if (did_stuff) sleep(1);    /* wait for them to respond */
+
+    info = open_pipes;
+    while (info) {
+      if (!info->done) {  /* We tried to be nice . . . */
+        sts = sys$delprc(&info->pid,0);
+        if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts); 
+        info->done = 1; /* so my_pclose doesn't try to write EOF */
+      }
+      info = info->next;
+    }
+
+    while(open_pipes) {
       if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
       else if (!(sts & 1)) retsts = sts;
     }
@@ -685,6 +1036,7 @@ my_popen(char *cmd, char *mode)
 {
     TAINT_ENV();
     TAINT_PROPER("popen");
+    PERL_FLUSHALL_FOR_CHILD;
     return safe_popen(cmd,mode);
 }
 
@@ -708,25 +1060,7 @@ I32 my_pclose(FILE *fp)
     /* If we were writing to a subprocess, insure that someone reading from
      * the mailbox gets an EOF.  It looks like a simple fclose() doesn't
      * produce an EOF record in the mailbox.  */
-    if (info->mode != 'r') {
-      char devnam[NAM$C_MAXRSS+1], *cp;
-      unsigned long int chan, iosb[2], retsts, retsts2;
-      struct dsc$descriptor devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam};
-
-      if (fgetname(info->fp,devnam,1)) {
-        /* It oughta be a mailbox, so fgetname should give just the device
-         * name, but just in case . . . */
-        if ((cp = strrchr(devnam,':')) != NULL) *(cp+1) = '\0';
-        devdsc.dsc$w_length = strlen(devnam);
-        _ckvmssts(sys$assign(&devdsc,&chan,0,0));
-        retsts = sys$qiow(0,chan,IO$_WRITEOF,iosb,0,0,0,0,0,0,0,0);
-        if (retsts & 1) retsts = iosb[0];
-        retsts2 = sys$dassgn(chan);  /* Be sure to deassign the channel */
-        if (retsts & 1) retsts = retsts2;
-        _ckvmssts(retsts);
-      }
-      else _ckvmssts(vaxc$errno);  /* Should never happen */
-    }
+    if (info->mode != 'r' && !info->done) pipe_eof(info->fp);
     PerlIO_close(info->fp);
 
     if (info->done) retsts = info->completion;
@@ -765,11 +1099,11 @@ my_waitpid(Pid_t pid, int *statusp, int flags)
       unsigned long int ownercode = JPI$_OWNER, ownerpid, mypid;
       unsigned long int interval[2],sts;
 
-      if (PL_dowarn) {
+      if (ckWARN(WARN_EXEC)) {
         _ckvmssts(lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0));
         _ckvmssts(lib$getjpi(&ownercode,0,0,&mypid,0,0));
         if (ownerpid != mypid)
-          warn("pid %x not a child",pid);
+          warner(WARN_EXEC,"pid %x not a child",pid);
       }
 
       _ckvmssts(sys$bintim(&intdsc,interval));
@@ -845,7 +1179,7 @@ do_rmsexpand(char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
   struct FAB myfab = cc$rms_fab;
   struct NAM mynam = cc$rms_nam;
   STRLEN speclen;
-  unsigned long int retsts, haslower = 0, isunix = 0;
+  unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
 
   if (!filespec || !*filespec) {
     set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
@@ -914,13 +1248,37 @@ do_rmsexpand(char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
     if (islower(*out)) { haslower = 1; break; }
   if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
   else                 { out = esa;    speclen = mynam.nam$b_esl; }
-  if (!(mynam.nam$l_fnb & NAM$M_EXP_VER) &&
-      (!defspec || !*defspec || !strchr(myfab.fab$l_dna,';')))
-    speclen = mynam.nam$l_ver - out;
-  if (!(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
-      (!defspec || !*defspec || defspec[myfab.fab$b_dns-1] != '.' ||
-       defspec[myfab.fab$b_dns-2] == '.'))
-    speclen = mynam.nam$l_type - out;
+  /* Trim off null fields added by $PARSE
+   * If type > 1 char, must have been specified in original or default spec
+   * (not true for version; $SEARCH may have added version of existing file).
+   */
+  trimver  = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
+  trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
+             (mynam.nam$l_ver - mynam.nam$l_type == 1);
+  if (trimver || trimtype) {
+    if (defspec && *defspec) {
+      char defesa[NAM$C_MAXRSS];
+      struct FAB deffab = cc$rms_fab;
+      struct NAM defnam = cc$rms_nam;
+     
+      deffab.fab$l_nam = &defnam;
+      deffab.fab$l_fna = defspec;  deffab.fab$b_fns = myfab.fab$b_dns;
+      defnam.nam$l_esa = defesa;   defnam.nam$b_ess = sizeof defesa;
+      defnam.nam$b_nop = NAM$M_SYNCHK;
+      if (sys$parse(&deffab,0,0) & 1) {
+        if (trimver)  trimver  = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
+        if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE); 
+      }
+    }
+    if (trimver) speclen = mynam.nam$l_ver - out;
+    if (trimtype) {
+      /* If we didn't already trim version, copy down */
+      if (speclen > mynam.nam$l_ver - out)
+        memcpy(mynam.nam$l_type, mynam.nam$l_ver, 
+               speclen - (mynam.nam$l_ver - out));
+      speclen -= mynam.nam$l_ver - mynam.nam$l_type; 
+    }
+  }
   /* If we just had a directory spec on input, $PARSE "helpfully"
    * adds an empty name and type for us */
   if (mynam.nam$l_name == mynam.nam$l_type &&
@@ -1530,7 +1888,7 @@ static char *do_tounixspec(char *spec, char *buf, int ts)
         while (*cp3 != ':' && *cp3) cp3++;
         *(cp3++) = '\0';
         if (strchr(cp3,']') != NULL) break;
-      } while (((cp3 = my_getenv(tmp)) != NULL) && strcpy(tmp,cp3));
+      } while (vmstrnenv(tmp,tmp,0,fildev,0));
       if (ts && !buf &&
           ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
         retlen = devlen + dirlen;
@@ -2113,14 +2471,18 @@ int isunix = 0;
 char *had_version;
 char *had_device;
 int had_directory;
-char *devdir;
+char *devdir,*cp;
 char vmsspec[NAM$C_MAXRSS+1];
 $DESCRIPTOR(filespec, "");
 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
 $DESCRIPTOR(resultspec, "");
 unsigned long int zero = 0, sts;
 
-    if (strcspn(item, "*%") == strlen(item) || strchr(item,' ') != NULL)
+    for (cp = item; *cp; cp++) {
+       if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
+       if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
+    }
+    if (!*cp || isspace(*cp))
        {
        add_item(head, tail, item, count);
        return;
@@ -2331,9 +2693,12 @@ unsigned long int flags = 17, one = 1, retsts;
 void
 vms_image_init(int *argcp, char ***argvp)
 {
-  unsigned long int *mask, iosb[2], i, rlst[128], rsz, add_taint = FALSE;
+  char eqv[LNM$C_NAMLENGTH+1] = "";
+  unsigned int len, tabct = 8, tabidx = 0;
+  unsigned long int *mask, iosb[2], i, rlst[128], rsz;
   unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
   unsigned short int dummy, rlen;
+  struct dsc$descriptor_s **tabvec;
   struct itmlst_3 jpilist[4] = { {sizeof iprv,    JPI$_IMAGPRIV, iprv, &dummy},
                                  {sizeof rlst,  JPI$_RIGHTSLIST, rlst,  &rlen},
                                  { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
@@ -2344,12 +2709,12 @@ vms_image_init(int *argcp, char ***argvp)
   for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
     if (iprv[i]) {           /* Running image installed with privs? */
       _ckvmssts(sys$setprv(0,iprv,0,NULL));       /* Turn 'em off. */
-      add_taint = TRUE;
+      will_taint = TRUE;
       break;
     }
   }
   /* Rights identifiers might trigger tainting as well. */
-  if (!add_taint && (rlen || rsz)) {
+  if (!will_taint && (rlen || rsz)) {
     while (rlen < rsz) {
       /* We didn't get all the identifiers on the first pass.  Allocate a
        * buffer much larger than $GETJPI wants (rsz is size in bytes that
@@ -2368,7 +2733,7 @@ vms_image_init(int *argcp, char ***argvp)
      */
     for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
       if (mask[i] & KGB$M_SUBSYSTEM) {
-        add_taint = TRUE;
+        will_taint = TRUE;
         break;
       }
     }
@@ -2378,7 +2743,7 @@ vms_image_init(int *argcp, char ***argvp)
    * since its tainting flag may be part of the PL_curinterp struct, which
    * hasn't been allocated when vms_image_init() is called.
    */
-  if (add_taint) {
+  if (will_taint) {
     char ***newap;
     New(1320,newap,*argcp+2,char **);
     newap[0] = argvp[0];
@@ -2389,6 +2754,37 @@ vms_image_init(int *argcp, char ***argvp)
      */
     *argcp++; argvp = newap;
   }
+  else {  /* Did user explicitly request tainting? */
+    int i;
+    char *cp, **av = *argvp;
+    for (i = 1; i < *argcp; i++) {
+      if (*av[i] != '-') break;
+      for (cp = av[i]+1; *cp; cp++) {
+        if (*cp == 'T') { will_taint = 1; break; }
+        else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
+                  strchr("DFIiMmx",*cp)) break;
+      }
+      if (will_taint) break;
+    }
+  }
+
+  for (tabidx = 0;
+       len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
+       tabidx++) {
+    if (!tabidx) New(1321,tabvec,tabct,struct dsc$descriptor_s *);
+    else if (tabidx >= tabct) {
+      tabct += 8;
+      Renew(tabvec,tabct,struct dsc$descriptor_s *);
+    }
+    New(1322,tabvec[tabidx],1,struct dsc$descriptor_s);
+    tabvec[tabidx]->dsc$w_length  = 0;
+    tabvec[tabidx]->dsc$b_dtype   = DSC$K_DTYPE_T;
+    tabvec[tabidx]->dsc$b_class   = DSC$K_CLASS_D;
+    tabvec[tabidx]->dsc$a_pointer = NULL;
+    _ckvmssts(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
+  }
+  if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
+
   getredirection(argcp,argvp);
 #if defined(USE_THREADS) && defined(__DECC)
   {
@@ -2727,7 +3123,8 @@ readdir(DIR *dd)
     dd->count++;
     /* Force the buffer to end with a NUL, and downcase name to match C convention. */
     buff[sizeof buff - 1] = '\0';
-    for (p = buff; !isspace(*p); p++) *p = _tolower(*p);
+    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. */
@@ -2804,12 +3201,12 @@ seekdir(DIR *dd, long count)
  * in 'VMSish fashion' (i.e. not after a call to vfork) The args
  * are concatenated to form a DCL command string.  If the first arg
  * begins with '$' (i.e. the perl script had "\$ Type" or some such),
- * the the command string is hrnded off to DCL directly.  Otherwise,
+ * the the command string is handed off to DCL directly.  Otherwise,
  * the first token of the command is taken as the filespec of an image
  * to run.  The filespec is expanded using a default type of '.EXE' and
- * the process defaults for device, directory, etc., and the resultant
+ * the process defaults for device, directory, etc., and if found, the resultant
  * filespec is invoked using the DCL verb 'MCR', and passed the rest of
- * the command string as parameters.  This is perhaps a bit compicated,
+ * the command string as parameters.  This is perhaps a bit complicated,
  * but I hope it will form a happy medium between what VMS folks expect
  * from lib$spawn and what Unix folks expect from exec.
  */
@@ -2875,8 +3272,10 @@ setup_argstr(SV *really, SV **mark, SV **sp)
   else *PL_Cmd = '\0';
   while (++mark <= sp) {
     if (*mark) {
-      strcat(PL_Cmd," ");
-      strcat(PL_Cmd,SvPVx(*mark,n_a));
+      char *s = SvPVx(*mark,n_a);
+      if (!*s) continue;
+      if (*PL_Cmd) strcat(PL_Cmd," ");
+      strcat(PL_Cmd,s);
     }
   }
   return PL_Cmd;
@@ -2891,7 +3290,7 @@ setup_cmddsc(char *cmd, int check_img)
   $DESCRIPTOR(defdsc,".EXE");
   $DESCRIPTOR(resdsc,resspec);
   struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
-  unsigned long int cxt = 0, flags = 1, retsts;
+  unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
   register char *s, *rest, *cp;
   register int isdcl = 0;
 
@@ -2909,43 +3308,45 @@ setup_cmddsc(char *cmd, int check_img)
     }
   }
   else isdcl = 1;
-  if (isdcl) {  /* It's a DCL command, just do it. */
-    VMScmd.dsc$w_length = strlen(cmd);
-    if (cmd == PL_Cmd) {
-       VMScmd.dsc$a_pointer = PL_Cmd;
-       PL_Cmd = Nullch;  /* Don't try to free twice in vms_execfree() */
-    }
-    else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
-  }
-  else {                           /* assume first token is an image spec */
+  if (!isdcl) {
     cmd = s;
     while (*s && !isspace(*s)) s++;
     rest = *s ? s : 0;
     imgdsc.dsc$a_pointer = cmd;
     imgdsc.dsc$w_length = s - cmd;
     retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
-    if (!(retsts & 1)) {
-      /* just hand off status values likely to be due to user error */
-      if (retsts == RMS$_FNF || retsts == RMS$_DNF ||
-          retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
-         (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
-      else { _ckvmssts(retsts); }
-    }
-    else {
+    if (retsts & 1) {
       _ckvmssts(lib$find_file_end(&cxt));
       s = resspec;
       while (*s && !isspace(*s)) s++;
       *s = '\0';
-      if (!cando_by_name(S_IXUSR,0,resspec)) return RMS$_PRV;
-      New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
-      strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
-      strcat(VMScmd.dsc$a_pointer,resspec);
-      if (rest) strcat(VMScmd.dsc$a_pointer,rest);
-      VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
+      if (cando_by_name(S_IXUSR,0,resspec)) {
+        New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
+        strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
+        strcat(VMScmd.dsc$a_pointer,resspec);
+        if (rest) strcat(VMScmd.dsc$a_pointer,rest);
+        VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
+        return retsts;
+      }
+      else retsts = RMS$_PRV;
     }
   }
+  /* It's either a DCL command or we couldn't find a suitable image */
+  VMScmd.dsc$w_length = strlen(cmd);
+  if (cmd == PL_Cmd) {
+    VMScmd.dsc$a_pointer = PL_Cmd;
+    PL_Cmd = Nullch;  /* Don't try to free twice in vms_execfree() */
+  }
+  else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
+  if (!(retsts & 1)) {
+    /* just hand off status values likely to be due to user error */
+    if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
+        retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
+       (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
+    else { _ckvmssts(retsts); }
+  }
 
-  return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : SS$_NORMAL);
+  return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : retsts);
 
 }  /* end of setup_cmddsc() */
 
@@ -3012,8 +3413,10 @@ vms_do_exec(char *cmd)
         set_errno(EVMSERR); 
     }
     set_vaxc_errno(retsts);
-    if (PL_dowarn)
-      warn("Can't exec \"%s\": %s", VMScmd.dsc$a_pointer, Strerror(errno));
+    if (ckWARN(WARN_EXEC)) {
+      warner(WARN_EXEC,"Can't exec \"%*s\": %s",
+             VMScmd.dsc$w_length, VMScmd.dsc$a_pointer, Strerror(errno));
+    }
     vms_execfree();
   }
 
@@ -3069,9 +3472,12 @@ do_spawn(char *cmd)
         set_errno(EVMSERR); 
     }
     set_vaxc_errno(sts);
-    if (PL_dowarn)
-      warn("Can't spawn \"%s\": %s",
-           hadcmd ? VMScmd.dsc$a_pointer : "", Strerror(errno));
+    if (ckWARN(WARN_EXEC)) {
+      warner(WARN_EXEC,"Can't spawn \"%*s\": %s",
+             hadcmd ? VMScmd.dsc$w_length :  0,
+             hadcmd ? VMScmd.dsc$a_pointer : "",
+             Strerror(errno));
+    }
   }
   vms_execfree();
   return substs;
@@ -3547,10 +3953,10 @@ time_t my_time(time_t *timep)
 
     gmtime_emulation_type++;
     if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
-      char *off;
+      char off[LNM$C_NAMLENGTH+1];;
 
       gmtime_emulation_type++;
-      if ((off = my_getenv("SYS$TIMEZONE_DIFFERENTIAL")) == NULL) {
+      if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
         gmtime_emulation_type++;
         warn("no UTC offset information; assuming local time is UTC");
       }
@@ -4153,49 +4559,6 @@ flex_stat(char *fspec, Stat_t *statbufp)
 }  /* end of flex_stat() */
 /*}}}*/
 
-/* Insures that no carriage-control translation will be done on a file. */
-/*{{{FILE *my_binmode(FILE *fp, char iotype)*/
-FILE *
-my_binmode(FILE *fp, char iotype)
-{
-    char filespec[NAM$C_MAXRSS], *acmode, *s, *colon, *dirend = Nullch;
-    int ret = 0, saverrno = errno, savevmserrno = vaxc$errno;
-    fpos_t pos;
-
-    if (!fgetname(fp,filespec,1)) return NULL;
-    for (s = filespec; *s; s++) {
-      if (*s == ':') colon = s;
-      else if (*s == ']' || *s == '>') dirend = s;
-    }
-    /* Looks like a tmpfile, which will go away if reopened */
-    if (s == dirend + 3) return fp;
-    /* If we've got a non-file-structured device, clip off the trailing
-     * junk, and don't lose sleep if we can't get a stream position.  */
-    if (dirend == Nullch) *(colon+1) = '\0'; 
-    if (iotype != '-'&& (ret = fgetpos(fp, &pos)) == -1 && dirend) return NULL;
-    switch (iotype) {
-      case '<': case 'r':           acmode = "rb";                      break;
-      case '>': case 'w': case '|':
-        /* use 'a' instead of 'w' to avoid creating new file;
-           fsetpos below will take care of restoring file position */
-      case 'a':                     acmode = "ab";                      break;
-      case '+':  case 's':          acmode = "rb+";                     break;
-      case '-':                     acmode = fileno(fp) ? "ab" : "rb";  break;
-      /* iotype'll be null for the SYS$INPUT:/SYS$OUTPUT:/SYS$ERROR: files */
-      /* since we didn't really open them and can't really */
-      /* reopen them */
-      case 0:                       return NULL;                        break;
-      default:
-        warn("Unrecognized iotype %x for %s in my_binmode",iotype, filespec);
-        acmode = "rb+";
-    }
-    if (freopen(filespec,acmode,fp) == NULL) return NULL;
-    if (iotype != '-' && ret != -1 && fsetpos(fp,&pos) == -1) return NULL;
-    if (ret == -1) { set_errno(saverrno); set_vaxc_errno(savevmserrno); }
-    return fp;
-}  /* end of my_binmode() */
-/*}}}*/
-
 
 /*{{{char *my_getlogin()*/
 /* VMS cuserid == Unix getlogin, except calling sequence */
@@ -4608,10 +4971,6 @@ init_os_extras()
   newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
   newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
 
-#ifdef PRIME_ENV_AT_STARTUP
-  prime_env_iter();
-#endif
-
   return;
 }