plan9fox/sys/src/cmd/rc/exec.c
cinap_lenrek b90036a062 rc: fix everything
Untangle the lexer and interpreter thread state.

Fix the file and line number error reporting, getting rid of
Xsrcfile instruction, as the whole code block can only come
from a single file, stuff the source file in slot[1] of the
code block instead.

Remove limitations for globber (path element limits)
and be more intelligent about handling globbing by
inserting Xglob instruction only when needed and not
run it over every Xsimple argument list.

Remove fragile ndot magic and make it explicit by adding
the -q flag to . builtin command.

Add -b flag for full compilation.

Make exitnext() smart, so we can speculate thru rcmain and
avoid the fork().

Get rid of all print(2) format functions and use io
instead.

Improve the io library, adding rstr() to handle tokenization,
which allows us to look ahead in the already read buffer
for the terminators, avoiding alot of string copies.

Auto indent pcmd(), to make line number reporting more usefull.

Implement here documents properly, so they can work everywhere.
2021-12-31 15:27:10 +00:00

1089 lines
18 KiB
C

#include "rc.h"
#include "getflags.h"
#include "exec.h"
#include "io.h"
#include "fns.h"
/*
* Start executing the given code at the given pc with the given redirection
*/
void
start(code *c, int pc, var *local, redir *redir)
{
thread *p = new(thread);
p->code = codecopy(c);
p->line = 0;
p->pc = pc;
p->argv = 0;
p->redir = p->startredir = redir;
p->lex = 0;
p->local = local;
p->iflag = 0;
p->ret = runq;
runq = p;
}
void
startfunc(var *func, word *starval, var *local, redir *redir)
{
start(func->fn, func->pc, local, redir);
runq->local = newvar("*", runq->local);
runq->local->val = starval;
runq->local->changed = 1;
}
static void
popthread(void)
{
thread *p = runq;
while(p->argv) poplist();
while(p->local && (p->ret==0 || p->local!=p->ret->local))
Xunlocal();
runq = p->ret;
if(p->lex) freelexer(p->lex);
codefree(p->code);
free(p);
}
word*
Newword(char *s, word *next)
{
word *p=new(word);
p->word = s;
p->next = next;
return p;
}
word*
newword(char *s, word *next)
{
return Newword(estrdup(s), next);
}
word*
Pushword(char *s)
{
word *p;
if(s==0)
panic("null pushword", 0);
if(runq->argv==0)
panic("pushword but no argv!", 0);
p = Newword(s, runq->argv->words);
runq->argv->words = p;
return p;
}
word*
pushword(char *s)
{
return Pushword(estrdup(s));
}
char*
Freeword(word *p)
{
char *s = p->word;
free(p);
return s;
}
void
freewords(word *w)
{
word *p;
while((p = w)!=0){
w = w->next;
free(Freeword(p));
}
}
char*
Popword(void)
{
word *p;
if(runq->argv==0)
panic("popword but no argv!", 0);
p = runq->argv->words;
if(p==0)
panic("popword but no word!", 0);
runq->argv->words = p->next;
return Freeword(p);
}
void
popword(void)
{
free(Popword());
}
void
pushlist(void)
{
list *p = new(list);
p->words = 0;
p->next = runq->argv;
runq->argv = p;
}
word*
Poplist(void)
{
word *w;
list *p = runq->argv;
if(p==0)
panic("poplist but no argv", 0);
w = p->words;
runq->argv = p->next;
free(p);
return w;
}
void
poplist(void)
{
freewords(Poplist());
}
int
count(word *w)
{
int n;
for(n = 0;w;n++) w = w->next;
return n;
}
void
pushredir(int type, int from, int to)
{
redir *rp = new(redir);
rp->type = type;
rp->from = from;
rp->to = to;
rp->next = runq->redir;
runq->redir = rp;
}
static void
dontclose(int fd)
{
redir *rp;
if(fd<0)
return;
for(rp = runq->redir; rp != runq->startredir; rp = rp->next){
if(rp->type == RCLOSE && rp->from == fd){
rp->type = 0;
break;
}
}
}
/*
* we are about to start a new thread that should exit on
* return, so the current stack is not needed anymore.
* free all the threads and lexers, but preserve the
* redirections and anything referenced by local.
*/
void
turfstack(var *local)
{
while(local){
thread *p;
for(p = runq; p && p->local == local; p = p->ret)
p->local = local->next;
local = local->next;
}
while(runq) {
if(runq->lex) dontclose(runq->lex->input->fd);
popthread();
}
}
void
shuffleredir(void)
{
redir **rr, *rp;
rp = runq->redir;
if(rp==0)
return;
runq->redir = rp->next;
rp->next = runq->startredir;
for(rr = &runq->redir; *rr != rp->next; rr = &((*rr)->next))
;
*rr = rp;
}
/*
* get command line flags, initialize keywords & traps.
* get values from environment.
* set $pid, $cflag, $*
* fabricate bootstrap code and start it (*=(argv);. -bq /usr/lib/rcmain $*)
* start interpreting code
*/
char *argv0="rc";
void
main(int argc, char *argv[])
{
code bootstrap[20];
char num[12];
char *rcmain=Rcmain;
int i;
argv0 = argv[0];
argc = getflags(argc, argv, "SsrdiIlxebpvVc:1m:1[command]", 1);
if(argc==-1)
usage("[file [arg ...]]");
if(argv[0][0]=='-')
flag['l'] = flagset;
if(flag['I'])
flag['i'] = 0;
else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
if(flag['m']) rcmain = flag['m'][0];
err = openiofd(2);
kinit();
Trapinit();
Vinit();
inttoascii(num, mypid = getpid());
setvar("pid", newword(num, (word *)0));
setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0)
:(word *)0);
setvar("rcname", newword(argv[0], (word *)0));
bootstrap[0].i = 1;
bootstrap[1].s="*bootstrap*";
bootstrap[2].f = Xmark;
bootstrap[3].f = Xword;
bootstrap[4].s="*";
bootstrap[5].f = Xassign;
bootstrap[6].f = Xmark;
bootstrap[7].f = Xmark;
bootstrap[8].f = Xword;
bootstrap[9].s="*";
bootstrap[10].f = Xdol;
bootstrap[11].f = Xword;
bootstrap[12].s = rcmain;
bootstrap[13].f = Xword;
bootstrap[14].s="-bq";
bootstrap[15].f = Xword;
bootstrap[16].s=".";
bootstrap[17].f = Xsimple;
bootstrap[18].f = Xexit;
bootstrap[19].f = 0;
start(bootstrap, 2, (var*)0, (redir*)0);
/* prime bootstrap argv */
pushlist();
for(i = argc-1;i!=0;--i) pushword(argv[i]);
for(;;){
if(flag['r'])
pfnc(err, runq);
(*runq->code[runq->pc++].f)();
if(ntrap)
dotrap();
}
}
/*
* Opcode routines
* Arguments on stack (...)
* Arguments in line [...]
* Code in line with jump around {...}
*
* Xappend(file)[fd] open file to append
* Xassign(name, val) assign val to name
* Xasync{... Xexit} make thread for {}, no wait
* Xbackq(split){... Xreturn} make thread for {}, push stdout
* Xbang complement condition
* Xcase(pat, value){...} exec code on match, leave (value) on
* stack
* Xclose[i] close file descriptor
* Xconc(left, right) concatenate, push results
* Xcount(name) push var count
* Xdelfn(name) delete function definition
* Xdol(name) get variable value
* Xdup[i j] dup file descriptor
* Xexit rc exits with status
* Xfalse{...} execute {} if false
* Xfn(name){... Xreturn} define function
* Xfor(var, list){... Xreturn} for loop
* Xglob(word) glob word inplace
* Xjump[addr] goto
* Xlocal(name, val) create local variable, assign value
* Xmark mark stack
* Xmatch(pat, str) match pattern, set status
* Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads,
* wait for both
* Xpipefd[type]{... Xreturn} connect {} to pipe (input or output,
* depending on type), push /dev/fd/??
* Xpopm(value) pop value from stack
* Xpush(words) push words down a list
* Xqw(words) quote words inplace
* Xrdwr(file)[fd] open file for reading and writing
* Xread(file)[fd] open file to read
* Xreturn kill thread
* Xsimple(args) run command and wait
* Xsrcline[line] set current source line number
* Xsubshell{... Xexit} execute {} in a subshell and wait
* Xtrue{...} execute {} if true
* Xunlocal delete local variable
* Xword[string] push string
* Xwrite(file)[fd] open file to write
*/
void
Xappend(void)
{
char *file;
int fd;
switch(count(runq->argv->words)){
default:
Xerror1(">> requires singleton");
return;
case 0:
Xerror1(">> requires file");
return;
case 1:
break;
}
file = runq->argv->words->word;
if((fd = Open(file, 1))<0 && (fd = Creat(file))<0){
Xerror("can't open");
return;
}
Seek(fd, 0L, 2);
pushredir(ROPEN, fd, runq->code[runq->pc++].i);
poplist();
}
void
Xsettrue(void)
{
setstatus("");
}
void
Xbang(void)
{
setstatus(truestatus()?"false":"");
}
void
Xclose(void)
{
pushredir(RCLOSE, runq->code[runq->pc++].i, 0);
}
void
Xdup(void)
{
pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
runq->pc+=2;
}
void
Xeflag(void)
{
if(!truestatus()) Xexit();
}
void
Xexit(void)
{
static int beenhere = 0;
if(getpid()==mypid && !beenhere){
var *trapreq = vlook("sigexit");
word *starval = vlook("*")->val;
if(trapreq->fn){
beenhere = 1;
--runq->pc;
startfunc(trapreq, copywords(starval, (word*)0), (var*)0, (redir*)0);
return;
}
}
Exit(getstatus());
}
void
Xfalse(void)
{
if(truestatus()) runq->pc = runq->code[runq->pc].i;
else runq->pc++;
}
int ifnot; /* dynamic if not flag */
void
Xifnot(void)
{
if(ifnot)
runq->pc++;
else
runq->pc = runq->code[runq->pc].i;
}
void
Xjump(void)
{
runq->pc = runq->code[runq->pc].i;
}
void
Xmark(void)
{
pushlist();
}
void
Xpopm(void)
{
poplist();
}
void
Xpush(void)
{
word *t, *h = Poplist();
for(t = h; t->next; t = t->next)
;
t->next = runq->argv->words;
runq->argv->words = h;
}
void
Xhere(void)
{
char *file;
int fd;
io *io;
switch(count(runq->argv->words)){
default:
Xerror1("<< requires singleton");
return;
case 0:
Xerror1("<< requires file");
return;
case 1:
break;
}
file = mktemp(runq->argv->words->word);
if((fd = Creat(file))<0){
Xerror("can't open");
return;
}
io = openiofd(fd);
psubst(io, (uchar*)runq->code[runq->pc++].s);
flushio(io);
closeio(io);
/* open for reading and unlink */
if((fd = Open(file, 3))<0){
Xerror("can't open");
return;
}
pushredir(ROPEN, fd, runq->code[runq->pc++].i);
poplist();
}
void
Xread(void)
{
int fd;
switch(count(runq->argv->words)){
default:
Xerror1("< requires singleton");
return;
case 0:
Xerror1("< requires file");
return;
case 1:
break;
}
if((fd = Open(runq->argv->words->word, 0))<0){
Xerror("can't open");
return;
}
pushredir(ROPEN, fd, runq->code[runq->pc++].i);
poplist();
}
void
Xrdwr(void)
{
int fd;
switch(count(runq->argv->words)){
default:
Xerror1("<> requires singleton");
return;
case 0:
Xerror1("<> requires file");
return;
case 1:
break;
}
if((fd = Open(runq->argv->words->word, 2))<0){
Xerror("can't open");
return;
}
pushredir(ROPEN, fd, runq->code[runq->pc++].i);
poplist();
}
void
Xpopredir(void)
{
redir *rp = runq->redir;
if(rp==0)
panic("Xpopredir null!", 0);
runq->redir = rp->next;
if(rp->type==ROPEN)
Close(rp->from);
free(rp);
}
void
Xreturn(void)
{
while(runq->redir!=runq->startredir)
Xpopredir();
popthread();
if(runq==0)
Exit(getstatus());
}
void
Xtrue(void)
{
if(truestatus()) runq->pc++;
else runq->pc = runq->code[runq->pc].i;
}
void
Xif(void)
{
ifnot = 1;
if(truestatus()) runq->pc++;
else runq->pc = runq->code[runq->pc].i;
}
void
Xwastrue(void)
{
ifnot = 0;
}
void
Xword(void)
{
pushword(runq->code[runq->pc++].s);
}
void
Xwrite(void)
{
int fd;
switch(count(runq->argv->words)){
default:
Xerror1("> requires singleton");
return;
case 0:
Xerror1("> requires file");
return;
case 1:
break;
}
if((fd = Creat(runq->argv->words->word))<0){
Xerror("can't open");
return;
}
pushredir(ROPEN, fd, runq->code[runq->pc++].i);
poplist();
}
void
Xmatch(void)
{
word *p;
char *s;
setstatus("no match");
s = runq->argv->words->word;
for(p = runq->argv->next->words;p;p = p->next)
if(match(s, p->word, '\0')){
setstatus("");
break;
}
poplist();
poplist();
}
void
Xcase(void)
{
word *p;
char *s;
int ok = 0;
s = runq->argv->next->words->word;
for(p = runq->argv->words;p;p = p->next){
if(match(s, p->word, '\0')){
ok = 1;
break;
}
}
if(ok)
runq->pc++;
else
runq->pc = runq->code[runq->pc].i;
poplist();
}
static word*
conclist(word *lp, word *rp, word *tail)
{
word *v, *p, **end;
int ln, rn;
for(end = &v;;){
ln = strlen(lp->word), rn = strlen(rp->word);
p = Newword(emalloc(ln+rn+1), (word *)0);
memmove(p->word, lp->word, ln);
memmove(p->word+ln, rp->word, rn+1);
*end = p, end = &p->next;
if(lp->next == 0 && rp->next == 0)
break;
if(lp->next) lp = lp->next;
if(rp->next) rp = rp->next;
}
*end = tail;
return v;
}
void
Xconc(void)
{
word *lp = runq->argv->words;
word *rp = runq->argv->next->words;
word *vp = runq->argv->next->next->words;
int lc = count(lp), rc = count(rp);
if(lc!=0 || rc!=0){
if(lc==0 || rc==0){
Xerror1("null list in concatenation");
return;
}
if(lc!=1 && rc!=1 && lc!=rc){
Xerror1("mismatched list lengths in concatenation");
return;
}
vp = conclist(lp, rp, vp);
}
poplist();
poplist();
runq->argv->words = vp;
}
void
Xassign(void)
{
var *v;
if(count(runq->argv->words)!=1){
Xerror1("variable name not singleton!");
return;
}
v = vlook(runq->argv->words->word);
poplist();
freewords(v->val);
v->val = Poplist();
v->changed = 1;
}
/*
* copy arglist a, adding the copy to the front of tail
*/
word*
copywords(word *a, word *tail)
{
word *v = 0, **end;
for(end=&v;a;a = a->next,end=&(*end)->next)
*end = newword(a->word, 0);
*end = tail;
return v;
}
void
Xdol(void)
{
word *a, *star;
char *s, *t;
int n;
if(count(runq->argv->words)!=1){
Xerror1("variable name not singleton!");
return;
}
n = 0;
s = runq->argv->words->word;
for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
a = runq->argv->next->words;
if(n==0 || *t)
a = copywords(vlook(s)->val, a);
else{
star = vlook("*")->val;
if(star && 1<=n && n<=count(star)){
while(--n) star = star->next;
a = newword(star->word, a);
}
}
poplist();
runq->argv->words = a;
}
void
Xqw(void)
{
char *s, *d;
word *a, *p;
int n;
a = runq->argv->words;
if(a==0){
pushword("");
return;
}
if(a->next==0)
return;
n=0;
for(p=a;p;p=p->next)
n+=1+strlen(p->word);
s = emalloc(n+1);
d = s;
d += strlen(strcpy(d, a->word));
for(p=a->next;p;p=p->next){
*d++=' ';
d += strlen(strcpy(d, p->word));
}
free(a->word);
freewords(a->next);
a->word = s;
a->next = 0;
}
static word*
copynwords(word *a, word *tail, int n)
{
word *v, **end;
v = 0;
end = &v;
while(n-- > 0){
*end = newword(a->word, 0);
end = &(*end)->next;
a = a->next;
}
*end = tail;
return v;
}
static word*
subwords(word *val, int len, word *sub, word *a)
{
int n, m;
char *s;
if(sub==0)
return a;
a = subwords(val, len, sub->next, a);
s = sub->word;
m = 0;
n = 0;
while('0'<=*s && *s<='9')
n = n*10+ *s++ -'0';
if(*s == '-'){
if(*++s == 0)
m = len - n;
else{
while('0'<=*s && *s<='9')
m = m*10+ *s++ -'0';
m -= n;
}
}
if(n<1 || n>len || m<0)
return a;
if(n+m>len)
m = len-n;
while(--n > 0)
val = val->next;
return copynwords(val, a, m+1);
}
void
Xsub(void)
{
word *a, *v;
char *s;
if(count(runq->argv->next->words)!=1){
Xerror1("variable name not singleton!");
return;
}
s = runq->argv->next->words->word;
a = runq->argv->next->next->words;
v = vlook(s)->val;
a = subwords(v, count(v), runq->argv->words, a);
poplist();
poplist();
runq->argv->words = a;
}
void
Xcount(void)
{
word *a;
char *s, *t, num[12];
int n;
if(count(runq->argv->words)!=1){
Xerror1("variable name not singleton!");
return;
}
n = 0;
s = runq->argv->words->word;
for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
if(n==0 || *t){
a = vlook(s)->val;
inttoascii(num, count(a));
}
else{
a = vlook("*")->val;
inttoascii(num, a && 1<=n && n<=count(a)?1:0);
}
poplist();
pushword(num);
}
void
Xlocal(void)
{
if(count(runq->argv->words)!=1){
Xerror1("variable name must be singleton");
return;
}
runq->local = newvar(runq->argv->words->word, runq->local);
poplist();
runq->local->val = Poplist();
runq->local->changed = 1;
}
void
Xunlocal(void)
{
var *hid, *v = runq->local;
if(v==0)
panic("Xunlocal: no locals!", 0);
runq->local = v->next;
hid = vlook(v->name);
hid->changed = 1;
freevar(v);
}
void
Xfn(void)
{
var *v;
word *a;
int pc = runq->pc;
runq->pc = runq->code[pc].i;
for(a = runq->argv->words;a;a = a->next){
v = gvlook(a->word);
if(v->fn)
codefree(v->fn);
v->fn = codecopy(runq->code);
v->pc = pc+2;
v->fnchanged = 1;
}
poplist();
}
void
Xdelfn(void)
{
var *v;
word *a;
for(a = runq->argv->words;a;a = a->next){
v = gvlook(a->word);
if(v->fn)
codefree(v->fn);
v->fn = 0;
v->fnchanged = 1;
}
poplist();
}
static char*
concstatus(char *s, char *t)
{
static char v[NSTATUS+1];
int n = strlen(s);
strncpy(v, s, NSTATUS);
if(n<NSTATUS){
v[n]='|';
strncpy(v+n+1, t, NSTATUS-n-1);
}
v[NSTATUS]='\0';
return v;
}
void
Xpipewait(void)
{
char status[NSTATUS+1];
if(runq->pid==-1)
setstatus(concstatus(runq->status, getstatus()));
else{
strncpy(status, getstatus(), NSTATUS);
status[NSTATUS]='\0';
Waitfor(runq->pid, 1);
runq->pid=-1;
setstatus(concstatus(getstatus(), status));
}
}
static char *promptstr;
void
Xrdcmds(void)
{
thread *p = runq;
if(flag['s'] && !truestatus())
pfmt(err, "status=%v\n", vlook("status")->val);
flushio(err);
lex = p->lex;
if(p->iflag){
word *prompt = vlook("prompt")->val;
if(prompt)
promptstr = prompt->word;
else
promptstr="% ";
}
Noerror();
nerror = 0;
if(yyparse()){
if(p->iflag && (!lex->eof || Eintr())){
if(Eintr()){
pchr(err, '\n');
lex->eof = 0;
}
--p->pc; /* go back for next command */
}
}
else{
if(lex->eof){
dontclose(lex->input->fd);
freelexer(lex);
p->lex = 0;
} else
--p->pc; /* re-execute Xrdcmds after codebuf runs */
ntrap = 0; /* avoid double-interrupts during blocked writes */
start(codebuf, 2, p->local, p->redir);
}
lex = 0;
freenodes();
}
void
pprompt(void)
{
word *prompt;
if(!runq->iflag)
return;
Prompt(promptstr);
doprompt = 0;
prompt = vlook("prompt")->val;
if(prompt && prompt->next)
promptstr = prompt->next->word;
else
promptstr = "\t";
}
char*
srcfile(thread *p)
{
return p->code[1].s;
}
void
Xerror(char *s)
{
pfln(err, srcfile(runq), runq->line);
pfmt(err, ": %s: %r\n", s);
flushio(err);
setstatus("error");
while(!runq->iflag) Xreturn();
}
void
Xerror1(char *s)
{
pfln(err, srcfile(runq), runq->line);
pfmt(err, ": %s\n", s);
flushio(err);
setstatus("error");
while(!runq->iflag) Xreturn();
}
void
setstatus(char *s)
{
setvar("status", newword(s, (word *)0));
}
char*
getstatus(void)
{
var *status = vlook("status");
return status->val?status->val->word:"";
}
int
truestatus(void)
{
char *s;
for(s = getstatus();*s;s++)
if(*s!='|' && *s!='0')
return 0;
return 1;
}
void
Xfor(void)
{
word *a = runq->argv->words;
if(a==0){
poplist();
runq->pc = runq->code[runq->pc].i;
}
else{
runq->argv->words = a->next;
a->next = 0;
freewords(runq->local->val);
runq->local->val = a;
runq->local->changed = 1;
runq->pc++;
}
}
void
Xglob(void)
{
globword(runq->argv->words);
}
void
Xsrcline(void)
{
runq->line = runq->code[runq->pc++].i;
}