2016-11-25 17:18:40 +01:00
|
|
|
/*
|
|
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
|
|
* in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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
|
|
|
|
*/
|
|
|
|
char *argv0="rc";
|
|
|
|
|
|
|
|
void
|
|
|
|
start(code *c, int pc, var *local)
|
|
|
|
{
|
|
|
|
struct thread *p = new(struct thread);
|
|
|
|
|
|
|
|
p->code = codecopy(c);
|
|
|
|
p->pc = pc;
|
|
|
|
p->argv = 0;
|
|
|
|
p->redir = p->startredir = runq?runq->redir:0;
|
|
|
|
p->local = local;
|
|
|
|
p->cmdfile = 0;
|
|
|
|
p->cmdfd = 0;
|
|
|
|
p->eof = 0;
|
|
|
|
p->iflag = 0;
|
|
|
|
p->lineno = 1;
|
|
|
|
p->ret = runq;
|
|
|
|
runq = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
word*
|
2017-10-18 01:10:06 +02:00
|
|
|
Newword(char *wd, word *next)
|
2016-11-25 17:18:40 +01:00
|
|
|
{
|
|
|
|
word *p = new(word);
|
2017-10-18 01:10:06 +02:00
|
|
|
p->word = wd;
|
2016-11-25 17:18:40 +01:00
|
|
|
p->next = next;
|
2017-10-18 01:10:06 +02:00
|
|
|
p->glob = 0;
|
2016-11-25 17:18:40 +01:00
|
|
|
return p;
|
|
|
|
}
|
2017-10-18 01:10:06 +02:00
|
|
|
word*
|
|
|
|
Pushword(char *wd)
|
2016-11-25 17:18:40 +01:00
|
|
|
{
|
2017-10-18 01:10:06 +02:00
|
|
|
word *w;
|
2016-11-25 17:18:40 +01:00
|
|
|
if(runq->argv==0)
|
|
|
|
panic("pushword but no argv!", 0);
|
2017-10-18 01:10:06 +02:00
|
|
|
w = Newword(wd, runq->argv->words);
|
|
|
|
runq->argv->words = w;
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
word*
|
|
|
|
newword(char *wd, word *next)
|
|
|
|
{
|
|
|
|
return Newword(estrdup(wd), next);
|
|
|
|
}
|
|
|
|
word*
|
|
|
|
pushword(char *wd)
|
|
|
|
{
|
|
|
|
return Pushword(estrdup(wd));
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
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;
|
2017-10-18 01:10:06 +02:00
|
|
|
free(p->word);
|
|
|
|
free(p);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
freelist(word *w)
|
|
|
|
{
|
|
|
|
word *nw;
|
|
|
|
while(w){
|
|
|
|
nw = w->next;
|
2017-10-18 01:10:06 +02:00
|
|
|
free(w->word);
|
|
|
|
free(w);
|
2016-11-25 17:18:40 +01:00
|
|
|
w = nw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pushlist(void)
|
|
|
|
{
|
|
|
|
list *p = new(list);
|
|
|
|
p->next = runq->argv;
|
|
|
|
p->words = 0;
|
|
|
|
runq->argv = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
poplist(void)
|
|
|
|
{
|
|
|
|
list *p = runq->argv;
|
|
|
|
if(p==0)
|
|
|
|
panic("poplist but no argv", 0);
|
|
|
|
freelist(p->words);
|
|
|
|
runq->argv = p->next;
|
2017-10-18 01:10:06 +02:00
|
|
|
free(p);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
var*
|
|
|
|
newvar(char *name, var *next)
|
|
|
|
{
|
|
|
|
var *v = new(var);
|
2017-10-18 01:10:06 +02:00
|
|
|
v->name = estrdup(name);
|
2016-11-25 17:18:40 +01:00
|
|
|
v->val = 0;
|
|
|
|
v->fn = 0;
|
|
|
|
v->changed = 0;
|
|
|
|
v->fnchanged = 0;
|
|
|
|
v->next = next;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get command line flags, initialize keywords & traps.
|
|
|
|
* get values from environment.
|
|
|
|
* set $pid, $cflag, $*
|
|
|
|
* fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*)
|
|
|
|
* start interpreting code
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
code bootstrap[17];
|
2017-08-11 02:10:02 +02:00
|
|
|
char num[12], *rcmain, **argvcopy;
|
2016-11-25 17:18:40 +01:00
|
|
|
int i;
|
2017-08-11 02:10:02 +02:00
|
|
|
argvcopy = mallocz(sizeof(char*)*(argc+1), 1);
|
|
|
|
memmove(argvcopy, argv, argc*sizeof(char*));
|
|
|
|
argc = getflags(argc, argvcopy, "SsrdiIlxepvVc:1m:1[command]", 1);
|
2016-11-25 17:18:40 +01:00
|
|
|
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;
|
2016-12-01 00:09:42 +01:00
|
|
|
rcmain = flag['m']?flag['m'][0]:Rcmain;
|
2016-11-25 17:18:40 +01:00
|
|
|
err = openfd(2);
|
|
|
|
kinit();
|
|
|
|
Trapinit();
|
|
|
|
Vinit();
|
|
|
|
inttoascii(num, mypid = getpid());
|
2017-10-18 22:38:30 +02:00
|
|
|
setvar(ENV_PID, newword(num, (word *)0));
|
2016-11-25 17:18:40 +01:00
|
|
|
setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0)
|
|
|
|
:(word *)0);
|
|
|
|
setvar("rcname", newword(argv[0], (word *)0));
|
|
|
|
i = 0;
|
|
|
|
bootstrap[i++].i = 1;
|
|
|
|
bootstrap[i++].f = Xmark;
|
|
|
|
bootstrap[i++].f = Xword;
|
|
|
|
bootstrap[i++].s="*";
|
|
|
|
bootstrap[i++].f = Xassign;
|
|
|
|
bootstrap[i++].f = Xmark;
|
|
|
|
bootstrap[i++].f = Xmark;
|
|
|
|
bootstrap[i++].f = Xword;
|
|
|
|
bootstrap[i++].s="*";
|
|
|
|
bootstrap[i++].f = Xdol;
|
|
|
|
bootstrap[i++].f = Xword;
|
|
|
|
bootstrap[i++].s = rcmain;
|
|
|
|
bootstrap[i++].f = Xword;
|
|
|
|
bootstrap[i++].s=".";
|
|
|
|
bootstrap[i++].f = Xsimple;
|
|
|
|
bootstrap[i++].f = Xexit;
|
|
|
|
bootstrap[i].i = 0;
|
|
|
|
start(bootstrap, 1, (var *)0);
|
|
|
|
/* prime bootstrap argv */
|
|
|
|
pushlist();
|
2017-10-18 01:10:06 +02:00
|
|
|
argv0 = estrdup(argvcopy[0]);
|
|
|
|
for(i = argc-1; i != 0; --i)
|
|
|
|
pushword(argv[i]);
|
2016-11-25 17:18:40 +01:00
|
|
|
for(;;){
|
|
|
|
if(flag['r'])
|
|
|
|
pfnc(err, runq);
|
|
|
|
runq->pc++;
|
|
|
|
(*runq->code[runq->pc-1].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
|
2017-10-18 01:10:06 +02:00
|
|
|
* Xbackq(split){... Xreturn} make thread for {}, push stdout
|
2016-11-25 17:18:40 +01:00
|
|
|
* 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
|
2017-10-18 01:10:06 +02:00
|
|
|
* Xdeltraps(names) delete named traps
|
2016-11-25 17:18:40 +01:00
|
|
|
* Xdol(name) get variable value
|
2017-10-18 01:10:06 +02:00
|
|
|
* Xqdol(name) concatenate variable components
|
2016-11-25 17:18:40 +01:00
|
|
|
* 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
|
2017-10-18 01:10:06 +02:00
|
|
|
* Xglobs[string globsize] push globbing string
|
2016-11-25 17:18:40 +01:00
|
|
|
* 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
|
|
|
|
* Xrdwr(file)[fd] open file for reading and writing
|
|
|
|
* Xread(file)[fd] open file to read
|
2017-10-18 01:10:06 +02:00
|
|
|
* Xsettraps(names){... Xreturn} define trap functions
|
|
|
|
* Xshowtraps print trap list
|
2016-11-25 17:18:40 +01:00
|
|
|
* Xsimple(args) run command and wait
|
2017-10-18 01:10:06 +02:00
|
|
|
* Xreturn kill thread
|
2016-11-25 17:18:40 +01:00
|
|
|
* 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 f;
|
|
|
|
switch(count(runq->argv->words)){
|
|
|
|
default:
|
|
|
|
Xerror1(">> requires singleton");
|
|
|
|
return;
|
|
|
|
case 0:
|
|
|
|
Xerror1(">> requires file");
|
|
|
|
return;
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
file = runq->argv->words->word;
|
2016-12-01 00:09:42 +01:00
|
|
|
if((f = open(file, OWRITE))<0 && (f = Creat(file))<0){
|
2016-11-25 17:18:40 +01:00
|
|
|
pfmt(err, "%s: ", file);
|
|
|
|
Xerror("can't open");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Seek(f, 0L, 2);
|
|
|
|
pushredir(ROPEN, f, runq->code[runq->pc].i);
|
|
|
|
runq->pc++;
|
|
|
|
poplist();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xsettrue(void)
|
|
|
|
{
|
|
|
|
setstatus("");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xbang(void)
|
|
|
|
{
|
|
|
|
setstatus(truestatus()?"false":"");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xclose(void)
|
|
|
|
{
|
|
|
|
pushredir(RCLOSE, runq->code[runq->pc].i, 0);
|
|
|
|
runq->pc++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xdup(void)
|
|
|
|
{
|
|
|
|
pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
|
|
|
|
runq->pc+=2;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xeflag(void)
|
|
|
|
{
|
|
|
|
if(eflagok && !truestatus()) Xexit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xexit(void)
|
|
|
|
{
|
|
|
|
struct var *trapreq;
|
|
|
|
struct word *starval;
|
|
|
|
static int beenhere = 0;
|
|
|
|
if(getpid()==mypid && !beenhere){
|
|
|
|
trapreq = vlook("sigexit");
|
|
|
|
if(trapreq->fn){
|
|
|
|
beenhere = 1;
|
|
|
|
--runq->pc;
|
2017-10-18 22:38:30 +02:00
|
|
|
starval = vlook(ENV_RCARGLIST)->val;
|
2016-11-25 17:18:40 +01:00
|
|
|
start(trapreq->fn, trapreq->pc, (struct var *)0);
|
2017-10-18 01:10:06 +02:00
|
|
|
runq->local = newvar("*", runq->local);
|
2016-11-25 17:18:40 +01:00
|
|
|
runq->local->val = copywords(starval, (struct word *)0);
|
|
|
|
runq->local->changed = 1;
|
|
|
|
runq->redir = runq->startredir = 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
|
|
|
|
Xread(void)
|
|
|
|
{
|
|
|
|
char *file;
|
|
|
|
int f;
|
|
|
|
switch(count(runq->argv->words)){
|
|
|
|
default:
|
|
|
|
Xerror1("< requires singleton\n");
|
|
|
|
return;
|
|
|
|
case 0:
|
|
|
|
Xerror1("< requires file\n");
|
|
|
|
return;
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
file = runq->argv->words->word;
|
|
|
|
if((f = open(file, OREAD))<0){
|
|
|
|
pfmt(err, "%s: ", file);
|
|
|
|
Xerror("can't open");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pushredir(ROPEN, f, runq->code[runq->pc].i);
|
|
|
|
runq->pc++;
|
|
|
|
poplist();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xrdwr(void)
|
|
|
|
{
|
|
|
|
char *file;
|
|
|
|
int f;
|
|
|
|
|
|
|
|
switch(count(runq->argv->words)){
|
|
|
|
default:
|
|
|
|
Xerror1("<> requires singleton\n");
|
|
|
|
return;
|
|
|
|
case 0:
|
|
|
|
Xerror1("<> requires file\n");
|
|
|
|
return;
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
file = runq->argv->words->word;
|
|
|
|
if((f = open(file, ORDWR))<0){
|
|
|
|
pfmt(err, "%s: ", file);
|
|
|
|
Xerror("can't open");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pushredir(ROPEN, f, runq->code[runq->pc].i);
|
|
|
|
runq->pc++;
|
|
|
|
poplist();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
turfredir(void)
|
|
|
|
{
|
|
|
|
while(runq->redir!=runq->startredir)
|
|
|
|
Xpopredir();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xpopredir(void)
|
|
|
|
{
|
|
|
|
struct redir *rp = runq->redir;
|
|
|
|
if(rp==0)
|
|
|
|
panic("turfredir null!", 0);
|
|
|
|
runq->redir = rp->next;
|
|
|
|
if(rp->type==ROPEN)
|
|
|
|
close(rp->from);
|
2017-10-18 01:10:06 +02:00
|
|
|
free(rp);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xreturn(void)
|
|
|
|
{
|
|
|
|
struct thread *p = runq;
|
|
|
|
turfredir();
|
|
|
|
while(p->argv) poplist();
|
|
|
|
codefree(p->code);
|
|
|
|
runq = p->ret;
|
2017-10-18 01:10:06 +02:00
|
|
|
free(p);
|
2016-11-25 17:18:40 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-10-18 01:10:06 +02:00
|
|
|
void
|
|
|
|
Xglobs(void)
|
|
|
|
{
|
|
|
|
word *w = pushword(runq->code[runq->pc++].s);
|
|
|
|
w->glob = runq->code[runq->pc++].i;
|
|
|
|
}
|
|
|
|
|
2016-11-25 17:18:40 +01:00
|
|
|
void
|
|
|
|
Xwrite(void)
|
|
|
|
{
|
|
|
|
char *file;
|
|
|
|
int f;
|
|
|
|
switch(count(runq->argv->words)){
|
|
|
|
default:
|
|
|
|
Xerror1("> requires singleton\n");
|
|
|
|
return;
|
|
|
|
case 0:
|
|
|
|
Xerror1("> requires file\n");
|
|
|
|
return;
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
file = runq->argv->words->word;
|
|
|
|
if((f = Creat(file))<0){
|
|
|
|
pfmt(err, "%s: ", file);
|
|
|
|
Xerror("can't open");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pushredir(ROPEN, f, runq->code[runq->pc].i);
|
|
|
|
runq->pc++;
|
|
|
|
poplist();
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
|
|
|
list2str(word *words)
|
|
|
|
{
|
|
|
|
char *value, *s, *t;
|
|
|
|
int len = 0;
|
|
|
|
word *ap;
|
|
|
|
for(ap = words;ap;ap = ap->next)
|
|
|
|
len+=1+strlen(ap->word);
|
|
|
|
value = emalloc(len+1);
|
|
|
|
s = value;
|
|
|
|
for(ap = words;ap;ap = ap->next){
|
|
|
|
for(t = ap->word;*t;) *s++=*t++;
|
|
|
|
*s++=' ';
|
|
|
|
}
|
|
|
|
if(s==value)
|
|
|
|
*s='\0';
|
|
|
|
else s[-1]='\0';
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xmatch(void)
|
|
|
|
{
|
|
|
|
word *p;
|
|
|
|
char *subject;
|
|
|
|
subject = list2str(runq->argv->words);
|
|
|
|
setstatus("no match");
|
|
|
|
for(p = runq->argv->next->words;p;p = p->next)
|
|
|
|
if(match(subject, p->word, '\0')){
|
|
|
|
setstatus("");
|
|
|
|
break;
|
|
|
|
}
|
2017-10-18 01:10:06 +02:00
|
|
|
free(subject);
|
2016-11-25 17:18:40 +01:00
|
|
|
poplist();
|
|
|
|
poplist();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xcase(void)
|
|
|
|
{
|
|
|
|
word *p;
|
|
|
|
char *s;
|
|
|
|
int ok = 0;
|
|
|
|
s = list2str(runq->argv->next->words);
|
|
|
|
for(p = runq->argv->words;p;p = p->next){
|
|
|
|
if(match(s, p->word, '\0')){
|
|
|
|
ok = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 01:10:06 +02:00
|
|
|
free(s);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(ok)
|
|
|
|
runq->pc++;
|
|
|
|
else
|
|
|
|
runq->pc = runq->code[runq->pc].i;
|
|
|
|
poplist();
|
|
|
|
}
|
|
|
|
|
|
|
|
word*
|
|
|
|
conclist(word *lp, word *rp, word *tail)
|
|
|
|
{
|
2017-10-18 01:10:06 +02:00
|
|
|
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);
|
|
|
|
if(lp->glob || rp->glob)
|
|
|
|
p->glob = Globsize(p->word);
|
|
|
|
*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;
|
2016-11-25 17:18:40 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-10-18 01:10:06 +02:00
|
|
|
char*
|
|
|
|
Str(word *a)
|
|
|
|
{
|
|
|
|
char *s = a->word;
|
|
|
|
if(a->glob){
|
|
|
|
a->glob = 0;
|
|
|
|
deglob(s);
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2016-11-25 17:18:40 +01:00
|
|
|
void
|
|
|
|
Xassign(void)
|
|
|
|
{
|
|
|
|
var *v;
|
|
|
|
if(count(runq->argv->words)!=1){
|
|
|
|
Xerror1("variable name not singleton!");
|
|
|
|
return;
|
|
|
|
}
|
2017-10-18 01:10:06 +02:00
|
|
|
v = vlook(Str(runq->argv->words));
|
2016-11-25 17:18:40 +01:00
|
|
|
poplist();
|
|
|
|
freewords(v->val);
|
2017-10-18 01:10:06 +02:00
|
|
|
v->val = globlist(runq->argv->words);
|
2016-11-25 17:18:40 +01:00
|
|
|
v->changed = 1;
|
|
|
|
runq->argv->words = 0;
|
|
|
|
poplist();
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
2017-10-18 01:10:06 +02:00
|
|
|
s = Str(runq->argv->words);
|
2016-11-25 17:18:40 +01:00
|
|
|
n = 0;
|
|
|
|
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{
|
2017-10-18 22:38:30 +02:00
|
|
|
star = vlook(ENV_RCARGLIST)->val;
|
2016-11-25 17:18:40 +01:00
|
|
|
if(star && 1<=n && n<=count(star)){
|
|
|
|
while(--n) star = star->next;
|
|
|
|
a = newword(star->word, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
poplist();
|
|
|
|
runq->argv->words = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xqdol(void)
|
|
|
|
{
|
2017-10-18 01:10:06 +02:00
|
|
|
word *a;
|
2016-11-25 17:18:40 +01:00
|
|
|
char *s;
|
2017-10-18 01:10:06 +02:00
|
|
|
|
2016-11-25 17:18:40 +01:00
|
|
|
if(count(runq->argv->words)!=1){
|
|
|
|
Xerror1("variable name not singleton!");
|
|
|
|
return;
|
|
|
|
}
|
2017-10-18 01:10:06 +02:00
|
|
|
s = Str(runq->argv->words);
|
2016-11-25 17:18:40 +01:00
|
|
|
a = vlook(s)->val;
|
|
|
|
poplist();
|
2017-10-18 01:10:06 +02:00
|
|
|
Pushword(list2str(a));
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
word*
|
|
|
|
copynwords(word *a, word *tail, int n)
|
|
|
|
{
|
|
|
|
word *v, **end;
|
2016-12-01 00:09:42 +01:00
|
|
|
|
2016-11-25 17:18:40 +01:00
|
|
|
v = 0;
|
|
|
|
end = &v;
|
|
|
|
while(n-- > 0){
|
|
|
|
*end = newword(a->word, 0);
|
|
|
|
end = &(*end)->next;
|
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
*end = tail;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
word*
|
|
|
|
subwords(word *val, int len, word *sub, word *a)
|
|
|
|
{
|
|
|
|
int n, m;
|
|
|
|
char *s;
|
|
|
|
if(!sub)
|
|
|
|
return a;
|
|
|
|
a = subwords(val, len, sub->next, a);
|
2017-10-18 01:10:06 +02:00
|
|
|
s = Str(sub);
|
2016-11-25 17:18:40 +01:00
|
|
|
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;
|
|
|
|
}
|
2017-10-18 01:10:06 +02:00
|
|
|
s = Str(runq->argv->next->words);
|
2016-11-25 17:18:40 +01:00
|
|
|
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;
|
|
|
|
int n;
|
|
|
|
char num[12];
|
|
|
|
if(count(runq->argv->words)!=1){
|
|
|
|
Xerror1("variable name not singleton!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
s = runq->argv->words->word;
|
|
|
|
deglob(s);
|
|
|
|
n = 0;
|
|
|
|
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{
|
2017-10-18 22:38:30 +02:00
|
|
|
a = vlook(ENV_RCARGLIST)->val;
|
2016-11-25 17:18:40 +01:00
|
|
|
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\n");
|
|
|
|
return;
|
|
|
|
}
|
2017-10-18 01:10:06 +02:00
|
|
|
runq->local = newvar(Str(runq->argv->words), runq->local);
|
2016-11-25 17:18:40 +01:00
|
|
|
poplist();
|
2017-10-18 01:10:06 +02:00
|
|
|
runq->local->val = globlist(runq->argv->words);
|
2016-11-25 17:18:40 +01:00
|
|
|
runq->local->changed = 1;
|
|
|
|
runq->argv->words = 0;
|
|
|
|
poplist();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xunlocal(void)
|
|
|
|
{
|
|
|
|
var *v = runq->local, *hid;
|
|
|
|
if(v==0)
|
|
|
|
panic("Xunlocal: no locals!", 0);
|
|
|
|
runq->local = v->next;
|
|
|
|
hid = vlook(v->name);
|
|
|
|
hid->changed = 1;
|
2017-10-18 01:10:06 +02:00
|
|
|
free(v->name);
|
2016-11-25 17:18:40 +01:00
|
|
|
freewords(v->val);
|
2017-10-18 01:10:06 +02:00
|
|
|
free(v);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
freewords(word *w)
|
|
|
|
{
|
|
|
|
word *nw;
|
|
|
|
while(w){
|
2017-10-18 01:10:06 +02:00
|
|
|
free(w->word);
|
2016-11-25 17:18:40 +01:00
|
|
|
nw = w->next;
|
2017-10-18 01:10:06 +02:00
|
|
|
free(w);
|
2016-11-25 17:18:40 +01:00
|
|
|
w = nw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xfn(void)
|
|
|
|
{
|
|
|
|
var *v;
|
|
|
|
word *a;
|
|
|
|
int end;
|
|
|
|
end = runq->code[runq->pc].i;
|
2017-10-18 01:10:06 +02:00
|
|
|
for(a = globlist(runq->argv->words);a;a = a->next){
|
2016-11-25 17:18:40 +01:00
|
|
|
v = gvlook(a->word);
|
|
|
|
if(v->fn)
|
|
|
|
codefree(v->fn);
|
|
|
|
v->fn = codecopy(runq->code);
|
|
|
|
v->pc = runq->pc+2;
|
|
|
|
v->fnchanged = 1;
|
|
|
|
}
|
|
|
|
runq->pc = end;
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xrdcmds(void)
|
|
|
|
{
|
|
|
|
struct thread *p = runq;
|
|
|
|
word *prompt;
|
|
|
|
flush(err);
|
|
|
|
nerror = 0;
|
|
|
|
if(flag['s'] && !truestatus())
|
2017-10-18 22:38:30 +02:00
|
|
|
pfmt(err, "%s=%v\n", ENV_STATUS, vlook(ENV_STATUS)->val);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(runq->iflag){
|
2017-10-18 22:38:30 +02:00
|
|
|
prompt = vlook(ENV_PROMPT)->val;
|
2016-11-25 17:18:40 +01:00
|
|
|
if(prompt)
|
|
|
|
promptstr = prompt->word;
|
|
|
|
else
|
|
|
|
promptstr="% ";
|
|
|
|
}
|
|
|
|
Noerror();
|
|
|
|
if(yyparse()){
|
|
|
|
if(!p->iflag || p->eof && !Eintr()){
|
|
|
|
if(p->cmdfile)
|
2017-10-18 01:10:06 +02:00
|
|
|
free(p->cmdfile);
|
2016-11-25 17:18:40 +01:00
|
|
|
closeio(p->cmdfd);
|
|
|
|
Xreturn(); /* should this be omitted? */
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
if(Eintr()){
|
|
|
|
pchr(err, '\n');
|
|
|
|
p->eof = 0;
|
|
|
|
}
|
|
|
|
--p->pc; /* go back for next command */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
ntrap = 0; /* avoid double-interrupts during blocked writes */
|
|
|
|
--p->pc; /* re-execute Xrdcmds after codebuf runs */
|
|
|
|
start(codebuf, 1, runq->local);
|
|
|
|
}
|
|
|
|
freenodes();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xerror(char *s)
|
|
|
|
{
|
|
|
|
if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/cmd/rc")==0)
|
|
|
|
pfmt(err, "rc: %s: %r\n", s);
|
|
|
|
else
|
|
|
|
pfmt(err, "rc (%s): %s: %r\n", argv0, s);
|
|
|
|
flush(err);
|
|
|
|
setstatus("error");
|
|
|
|
while(!runq->iflag) Xreturn();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xerror1(char *s)
|
|
|
|
{
|
|
|
|
if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/cmd/rc")==0)
|
|
|
|
pfmt(err, "rc: %s\n", s);
|
|
|
|
else
|
|
|
|
pfmt(err, "rc (%s): %s\n", argv0, s);
|
|
|
|
flush(err);
|
|
|
|
setstatus("error");
|
|
|
|
while(!runq->iflag) Xreturn();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
setstatus(char *s)
|
|
|
|
{
|
2017-10-18 22:38:30 +02:00
|
|
|
setvar(ENV_STATUS, newword(s, (word *)0));
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
|
|
|
getstatus(void)
|
|
|
|
{
|
2017-10-18 22:38:30 +02:00
|
|
|
var *status = vlook(ENV_STATUS);
|
2016-11-25 17:18:40 +01:00
|
|
|
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
|
|
|
|
Xdelhere(void)
|
|
|
|
{
|
|
|
|
Unlink(runq->code[runq->pc++].s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xfor(void)
|
|
|
|
{
|
|
|
|
if(runq->argv->words==0){
|
|
|
|
poplist();
|
|
|
|
runq->pc = runq->code[runq->pc].i;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
freelist(runq->local->val);
|
|
|
|
runq->local->val = runq->argv->words;
|
|
|
|
runq->local->changed = 1;
|
|
|
|
runq->argv->words = runq->argv->words->next;
|
|
|
|
runq->local->val->next = 0;
|
|
|
|
runq->pc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Xglob(void)
|
|
|
|
{
|
2017-10-18 01:10:06 +02:00
|
|
|
globlist(runq->argv->words);
|
2016-11-25 17:18:40 +01:00
|
|
|
}
|