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