perl 4.0 patch 36: (combined patch)
[p5sagit/p5-mst-13.2.git] / str.c.orig
1 /* $RCSfile: str.c,v $$Revision: 4.0.1.6 $$Date: 92/06/11 21:14:21 $
2  *
3  *    Copyright (c) 1991, Larry Wall
4  *
5  *    You may distribute under the terms of either the GNU General Public
6  *    License or the Artistic License, as specified in the README file.
7  *
8  * $Log:        str.c,v $
9  * Revision 4.0.1.6  92/06/11  21:14:21  lwall
10  * patch34: quotes containing subscripts containing variables didn't parse right
11  * 
12  * Revision 4.0.1.5  92/06/08  15:40:43  lwall
13  * patch20: removed implicit int declarations on functions
14  * patch20: Perl now distinguishes overlapped copies from non-overlapped
15  * patch20: paragraph mode now skips extra newlines automatically
16  * patch20: fixed memory leak in doube-quote interpretation
17  * patch20: made /\$$foo/ look for literal '$foo'
18  * patch20: "$var{$foo'bar}" didn't scan subscript correctly
19  * patch20: a splice on non-existent array elements could dump core
20  * patch20: running taintperl explicitly now does checks even if $< == $>
21  * 
22  * Revision 4.0.1.4  91/11/05  18:40:51  lwall
23  * patch11: $foo .= <BAR> could overrun malloced memory
24  * patch11: \$ didn't always make it through double-quoter to regexp routines
25  * patch11: prepared for ctype implementations that don't define isascii()
26  * 
27  * Revision 4.0.1.3  91/06/10  01:27:54  lwall
28  * patch10: $) and $| incorrectly handled in run-time patterns
29  * 
30  * Revision 4.0.1.2  91/06/07  11:58:13  lwall
31  * patch4: new copyright notice
32  * patch4: taint check on undefined string could cause core dump
33  * 
34  * Revision 4.0.1.1  91/04/12  09:15:30  lwall
35  * patch1: fixed undefined environ problem
36  * patch1: substr($ENV{"PATH"},0,0) = "/foo:" didn't modify environment
37  * patch1: $foo .= <BAR> could cause core dump for certain lengths of $foo
38  * 
39  * Revision 4.0  91/03/20  01:39:55  lwall
40  * 4.0 baseline.
41  * 
42  */
43
44 #include "EXTERN.h"
45 #include "perl.h"
46 #include "perly.h"
47
48 static void ucase();
49 static void lcase();
50
51 #ifndef str_get
52 char *
53 str_get(str)
54 STR *str;
55 {
56 #ifdef TAINT
57     tainted |= str->str_tainted;
58 #endif
59     return str->str_pok ? str->str_ptr : str_2ptr(str);
60 }
61 #endif
62
63 /* dlb ... guess we have a "crippled cc".
64  * dlb the following functions are usually macros.
65  */
66 #ifndef str_true
67 int
68 str_true(Str)
69 STR *Str;
70 {
71         if (Str->str_pok) {
72             if (*Str->str_ptr > '0' ||
73               Str->str_cur > 1 ||
74               (Str->str_cur && *Str->str_ptr != '0'))
75                 return 1;
76             return 0;
77         }
78         if (Str->str_nok)
79                 return (Str->str_u.str_nval != 0.0);
80         return 0;
81 }
82 #endif /* str_true */
83
84 #ifndef str_gnum
85 double str_gnum(Str)
86 STR *Str;
87 {
88 #ifdef TAINT
89         tainted |= Str->str_tainted;
90 #endif /* TAINT*/
91         if (Str->str_nok)
92                 return Str->str_u.str_nval;
93         return str_2num(Str);
94 }
95 #endif /* str_gnum */
96 /* dlb ... end of crutch */
97
98 char *
99 str_grow(str,newlen)
100 register STR *str;
101 #ifndef DOSISH
102 register int newlen;
103 #else
104 unsigned long newlen;
105 #endif
106 {
107     register char *s = str->str_ptr;
108
109 #ifdef MSDOS
110     if (newlen >= 0x10000) {
111         fprintf(stderr, "Allocation too large: %lx\n", newlen);
112         exit(1);
113     }
114 #endif /* MSDOS */
115     if (str->str_state == SS_INCR) {            /* data before str_ptr? */
116         str->str_len += str->str_u.str_useful;
117         str->str_ptr -= str->str_u.str_useful;
118         str->str_u.str_useful = 0L;
119         Move(s, str->str_ptr, str->str_cur+1, char);
120         s = str->str_ptr;
121         str->str_state = SS_NORM;                       /* normal again */
122         if (newlen > str->str_len)
123             newlen += 10 * (newlen - str->str_cur); /* avoid copy each time */
124     }
125     if (newlen > str->str_len) {                /* need more room? */
126         if (str->str_len)
127             Renew(s,newlen,char);
128         else
129             New(703,s,newlen,char);
130         str->str_ptr = s;
131         str->str_len = newlen;
132     }
133     return s;
134 }
135
136 void
137 str_numset(str,num)
138 register STR *str;
139 double num;
140 {
141     if (str->str_pok) {
142         str->str_pok = 0;       /* invalidate pointer */
143         if (str->str_state == SS_INCR)
144             Str_Grow(str,0);
145     }
146     str->str_u.str_nval = num;
147     str->str_state = SS_NORM;
148     str->str_nok = 1;                   /* validate number */
149 #ifdef TAINT
150     str->str_tainted = tainted;
151 #endif
152 }
153
154 char *
155 str_2ptr(str)
156 register STR *str;
157 {
158     register char *s;
159     int olderrno;
160
161     if (!str)
162         return "";
163     if (str->str_nok) {
164         STR_GROW(str, 30);
165         s = str->str_ptr;
166         olderrno = errno;       /* some Xenix systems wipe out errno here */
167 #if defined(scs) && defined(ns32000)
168         gcvt(str->str_u.str_nval,20,s);
169 #else
170 #ifdef apollo
171         if (str->str_u.str_nval == 0.0)
172             (void)strcpy(s,"0");
173         else
174 #endif /*apollo*/
175         (void)sprintf(s,"%.20g",str->str_u.str_nval);
176 #endif /*scs*/
177         errno = olderrno;
178         while (*s) s++;
179 #ifdef hcx
180         if (s[-1] == '.')
181             s--;
182 #endif
183     }
184     else {
185         if (str == &str_undef)
186             return No;
187         if (dowarn)
188             warn("Use of uninitialized variable");
189         STR_GROW(str, 30);
190         s = str->str_ptr;
191     }
192     *s = '\0';
193     str->str_cur = s - str->str_ptr;
194     str->str_pok = 1;
195 #ifdef DEBUGGING
196     if (debug & 32)
197         fprintf(stderr,"0x%lx ptr(%s)\n",str,str->str_ptr);
198 #endif
199     return str->str_ptr;
200 }
201
202 double
203 str_2num(str)
204 register STR *str;
205 {
206     if (!str)
207         return 0.0;
208     if (str->str_state == SS_INCR)
209         Str_Grow(str,0);       /* just force copy down */
210     str->str_state = SS_NORM;
211     if (str->str_len && str->str_pok)
212         str->str_u.str_nval = atof(str->str_ptr);
213     else  {
214         if (str == &str_undef)
215             return 0.0;
216         if (dowarn)
217             warn("Use of uninitialized variable");
218         str->str_u.str_nval = 0.0;
219     }
220     str->str_nok = 1;
221 #ifdef DEBUGGING
222     if (debug & 32)
223         fprintf(stderr,"0x%lx num(%g)\n",str,str->str_u.str_nval);
224 #endif
225     return str->str_u.str_nval;
226 }
227
228 /* Note: str_sset() should not be called with a source string that needs
229  * be reused, since it may destroy the source string if it is marked
230  * as temporary.
231  */
232
233 void
234 str_sset(dstr,sstr)
235 STR *dstr;
236 register STR *sstr;
237 {
238 #ifdef TAINT
239     if (sstr)
240         tainted |= sstr->str_tainted;
241 #endif
242     if (sstr == dstr || dstr == &str_undef)
243         return;
244     if (!sstr)
245         dstr->str_pok = dstr->str_nok = 0;
246     else if (sstr->str_pok) {
247
248         /*
249          * Check to see if we can just swipe the string.  If so, it's a
250          * possible small lose on short strings, but a big win on long ones.
251          * It might even be a win on short strings if dstr->str_ptr
252          * has to be allocated and sstr->str_ptr has to be freed.
253          */
254
255         if (sstr->str_pok & SP_TEMP) {          /* slated for free anyway? */
256             if (dstr->str_ptr) {
257                 if (dstr->str_state == SS_INCR)
258                     dstr->str_ptr -= dstr->str_u.str_useful;
259                 Safefree(dstr->str_ptr);
260             }
261             dstr->str_ptr = sstr->str_ptr;
262             dstr->str_len = sstr->str_len;
263             dstr->str_cur = sstr->str_cur;
264             dstr->str_state = sstr->str_state;
265             dstr->str_pok = sstr->str_pok & ~SP_TEMP;
266 #ifdef TAINT
267             dstr->str_tainted = sstr->str_tainted;
268 #endif
269             sstr->str_ptr = Nullch;
270             sstr->str_len = 0;
271             sstr->str_pok = 0;                  /* wipe out any weird flags */
272             sstr->str_state = 0;                /* so sstr frees uneventfully */
273         }
274         else {                                  /* have to copy actual string */
275             if (dstr->str_ptr) {
276                 if (dstr->str_state == SS_INCR) {
277                         Str_Grow(dstr,0);
278                 }
279             }
280             str_nset(dstr,sstr->str_ptr,sstr->str_cur);
281         }
282         /*SUPPRESS 560*/
283         if (dstr->str_nok = sstr->str_nok)
284             dstr->str_u.str_nval = sstr->str_u.str_nval;
285         else {
286 #ifdef STRUCTCOPY
287             dstr->str_u = sstr->str_u;
288 #else
289             dstr->str_u.str_nval = sstr->str_u.str_nval;
290 #endif
291             if (dstr->str_cur == sizeof(STBP)) {
292                 char *tmps = dstr->str_ptr;
293
294                 if (*tmps == 'S' && bcmp(tmps,"StB",4) == 0) {
295                     if (dstr->str_magic && dstr->str_magic->str_rare == 'X') {
296                         str_free(dstr->str_magic);
297                         dstr->str_magic = Nullstr;
298                     }
299                     if (!dstr->str_magic) {
300                         dstr->str_magic = str_smake(sstr->str_magic);
301                         dstr->str_magic->str_rare = 'X';
302                     }
303                 }
304             }
305         }
306     }
307     else if (sstr->str_nok)
308         str_numset(dstr,sstr->str_u.str_nval);
309     else {
310         if (dstr->str_state == SS_INCR)
311             Str_Grow(dstr,0);       /* just force copy down */
312
313 #ifdef STRUCTCOPY
314         dstr->str_u = sstr->str_u;
315 #else
316         dstr->str_u.str_nval = sstr->str_u.str_nval;
317 #endif
318         dstr->str_pok = dstr->str_nok = 0;
319     }
320 }
321
322 void
323 str_nset(str,ptr,len)
324 register STR *str;
325 register char *ptr;
326 register STRLEN len;
327 {
328     if (str == &str_undef)
329         return;
330     STR_GROW(str, len + 1);
331     if (ptr)
332         Move(ptr,str->str_ptr,len,char);
333     str->str_cur = len;
334     *(str->str_ptr+str->str_cur) = '\0';
335     str->str_nok = 0;           /* invalidate number */
336     str->str_pok = 1;           /* validate pointer */
337 #ifdef TAINT
338     str->str_tainted = tainted;
339 #endif
340 }
341
342 void
343 str_set(str,ptr)
344 register STR *str;
345 register char *ptr;
346 {
347     register STRLEN len;
348
349     if (str == &str_undef)
350         return;
351     if (!ptr)
352         ptr = "";
353     len = strlen(ptr);
354     STR_GROW(str, len + 1);
355     Move(ptr,str->str_ptr,len+1,char);
356     str->str_cur = len;
357     str->str_nok = 0;           /* invalidate number */
358     str->str_pok = 1;           /* validate pointer */
359 #ifdef TAINT
360     str->str_tainted = tainted;
361 #endif
362 }
363
364 void
365 str_chop(str,ptr)       /* like set but assuming ptr is in str */
366 register STR *str;
367 register char *ptr;
368 {
369     register STRLEN delta;
370
371     if (!ptr || !(str->str_pok))
372         return;
373     delta = ptr - str->str_ptr;
374     str->str_len -= delta;
375     str->str_cur -= delta;
376     str->str_ptr += delta;
377     if (str->str_state == SS_INCR)
378         str->str_u.str_useful += delta;
379     else {
380         str->str_u.str_useful = delta;
381         str->str_state = SS_INCR;
382     }
383     str->str_nok = 0;           /* invalidate number */
384     str->str_pok = 1;           /* validate pointer (and unstudy str) */
385 }
386
387 void
388 str_ncat(str,ptr,len)
389 register STR *str;
390 register char *ptr;
391 register STRLEN len;
392 {
393     if (str == &str_undef)
394         return;
395     if (!(str->str_pok))
396         (void)str_2ptr(str);
397     STR_GROW(str, str->str_cur + len + 1);
398     Move(ptr,str->str_ptr+str->str_cur,len,char);
399     str->str_cur += len;
400     *(str->str_ptr+str->str_cur) = '\0';
401     str->str_nok = 0;           /* invalidate number */
402     str->str_pok = 1;           /* validate pointer */
403 #ifdef TAINT
404     str->str_tainted |= tainted;
405 #endif
406 }
407
408 void
409 str_scat(dstr,sstr)
410 STR *dstr;
411 register STR *sstr;
412 {
413     if (!sstr)
414         return;
415 #ifdef TAINT
416     tainted |= sstr->str_tainted;
417 #endif
418     if (!(sstr->str_pok))
419         (void)str_2ptr(sstr);
420     if (sstr)
421         str_ncat(dstr,sstr->str_ptr,sstr->str_cur);
422 }
423
424 void
425 str_cat(str,ptr)
426 register STR *str;
427 register char *ptr;
428 {
429     register STRLEN len;
430
431     if (str == &str_undef)
432         return;
433     if (!ptr)
434         return;
435     if (!(str->str_pok))
436         (void)str_2ptr(str);
437     len = strlen(ptr);
438     STR_GROW(str, str->str_cur + len + 1);
439     Move(ptr,str->str_ptr+str->str_cur,len+1,char);
440     str->str_cur += len;
441     str->str_nok = 0;           /* invalidate number */
442     str->str_pok = 1;           /* validate pointer */
443 #ifdef TAINT
444     str->str_tainted |= tainted;
445 #endif
446 }
447
448 char *
449 str_append_till(str,from,fromend,delim,keeplist)
450 register STR *str;
451 register char *from;
452 register char *fromend;
453 register int delim;
454 char *keeplist;
455 {
456     register char *to;
457     register STRLEN len;
458
459     if (str == &str_undef)
460         return Nullch;
461     if (!from)
462         return Nullch;
463     len = fromend - from;
464     STR_GROW(str, str->str_cur + len + 1);
465     str->str_nok = 0;           /* invalidate number */
466     str->str_pok = 1;           /* validate pointer */
467     to = str->str_ptr+str->str_cur;
468     for (; from < fromend; from++,to++) {
469         if (*from == '\\' && from+1 < fromend && delim != '\\') {
470             if (!keeplist) {
471                 if (from[1] == delim || from[1] == '\\')
472                     from++;
473                 else
474                     *to++ = *from++;
475             }
476             else if (from[1] && index(keeplist,from[1]))
477                 *to++ = *from++;
478             else
479                 from++;
480         }
481         else if (*from == delim)
482             break;
483         *to = *from;
484     }
485     *to = '\0';
486     str->str_cur = to - str->str_ptr;
487     return from;
488 }
489
490 STR *
491 #ifdef LEAKTEST
492 str_new(x,len)
493 int x;
494 #else
495 str_new(len)
496 #endif
497 STRLEN len;
498 {
499     register STR *str;
500     
501     if (freestrroot) {
502         str = freestrroot;
503         freestrroot = str->str_magic;
504         str->str_magic = Nullstr;
505         str->str_state = SS_NORM;
506     }
507     else {
508         Newz(700+x,str,1,STR);
509     }
510     if (len)
511         STR_GROW(str, len + 1);
512     return str;
513 }
514
515 void
516 str_magic(str, stab, how, name, namlen)
517 register STR *str;
518 STAB *stab;
519 int how;
520 char *name;
521 STRLEN namlen;
522 {
523     if (str == &str_undef || str->str_magic)
524         return;
525     str->str_magic = Str_new(75,namlen);
526     str = str->str_magic;
527     str->str_u.str_stab = stab;
528     str->str_rare = how;
529     if (name)
530         str_nset(str,name,namlen);
531 }
532
533 void
534 str_insert(bigstr,offset,len,little,littlelen)
535 STR *bigstr;
536 STRLEN offset;
537 STRLEN len;
538 char *little;
539 STRLEN littlelen;
540 {
541     register char *big;
542     register char *mid;
543     register char *midend;
544     register char *bigend;
545     register int i;
546
547     if (bigstr == &str_undef)
548         return;
549     bigstr->str_nok = 0;
550     bigstr->str_pok = SP_VALID; /* disable possible screamer */
551
552     i = littlelen - len;
553     if (i > 0) {                        /* string might grow */
554         STR_GROW(bigstr, bigstr->str_cur + i + 1);
555         big = bigstr->str_ptr;
556         mid = big + offset + len;
557         midend = bigend = big + bigstr->str_cur;
558         bigend += i;
559         *bigend = '\0';
560         while (midend > mid)            /* shove everything down */
561             *--bigend = *--midend;
562         Move(little,big+offset,littlelen,char);
563         bigstr->str_cur += i;
564         STABSET(bigstr);
565         return;
566     }
567     else if (i == 0) {
568         Move(little,bigstr->str_ptr+offset,len,char);
569         STABSET(bigstr);
570         return;
571     }
572
573     big = bigstr->str_ptr;
574     mid = big + offset;
575     midend = mid + len;
576     bigend = big + bigstr->str_cur;
577
578     if (midend > bigend)
579         fatal("panic: str_insert");
580
581     if (mid - big > bigend - midend) {  /* faster to shorten from end */
582         if (littlelen) {
583             Move(little, mid, littlelen,char);
584             mid += littlelen;
585         }
586         i = bigend - midend;
587         if (i > 0) {
588             Move(midend, mid, i,char);
589             mid += i;
590         }
591         *mid = '\0';
592         bigstr->str_cur = mid - big;
593     }
594     /*SUPPRESS 560*/
595     else if (i = mid - big) {   /* faster from front */
596         midend -= littlelen;
597         mid = midend;
598         str_chop(bigstr,midend-i);
599         big += i;
600         while (i--)
601             *--midend = *--big;
602         if (littlelen)
603             Move(little, mid, littlelen,char);
604     }
605     else if (littlelen) {
606         midend -= littlelen;
607         str_chop(bigstr,midend);
608         Move(little,midend,littlelen,char);
609     }
610     else {
611         str_chop(bigstr,midend);
612     }
613     STABSET(bigstr);
614 }
615
616 /* make str point to what nstr did */
617
618 void
619 str_replace(str,nstr)
620 register STR *str;
621 register STR *nstr;
622 {
623     if (str == &str_undef)
624         return;
625     if (str->str_state == SS_INCR)
626         Str_Grow(str,0);        /* just force copy down */
627     if (nstr->str_state == SS_INCR)
628         Str_Grow(nstr,0);
629     if (str->str_ptr)
630         Safefree(str->str_ptr);
631     str->str_ptr = nstr->str_ptr;
632     str->str_len = nstr->str_len;
633     str->str_cur = nstr->str_cur;
634     str->str_pok = nstr->str_pok;
635     str->str_nok = nstr->str_nok;
636 #ifdef STRUCTCOPY
637     str->str_u = nstr->str_u;
638 #else
639     str->str_u.str_nval = nstr->str_u.str_nval;
640 #endif
641 #ifdef TAINT
642     str->str_tainted = nstr->str_tainted;
643 #endif
644     if (nstr->str_magic)
645         str_free(nstr->str_magic);
646     Safefree(nstr);
647 }
648
649 void
650 str_free(str)
651 register STR *str;
652 {
653     if (!str || str == &str_undef)
654         return;
655     if (str->str_state) {
656         if (str->str_state == SS_FREE)  /* already freed */
657             return;
658         if (str->str_state == SS_INCR && !(str->str_pok & 2)) {
659             str->str_ptr -= str->str_u.str_useful;
660             str->str_len += str->str_u.str_useful;
661         }
662     }
663     if (str->str_magic)
664         str_free(str->str_magic);
665     str->str_magic = freestrroot;
666 #ifdef LEAKTEST
667     if (str->str_len) {
668         Safefree(str->str_ptr);
669         str->str_ptr = Nullch;
670     }
671     if ((str->str_pok & SP_INTRP) && str->str_u.str_args)
672         arg_free(str->str_u.str_args);
673     Safefree(str);
674 #else /* LEAKTEST */
675     if (str->str_len) {
676         if (str->str_len > 127) {       /* next user not likely to want more */
677             Safefree(str->str_ptr);     /* so give it back to malloc */
678             str->str_ptr = Nullch;
679             str->str_len = 0;
680         }
681         else
682             str->str_ptr[0] = '\0';
683     }
684     if ((str->str_pok & SP_INTRP) && str->str_u.str_args)
685         arg_free(str->str_u.str_args);
686     str->str_cur = 0;
687     str->str_nok = 0;
688     str->str_pok = 0;
689     str->str_state = SS_FREE;
690 #ifdef TAINT
691     str->str_tainted = 0;
692 #endif
693     freestrroot = str;
694 #endif /* LEAKTEST */
695 }
696
697 STRLEN
698 str_len(str)
699 register STR *str;
700 {
701     if (!str)
702         return 0;
703     if (!(str->str_pok))
704         (void)str_2ptr(str);
705     if (str->str_ptr)
706         return str->str_cur;
707     else
708         return 0;
709 }
710
711 int
712 str_eq(str1,str2)
713 register STR *str1;
714 register STR *str2;
715 {
716     if (!str1 || str1 == &str_undef)
717         return (str2 == Nullstr || str2 == &str_undef || !str2->str_cur);
718     if (!str2 || str2 == &str_undef)
719         return !str1->str_cur;
720
721     if (!str1->str_pok)
722         (void)str_2ptr(str1);
723     if (!str2->str_pok)
724         (void)str_2ptr(str2);
725
726     if (str1->str_cur != str2->str_cur)
727         return 0;
728
729     return !bcmp(str1->str_ptr, str2->str_ptr, str1->str_cur);
730 }
731
732 int
733 str_cmp(str1,str2)
734 register STR *str1;
735 register STR *str2;
736 {
737     int retval;
738
739     if (!str1 || str1 == &str_undef)
740         return (str2 == Nullstr || str2 == &str_undef || !str2->str_cur)?0:-1;
741     if (!str2 || str2 == &str_undef)
742         return str1->str_cur != 0;
743
744     if (!str1->str_pok)
745         (void)str_2ptr(str1);
746     if (!str2->str_pok)
747         (void)str_2ptr(str2);
748
749     if (str1->str_cur < str2->str_cur) {
750         /*SUPPRESS 560*/
751         if (retval = memcmp(str1->str_ptr, str2->str_ptr, str1->str_cur))
752             return retval < 0 ? -1 : 1;
753         else
754             return -1;
755     }
756     /*SUPPRESS 560*/
757     else if (retval = memcmp(str1->str_ptr, str2->str_ptr, str2->str_cur))
758         return retval < 0 ? -1 : 1;
759     else if (str1->str_cur == str2->str_cur)
760         return 0;
761     else
762         return 1;
763 }
764
765 char *
766 str_gets(str,fp,append)
767 register STR *str;
768 register FILE *fp;
769 int append;
770 {
771     register char *bp;          /* we're going to steal some values */
772     register int cnt;           /*  from the stdio struct and put EVERYTHING */
773     register STDCHAR *ptr;      /*   in the innermost loop into registers */
774     register int newline = rschar;/* (assuming >= 6 registers) */
775     int i;
776     STRLEN bpx;
777     int shortbuffered;
778
779     if (str == &str_undef)
780         return Nullch;
781     if (rspara) {               /* have to do this both before and after */
782         do {                    /* to make sure file boundaries work right */
783             i = getc(fp);
784             if (i != '\n') {
785                 ungetc(i,fp);
786                 break;
787             }
788         } while (i != EOF);
789     }
790 #ifdef STDSTDIO         /* Here is some breathtakingly efficient cheating */
791     cnt = fp->_cnt;                     /* get count into register */
792     str->str_nok = 0;                   /* invalidate number */
793     str->str_pok = 1;                   /* validate pointer */
794     if (str->str_len - append <= cnt + 1) { /* make sure we have the room */
795         if (cnt > 80 && str->str_len > append) {
796             shortbuffered = cnt - str->str_len + append + 1;
797             cnt -= shortbuffered;
798         }
799         else {
800             shortbuffered = 0;
801             STR_GROW(str, append+cnt+2);/* (remembering cnt can be -1) */
802         }
803     }
804     else
805         shortbuffered = 0;
806     bp = str->str_ptr + append;         /* move these two too to registers */
807     ptr = fp->_ptr;
808     for (;;) {
809       screamer:
810         while (--cnt >= 0) {                    /* this */      /* eat */
811             if ((*bp++ = *ptr++) == newline)    /* really */    /* dust */
812                 goto thats_all_folks;           /* screams */   /* sed :-) */ 
813         }
814         
815         if (shortbuffered) {                    /* oh well, must extend */
816             cnt = shortbuffered;
817             shortbuffered = 0;
818             bpx = bp - str->str_ptr;    /* prepare for possible relocation */
819             str->str_cur = bpx;
820             STR_GROW(str, str->str_len + append + cnt + 2);
821             bp = str->str_ptr + bpx;    /* reconstitute our pointer */
822             continue;
823         }
824
825         fp->_cnt = cnt;                 /* deregisterize cnt and ptr */
826         fp->_ptr = ptr;
827         i = _filbuf(fp);                /* get more characters */
828         cnt = fp->_cnt;
829         ptr = fp->_ptr;                 /* reregisterize cnt and ptr */
830
831         bpx = bp - str->str_ptr;        /* prepare for possible relocation */
832         str->str_cur = bpx;
833         STR_GROW(str, bpx + cnt + 2);
834         bp = str->str_ptr + bpx;        /* reconstitute our pointer */
835
836         if (i == newline) {             /* all done for now? */
837             *bp++ = i;
838             goto thats_all_folks;
839         }
840         else if (i == EOF)              /* all done for ever? */
841             goto thats_really_all_folks;
842         *bp++ = i;                      /* now go back to screaming loop */
843     }
844
845 thats_all_folks:
846     if (rslen > 1 && (bp - str->str_ptr < rslen || bcmp(bp - rslen, rs, rslen)))
847         goto screamer;  /* go back to the fray */
848 thats_really_all_folks:
849     if (shortbuffered)
850         cnt += shortbuffered;
851     fp->_cnt = cnt;                     /* put these back or we're in trouble */
852     fp->_ptr = ptr;
853     *bp = '\0';
854     str->str_cur = bp - str->str_ptr;   /* set length */
855
856 #else /* !STDSTDIO */   /* The big, slow, and stupid way */
857
858     {
859         static char buf[8192];
860         char * bpe = buf + sizeof(buf) - 3;
861
862 screamer:
863         bp = buf;
864         while ((i = getc(fp)) != EOF && (*bp++ = i) != newline && bp < bpe) ;
865
866         *bp = '\0';
867         if (append)
868             str_cat(str, buf);
869         else
870             str_set(str, buf);
871         if (i != EOF                    /* joy */
872             &&
873             (i != newline
874              ||
875              (rslen > 1
876               &&
877               (str->str_cur < rslen
878                ||
879                bcmp(str->str_ptr + str->str_cur - rslen, rs, rslen)
880               )
881              )
882             )
883            )
884         {
885             append = -1;
886             goto screamer;
887         }
888     }
889
890 #endif /* STDSTDIO */
891
892     if (rspara) {
893         while (i != EOF) {
894             i = getc(fp);
895             if (i != '\n') {
896                 ungetc(i,fp);
897                 break;
898             }
899         }
900     }
901     return str->str_cur - append ? str->str_ptr : Nullch;
902 }
903
904 ARG *
905 parselist(str)
906 STR *str;
907 {
908     register CMD *cmd;
909     register ARG *arg;
910     CMD *oldcurcmd = curcmd;
911     int oldperldb = perldb;
912     int retval;
913
914     perldb = 0;
915     str_sset(linestr,str);
916     in_eval++;
917     oldoldbufptr = oldbufptr = bufptr = str_get(linestr);
918     bufend = bufptr + linestr->str_cur;
919     if (++loop_ptr >= loop_max) {
920         loop_max += 128;
921         Renew(loop_stack, loop_max, struct loop);
922     }
923     loop_stack[loop_ptr].loop_label = "_EVAL_";
924     loop_stack[loop_ptr].loop_sp = 0;
925 #ifdef DEBUGGING
926     if (debug & 4) {
927         deb("(Pushing label #%d _EVAL_)\n", loop_ptr);
928     }
929 #endif
930     if (setjmp(loop_stack[loop_ptr].loop_env)) {
931         in_eval--;
932         loop_ptr--;
933         perldb = oldperldb;
934         fatal("%s\n",stab_val(stabent("@",TRUE))->str_ptr);
935     }
936 #ifdef DEBUGGING
937     if (debug & 4) {
938         char *tmps = loop_stack[loop_ptr].loop_label;
939         deb("(Popping label #%d %s)\n",loop_ptr,
940             tmps ? tmps : "" );
941     }
942 #endif
943     loop_ptr--;
944     error_count = 0;
945     curcmd = &compiling;
946     curcmd->c_line = oldcurcmd->c_line;
947     retval = yyparse();
948     curcmd = oldcurcmd;
949     perldb = oldperldb;
950     in_eval--;
951     if (retval || error_count)
952         fatal("Invalid component in string or format");
953     cmd = eval_root;
954     arg = cmd->c_expr;
955     if (cmd->c_type != C_EXPR || cmd->c_next || arg->arg_type != O_LIST)
956         fatal("panic: error in parselist %d %x %d", cmd->c_type,
957           cmd->c_next, arg ? arg->arg_type : -1);
958     cmd->c_expr = Nullarg;
959     cmd_free(cmd);
960     eval_root = Nullcmd;
961     return arg;
962 }
963
964 void
965 intrpcompile(src)
966 STR *src;
967 {
968     register char *s = str_get(src);
969     register char *send = s + src->str_cur;
970     register STR *str;
971     register char *t;
972     STR *toparse;
973     STRLEN len;
974     register int brackets;
975     register char *d;
976     STAB *stab;
977     char *checkpoint;
978     int sawcase = 0;
979
980     toparse = Str_new(76,0);
981     str = Str_new(77,0);
982
983     str_nset(str,"",0);
984     str_nset(toparse,"",0);
985     t = s;
986     while (s < send) {
987         if (*s == '\\' && s[1] && index("$@[{\\]}lLuUE",s[1])) {
988             str_ncat(str, t, s - t);
989             ++s;
990             if (isALPHA(*s)) {
991                 str_ncat(str, "$c", 2);
992                 sawcase = (*s != 'E');
993             }
994             else {
995                 if (*nointrp) {         /* in a regular expression */
996                     if (*s == '@')      /* always strip \@ */ /*SUPPRESS 530*/
997                         ;
998                     else                /* don't strip \\, \[, \{ etc. */
999                         str_ncat(str,s-1,1);
1000                 }
1001                 str_ncat(str, "$b", 2);
1002             }
1003             str_ncat(str, s, 1);
1004             ++s;
1005             t = s;
1006         }
1007         else if (*s == '$' && s+1 < send && *nointrp && index(nointrp,s[1])) {
1008             str_ncat(str, t, s - t);
1009             str_ncat(str, "$b", 2);
1010             str_ncat(str, s, 2);
1011             s += 2;
1012             t = s;
1013         }
1014         else if ((*s == '@' || *s == '$') && s+1 < send) {
1015             str_ncat(str,t,s-t);
1016             t = s;
1017             if (*s == '$' && s[1] == '#' && (isALPHA(s[2]) || s[2] == '_'))
1018                 s++;
1019             s = scanident(s,send,tokenbuf);
1020             if (*t == '@' &&
1021               (!(stab = stabent(tokenbuf,FALSE)) || 
1022                  (*s == '{' ? !stab_xhash(stab) : !stab_xarray(stab)) )) {
1023                 str_ncat(str,"@",1);
1024                 s = ++t;
1025                 continue;       /* grandfather @ from old scripts */
1026             }
1027             str_ncat(str,"$a",2);
1028             str_ncat(toparse,",",1);
1029             if (t[1] != '{' && (*s == '['  || *s == '{' /* }} */ ) &&
1030               (stab = stabent(tokenbuf,FALSE)) &&
1031               ((*s == '[') ? (stab_xarray(stab) != 0) : (stab_xhash(stab) != 0)) ) {
1032                 brackets = 0;
1033                 checkpoint = s;
1034                 do {
1035                     switch (*s) {
1036                     case '[':
1037                         brackets++;
1038                         break;
1039                     case '{':
1040                         brackets++;
1041                         break;
1042                     case ']':
1043                         brackets--;
1044                         break;
1045                     case '}':
1046                         brackets--;
1047                         break;
1048                     case '$':
1049                     case '%':
1050                     case '@':
1051                     case '&':
1052                     case '*':
1053                         s = scanident(s,send,tokenbuf);
1054                         continue;
1055                     case '\'':
1056                     case '"':
1057                         /*SUPPRESS 68*/
1058                         s = cpytill(tokenbuf,s+1,send,*s,&len);
1059                         if (s >= send)
1060                             fatal("Unterminated string");
1061                         break;
1062                     }
1063                     s++;
1064                 } while (brackets > 0 && s < send);
1065                 if (s > send)
1066                     fatal("Unmatched brackets in string");
1067                 if (*nointrp) {         /* we're in a regular expression */
1068                     d = checkpoint;
1069                     if (*d == '{' && s[-1] == '}') {    /* maybe {n,m} */
1070                         ++d;
1071                         if (isDIGIT(*d)) {      /* matches /^{\d,?\d*}$/ */
1072                             if (*++d == ',')
1073                                 ++d;
1074                             while (isDIGIT(*d))
1075                                 d++;
1076                             if (d == s - 1)
1077                                 s = checkpoint;         /* Is {n,m}! Backoff! */
1078                         }
1079                     }
1080                     else if (*d == '[' && s[-1] == ']') { /* char class? */
1081                         int weight = 2;         /* let's weigh the evidence */
1082                         char seen[256];
1083                         unsigned char un_char = 0, last_un_char;
1084
1085                         Zero(seen,256,char);
1086                         *--s = '\0';
1087                         if (d[1] == '^')
1088                             weight += 150;
1089                         else if (d[1] == '$')
1090                             weight -= 3;
1091                         if (isDIGIT(d[1])) {
1092                             if (d[2]) {
1093                                 if (isDIGIT(d[2]) && !d[3])
1094                                     weight -= 10;
1095                             }
1096                             else
1097                                 weight -= 100;
1098                         }
1099                         for (d++; d < s; d++) {
1100                             last_un_char = un_char;
1101                             un_char = (unsigned char)*d;
1102                             switch (*d) {
1103                             case '&':
1104                             case '$':
1105                                 weight -= seen[un_char] * 10;
1106                                 if (isALNUM(d[1])) {
1107                                     d = scanident(d,s,tokenbuf);
1108                                     if (stabent(tokenbuf,FALSE))
1109                                         weight -= 100;
1110                                     else
1111                                         weight -= 10;
1112                                 }
1113                                 else if (*d == '$' && d[1] &&
1114                                   index("[#!%*<>()-=",d[1])) {
1115                                     if (!d[2] || /*{*/ index("])} =",d[2]))
1116                                         weight -= 10;
1117                                     else
1118                                         weight -= 1;
1119                                 }
1120                                 break;
1121                             case '\\':
1122                                 un_char = 254;
1123                                 if (d[1]) {
1124                                     if (index("wds",d[1]))
1125                                         weight += 100;
1126                                     else if (seen['\''] || seen['"'])
1127                                         weight += 1;
1128                                     else if (index("rnftb",d[1]))
1129                                         weight += 40;
1130                                     else if (isDIGIT(d[1])) {
1131                                         weight += 40;
1132                                         while (d[1] && isDIGIT(d[1]))
1133                                             d++;
1134                                     }
1135                                 }
1136                                 else
1137                                     weight += 100;
1138                                 break;
1139                             case '-':
1140                                 if (last_un_char < (unsigned char) d[1]
1141                                   || d[1] == '\\') {
1142                                     if (index("aA01! ",last_un_char))
1143                                         weight += 30;
1144                                     if (index("zZ79~",d[1]))
1145                                         weight += 30;
1146                                 }
1147                                 else
1148                                     weight -= 1;
1149                             default:
1150                                 if (isALPHA(*d) && d[1] && isALPHA(d[1])) {
1151                                     bufptr = d;
1152                                     if (yylex() != WORD)
1153                                         weight -= 150;
1154                                     d = bufptr;
1155                                 }
1156                                 if (un_char == last_un_char + 1)
1157                                     weight += 5;
1158                                 weight -= seen[un_char];
1159                                 break;
1160                             }
1161                             seen[un_char]++;
1162                         }
1163 #ifdef DEBUGGING
1164                         if (debug & 512)
1165                             fprintf(stderr,"[%s] weight %d\n",
1166                               checkpoint+1,weight);
1167 #endif
1168                         *s++ = ']';
1169                         if (weight >= 0)        /* probably a character class */
1170                             s = checkpoint;
1171                     }
1172                 }
1173             }
1174             if (*t == '@')
1175                 str_ncat(toparse, "join($\",", 8);
1176             if (t[1] == '{' && s[-1] == '}') {
1177                 str_ncat(toparse, t, 1);
1178                 str_ncat(toparse, t+2, s - t - 3);
1179             }
1180             else
1181                 str_ncat(toparse, t, s - t);
1182             if (*t == '@')
1183                 str_ncat(toparse, ")", 1);
1184             t = s;
1185         }
1186         else
1187             s++;
1188     }
1189     str_ncat(str,t,s-t);
1190     if (sawcase)
1191         str_ncat(str, "$cE", 3);
1192     if (toparse->str_ptr && *toparse->str_ptr == ',') {
1193         *toparse->str_ptr = '(';
1194         str_ncat(toparse,",$$);",5);
1195         str->str_u.str_args = parselist(toparse);
1196         str->str_u.str_args->arg_len--;         /* ignore $$ reference */
1197     }
1198     else
1199         str->str_u.str_args = Nullarg;
1200     str_free(toparse);
1201     str->str_pok |= SP_INTRP;
1202     str->str_nok = 0;
1203     str_replace(src,str);
1204 }
1205
1206 STR *
1207 interp(str,src,sp)
1208 register STR *str;
1209 STR *src;
1210 int sp;
1211 {
1212     register char *s;
1213     register char *t;
1214     register char *send;
1215     register STR **elem;
1216     int docase = 0;
1217     int l = 0;
1218     int u = 0;
1219     int L = 0;
1220     int U = 0;
1221
1222     if (str == &str_undef)
1223         return Nullstr;
1224     if (!(src->str_pok & SP_INTRP)) {
1225         int oldsave = savestack->ary_fill;
1226
1227         (void)savehptr(&curstash);
1228         curstash = curcmd->c_stash;     /* so stabent knows right package */
1229         intrpcompile(src);
1230         restorelist(oldsave);
1231     }
1232     s = src->str_ptr;           /* assumed valid since str_pok set */
1233     t = s;
1234     send = s + src->str_cur;
1235
1236     if (src->str_u.str_args) {
1237         (void)eval(src->str_u.str_args,G_ARRAY,sp);
1238         /* Assuming we have correct # of args */
1239         elem = stack->ary_array + sp;
1240     }
1241
1242     str_nset(str,"",0);
1243     while (s < send) {
1244         if (*s == '$' && s+1 < send) {
1245             if (s-t > 0)
1246                 str_ncat(str,t,s-t);
1247             switch(*++s) {
1248             default:
1249                 fatal("panic: unknown interp cookie\n");
1250                 break;
1251             case 'a':
1252                 str_scat(str,*++elem);
1253                 break;
1254             case 'b':
1255                 str_ncat(str,++s,1);
1256                 break;
1257             case 'c':
1258                 if (docase && str->str_cur >= docase) {
1259                     char *b = str->str_ptr + --docase;
1260
1261                     if (L)
1262                         lcase(b, str->str_ptr + str->str_cur);
1263                     else if (U)
1264                         ucase(b, str->str_ptr + str->str_cur);
1265
1266                     if (u)      /* note that l & u are independent of L & U */
1267                         ucase(b, b+1);
1268                     else if (l)
1269                         lcase(b, b+1);
1270                     l = u = 0;
1271                 }
1272                 docase = str->str_cur + 1;
1273                 switch (*++s) {
1274                 case 'u':
1275                     u = 1;
1276                     l = 0;
1277                     break;
1278                 case 'U':
1279                     U = 1;
1280                     L = 0;
1281                     break;
1282                 case 'l':
1283                     l = 1;
1284                     u = 0;
1285                     break;
1286                 case 'L':
1287                     L = 1;
1288                     U = 0;
1289                     break;
1290                 case 'E':
1291                     docase = L = U = l = u = 0;
1292                     break;
1293                 }
1294                 break;
1295             }
1296             t = ++s;
1297         }
1298         else
1299             s++;
1300     }
1301     if (s-t > 0)
1302         str_ncat(str,t,s-t);
1303     return str;
1304 }
1305
1306 static void
1307 ucase(s,send)
1308 register char *s;
1309 register char *send;
1310 {
1311     while (s < send) {
1312         if (isLOWER(*s))
1313             *s = toupper(*s);
1314         s++;
1315     }
1316 }
1317
1318 static void
1319 lcase(s,send)
1320 register char *s;
1321 register char *send;
1322 {
1323     while (s < send) {
1324         if (isUPPER(*s))
1325             *s = tolower(*s);
1326         s++;
1327     }
1328 }
1329
1330 void
1331 str_inc(str)
1332 register STR *str;
1333 {
1334     register char *d;
1335
1336     if (!str || str == &str_undef)
1337         return;
1338     if (str->str_nok) {
1339         str->str_u.str_nval += 1.0;
1340         str->str_pok = 0;
1341         return;
1342     }
1343     if (!str->str_pok || !*str->str_ptr) {
1344         str->str_u.str_nval = 1.0;
1345         str->str_nok = 1;
1346         str->str_pok = 0;
1347         return;
1348     }
1349     d = str->str_ptr;
1350     while (isALPHA(*d)) d++;
1351     while (isDIGIT(*d)) d++;
1352     if (*d) {
1353         str_numset(str,atof(str->str_ptr) + 1.0);  /* punt */
1354         return;
1355     }
1356     d--;
1357     while (d >= str->str_ptr) {
1358         if (isDIGIT(*d)) {
1359             if (++*d <= '9')
1360                 return;
1361             *(d--) = '0';
1362         }
1363         else {
1364             ++*d;
1365             if (isALPHA(*d))
1366                 return;
1367             *(d--) -= 'z' - 'a' + 1;
1368         }
1369     }
1370     /* oh,oh, the number grew */
1371     STR_GROW(str, str->str_cur + 2);
1372     str->str_cur++;
1373     for (d = str->str_ptr + str->str_cur; d > str->str_ptr; d--)
1374         *d = d[-1];
1375     if (isDIGIT(d[1]))
1376         *d = '1';
1377     else
1378         *d = d[1];
1379 }
1380
1381 void
1382 str_dec(str)
1383 register STR *str;
1384 {
1385     if (!str || str == &str_undef)
1386         return;
1387     if (str->str_nok) {
1388         str->str_u.str_nval -= 1.0;
1389         str->str_pok = 0;
1390         return;
1391     }
1392     if (!str->str_pok) {
1393         str->str_u.str_nval = -1.0;
1394         str->str_nok = 1;
1395         return;
1396     }
1397     str_numset(str,atof(str->str_ptr) - 1.0);
1398 }
1399
1400 /* Make a string that will exist for the duration of the expression
1401  * evaluation.  Actually, it may have to last longer than that, but
1402  * hopefully cmd_exec won't free it until it has been assigned to a
1403  * permanent location. */
1404
1405 static long tmps_size = -1;
1406
1407 STR *
1408 str_mortal(oldstr)
1409 STR *oldstr;
1410 {
1411     register STR *str = Str_new(78,0);
1412
1413     str_sset(str,oldstr);
1414     if (++tmps_max > tmps_size) {
1415         tmps_size = tmps_max;
1416         if (!(tmps_size & 127)) {
1417             if (tmps_size)
1418                 Renew(tmps_list, tmps_size + 128, STR*);
1419             else
1420                 New(702,tmps_list, 128, STR*);
1421         }
1422     }
1423     tmps_list[tmps_max] = str;
1424     if (str->str_pok)
1425         str->str_pok |= SP_TEMP;
1426     return str;
1427 }
1428
1429 /* same thing without the copying */
1430
1431 STR *
1432 str_2mortal(str)
1433 register STR *str;
1434 {
1435     if (!str || str == &str_undef)
1436         return str;
1437     if (++tmps_max > tmps_size) {
1438         tmps_size = tmps_max;
1439         if (!(tmps_size & 127)) {
1440             if (tmps_size)
1441                 Renew(tmps_list, tmps_size + 128, STR*);
1442             else
1443                 New(704,tmps_list, 128, STR*);
1444         }
1445     }
1446     tmps_list[tmps_max] = str;
1447     if (str->str_pok)
1448         str->str_pok |= SP_TEMP;
1449     return str;
1450 }
1451
1452 STR *
1453 str_make(s,len)
1454 char *s;
1455 STRLEN len;
1456 {
1457     register STR *str = Str_new(79,0);
1458
1459     if (!len)
1460         len = strlen(s);
1461     str_nset(str,s,len);
1462     return str;
1463 }
1464
1465 STR *
1466 str_nmake(n)
1467 double n;
1468 {
1469     register STR *str = Str_new(80,0);
1470
1471     str_numset(str,n);
1472     return str;
1473 }
1474
1475 /* make an exact duplicate of old */
1476
1477 STR *
1478 str_smake(old)
1479 register STR *old;
1480 {
1481     register STR *new = Str_new(81,0);
1482
1483     if (!old)
1484         return Nullstr;
1485     if (old->str_state == SS_FREE) {
1486         warn("semi-panic: attempt to dup freed string");
1487         return Nullstr;
1488     }
1489     if (old->str_state == SS_INCR && !(old->str_pok & 2))
1490         Str_Grow(old,0);
1491     if (new->str_ptr)
1492         Safefree(new->str_ptr);
1493     StructCopy(old,new,STR);
1494     if (old->str_ptr) {
1495         new->str_ptr = nsavestr(old->str_ptr,old->str_len);
1496         new->str_pok &= ~SP_TEMP;
1497     }
1498     return new;
1499 }
1500
1501 void
1502 str_reset(s,stash)
1503 register char *s;
1504 HASH *stash;
1505 {
1506     register HENT *entry;
1507     register STAB *stab;
1508     register STR *str;
1509     register int i;
1510     register SPAT *spat;
1511     register int max;
1512
1513     if (!*s) {          /* reset ?? searches */
1514         for (spat = stash->tbl_spatroot;
1515           spat != Nullspat;
1516           spat = spat->spat_next) {
1517             spat->spat_flags &= ~SPAT_USED;
1518         }
1519         return;
1520     }
1521
1522     /* reset variables */
1523
1524     if (!stash->tbl_array)
1525         return;
1526     while (*s) {
1527         i = *s;
1528         if (s[1] == '-') {
1529             s += 2;
1530         }
1531         max = *s++;
1532         for ( ; i <= max; i++) {
1533             for (entry = stash->tbl_array[i];
1534               entry;
1535               entry = entry->hent_next) {
1536                 stab = (STAB*)entry->hent_val;
1537                 str = stab_val(stab);
1538                 str->str_cur = 0;
1539                 str->str_nok = 0;
1540 #ifdef TAINT
1541                 str->str_tainted = tainted;
1542 #endif
1543                 if (str->str_ptr != Nullch)
1544                     str->str_ptr[0] = '\0';
1545                 if (stab_xarray(stab)) {
1546                     aclear(stab_xarray(stab));
1547                 }
1548                 if (stab_xhash(stab)) {
1549                     hclear(stab_xhash(stab), FALSE);
1550                     if (stab == envstab)
1551                         environ[0] = Nullch;
1552                 }
1553             }
1554         }
1555     }
1556 }
1557
1558 #ifdef TAINT
1559 void
1560 taintproper(s)
1561 char *s;
1562 {
1563 #ifdef DEBUGGING
1564     if (debug & 2048)
1565         fprintf(stderr,"%s %d %d %d\n",s,tainted,uid, euid);
1566 #endif
1567     if (tainted && (!euid || euid != uid || egid != gid || taintanyway)) {
1568         if (!unsafe)
1569             fatal("%s", s);
1570         else if (dowarn)
1571             warn("%s", s);
1572     }
1573 }
1574
1575 void
1576 taintenv()
1577 {
1578     register STR *envstr;
1579
1580     envstr = hfetch(stab_hash(envstab),"PATH",4,FALSE);
1581     if (envstr == &str_undef || envstr->str_tainted) {
1582         tainted = 1;
1583         if (envstr->str_tainted == 2)
1584             taintproper("Insecure directory in PATH");
1585         else
1586             taintproper("Insecure PATH");
1587     }
1588     envstr = hfetch(stab_hash(envstab),"IFS",3,FALSE);
1589     if (envstr != &str_undef && envstr->str_tainted) {
1590         tainted = 1;
1591         taintproper("Insecure IFS");
1592     }
1593 }
1594 #endif /* TAINT */