perl 3.0 patch #9 (combined patch)
[p5sagit/p5-mst-13.2.git] / str.c
1 /* $Header: str.c,v 3.0.1.4 89/12/21 20:21:35 lwall Locked $
2  *
3  *    Copyright (c) 1989, Larry Wall
4  *
5  *    You may distribute under the terms of the GNU General Public License
6  *    as specified in the README file that comes with the perl 3.0 kit.
7  *
8  * $Log:        str.c,v $
9  * Revision 3.0.1.4  89/12/21  20:21:35  lwall
10  * patch7: errno may now be a macro with an lvalue
11  * patch7: made nested or recursive foreach work right
12  * 
13  * Revision 3.0.1.3  89/11/17  15:38:23  lwall
14  * patch5: some machines typedef unchar too
15  * patch5: substitution on leading components occasionally caused <> corruption
16  * 
17  * Revision 3.0.1.2  89/11/11  04:56:22  lwall
18  * patch2: uchar gives Crays fits
19  * 
20  * Revision 3.0.1.1  89/10/26  23:23:41  lwall
21  * patch1: string ordering tests were wrong
22  * patch1: $/ now works even when STDSTDIO undefined
23  * 
24  * Revision 3.0  89/10/18  15:23:38  lwall
25  * 3.0 baseline
26  * 
27  */
28
29 #include "EXTERN.h"
30 #include "perl.h"
31 #include "perly.h"
32
33 extern char **environ;
34
35 #ifndef str_get
36 char *
37 str_get(str)
38 STR *str;
39 {
40 #ifdef TAINT
41     tainted |= str->str_tainted;
42 #endif
43     return str->str_pok ? str->str_ptr : str_2ptr(str);
44 }
45 #endif
46
47 /* dlb ... guess we have a "crippled cc".
48  * dlb the following functions are usually macros.
49  */
50 #ifndef str_true
51 str_true(Str)
52 STR *Str;
53 {
54         if (Str->str_pok) {
55             if (*Str->str_ptr > '0' ||
56               Str->str_cur > 1 ||
57               (Str->str_cur && *Str->str_ptr != '0'))
58                 return 1;
59             return 0;
60         }
61         if (Str->str_nok)
62                 return (Str->str_u.str_nval != 0.0);
63         return 0;
64 }
65 #endif /* str_true */
66
67 #ifndef str_gnum
68 double str_gnum(Str)
69 STR *Str;
70 {
71 #ifdef TAINT
72         tainted |= Str->str_tainted;
73 #endif /* TAINT*/
74         if (Str->str_nok)
75                 return Str->str_u.str_nval;
76         return str_2num(Str);
77 }
78 #endif /* str_gnum */
79 /* dlb ... end of crutch */
80
81 char *
82 str_grow(str,newlen)
83 register STR *str;
84 register int newlen;
85 {
86     register char *s = str->str_ptr;
87
88     if (str->str_state == SS_INCR) {            /* data before str_ptr? */
89         str->str_len += str->str_u.str_useful;
90         str->str_ptr -= str->str_u.str_useful;
91         str->str_u.str_useful = 0L;
92         bcopy(s, str->str_ptr, str->str_cur+1);
93         s = str->str_ptr;
94         str->str_state = SS_NORM;                       /* normal again */
95         if (newlen > str->str_len)
96             newlen += 10 * (newlen - str->str_cur); /* avoid copy each time */
97     }
98     if (newlen > str->str_len) {                /* need more room? */
99         if (str->str_len)
100             Renew(s,newlen,char);
101         else
102             New(703,s,newlen,char);
103         str->str_ptr = s;
104         str->str_len = newlen;
105     }
106     return s;
107 }
108
109 str_numset(str,num)
110 register STR *str;
111 double num;
112 {
113     str->str_u.str_nval = num;
114     str->str_state = SS_NORM;
115     str->str_pok = 0;   /* invalidate pointer */
116     str->str_nok = 1;                   /* validate number */
117 #ifdef TAINT
118     str->str_tainted = tainted;
119 #endif
120 }
121
122 char *
123 str_2ptr(str)
124 register STR *str;
125 {
126     register char *s;
127     int olderrno;
128
129     if (!str)
130         return "";
131     if (str->str_nok) {
132         STR_GROW(str, 24);
133         s = str->str_ptr;
134         olderrno = errno;       /* some Xenix systems wipe out errno here */
135 #if defined(scs) && defined(ns32000)
136         gcvt(str->str_u.str_nval,20,s);
137 #else
138 #ifdef apollo
139         if (str->str_u.str_nval == 0.0)
140             (void)strcpy(s,"0");
141         else
142 #endif /*apollo*/
143         (void)sprintf(s,"%.20g",str->str_u.str_nval);
144 #endif /*scs*/
145         errno = olderrno;
146         while (*s) s++;
147     }
148     else {
149         if (str == &str_undef)
150             return No;
151         if (dowarn)
152             warn("Use of uninitialized variable");
153         STR_GROW(str, 24);
154         s = str->str_ptr;
155     }
156     *s = '\0';
157     str->str_cur = s - str->str_ptr;
158     str->str_pok = 1;
159 #ifdef DEBUGGING
160     if (debug & 32)
161         fprintf(stderr,"0x%lx ptr(%s)\n",str,str->str_ptr);
162 #endif
163     return str->str_ptr;
164 }
165
166 double
167 str_2num(str)
168 register STR *str;
169 {
170     if (!str)
171         return 0.0;
172     str->str_state = SS_NORM;
173     if (str->str_len && str->str_pok)
174         str->str_u.str_nval = atof(str->str_ptr);
175     else  {
176         if (str == &str_undef)
177             return 0.0;
178         if (dowarn)
179             warn("Use of uninitialized variable");
180         str->str_u.str_nval = 0.0;
181     }
182     str->str_nok = 1;
183 #ifdef DEBUGGING
184     if (debug & 32)
185         fprintf(stderr,"0x%lx num(%g)\n",str,str->str_u.str_nval);
186 #endif
187     return str->str_u.str_nval;
188 }
189
190 str_sset(dstr,sstr)
191 STR *dstr;
192 register STR *sstr;
193 {
194 #ifdef TAINT
195     tainted |= sstr->str_tainted;
196 #endif
197     if (!sstr)
198         dstr->str_pok = dstr->str_nok = 0;
199     else if (sstr->str_pok) {
200         str_nset(dstr,sstr->str_ptr,sstr->str_cur);
201         if (sstr->str_nok) {
202             dstr->str_u.str_nval = sstr->str_u.str_nval;
203             dstr->str_nok = 1;
204             dstr->str_state = SS_NORM;
205         }
206         else if (sstr->str_cur == sizeof(STBP)) {
207             char *tmps = sstr->str_ptr;
208
209             if (*tmps == 'S' && bcmp(tmps,"Stab",4) == 0) {
210                 dstr->str_magic = str_smake(sstr->str_magic);
211                 dstr->str_magic->str_rare = 'X';
212             }
213         }
214     }
215     else if (sstr->str_nok)
216         str_numset(dstr,sstr->str_u.str_nval);
217     else {
218 #ifdef STRUCTCOPY
219         dstr->str_u = sstr->str_u;
220 #else
221         dstr->str_u.str_nval = sstr->str_u.str_nval;
222 #endif
223         dstr->str_pok = dstr->str_nok = 0;
224     }
225 }
226
227 str_nset(str,ptr,len)
228 register STR *str;
229 register char *ptr;
230 register int len;
231 {
232     STR_GROW(str, len + 1);
233     (void)bcopy(ptr,str->str_ptr,len);
234     str->str_cur = len;
235     *(str->str_ptr+str->str_cur) = '\0';
236     str->str_nok = 0;           /* invalidate number */
237     str->str_pok = 1;           /* validate pointer */
238 #ifdef TAINT
239     str->str_tainted = tainted;
240 #endif
241 }
242
243 str_set(str,ptr)
244 register STR *str;
245 register char *ptr;
246 {
247     register int len;
248
249     if (!ptr)
250         ptr = "";
251     len = strlen(ptr);
252     STR_GROW(str, len + 1);
253     (void)bcopy(ptr,str->str_ptr,len+1);
254     str->str_cur = len;
255     str->str_nok = 0;           /* invalidate number */
256     str->str_pok = 1;           /* validate pointer */
257 #ifdef TAINT
258     str->str_tainted = tainted;
259 #endif
260 }
261
262 str_chop(str,ptr)       /* like set but assuming ptr is in str */
263 register STR *str;
264 register char *ptr;
265 {
266     register int delta;
267
268     if (!(str->str_pok))
269         fatal("str_chop: internal inconsistency");
270     delta = ptr - str->str_ptr;
271     str->str_len -= delta;
272     str->str_cur -= delta;
273     str->str_ptr += delta;
274     if (str->str_state == SS_INCR)
275         str->str_u.str_useful += delta;
276     else {
277         str->str_u.str_useful = delta;
278         str->str_state = SS_INCR;
279     }
280     str->str_nok = 0;           /* invalidate number */
281     str->str_pok = 1;           /* validate pointer (and unstudy str) */
282 }
283
284 str_ncat(str,ptr,len)
285 register STR *str;
286 register char *ptr;
287 register int len;
288 {
289     if (!(str->str_pok))
290         (void)str_2ptr(str);
291     STR_GROW(str, str->str_cur + len + 1);
292     (void)bcopy(ptr,str->str_ptr+str->str_cur,len);
293     str->str_cur += len;
294     *(str->str_ptr+str->str_cur) = '\0';
295     str->str_nok = 0;           /* invalidate number */
296     str->str_pok = 1;           /* validate pointer */
297 #ifdef TAINT
298     str->str_tainted |= tainted;
299 #endif
300 }
301
302 str_scat(dstr,sstr)
303 STR *dstr;
304 register STR *sstr;
305 {
306 #ifdef TAINT
307     tainted |= sstr->str_tainted;
308 #endif
309     if (!sstr)
310         return;
311     if (!(sstr->str_pok))
312         (void)str_2ptr(sstr);
313     if (sstr)
314         str_ncat(dstr,sstr->str_ptr,sstr->str_cur);
315 }
316
317 str_cat(str,ptr)
318 register STR *str;
319 register char *ptr;
320 {
321     register int len;
322
323     if (!ptr)
324         return;
325     if (!(str->str_pok))
326         (void)str_2ptr(str);
327     len = strlen(ptr);
328     STR_GROW(str, str->str_cur + len + 1);
329     (void)bcopy(ptr,str->str_ptr+str->str_cur,len+1);
330     str->str_cur += len;
331     str->str_nok = 0;           /* invalidate number */
332     str->str_pok = 1;           /* validate pointer */
333 #ifdef TAINT
334     str->str_tainted |= tainted;
335 #endif
336 }
337
338 char *
339 str_append_till(str,from,fromend,delim,keeplist)
340 register STR *str;
341 register char *from;
342 register char *fromend;
343 register int delim;
344 char *keeplist;
345 {
346     register char *to;
347     register int len;
348
349     if (!from)
350         return Nullch;
351     len = fromend - from;
352     STR_GROW(str, str->str_cur + len + 1);
353     str->str_nok = 0;           /* invalidate number */
354     str->str_pok = 1;           /* validate pointer */
355     to = str->str_ptr+str->str_cur;
356     for (; from < fromend; from++,to++) {
357         if (*from == '\\' && from+1 < fromend && delim != '\\') {
358             if (!keeplist) {
359                 if (from[1] == delim || from[1] == '\\')
360                     from++;
361                 else
362                     *to++ = *from++;
363             }
364             else if (from[1] && index(keeplist,from[1]))
365                 *to++ = *from++;
366             else
367                 from++;
368         }
369         else if (*from == delim)
370             break;
371         *to = *from;
372     }
373     *to = '\0';
374     str->str_cur = to - str->str_ptr;
375     return from;
376 }
377
378 STR *
379 #ifdef LEAKTEST
380 str_new(x,len)
381 int x;
382 #else
383 str_new(len)
384 #endif
385 int len;
386 {
387     register STR *str;
388     
389     if (freestrroot) {
390         str = freestrroot;
391         freestrroot = str->str_magic;
392         str->str_magic = Nullstr;
393         str->str_state = SS_NORM;
394     }
395     else {
396         Newz(700+x,str,1,STR);
397     }
398     if (len)
399         STR_GROW(str, len + 1);
400     return str;
401 }
402
403 void
404 str_magic(str, stab, how, name, namlen)
405 register STR *str;
406 STAB *stab;
407 int how;
408 char *name;
409 int namlen;
410 {
411     if (str->str_magic)
412         return;
413     str->str_magic = Str_new(75,namlen);
414     str = str->str_magic;
415     str->str_u.str_stab = stab;
416     str->str_rare = how;
417     if (name)
418         str_nset(str,name,namlen);
419 }
420
421 void
422 str_insert(bigstr,offset,len,little,littlelen)
423 STR *bigstr;
424 int offset;
425 int len;
426 char *little;
427 int littlelen;
428 {
429     register char *big;
430     register char *mid;
431     register char *midend;
432     register char *bigend;
433     register int i;
434
435     i = littlelen - len;
436     if (i > 0) {                        /* string might grow */
437         STR_GROW(bigstr, bigstr->str_cur + i + 1);
438         big = bigstr->str_ptr;
439         mid = big + offset + len;
440         midend = bigend = big + bigstr->str_cur;
441         bigend += i;
442         *bigend = '\0';
443         while (midend > mid)            /* shove everything down */
444             *--bigend = *--midend;
445         (void)bcopy(little,big+offset,littlelen);
446         bigstr->str_cur += i;
447         return;
448     }
449     else if (i == 0) {
450         (void)bcopy(little,bigstr->str_ptr+offset,len);
451         return;
452     }
453
454     big = bigstr->str_ptr;
455     mid = big + offset;
456     midend = mid + len;
457     bigend = big + bigstr->str_cur;
458
459     if (midend > bigend)
460         fatal("panic: str_insert");
461
462     bigstr->str_pok = SP_VALID; /* disable possible screamer */
463
464     if (mid - big > bigend - midend) {  /* faster to shorten from end */
465         if (littlelen) {
466             (void)bcopy(little, mid, littlelen);
467             mid += littlelen;
468         }
469         i = bigend - midend;
470         if (i > 0) {
471             (void)bcopy(midend, mid, i);
472             mid += i;
473         }
474         *mid = '\0';
475         bigstr->str_cur = mid - big;
476     }
477     else if (i = mid - big) {   /* faster from front */
478         midend -= littlelen;
479         mid = midend;
480         str_chop(bigstr,midend-i);
481         big += i;
482         while (i--)
483             *--midend = *--big;
484         if (littlelen)
485             (void)bcopy(little, mid, littlelen);
486     }
487     else if (littlelen) {
488         midend -= littlelen;
489         str_chop(bigstr,midend);
490         (void)bcopy(little,midend,littlelen);
491     }
492     else {
493         str_chop(bigstr,midend);
494     }
495     STABSET(bigstr);
496 }
497
498 /* make str point to what nstr did */
499
500 void
501 str_replace(str,nstr)
502 register STR *str;
503 register STR *nstr;
504 {
505     if (str->str_state == SS_INCR)
506         str_grow(str,0);        /* just force copy down */
507     if (nstr->str_state == SS_INCR)
508         str_grow(nstr,0);
509     if (str->str_ptr)
510         Safefree(str->str_ptr);
511     str->str_ptr = nstr->str_ptr;
512     str->str_len = nstr->str_len;
513     str->str_cur = nstr->str_cur;
514     str->str_pok = nstr->str_pok;
515     str->str_nok = nstr->str_nok;
516 #ifdef STRUCTCOPY
517     str->str_u = nstr->str_u;
518 #else
519     str->str_u.str_nval = nstr->str_u.str_nval;
520 #endif
521 #ifdef TAINT
522     str->str_tainted = nstr->str_tainted;
523 #endif
524     Safefree(nstr);
525 }
526
527 void
528 str_free(str)
529 register STR *str;
530 {
531     if (!str)
532         return;
533     if (str->str_state) {
534         if (str->str_state == SS_FREE)  /* already freed */
535             return;
536         if (str->str_state == SS_INCR && !(str->str_pok & 2)) {
537             str->str_ptr -= str->str_u.str_useful;
538             str->str_len += str->str_u.str_useful;
539         }
540     }
541     if (str->str_magic)
542         str_free(str->str_magic);
543 #ifdef LEAKTEST
544     if (str->str_len)
545         Safefree(str->str_ptr);
546     if ((str->str_pok & SP_INTRP) && str->str_u.str_args)
547         arg_free(str->str_u.str_args);
548     Safefree(str);
549 #else /* LEAKTEST */
550     if (str->str_len) {
551         if (str->str_len > 127) {       /* next user not likely to want more */
552             Safefree(str->str_ptr);     /* so give it back to malloc */
553             str->str_ptr = Nullch;
554             str->str_len = 0;
555         }
556         else
557             str->str_ptr[0] = '\0';
558     }
559     if ((str->str_pok & SP_INTRP) && str->str_u.str_args)
560         arg_free(str->str_u.str_args);
561     str->str_cur = 0;
562     str->str_nok = 0;
563     str->str_pok = 0;
564     str->str_state = SS_FREE;
565 #ifdef TAINT
566     str->str_tainted = 0;
567 #endif
568     str->str_magic = freestrroot;
569     freestrroot = str;
570 #endif /* LEAKTEST */
571 }
572
573 str_len(str)
574 register STR *str;
575 {
576     if (!str)
577         return 0;
578     if (!(str->str_pok))
579         (void)str_2ptr(str);
580     if (str->str_ptr)
581         return str->str_cur;
582     else
583         return 0;
584 }
585
586 str_eq(str1,str2)
587 register STR *str1;
588 register STR *str2;
589 {
590     if (!str1)
591         return str2 == Nullstr;
592     if (!str2)
593         return 0;
594
595     if (!str1->str_pok)
596         (void)str_2ptr(str1);
597     if (!str2->str_pok)
598         (void)str_2ptr(str2);
599
600     if (str1->str_cur != str2->str_cur)
601         return 0;
602
603     return !bcmp(str1->str_ptr, str2->str_ptr, str1->str_cur);
604 }
605
606 str_cmp(str1,str2)
607 register STR *str1;
608 register STR *str2;
609 {
610     int retval;
611
612     if (!str1)
613         return str2 == Nullstr;
614     if (!str2)
615         return 0;
616
617     if (!str1->str_pok)
618         (void)str_2ptr(str1);
619     if (!str2->str_pok)
620         (void)str_2ptr(str2);
621
622     if (str1->str_cur < str2->str_cur) {
623         if (retval = memcmp(str1->str_ptr, str2->str_ptr, str1->str_cur))
624             return retval;
625         else
626             return -1;
627     }
628     else if (retval = memcmp(str1->str_ptr, str2->str_ptr, str2->str_cur))
629         return retval;
630     else if (str1->str_cur == str2->str_cur)
631         return 0;
632     else
633         return 1;
634 }
635
636 char *
637 str_gets(str,fp,append)
638 register STR *str;
639 register FILE *fp;
640 int append;
641 {
642     register char *bp;          /* we're going to steal some values */
643     register int cnt;           /*  from the stdio struct and put EVERYTHING */
644     register STDCHAR *ptr;      /*   in the innermost loop into registers */
645     register char newline = record_separator;/* (assuming >= 6 registers) */
646     int i;
647     int bpx;
648     int obpx;
649     register int get_paragraph;
650     register char *oldbp;
651
652     if (get_paragraph = !rslen) {       /* yes, that's an assignment */
653         newline = '\n';
654         oldbp = Nullch;                 /* remember last \n position (none) */
655     }
656 #ifdef STDSTDIO         /* Here is some breathtakingly efficient cheating */
657
658     cnt = fp->_cnt;                     /* get count into register */
659     str->str_nok = 0;                   /* invalidate number */
660     str->str_pok = 1;                   /* validate pointer */
661     if (str->str_len <= cnt + 1)        /* make sure we have the room */
662         STR_GROW(str, append+cnt+2);    /* (remembering cnt can be -1) */
663     bp = str->str_ptr + append;         /* move these two too to registers */
664     ptr = fp->_ptr;
665     for (;;) {
666       screamer:
667         while (--cnt >= 0) {                    /* this */      /* eat */
668             if ((*bp++ = *ptr++) == newline)    /* really */    /* dust */
669                 goto thats_all_folks;           /* screams */   /* sed :-) */ 
670         }
671         
672         fp->_cnt = cnt;                 /* deregisterize cnt and ptr */
673         fp->_ptr = ptr;
674         i = _filbuf(fp);                /* get more characters */
675         cnt = fp->_cnt;
676         ptr = fp->_ptr;                 /* reregisterize cnt and ptr */
677
678         bpx = bp - str->str_ptr;        /* prepare for possible relocation */
679         if (get_paragraph && oldbp)
680             obpx = oldbp - str->str_ptr;
681         str->str_cur = bpx;
682         STR_GROW(str, bpx + cnt + 2);
683         bp = str->str_ptr + bpx;        /* reconstitute our pointer */
684         if (get_paragraph && oldbp)
685             oldbp = str->str_ptr + obpx;
686
687         if (i == newline) {             /* all done for now? */
688             *bp++ = i;
689             goto thats_all_folks;
690         }
691         else if (i == EOF)              /* all done for ever? */
692             goto thats_really_all_folks;
693         *bp++ = i;                      /* now go back to screaming loop */
694     }
695
696 thats_all_folks:
697     if (get_paragraph && bp - 1 != oldbp) {
698         oldbp = bp;     /* remember where this newline was */
699         goto screamer;  /* and go back to the fray */
700     }
701 thats_really_all_folks:
702     fp->_cnt = cnt;                     /* put these back or we're in trouble */
703     fp->_ptr = ptr;
704     *bp = '\0';
705     str->str_cur = bp - str->str_ptr;   /* set length */
706
707 #else /* !STDSTDIO */   /* The big, slow, and stupid way */
708
709     {
710         static char buf[8192];
711         char * bpe = buf + sizeof(buf) - 3;
712
713 screamer:
714         bp = buf;
715 filler:
716         while ((i = getc(fp)) != EOF && (*bp++ = i) != newline && bp < bpe);
717         if (i == newline && get_paragraph &&
718             (i = getc(fp)) != EOF && (*bp++ = i) != newline && bp < bpe)
719             goto filler;
720
721         *bp = '\0';
722         if (append)
723             str_cat(str, buf);
724         else
725             str_set(str, buf);
726         if (i != newline && i != EOF) {
727             append = -1;
728             goto screamer;
729         }
730     }
731
732 #endif /* STDSTDIO */
733
734     return str->str_cur - append ? str->str_ptr : Nullch;
735 }
736
737 ARG *
738 parselist(str)
739 STR *str;
740 {
741     register CMD *cmd;
742     register ARG *arg;
743     line_t oldline = line;
744     int retval;
745
746     str_sset(linestr,str);
747     in_eval++;
748     oldoldbufptr = oldbufptr = bufptr = str_get(linestr);
749     bufend = bufptr + linestr->str_cur;
750     if (setjmp(eval_env)) {
751         in_eval = 0;
752         fatal("%s\n",stab_val(stabent("@",TRUE))->str_ptr);
753     }
754     error_count = 0;
755     retval = yyparse();
756     in_eval--;
757     if (retval || error_count)
758         fatal("Invalid component in string or format");
759     cmd = eval_root;
760     arg = cmd->c_expr;
761     if (cmd->c_type != C_EXPR || cmd->c_next || arg->arg_type != O_LIST)
762         fatal("panic: error in parselist %d %x %d", cmd->c_type,
763           cmd->c_next, arg ? arg->arg_type : -1);
764     line = oldline;
765     Safefree(cmd);
766     return arg;
767 }
768
769 void
770 intrpcompile(src)
771 STR *src;
772 {
773     register char *s = str_get(src);
774     register char *send = s + src->str_cur;
775     register STR *str;
776     register char *t;
777     STR *toparse;
778     int len;
779     register int brackets;
780     register char *d;
781     STAB *stab;
782     char *checkpoint;
783
784     toparse = Str_new(76,0);
785     str = Str_new(77,0);
786
787     str_nset(str,"",0);
788     str_nset(toparse,"",0);
789     t = s;
790     while (s < send) {
791         if (*s == '\\' && s[1] && index("$@[{\\]}",s[1])) {
792             str_ncat(str, t, s - t);
793             ++s;
794             if (*nointrp && s+1 < send)
795                 if (*s != '@' && (*s != '$' || index(nointrp,s[1])))
796                     str_ncat(str,s-1,1);
797             str_ncat(str, "$b", 2);
798             str_ncat(str, s, 1);
799             ++s;
800             t = s;
801         }
802         else if ((*s == '@' || (*s == '$' && !index(nointrp,s[1]))) &&
803           s+1 < send) {
804             str_ncat(str,t,s-t);
805             t = s;
806             if (*s == '$' && s[1] == '#' && isalpha(s[2]) || s[2] == '_')
807                 s++;
808             s = scanreg(s,send,tokenbuf);
809             if (*t == '@' &&
810               (!(stab = stabent(tokenbuf,FALSE)) || !stab_xarray(stab)) ) {
811                 str_ncat(str,"@",1);
812                 s = ++t;
813                 continue;       /* grandfather @ from old scripts */
814             }
815             str_ncat(str,"$a",2);
816             str_ncat(toparse,",",1);
817             if (t[1] != '{' && (*s == '['  || *s == '{' /* }} */ ) &&
818               (stab = stabent(tokenbuf,FALSE)) &&
819               ((*s == '[') ? (stab_xarray(stab) != 0) : (stab_xhash(stab) != 0)) ) {
820                 brackets = 0;
821                 checkpoint = s;
822                 do {
823                     switch (*s) {
824                     case '[': case '{':
825                         brackets++;
826                         break;
827                     case ']': case '}':
828                         brackets--;
829                         break;
830                     case '\'':
831                     case '"':
832                         if (s[-1] != '$') {
833                             s = cpytill(tokenbuf,s+1,send,*s,&len);
834                             if (s >= send)
835                                 fatal("Unterminated string");
836                         }
837                         break;
838                     }
839                     s++;
840                 } while (brackets > 0 && s < send);
841                 if (s > send)
842                     fatal("Unmatched brackets in string");
843                 if (*nointrp) {         /* we're in a regular expression */
844                     d = checkpoint;
845                     if (*d == '{' && s[-1] == '}') {    /* maybe {n,m} */
846                         ++d;
847                         if (isdigit(*d)) {      /* matches /^{\d,?\d*}$/ */
848                             if (*++d == ',')
849                                 ++d;
850                             while (isdigit(*d))
851                                 d++;
852                             if (d == s - 1)
853                                 s = checkpoint;         /* Is {n,m}! Backoff! */
854                         }
855                     }
856                     else if (*d == '[' && s[-1] == ']') { /* char class? */
857                         int weight = 2;         /* let's weigh the evidence */
858                         char seen[256];
859                         unsigned char un_char = 0, last_un_char;
860
861                         Zero(seen,256,char);
862                         *--s = '\0';
863                         if (d[1] == '^')
864                             weight += 150;
865                         else if (d[1] == '$')
866                             weight -= 3;
867                         if (isdigit(d[1])) {
868                             if (d[2]) {
869                                 if (isdigit(d[2]) && !d[3])
870                                     weight -= 10;
871                             }
872                             else
873                                 weight -= 100;
874                         }
875                         for (d++; d < s; d++) {
876                             last_un_char = un_char;
877                             un_char = (unsigned char)*d;
878                             switch (*d) {
879                             case '&':
880                             case '$':
881                                 weight -= seen[un_char] * 10;
882                                 if (isalpha(d[1]) || isdigit(d[1]) ||
883                                   d[1] == '_') {
884                                     d = scanreg(d,s,tokenbuf);
885                                     if (stabent(tokenbuf,FALSE))
886                                         weight -= 100;
887                                     else
888                                         weight -= 10;
889                                 }
890                                 else if (*d == '$' && d[1] &&
891                                   index("[#!%*<>()-=",d[1])) {
892                                     if (!d[2] || /*{*/ index("])} =",d[2]))
893                                         weight -= 10;
894                                     else
895                                         weight -= 1;
896                                 }
897                                 break;
898                             case '\\':
899                                 un_char = 254;
900                                 if (d[1]) {
901                                     if (index("wds",d[1]))
902                                         weight += 100;
903                                     else if (seen['\''] || seen['"'])
904                                         weight += 1;
905                                     else if (index("rnftb",d[1]))
906                                         weight += 40;
907                                     else if (isdigit(d[1])) {
908                                         weight += 40;
909                                         while (d[1] && isdigit(d[1]))
910                                             d++;
911                                     }
912                                 }
913                                 else
914                                     weight += 100;
915                                 break;
916                             case '-':
917                                 if (last_un_char < d[1] || d[1] == '\\') {
918                                     if (index("aA01! ",last_un_char))
919                                         weight += 30;
920                                     if (index("zZ79~",d[1]))
921                                         weight += 30;
922                                 }
923                                 else
924                                     weight -= 1;
925                             default:
926                                 if (isalpha(*d) && d[1] && isalpha(d[1])) {
927                                     bufptr = d;
928                                     if (yylex() != WORD)
929                                         weight -= 150;
930                                     d = bufptr;
931                                 }
932                                 if (un_char == last_un_char + 1)
933                                     weight += 5;
934                                 weight -= seen[un_char];
935                                 break;
936                             }
937                             seen[un_char]++;
938                         }
939 #ifdef DEBUGGING
940                         if (debug & 512)
941                             fprintf(stderr,"[%s] weight %d\n",
942                               checkpoint+1,weight);
943 #endif
944                         *s++ = ']';
945                         if (weight >= 0)        /* probably a character class */
946                             s = checkpoint;
947                     }
948                 }
949             }
950             if (*t == '@')
951                 str_ncat(toparse, "join($\",", 8);
952             if (t[1] == '{' && s[-1] == '}') {
953                 str_ncat(toparse, t, 1);
954                 str_ncat(toparse, t+2, s - t - 3);
955             }
956             else
957                 str_ncat(toparse, t, s - t);
958             if (*t == '@')
959                 str_ncat(toparse, ")", 1);
960             t = s;
961         }
962         else
963             s++;
964     }
965     str_ncat(str,t,s-t);
966     if (toparse->str_ptr && *toparse->str_ptr == ',') {
967         *toparse->str_ptr = '(';
968         str_ncat(toparse,",$$);",5);
969         str->str_u.str_args = parselist(toparse);
970         str->str_u.str_args->arg_len--;         /* ignore $$ reference */
971     }
972     else
973         str->str_u.str_args = Nullarg;
974     str_free(toparse);
975     str->str_pok |= SP_INTRP;
976     str->str_nok = 0;
977     str_replace(src,str);
978 }
979
980 STR *
981 interp(str,src,sp)
982 register STR *str;
983 STR *src;
984 int sp;
985 {
986     register char *s;
987     register char *t;
988     register char *send;
989     register STR **elem;
990
991     if (!(src->str_pok & SP_INTRP)) {
992         int oldsave = savestack->ary_fill;
993
994         (void)savehptr(&curstash);
995         curstash = src->str_u.str_hash; /* so stabent knows right package */
996         intrpcompile(src);
997         restorelist(oldsave);
998     }
999     s = src->str_ptr;           /* assumed valid since str_pok set */
1000     t = s;
1001     send = s + src->str_cur;
1002
1003     if (src->str_u.str_args) {
1004         (void)eval(src->str_u.str_args,G_ARRAY,sp);
1005         /* Assuming we have correct # of args */
1006         elem = stack->ary_array + sp;
1007     }
1008
1009     str_nset(str,"",0);
1010     while (s < send) {
1011         if (*s == '$' && s+1 < send) {
1012             str_ncat(str,t,s-t);
1013             switch(*++s) {
1014             case 'a':
1015                 str_scat(str,*++elem);
1016                 break;
1017             case 'b':
1018                 str_ncat(str,++s,1);
1019                 break;
1020             }
1021             t = ++s;
1022         }
1023         else
1024             s++;
1025     }
1026     str_ncat(str,t,s-t);
1027     return str;
1028 }
1029
1030 void
1031 str_inc(str)
1032 register STR *str;
1033 {
1034     register char *d;
1035
1036     if (!str)
1037         return;
1038     if (str->str_nok) {
1039         str->str_u.str_nval += 1.0;
1040         str->str_pok = 0;
1041         return;
1042     }
1043     if (!str->str_pok || !*str->str_ptr) {
1044         str->str_u.str_nval = 1.0;
1045         str->str_nok = 1;
1046         str->str_pok = 0;
1047         return;
1048     }
1049     d = str->str_ptr;
1050     while (isalpha(*d)) d++;
1051     while (isdigit(*d)) d++;
1052     if (*d) {
1053         str_numset(str,atof(str->str_ptr) + 1.0);  /* punt */
1054         return;
1055     }
1056     d--;
1057     while (d >= str->str_ptr) {
1058         if (isdigit(*d)) {
1059             if (++*d <= '9')
1060                 return;
1061             *(d--) = '0';
1062         }
1063         else {
1064             ++*d;
1065             if (isalpha(*d))
1066                 return;
1067             *(d--) -= 'z' - 'a' + 1;
1068         }
1069     }
1070     /* oh,oh, the number grew */
1071     STR_GROW(str, str->str_cur + 2);
1072     str->str_cur++;
1073     for (d = str->str_ptr + str->str_cur; d > str->str_ptr; d--)
1074         *d = d[-1];
1075     if (isdigit(d[1]))
1076         *d = '1';
1077     else
1078         *d = d[1];
1079 }
1080
1081 void
1082 str_dec(str)
1083 register STR *str;
1084 {
1085     if (!str)
1086         return;
1087     if (str->str_nok) {
1088         str->str_u.str_nval -= 1.0;
1089         str->str_pok = 0;
1090         return;
1091     }
1092     if (!str->str_pok) {
1093         str->str_u.str_nval = -1.0;
1094         str->str_nok = 1;
1095         return;
1096     }
1097     str_numset(str,atof(str->str_ptr) - 1.0);
1098 }
1099
1100 /* Make a string that will exist for the duration of the expression
1101  * evaluation.  Actually, it may have to last longer than that, but
1102  * hopefully cmd_exec won't free it until it has been assigned to a
1103  * permanent location. */
1104
1105 static long tmps_size = -1;
1106
1107 STR *
1108 str_static(oldstr)
1109 STR *oldstr;
1110 {
1111     register STR *str = Str_new(78,0);
1112
1113     str_sset(str,oldstr);
1114     if (++tmps_max > tmps_size) {
1115         tmps_size = tmps_max;
1116         if (!(tmps_size & 127)) {
1117             if (tmps_size)
1118                 Renew(tmps_list, tmps_size + 128, STR*);
1119             else
1120                 New(702,tmps_list, 128, STR*);
1121         }
1122     }
1123     tmps_list[tmps_max] = str;
1124     return str;
1125 }
1126
1127 /* same thing without the copying */
1128
1129 STR *
1130 str_2static(str)
1131 register STR *str;
1132 {
1133     if (++tmps_max > tmps_size) {
1134         tmps_size = tmps_max;
1135         if (!(tmps_size & 127)) {
1136             if (tmps_size)
1137                 Renew(tmps_list, tmps_size + 128, STR*);
1138             else
1139                 New(704,tmps_list, 128, STR*);
1140         }
1141     }
1142     tmps_list[tmps_max] = str;
1143     return str;
1144 }
1145
1146 STR *
1147 str_make(s,len)
1148 char *s;
1149 int len;
1150 {
1151     register STR *str = Str_new(79,0);
1152
1153     if (!len)
1154         len = strlen(s);
1155     str_nset(str,s,len);
1156     return str;
1157 }
1158
1159 STR *
1160 str_nmake(n)
1161 double n;
1162 {
1163     register STR *str = Str_new(80,0);
1164
1165     str_numset(str,n);
1166     return str;
1167 }
1168
1169 /* make an exact duplicate of old */
1170
1171 STR *
1172 str_smake(old)
1173 register STR *old;
1174 {
1175     register STR *new = Str_new(81,0);
1176
1177     if (!old)
1178         return Nullstr;
1179     if (old->str_state == SS_FREE) {
1180         warn("semi-panic: attempt to dup freed string");
1181         return Nullstr;
1182     }
1183     if (old->str_state == SS_INCR && !(old->str_pok & 2))
1184         str_grow(old,0);
1185     if (new->str_ptr)
1186         Safefree(new->str_ptr);
1187     Copy(old,new,1,STR);
1188     if (old->str_ptr)
1189         new->str_ptr = nsavestr(old->str_ptr,old->str_len);
1190     return new;
1191 }
1192
1193 str_reset(s,stash)
1194 register char *s;
1195 HASH *stash;
1196 {
1197     register HENT *entry;
1198     register STAB *stab;
1199     register STR *str;
1200     register int i;
1201     register SPAT *spat;
1202     register int max;
1203
1204     if (!*s) {          /* reset ?? searches */
1205         for (spat = stash->tbl_spatroot;
1206           spat != Nullspat;
1207           spat = spat->spat_next) {
1208             spat->spat_flags &= ~SPAT_USED;
1209         }
1210         return;
1211     }
1212
1213     /* reset variables */
1214
1215     while (*s) {
1216         i = *s;
1217         if (s[1] == '-') {
1218             s += 2;
1219         }
1220         max = *s++;
1221         for ( ; i <= max; i++) {
1222             for (entry = stash->tbl_array[i];
1223               entry;
1224               entry = entry->hent_next) {
1225                 stab = (STAB*)entry->hent_val;
1226                 str = stab_val(stab);
1227                 str->str_cur = 0;
1228                 str->str_nok = 0;
1229 #ifdef TAINT
1230                 str->str_tainted = tainted;
1231 #endif
1232                 if (str->str_ptr != Nullch)
1233                     str->str_ptr[0] = '\0';
1234                 if (stab_xarray(stab)) {
1235                     aclear(stab_xarray(stab));
1236                 }
1237                 if (stab_xhash(stab)) {
1238                     hclear(stab_xhash(stab));
1239                     if (stab == envstab)
1240                         environ[0] = Nullch;
1241                 }
1242             }
1243         }
1244     }
1245 }
1246
1247 #ifdef TAINT
1248 taintproper(s)
1249 char *s;
1250 {
1251 #ifdef DEBUGGING
1252     if (debug & 2048)
1253         fprintf(stderr,"%s %d %d %d\n",s,tainted,uid, euid);
1254 #endif
1255     if (tainted && (!euid || euid != uid)) {
1256         if (!unsafe)
1257             fatal("%s", s);
1258         else if (dowarn)
1259             warn("%s", s);
1260     }
1261 }
1262
1263 taintenv()
1264 {
1265     register STR *envstr;
1266
1267     envstr = hfetch(stab_hash(envstab),"PATH",4,FALSE);
1268     if (!envstr || envstr->str_tainted) {
1269         tainted = 1;
1270         taintproper("Insecure PATH");
1271     }
1272     envstr = hfetch(stab_hash(envstab),"IFS",3,FALSE);
1273     if (envstr && envstr->str_tainted) {
1274         tainted = 1;
1275         taintproper("Insecure IFS");
1276     }
1277 }
1278 #endif /* TAINT */