xref: /plan9-contrib/sys/src/cmd/rc/exec.c (revision dbee78772299b06484db59f95f481ad96b19f995)
13e12c5d1SDavid du Colombier #include "rc.h"
23e12c5d1SDavid du Colombier #include "exec.h"
33e12c5d1SDavid du Colombier #include "io.h"
43e12c5d1SDavid du Colombier #include "fns.h"
5c6df1444SDavid du Colombier 
6c6df1444SDavid du Colombier char flagset[] = "<flag>";	/* anything non-nil will do */
7c6df1444SDavid du Colombier char *flag[NFLAG];
8c6df1444SDavid du Colombier 
93e12c5d1SDavid du Colombier /*
103e12c5d1SDavid du Colombier  * Start executing the given code at the given pc with the given redirection
113e12c5d1SDavid du Colombier  */
12c6df1444SDavid du Colombier char *argv0;
13dc5a79c1SDavid du Colombier 
14dc5a79c1SDavid du Colombier void
15dc5a79c1SDavid du Colombier start(code *c, int pc, var *local)
163e12c5d1SDavid du Colombier {
173e12c5d1SDavid du Colombier 	struct thread *p = new(struct thread);
18dc5a79c1SDavid du Colombier 
193e12c5d1SDavid du Colombier 	p->code = codecopy(c);
203e12c5d1SDavid du Colombier 	p->pc = pc;
213e12c5d1SDavid du Colombier 	p->argv = 0;
223e12c5d1SDavid du Colombier 	p->redir = p->startredir = runq?runq->redir:0;
233e12c5d1SDavid du Colombier 	p->local = local;
243e12c5d1SDavid du Colombier 	p->cmdfile = 0;
253e12c5d1SDavid du Colombier 	p->cmdfd = 0;
263e12c5d1SDavid du Colombier 	p->eof = 0;
273e12c5d1SDavid du Colombier 	p->iflag = 0;
283e12c5d1SDavid du Colombier 	p->lineno = 1;
293e12c5d1SDavid du Colombier 	p->ret = runq;
303e12c5d1SDavid du Colombier 	runq = p;
313e12c5d1SDavid du Colombier }
32dc5a79c1SDavid du Colombier 
33dc5a79c1SDavid du Colombier word*
34dc5a79c1SDavid du Colombier newword(char *wd, word *next)
353e12c5d1SDavid du Colombier {
363e12c5d1SDavid du Colombier 	word *p = new(word);
373e12c5d1SDavid du Colombier 	p->word = strdup(wd);
383e12c5d1SDavid du Colombier 	p->next = next;
393e12c5d1SDavid du Colombier 	return p;
403e12c5d1SDavid du Colombier }
41dc5a79c1SDavid du Colombier 
42dc5a79c1SDavid du Colombier void
43dc5a79c1SDavid du Colombier pushword(char *wd)
443e12c5d1SDavid du Colombier {
45dc5a79c1SDavid du Colombier 	if(runq->argv==0)
46dc5a79c1SDavid du Colombier 		panic("pushword but no argv!", 0);
473e12c5d1SDavid du Colombier 	runq->argv->words = newword(wd, runq->argv->words);
483e12c5d1SDavid du Colombier }
49dc5a79c1SDavid du Colombier 
50dc5a79c1SDavid du Colombier void
51dc5a79c1SDavid du Colombier popword(void)
52dc5a79c1SDavid du Colombier {
533e12c5d1SDavid du Colombier 	word *p;
54dc5a79c1SDavid du Colombier 	if(runq->argv==0)
55dc5a79c1SDavid du Colombier 		panic("popword but no argv!", 0);
563e12c5d1SDavid du Colombier 	p = runq->argv->words;
57dc5a79c1SDavid du Colombier 	if(p==0)
58dc5a79c1SDavid du Colombier 		panic("popword but no word!", 0);
593e12c5d1SDavid du Colombier 	runq->argv->words = p->next;
603e12c5d1SDavid du Colombier 	efree(p->word);
613e12c5d1SDavid du Colombier 	efree((char *)p);
623e12c5d1SDavid du Colombier }
63dc5a79c1SDavid du Colombier 
64dc5a79c1SDavid du Colombier void
65dc5a79c1SDavid du Colombier freelist(word *w)
663e12c5d1SDavid du Colombier {
673e12c5d1SDavid du Colombier 	word *nw;
683e12c5d1SDavid du Colombier 	while(w){
693e12c5d1SDavid du Colombier 		nw = w->next;
703e12c5d1SDavid du Colombier 		efree(w->word);
713e12c5d1SDavid du Colombier 		efree((char *)w);
723e12c5d1SDavid du Colombier 		w = nw;
733e12c5d1SDavid du Colombier 	}
743e12c5d1SDavid du Colombier }
75dc5a79c1SDavid du Colombier 
76dc5a79c1SDavid du Colombier void
77dc5a79c1SDavid du Colombier pushlist(void)
78dc5a79c1SDavid du Colombier {
793e12c5d1SDavid du Colombier 	list *p = new(list);
803e12c5d1SDavid du Colombier 	p->next = runq->argv;
813e12c5d1SDavid du Colombier 	p->words = 0;
823e12c5d1SDavid du Colombier 	runq->argv = p;
833e12c5d1SDavid du Colombier }
84dc5a79c1SDavid du Colombier 
85dc5a79c1SDavid du Colombier void
86dc5a79c1SDavid du Colombier poplist(void)
87dc5a79c1SDavid du Colombier {
883e12c5d1SDavid du Colombier 	list *p = runq->argv;
89dc5a79c1SDavid du Colombier 	if(p==0)
90dc5a79c1SDavid du Colombier 		panic("poplist but no argv", 0);
913e12c5d1SDavid du Colombier 	freelist(p->words);
923e12c5d1SDavid du Colombier 	runq->argv = p->next;
933e12c5d1SDavid du Colombier 	efree((char *)p);
943e12c5d1SDavid du Colombier }
95dc5a79c1SDavid du Colombier 
96dc5a79c1SDavid du Colombier int
97dc5a79c1SDavid du Colombier count(word *w)
983e12c5d1SDavid du Colombier {
993e12c5d1SDavid du Colombier 	int n;
1003e12c5d1SDavid du Colombier 	for(n = 0;w;n++) w = w->next;
1013e12c5d1SDavid du Colombier 	return n;
1023e12c5d1SDavid du Colombier }
103dc5a79c1SDavid du Colombier 
104dc5a79c1SDavid du Colombier void
105dc5a79c1SDavid du Colombier pushredir(int type, int from, int to)
106dc5a79c1SDavid du Colombier {
1073e12c5d1SDavid du Colombier 	redir * rp = new(redir);
1083e12c5d1SDavid du Colombier 	rp->type = type;
1093e12c5d1SDavid du Colombier 	rp->from = from;
1103e12c5d1SDavid du Colombier 	rp->to = to;
1113e12c5d1SDavid du Colombier 	rp->next = runq->redir;
1123e12c5d1SDavid du Colombier 	runq->redir = rp;
1133e12c5d1SDavid du Colombier }
114dc5a79c1SDavid du Colombier 
115dc5a79c1SDavid du Colombier var*
116dc5a79c1SDavid du Colombier newvar(char *name, var *next)
1173e12c5d1SDavid du Colombier {
1183e12c5d1SDavid du Colombier 	var *v = new(var);
119c6df1444SDavid du Colombier 
120c6df1444SDavid du Colombier 	assert(name != nil);
1213e12c5d1SDavid du Colombier 	v->name = name;
1223e12c5d1SDavid du Colombier 	v->val = 0;
1233e12c5d1SDavid du Colombier 	v->fn = 0;
1243e12c5d1SDavid du Colombier 	v->changed = 0;
1253e12c5d1SDavid du Colombier 	v->fnchanged = 0;
1263e12c5d1SDavid du Colombier 	v->next = next;
1273e12c5d1SDavid du Colombier 	return v;
1283e12c5d1SDavid du Colombier }
129c6df1444SDavid du Colombier 
130c6df1444SDavid du Colombier /* fabricate bootstrap code (*=(argv);. /rc/lib/rcmain $*; exit) */
131c6df1444SDavid du Colombier static void
132c6df1444SDavid du Colombier loadboot(code *base, int nel, char *rcmain)
133c6df1444SDavid du Colombier {
134c6df1444SDavid du Colombier 	code *bs;
135c6df1444SDavid du Colombier 
136c6df1444SDavid du Colombier 	bs = base;
137c6df1444SDavid du Colombier 	bs++->i = 1;			/* reference count */
138c6df1444SDavid du Colombier 	bs++->f = Xmark;		/* "* = $*" */
139c6df1444SDavid du Colombier 	bs++->f = Xword;
140c6df1444SDavid du Colombier 	bs++->s="*";
141c6df1444SDavid du Colombier 	bs++->f = Xassign;
142c6df1444SDavid du Colombier 	bs++->f = Xmark;
143c6df1444SDavid du Colombier 	bs++->f = Xmark;
144c6df1444SDavid du Colombier 	bs++->f = Xword;
145c6df1444SDavid du Colombier 	bs++->s="*";
146c6df1444SDavid du Colombier 	bs++->f = Xdol;
147c6df1444SDavid du Colombier 	bs++->f = Xword;
148c6df1444SDavid du Colombier 	bs++->s = rcmain;		/* ". /rc/lib/rcmain $*" */
149c6df1444SDavid du Colombier 	bs++->f = Xword;
150c6df1444SDavid du Colombier 	bs++->s=".";
151c6df1444SDavid du Colombier 	bs++->f = Xsimple;
152c6df1444SDavid du Colombier 	bs++->f = Xexit;		/* exit */
153c6df1444SDavid du Colombier 	bs++->i = 0;			/* not reached */
154c6df1444SDavid du Colombier 	if (bs > base + nel)
155c6df1444SDavid du Colombier 		panic("bootstrap array too small", 0);
156c6df1444SDavid du Colombier }
157c6df1444SDavid du Colombier 
158c6df1444SDavid du Colombier void
159c6df1444SDavid du Colombier usage(void)
160c6df1444SDavid du Colombier {
161c6df1444SDavid du Colombier 	pfmt(err, "Usage: rc [-srdiIlxepvV] [-c arg] [-m command] "
162c6df1444SDavid du Colombier 		"[file [arg ...]]\n");
163c6df1444SDavid du Colombier 	Exit("bad flags");
164c6df1444SDavid du Colombier }
165c6df1444SDavid du Colombier 
1663e12c5d1SDavid du Colombier /*
1673e12c5d1SDavid du Colombier  * get command line flags, initialize keywords & traps.
1683e12c5d1SDavid du Colombier  * get values from environment.
1693e12c5d1SDavid du Colombier  * set $pid, $cflag, $*
170c6df1444SDavid du Colombier  * fabricate bootstrap code and start it
1713e12c5d1SDavid du Colombier  * start interpreting code
1723e12c5d1SDavid du Colombier  */
173dc5a79c1SDavid du Colombier void
174dc5a79c1SDavid du Colombier main(int argc, char *argv[])
1753e12c5d1SDavid du Colombier {
1763e12c5d1SDavid du Colombier 	code bootstrap[17];
177c6df1444SDavid du Colombier 	char num[12];
1783e12c5d1SDavid du Colombier 	int i;
179c6df1444SDavid du Colombier 
180c6df1444SDavid du Colombier 	err = openfd(2);
181c6df1444SDavid du Colombier 	ARGBEGIN {
182c6df1444SDavid du Colombier 	case 'd': case 'e': case 'i': case 'l':
183c6df1444SDavid du Colombier 	case 'p': case 'r': case 's': case 'v':
184c6df1444SDavid du Colombier 	case 'x': case 'I': case 'V':
185c6df1444SDavid du Colombier 		flag[ARGC()] = flagset;
186c6df1444SDavid du Colombier 		break;
187c6df1444SDavid du Colombier 	case 'c':
188c6df1444SDavid du Colombier 	case 'm':
189c6df1444SDavid du Colombier 		if (flag[ARGC()])
190c6df1444SDavid du Colombier 			usage();
191c6df1444SDavid du Colombier 		flag[ARGC()] = EARGF(usage());
192c6df1444SDavid du Colombier 		break;
193c6df1444SDavid du Colombier 	default:
194c6df1444SDavid du Colombier 		usage();
195c6df1444SDavid du Colombier 		break;
196c6df1444SDavid du Colombier 	} ARGEND
197c6df1444SDavid du Colombier 	if(argc < 0)
198c6df1444SDavid du Colombier 		usage();
199c6df1444SDavid du Colombier 	if(argv0 == nil)
200c6df1444SDavid du Colombier 		argv0 = "rc";
201c6df1444SDavid du Colombier 	if(argv0[0]=='-')			/* login shell? */
202dc5a79c1SDavid du Colombier 		flag['l'] = flagset;
203dc5a79c1SDavid du Colombier 	if(flag['I'])
204dc5a79c1SDavid du Colombier 		flag['i'] = 0;
205c6df1444SDavid du Colombier 	else if(flag['i']==0 && argc==0 && Isatty(0))
206c6df1444SDavid du Colombier 		flag['i'] = flagset;		/* force interactive */
207c6df1444SDavid du Colombier 
2083e12c5d1SDavid du Colombier 	kinit();
2093e12c5d1SDavid du Colombier 	Trapinit();
2103e12c5d1SDavid du Colombier 	Vinit();
211dc5a79c1SDavid du Colombier 	inttoascii(num, mypid = getpid());
2123e12c5d1SDavid du Colombier 	setvar("pid", newword(num, (word *)0));
213c6df1444SDavid du Colombier 	setvar("cflag", flag['c']? newword(flag['c'], (word *)0): (word *)0);
214c6df1444SDavid du Colombier 	setvar("rcname", newword(argv0, (word *)0));
215c6df1444SDavid du Colombier 
216c6df1444SDavid du Colombier 	loadboot(bootstrap, nelem(bootstrap), (flag['m']? flag['m']: Rcmain));
2173e12c5d1SDavid du Colombier 	start(bootstrap, 1, (var *)0);
2183e12c5d1SDavid du Colombier 	/* prime bootstrap argv */
2193e12c5d1SDavid du Colombier 	pushlist();
220c6df1444SDavid du Colombier 	for(i = argc-1; i >= 0; --i)
221c6df1444SDavid du Colombier 		pushword(argv[i]);
2223e12c5d1SDavid du Colombier 	for(;;){
223dc5a79c1SDavid du Colombier 		if(flag['r'])
224dc5a79c1SDavid du Colombier 			pfnc(err, runq);
2253e12c5d1SDavid du Colombier 		runq->pc++;
2263e12c5d1SDavid du Colombier 		(*runq->code[runq->pc-1].f)();
227dc5a79c1SDavid du Colombier 		if(ntrap)
228dc5a79c1SDavid du Colombier 			dotrap();
2293e12c5d1SDavid du Colombier 	}
2303e12c5d1SDavid du Colombier }
231c6df1444SDavid du Colombier 
2323e12c5d1SDavid du Colombier /*
2333e12c5d1SDavid du Colombier  * Opcode routines
2343e12c5d1SDavid du Colombier  * Arguments on stack (...)
2353e12c5d1SDavid du Colombier  * Arguments in line [...]
2363e12c5d1SDavid du Colombier  * Code in line with jump around {...}
2373e12c5d1SDavid du Colombier  *
2383e12c5d1SDavid du Colombier  * Xappend(file)[fd]			open file to append
2393e12c5d1SDavid du Colombier  * Xassign(name, val)			assign val to name
2403e12c5d1SDavid du Colombier  * Xasync{... Xexit}			make thread for {}, no wait
241*dbee7877SDavid du Colombier  * Xbackq(split){... Xreturn}		make thread for {}, push stdout
2423e12c5d1SDavid du Colombier  * Xbang				complement condition
2433e12c5d1SDavid du Colombier  * Xcase(pat, value){...}		exec code on match, leave (value) on
2443e12c5d1SDavid du Colombier  * 					stack
2453e12c5d1SDavid du Colombier  * Xclose[i]				close file descriptor
2463e12c5d1SDavid du Colombier  * Xconc(left, right)			concatenate, push results
2473e12c5d1SDavid du Colombier  * Xcount(name)				push var count
2483e12c5d1SDavid du Colombier  * Xdelfn(name)				delete function definition
2494e3613abSDavid du Colombier  * Xdelhere
2503e12c5d1SDavid du Colombier  * Xdol(name)				get variable value
2513e12c5d1SDavid du Colombier  * Xdup[i j]				dup file descriptor
2524e3613abSDavid du Colombier  * Xeflag
2534e3613abSDavid du Colombier  * Xerror
2543e12c5d1SDavid du Colombier  * Xexit				rc exits with status
2553e12c5d1SDavid du Colombier  * Xfalse{...}				execute {} if false
2563e12c5d1SDavid du Colombier  * Xfn(name){... Xreturn}			define function
2573e12c5d1SDavid du Colombier  * Xfor(var, list){... Xreturn}		for loop
2584e3613abSDavid du Colombier  * Xglob
2594e3613abSDavid du Colombier  * Xif
2604e3613abSDavid du Colombier  * Xifnot
2613e12c5d1SDavid du Colombier  * Xjump[addr]				goto
2623e12c5d1SDavid du Colombier  * Xlocal(name, val)			create local variable, assign value
2633e12c5d1SDavid du Colombier  * Xmark				mark stack
2643e12c5d1SDavid du Colombier  * Xmatch(pat, str)			match pattern, set status
2653e12c5d1SDavid du Colombier  * Xpipe[i j]{... Xreturn}{... Xreturn}	construct a pipe between 2 new threads,
2663e12c5d1SDavid du Colombier  * 					wait for both
2673e12c5d1SDavid du Colombier  * Xpipefd[type]{... Xreturn}		connect {} to pipe (input or output,
2683e12c5d1SDavid du Colombier  * 					depending on type), push /dev/fd/??
2694e3613abSDavid du Colombier  * Xpipewait
2703e12c5d1SDavid du Colombier  * Xpopm(value)				pop value from stack
2714e3613abSDavid du Colombier  * Xpopredir
272c6df1444SDavid du Colombier  * Xqdol(name)				concatenate variable components
2734e3613abSDavid du Colombier  * Xrdcmds
2744e3613abSDavid du Colombier  * Xrdfn
275119a69faSDavid du Colombier  * Xrdwr(file)[fd]			open file for reading and writing
2763e12c5d1SDavid du Colombier  * Xread(file)[fd]			open file to read
2773e12c5d1SDavid du Colombier  * Xreturn				kill thread
2784e3613abSDavid du Colombier  * Xsimple(args)			run command and wait
279c6df1444SDavid du Colombier  * Xsettrue
2804e3613abSDavid du Colombier  * Xsub
2813e12c5d1SDavid du Colombier  * Xsubshell{... Xexit}			execute {} in a subshell and wait
2823e12c5d1SDavid du Colombier  * Xtrue{...}				execute {} if true
2833e12c5d1SDavid du Colombier  * Xunlocal				delete local variable
2844e3613abSDavid du Colombier  * Xwastrue
2853e12c5d1SDavid du Colombier  * Xword[string]			push string
2863e12c5d1SDavid du Colombier  * Xwrite(file)[fd]			open file to write
2873e12c5d1SDavid du Colombier  */
288dc5a79c1SDavid du Colombier 
289dc5a79c1SDavid du Colombier void
290dc5a79c1SDavid du Colombier Xappend(void)
291dc5a79c1SDavid du Colombier {
2923e12c5d1SDavid du Colombier 	char *file;
2933e12c5d1SDavid du Colombier 	int f;
2943e12c5d1SDavid du Colombier 	switch(count(runq->argv->words)){
295dc5a79c1SDavid du Colombier 	default:
296dc5a79c1SDavid du Colombier 		Xerror1(">> requires singleton");
297dc5a79c1SDavid du Colombier 		return;
298dc5a79c1SDavid du Colombier 	case 0:
299dc5a79c1SDavid du Colombier 		Xerror1(">> requires file");
300dc5a79c1SDavid du Colombier 		return;
301dc5a79c1SDavid du Colombier 	case 1:
302dc5a79c1SDavid du Colombier 		break;
3033e12c5d1SDavid du Colombier 	}
3043e12c5d1SDavid du Colombier 	file = runq->argv->words->word;
3053e12c5d1SDavid du Colombier 	if((f = open(file, 1))<0 && (f = Creat(file))<0){
3067dd7cddfSDavid du Colombier 		pfmt(err, "%s: ", file);
3077dd7cddfSDavid du Colombier 		Xerror("can't open");
3083e12c5d1SDavid du Colombier 		return;
3093e12c5d1SDavid du Colombier 	}
310c6df1444SDavid du Colombier 	seek(f, 0, 2);
3113e12c5d1SDavid du Colombier 	pushredir(ROPEN, f, runq->code[runq->pc].i);
3123e12c5d1SDavid du Colombier 	runq->pc++;
3133e12c5d1SDavid du Colombier 	poplist();
3143e12c5d1SDavid du Colombier }
315dc5a79c1SDavid du Colombier 
316dc5a79c1SDavid du Colombier void
317dc5a79c1SDavid du Colombier Xsettrue(void)
318dc5a79c1SDavid du Colombier {
319219b2ee8SDavid du Colombier 	setstatus("");
320219b2ee8SDavid du Colombier }
321dc5a79c1SDavid du Colombier 
322dc5a79c1SDavid du Colombier void
323dc5a79c1SDavid du Colombier Xbang(void)
324dc5a79c1SDavid du Colombier {
3253e12c5d1SDavid du Colombier 	setstatus(truestatus()?"false":"");
3263e12c5d1SDavid du Colombier }
327dc5a79c1SDavid du Colombier 
328dc5a79c1SDavid du Colombier void
329dc5a79c1SDavid du Colombier Xclose(void)
330dc5a79c1SDavid du Colombier {
3313e12c5d1SDavid du Colombier 	pushredir(RCLOSE, runq->code[runq->pc].i, 0);
3323e12c5d1SDavid du Colombier 	runq->pc++;
3333e12c5d1SDavid du Colombier }
334dc5a79c1SDavid du Colombier 
335dc5a79c1SDavid du Colombier void
336dc5a79c1SDavid du Colombier Xdup(void)
337dc5a79c1SDavid du Colombier {
3383e12c5d1SDavid du Colombier 	pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
3393e12c5d1SDavid du Colombier 	runq->pc+=2;
3403e12c5d1SDavid du Colombier }
341dc5a79c1SDavid du Colombier 
342dc5a79c1SDavid du Colombier void
343dc5a79c1SDavid du Colombier Xeflag(void)
344dc5a79c1SDavid du Colombier {
345219b2ee8SDavid du Colombier 	if(eflagok && !truestatus()) Xexit();
346219b2ee8SDavid du Colombier }
347dc5a79c1SDavid du Colombier 
348dc5a79c1SDavid du Colombier void
349dc5a79c1SDavid du Colombier Xexit(void)
350dc5a79c1SDavid du Colombier {
3513e12c5d1SDavid du Colombier 	struct var *trapreq;
3523e12c5d1SDavid du Colombier 	struct word *starval;
3533e12c5d1SDavid du Colombier 	static int beenhere = 0;
3543e12c5d1SDavid du Colombier 	if(getpid()==mypid && !beenhere){
3553e12c5d1SDavid du Colombier 		trapreq = vlook("sigexit");
3563e12c5d1SDavid du Colombier 		if(trapreq->fn){
3573e12c5d1SDavid du Colombier 			beenhere = 1;
3583e12c5d1SDavid du Colombier 			--runq->pc;
359219b2ee8SDavid du Colombier 			starval = vlook("*")->val;
3603e12c5d1SDavid du Colombier 			start(trapreq->fn, trapreq->pc, (struct var *)0);
3613e12c5d1SDavid du Colombier 			runq->local = newvar(strdup("*"), runq->local);
3623e12c5d1SDavid du Colombier 			runq->local->val = copywords(starval, (struct word *)0);
3633e12c5d1SDavid du Colombier 			runq->local->changed = 1;
3643e12c5d1SDavid du Colombier 			runq->redir = runq->startredir = 0;
3653e12c5d1SDavid du Colombier 			return;
3663e12c5d1SDavid du Colombier 		}
3673e12c5d1SDavid du Colombier 	}
3683e12c5d1SDavid du Colombier 	Exit(getstatus());
3693e12c5d1SDavid du Colombier }
370dc5a79c1SDavid du Colombier 
371dc5a79c1SDavid du Colombier void
372dc5a79c1SDavid du Colombier Xfalse(void)
373dc5a79c1SDavid du Colombier {
3743e12c5d1SDavid du Colombier 	if(truestatus()) runq->pc = runq->code[runq->pc].i;
3753e12c5d1SDavid du Colombier 	else runq->pc++;
3763e12c5d1SDavid du Colombier }
3773e12c5d1SDavid du Colombier int ifnot;		/* dynamic if not flag */
378dc5a79c1SDavid du Colombier 
379dc5a79c1SDavid du Colombier void
380dc5a79c1SDavid du Colombier Xifnot(void)
381dc5a79c1SDavid du Colombier {
3823e12c5d1SDavid du Colombier 	if(ifnot)
3833e12c5d1SDavid du Colombier 		runq->pc++;
3843e12c5d1SDavid du Colombier 	else
3853e12c5d1SDavid du Colombier 		runq->pc = runq->code[runq->pc].i;
3863e12c5d1SDavid du Colombier }
387dc5a79c1SDavid du Colombier 
388dc5a79c1SDavid du Colombier void
389dc5a79c1SDavid du Colombier Xjump(void)
390dc5a79c1SDavid du Colombier {
3913e12c5d1SDavid du Colombier 	runq->pc = runq->code[runq->pc].i;
3923e12c5d1SDavid du Colombier }
393dc5a79c1SDavid du Colombier 
394dc5a79c1SDavid du Colombier void
395dc5a79c1SDavid du Colombier Xmark(void)
396dc5a79c1SDavid du Colombier {
3973e12c5d1SDavid du Colombier 	pushlist();
3983e12c5d1SDavid du Colombier }
399dc5a79c1SDavid du Colombier 
400dc5a79c1SDavid du Colombier void
401dc5a79c1SDavid du Colombier Xpopm(void)
402dc5a79c1SDavid du Colombier {
4033e12c5d1SDavid du Colombier 	poplist();
4043e12c5d1SDavid du Colombier }
405dc5a79c1SDavid du Colombier 
406dc5a79c1SDavid du Colombier void
407dc5a79c1SDavid du Colombier Xread(void)
408dc5a79c1SDavid du Colombier {
4093e12c5d1SDavid du Colombier 	char *file;
4103e12c5d1SDavid du Colombier 	int f;
4113e12c5d1SDavid du Colombier 	switch(count(runq->argv->words)){
412dc5a79c1SDavid du Colombier 	default:
413dc5a79c1SDavid du Colombier 		Xerror1("< requires singleton\n");
414dc5a79c1SDavid du Colombier 		return;
415dc5a79c1SDavid du Colombier 	case 0:
416dc5a79c1SDavid du Colombier 		Xerror1("< requires file\n");
417dc5a79c1SDavid du Colombier 		return;
418dc5a79c1SDavid du Colombier 	case 1:
419dc5a79c1SDavid du Colombier 		break;
4203e12c5d1SDavid du Colombier 	}
4213e12c5d1SDavid du Colombier 	file = runq->argv->words->word;
4223e12c5d1SDavid du Colombier 	if((f = open(file, 0))<0){
4237dd7cddfSDavid du Colombier 		pfmt(err, "%s: ", file);
4247dd7cddfSDavid du Colombier 		Xerror("can't open");
4253e12c5d1SDavid du Colombier 		return;
4263e12c5d1SDavid du Colombier 	}
4273e12c5d1SDavid du Colombier 	pushredir(ROPEN, f, runq->code[runq->pc].i);
4283e12c5d1SDavid du Colombier 	runq->pc++;
4293e12c5d1SDavid du Colombier 	poplist();
4303e12c5d1SDavid du Colombier }
431dc5a79c1SDavid du Colombier 
432dc5a79c1SDavid du Colombier void
433119a69faSDavid du Colombier Xrdwr(void)
434119a69faSDavid du Colombier {
435119a69faSDavid du Colombier 	char *file;
436119a69faSDavid du Colombier 	int f;
437119a69faSDavid du Colombier 
438119a69faSDavid du Colombier 	switch(count(runq->argv->words)){
439119a69faSDavid du Colombier 	default:
440119a69faSDavid du Colombier 		Xerror1("<> requires singleton\n");
441119a69faSDavid du Colombier 		return;
442119a69faSDavid du Colombier 	case 0:
443119a69faSDavid du Colombier 		Xerror1("<> requires file\n");
444119a69faSDavid du Colombier 		return;
445119a69faSDavid du Colombier 	case 1:
446119a69faSDavid du Colombier 		break;
447119a69faSDavid du Colombier 	}
448119a69faSDavid du Colombier 	file = runq->argv->words->word;
449119a69faSDavid du Colombier 	if((f = open(file, ORDWR))<0){
450119a69faSDavid du Colombier 		pfmt(err, "%s: ", file);
451119a69faSDavid du Colombier 		Xerror("can't open");
452119a69faSDavid du Colombier 		return;
453119a69faSDavid du Colombier 	}
454119a69faSDavid du Colombier 	pushredir(ROPEN, f, runq->code[runq->pc].i);
455119a69faSDavid du Colombier 	runq->pc++;
456119a69faSDavid du Colombier 	poplist();
457119a69faSDavid du Colombier }
458119a69faSDavid du Colombier 
459119a69faSDavid du Colombier void
460dc5a79c1SDavid du Colombier turfredir(void)
461dc5a79c1SDavid du Colombier {
4623e12c5d1SDavid du Colombier 	while(runq->redir!=runq->startredir)
4633e12c5d1SDavid du Colombier 		Xpopredir();
4643e12c5d1SDavid du Colombier }
465dc5a79c1SDavid du Colombier 
466dc5a79c1SDavid du Colombier void
467dc5a79c1SDavid du Colombier Xpopredir(void)
468dc5a79c1SDavid du Colombier {
4693e12c5d1SDavid du Colombier 	struct redir *rp = runq->redir;
470dc5a79c1SDavid du Colombier 	if(rp==0)
471dc5a79c1SDavid du Colombier 		panic("turfredir null!", 0);
4723e12c5d1SDavid du Colombier 	runq->redir = rp->next;
473dc5a79c1SDavid du Colombier 	if(rp->type==ROPEN)
474dc5a79c1SDavid du Colombier 		close(rp->from);
4753e12c5d1SDavid du Colombier 	efree((char *)rp);
4763e12c5d1SDavid du Colombier }
477dc5a79c1SDavid du Colombier 
478dc5a79c1SDavid du Colombier void
479dc5a79c1SDavid du Colombier Xreturn(void)
480dc5a79c1SDavid du Colombier {
4813e12c5d1SDavid du Colombier 	struct thread *p = runq;
4823e12c5d1SDavid du Colombier 	turfredir();
4833e12c5d1SDavid du Colombier 	while(p->argv) poplist();
4843e12c5d1SDavid du Colombier 	codefree(p->code);
4853e12c5d1SDavid du Colombier 	runq = p->ret;
4863e12c5d1SDavid du Colombier 	efree((char *)p);
487dc5a79c1SDavid du Colombier 	if(runq==0)
488dc5a79c1SDavid du Colombier 		Exit(getstatus());
4893e12c5d1SDavid du Colombier }
490dc5a79c1SDavid du Colombier 
491dc5a79c1SDavid du Colombier void
492dc5a79c1SDavid du Colombier Xtrue(void)
493dc5a79c1SDavid du Colombier {
4943e12c5d1SDavid du Colombier 	if(truestatus()) runq->pc++;
4953e12c5d1SDavid du Colombier 	else runq->pc = runq->code[runq->pc].i;
4963e12c5d1SDavid du Colombier }
497dc5a79c1SDavid du Colombier 
498dc5a79c1SDavid du Colombier void
499dc5a79c1SDavid du Colombier Xif(void)
500dc5a79c1SDavid du Colombier {
5013e12c5d1SDavid du Colombier 	ifnot = 1;
5023e12c5d1SDavid du Colombier 	if(truestatus()) runq->pc++;
5033e12c5d1SDavid du Colombier 	else runq->pc = runq->code[runq->pc].i;
5043e12c5d1SDavid du Colombier }
505dc5a79c1SDavid du Colombier 
506dc5a79c1SDavid du Colombier void
507dc5a79c1SDavid du Colombier Xwastrue(void)
508dc5a79c1SDavid du Colombier {
5093e12c5d1SDavid du Colombier 	ifnot = 0;
5103e12c5d1SDavid du Colombier }
511dc5a79c1SDavid du Colombier 
512dc5a79c1SDavid du Colombier void
513dc5a79c1SDavid du Colombier Xword(void)
514dc5a79c1SDavid du Colombier {
5153e12c5d1SDavid du Colombier 	pushword(runq->code[runq->pc++].s);
5163e12c5d1SDavid du Colombier }
517dc5a79c1SDavid du Colombier 
518dc5a79c1SDavid du Colombier void
519dc5a79c1SDavid du Colombier Xwrite(void)
520dc5a79c1SDavid du Colombier {
5213e12c5d1SDavid du Colombier 	char *file;
5223e12c5d1SDavid du Colombier 	int f;
5233e12c5d1SDavid du Colombier 	switch(count(runq->argv->words)){
524dc5a79c1SDavid du Colombier 	default:
525dc5a79c1SDavid du Colombier 		Xerror1("> requires singleton\n");
526dc5a79c1SDavid du Colombier 		return;
527dc5a79c1SDavid du Colombier 	case 0:
528dc5a79c1SDavid du Colombier 		Xerror1("> requires file\n");
529dc5a79c1SDavid du Colombier 		return;
530dc5a79c1SDavid du Colombier 	case 1:
531dc5a79c1SDavid du Colombier 		break;
5323e12c5d1SDavid du Colombier 	}
5333e12c5d1SDavid du Colombier 	file = runq->argv->words->word;
5343e12c5d1SDavid du Colombier 	if((f = Creat(file))<0){
5357dd7cddfSDavid du Colombier 		pfmt(err, "%s: ", file);
5367dd7cddfSDavid du Colombier 		Xerror("can't open");
5373e12c5d1SDavid du Colombier 		return;
5383e12c5d1SDavid du Colombier 	}
5393e12c5d1SDavid du Colombier 	pushredir(ROPEN, f, runq->code[runq->pc].i);
5403e12c5d1SDavid du Colombier 	runq->pc++;
5413e12c5d1SDavid du Colombier 	poplist();
5423e12c5d1SDavid du Colombier }
543dc5a79c1SDavid du Colombier 
544dc5a79c1SDavid du Colombier char*
545dc5a79c1SDavid du Colombier list2str(word *words)
546dc5a79c1SDavid du Colombier {
5473e12c5d1SDavid du Colombier 	char *value, *s, *t;
5483e12c5d1SDavid du Colombier 	int len = 0;
5493e12c5d1SDavid du Colombier 	word *ap;
5503e12c5d1SDavid du Colombier 	for(ap = words;ap;ap = ap->next)
5513e12c5d1SDavid du Colombier 		len+=1+strlen(ap->word);
5523e12c5d1SDavid du Colombier 	value = emalloc(len+1);
5533e12c5d1SDavid du Colombier 	s = value;
5543e12c5d1SDavid du Colombier 	for(ap = words;ap;ap = ap->next){
5553e12c5d1SDavid du Colombier 		for(t = ap->word;*t;) *s++=*t++;
5563e12c5d1SDavid du Colombier 		*s++=' ';
5573e12c5d1SDavid du Colombier 	}
558dc5a79c1SDavid du Colombier 	if(s==value)
559dc5a79c1SDavid du Colombier 		*s='\0';
5603e12c5d1SDavid du Colombier 	else s[-1]='\0';
5613e12c5d1SDavid du Colombier 	return value;
5623e12c5d1SDavid du Colombier }
563dc5a79c1SDavid du Colombier 
564dc5a79c1SDavid du Colombier void
565dc5a79c1SDavid du Colombier Xmatch(void)
566dc5a79c1SDavid du Colombier {
5673e12c5d1SDavid du Colombier 	word *p;
5683e12c5d1SDavid du Colombier 	char *subject;
5693e12c5d1SDavid du Colombier 	subject = list2str(runq->argv->words);
5703e12c5d1SDavid du Colombier 	setstatus("no match");
5713e12c5d1SDavid du Colombier 	for(p = runq->argv->next->words;p;p = p->next)
5723e12c5d1SDavid du Colombier 		if(match(subject, p->word, '\0')){
5733e12c5d1SDavid du Colombier 			setstatus("");
5743e12c5d1SDavid du Colombier 			break;
5753e12c5d1SDavid du Colombier 		}
5763e12c5d1SDavid du Colombier 	efree(subject);
5773e12c5d1SDavid du Colombier 	poplist();
5783e12c5d1SDavid du Colombier 	poplist();
5793e12c5d1SDavid du Colombier }
580dc5a79c1SDavid du Colombier 
581dc5a79c1SDavid du Colombier void
582dc5a79c1SDavid du Colombier Xcase(void)
583dc5a79c1SDavid du Colombier {
5843e12c5d1SDavid du Colombier 	word *p;
5853e12c5d1SDavid du Colombier 	char *s;
5863e12c5d1SDavid du Colombier 	int ok = 0;
5873e12c5d1SDavid du Colombier 	s = list2str(runq->argv->next->words);
5883e12c5d1SDavid du Colombier 	for(p = runq->argv->words;p;p = p->next){
5893e12c5d1SDavid du Colombier 		if(match(s, p->word, '\0')){
5903e12c5d1SDavid du Colombier 			ok = 1;
5913e12c5d1SDavid du Colombier 			break;
5923e12c5d1SDavid du Colombier 		}
5933e12c5d1SDavid du Colombier 	}
5943e12c5d1SDavid du Colombier 	efree(s);
5953e12c5d1SDavid du Colombier 	if(ok)
5963e12c5d1SDavid du Colombier 		runq->pc++;
5973e12c5d1SDavid du Colombier 	else
5983e12c5d1SDavid du Colombier 		runq->pc = runq->code[runq->pc].i;
5993e12c5d1SDavid du Colombier 	poplist();
6003e12c5d1SDavid du Colombier }
601dc5a79c1SDavid du Colombier 
602dc5a79c1SDavid du Colombier word*
603dc5a79c1SDavid du Colombier conclist(word *lp, word *rp, word *tail)
6043e12c5d1SDavid du Colombier {
6053e12c5d1SDavid du Colombier 	char *buf;
6063e12c5d1SDavid du Colombier 	word *v;
6073e12c5d1SDavid du Colombier 	if(lp->next || rp->next)
608276e7d6dSDavid du Colombier 		tail = conclist(lp->next==0? lp: lp->next,
609276e7d6dSDavid du Colombier 			rp->next==0? rp: rp->next, tail);
610276e7d6dSDavid du Colombier 	buf = emalloc(strlen(lp->word)+strlen((char *)rp->word)+1);
6113e12c5d1SDavid du Colombier 	strcpy(buf, lp->word);
6123e12c5d1SDavid du Colombier 	strcat(buf, rp->word);
6133e12c5d1SDavid du Colombier 	v = newword(buf, tail);
6143e12c5d1SDavid du Colombier 	efree(buf);
6153e12c5d1SDavid du Colombier 	return v;
6163e12c5d1SDavid du Colombier }
617dc5a79c1SDavid du Colombier 
618dc5a79c1SDavid du Colombier void
619dc5a79c1SDavid du Colombier Xconc(void)
620dc5a79c1SDavid du Colombier {
6213e12c5d1SDavid du Colombier 	word *lp = runq->argv->words;
6223e12c5d1SDavid du Colombier 	word *rp = runq->argv->next->words;
6233e12c5d1SDavid du Colombier 	word *vp = runq->argv->next->next->words;
6243e12c5d1SDavid du Colombier 	int lc = count(lp), rc = count(rp);
6253e12c5d1SDavid du Colombier 	if(lc!=0 || rc!=0){
6263e12c5d1SDavid du Colombier 		if(lc==0 || rc==0){
6279a747e4fSDavid du Colombier 			Xerror1("null list in concatenation");
6283e12c5d1SDavid du Colombier 			return;
6293e12c5d1SDavid du Colombier 		}
6303e12c5d1SDavid du Colombier 		if(lc!=1 && rc!=1 && lc!=rc){
6319a747e4fSDavid du Colombier 			Xerror1("mismatched list lengths in concatenation");
6323e12c5d1SDavid du Colombier 			return;
6333e12c5d1SDavid du Colombier 		}
6343e12c5d1SDavid du Colombier 		vp = conclist(lp, rp, vp);
6353e12c5d1SDavid du Colombier 	}
6363e12c5d1SDavid du Colombier 	poplist();
6373e12c5d1SDavid du Colombier 	poplist();
6383e12c5d1SDavid du Colombier 	runq->argv->words = vp;
6393e12c5d1SDavid du Colombier }
640dc5a79c1SDavid du Colombier 
641dc5a79c1SDavid du Colombier void
642dc5a79c1SDavid du Colombier Xassign(void)
643dc5a79c1SDavid du Colombier {
6443e12c5d1SDavid du Colombier 	var *v;
6453e12c5d1SDavid du Colombier 	if(count(runq->argv->words)!=1){
6469a747e4fSDavid du Colombier 		Xerror1("variable name not singleton!");
6473e12c5d1SDavid du Colombier 		return;
6483e12c5d1SDavid du Colombier 	}
6493e12c5d1SDavid du Colombier 	deglob(runq->argv->words->word);
6503e12c5d1SDavid du Colombier 	v = vlook(runq->argv->words->word);
6513e12c5d1SDavid du Colombier 	poplist();
6523e12c5d1SDavid du Colombier 	globlist();
6533e12c5d1SDavid du Colombier 	freewords(v->val);
6543e12c5d1SDavid du Colombier 	v->val = runq->argv->words;
6553e12c5d1SDavid du Colombier 	v->changed = 1;
6563e12c5d1SDavid du Colombier 	runq->argv->words = 0;
6573e12c5d1SDavid du Colombier 	poplist();
6583e12c5d1SDavid du Colombier }
6593e12c5d1SDavid du Colombier /*
6603e12c5d1SDavid du Colombier  * copy arglist a, adding the copy to the front of tail
6613e12c5d1SDavid du Colombier  */
662dc5a79c1SDavid du Colombier 
663dc5a79c1SDavid du Colombier word*
664dc5a79c1SDavid du Colombier copywords(word *a, word *tail)
6653e12c5d1SDavid du Colombier {
6663e12c5d1SDavid du Colombier 	word *v = 0, **end;
6673e12c5d1SDavid du Colombier 	for(end=&v;a;a = a->next,end=&(*end)->next)
6683e12c5d1SDavid du Colombier 		*end = newword(a->word, 0);
6693e12c5d1SDavid du Colombier 	*end = tail;
6703e12c5d1SDavid du Colombier 	return v;
6713e12c5d1SDavid du Colombier }
672dc5a79c1SDavid du Colombier 
673dc5a79c1SDavid du Colombier void
674dc5a79c1SDavid du Colombier Xdol(void)
675dc5a79c1SDavid du Colombier {
6763e12c5d1SDavid du Colombier 	word *a, *star;
6773e12c5d1SDavid du Colombier 	char *s, *t;
6783e12c5d1SDavid du Colombier 	int n;
6793e12c5d1SDavid du Colombier 	if(count(runq->argv->words)!=1){
6809a747e4fSDavid du Colombier 		Xerror1("variable name not singleton!");
6813e12c5d1SDavid du Colombier 		return;
6823e12c5d1SDavid du Colombier 	}
6833e12c5d1SDavid du Colombier 	s = runq->argv->words->word;
6843e12c5d1SDavid du Colombier 	deglob(s);
6853e12c5d1SDavid du Colombier 	n = 0;
68699eb86a7SDavid du Colombier 	for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
6873e12c5d1SDavid du Colombier 	a = runq->argv->next->words;
6883e12c5d1SDavid du Colombier 	if(n==0 || *t)
6893e12c5d1SDavid du Colombier 		a = copywords(vlook(s)->val, a);
6903e12c5d1SDavid du Colombier 	else{
6913e12c5d1SDavid du Colombier 		star = vlook("*")->val;
6923e12c5d1SDavid du Colombier 		if(star && 1<=n && n<=count(star)){
69399eb86a7SDavid du Colombier 			while(--n) star = star->next;
6943e12c5d1SDavid du Colombier 			a = newword(star->word, a);
6953e12c5d1SDavid du Colombier 		}
6963e12c5d1SDavid du Colombier 	}
6973e12c5d1SDavid du Colombier 	poplist();
6983e12c5d1SDavid du Colombier 	runq->argv->words = a;
6993e12c5d1SDavid du Colombier }
700dc5a79c1SDavid du Colombier 
701dc5a79c1SDavid du Colombier void
702dc5a79c1SDavid du Colombier Xqdol(void)
703dc5a79c1SDavid du Colombier {
7043e12c5d1SDavid du Colombier 	word *a, *p;
7053e12c5d1SDavid du Colombier 	char *s;
7063e12c5d1SDavid du Colombier 	int n;
7073e12c5d1SDavid du Colombier 	if(count(runq->argv->words)!=1){
7089a747e4fSDavid du Colombier 		Xerror1("variable name not singleton!");
7093e12c5d1SDavid du Colombier 		return;
7103e12c5d1SDavid du Colombier 	}
7113e12c5d1SDavid du Colombier 	s = runq->argv->words->word;
7123e12c5d1SDavid du Colombier 	deglob(s);
7133e12c5d1SDavid du Colombier 	a = vlook(s)->val;
7143e12c5d1SDavid du Colombier 	poplist();
7153e12c5d1SDavid du Colombier 	n = count(a);
7163e12c5d1SDavid du Colombier 	if(n==0){
7173e12c5d1SDavid du Colombier 		pushword("");
7183e12c5d1SDavid du Colombier 		return;
7193e12c5d1SDavid du Colombier 	}
7203e12c5d1SDavid du Colombier 	for(p = a;p;p = p->next) n+=strlen(p->word);
7213e12c5d1SDavid du Colombier 	s = emalloc(n);
7223e12c5d1SDavid du Colombier 	if(a){
7233e12c5d1SDavid du Colombier 		strcpy(s, a->word);
7243e12c5d1SDavid du Colombier 		for(p = a->next;p;p = p->next){
7253e12c5d1SDavid du Colombier 			strcat(s, " ");
7263e12c5d1SDavid du Colombier 			strcat(s, p->word);
7273e12c5d1SDavid du Colombier 		}
7283e12c5d1SDavid du Colombier 	}
7293e12c5d1SDavid du Colombier 	else
7303e12c5d1SDavid du Colombier 		s[0]='\0';
7313e12c5d1SDavid du Colombier 	pushword(s);
7323e12c5d1SDavid du Colombier 	efree(s);
7333e12c5d1SDavid du Colombier }
734dc5a79c1SDavid du Colombier 
735dc5a79c1SDavid du Colombier word*
7365e061cc0SDavid du Colombier copynwords(word *a, word *tail, int n)
7375e061cc0SDavid du Colombier {
7385e061cc0SDavid du Colombier 	word *v, **end;
7395e061cc0SDavid du Colombier 
7405e061cc0SDavid du Colombier 	v = 0;
7415e061cc0SDavid du Colombier 	end = &v;
7425e061cc0SDavid du Colombier 	while(n-- > 0){
7435e061cc0SDavid du Colombier 		*end = newword(a->word, 0);
7445e061cc0SDavid du Colombier 		end = &(*end)->next;
7455e061cc0SDavid du Colombier 		a = a->next;
7465e061cc0SDavid du Colombier 	}
7475e061cc0SDavid du Colombier 	*end = tail;
7485e061cc0SDavid du Colombier 	return v;
7495e061cc0SDavid du Colombier }
7505e061cc0SDavid du Colombier 
7515e061cc0SDavid du Colombier word*
752dc5a79c1SDavid du Colombier subwords(word *val, int len, word *sub, word *a)
7533e12c5d1SDavid du Colombier {
7545e061cc0SDavid du Colombier 	int n, m;
7553e12c5d1SDavid du Colombier 	char *s;
756dc5a79c1SDavid du Colombier 	if(!sub)
757dc5a79c1SDavid du Colombier 		return a;
7583e12c5d1SDavid du Colombier 	a = subwords(val, len, sub->next, a);
7593e12c5d1SDavid du Colombier 	s = sub->word;
7603e12c5d1SDavid du Colombier 	deglob(s);
7615e061cc0SDavid du Colombier 	m = 0;
7623e12c5d1SDavid du Colombier 	n = 0;
7635e061cc0SDavid du Colombier 	while('0'<=*s && *s<='9')
7645e061cc0SDavid du Colombier 		n = n*10+ *s++ -'0';
7655e061cc0SDavid du Colombier 	if(*s == '-'){
7665e061cc0SDavid du Colombier 		if(*++s == 0)
7675e061cc0SDavid du Colombier 			m = len - n;
7685e061cc0SDavid du Colombier 		else{
7695e061cc0SDavid du Colombier 			while('0'<=*s && *s<='9')
7705e061cc0SDavid du Colombier 				m = m*10+ *s++ -'0';
7715e061cc0SDavid du Colombier 			m -= n;
7725e061cc0SDavid du Colombier 		}
7735e061cc0SDavid du Colombier 	}
7745e061cc0SDavid du Colombier 	if(n<1 || n>len || m<0)
775dc5a79c1SDavid du Colombier 		return a;
7765e061cc0SDavid du Colombier 	if(n+m>len)
7775e061cc0SDavid du Colombier 		m = len-n;
7785e061cc0SDavid du Colombier 	while(--n > 0)
7795e061cc0SDavid du Colombier 		val = val->next;
7805e061cc0SDavid du Colombier 	return copynwords(val, a, m+1);
7813e12c5d1SDavid du Colombier }
782dc5a79c1SDavid du Colombier 
783dc5a79c1SDavid du Colombier void
784dc5a79c1SDavid du Colombier Xsub(void)
785dc5a79c1SDavid du Colombier {
7863e12c5d1SDavid du Colombier 	word *a, *v;
7873e12c5d1SDavid du Colombier 	char *s;
7883e12c5d1SDavid du Colombier 	if(count(runq->argv->next->words)!=1){
7899a747e4fSDavid du Colombier 		Xerror1("variable name not singleton!");
7903e12c5d1SDavid du Colombier 		return;
7913e12c5d1SDavid du Colombier 	}
7923e12c5d1SDavid du Colombier 	s = runq->argv->next->words->word;
7933e12c5d1SDavid du Colombier 	deglob(s);
7943e12c5d1SDavid du Colombier 	a = runq->argv->next->next->words;
7953e12c5d1SDavid du Colombier 	v = vlook(s)->val;
7963e12c5d1SDavid du Colombier 	a = subwords(v, count(v), runq->argv->words, a);
7973e12c5d1SDavid du Colombier 	poplist();
7983e12c5d1SDavid du Colombier 	poplist();
7993e12c5d1SDavid du Colombier 	runq->argv->words = a;
8003e12c5d1SDavid du Colombier }
801dc5a79c1SDavid du Colombier 
802dc5a79c1SDavid du Colombier void
803dc5a79c1SDavid du Colombier Xcount(void)
804dc5a79c1SDavid du Colombier {
8053e12c5d1SDavid du Colombier 	word *a;
8063e12c5d1SDavid du Colombier 	char *s, *t;
8073e12c5d1SDavid du Colombier 	int n;
8083e12c5d1SDavid du Colombier 	char num[12];
8093e12c5d1SDavid du Colombier 	if(count(runq->argv->words)!=1){
8109a747e4fSDavid du Colombier 		Xerror1("variable name not singleton!");
8113e12c5d1SDavid du Colombier 		return;
8123e12c5d1SDavid du Colombier 	}
8133e12c5d1SDavid du Colombier 	s = runq->argv->words->word;
8143e12c5d1SDavid du Colombier 	deglob(s);
8153e12c5d1SDavid du Colombier 	n = 0;
8163e12c5d1SDavid du Colombier 	for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
8173e12c5d1SDavid du Colombier 	if(n==0 || *t){
8183e12c5d1SDavid du Colombier 		a = vlook(s)->val;
819dc5a79c1SDavid du Colombier 		inttoascii(num, count(a));
8203e12c5d1SDavid du Colombier 	}
8213e12c5d1SDavid du Colombier 	else{
8223e12c5d1SDavid du Colombier 		a = vlook("*")->val;
823dc5a79c1SDavid du Colombier 		inttoascii(num, a && 1<=n && n<=count(a)?1:0);
8243e12c5d1SDavid du Colombier 	}
8253e12c5d1SDavid du Colombier 	poplist();
8263e12c5d1SDavid du Colombier 	pushword(num);
8273e12c5d1SDavid du Colombier }
828dc5a79c1SDavid du Colombier 
829dc5a79c1SDavid du Colombier void
830dc5a79c1SDavid du Colombier Xlocal(void)
831dc5a79c1SDavid du Colombier {
8323e12c5d1SDavid du Colombier 	if(count(runq->argv->words)!=1){
8339a747e4fSDavid du Colombier 		Xerror1("variable name must be singleton\n");
8343e12c5d1SDavid du Colombier 		return;
8353e12c5d1SDavid du Colombier 	}
8363e12c5d1SDavid du Colombier 	deglob(runq->argv->words->word);
8373e12c5d1SDavid du Colombier 	runq->local = newvar(strdup(runq->argv->words->word), runq->local);
8383e12c5d1SDavid du Colombier 	poplist();
839fed0fa9eSDavid du Colombier 	globlist();
840fed0fa9eSDavid du Colombier 	runq->local->val = runq->argv->words;
841fed0fa9eSDavid du Colombier 	runq->local->changed = 1;
842fed0fa9eSDavid du Colombier 	runq->argv->words = 0;
8433e12c5d1SDavid du Colombier 	poplist();
8443e12c5d1SDavid du Colombier }
845dc5a79c1SDavid du Colombier 
846dc5a79c1SDavid du Colombier void
847dc5a79c1SDavid du Colombier Xunlocal(void)
848dc5a79c1SDavid du Colombier {
8493e12c5d1SDavid du Colombier 	var *v = runq->local, *hid;
850dc5a79c1SDavid du Colombier 	if(v==0)
851dc5a79c1SDavid du Colombier 		panic("Xunlocal: no locals!", 0);
8523e12c5d1SDavid du Colombier 	runq->local = v->next;
8533e12c5d1SDavid du Colombier 	hid = vlook(v->name);
8543e12c5d1SDavid du Colombier 	hid->changed = 1;
8553e12c5d1SDavid du Colombier 	efree(v->name);
8563e12c5d1SDavid du Colombier 	freewords(v->val);
8573e12c5d1SDavid du Colombier 	efree((char *)v);
8583e12c5d1SDavid du Colombier }
859dc5a79c1SDavid du Colombier 
860dc5a79c1SDavid du Colombier void
861dc5a79c1SDavid du Colombier freewords(word *w)
8623e12c5d1SDavid du Colombier {
8633e12c5d1SDavid du Colombier 	word *nw;
8643e12c5d1SDavid du Colombier 	while(w){
8653e12c5d1SDavid du Colombier 		efree(w->word);
8663e12c5d1SDavid du Colombier 		nw = w->next;
8673e12c5d1SDavid du Colombier 		efree((char *)w);
8683e12c5d1SDavid du Colombier 		w = nw;
8693e12c5d1SDavid du Colombier 	}
8703e12c5d1SDavid du Colombier }
871dc5a79c1SDavid du Colombier 
872dc5a79c1SDavid du Colombier void
873dc5a79c1SDavid du Colombier Xfn(void)
874dc5a79c1SDavid du Colombier {
8753e12c5d1SDavid du Colombier 	var *v;
8763e12c5d1SDavid du Colombier 	word *a;
8773e12c5d1SDavid du Colombier 	int end;
8783e12c5d1SDavid du Colombier 	end = runq->code[runq->pc].i;
879fed0fa9eSDavid du Colombier 	globlist();
8803e12c5d1SDavid du Colombier 	for(a = runq->argv->words;a;a = a->next){
8813e12c5d1SDavid du Colombier 		v = gvlook(a->word);
882dc5a79c1SDavid du Colombier 		if(v->fn)
883dc5a79c1SDavid du Colombier 			codefree(v->fn);
8843e12c5d1SDavid du Colombier 		v->fn = codecopy(runq->code);
8853e12c5d1SDavid du Colombier 		v->pc = runq->pc+2;
8863e12c5d1SDavid du Colombier 		v->fnchanged = 1;
8873e12c5d1SDavid du Colombier 	}
8883e12c5d1SDavid du Colombier 	runq->pc = end;
8893e12c5d1SDavid du Colombier 	poplist();
8903e12c5d1SDavid du Colombier }
891dc5a79c1SDavid du Colombier 
892dc5a79c1SDavid du Colombier void
893dc5a79c1SDavid du Colombier Xdelfn(void)
894dc5a79c1SDavid du Colombier {
8953e12c5d1SDavid du Colombier 	var *v;
8963e12c5d1SDavid du Colombier 	word *a;
8973e12c5d1SDavid du Colombier 	for(a = runq->argv->words;a;a = a->next){
8983e12c5d1SDavid du Colombier 		v = gvlook(a->word);
899dc5a79c1SDavid du Colombier 		if(v->fn)
900dc5a79c1SDavid du Colombier 			codefree(v->fn);
9013e12c5d1SDavid du Colombier 		v->fn = 0;
9023e12c5d1SDavid du Colombier 		v->fnchanged = 1;
9033e12c5d1SDavid du Colombier 	}
9043e12c5d1SDavid du Colombier 	poplist();
9053e12c5d1SDavid du Colombier }
906dc5a79c1SDavid du Colombier 
907dc5a79c1SDavid du Colombier char*
908dc5a79c1SDavid du Colombier concstatus(char *s, char *t)
9093e12c5d1SDavid du Colombier {
9103e12c5d1SDavid du Colombier 	static char v[NSTATUS+1];
9113e12c5d1SDavid du Colombier 	int n = strlen(s);
9123e12c5d1SDavid du Colombier 	strncpy(v, s, NSTATUS);
9133e12c5d1SDavid du Colombier 	if(n<NSTATUS){
9143e12c5d1SDavid du Colombier 		v[n]='|';
9153e12c5d1SDavid du Colombier 		strncpy(v+n+1, t, NSTATUS-n-1);
9163e12c5d1SDavid du Colombier 	}
9173e12c5d1SDavid du Colombier 	v[NSTATUS]='\0';
9183e12c5d1SDavid du Colombier 	return v;
9193e12c5d1SDavid du Colombier }
920dc5a79c1SDavid du Colombier 
921dc5a79c1SDavid du Colombier void
922dc5a79c1SDavid du Colombier Xpipewait(void)
923dc5a79c1SDavid du Colombier {
9243e12c5d1SDavid du Colombier 	char status[NSTATUS+1];
9253e12c5d1SDavid du Colombier 	if(runq->pid==-1)
9263e12c5d1SDavid du Colombier 		setstatus(concstatus(runq->status, getstatus()));
9273e12c5d1SDavid du Colombier 	else{
9283e12c5d1SDavid du Colombier 		strncpy(status, getstatus(), NSTATUS);
9293e12c5d1SDavid du Colombier 		status[NSTATUS]='\0';
9303e12c5d1SDavid du Colombier 		Waitfor(runq->pid, 1);
9313e12c5d1SDavid du Colombier 		runq->pid=-1;
9323e12c5d1SDavid du Colombier 		setstatus(concstatus(getstatus(), status));
9333e12c5d1SDavid du Colombier 	}
9343e12c5d1SDavid du Colombier }
935dc5a79c1SDavid du Colombier 
936dc5a79c1SDavid du Colombier void
937dc5a79c1SDavid du Colombier Xrdcmds(void)
938dc5a79c1SDavid du Colombier {
9393e12c5d1SDavid du Colombier 	struct thread *p = runq;
9403e12c5d1SDavid du Colombier 	word *prompt;
9413e12c5d1SDavid du Colombier 	flush(err);
9423e12c5d1SDavid du Colombier 	nerror = 0;
9433e12c5d1SDavid du Colombier 	if(flag['s'] && !truestatus())
9443e12c5d1SDavid du Colombier 		pfmt(err, "status=%v\n", vlook("status")->val);
9453e12c5d1SDavid du Colombier 	if(runq->iflag){
9463e12c5d1SDavid du Colombier 		prompt = vlook("prompt")->val;
9473e12c5d1SDavid du Colombier 		if(prompt)
9483e12c5d1SDavid du Colombier 			promptstr = prompt->word;
9493e12c5d1SDavid du Colombier 		else
9503e12c5d1SDavid du Colombier 			promptstr="% ";
9513e12c5d1SDavid du Colombier 	}
9523e12c5d1SDavid du Colombier 	Noerror();
9533e12c5d1SDavid du Colombier 	if(yyparse()){
9543e12c5d1SDavid du Colombier 		if(!p->iflag || p->eof && !Eintr()){
955dc5a79c1SDavid du Colombier 			if(p->cmdfile)
956dc5a79c1SDavid du Colombier 				efree(p->cmdfile);
9573e12c5d1SDavid du Colombier 			closeio(p->cmdfd);
9583e12c5d1SDavid du Colombier 			Xreturn();	/* should this be omitted? */
9593e12c5d1SDavid du Colombier 		}
9603e12c5d1SDavid du Colombier 		else{
9613e12c5d1SDavid du Colombier 			if(Eintr()){
9623e12c5d1SDavid du Colombier 				pchr(err, '\n');
9633e12c5d1SDavid du Colombier 				p->eof = 0;
9643e12c5d1SDavid du Colombier 			}
9653e12c5d1SDavid du Colombier 			--p->pc;	/* go back for next command */
9663e12c5d1SDavid du Colombier 		}
9673e12c5d1SDavid du Colombier 	}
9683e12c5d1SDavid du Colombier 	else{
9697dd7cddfSDavid du Colombier 		ntrap = 0;	/* avoid double-interrupts during blocked writes */
9703e12c5d1SDavid du Colombier 		--p->pc;	/* re-execute Xrdcmds after codebuf runs */
9713e12c5d1SDavid du Colombier 		start(codebuf, 1, runq->local);
9723e12c5d1SDavid du Colombier 	}
9733e12c5d1SDavid du Colombier 	freenodes();
9743e12c5d1SDavid du Colombier }
975dc5a79c1SDavid du Colombier 
976dc5a79c1SDavid du Colombier void
977c6df1444SDavid du Colombier pargv0(io *f)
9783e12c5d1SDavid du Colombier {
9799a747e4fSDavid du Colombier 	if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
980c6df1444SDavid du Colombier 		pfmt(f, "rc: ");
9819a747e4fSDavid du Colombier 	else
982c6df1444SDavid du Colombier 		pfmt(f, "rc (%s): ", argv0);
983c6df1444SDavid du Colombier }
984c6df1444SDavid du Colombier 
985c6df1444SDavid du Colombier void
986c6df1444SDavid du Colombier hisfault(io *f)
987c6df1444SDavid du Colombier {
988c6df1444SDavid du Colombier 	thread *t;
989c6df1444SDavid du Colombier 
990c6df1444SDavid du Colombier 	for(t = runq; !t->cmdfile && t->ret != 0; t = t->ret)
991c6df1444SDavid du Colombier 		;
992c6df1444SDavid du Colombier 	if(t->cmdfile && !t->iflag)
993c6df1444SDavid du Colombier 		pfmt(f, "%s:%d ", t->cmdfile, t->lineno);
994c6df1444SDavid du Colombier }
995c6df1444SDavid du Colombier 
996c6df1444SDavid du Colombier void
997c6df1444SDavid du Colombier Xerror(char *s)
998c6df1444SDavid du Colombier {
999c6df1444SDavid du Colombier 	io *msg = openstr();
1000c6df1444SDavid du Colombier 
1001c6df1444SDavid du Colombier 	pargv0(msg);
1002c6df1444SDavid du Colombier 	hisfault(msg);		/* capture errstr before another sys call */
1003c6df1444SDavid du Colombier 	pfmt(err, "%s%s: %r\n", (char *)msg->strp, s);
1004c6df1444SDavid du Colombier 	closeio(msg);
10059a747e4fSDavid du Colombier 	flush(err);
1006d3907fe5SDavid du Colombier 	setstatus("error");
10079a747e4fSDavid du Colombier 	while(!runq->iflag) Xreturn();
10089a747e4fSDavid du Colombier }
1009dc5a79c1SDavid du Colombier 
1010dc5a79c1SDavid du Colombier void
1011c6df1444SDavid du Colombier Xerror1(char *s)			/* omit errstr */
10129a747e4fSDavid du Colombier {
1013c6df1444SDavid du Colombier 	pargv0(err);
1014c6df1444SDavid du Colombier 	hisfault(err);
1015c6df1444SDavid du Colombier 	pfmt(err, "%s\n", s);
10163e12c5d1SDavid du Colombier 	flush(err);
1017d3907fe5SDavid du Colombier 	setstatus("error");
10183e12c5d1SDavid du Colombier 	while(!runq->iflag) Xreturn();
10193e12c5d1SDavid du Colombier }
1020dc5a79c1SDavid du Colombier 
1021dc5a79c1SDavid du Colombier void
1022dc5a79c1SDavid du Colombier setstatus(char *s)
10233e12c5d1SDavid du Colombier {
10243e12c5d1SDavid du Colombier 	setvar("status", newword(s, (word *)0));
10253e12c5d1SDavid du Colombier }
1026dc5a79c1SDavid du Colombier 
1027dc5a79c1SDavid du Colombier char*
1028dc5a79c1SDavid du Colombier getstatus(void)
1029dc5a79c1SDavid du Colombier {
10303e12c5d1SDavid du Colombier 	var *status = vlook("status");
10313e12c5d1SDavid du Colombier 	return status->val?status->val->word:"";
10323e12c5d1SDavid du Colombier }
1033dc5a79c1SDavid du Colombier 
1034dc5a79c1SDavid du Colombier int
1035dc5a79c1SDavid du Colombier truestatus(void)
1036dc5a79c1SDavid du Colombier {
10373e12c5d1SDavid du Colombier 	char *s;
10383e12c5d1SDavid du Colombier 	for(s = getstatus();*s;s++)
1039dc5a79c1SDavid du Colombier 		if(*s!='|' && *s!='0')
1040dc5a79c1SDavid du Colombier 			return 0;
10413e12c5d1SDavid du Colombier 	return 1;
10423e12c5d1SDavid du Colombier }
1043dc5a79c1SDavid du Colombier 
1044dc5a79c1SDavid du Colombier void
1045dc5a79c1SDavid du Colombier Xdelhere(void)
1046dc5a79c1SDavid du Colombier {
10473e12c5d1SDavid du Colombier 	Unlink(runq->code[runq->pc++].s);
10483e12c5d1SDavid du Colombier }
1049dc5a79c1SDavid du Colombier 
1050dc5a79c1SDavid du Colombier void
1051dc5a79c1SDavid du Colombier Xfor(void)
1052dc5a79c1SDavid du Colombier {
10533e12c5d1SDavid du Colombier 	if(runq->argv->words==0){
10543e12c5d1SDavid du Colombier 		poplist();
10553e12c5d1SDavid du Colombier 		runq->pc = runq->code[runq->pc].i;
10563e12c5d1SDavid du Colombier 	}
10573e12c5d1SDavid du Colombier 	else{
10583e12c5d1SDavid du Colombier 		freelist(runq->local->val);
10593e12c5d1SDavid du Colombier 		runq->local->val = runq->argv->words;
10603e12c5d1SDavid du Colombier 		runq->local->changed = 1;
10613e12c5d1SDavid du Colombier 		runq->argv->words = runq->argv->words->next;
10623e12c5d1SDavid du Colombier 		runq->local->val->next = 0;
10633e12c5d1SDavid du Colombier 		runq->pc++;
10643e12c5d1SDavid du Colombier 	}
10653e12c5d1SDavid du Colombier }
1066dc5a79c1SDavid du Colombier 
1067dc5a79c1SDavid du Colombier void
1068dc5a79c1SDavid du Colombier Xglob(void)
1069dc5a79c1SDavid du Colombier {
10703e12c5d1SDavid du Colombier 	globlist();
10713e12c5d1SDavid du Colombier }
1072