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