/* $RCSfile: str.c,v $$Revision: 4.0.1.6 $$Date: 92/06/11 21:14:21 $ * * Copyright (c) 1991, Larry Wall * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. * * $Log: str.c,v $ * Revision 4.0.1.6 92/06/11 21:14:21 lwall * patch34: quotes containing subscripts containing variables didn't parse right * * Revision 4.0.1.5 92/06/08 15:40:43 lwall * patch20: removed implicit int declarations on functions * patch20: Perl now distinguishes overlapped copies from non-overlapped * patch20: paragraph mode now skips extra newlines automatically * patch20: fixed memory leak in doube-quote interpretation * patch20: made /\$$foo/ look for literal '$foo' * patch20: "$var{$foo'bar}" didn't scan subscript correctly * patch20: a splice on non-existent array elements could dump core * patch20: running taintperl explicitly now does checks even if $< == $> * * Revision 4.0.1.4 91/11/05 18:40:51 lwall * patch11: $foo .= could overrun malloced memory * patch11: \$ didn't always make it through double-quoter to regexp routines * patch11: prepared for ctype implementations that don't define isascii() * * Revision 4.0.1.3 91/06/10 01:27:54 lwall * patch10: $) and $| incorrectly handled in run-time patterns * * Revision 4.0.1.2 91/06/07 11:58:13 lwall * patch4: new copyright notice * patch4: taint check on undefined string could cause core dump * * Revision 4.0.1.1 91/04/12 09:15:30 lwall * patch1: fixed undefined environ problem * patch1: substr($ENV{"PATH"},0,0) = "/foo:" didn't modify environment * patch1: $foo .= could cause core dump for certain lengths of $foo * * Revision 4.0 91/03/20 01:39:55 lwall * 4.0 baseline. * */ #include "EXTERN.h" #include "perl.h" #include "perly.h" static void ucase(); static void lcase(); #ifndef str_get char * str_get(str) STR *str; { #ifdef TAINT tainted |= str->str_tainted; #endif return str->str_pok ? str->str_ptr : str_2ptr(str); } #endif /* dlb ... guess we have a "crippled cc". * dlb the following functions are usually macros. */ #ifndef str_true int str_true(Str) STR *Str; { if (Str->str_pok) { if (*Str->str_ptr > '0' || Str->str_cur > 1 || (Str->str_cur && *Str->str_ptr != '0')) return 1; return 0; } if (Str->str_nok) return (Str->str_u.str_nval != 0.0); return 0; } #endif /* str_true */ #ifndef str_gnum double str_gnum(Str) STR *Str; { #ifdef TAINT tainted |= Str->str_tainted; #endif /* TAINT*/ if (Str->str_nok) return Str->str_u.str_nval; return str_2num(Str); } #endif /* str_gnum */ /* dlb ... end of crutch */ char * str_grow(str,newlen) register STR *str; #ifndef DOSISH register int newlen; #else unsigned long newlen; #endif { register char *s = str->str_ptr; #ifdef MSDOS if (newlen >= 0x10000) { fprintf(stderr, "Allocation too large: %lx\n", newlen); exit(1); } #endif /* MSDOS */ if (str->str_state == SS_INCR) { /* data before str_ptr? */ str->str_len += str->str_u.str_useful; str->str_ptr -= str->str_u.str_useful; str->str_u.str_useful = 0L; Move(s, str->str_ptr, str->str_cur+1, char); s = str->str_ptr; str->str_state = SS_NORM; /* normal again */ if (newlen > str->str_len) newlen += 10 * (newlen - str->str_cur); /* avoid copy each time */ } if (newlen > str->str_len) { /* need more room? */ if (str->str_len) Renew(s,newlen,char); else New(703,s,newlen,char); str->str_ptr = s; str->str_len = newlen; } return s; } void str_numset(str,num) register STR *str; double num; { if (str->str_pok) { str->str_pok = 0; /* invalidate pointer */ if (str->str_state == SS_INCR) Str_Grow(str,0); } str->str_u.str_nval = num; str->str_state = SS_NORM; str->str_nok = 1; /* validate number */ #ifdef TAINT str->str_tainted = tainted; #endif } char * str_2ptr(str) register STR *str; { register char *s; int olderrno; if (!str) return ""; if (str->str_nok) { STR_GROW(str, 30); s = str->str_ptr; olderrno = errno; /* some Xenix systems wipe out errno here */ #if defined(scs) && defined(ns32000) gcvt(str->str_u.str_nval,20,s); #else #ifdef apollo if (str->str_u.str_nval == 0.0) (void)strcpy(s,"0"); else #endif /*apollo*/ (void)sprintf(s,"%.20g",str->str_u.str_nval); #endif /*scs*/ errno = olderrno; while (*s) s++; #ifdef hcx if (s[-1] == '.') s--; #endif } else { if (str == &str_undef) return No; if (dowarn) warn("Use of uninitialized variable"); STR_GROW(str, 30); s = str->str_ptr; } *s = '\0'; str->str_cur = s - str->str_ptr; str->str_pok = 1; #ifdef DEBUGGING if (debug & 32) fprintf(stderr,"0x%lx ptr(%s)\n",str,str->str_ptr); #endif return str->str_ptr; } double str_2num(str) register STR *str; { if (!str) return 0.0; if (str->str_state == SS_INCR) Str_Grow(str,0); /* just force copy down */ str->str_state = SS_NORM; if (str->str_len && str->str_pok) str->str_u.str_nval = atof(str->str_ptr); else { if (str == &str_undef) return 0.0; if (dowarn) warn("Use of uninitialized variable"); str->str_u.str_nval = 0.0; } str->str_nok = 1; #ifdef DEBUGGING if (debug & 32) fprintf(stderr,"0x%lx num(%g)\n",str,str->str_u.str_nval); #endif return str->str_u.str_nval; } /* Note: str_sset() should not be called with a source string that needs * be reused, since it may destroy the source string if it is marked * as temporary. */ void str_sset(dstr,sstr) STR *dstr; register STR *sstr; { #ifdef TAINT if (sstr) tainted |= sstr->str_tainted; #endif if (sstr == dstr || dstr == &str_undef) return; if (!sstr) dstr->str_pok = dstr->str_nok = 0; else if (sstr->str_pok) { /* * Check to see if we can just swipe the string. If so, it's a * possible small lose on short strings, but a big win on long ones. * It might even be a win on short strings if dstr->str_ptr * has to be allocated and sstr->str_ptr has to be freed. */ if (sstr->str_pok & SP_TEMP) { /* slated for free anyway? */ if (dstr->str_ptr) { if (dstr->str_state == SS_INCR) dstr->str_ptr -= dstr->str_u.str_useful; Safefree(dstr->str_ptr); } dstr->str_ptr = sstr->str_ptr; dstr->str_len = sstr->str_len; dstr->str_cur = sstr->str_cur; dstr->str_state = sstr->str_state; dstr->str_pok = sstr->str_pok & ~SP_TEMP; #ifdef TAINT dstr->str_tainted = sstr->str_tainted; #endif sstr->str_ptr = Nullch; sstr->str_len = 0; sstr->str_pok = 0; /* wipe out any weird flags */ sstr->str_state = 0; /* so sstr frees uneventfully */ } else { /* have to copy actual string */ if (dstr->str_ptr) { if (dstr->str_state == SS_INCR) { Str_Grow(dstr,0); } } str_nset(dstr,sstr->str_ptr,sstr->str_cur); } /*SUPPRESS 560*/ if (dstr->str_nok = sstr->str_nok) dstr->str_u.str_nval = sstr->str_u.str_nval; else { #ifdef STRUCTCOPY dstr->str_u = sstr->str_u; #else dstr->str_u.str_nval = sstr->str_u.str_nval; #endif if (dstr->str_cur == sizeof(STBP)) { char *tmps = dstr->str_ptr; if (*tmps == 'S' && bcmp(tmps,"StB",4) == 0) { if (dstr->str_magic && dstr->str_magic->str_rare == 'X') { str_free(dstr->str_magic); dstr->str_magic = Nullstr; } if (!dstr->str_magic) { dstr->str_magic = str_smake(sstr->str_magic); dstr->str_magic->str_rare = 'X'; } } } } } else if (sstr->str_nok) str_numset(dstr,sstr->str_u.str_nval); else { if (dstr->str_state == SS_INCR) Str_Grow(dstr,0); /* just force copy down */ #ifdef STRUCTCOPY dstr->str_u = sstr->str_u; #else dstr->str_u.str_nval = sstr->str_u.str_nval; #endif dstr->str_pok = dstr->str_nok = 0; } } void str_nset(str,ptr,len) register STR *str; register char *ptr; register STRLEN len; { if (str == &str_undef) return; STR_GROW(str, len + 1); if (ptr) Move(ptr,str->str_ptr,len,char); str->str_cur = len; *(str->str_ptr+str->str_cur) = '\0'; str->str_nok = 0; /* invalidate number */ str->str_pok = 1; /* validate pointer */ #ifdef TAINT str->str_tainted = tainted; #endif } void str_set(str,ptr) register STR *str; register char *ptr; { register STRLEN len; if (str == &str_undef) return; if (!ptr) ptr = ""; len = strlen(ptr); STR_GROW(str, len + 1); Move(ptr,str->str_ptr,len+1,char); str->str_cur = len; str->str_nok = 0; /* invalidate number */ str->str_pok = 1; /* validate pointer */ #ifdef TAINT str->str_tainted = tainted; #endif } void str_chop(str,ptr) /* like set but assuming ptr is in str */ register STR *str; register char *ptr; { register STRLEN delta; if (!ptr || !(str->str_pok)) return; delta = ptr - str->str_ptr; str->str_len -= delta; str->str_cur -= delta; str->str_ptr += delta; if (str->str_state == SS_INCR) str->str_u.str_useful += delta; else { str->str_u.str_useful = delta; str->str_state = SS_INCR; } str->str_nok = 0; /* invalidate number */ str->str_pok = 1; /* validate pointer (and unstudy str) */ } void str_ncat(str,ptr,len) register STR *str; register char *ptr; register STRLEN len; { if (str == &str_undef) return; if (!(str->str_pok)) (void)str_2ptr(str); STR_GROW(str, str->str_cur + len + 1); Move(ptr,str->str_ptr+str->str_cur,len,char); str->str_cur += len; *(str->str_ptr+str->str_cur) = '\0'; str->str_nok = 0; /* invalidate number */ str->str_pok = 1; /* validate pointer */ #ifdef TAINT str->str_tainted |= tainted; #endif } void str_scat(dstr,sstr) STR *dstr; register STR *sstr; { if (!sstr) return; #ifdef TAINT tainted |= sstr->str_tainted; #endif if (!(sstr->str_pok)) (void)str_2ptr(sstr); if (sstr) str_ncat(dstr,sstr->str_ptr,sstr->str_cur); } void str_cat(str,ptr) register STR *str; register char *ptr; { register STRLEN len; if (str == &str_undef) return; if (!ptr) return; if (!(str->str_pok)) (void)str_2ptr(str); len = strlen(ptr); STR_GROW(str, str->str_cur + len + 1); Move(ptr,str->str_ptr+str->str_cur,len+1,char); str->str_cur += len; str->str_nok = 0; /* invalidate number */ str->str_pok = 1; /* validate pointer */ #ifdef TAINT str->str_tainted |= tainted; #endif } char * str_append_till(str,from,fromend,delim,keeplist) register STR *str; register char *from; register char *fromend; register int delim; char *keeplist; { register char *to; register STRLEN len; if (str == &str_undef) return Nullch; if (!from) return Nullch; len = fromend - from; STR_GROW(str, str->str_cur + len + 1); str->str_nok = 0; /* invalidate number */ str->str_pok = 1; /* validate pointer */ to = str->str_ptr+str->str_cur; for (; from < fromend; from++,to++) { if (*from == '\\' && from+1 < fromend && delim != '\\') { if (!keeplist) { if (from[1] == delim || from[1] == '\\') from++; else *to++ = *from++; } else if (from[1] && index(keeplist,from[1])) *to++ = *from++; else from++; } else if (*from == delim) break; *to = *from; } *to = '\0'; str->str_cur = to - str->str_ptr; return from; } STR * #ifdef LEAKTEST str_new(x,len) int x; #else str_new(len) #endif STRLEN len; { register STR *str; if (freestrroot) { str = freestrroot; freestrroot = str->str_magic; str->str_magic = Nullstr; str->str_state = SS_NORM; } else { Newz(700+x,str,1,STR); } if (len) STR_GROW(str, len + 1); return str; } void str_magic(str, stab, how, name, namlen) register STR *str; STAB *stab; int how; char *name; STRLEN namlen; { if (str == &str_undef || str->str_magic) return; str->str_magic = Str_new(75,namlen); str = str->str_magic; str->str_u.str_stab = stab; str->str_rare = how; if (name) str_nset(str,name,namlen); } void str_insert(bigstr,offset,len,little,littlelen) STR *bigstr; STRLEN offset; STRLEN len; char *little; STRLEN littlelen; { register char *big; register char *mid; register char *midend; register char *bigend; register int i; if (bigstr == &str_undef) return; bigstr->str_nok = 0; bigstr->str_pok = SP_VALID; /* disable possible screamer */ i = littlelen - len; if (i > 0) { /* string might grow */ STR_GROW(bigstr, bigstr->str_cur + i + 1); big = bigstr->str_ptr; mid = big + offset + len; midend = bigend = big + bigstr->str_cur; bigend += i; *bigend = '\0'; while (midend > mid) /* shove everything down */ *--bigend = *--midend; Move(little,big+offset,littlelen,char); bigstr->str_cur += i; STABSET(bigstr); return; } else if (i == 0) { Move(little,bigstr->str_ptr+offset,len,char); STABSET(bigstr); return; } big = bigstr->str_ptr; mid = big + offset; midend = mid + len; bigend = big + bigstr->str_cur; if (midend > bigend) fatal("panic: str_insert"); if (mid - big > bigend - midend) { /* faster to shorten from end */ if (littlelen) { Move(little, mid, littlelen,char); mid += littlelen; } i = bigend - midend; if (i > 0) { Move(midend, mid, i,char); mid += i; } *mid = '\0'; bigstr->str_cur = mid - big; } /*SUPPRESS 560*/ else if (i = mid - big) { /* faster from front */ midend -= littlelen; mid = midend; str_chop(bigstr,midend-i); big += i; while (i--) *--midend = *--big; if (littlelen) Move(little, mid, littlelen,char); } else if (littlelen) { midend -= littlelen; str_chop(bigstr,midend); Move(little,midend,littlelen,char); } else { str_chop(bigstr,midend); } STABSET(bigstr); } /* make str point to what nstr did */ void str_replace(str,nstr) register STR *str; register STR *nstr; { if (str == &str_undef) return; if (str->str_state == SS_INCR) Str_Grow(str,0); /* just force copy down */ if (nstr->str_state == SS_INCR) Str_Grow(nstr,0); if (str->str_ptr) Safefree(str->str_ptr); str->str_ptr = nstr->str_ptr; str->str_len = nstr->str_len; str->str_cur = nstr->str_cur; str->str_pok = nstr->str_pok; str->str_nok = nstr->str_nok; #ifdef STRUCTCOPY str->str_u = nstr->str_u; #else str->str_u.str_nval = nstr->str_u.str_nval; #endif #ifdef TAINT str->str_tainted = nstr->str_tainted; #endif if (nstr->str_magic) str_free(nstr->str_magic); Safefree(nstr); } void str_free(str) register STR *str; { if (!str || str == &str_undef) return; if (str->str_state) { if (str->str_state == SS_FREE) /* already freed */ return; if (str->str_state == SS_INCR && !(str->str_pok & 2)) { str->str_ptr -= str->str_u.str_useful; str->str_len += str->str_u.str_useful; } } if (str->str_magic) str_free(str->str_magic); str->str_magic = freestrroot; #ifdef LEAKTEST if (str->str_len) { Safefree(str->str_ptr); str->str_ptr = Nullch; } if ((str->str_pok & SP_INTRP) && str->str_u.str_args) arg_free(str->str_u.str_args); Safefree(str); #else /* LEAKTEST */ if (str->str_len) { if (str->str_len > 127) { /* next user not likely to want more */ Safefree(str->str_ptr); /* so give it back to malloc */ str->str_ptr = Nullch; str->str_len = 0; } else str->str_ptr[0] = '\0'; } if ((str->str_pok & SP_INTRP) && str->str_u.str_args) arg_free(str->str_u.str_args); str->str_cur = 0; str->str_nok = 0; str->str_pok = 0; str->str_state = SS_FREE; #ifdef TAINT str->str_tainted = 0; #endif freestrroot = str; #endif /* LEAKTEST */ } STRLEN str_len(str) register STR *str; { if (!str) return 0; if (!(str->str_pok)) (void)str_2ptr(str); if (str->str_ptr) return str->str_cur; else return 0; } int str_eq(str1,str2) register STR *str1; register STR *str2; { if (!str1 || str1 == &str_undef) return (str2 == Nullstr || str2 == &str_undef || !str2->str_cur); if (!str2 || str2 == &str_undef) return !str1->str_cur; if (!str1->str_pok) (void)str_2ptr(str1); if (!str2->str_pok) (void)str_2ptr(str2); if (str1->str_cur != str2->str_cur) return 0; return !bcmp(str1->str_ptr, str2->str_ptr, str1->str_cur); } int str_cmp(str1,str2) register STR *str1; register STR *str2; { int retval; if (!str1 || str1 == &str_undef) return (str2 == Nullstr || str2 == &str_undef || !str2->str_cur)?0:-1; if (!str2 || str2 == &str_undef) return str1->str_cur != 0; if (!str1->str_pok) (void)str_2ptr(str1); if (!str2->str_pok) (void)str_2ptr(str2); if (str1->str_cur < str2->str_cur) { /*SUPPRESS 560*/ if (retval = memcmp(str1->str_ptr, str2->str_ptr, str1->str_cur)) return retval < 0 ? -1 : 1; else return -1; } /*SUPPRESS 560*/ else if (retval = memcmp(str1->str_ptr, str2->str_ptr, str2->str_cur)) return retval < 0 ? -1 : 1; else if (str1->str_cur == str2->str_cur) return 0; else return 1; } char * str_gets(str,fp,append) register STR *str; register FILE *fp; int append; { register char *bp; /* we're going to steal some values */ register int cnt; /* from the stdio struct and put EVERYTHING */ register STDCHAR *ptr; /* in the innermost loop into registers */ register int newline = rschar;/* (assuming >= 6 registers) */ int i; STRLEN bpx; int shortbuffered; if (str == &str_undef) return Nullch; if (rspara) { /* have to do this both before and after */ do { /* to make sure file boundaries work right */ i = getc(fp); if (i != '\n') { ungetc(i,fp); break; } } while (i != EOF); } #ifdef STDSTDIO /* Here is some breathtakingly efficient cheating */ cnt = fp->_cnt; /* get count into register */ str->str_nok = 0; /* invalidate number */ str->str_pok = 1; /* validate pointer */ if (str->str_len - append <= cnt + 1) { /* make sure we have the room */ if (cnt > 80 && str->str_len > append) { shortbuffered = cnt - str->str_len + append + 1; cnt -= shortbuffered; } else { shortbuffered = 0; STR_GROW(str, append+cnt+2);/* (remembering cnt can be -1) */ } } else shortbuffered = 0; bp = str->str_ptr + append; /* move these two too to registers */ ptr = fp->_ptr; for (;;) { screamer: while (--cnt >= 0) { /* this */ /* eat */ if ((*bp++ = *ptr++) == newline) /* really */ /* dust */ goto thats_all_folks; /* screams */ /* sed :-) */ } if (shortbuffered) { /* oh well, must extend */ cnt = shortbuffered; shortbuffered = 0; bpx = bp - str->str_ptr; /* prepare for possible relocation */ str->str_cur = bpx; STR_GROW(str, str->str_len + append + cnt + 2); bp = str->str_ptr + bpx; /* reconstitute our pointer */ continue; } fp->_cnt = cnt; /* deregisterize cnt and ptr */ fp->_ptr = ptr; i = _filbuf(fp); /* get more characters */ cnt = fp->_cnt; ptr = fp->_ptr; /* reregisterize cnt and ptr */ bpx = bp - str->str_ptr; /* prepare for possible relocation */ str->str_cur = bpx; STR_GROW(str, bpx + cnt + 2); bp = str->str_ptr + bpx; /* reconstitute our pointer */ if (i == newline) { /* all done for now? */ *bp++ = i; goto thats_all_folks; } else if (i == EOF) /* all done for ever? */ goto thats_really_all_folks; *bp++ = i; /* now go back to screaming loop */ } thats_all_folks: if (rslen > 1 && (bp - str->str_ptr < rslen || bcmp(bp - rslen, rs, rslen))) goto screamer; /* go back to the fray */ thats_really_all_folks: if (shortbuffered) cnt += shortbuffered; fp->_cnt = cnt; /* put these back or we're in trouble */ fp->_ptr = ptr; *bp = '\0'; str->str_cur = bp - str->str_ptr; /* set length */ #else /* !STDSTDIO */ /* The big, slow, and stupid way */ { static char buf[8192]; char * bpe = buf + sizeof(buf) - 3; screamer: bp = buf; while ((i = getc(fp)) != EOF && (*bp++ = i) != newline && bp < bpe) ; *bp = '\0'; if (append) str_cat(str, buf); else str_set(str, buf); if (i != EOF /* joy */ && (i != newline || (rslen > 1 && (str->str_cur < rslen || bcmp(str->str_ptr + str->str_cur - rslen, rs, rslen) ) ) ) ) { append = -1; goto screamer; } } #endif /* STDSTDIO */ if (rspara) { while (i != EOF) { i = getc(fp); if (i != '\n') { ungetc(i,fp); break; } } } return str->str_cur - append ? str->str_ptr : Nullch; } ARG * parselist(str) STR *str; { register CMD *cmd; register ARG *arg; CMD *oldcurcmd = curcmd; int oldperldb = perldb; int retval; perldb = 0; str_sset(linestr,str); in_eval++; oldoldbufptr = oldbufptr = bufptr = str_get(linestr); bufend = bufptr + linestr->str_cur; if (++loop_ptr >= loop_max) { loop_max += 128; Renew(loop_stack, loop_max, struct loop); } loop_stack[loop_ptr].loop_label = "_EVAL_"; loop_stack[loop_ptr].loop_sp = 0; #ifdef DEBUGGING if (debug & 4) { deb("(Pushing label #%d _EVAL_)\n", loop_ptr); } #endif if (setjmp(loop_stack[loop_ptr].loop_env)) { in_eval--; loop_ptr--; perldb = oldperldb; fatal("%s\n",stab_val(stabent("@",TRUE))->str_ptr); } #ifdef DEBUGGING if (debug & 4) { char *tmps = loop_stack[loop_ptr].loop_label; deb("(Popping label #%d %s)\n",loop_ptr, tmps ? tmps : "" ); } #endif loop_ptr--; error_count = 0; curcmd = &compiling; curcmd->c_line = oldcurcmd->c_line; retval = yyparse(); curcmd = oldcurcmd; perldb = oldperldb; in_eval--; if (retval || error_count) fatal("Invalid component in string or format"); cmd = eval_root; arg = cmd->c_expr; if (cmd->c_type != C_EXPR || cmd->c_next || arg->arg_type != O_LIST) fatal("panic: error in parselist %d %x %d", cmd->c_type, cmd->c_next, arg ? arg->arg_type : -1); cmd->c_expr = Nullarg; cmd_free(cmd); eval_root = Nullcmd; return arg; } void intrpcompile(src) STR *src; { register char *s = str_get(src); register char *send = s + src->str_cur; register STR *str; register char *t; STR *toparse; STRLEN len; register int brackets; register char *d; STAB *stab; char *checkpoint; int sawcase = 0; toparse = Str_new(76,0); str = Str_new(77,0); str_nset(str,"",0); str_nset(toparse,"",0); t = s; while (s < send) { if (*s == '\\' && s[1] && index("$@[{\\]}lLuUE",s[1])) { str_ncat(str, t, s - t); ++s; if (isALPHA(*s)) { str_ncat(str, "$c", 2); sawcase = (*s != 'E'); } else { if (*nointrp) { /* in a regular expression */ if (*s == '@') /* always strip \@ */ /*SUPPRESS 530*/ ; else /* don't strip \\, \[, \{ etc. */ str_ncat(str,s-1,1); } str_ncat(str, "$b", 2); } str_ncat(str, s, 1); ++s; t = s; } else if (*s == '$' && s+1 < send && *nointrp && index(nointrp,s[1])) { str_ncat(str, t, s - t); str_ncat(str, "$b", 2); str_ncat(str, s, 2); s += 2; t = s; } else if ((*s == '@' || *s == '$') && s+1 < send) { str_ncat(str,t,s-t); t = s; if (*s == '$' && s[1] == '#' && (isALPHA(s[2]) || s[2] == '_')) s++; s = scanident(s,send,tokenbuf); if (*t == '@' && (!(stab = stabent(tokenbuf,FALSE)) || (*s == '{' ? !stab_xhash(stab) : !stab_xarray(stab)) )) { str_ncat(str,"@",1); s = ++t; continue; /* grandfather @ from old scripts */ } str_ncat(str,"$a",2); str_ncat(toparse,",",1); if (t[1] != '{' && (*s == '[' || *s == '{' /* }} */ ) && (stab = stabent(tokenbuf,FALSE)) && ((*s == '[') ? (stab_xarray(stab) != 0) : (stab_xhash(stab) != 0)) ) { brackets = 0; checkpoint = s; do { switch (*s) { case '[': brackets++; break; case '{': brackets++; break; case ']': brackets--; break; case '}': brackets--; break; case '$': case '%': case '@': case '&': case '*': s = scanident(s,send,tokenbuf); continue; case '\'': case '"': /*SUPPRESS 68*/ s = cpytill(tokenbuf,s+1,send,*s,&len); if (s >= send) fatal("Unterminated string"); break; } s++; } while (brackets > 0 && s < send); if (s > send) fatal("Unmatched brackets in string"); if (*nointrp) { /* we're in a regular expression */ d = checkpoint; if (*d == '{' && s[-1] == '}') { /* maybe {n,m} */ ++d; if (isDIGIT(*d)) { /* matches /^{\d,?\d*}$/ */ if (*++d == ',') ++d; while (isDIGIT(*d)) d++; if (d == s - 1) s = checkpoint; /* Is {n,m}! Backoff! */ } } else if (*d == '[' && s[-1] == ']') { /* char class? */ int weight = 2; /* let's weigh the evidence */ char seen[256]; unsigned char un_char = 0, last_un_char; Zero(seen,256,char); *--s = '\0'; if (d[1] == '^') weight += 150; else if (d[1] == '$') weight -= 3; if (isDIGIT(d[1])) { if (d[2]) { if (isDIGIT(d[2]) && !d[3]) weight -= 10; } else weight -= 100; } for (d++; d < s; d++) { last_un_char = un_char; un_char = (unsigned char)*d; switch (*d) { case '&': case '$': weight -= seen[un_char] * 10; if (isALNUM(d[1])) { d = scanident(d,s,tokenbuf); if (stabent(tokenbuf,FALSE)) weight -= 100; else weight -= 10; } else if (*d == '$' && d[1] && index("[#!%*<>()-=",d[1])) { if (!d[2] || /*{*/ index("])} =",d[2])) weight -= 10; else weight -= 1; } break; case '\\': un_char = 254; if (d[1]) { if (index("wds",d[1])) weight += 100; else if (seen['\''] || seen['"']) weight += 1; else if (index("rnftb",d[1])) weight += 40; else if (isDIGIT(d[1])) { weight += 40; while (d[1] && isDIGIT(d[1])) d++; } } else weight += 100; break; case '-': if (last_un_char < (unsigned char) d[1] || d[1] == '\\') { if (index("aA01! ",last_un_char)) weight += 30; if (index("zZ79~",d[1])) weight += 30; } else weight -= 1; default: if (isALPHA(*d) && d[1] && isALPHA(d[1])) { bufptr = d; if (yylex() != WORD) weight -= 150; d = bufptr; } if (un_char == last_un_char + 1) weight += 5; weight -= seen[un_char]; break; } seen[un_char]++; } #ifdef DEBUGGING if (debug & 512) fprintf(stderr,"[%s] weight %d\n", checkpoint+1,weight); #endif *s++ = ']'; if (weight >= 0) /* probably a character class */ s = checkpoint; } } } if (*t == '@') str_ncat(toparse, "join($\",", 8); if (t[1] == '{' && s[-1] == '}') { str_ncat(toparse, t, 1); str_ncat(toparse, t+2, s - t - 3); } else str_ncat(toparse, t, s - t); if (*t == '@') str_ncat(toparse, ")", 1); t = s; } else s++; } str_ncat(str,t,s-t); if (sawcase) str_ncat(str, "$cE", 3); if (toparse->str_ptr && *toparse->str_ptr == ',') { *toparse->str_ptr = '('; str_ncat(toparse,",$$);",5); str->str_u.str_args = parselist(toparse); str->str_u.str_args->arg_len--; /* ignore $$ reference */ } else str->str_u.str_args = Nullarg; str_free(toparse); str->str_pok |= SP_INTRP; str->str_nok = 0; str_replace(src,str); } STR * interp(str,src,sp) register STR *str; STR *src; int sp; { register char *s; register char *t; register char *send; register STR **elem; int docase = 0; int l = 0; int u = 0; int L = 0; int U = 0; if (str == &str_undef) return Nullstr; if (!(src->str_pok & SP_INTRP)) { int oldsave = savestack->ary_fill; (void)savehptr(&curstash); curstash = curcmd->c_stash; /* so stabent knows right package */ intrpcompile(src); restorelist(oldsave); } s = src->str_ptr; /* assumed valid since str_pok set */ t = s; send = s + src->str_cur; if (src->str_u.str_args) { (void)eval(src->str_u.str_args,G_ARRAY,sp); /* Assuming we have correct # of args */ elem = stack->ary_array + sp; } str_nset(str,"",0); while (s < send) { if (*s == '$' && s+1 < send) { if (s-t > 0) str_ncat(str,t,s-t); switch(*++s) { default: fatal("panic: unknown interp cookie\n"); break; case 'a': str_scat(str,*++elem); break; case 'b': str_ncat(str,++s,1); break; case 'c': if (docase && str->str_cur >= docase) { char *b = str->str_ptr + --docase; if (L) lcase(b, str->str_ptr + str->str_cur); else if (U) ucase(b, str->str_ptr + str->str_cur); if (u) /* note that l & u are independent of L & U */ ucase(b, b+1); else if (l) lcase(b, b+1); l = u = 0; } docase = str->str_cur + 1; switch (*++s) { case 'u': u = 1; l = 0; break; case 'U': U = 1; L = 0; break; case 'l': l = 1; u = 0; break; case 'L': L = 1; U = 0; break; case 'E': docase = L = U = l = u = 0; break; } break; } t = ++s; } else s++; } if (s-t > 0) str_ncat(str,t,s-t); return str; } static void ucase(s,send) register char *s; register char *send; { while (s < send) { if (isLOWER(*s)) *s = toupper(*s); s++; } } static void lcase(s,send) register char *s; register char *send; { while (s < send) { if (isUPPER(*s)) *s = tolower(*s); s++; } } void str_inc(str) register STR *str; { register char *d; if (!str || str == &str_undef) return; if (str->str_nok) { str->str_u.str_nval += 1.0; str->str_pok = 0; return; } if (!str->str_pok || !*str->str_ptr) { str->str_u.str_nval = 1.0; str->str_nok = 1; str->str_pok = 0; return; } d = str->str_ptr; while (isALPHA(*d)) d++; while (isDIGIT(*d)) d++; if (*d) { str_numset(str,atof(str->str_ptr) + 1.0); /* punt */ return; } d--; while (d >= str->str_ptr) { if (isDIGIT(*d)) { if (++*d <= '9') return; *(d--) = '0'; } else { ++*d; if (isALPHA(*d)) return; *(d--) -= 'z' - 'a' + 1; } } /* oh,oh, the number grew */ STR_GROW(str, str->str_cur + 2); str->str_cur++; for (d = str->str_ptr + str->str_cur; d > str->str_ptr; d--) *d = d[-1]; if (isDIGIT(d[1])) *d = '1'; else *d = d[1]; } void str_dec(str) register STR *str; { if (!str || str == &str_undef) return; if (str->str_nok) { str->str_u.str_nval -= 1.0; str->str_pok = 0; return; } if (!str->str_pok) { str->str_u.str_nval = -1.0; str->str_nok = 1; return; } str_numset(str,atof(str->str_ptr) - 1.0); } /* Make a string that will exist for the duration of the expression * evaluation. Actually, it may have to last longer than that, but * hopefully cmd_exec won't free it until it has been assigned to a * permanent location. */ static long tmps_size = -1; STR * str_mortal(oldstr) STR *oldstr; { register STR *str = Str_new(78,0); str_sset(str,oldstr); if (++tmps_max > tmps_size) { tmps_size = tmps_max; if (!(tmps_size & 127)) { if (tmps_size) Renew(tmps_list, tmps_size + 128, STR*); else New(702,tmps_list, 128, STR*); } } tmps_list[tmps_max] = str; if (str->str_pok) str->str_pok |= SP_TEMP; return str; } /* same thing without the copying */ STR * str_2mortal(str) register STR *str; { if (!str || str == &str_undef) return str; if (++tmps_max > tmps_size) { tmps_size = tmps_max; if (!(tmps_size & 127)) { if (tmps_size) Renew(tmps_list, tmps_size + 128, STR*); else New(704,tmps_list, 128, STR*); } } tmps_list[tmps_max] = str; if (str->str_pok) str->str_pok |= SP_TEMP; return str; } STR * str_make(s,len) char *s; STRLEN len; { register STR *str = Str_new(79,0); if (!len) len = strlen(s); str_nset(str,s,len); return str; } STR * str_nmake(n) double n; { register STR *str = Str_new(80,0); str_numset(str,n); return str; } /* make an exact duplicate of old */ STR * str_smake(old) register STR *old; { register STR *new = Str_new(81,0); if (!old) return Nullstr; if (old->str_state == SS_FREE) { warn("semi-panic: attempt to dup freed string"); return Nullstr; } if (old->str_state == SS_INCR && !(old->str_pok & 2)) Str_Grow(old,0); if (new->str_ptr) Safefree(new->str_ptr); StructCopy(old,new,STR); if (old->str_ptr) { new->str_ptr = nsavestr(old->str_ptr,old->str_len); new->str_pok &= ~SP_TEMP; } return new; } void str_reset(s,stash) register char *s; HASH *stash; { register HENT *entry; register STAB *stab; register STR *str; register int i; register SPAT *spat; register int max; if (!*s) { /* reset ?? searches */ for (spat = stash->tbl_spatroot; spat != Nullspat; spat = spat->spat_next) { spat->spat_flags &= ~SPAT_USED; } return; } /* reset variables */ if (!stash->tbl_array) return; while (*s) { i = *s; if (s[1] == '-') { s += 2; } max = *s++; for ( ; i <= max; i++) { for (entry = stash->tbl_array[i]; entry; entry = entry->hent_next) { stab = (STAB*)entry->hent_val; str = stab_val(stab); str->str_cur = 0; str->str_nok = 0; #ifdef TAINT str->str_tainted = tainted; #endif if (str->str_ptr != Nullch) str->str_ptr[0] = '\0'; if (stab_xarray(stab)) { aclear(stab_xarray(stab)); } if (stab_xhash(stab)) { hclear(stab_xhash(stab), FALSE); if (stab == envstab) environ[0] = Nullch; } } } } } #ifdef TAINT void taintproper(s) char *s; { #ifdef DEBUGGING if (debug & 2048) fprintf(stderr,"%s %d %d %d\n",s,tainted,uid, euid); #endif if (tainted && (!euid || euid != uid || egid != gid || taintanyway)) { if (!unsafe) fatal("%s", s); else if (dowarn) warn("%s", s); } } void taintenv() { register STR *envstr; envstr = hfetch(stab_hash(envstab),"PATH",4,FALSE); if (envstr == &str_undef || envstr->str_tainted) { tainted = 1; if (envstr->str_tainted == 2) taintproper("Insecure directory in PATH"); else taintproper("Insecure PATH"); } envstr = hfetch(stab_hash(envstab),"IFS",3,FALSE); if (envstr != &str_undef && envstr->str_tainted) { tainted = 1; taintproper("Insecure IFS"); } } #endif /* TAINT */