diff options
Diffstat (limited to 'src/cmd/rc/code.c')
-rw-r--r-- | src/cmd/rc/code.c | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/src/cmd/rc/code.c b/src/cmd/rc/code.c new file mode 100644 index 00000000..748b9f82 --- /dev/null +++ b/src/cmd/rc/code.c @@ -0,0 +1,430 @@ +#include "rc.h" +#include "io.h" +#include "exec.h" +#include "fns.h" +#include "getflags.h" +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] +int codep, ncode; +#define emitf(x) ((void)(codep!=ncode || morecode()), codebuf[codep].f=(x), codep++) +#define emiti(x) ((void)(codep!=ncode || morecode()), codebuf[codep].i=(x), codep++) +#define emits(x) ((void)(codep!=ncode || morecode()), codebuf[codep].s=(x), codep++) +void stuffdot(int); +char *fnstr(tree*); +void outcode(tree*, int); +void codeswitch(tree*, int); +int iscase(tree*); +code *codecopy(code*); +void codefree(code*); +int morecode(void){ + ncode+=100; + codebuf=(code *)realloc((char *)codebuf, ncode*sizeof codebuf[0]); + if(codebuf==0) panic("Can't realloc %d bytes in morecode!", + ncode*sizeof codebuf[0]); + return 0; +} +void stuffdot(int a){ + if(a<0 || codep<=a) panic("Bad address %d in stuffdot", a); + codebuf[a].i=codep; +} +int compile(tree *t) +{ + ncode=100; + codebuf=(code *)emalloc(ncode*sizeof codebuf[0]); + codep=0; + emiti(0); /* reference count */ + outcode(t, flag['e']?1:0); + if(nerror){ + efree((char *)codebuf); + return 0; + } + readhere(); + emitf(Xreturn); + emitf(0); + return 1; +} +void cleanhere(char *f) +{ + emitf(Xdelhere); + emits(strdup(f)); +} +char *fnstr(tree *t) +{ + io *f=openstr(); + char *v; + extern char nl; + char svnl=nl; + nl=';'; + pfmt(f, "%t", t); + nl=svnl; + v=f->strp; + f->strp=0; + closeio(f); + return v; +} +void outcode(tree *t, int eflag) +{ + int p, q; + tree *tt; + if(t==0) return; + if(t->type!=NOT && t->type!=';') runq->iflast=0; + switch(t->type){ + default: + pfmt(err, "bad type %d in outcode\n", t->type); + break; + case '$': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xdol); + break; + case '"': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xqdol); + break; + case SUB: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmark); + outcode(c1, eflag); + emitf(Xsub); + break; + case '&': + emitf(Xasync); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + break; + case ';': + outcode(c0, eflag); + outcode(c1, eflag); + break; + case '^': + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xconc); + break; + case '`': + emitf(Xbackq); + p=emiti(0); + outcode(c0, 0); + emitf(Xexit); + stuffdot(p); + break; + case ANDAND: + outcode(c0, 0); + emitf(Xtrue); + p=emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case ARGLIST: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case BANG: + outcode(c0, eflag); + emitf(Xbang); + break; + case PCMD: + case BRACE: + outcode(c0, eflag); + break; + case COUNT: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xcount); + break; + case FN: + emitf(Xmark); + outcode(c0, eflag); + if(c1){ + emitf(Xfn); + p=emiti(0); + emits(fnstr(c1)); + outcode(c1, eflag); + emitf(Xunlocal); /* get rid of $* */ + emitf(Xreturn); + stuffdot(p); + } + else + emitf(Xdelfn); + break; + case IF: + outcode(c0, 0); + emitf(Xif); + p=emiti(0); + outcode(c1, eflag); + emitf(Xwastrue); + stuffdot(p); + break; + case NOT: + if(!runq->iflast) yyerror("`if not' does not follow `if(...)'"); + emitf(Xifnot); + p=emiti(0); + outcode(c0, eflag); + stuffdot(p); + break; + case OROR: + outcode(c0, 0); + emitf(Xfalse); + p=emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case PAREN: + outcode(c0, eflag); + break; + case SIMPLE: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xsimple); + if(eflag) emitf(Xeflag); + break; + case SUBSHELL: + emitf(Xsubshell); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + if(eflag) emitf(Xeflag); + break; + case SWITCH: + codeswitch(t, eflag); + break; + case TWIDDLE: + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmatch); + if(eflag) emitf(Xeflag); + break; + case WHILE: + q=codep; + outcode(c0, 0); + if(q==codep) emitf(Xsettrue); /* empty condition == while(true) */ + emitf(Xtrue); + p=emiti(0); + outcode(c1, eflag); + emitf(Xjump); + emiti(q); + stuffdot(p); + break; + case WORDS: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case FOR: + emitf(Xmark); + if(c1){ + outcode(c1, eflag); + emitf(Xglob); + } + else{ + emitf(Xmark); + emitf(Xword); + emits(strdup("*")); + emitf(Xdol); + } + emitf(Xmark); /* dummy value for Xlocal */ + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + p=emitf(Xfor); + q=emiti(0); + outcode(c2, eflag); + emitf(Xjump); + emiti(p); + stuffdot(q); + emitf(Xunlocal); + break; + case WORD: + emitf(Xword); + emits(strdup(t->str)); + break; + case DUP: + if(t->rtype==DUPFD){ + emitf(Xdup); + emiti(t->fd0); + emiti(t->fd1); + } + else{ + emitf(Xclose); + emiti(t->fd0); + } + outcode(c1, eflag); + emitf(Xpopredir); + break; + case PIPEFD: + emitf(Xpipefd); + emiti(t->rtype); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + break; + case REDIR: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xglob); + switch(t->rtype){ + case APPEND: + emitf(Xappend); + break; + case WRITE: + emitf(Xwrite); + break; + case READ: + case HERE: + emitf(Xread); + break; + } + emiti(t->fd0); + outcode(c1, eflag); + emitf(Xpopredir); + break; + case '=': + tt=t; + for(;t && t->type=='=';t=c2); + if(t){ + for(t=tt;t->type=='=';t=c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + } + t=tt; + outcode(c2, eflag); + for(;t->type=='=';t=c2) emitf(Xunlocal); + } + else{ + for(t=tt;t;t=c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xassign); + } + } + t=tt; /* so tests below will work */ + break; + case PIPE: + emitf(Xpipe); + emiti(t->fd0); + emiti(t->fd1); + p=emiti(0); + q=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + outcode(c1, eflag); + emitf(Xreturn); + stuffdot(q); + emitf(Xpipewait); + break; + } + if(t->type!=NOT && t->type!=';') + runq->iflast=t->type==IF; + else if(c0) runq->iflast=c0->type==IF; +} +/* + * switch code looks like this: + * Xmark + * (get switch value) + * Xjump 1f + * out: Xjump leave + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: + * leave: + * Xpopm + */ +void codeswitch(tree *t, int eflag) +{ + int leave; /* patch jump address to leave switch */ + int out; /* jump here to leave switch */ + int nextcase; /* patch jump address to next case */ + tree *tt; + if(c1->child[0]==nil + || c1->child[0]->type!=';' + || !iscase(c1->child[0]->child[0])){ + yyerror("case missing in switch"); + return; + } + emitf(Xmark); + outcode(c0, eflag); + emitf(Xjump); + nextcase=emiti(0); + out=emitf(Xjump); + leave=emiti(0); + stuffdot(nextcase); + t=c1->child[0]; + while(t->type==';'){ + tt=c1; + emitf(Xmark); + for(t=c0->child[0];t->type==ARGLIST;t=c0) outcode(c1, eflag); + emitf(Xcase); + nextcase=emiti(0); + t=tt; + for(;;){ + if(t->type==';'){ + if(iscase(c0)) break; + outcode(c0, eflag); + t=c1; + } + else{ + if(!iscase(t)) outcode(t, eflag); + break; + } + } + emitf(Xjump); + emiti(out); + stuffdot(nextcase); + } + stuffdot(leave); + emitf(Xpopm); +} +int iscase(tree *t) +{ + if(t->type!=SIMPLE) return 0; + do t=c0; while(t->type==ARGLIST); + return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0; +} +code *codecopy(code *cp) +{ + cp[0].i++; + return cp; +} +void codefree(code *cp) +{ + code *p; + if(--cp[0].i!=0) return; + for(p=cp+1;p->f;p++){ + if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite + || p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse + || p->f==Xfor || p->f==Xjump + || p->f==Xsubshell || p->f==Xtrue) p++; + else if(p->f==Xdup || p->f==Xpipefd) p+=2; + else if(p->f==Xpipe) p+=4; + else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s); + else if(p->f==Xfn){ + efree(p[2].s); + p+=2; + } + } + efree((char *)cp); +} |