xref: /plan9/sys/src/cmd/sam/cmd.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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,	1,	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(void)
154 {
155 	Posn n=0;
156 	int c;
157 
158 	if((c=nextc())<'0' || '9'<c)	/* no number defaults to 1 */
159 		return 1;
160 	while('0'<=(c=getch()) && c<='9')
161 		n = n*10 + (c-'0');
162 	ungetch();
163 	return n;
164 }
165 
166 int
167 skipbl(void)
168 {
169 	int c;
170 	do
171 		c = getch();
172 	while(c==' ' || c=='\t');
173 	if(c >= 0)
174 		ungetch();
175 	return c;
176 }
177 
178 void
179 termcommand(void)
180 {
181 	Posn p;
182 
183 	Fgetcset(cmd, cmdpt);
184 	for(p=cmdpt; p<cmd->nrunes; p++){
185 		if(terminp >= &termline[BLOCKSIZE]){
186 			cmdpt = cmd->nrunes;
187 			error(Etoolong);
188 		}
189 		*terminp++ = Fgetc(cmd);
190 	}
191 	cmdpt = cmd->nrunes;
192 }
193 
194 void
195 cmdloop(void)
196 {
197 	Cmd *cmdp;
198 	File *ocurfile;
199 	int loaded;
200 
201 	for(;;){
202 		if(!downloaded && curfile && curfile->state==Unread)
203 			load(curfile);
204 		if((cmdp = parsecmd(0))==0){
205 			if(downloaded){
206 				rescue();
207 				exits("eof");
208 			}
209 			break;
210 		}
211 		ocurfile = curfile;
212 		loaded = curfile && curfile->state!=Unread;
213 		if(cmdexec(curfile, cmdp) == 0)
214 			break;
215 		freecmd();
216 		cmdupdate();
217 		update();
218 		if(downloaded && curfile &&
219 		    (ocurfile!=curfile || (!loaded && curfile->state!=Unread)))
220 			outTs(Hcurrent, curfile->tag);
221 	}
222 }
223 
224 Cmd *
225 newcmd(void){
226 	Cmd *p;
227 
228 	p = emalloc(sizeof(Cmd));
229 	inslist(&cmdlist, cmdlist.nused, (long)p);
230 	return p;
231 }
232 
233 Addr*
234 newaddr(void)
235 {
236 	Addr *p;
237 
238 	p = emalloc(sizeof(Addr));
239 	inslist(&addrlist, addrlist.nused, (long)p);
240 	return p;
241 }
242 
243 String*
244 newre(void)
245 {
246 	String *p;
247 
248 	p = emalloc(sizeof(String));
249 	inslist(&relist, relist.nused, (long)p);
250 	Strinit(p);
251 	return p;
252 }
253 
254 String*
255 newstring(void)
256 {
257 	String *p;
258 
259 	p = emalloc(sizeof(String));
260 	inslist(&stringlist, stringlist.nused, (long)p);
261 	Strinit(p);
262 	return p;
263 }
264 
265 void
266 freecmd(void)
267 {
268 	int i;
269 
270 	while(cmdlist.nused > 0)
271 		free(cmdlist.ucharpptr[--cmdlist.nused]);
272 	while(addrlist.nused > 0)
273 		free(addrlist.ucharpptr[--addrlist.nused]);
274 	while(relist.nused > 0){
275 		i = --relist.nused;
276 		Strclose(relist.stringpptr[i]);
277 		free(relist.stringpptr[i]);
278 	}
279 	while(stringlist.nused>0){
280 		i = --stringlist.nused;
281 		Strclose(stringlist.stringpptr[i]);
282 		free(stringlist.stringpptr[i]);
283 	}
284 }
285 
286 int
287 lookup(int c)
288 {
289 	int i;
290 
291 	for(i=0; cmdtab[i].cmdc; i++)
292 		if(cmdtab[i].cmdc == c)
293 			return i;
294 	return -1;
295 }
296 
297 void
298 okdelim(int c)
299 {
300 	if(c=='\\' || ('a'<=c && c<='z')
301 	|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
302 		error_c(Edelim, c);
303 }
304 
305 void
306 atnl(void)
307 {
308 	skipbl();
309 	if(getch() != '\n')
310 		error(Enewline);
311 }
312 
313 void
314 getrhs(String *s, int delim, int cmd)
315 {
316 	int c;
317 
318 	while((c = getch())>0 && c!=delim && c!='\n'){
319 		if(c == '\\'){
320 			if((c=getch()) <= 0)
321 				error(Ebadrhs);
322 			if(c == '\n'){
323 				ungetch();
324 				c='\\';
325 			}else if(c == 'n')
326 				c='\n';
327 			else if(c!=delim && (cmd=='s' || c!='\\'))	/* s does its own */
328 				Straddc(s, '\\');
329 		}
330 		Straddc(s, c);
331 	}
332 	ungetch();	/* let client read whether delimeter, '\n' or whatever */
333 }
334 
335 String *
336 collecttoken(char *end)
337 {
338 	String *s = newstring();
339 	int c;
340 
341 	while((c=nextc())==' ' || c=='\t')
342 		Straddc(s, getch()); /* blanks significant for getname() */
343 	while((c=getch())>0 && utfrune(end, c)==0)
344 		Straddc(s, c);
345 	Straddc(s, 0);
346 	if(c != '\n')
347 		atnl();
348 	return s;
349 }
350 
351 String *
352 collecttext(void)
353 {
354 	String *s = newstring();
355 	int begline, i, c, delim;
356 
357 	if(skipbl()=='\n'){
358 		getch();
359 		i = 0;
360 		do{
361 			begline = i;
362 			while((c = getch())>0 && c!='\n')
363 				i++, Straddc(s, c);
364 			i++, Straddc(s, '\n');
365 			if(c < 0)
366 				goto Return;
367 		}while(s->s[begline]!='.' || s->s[begline+1]!='\n');
368 		Strdelete(s, s->n-2, s->n);
369 	}else{
370 		okdelim(delim = getch());
371 		getrhs(s, delim, 'a');
372 		if(nextc()==delim)
373 			getch();
374 		atnl();
375 	}
376     Return:
377 	Straddc(s, 0);		/* JUST FOR CMDPRINT() */
378 	return s;
379 }
380 
381 Cmd *
382 parsecmd(int nest)
383 {
384 	int i, c;
385 	struct cmdtab *ct;
386 	Cmd *cp, *ncp;
387 	Cmd cmd;
388 
389 	cmd.next = cmd.ccmd = 0;
390 	cmd.re = 0;
391 	cmd.flag = cmd.num = 0;
392 	cmd.addr = compoundaddr();
393 	if(skipbl() == -1)
394 		return 0;
395 	if((c=getch())==-1)
396 		return 0;
397 	cmd.cmdc = c;
398 	if(cmd.cmdc=='c' && nextc()=='d'){	/* sleazy two-character case */
399 		getch();		/* the 'd' */
400 		cmd.cmdc='c'|0x100;
401 	}
402 	i = lookup(cmd.cmdc);
403 	if(i >= 0){
404 		if(cmd.cmdc == '\n')
405 			goto Return;	/* let nl_cmd work it all out */
406 		ct = &cmdtab[i];
407 		if(ct->defaddr==aNo && cmd.addr)
408 			error(Enoaddr);
409 		if(ct->count)
410 			cmd.num = getnum();
411 		if(ct->regexp){
412 			/* x without pattern -> .*\n, indicated by cmd.re==0 */
413 			/* X without pattern is all files */
414 			if((ct->cmdc!='x' && ct->cmdc!='X') ||
415 			   ((c = nextc())!=' ' && c!='\t' && c!='\n')){
416 				skipbl();
417 				if((c = getch())=='\n' || c<0)
418 					error(Enopattern);
419 				okdelim(c);
420 				cmd.re = getregexp(c);
421 				if(ct->cmdc == 's'){
422 					cmd.ctext = newstring();
423 					getrhs(cmd.ctext, c, 's');
424 					if(nextc() == c){
425 						getch();
426 						if(nextc() == 'g')
427 							cmd.flag = getch();
428 					}
429 
430 				}
431 			}
432 		}
433 		if(ct->addr && (cmd.caddr=simpleaddr())==0)
434 			error(Eaddress);
435 		if(ct->defcmd){
436 			if(skipbl() == '\n'){
437 				getch();
438 				cmd.ccmd = newcmd();
439 				cmd.ccmd->cmdc = ct->defcmd;
440 			}else if((cmd.ccmd = parsecmd(nest))==0)
441 				panic("defcmd");
442 		}else if(ct->text)
443 			cmd.ctext = collecttext();
444 		else if(ct->token)
445 			cmd.ctext = collecttoken(ct->token);
446 		else
447 			atnl();
448 	}else
449 		switch(cmd.cmdc){
450 		case '{':
451 			cp = 0;
452 			do{
453 				if(skipbl()=='\n')
454 					getch();
455 				ncp = parsecmd(nest+1);
456 				if(cp)
457 					cp->next = ncp;
458 				else
459 					cmd.ccmd = ncp;
460 			}while(cp = ncp);
461 			break;
462 		case '}':
463 			atnl();
464 			if(nest==0)
465 				error(Enolbrace);
466 			return 0;
467 		default:
468 			error_c(Eunk, cmd.cmdc);
469 		}
470     Return:
471 	cp = newcmd();
472 	*cp = cmd;
473 	return cp;
474 }
475 
476 String*				/* BUGGERED */
477 getregexp(int delim)
478 {
479 	String *r = newre();
480 	int c;
481 
482 	for(Strzero(&genstr); ; Straddc(&genstr, c))
483 		if((c = getch())=='\\'){
484 			if(nextc()==delim)
485 				c = getch();
486 			else if(nextc()=='\\'){
487 				Straddc(&genstr, c);
488 				c = getch();
489 			}
490 		}else if(c==delim || c=='\n')
491 			break;
492 	if(c!=delim && c)
493 		ungetch();
494 	if(genstr.n > 0){
495 		patset = TRUE;
496 		Strduplstr(&lastpat, &genstr);
497 		Straddc(&lastpat, '\0');
498 	}
499 	if(lastpat.n <= 1)
500 		error(Epattern);
501 	Strduplstr(r, &lastpat);
502 	return r;
503 }
504 
505 Addr *
506 simpleaddr(void)
507 {
508 	Addr addr;
509 	Addr *ap, *nap;
510 
511 	addr.next = 0;
512 	addr.left = 0;
513 	switch(skipbl()){
514 	case '#':
515 		addr.type = getch();
516 		addr.num = getnum();
517 		break;
518 	case '0': case '1': case '2': case '3': case '4':
519 	case '5': case '6': case '7': case '8': case '9':
520 		addr.num = getnum();
521 		addr.type='l';
522 		break;
523 	case '/': case '?': case '"':
524 		addr.are = getregexp(addr.type = getch());
525 		break;
526 	case '.':
527 	case '$':
528 	case '+':
529 	case '-':
530 	case '\'':
531 		addr.type = getch();
532 		break;
533 	default:
534 		return 0;
535 	}
536 	if(addr.next = simpleaddr())
537 		switch(addr.next->type){
538 		case '.':
539 		case '$':
540 		case '\'':
541 			if(addr.type!='"')
542 		case '"':
543 				error(Eaddress);
544 			break;
545 		case 'l':
546 		case '#':
547 			if(addr.type=='"')
548 				break;
549 			/* fall through */
550 		case '/':
551 		case '?':
552 			if(addr.type!='+' && addr.type!='-'){
553 				/* insert the missing '+' */
554 				nap = newaddr();
555 				nap->type='+';
556 				nap->next = addr.next;
557 				addr.next = nap;
558 			}
559 			break;
560 		case '+':
561 		case '-':
562 			break;
563 		default:
564 			panic("simpleaddr");
565 		}
566 	ap = newaddr();
567 	*ap = addr;
568 	return ap;
569 }
570 
571 Addr *
572 compoundaddr(void)
573 {
574 	Addr addr;
575 	Addr *ap, *next;
576 
577 	addr.left = simpleaddr();
578 	if((addr.type = skipbl())!=',' && addr.type!=';')
579 		return addr.left;
580 	getch();
581 	next = addr.next = compoundaddr();
582 	if(next && (next->type==',' || next->type==';') && next->left==0)
583 		error(Eaddress);
584 	ap = newaddr();
585 	*ap = addr;
586 	return ap;
587 }
588