xref: /plan9-contrib/sys/src/cmd/sam/cmd.c (revision 9b943567965ba040fd275927fbe088656eb8ce4f)
1 #include "sam.h"
2 #include "parse.h"
3 
4 static char	linex[]="\n";
5 static char	wordx[]=" \t\n";
6 struct cmdtab cmdtab[]={
7 /*	cmdc	text	regexp	addr	defcmd	defaddr	count	token	 fn	*/
8 	'\n',	0,	0,	0,	0,	aDot,	0,	0,	nl_cmd,
9 	'a',	1,	0,	0,	0,	aDot,	0,	0,	a_cmd,
10 	'b',	0,	0,	0,	0,	aNo,	0,	linex,	b_cmd,
11 	'B',	0,	0,	0,	0,	aNo,	0,	linex,	b_cmd,
12 	'c',	1,	0,	0,	0,	aDot,	0,	0,	c_cmd,
13 	'd',	0,	0,	0,	0,	aDot,	0,	0,	d_cmd,
14 	'D',	0,	0,	0,	0,	aNo,	0,	linex,	D_cmd,
15 	'e',	0,	0,	0,	0,	aNo,	0,	wordx,	e_cmd,
16 	'f',	0,	0,	0,	0,	aNo,	0,	wordx,	f_cmd,
17 	'g',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
18 	'i',	1,	0,	0,	0,	aDot,	0,	0,	i_cmd,
19 	'k',	0,	0,	0,	0,	aDot,	0,	0,	k_cmd,
20 	'm',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
21 	'n',	0,	0,	0,	0,	aNo,	0,	0,	n_cmd,
22 	'p',	0,	0,	0,	0,	aDot,	0,	0,	p_cmd,
23 	'q',	0,	0,	0,	0,	aNo,	0,	0,	q_cmd,
24 	'r',	0,	0,	0,	0,	aDot,	0,	wordx,	e_cmd,
25 	's',	0,	1,	0,	0,	aDot,	1,	0,	s_cmd,
26 	't',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
27 	'u',	0,	0,	0,	0,	aNo,	2,	0,	u_cmd,
28 	'v',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
29 	'w',	0,	0,	0,	0,	aAll,	0,	wordx,	w_cmd,
30 	'x',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
31 	'y',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
32 	'X',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
33 	'Y',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
34 	'!',	0,	0,	0,	0,	aNo,	0,	linex,	plan9_cmd,
35 	'>',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
36 	'<',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
37 	'|',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
38 	'=',	0,	0,	0,	0,	aDot,	0,	linex,	eq_cmd,
39 	'c'|0x100,0,	0,	0,	0,	aNo,	0,	wordx,	cd_cmd,
40 	0,	0,	0,	0,	0,	0,	0,	0,
41 };
42 Cmd	*parsecmd(int);
43 Addr	*compoundaddr(void);
44 Addr	*simpleaddr(void);
45 void	freecmd(void);
46 void	okdelim(int);
47 
48 Rune	line[BLOCKSIZE];
49 Rune	termline[BLOCKSIZE];
50 Rune	*linep = line;
51 Rune	*terminp = termline;
52 Rune	*termoutp = termline;
53 List	cmdlist;
54 List	addrlist;
55 List	relist;
56 List	stringlist;
57 int	eof;
58 
59 void
60 resetcmd(void)
61 {
62 	linep = line;
63 	*linep = 0;
64 	terminp = termoutp = termline;
65 	freecmd();
66 }
67 
68 int
69 inputc(void)
70 {
71 	int n, nbuf;
72 	char buf[3];
73 	Rune r;
74 
75     Again:
76 	nbuf = 0;
77 	if(downloaded){
78 		while(termoutp == terminp){
79 			cmdupdate();
80 			if(patset)
81 				tellpat();
82 			while(termlocked > 0){
83 				outT0(Hunlock);
84 				termlocked--;
85 			}
86 			if(rcv() == 0)
87 				return -1;
88 		}
89 		r = *termoutp++;
90 		if(termoutp == terminp)
91 			terminp = termoutp = termline;
92 	}else{
93    		do{
94 			n = read(0, buf+nbuf, 1);
95 			if(n <= 0)
96 				return -1;
97 			nbuf += n;
98 		}while(!fullrune(buf, nbuf));
99 		chartorune(&r, buf);
100 	}
101 	if(r == 0){
102 		warn(Wnulls);
103 		goto Again;
104 	}
105 	return r;
106 }
107 
108 int
109 inputline(void)
110 {
111 	int i, c;
112 
113 	linep = line;
114 	i = 0;
115 	do{
116 		if((c = inputc())<=0)
117 			return -1;
118 		if(i == (sizeof line)/RUNESIZE-1)
119 			error(Etoolong);
120 	}while((line[i++]=c) != '\n');
121 	line[i] = 0;
122 	return 1;
123 }
124 
125 int
126 getch(void)
127 {
128 	if(eof)
129 		return -1;
130 	if(*linep==0 && inputline()<0){
131 		eof = TRUE;
132 		return -1;
133 	}
134 	return *linep++;
135 }
136 
137 int
138 nextc(void)
139 {
140 	if(*linep == 0)
141 		return -1;
142 	return *linep;
143 }
144 
145 void
146 ungetch(void)
147 {
148 	if(--linep < line)
149 		panic("ungetch");
150 }
151 
152 Posn
153 getnum(int signok)
154 {
155 	Posn n=0;
156 	int c, sign;
157 
158 	sign = 1;
159 	if(signok>1 && nextc()=='-'){
160 		sign = -1;
161 		getch();
162 	}
163 	if((c=nextc())<'0' || '9'<c)	/* no number defaults to 1 */
164 		return sign;
165 	while('0'<=(c=getch()) && c<='9')
166 		n = n*10 + (c-'0');
167 	ungetch();
168 	return sign*n;
169 }
170 
171 int
172 skipbl(void)
173 {
174 	int c;
175 	do
176 		c = getch();
177 	while(c==' ' || c=='\t');
178 	if(c >= 0)
179 		ungetch();
180 	return c;
181 }
182 
183 void
184 termcommand(void)
185 {
186 	Posn p;
187 
188 	for(p=cmdpt; p<cmd->nc; p++){
189 		if(terminp >= &termline[BLOCKSIZE]){
190 			cmdpt = cmd->nc;
191 			error(Etoolong);
192 		}
193 		*terminp++ = filereadc(cmd, p);
194 	}
195 	cmdpt = cmd->nc;
196 }
197 
198 void
199 cmdloop(void)
200 {
201 	Cmd *cmdp;
202 	File *ocurfile;
203 	int loaded;
204 
205 	for(;;){
206 		if(!downloaded && curfile && curfile->unread)
207 			load(curfile);
208 		if((cmdp = parsecmd(0))==0){
209 			if(downloaded){
210 				rescue();
211 				exits("eof");
212 			}
213 			break;
214 		}
215 		ocurfile = curfile;
216 		loaded = curfile && !curfile->unread;
217 		if(cmdexec(curfile, cmdp) == 0)
218 			break;
219 		freecmd();
220 		cmdupdate();
221 		update();
222 		if(downloaded && curfile &&
223 		    (ocurfile!=curfile || (!loaded && !curfile->unread)))
224 			outTs(Hcurrent, curfile->tag);
225 			/* don't allow type ahead on files that aren't bound */
226 		if(downloaded && curfile && curfile->rasp == 0)
227 			terminp = termoutp;
228 	}
229 }
230 
231 Cmd *
232 newcmd(void){
233 	Cmd *p;
234 
235 	p = emalloc(sizeof(Cmd));
236 	inslist(&cmdlist, cmdlist.nused, (long)p);
237 	return p;
238 }
239 
240 Addr*
241 newaddr(void)
242 {
243 	Addr *p;
244 
245 	p = emalloc(sizeof(Addr));
246 	inslist(&addrlist, addrlist.nused, (long)p);
247 	return p;
248 }
249 
250 String*
251 newre(void)
252 {
253 	String *p;
254 
255 	p = emalloc(sizeof(String));
256 	inslist(&relist, relist.nused, (long)p);
257 	Strinit(p);
258 	return p;
259 }
260 
261 String*
262 newstring(void)
263 {
264 	String *p;
265 
266 	p = emalloc(sizeof(String));
267 	inslist(&stringlist, stringlist.nused, (long)p);
268 	Strinit(p);
269 	return p;
270 }
271 
272 void
273 freecmd(void)
274 {
275 	int i;
276 
277 	while(cmdlist.nused > 0)
278 		free(cmdlist.ucharpptr[--cmdlist.nused]);
279 	while(addrlist.nused > 0)
280 		free(addrlist.ucharpptr[--addrlist.nused]);
281 	while(relist.nused > 0){
282 		i = --relist.nused;
283 		Strclose(relist.stringpptr[i]);
284 		free(relist.stringpptr[i]);
285 	}
286 	while(stringlist.nused>0){
287 		i = --stringlist.nused;
288 		Strclose(stringlist.stringpptr[i]);
289 		free(stringlist.stringpptr[i]);
290 	}
291 }
292 
293 int
294 lookup(int c)
295 {
296 	int i;
297 
298 	for(i=0; cmdtab[i].cmdc; i++)
299 		if(cmdtab[i].cmdc == c)
300 			return i;
301 	return -1;
302 }
303 
304 void
305 okdelim(int c)
306 {
307 	if(c=='\\' || ('a'<=c && c<='z')
308 	|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
309 		error_c(Edelim, c);
310 }
311 
312 void
313 atnl(void)
314 {
315 	skipbl();
316 	if(getch() != '\n')
317 		error(Enewline);
318 }
319 
320 void
321 getrhs(String *s, int delim, int cmd)
322 {
323 	int c;
324 
325 	while((c = getch())>0 && c!=delim && c!='\n'){
326 		if(c == '\\'){
327 			if((c=getch()) <= 0)
328 				error(Ebadrhs);
329 			if(c == '\n'){
330 				ungetch();
331 				c='\\';
332 			}else if(c == 'n')
333 				c='\n';
334 			else if(c!=delim && (cmd=='s' || c!='\\'))	/* s does its own */
335 				Straddc(s, '\\');
336 		}
337 		Straddc(s, c);
338 	}
339 	ungetch();	/* let client read whether delimeter, '\n' or whatever */
340 }
341 
342 String *
343 collecttoken(char *end)
344 {
345 	String *s = newstring();
346 	int c;
347 
348 	while((c=nextc())==' ' || c=='\t')
349 		Straddc(s, getch()); /* blanks significant for getname() */
350 	while((c=getch())>0 && utfrune(end, c)==0)
351 		Straddc(s, c);
352 	Straddc(s, 0);
353 	if(c != '\n')
354 		atnl();
355 	return s;
356 }
357 
358 String *
359 collecttext(void)
360 {
361 	String *s = newstring();
362 	int begline, i, c, delim;
363 
364 	if(skipbl()=='\n'){
365 		getch();
366 		i = 0;
367 		do{
368 			begline = i;
369 			while((c = getch())>0 && c!='\n')
370 				i++, Straddc(s, c);
371 			i++, Straddc(s, '\n');
372 			if(c < 0)
373 				goto Return;
374 		}while(s->s[begline]!='.' || s->s[begline+1]!='\n');
375 		Strdelete(s, s->n-2, s->n);
376 	}else{
377 		okdelim(delim = getch());
378 		getrhs(s, delim, 'a');
379 		if(nextc()==delim)
380 			getch();
381 		atnl();
382 	}
383     Return:
384 	Straddc(s, 0);		/* JUST FOR CMDPRINT() */
385 	return s;
386 }
387 
388 Cmd *
389 parsecmd(int nest)
390 {
391 	int i, c;
392 	struct cmdtab *ct;
393 	Cmd *cp, *ncp;
394 	Cmd cmd;
395 
396 	cmd.next = cmd.ccmd = 0;
397 	cmd.re = 0;
398 	cmd.flag = cmd.num = 0;
399 	cmd.addr = compoundaddr();
400 	if(skipbl() == -1)
401 		return 0;
402 	if((c=getch())==-1)
403 		return 0;
404 	cmd.cmdc = c;
405 	if(cmd.cmdc=='c' && nextc()=='d'){	/* sleazy two-character case */
406 		getch();		/* the 'd' */
407 		cmd.cmdc='c'|0x100;
408 	}
409 	i = lookup(cmd.cmdc);
410 	if(i >= 0){
411 		if(cmd.cmdc == '\n')
412 			goto Return;	/* let nl_cmd work it all out */
413 		ct = &cmdtab[i];
414 		if(ct->defaddr==aNo && cmd.addr)
415 			error(Enoaddr);
416 		if(ct->count)
417 			cmd.num = getnum(ct->count);
418 		if(ct->regexp){
419 			/* x without pattern -> .*\n, indicated by cmd.re==0 */
420 			/* X without pattern is all files */
421 			if((ct->cmdc!='x' && ct->cmdc!='X') ||
422 			   ((c = nextc())!=' ' && c!='\t' && c!='\n')){
423 				skipbl();
424 				if((c = getch())=='\n' || c<0)
425 					error(Enopattern);
426 				okdelim(c);
427 				cmd.re = getregexp(c);
428 				if(ct->cmdc == 's'){
429 					cmd.ctext = newstring();
430 					getrhs(cmd.ctext, c, 's');
431 					if(nextc() == c){
432 						getch();
433 						if(nextc() == 'g')
434 							cmd.flag = getch();
435 					}
436 
437 				}
438 			}
439 		}
440 		if(ct->addr && (cmd.caddr=simpleaddr())==0)
441 			error(Eaddress);
442 		if(ct->defcmd){
443 			if(skipbl() == '\n'){
444 				getch();
445 				cmd.ccmd = newcmd();
446 				cmd.ccmd->cmdc = ct->defcmd;
447 			}else if((cmd.ccmd = parsecmd(nest))==0)
448 				panic("defcmd");
449 		}else if(ct->text)
450 			cmd.ctext = collecttext();
451 		else if(ct->token)
452 			cmd.ctext = collecttoken(ct->token);
453 		else
454 			atnl();
455 	}else
456 		switch(cmd.cmdc){
457 		case '{':
458 			cp = 0;
459 			do{
460 				if(skipbl()=='\n')
461 					getch();
462 				ncp = parsecmd(nest+1);
463 				if(cp)
464 					cp->next = ncp;
465 				else
466 					cmd.ccmd = ncp;
467 			}while(cp = ncp);
468 			break;
469 		case '}':
470 			atnl();
471 			if(nest==0)
472 				error(Enolbrace);
473 			return 0;
474 		default:
475 			error_c(Eunk, cmd.cmdc);
476 		}
477     Return:
478 	cp = newcmd();
479 	*cp = cmd;
480 	return cp;
481 }
482 
483 String*				/* BUGGERED */
484 getregexp(int delim)
485 {
486 	String *r = newre();
487 	int c;
488 
489 	for(Strzero(&genstr); ; Straddc(&genstr, c))
490 		if((c = getch())=='\\'){
491 			if(nextc()==delim)
492 				c = getch();
493 			else if(nextc()=='\\'){
494 				Straddc(&genstr, c);
495 				c = getch();
496 			}
497 		}else if(c==delim || c=='\n')
498 			break;
499 	if(c!=delim && c)
500 		ungetch();
501 	if(genstr.n > 0){
502 		patset = TRUE;
503 		Strduplstr(&lastpat, &genstr);
504 		Straddc(&lastpat, '\0');
505 	}
506 	if(lastpat.n <= 1)
507 		error(Epattern);
508 	Strduplstr(r, &lastpat);
509 	return r;
510 }
511 
512 Addr *
513 simpleaddr(void)
514 {
515 	Addr addr;
516 	Addr *ap, *nap;
517 
518 	addr.next = 0;
519 	addr.left = 0;
520 	switch(skipbl()){
521 	case '#':
522 		addr.type = getch();
523 		addr.num = getnum(1);
524 		break;
525 	case '0': case '1': case '2': case '3': case '4':
526 	case '5': case '6': case '7': case '8': case '9':
527 		addr.num = getnum(1);
528 		addr.type='l';
529 		break;
530 	case '/': case '?': case '"':
531 		addr.are = getregexp(addr.type = getch());
532 		break;
533 	case '.':
534 	case '$':
535 	case '+':
536 	case '-':
537 	case '\'':
538 		addr.type = getch();
539 		break;
540 	default:
541 		return 0;
542 	}
543 	if(addr.next = simpleaddr())
544 		switch(addr.next->type){
545 		case '.':
546 		case '$':
547 		case '\'':
548 			if(addr.type!='"')
549 		case '"':
550 				error(Eaddress);
551 			break;
552 		case 'l':
553 		case '#':
554 			if(addr.type=='"')
555 				break;
556 			/* fall through */
557 		case '/':
558 		case '?':
559 			if(addr.type!='+' && addr.type!='-'){
560 				/* insert the missing '+' */
561 				nap = newaddr();
562 				nap->type='+';
563 				nap->next = addr.next;
564 				addr.next = nap;
565 			}
566 			break;
567 		case '+':
568 		case '-':
569 			break;
570 		default:
571 			panic("simpleaddr");
572 		}
573 	ap = newaddr();
574 	*ap = addr;
575 	return ap;
576 }
577 
578 Addr *
579 compoundaddr(void)
580 {
581 	Addr addr;
582 	Addr *ap, *next;
583 
584 	addr.left = simpleaddr();
585 	if((addr.type = skipbl())!=',' && addr.type!=';')
586 		return addr.left;
587 	getch();
588 	next = addr.next = compoundaddr();
589 	if(next && (next->type==',' || next->type==';') && next->left==0)
590 		error(Eaddress);
591 	ap = newaddr();
592 	*ap = addr;
593 	return ap;
594 }
595