
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.
1089 lines
18 KiB
C
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;
|
|
}
|