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