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