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