Blob


1 #include "sam.h"
2 #include "parse.h"
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);
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;
59 void
60 resetcmd(void)
61 {
62 linep = line;
63 *linep = 0;
64 terminp = termoutp = termline;
65 freecmd();
66 }
68 int
69 inputc(void)
70 {
71 int n, nbuf;
72 char buf[3];
73 Rune r;
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);
101 if(r == 0){
102 warn(Wnulls);
103 goto Again;
105 return r;
108 int
109 inputline(void)
111 int i, c;
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;
125 int
126 getch(void)
128 if(eof)
129 return -1;
130 if(*linep==0 && inputline()<0){
131 eof = TRUE;
132 return -1;
134 return *linep++;
137 int
138 nextc(void)
140 if(*linep == 0)
141 return -1;
142 return *linep;
145 void
146 ungetch(void)
148 if(--linep < line)
149 panic("ungetch");
152 Posn
153 getnum(int signok)
155 Posn n=0;
156 int c, sign;
158 sign = 1;
159 if(signok>1 && nextc()=='-'){
160 sign = -1;
161 getch();
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;
171 int
172 skipbl(void)
174 int c;
175 do
176 c = getch();
177 while(c==' ' || c=='\t');
178 if(c >= 0)
179 ungetch();
180 return c;
183 void
184 termcommand(void)
186 Posn p;
188 for(p=cmdpt; p<cmd->b.nc; p++){
189 if(terminp >= &termline[BLOCKSIZE]){
190 cmdpt = cmd->b.nc;
191 error(Etoolong);
193 *terminp++ = filereadc(cmd, p);
195 cmdpt = cmd->b.nc;
198 void
199 cmdloop(void)
201 Cmd *cmdp;
202 File *ocurfile;
203 int loaded;
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");
213 break;
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;
231 Cmd *
232 newcmd(void){
233 Cmd *p;
235 p = emalloc(sizeof(Cmd));
236 inslist(&cmdlist, cmdlist.nused, (long)p);
237 return p;
240 Addr*
241 newaddr(void)
243 Addr *p;
245 p = emalloc(sizeof(Addr));
246 inslist(&addrlist, addrlist.nused, (long)p);
247 return p;
250 String*
251 newre(void)
253 String *p;
255 p = emalloc(sizeof(String));
256 inslist(&relist, relist.nused, (long)p);
257 Strinit(p);
258 return p;
261 String*
262 newstring(void)
264 String *p;
266 p = emalloc(sizeof(String));
267 inslist(&stringlist, stringlist.nused, (long)p);
268 Strinit(p);
269 return p;
272 void
273 freecmd(void)
275 int i;
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]);
286 while(stringlist.nused>0){
287 i = --stringlist.nused;
288 Strclose(stringlist.stringpptr[i]);
289 free(stringlist.stringpptr[i]);
293 int
294 lookup(int c)
296 int i;
298 for(i=0; cmdtab[i].cmdc; i++)
299 if(cmdtab[i].cmdc == c)
300 return i;
301 return -1;
304 void
305 okdelim(int c)
307 if(c=='\\' || ('a'<=c && c<='z')
308 || ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
309 error_c(Edelim, c);
312 void
313 atnl(void)
315 skipbl();
316 if(getch() != '\n')
317 error(Enewline);
320 void
321 getrhs(String *s, int delim, int cmd)
323 int c;
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, '\\');
337 Straddc(s, c);
339 ungetch(); /* let client read whether delimeter, '\n' or whatever */
342 String *
343 collecttoken(char *end)
345 String *s = newstring();
346 int c;
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;
358 String *
359 collecttext(void)
361 String *s = newstring();
362 int begline, i, c, delim;
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();
383 Return:
384 Straddc(s, 0); /* JUST FOR CMDPRINT() */
385 return s;
388 Cmd *
389 parsecmd(int nest)
391 int i, c;
392 struct cmdtab *ct;
393 Cmd *cp, *ncp;
394 Cmd cmd;
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;
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();
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);
477 Return:
478 cp = newcmd();
479 *cp = cmd;
480 return cp;
483 String* /* BUGGERED */
484 getregexp(int delim)
486 String *r = newre();
487 int c;
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();
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');
506 if(lastpat.n <= 1)
507 error(Epattern);
508 Strduplstr(r, &lastpat);
509 return r;
512 Addr *
513 simpleaddr(void)
515 Addr addr;
516 Addr *ap, *nap;
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;
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;
566 break;
567 case '+':
568 case '-':
569 break;
570 default:
571 panic("simpleaddr");
573 ap = newaddr();
574 *ap = addr;
575 return ap;
578 Addr *
579 compoundaddr(void)
581 Addr addr;
582 Addr *ap, *next;
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;