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;
54 List cmdlist = { 'p' };
55 List addrlist = { 'p' };
56 List relist = { 'p' };
57 List stringlist = { 'p' };
59 int eof;
61 void
62 resetcmd(void)
63 {
64 linep = line;
65 *linep = 0;
66 terminp = termoutp = termline;
67 freecmd();
68 }
70 int
71 inputc(void)
72 {
73 int n, nbuf;
74 char buf[3];
75 Rune r;
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);
103 if(r == 0){
104 warn(Wnulls);
105 goto Again;
107 return r;
110 int
111 inputline(void)
113 int i, c, start;
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;
133 }while((line[i++]=c) != '\n');
134 line[i] = 0;
135 return 1;
138 int
139 getch(void)
141 if(eof)
142 return -1;
143 if(*linep==0 && inputline()<0){
144 eof = TRUE;
145 return -1;
147 return *linep++;
150 int
151 nextc(void)
153 if(*linep == 0)
154 return -1;
155 return *linep;
158 void
159 ungetch(void)
161 if(--linep < line)
162 panic("ungetch");
165 Posn
166 getnum(int signok)
168 Posn n=0;
169 int c, sign;
171 sign = 1;
172 if(signok>1 && nextc()=='-'){
173 sign = -1;
174 getch();
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;
184 int
185 skipbl(void)
187 int c;
188 do
189 c = getch();
190 while(c==' ' || c=='\t');
191 if(c >= 0)
192 ungetch();
193 return c;
196 void
197 termcommand(void)
199 Posn p;
201 for(p=cmdpt; p<cmd->b.nc; p++){
202 if(terminp >= &termline[BLOCKSIZE]){
203 cmdpt = cmd->b.nc;
204 error(Etoolong);
206 *terminp++ = filereadc(cmd, p);
208 cmdpt = cmd->b.nc;
211 void
212 cmdloop(void)
214 Cmd *cmdp;
215 File *ocurfile;
216 int loaded;
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");
226 break;
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;
244 Cmd *
245 newcmd(void){
246 Cmd *p;
248 p = emalloc(sizeof(Cmd));
249 inslist(&cmdlist, cmdlist.nused, (long)p);
250 return p;
253 Addr*
254 newaddr(void)
256 Addr *p;
258 p = emalloc(sizeof(Addr));
259 inslist(&addrlist, addrlist.nused, (long)p);
260 return p;
263 String*
264 newre(void)
266 String *p;
268 p = emalloc(sizeof(String));
269 inslist(&relist, relist.nused, (long)p);
270 Strinit(p);
271 return p;
274 String*
275 newstring(void)
277 String *p;
279 p = emalloc(sizeof(String));
280 inslist(&stringlist, stringlist.nused, (long)p);
281 Strinit(p);
282 return p;
285 void
286 freecmd(void)
288 int i;
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]);
299 while(stringlist.nused>0){
300 i = --stringlist.nused;
301 Strclose(stringlist.stringpptr[i]);
302 free(stringlist.stringpptr[i]);
306 int
307 lookup(int c)
309 int i;
311 for(i=0; cmdtab[i].cmdc; i++)
312 if(cmdtab[i].cmdc == c)
313 return i;
314 return -1;
317 void
318 okdelim(int c)
320 if(c=='\\' || ('a'<=c && c<='z')
321 || ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
322 error_c(Edelim, c);
325 void
326 atnl(void)
328 skipbl();
329 if(getch() != '\n')
330 error(Enewline);
333 void
334 getrhs(String *s, int delim, int cmd)
336 int c;
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, '\\');
350 Straddc(s, c);
352 ungetch(); /* let client read whether delimeter, '\n' or whatever */
355 String *
356 collecttoken(char *end)
358 String *s = newstring();
359 int c;
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;
371 String *
372 collecttext(void)
374 String *s = newstring();
375 int begline, i, c, delim;
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();
396 Return:
397 Straddc(s, 0); /* JUST FOR CMDPRINT() */
398 return s;
401 Cmd *
402 parsecmd(int nest)
404 int i, c;
405 struct cmdtab *ct;
406 Cmd *cp, *ncp;
407 Cmd cmd;
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;
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();
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);
490 Return:
491 cp = newcmd();
492 *cp = cmd;
493 return cp;
496 String* /* BUGGERED */
497 getregexp(int delim)
499 String *r = newre();
500 int c;
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();
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');
519 if(lastpat.n <= 1)
520 error(Epattern);
521 Strduplstr(r, &lastpat);
522 return r;
525 Addr *
526 simpleaddr(void)
528 Addr addr;
529 Addr *ap, *nap;
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;
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;
580 break;
581 case '+':
582 case '-':
583 break;
584 default:
585 panic("simpleaddr");
587 ap = newaddr();
588 *ap = addr;
589 return ap;
592 Addr *
593 compoundaddr(void)
595 Addr addr;
596 Addr *ap, *next;
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;