This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
perl 3.0 patch #41 (combined patch)
[perl5.git] / str.c
diff --git a/str.c b/str.c
index 3175e91..e392cee 100644 (file)
--- a/str.c
+++ b/str.c
-/* $Header: str.c,v 2.0 88/06/05 00:11:07 root Exp $
+/* $Header: str.c,v 3.0.1.11 90/11/13 15:27:14 lwall Locked $
+ *
+ *    Copyright (c) 1989, Larry Wall
+ *
+ *    You may distribute under the terms of the GNU General Public License
+ *    as specified in the README file that comes with the perl 3.0 kit.
  *
  * $Log:       str.c,v $
- * Revision 2.0  88/06/05  00:11:07  root
- * Baseline version 2.0.
+ * Revision 3.0.1.11  90/11/13  15:27:14  lwall
+ * patch41: fixed a couple of malloc/free problems
+ * 
+ * Revision 3.0.1.10  90/11/10  02:06:29  lwall
+ * patch38: temp string values are now copied less often
+ * patch38: array slurps are now faster and take less memory
+ * patch38: fixed a memory leakage on local(*foo)
+ * 
+ * Revision 3.0.1.9  90/10/16  10:41:21  lwall
+ * patch29: the undefined value could get defined by devious means
+ * patch29: undefined values compared inconsistently 
+ * patch29: taintperl now checks for world writable PATH components
+ * 
+ * Revision 3.0.1.8  90/08/09  05:22:18  lwall
+ * patch19: the number to string converter wasn't allocating enough space
+ * patch19: tainting didn't work on setgid scripts
+ * 
+ * Revision 3.0.1.7  90/03/27  16:24:11  lwall
+ * patch16: strings with prefix chopped off sometimes freed wrong
+ * patch16: taint check blows up on undefined array element
+ * 
+ * Revision 3.0.1.6  90/03/12  17:02:14  lwall
+ * patch13: substr as lvalue didn't invalidate old numeric value
+ * 
+ * Revision 3.0.1.5  90/02/28  18:30:38  lwall
+ * patch9: you may now undef $/ to have no input record separator
+ * patch9: nested evals clobbered their longjmp environment
+ * patch9: sometimes perl thought ordinary data was a symbol table entry
+ * patch9: insufficient space allocated for numeric string on sun4
+ * patch9: underscore in an array name in a double-quoted string not recognized
+ * patch9: "@foo{}" not recognized unless %foo defined
+ * patch9: "$foo[$[]" gives error
+ * 
+ * Revision 3.0.1.4  89/12/21  20:21:35  lwall
+ * patch7: errno may now be a macro with an lvalue
+ * patch7: made nested or recursive foreach work right
+ * 
+ * Revision 3.0.1.3  89/11/17  15:38:23  lwall
+ * patch5: some machines typedef unchar too
+ * patch5: substitution on leading components occasionally caused <> corruption
+ * 
+ * Revision 3.0.1.2  89/11/11  04:56:22  lwall
+ * patch2: uchar gives Crays fits
+ * 
+ * Revision 3.0.1.1  89/10/26  23:23:41  lwall
+ * patch1: string ordering tests were wrong
+ * patch1: $/ now works even when STDSTDIO undefined
+ * 
+ * Revision 3.0  89/10/18  15:23:38  lwall
+ * 3.0 baseline
  * 
  */
 
 #include "EXTERN.h"
 #include "perl.h"
+#include "perly.h"
 
-str_reset(s)
-register char *s;
+extern char **environ;
+
+#ifndef str_get
+char *
+str_get(str)
+STR *str;
 {
-    register STAB *stab;
-    register STR *str;
-    register int i;
-    register int max;
-    register SPAT *spat;
+#ifdef TAINT
+    tainted |= str->str_tainted;
+#endif
+    return str->str_pok ? str->str_ptr : str_2ptr(str);
+}
+#endif
 
-    if (!*s) {         /* reset ?? searches */
-       for (spat = spat_root; spat != Nullspat; spat = spat->spat_next) {
-           spat->spat_flags &= ~SPAT_USED;
+/* dlb ... guess we have a "crippled cc".
+ * dlb the following functions are usually macros.
+ */
+#ifndef str_true
+str_true(Str)
+STR *Str;
+{
+       if (Str->str_pok) {
+           if (*Str->str_ptr > '0' ||
+             Str->str_cur > 1 ||
+             (Str->str_cur && *Str->str_ptr != '0'))
+               return 1;
+           return 0;
        }
-       return;
-    }
+       if (Str->str_nok)
+               return (Str->str_u.str_nval != 0.0);
+       return 0;
+}
+#endif /* str_true */
 
-    /* reset variables */
+#ifndef str_gnum
+double str_gnum(Str)
+STR *Str;
+{
+#ifdef TAINT
+       tainted |= Str->str_tainted;
+#endif /* TAINT*/
+       if (Str->str_nok)
+               return Str->str_u.str_nval;
+       return str_2num(Str);
+}
+#endif /* str_gnum */
+/* dlb ... end of crutch */
 
-    while (*s) {
-       i = *s;
-       if (s[1] == '-') {
-           s += 2;
-       }
-       max = *s++;
-       for ( ; i <= max; i++) {
-           for (stab = stab_index[i]; stab; stab = stab->stab_next) {
-               str = stab->stab_val;
-               str->str_cur = 0;
-               str->str_nok = 0;
-               if (str->str_ptr != Nullch)
-                   str->str_ptr[0] = '\0';
-               if (stab->stab_array) {
-                   aclear(stab->stab_array);
-               }
-               if (stab->stab_hash) {
-                   hclear(stab->stab_hash);
-               }
-           }
-       }
+char *
+str_grow(str,newlen)
+register STR *str;
+#ifndef MSDOS
+register int newlen;
+#else
+unsigned long newlen;
+#endif
+{
+    register char *s = str->str_ptr;
+
+#ifdef MSDOS
+    if (newlen >= 0x10000) {
+       fprintf(stderr, "Allocation too large: %lx\n", newlen);
+       exit(1);
+    }
+#endif /* MSDOS */
+    if (str->str_state == SS_INCR) {           /* data before str_ptr? */
+       str->str_len += str->str_u.str_useful;
+       str->str_ptr -= str->str_u.str_useful;
+       str->str_u.str_useful = 0L;
+       bcopy(s, str->str_ptr, str->str_cur+1);
+       s = str->str_ptr;
+       str->str_state = SS_NORM;                       /* normal again */
+       if (newlen > str->str_len)
+           newlen += 10 * (newlen - str->str_cur); /* avoid copy each time */
     }
+    if (newlen > str->str_len) {               /* need more room? */
+        if (str->str_len)
+           Renew(s,newlen,char);
+        else
+           New(703,s,newlen,char);
+       str->str_ptr = s;
+        str->str_len = newlen;
+    }
+    return s;
 }
 
 str_numset(str,num)
 register STR *str;
 double num;
 {
-    str->str_nval = num;
-    str->str_pok = 0;          /* invalidate pointer */
-    str->str_nok = 1;          /* validate number */
+    if (str->str_pok) {
+       str->str_pok = 0;       /* invalidate pointer */
+       if (str->str_state == SS_INCR)
+           Str_Grow(str,0);
+    }
+    str->str_u.str_nval = num;
+    str->str_state = SS_NORM;
+    str->str_nok = 1;                  /* validate number */
+#ifdef TAINT
+    str->str_tainted = tainted;
+#endif
 }
 
-extern int errno;
-
 char *
 str_2ptr(str)
 register STR *str;
@@ -71,25 +175,35 @@ register STR *str;
 
     if (!str)
        return "";
-    GROWSTR(&(str->str_ptr), &(str->str_len), 24);
-    s = str->str_ptr;
     if (str->str_nok) {
+       STR_GROW(str, 30);
+       s = str->str_ptr;
        olderrno = errno;       /* some Xenix systems wipe out errno here */
 #if defined(scs) && defined(ns32000)
-       gcvt(str->str_nval,20,s);
+       gcvt(str->str_u.str_nval,20,s);
 #else
 #ifdef apollo
-       if (str->str_nval == 0.0)
-           strcpy(s,"0");
+       if (str->str_u.str_nval == 0.0)
+           (void)strcpy(s,"0");
        else
 #endif /*apollo*/
-       sprintf(s,"%.20g",str->str_nval);
+       (void)sprintf(s,"%.20g",str->str_u.str_nval);
 #endif /*scs*/
        errno = olderrno;
        while (*s) s++;
+#ifdef hcx
+       if (s[-1] == '.')
+           s--;
+#endif
+    }
+    else {
+       if (str == &str_undef)
+           return No;
+       if (dowarn)
+           warn("Use of uninitialized variable");
+       STR_GROW(str, 30);
+       s = str->str_ptr;
     }
-    else if (dowarn)
-       warn("Use of uninitialized variable");
     *s = '\0';
     str->str_cur = s - str->str_ptr;
     str->str_pok = 1;
@@ -106,100 +220,201 @@ register STR *str;
 {
     if (!str)
        return 0.0;
+    if (str->str_state == SS_INCR)
+       Str_Grow(str,0);       /* just force copy down */
+    str->str_state = SS_NORM;
     if (str->str_len && str->str_pok)
-       str->str_nval = atof(str->str_ptr);
-    else {
+       str->str_u.str_nval = atof(str->str_ptr);
+    else  {
+       if (str == &str_undef)
+           return 0.0;
        if (dowarn)
-           fprintf(stderr,"Use of uninitialized variable in %s line %ld.\n",
-               filename,(long)line);
-       str->str_nval = 0.0;
+           warn("Use of uninitialized variable");
+       str->str_u.str_nval = 0.0;
     }
     str->str_nok = 1;
 #ifdef DEBUGGING
     if (debug & 32)
-       fprintf(stderr,"0x%lx num(%g)\n",str,str->str_nval);
+       fprintf(stderr,"0x%lx num(%g)\n",str,str->str_u.str_nval);
 #endif
-    return str->str_nval;
+    return str->str_u.str_nval;
 }
 
+/* Note: str_sset() should not be called with a source string that needs
+ * be reused, since it may destroy the source string if it is marked
+ * as temporary.
+ */
+
 str_sset(dstr,sstr)
 STR *dstr;
 register STR *sstr;
 {
+#ifdef TAINT
+    if (sstr)
+       tainted |= sstr->str_tainted;
+#endif
+    if (sstr == dstr || dstr == &str_undef)
+       return;
     if (!sstr)
-       str_nset(dstr,No,0);
+       dstr->str_pok = dstr->str_nok = 0;
+    else if (sstr->str_pok) {
+
+       /*
+        * Check to see if we can just swipe the string.  If so, it's a
+        * possible small lose on short strings, but a big win on long ones.
+        * It might even be a win on short strings if dstr->str_ptr
+        * has to be allocated and sstr->str_ptr has to be freed.
+        */
+
+       if (sstr->str_pok & SP_TEMP) {          /* slated for free anyway? */
+           if (dstr->str_ptr) {
+               if (dstr->str_state == SS_INCR)
+                   dstr->str_ptr -= dstr->str_u.str_useful;
+               Safefree(dstr->str_ptr);
+           }
+           dstr->str_ptr = sstr->str_ptr;
+           dstr->str_len = sstr->str_len;
+           dstr->str_cur = sstr->str_cur;
+           dstr->str_state = sstr->str_state;
+           dstr->str_pok = sstr->str_pok & ~SP_TEMP;
+#ifdef TAINT
+           dstr->str_tainted = sstr->str_tainted;
+#endif
+           sstr->str_ptr = Nullch;
+           sstr->str_len = 0;
+           sstr->str_pok = 0;                  /* wipe out any weird flags */
+           sstr->str_state = 0;                /* so sstr frees uneventfully */
+       }
+       else                                    /* have to copy actual string */
+           str_nset(dstr,sstr->str_ptr,sstr->str_cur);
+       if (dstr->str_nok = sstr->str_nok)
+           dstr->str_u.str_nval = sstr->str_u.str_nval;
+       else {
+#ifdef STRUCTCOPY
+           dstr->str_u = sstr->str_u;
+#else
+           dstr->str_u.str_nval = sstr->str_u.str_nval;
+#endif
+           if (dstr->str_cur == sizeof(STBP)) {
+               char *tmps = dstr->str_ptr;
+
+               if (*tmps == 'S' && bcmp(tmps,"StB",4) == 0) {
+                   if (!dstr->str_magic) {
+                       dstr->str_magic = str_smake(sstr->str_magic);
+                       dstr->str_magic->str_rare = 'X';
+                   }
+               }
+           }
+       }
+    }
     else if (sstr->str_nok)
-       str_numset(dstr,sstr->str_nval);
-    else if (sstr->str_pok)
-       str_nset(dstr,sstr->str_ptr,sstr->str_cur);
-    else
-       str_nset(dstr,"",0);
+       str_numset(dstr,sstr->str_u.str_nval);
+    else {
+       if (dstr->str_state == SS_INCR)
+           Str_Grow(dstr,0);       /* just force copy down */
+
+#ifdef STRUCTCOPY
+       dstr->str_u = sstr->str_u;
+#else
+       dstr->str_u.str_nval = sstr->str_u.str_nval;
+#endif
+       dstr->str_pok = dstr->str_nok = 0;
+    }
 }
 
 str_nset(str,ptr,len)
 register STR *str;
 register char *ptr;
-register int len;
+register STRLEN len;
 {
-    GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
-    bcopy(ptr,str->str_ptr,len);
+    if (str == &str_undef)
+       return;
+    STR_GROW(str, len + 1);
+    if (ptr)
+       (void)bcopy(ptr,str->str_ptr,len);
     str->str_cur = len;
     *(str->str_ptr+str->str_cur) = '\0';
     str->str_nok = 0;          /* invalidate number */
     str->str_pok = 1;          /* validate pointer */
+#ifdef TAINT
+    str->str_tainted = tainted;
+#endif
 }
 
 str_set(str,ptr)
 register STR *str;
 register char *ptr;
 {
-    register int len;
+    register STRLEN len;
 
+    if (str == &str_undef)
+       return;
     if (!ptr)
        ptr = "";
     len = strlen(ptr);
-    GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
-    bcopy(ptr,str->str_ptr,len+1);
+    STR_GROW(str, len + 1);
+    (void)bcopy(ptr,str->str_ptr,len+1);
     str->str_cur = len;
     str->str_nok = 0;          /* invalidate number */
     str->str_pok = 1;          /* validate pointer */
+#ifdef TAINT
+    str->str_tainted = tainted;
+#endif
 }
 
 str_chop(str,ptr)      /* like set but assuming ptr is in str */
 register STR *str;
 register char *ptr;
 {
+    register STRLEN delta;
+
     if (!(str->str_pok))
-       str_2ptr(str);
-    str->str_cur -= (ptr - str->str_ptr);
-    bcopy(ptr,str->str_ptr, str->str_cur + 1);
+       fatal("str_chop: internal inconsistency");
+    delta = ptr - str->str_ptr;
+    str->str_len -= delta;
+    str->str_cur -= delta;
+    str->str_ptr += delta;
+    if (str->str_state == SS_INCR)
+       str->str_u.str_useful += delta;
+    else {
+       str->str_u.str_useful = delta;
+       str->str_state = SS_INCR;
+    }
     str->str_nok = 0;          /* invalidate number */
-    str->str_pok = 1;          /* validate pointer */
+    str->str_pok = 1;          /* validate pointer (and unstudy str) */
 }
 
 str_ncat(str,ptr,len)
 register STR *str;
 register char *ptr;
-register int len;
+register STRLEN len;
 {
+    if (str == &str_undef)
+       return;
     if (!(str->str_pok))
-       str_2ptr(str);
-    GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
-    bcopy(ptr,str->str_ptr+str->str_cur,len);
+       (void)str_2ptr(str);
+    STR_GROW(str, str->str_cur + len + 1);
+    (void)bcopy(ptr,str->str_ptr+str->str_cur,len);
     str->str_cur += len;
     *(str->str_ptr+str->str_cur) = '\0';
     str->str_nok = 0;          /* invalidate number */
     str->str_pok = 1;          /* validate pointer */
+#ifdef TAINT
+    str->str_tainted |= tainted;
+#endif
 }
 
 str_scat(dstr,sstr)
 STR *dstr;
 register STR *sstr;
 {
+#ifdef TAINT
+    tainted |= sstr->str_tainted;
+#endif
     if (!sstr)
        return;
     if (!(sstr->str_pok))
-       str_2ptr(sstr);
+       (void)str_2ptr(sstr);
     if (sstr)
        str_ncat(dstr,sstr->str_ptr,sstr->str_cur);
 }
@@ -208,46 +423,54 @@ str_cat(str,ptr)
 register STR *str;
 register char *ptr;
 {
-    register int len;
+    register STRLEN len;
 
+    if (str == &str_undef)
+       return;
     if (!ptr)
        return;
     if (!(str->str_pok))
-       str_2ptr(str);
+       (void)str_2ptr(str);
     len = strlen(ptr);
-    GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
-    bcopy(ptr,str->str_ptr+str->str_cur,len+1);
+    STR_GROW(str, str->str_cur + len + 1);
+    (void)bcopy(ptr,str->str_ptr+str->str_cur,len+1);
     str->str_cur += len;
     str->str_nok = 0;          /* invalidate number */
     str->str_pok = 1;          /* validate pointer */
+#ifdef TAINT
+    str->str_tainted |= tainted;
+#endif
 }
 
 char *
-str_append_till(str,from,delim,keeplist)
+str_append_till(str,from,fromend,delim,keeplist)
 register STR *str;
 register char *from;
+register char *fromend;
 register int delim;
 char *keeplist;
 {
     register char *to;
-    register int len;
+    register STRLEN len;
 
+    if (str == &str_undef)
+       return Nullch;
     if (!from)
        return Nullch;
-    len = strlen(from);
-    GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
+    len = fromend - from;
+    STR_GROW(str, str->str_cur + len + 1);
     str->str_nok = 0;          /* invalidate number */
     str->str_pok = 1;          /* validate pointer */
     to = str->str_ptr+str->str_cur;
-    for (; *from; from++,to++) {
-       if (*from == '\\' && from[1] && delim != '\\') {
+    for (; from < fromend; from++,to++) {
+       if (*from == '\\' && from+1 < fromend && delim != '\\') {
            if (!keeplist) {
                if (from[1] == delim || from[1] == '\\')
                    from++;
                else
                    *to++ = *from++;
            }
-           else if (index(keeplist,from[1]))
+           else if (from[1] && index(keeplist,from[1]))
                *to++ = *from++;
            else
                from++;
@@ -262,32 +485,126 @@ char *keeplist;
 }
 
 STR *
+#ifdef LEAKTEST
+str_new(x,len)
+int x;
+#else
 str_new(len)
-int len;
+#endif
+STRLEN len;
 {
     register STR *str;
     
     if (freestrroot) {
        str = freestrroot;
-       freestrroot = str->str_link.str_next;
-       str->str_link.str_magic = Nullstab;
+       freestrroot = str->str_magic;
+       str->str_magic = Nullstr;
+       str->str_state = SS_NORM;
     }
     else {
-       str = (STR *) safemalloc(sizeof(STR));
-       bzero((char*)str,sizeof(STR));
+       Newz(700+x,str,1,STR);
     }
     if (len)
-       GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
+       STR_GROW(str, len + 1);
     return str;
 }
 
 void
-str_grow(str,len)
+str_magic(str, stab, how, name, namlen)
 register STR *str;
-int len;
+STAB *stab;
+int how;
+char *name;
+STRLEN namlen;
 {
-    if (len && str)
-       GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
+    if (str == &str_undef || str->str_magic)
+       return;
+    str->str_magic = Str_new(75,namlen);
+    str = str->str_magic;
+    str->str_u.str_stab = stab;
+    str->str_rare = how;
+    if (name)
+       str_nset(str,name,namlen);
+}
+
+void
+str_insert(bigstr,offset,len,little,littlelen)
+STR *bigstr;
+STRLEN offset;
+STRLEN len;
+char *little;
+STRLEN littlelen;
+{
+    register char *big;
+    register char *mid;
+    register char *midend;
+    register char *bigend;
+    register int i;
+
+    if (bigstr == &str_undef)
+       return;
+    bigstr->str_nok = 0;
+    bigstr->str_pok = SP_VALID;        /* disable possible screamer */
+
+    i = littlelen - len;
+    if (i > 0) {                       /* string might grow */
+       STR_GROW(bigstr, bigstr->str_cur + i + 1);
+       big = bigstr->str_ptr;
+       mid = big + offset + len;
+       midend = bigend = big + bigstr->str_cur;
+       bigend += i;
+       *bigend = '\0';
+       while (midend > mid)            /* shove everything down */
+           *--bigend = *--midend;
+       (void)bcopy(little,big+offset,littlelen);
+       bigstr->str_cur += i;
+       return;
+    }
+    else if (i == 0) {
+       (void)bcopy(little,bigstr->str_ptr+offset,len);
+       return;
+    }
+
+    big = bigstr->str_ptr;
+    mid = big + offset;
+    midend = mid + len;
+    bigend = big + bigstr->str_cur;
+
+    if (midend > bigend)
+       fatal("panic: str_insert");
+
+    if (mid - big > bigend - midend) { /* faster to shorten from end */
+       if (littlelen) {
+           (void)bcopy(little, mid, littlelen);
+           mid += littlelen;
+       }
+       i = bigend - midend;
+       if (i > 0) {
+           (void)bcopy(midend, mid, i);
+           mid += i;
+       }
+       *mid = '\0';
+       bigstr->str_cur = mid - big;
+    }
+    else if (i = mid - big) {  /* faster from front */
+       midend -= littlelen;
+       mid = midend;
+       str_chop(bigstr,midend-i);
+       big += i;
+       while (i--)
+           *--midend = *--big;
+       if (littlelen)
+           (void)bcopy(little, mid, littlelen);
+    }
+    else if (littlelen) {
+       midend -= littlelen;
+       str_chop(bigstr,midend);
+       (void)bcopy(little,midend,littlelen);
+    }
+    else {
+       str_chop(bigstr,midend);
+    }
+    STABSET(bigstr);
 }
 
 /* make str point to what nstr did */
@@ -297,71 +614,182 @@ str_replace(str,nstr)
 register STR *str;
 register STR *nstr;
 {
-    safefree(str->str_ptr);
+    if (str == &str_undef)
+       return;
+    if (str->str_state == SS_INCR)
+       Str_Grow(str,0);        /* just force copy down */
+    if (nstr->str_state == SS_INCR)
+       Str_Grow(nstr,0);
+    if (str->str_ptr)
+       Safefree(str->str_ptr);
     str->str_ptr = nstr->str_ptr;
     str->str_len = nstr->str_len;
     str->str_cur = nstr->str_cur;
     str->str_pok = nstr->str_pok;
-    if (str->str_nok = nstr->str_nok)
-       str->str_nval = nstr->str_nval;
-    safefree((char*)nstr);
+    str->str_nok = nstr->str_nok;
+#ifdef STRUCTCOPY
+    str->str_u = nstr->str_u;
+#else
+    str->str_u.str_nval = nstr->str_u.str_nval;
+#endif
+#ifdef TAINT
+    str->str_tainted = nstr->str_tainted;
+#endif
+    if (nstr->str_magic)
+       str_free(nstr->str_magic);
+    Safefree(nstr);
 }
 
 void
 str_free(str)
 register STR *str;
 {
-    if (!str)
+    if (!str || str == &str_undef)
        return;
+    if (str->str_state) {
+       if (str->str_state == SS_FREE)  /* already freed */
+           return;
+       if (str->str_state == SS_INCR && !(str->str_pok & 2)) {
+           str->str_ptr -= str->str_u.str_useful;
+           str->str_len += str->str_u.str_useful;
+       }
+    }
+    if (str->str_magic)
+       str_free(str->str_magic);
+#ifdef LEAKTEST
     if (str->str_len)
-       str->str_ptr[0] = '\0';
+       Safefree(str->str_ptr);
+    if ((str->str_pok & SP_INTRP) && str->str_u.str_args)
+       arg_free(str->str_u.str_args);
+    Safefree(str);
+#else /* LEAKTEST */
+    if (str->str_len) {
+       if (str->str_len > 127) {       /* next user not likely to want more */
+           Safefree(str->str_ptr);     /* so give it back to malloc */
+           str->str_ptr = Nullch;
+           str->str_len = 0;
+       }
+       else
+           str->str_ptr[0] = '\0';
+    }
+    if ((str->str_pok & SP_INTRP) && str->str_u.str_args)
+       arg_free(str->str_u.str_args);
     str->str_cur = 0;
     str->str_nok = 0;
     str->str_pok = 0;
-    str->str_link.str_next = freestrroot;
+    str->str_state = SS_FREE;
+#ifdef TAINT
+    str->str_tainted = 0;
+#endif
+    str->str_magic = freestrroot;
     freestrroot = str;
+#endif /* LEAKTEST */
 }
 
+STRLEN
 str_len(str)
 register STR *str;
 {
     if (!str)
        return 0;
     if (!(str->str_pok))
-       str_2ptr(str);
-    if (str->str_len)
+       (void)str_2ptr(str);
+    if (str->str_ptr)
        return str->str_cur;
     else
        return 0;
 }
 
+str_eq(str1,str2)
+register STR *str1;
+register STR *str2;
+{
+    if (!str1 || str1 == &str_undef)
+       return (str2 == Nullstr || str2 == &str_undef || !str2->str_cur);
+    if (!str2 || str2 == &str_undef)
+       return !str1->str_cur;
+
+    if (!str1->str_pok)
+       (void)str_2ptr(str1);
+    if (!str2->str_pok)
+       (void)str_2ptr(str2);
+
+    if (str1->str_cur != str2->str_cur)
+       return 0;
+
+    return !bcmp(str1->str_ptr, str2->str_ptr, str1->str_cur);
+}
+
+str_cmp(str1,str2)
+register STR *str1;
+register STR *str2;
+{
+    int retval;
+
+    if (!str1 || str1 == &str_undef)
+       return (str2 == Nullstr || str2 == &str_undef || !str2->str_cur)?0:-1;
+    if (!str2 || str2 == &str_undef)
+       return str1->str_cur != 0;
+
+    if (!str1->str_pok)
+       (void)str_2ptr(str1);
+    if (!str2->str_pok)
+       (void)str_2ptr(str2);
+
+    if (str1->str_cur < str2->str_cur) {
+       if (retval = memcmp(str1->str_ptr, str2->str_ptr, str1->str_cur))
+           return retval;
+       else
+           return -1;
+    }
+    else if (retval = memcmp(str1->str_ptr, str2->str_ptr, str2->str_cur))
+       return retval;
+    else if (str1->str_cur == str2->str_cur)
+       return 0;
+    else
+       return 1;
+}
+
 char *
-str_gets(str,fp)
+str_gets(str,fp,append)
 register STR *str;
 register FILE *fp;
+int append;
 {
-#ifdef STDSTDIO                /* Here is some breathtakingly efficient cheating */
-
     register char *bp;         /* we're going to steal some values */
     register int cnt;          /*  from the stdio struct and put EVERYTHING */
     register STDCHAR *ptr;     /*   in the innermost loop into registers */
-    register char newline = record_separator;/* (assuming >= 6 registers) */
+    register int newline = record_separator;/* (assuming >= 6 registers) */
     int i;
-    int bpx;
-    int obpx;
+    STRLEN bpx;
+    STRLEN obpx;
     register int get_paragraph;
     register char *oldbp;
+    int shortbuffered;
 
-    if (get_paragraph = !newline) {    /* yes, that's an assignment */
+    if (str == &str_undef)
+       return Nullch;
+    if (get_paragraph = !rslen) {      /* yes, that's an assignment */
        newline = '\n';
        oldbp = Nullch;                 /* remember last \n position (none) */
     }
+#ifdef STDSTDIO                /* Here is some breathtakingly efficient cheating */
     cnt = fp->_cnt;                    /* get count into register */
     str->str_nok = 0;                  /* invalidate number */
     str->str_pok = 1;                  /* validate pointer */
-    if (str->str_len <= cnt)           /* make sure we have the room */
-       GROWSTR(&(str->str_ptr), &(str->str_len), cnt+1);
-    bp = str->str_ptr;                 /* move these two too to registers */
+    if (str->str_len <= cnt + 1) {     /* make sure we have the room */
+       if (cnt > 80 && str->str_len > 0) {
+           shortbuffered = cnt - str->str_len + 1;
+           cnt = str->str_len - 1;
+       }
+       else {
+           shortbuffered = 0;
+           STR_GROW(str, append+cnt+2);/* (remembering cnt can be -1) */
+       }
+    }
+    else
+       shortbuffered = 0;
+    bp = str->str_ptr + append;                /* move these two too to registers */
     ptr = fp->_ptr;
     for (;;) {
       screamer:
@@ -370,6 +798,19 @@ register FILE *fp;
                goto thats_all_folks;           /* screams */   /* sed :-) */ 
        }
        
+       if (shortbuffered) {                    /* oh well, must extend */
+           cnt = shortbuffered;
+           shortbuffered = 0;
+           if (get_paragraph && oldbp)
+               obpx = oldbp - str->str_ptr;
+           bpx = bp - str->str_ptr;    /* prepare for possible relocation */
+           STR_GROW(str, str->str_len + append + cnt + 2);
+           bp = str->str_ptr + bpx;    /* reconstitute our pointer */
+           if (get_paragraph && oldbp)
+               oldbp = str->str_ptr + obpx;
+           continue;
+       }
+
        fp->_cnt = cnt;                 /* deregisterize cnt and ptr */
        fp->_ptr = ptr;
        i = _filbuf(fp);                /* get more characters */
@@ -379,7 +820,8 @@ register FILE *fp;
        bpx = bp - str->str_ptr;        /* prepare for possible relocation */
        if (get_paragraph && oldbp)
            obpx = oldbp - str->str_ptr;
-       GROWSTR(&(str->str_ptr), &(str->str_len), bpx + cnt + 2);
+       str->str_cur = bpx;
+       STR_GROW(str, bpx + cnt + 2);
        bp = str->str_ptr + bpx;        /* reconstitute our pointer */
        if (get_paragraph && oldbp)
            oldbp = str->str_ptr + obpx;
@@ -399,6 +841,8 @@ thats_all_folks:
        goto screamer;  /* and go back to the fray */
     }
 thats_really_all_folks:
+    if (shortbuffered)
+       cnt += shortbuffered;
     fp->_cnt = cnt;                    /* put these back or we're in trouble */
     fp->_ptr = ptr;
     *bp = '\0';
@@ -406,48 +850,361 @@ thats_really_all_folks:
 
 #else /* !STDSTDIO */  /* The big, slow, and stupid way */
 
-    static char buf[4192];
+    {
+       static char buf[8192];
+       char * bpe = buf + sizeof(buf) - 3;
 
-    if (fgets(buf, sizeof buf, fp) != Nullch)
-       str_set(str, buf);
-    else
-       str_set(str, No);
+screamer:
+       bp = buf;
+filler:
+       while ((i = getc(fp)) != EOF && (*bp++ = i) != newline && bp < bpe);
+       if (i == newline && get_paragraph &&
+           (i = getc(fp)) != EOF && (*bp++ = i) != newline && bp < bpe)
+           goto filler;
+
+       *bp = '\0';
+       if (append)
+           str_cat(str, buf);
+       else
+           str_set(str, buf);
+       if (i != newline && i != EOF) {
+           append = -1;
+           goto screamer;
+       }
+    }
 
 #endif /* STDSTDIO */
 
-    return str->str_cur ? str->str_ptr : Nullch;
+    return str->str_cur - append ? str->str_ptr : Nullch;
 }
 
+ARG *
+parselist(str)
+STR *str;
+{
+    register CMD *cmd;
+    register ARG *arg;
+    CMD *oldcurcmd = curcmd;
+    int oldperldb = perldb;
+    int retval;
 
-STR *
-interp(str,s)
-register STR *str;
-register char *s;
+    perldb = 0;
+    str_sset(linestr,str);
+    in_eval++;
+    oldoldbufptr = oldbufptr = bufptr = str_get(linestr);
+    bufend = bufptr + linestr->str_cur;
+    if (++loop_ptr >= loop_max) {
+        loop_max += 128;
+        Renew(loop_stack, loop_max, struct loop);
+    }
+    loop_stack[loop_ptr].loop_label = "_EVAL_";
+    loop_stack[loop_ptr].loop_sp = 0;
+#ifdef DEBUGGING
+    if (debug & 4) {
+        deb("(Pushing label #%d _EVAL_)\n", loop_ptr);
+    }
+#endif
+    if (setjmp(loop_stack[loop_ptr].loop_env)) {
+       in_eval--;
+       loop_ptr--;
+       perldb = oldperldb;
+       fatal("%s\n",stab_val(stabent("@",TRUE))->str_ptr);
+    }
+#ifdef DEBUGGING
+    if (debug & 4) {
+       char *tmps = loop_stack[loop_ptr].loop_label;
+       deb("(Popping label #%d %s)\n",loop_ptr,
+           tmps ? tmps : "" );
+    }
+#endif
+    loop_ptr--;
+    error_count = 0;
+    curcmd = &compiling;
+    curcmd->c_line = oldcurcmd->c_line;
+    retval = yyparse();
+    curcmd = oldcurcmd;
+    perldb = oldperldb;
+    in_eval--;
+    if (retval || error_count)
+       fatal("Invalid component in string or format");
+    cmd = eval_root;
+    arg = cmd->c_expr;
+    if (cmd->c_type != C_EXPR || cmd->c_next || arg->arg_type != O_LIST)
+       fatal("panic: error in parselist %d %x %d", cmd->c_type,
+         cmd->c_next, arg ? arg->arg_type : -1);
+    Safefree(cmd);
+    return arg;
+}
+
+void
+intrpcompile(src)
+STR *src;
 {
-    register char *t = s;
-    char *envsave = envname;
-    envname = Nullch;
+    register char *s = str_get(src);
+    register char *send = s + src->str_cur;
+    register STR *str;
+    register char *t;
+    STR *toparse;
+    STRLEN len;
+    register int brackets;
+    register char *d;
+    STAB *stab;
+    char *checkpoint;
 
-    str_set(str,"");
-    while (*s) {
-       if (*s == '\\' && s[1] == '\\') {
-           str_ncat(str, t, s++ - t);
-           t = s++;
-       }
-       else if (*s == '\\' && s[1] == '$') {
-           str_ncat(str, t, s++ - t);
-           t = s++;
+    toparse = Str_new(76,0);
+    str = Str_new(77,0);
+
+    str_nset(str,"",0);
+    str_nset(toparse,"",0);
+    t = s;
+    while (s < send) {
+       if (*s == '\\' && s[1] && index("$@[{\\]}",s[1])) {
+           str_ncat(str, t, s - t);
+           ++s;
+           if (*nointrp && s+1 < send)
+               if (*s != '@' && (*s != '$' || index(nointrp,s[1])))
+                   str_ncat(str,s-1,1);
+           str_ncat(str, "$b", 2);
+           str_ncat(str, s, 1);
+           ++s;
+           t = s;
        }
-       else if (*s == '$' && s[1] && s[1] != '|') {
+       else if ((*s == '@' || (*s == '$' && !index(nointrp,s[1]))) &&
+         s+1 < send) {
            str_ncat(str,t,s-t);
-           s = scanreg(s,tokenbuf);
-           str_cat(str,reg_get(tokenbuf));
            t = s;
+           if (*s == '$' && s[1] == '#' && (isalpha(s[2]) || s[2] == '_'))
+               s++;
+           s = scanreg(s,send,tokenbuf);
+           if (*t == '@' &&
+             (!(stab = stabent(tokenbuf,FALSE)) || 
+                (*s == '{' ? !stab_xhash(stab) : !stab_xarray(stab)) )) {
+               str_ncat(str,"@",1);
+               s = ++t;
+               continue;       /* grandfather @ from old scripts */
+           }
+           str_ncat(str,"$a",2);
+           str_ncat(toparse,",",1);
+           if (t[1] != '{' && (*s == '['  || *s == '{' /* }} */ ) &&
+             (stab = stabent(tokenbuf,FALSE)) &&
+             ((*s == '[') ? (stab_xarray(stab) != 0) : (stab_xhash(stab) != 0)) ) {
+               brackets = 0;
+               checkpoint = s;
+               do {
+                   switch (*s) {
+                   case '[':
+                       if (s[-1] != '$')
+                           brackets++;
+                       break;
+                   case '{':
+                       brackets++;
+                       break;
+                   case ']':
+                       if (s[-1] != '$')
+                           brackets--;
+                       break;
+                   case '}':
+                       brackets--;
+                       break;
+                   case '\'':
+                   case '"':
+                       if (s[-1] != '$') {
+                           s = cpytill(tokenbuf,s+1,send,*s,&len);
+                           if (s >= send)
+                               fatal("Unterminated string");
+                       }
+                       break;
+                   }
+                   s++;
+               } while (brackets > 0 && s < send);
+               if (s > send)
+                   fatal("Unmatched brackets in string");
+               if (*nointrp) {         /* we're in a regular expression */
+                   d = checkpoint;
+                   if (*d == '{' && s[-1] == '}') {    /* maybe {n,m} */
+                       ++d;
+                       if (isdigit(*d)) {      /* matches /^{\d,?\d*}$/ */
+                           if (*++d == ',')
+                               ++d;
+                           while (isdigit(*d))
+                               d++;
+                           if (d == s - 1)
+                               s = checkpoint;         /* Is {n,m}! Backoff! */
+                       }
+                   }
+                   else if (*d == '[' && s[-1] == ']') { /* char class? */
+                       int weight = 2;         /* let's weigh the evidence */
+                       char seen[256];
+                       unsigned char un_char = 0, last_un_char;
+
+                       Zero(seen,256,char);
+                       *--s = '\0';
+                       if (d[1] == '^')
+                           weight += 150;
+                       else if (d[1] == '$')
+                           weight -= 3;
+                       if (isdigit(d[1])) {
+                           if (d[2]) {
+                               if (isdigit(d[2]) && !d[3])
+                                   weight -= 10;
+                           }
+                           else
+                               weight -= 100;
+                       }
+                       for (d++; d < s; d++) {
+                           last_un_char = un_char;
+                           un_char = (unsigned char)*d;
+                           switch (*d) {
+                           case '&':
+                           case '$':
+                               weight -= seen[un_char] * 10;
+                               if (isalpha(d[1]) || isdigit(d[1]) ||
+                                 d[1] == '_') {
+                                   d = scanreg(d,s,tokenbuf);
+                                   if (stabent(tokenbuf,FALSE))
+                                       weight -= 100;
+                                   else
+                                       weight -= 10;
+                               }
+                               else if (*d == '$' && d[1] &&
+                                 index("[#!%*<>()-=",d[1])) {
+                                   if (!d[2] || /*{*/ index("])} =",d[2]))
+                                       weight -= 10;
+                                   else
+                                       weight -= 1;
+                               }
+                               break;
+                           case '\\':
+                               un_char = 254;
+                               if (d[1]) {
+                                   if (index("wds",d[1]))
+                                       weight += 100;
+                                   else if (seen['\''] || seen['"'])
+                                       weight += 1;
+                                   else if (index("rnftb",d[1]))
+                                       weight += 40;
+                                   else if (isdigit(d[1])) {
+                                       weight += 40;
+                                       while (d[1] && isdigit(d[1]))
+                                           d++;
+                                   }
+                               }
+                               else
+                                   weight += 100;
+                               break;
+                           case '-':
+                               if (last_un_char < (unsigned char) d[1]
+                                 || d[1] == '\\') {
+                                   if (index("aA01! ",last_un_char))
+                                       weight += 30;
+                                   if (index("zZ79~",d[1]))
+                                       weight += 30;
+                               }
+                               else
+                                   weight -= 1;
+                           default:
+                               if (isalpha(*d) && d[1] && isalpha(d[1])) {
+                                   bufptr = d;
+                                   if (yylex() != WORD)
+                                       weight -= 150;
+                                   d = bufptr;
+                               }
+                               if (un_char == last_un_char + 1)
+                                   weight += 5;
+                               weight -= seen[un_char];
+                               break;
+                           }
+                           seen[un_char]++;
+                       }
+#ifdef DEBUGGING
+                       if (debug & 512)
+                           fprintf(stderr,"[%s] weight %d\n",
+                             checkpoint+1,weight);
+#endif
+                       *s++ = ']';
+                       if (weight >= 0)        /* probably a character class */
+                           s = checkpoint;
+                   }
+               }
+           }
+           if (*t == '@')
+               str_ncat(toparse, "join($\",", 8);
+           if (t[1] == '{' && s[-1] == '}') {
+               str_ncat(toparse, t, 1);
+               str_ncat(toparse, t+2, s - t - 3);
+           }
+           else
+               str_ncat(toparse, t, s - t);
+           if (*t == '@')
+               str_ncat(toparse, ")", 1);
+           t = s;
+       }
+       else
+           s++;
+    }
+    str_ncat(str,t,s-t);
+    if (toparse->str_ptr && *toparse->str_ptr == ',') {
+       *toparse->str_ptr = '(';
+       str_ncat(toparse,",$$);",5);
+       str->str_u.str_args = parselist(toparse);
+       str->str_u.str_args->arg_len--;         /* ignore $$ reference */
+    }
+    else
+       str->str_u.str_args = Nullarg;
+    str_free(toparse);
+    str->str_pok |= SP_INTRP;
+    str->str_nok = 0;
+    str_replace(src,str);
+}
+
+STR *
+interp(str,src,sp)
+register STR *str;
+STR *src;
+int sp;
+{
+    register char *s;
+    register char *t;
+    register char *send;
+    register STR **elem;
+
+    if (str == &str_undef)
+       return Nullstr;
+    if (!(src->str_pok & SP_INTRP)) {
+       int oldsave = savestack->ary_fill;
+
+       (void)savehptr(&curstash);
+       curstash = curcmd->c_stash;     /* so stabent knows right package */
+       intrpcompile(src);
+       restorelist(oldsave);
+    }
+    s = src->str_ptr;          /* assumed valid since str_pok set */
+    t = s;
+    send = s + src->str_cur;
+
+    if (src->str_u.str_args) {
+       (void)eval(src->str_u.str_args,G_ARRAY,sp);
+       /* Assuming we have correct # of args */
+       elem = stack->ary_array + sp;
+    }
+
+    str_nset(str,"",0);
+    while (s < send) {
+       if (*s == '$' && s+1 < send) {
+           str_ncat(str,t,s-t);
+           switch(*++s) {
+           case 'a':
+               str_scat(str,*++elem);
+               break;
+           case 'b':
+               str_ncat(str,++s,1);
+               break;
+           }
+           t = ++s;
        }
        else
            s++;
     }
-    envname = envsave;
     str_ncat(str,t,s-t);
     return str;
 }
@@ -458,16 +1215,17 @@ register STR *str;
 {
     register char *d;
 
-    if (!str)
+    if (!str || str == &str_undef)
        return;
     if (str->str_nok) {
-       str->str_nval += 1.0;
+       str->str_u.str_nval += 1.0;
        str->str_pok = 0;
        return;
     }
     if (!str->str_pok || !*str->str_ptr) {
-       str->str_nval = 1.0;
+       str->str_u.str_nval = 1.0;
        str->str_nok = 1;
+       str->str_pok = 0;
        return;
     }
     d = str->str_ptr;
@@ -492,7 +1250,7 @@ register STR *str;
        }
     }
     /* oh,oh, the number grew */
-    GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + 2);
+    STR_GROW(str, str->str_cur + 2);
     str->str_cur++;
     for (d = str->str_ptr + str->str_cur; d > str->str_ptr; d--)
        *d = d[-1];
@@ -506,52 +1264,83 @@ void
 str_dec(str)
 register STR *str;
 {
-    if (!str)
+    if (!str || str == &str_undef)
        return;
     if (str->str_nok) {
-       str->str_nval -= 1.0;
+       str->str_u.str_nval -= 1.0;
        str->str_pok = 0;
        return;
     }
     if (!str->str_pok) {
-       str->str_nval = -1.0;
+       str->str_u.str_nval = -1.0;
        str->str_nok = 1;
        return;
     }
     str_numset(str,atof(str->str_ptr) - 1.0);
 }
 
-/* make a string that will exist for the duration of the expression eval */
+/* Make a string that will exist for the duration of the expression
+ * evaluation.  Actually, it may have to last longer than that, but
+ * hopefully cmd_exec won't free it until it has been assigned to a
+ * permanent location. */
+
+static long tmps_size = -1;
 
 STR *
 str_static(oldstr)
 STR *oldstr;
 {
-    register STR *str = str_new(0);
-    static long tmps_size = -1;
+    register STR *str = Str_new(78,0);
 
     str_sset(str,oldstr);
     if (++tmps_max > tmps_size) {
        tmps_size = tmps_max;
        if (!(tmps_size & 127)) {
            if (tmps_size)
-               tmps_list = (STR**)saferealloc((char*)tmps_list,
-                   (MEM_SIZE)((tmps_size + 128) * sizeof(STR*)) );
+               Renew(tmps_list, tmps_size + 128, STR*);
            else
-               tmps_list = (STR**)safemalloc(128 * sizeof(char*));
+               New(702,tmps_list, 128, STR*);
        }
     }
     tmps_list[tmps_max] = str;
+    if (str->str_pok)
+       str->str_pok |= SP_TEMP;
     return str;
 }
 
+/* same thing without the copying */
+
 STR *
-str_make(s)
+str_2static(str)
+register STR *str;
+{
+    if (str == &str_undef)
+       return str;
+    if (++tmps_max > tmps_size) {
+       tmps_size = tmps_max;
+       if (!(tmps_size & 127)) {
+           if (tmps_size)
+               Renew(tmps_list, tmps_size + 128, STR*);
+           else
+               New(704,tmps_list, 128, STR*);
+       }
+    }
+    tmps_list[tmps_max] = str;
+    if (str->str_pok)
+       str->str_pok |= SP_TEMP;
+    return str;
+}
+
+STR *
+str_make(s,len)
 char *s;
+STRLEN len;
 {
-    register STR *str = str_new(0);
+    register STR *str = Str_new(79,0);
 
-    str_set(str,s);
+    if (!len)
+       len = strlen(s);
+    str_nset(str,s,len);
     return str;
 }
 
@@ -559,8 +1348,124 @@ STR *
 str_nmake(n)
 double n;
 {
-    register STR *str = str_new(0);
+    register STR *str = Str_new(80,0);
 
     str_numset(str,n);
     return str;
 }
+
+/* make an exact duplicate of old */
+
+STR *
+str_smake(old)
+register STR *old;
+{
+    register STR *new = Str_new(81,0);
+
+    if (!old)
+       return Nullstr;
+    if (old->str_state == SS_FREE) {
+       warn("semi-panic: attempt to dup freed string");
+       return Nullstr;
+    }
+    if (old->str_state == SS_INCR && !(old->str_pok & 2))
+       Str_Grow(old,0);
+    if (new->str_ptr)
+       Safefree(new->str_ptr);
+    Copy(old,new,1,STR);
+    if (old->str_ptr)
+       new->str_ptr = nsavestr(old->str_ptr,old->str_len);
+    return new;
+}
+
+str_reset(s,stash)
+register char *s;
+HASH *stash;
+{
+    register HENT *entry;
+    register STAB *stab;
+    register STR *str;
+    register int i;
+    register SPAT *spat;
+    register int max;
+
+    if (!*s) {         /* reset ?? searches */
+       for (spat = stash->tbl_spatroot;
+         spat != Nullspat;
+         spat = spat->spat_next) {
+           spat->spat_flags &= ~SPAT_USED;
+       }
+       return;
+    }
+
+    /* reset variables */
+
+    if (!stash->tbl_array)
+       return;
+    while (*s) {
+       i = *s;
+       if (s[1] == '-') {
+           s += 2;
+       }
+       max = *s++;
+       for ( ; i <= max; i++) {
+           for (entry = stash->tbl_array[i];
+             entry;
+             entry = entry->hent_next) {
+               stab = (STAB*)entry->hent_val;
+               str = stab_val(stab);
+               str->str_cur = 0;
+               str->str_nok = 0;
+#ifdef TAINT
+               str->str_tainted = tainted;
+#endif
+               if (str->str_ptr != Nullch)
+                   str->str_ptr[0] = '\0';
+               if (stab_xarray(stab)) {
+                   aclear(stab_xarray(stab));
+               }
+               if (stab_xhash(stab)) {
+                   hclear(stab_xhash(stab), FALSE);
+                   if (stab == envstab)
+                       environ[0] = Nullch;
+               }
+           }
+       }
+    }
+}
+
+#ifdef TAINT
+taintproper(s)
+char *s;
+{
+#ifdef DEBUGGING
+    if (debug & 2048)
+       fprintf(stderr,"%s %d %d %d\n",s,tainted,uid, euid);
+#endif
+    if (tainted && (!euid || euid != uid || egid != gid)) {
+       if (!unsafe)
+           fatal("%s", s);
+       else if (dowarn)
+           warn("%s", s);
+    }
+}
+
+taintenv()
+{
+    register STR *envstr;
+
+    envstr = hfetch(stab_hash(envstab),"PATH",4,FALSE);
+    if (envstr == &str_undef || envstr->str_tainted) {
+       tainted = 1;
+       if (envstr->str_tainted == 2)
+           taintproper("Insecure directory in PATH");
+       else
+           taintproper("Insecure PATH");
+    }
+    envstr = hfetch(stab_hash(envstab),"IFS",3,FALSE);
+    if (envstr != &str_undef && envstr->str_tainted) {
+       tainted = 1;
+       taintproper("Insecure IFS");
+    }
+}
+#endif /* TAINT */