| #include "sam.h" |
| #include "parse.h" |
| |
| static char linex[]="\n"; |
| static char wordx[]=" \t\n"; |
| struct cmdtab cmdtab[]={ |
| /* cmdc text regexp addr defcmd defaddr count token fn */ |
| '\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd, |
| 'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd, |
| 'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd, |
| 'B', 0, 0, 0, 0, aNo, 0, linex, b_cmd, |
| 'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd, |
| 'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd, |
| 'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd, |
| 'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd, |
| 'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd, |
| 'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, |
| 'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd, |
| 'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd, |
| 'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd, |
| 'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd, |
| 'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd, |
| 'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd, |
| 'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd, |
| 's', 0, 1, 0, 0, aDot, 1, 0, s_cmd, |
| 't', 0, 0, 1, 0, aDot, 0, 0, m_cmd, |
| 'u', 0, 0, 0, 0, aNo, 2, 0, u_cmd, |
| 'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, |
| 'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd, |
| 'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, |
| 'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, |
| 'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, |
| 'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, |
| '!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd, |
| '>', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, |
| '<', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, |
| '|', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, |
| '=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd, |
| 'c'|0x100,0, 0, 0, 0, aNo, 0, wordx, cd_cmd, |
| 0, 0, 0, 0, 0, 0, 0, 0 |
| }; |
| Cmd *parsecmd(int); |
| Addr *compoundaddr(void); |
| Addr *simpleaddr(void); |
| void freecmd(void); |
| void okdelim(int); |
| |
| Rune line[BLOCKSIZE]; |
| Rune termline[BLOCKSIZE]; |
| Rune *linep = line; |
| Rune *terminp = termline; |
| Rune *termoutp = termline; |
| |
| List cmdlist = { 'p' }; |
| List addrlist = { 'p' }; |
| List relist = { 'p' }; |
| List stringlist = { 'p' }; |
| |
| int eof; |
| |
| void |
| resetcmd(void) |
| { |
| linep = line; |
| *linep = 0; |
| terminp = termoutp = termline; |
| freecmd(); |
| } |
| |
| int |
| inputc(void) |
| { |
| int n, nbuf; |
| char buf[UTFmax]; |
| Rune r; |
| |
| Again: |
| nbuf = 0; |
| if(downloaded){ |
| while(termoutp == terminp){ |
| cmdupdate(); |
| if(patset) |
| tellpat(); |
| while(termlocked > 0){ |
| outT0(Hunlock); |
| termlocked--; |
| } |
| if(rcv() == 0) |
| return -1; |
| } |
| r = *termoutp++; |
| if(termoutp == terminp) |
| terminp = termoutp = termline; |
| }else{ |
| do{ |
| n = read(0, buf+nbuf, 1); |
| if(n <= 0) |
| return -1; |
| nbuf += n; |
| }while(!fullrune(buf, nbuf)); |
| chartorune(&r, buf); |
| } |
| if(r == 0){ |
| warn(Wnulls); |
| goto Again; |
| } |
| return r; |
| } |
| |
| int |
| inputline(void) |
| { |
| int i, c, start; |
| |
| /* |
| * Could set linep = line and i = 0 here and just |
| * error(Etoolong) below, but this way we keep |
| * old input buffer history around for a while. |
| * This is useful only for debugging. |
| */ |
| i = linep - line; |
| do{ |
| if((c = inputc())<=0) |
| return -1; |
| if(i == nelem(line)-1){ |
| if(linep == line) |
| error(Etoolong); |
| start = linep - line; |
| runemove(line, linep, i-start); |
| i -= start; |
| linep = line; |
| } |
| }while((line[i++]=c) != '\n'); |
| line[i] = 0; |
| return 1; |
| } |
| |
| int |
| getch(void) |
| { |
| if(eof) |
| return -1; |
| if(*linep==0 && inputline()<0){ |
| eof = TRUE; |
| return -1; |
| } |
| return *linep++; |
| } |
| |
| int |
| nextc(void) |
| { |
| if(*linep == 0) |
| return -1; |
| return *linep; |
| } |
| |
| void |
| ungetch(void) |
| { |
| if(--linep < line) |
| panic("ungetch"); |
| } |
| |
| Posn |
| getnum(int signok) |
| { |
| Posn n=0; |
| int c, sign; |
| |
| sign = 1; |
| if(signok>1 && nextc()=='-'){ |
| sign = -1; |
| getch(); |
| } |
| if((c=nextc())<'0' || '9'<c) /* no number defaults to 1 */ |
| return sign; |
| while('0'<=(c=getch()) && c<='9') |
| n = n*10 + (c-'0'); |
| ungetch(); |
| return sign*n; |
| } |
| |
| int |
| skipbl(void) |
| { |
| int c; |
| do |
| c = getch(); |
| while(c==' ' || c=='\t'); |
| if(c >= 0) |
| ungetch(); |
| return c; |
| } |
| |
| void |
| termcommand(void) |
| { |
| Posn p; |
| |
| for(p=cmdpt; p<cmd->b.nc; p++){ |
| if(terminp >= &termline[BLOCKSIZE]){ |
| cmdpt = cmd->b.nc; |
| error(Etoolong); |
| } |
| *terminp++ = filereadc(cmd, p); |
| } |
| cmdpt = cmd->b.nc; |
| } |
| |
| void |
| cmdloop(void) |
| { |
| Cmd *cmdp; |
| File *ocurfile; |
| int loaded; |
| |
| for(;;){ |
| if(!downloaded && curfile && curfile->unread) |
| load(curfile); |
| if((cmdp = parsecmd(0))==0){ |
| if(downloaded){ |
| rescue(); |
| exits("eof"); |
| } |
| break; |
| } |
| ocurfile = curfile; |
| loaded = curfile && !curfile->unread; |
| if(cmdexec(curfile, cmdp) == 0) |
| break; |
| freecmd(); |
| cmdupdate(); |
| update(); |
| if(downloaded && curfile && |
| (ocurfile!=curfile || (!loaded && !curfile->unread))) |
| outTs(Hcurrent, curfile->tag); |
| /* don't allow type ahead on files that aren't bound */ |
| if(downloaded && curfile && curfile->rasp == 0) |
| terminp = termoutp; |
| } |
| } |
| |
| Cmd * |
| newcmd(void){ |
| Cmd *p; |
| |
| p = emalloc(sizeof(Cmd)); |
| inslist(&cmdlist, cmdlist.nused, (long)p); |
| return p; |
| } |
| |
| Addr* |
| newaddr(void) |
| { |
| Addr *p; |
| |
| p = emalloc(sizeof(Addr)); |
| inslist(&addrlist, addrlist.nused, (long)p); |
| return p; |
| } |
| |
| String* |
| newre(void) |
| { |
| String *p; |
| |
| p = emalloc(sizeof(String)); |
| inslist(&relist, relist.nused, (long)p); |
| Strinit(p); |
| return p; |
| } |
| |
| String* |
| newstring(void) |
| { |
| String *p; |
| |
| p = emalloc(sizeof(String)); |
| inslist(&stringlist, stringlist.nused, (long)p); |
| Strinit(p); |
| return p; |
| } |
| |
| void |
| freecmd(void) |
| { |
| int i; |
| |
| while(cmdlist.nused > 0) |
| free(cmdlist.voidpptr[--cmdlist.nused]); |
| while(addrlist.nused > 0) |
| free(addrlist.voidpptr[--addrlist.nused]); |
| while(relist.nused > 0){ |
| i = --relist.nused; |
| Strclose(relist.stringpptr[i]); |
| free(relist.stringpptr[i]); |
| } |
| while(stringlist.nused>0){ |
| i = --stringlist.nused; |
| Strclose(stringlist.stringpptr[i]); |
| free(stringlist.stringpptr[i]); |
| } |
| } |
| |
| int |
| lookup(int c) |
| { |
| int i; |
| |
| for(i=0; cmdtab[i].cmdc; i++) |
| if(cmdtab[i].cmdc == c) |
| return i; |
| return -1; |
| } |
| |
| void |
| okdelim(int c) |
| { |
| if(c=='\\' || ('a'<=c && c<='z') |
| || ('A'<=c && c<='Z') || ('0'<=c && c<='9')) |
| error_c(Edelim, c); |
| } |
| |
| void |
| atnl(void) |
| { |
| skipbl(); |
| if(getch() != '\n') |
| error(Enewline); |
| } |
| |
| void |
| getrhs(String *s, int delim, int cmd) |
| { |
| int c; |
| |
| while((c = getch())>0 && c!=delim && c!='\n'){ |
| if(c == '\\'){ |
| if((c=getch()) <= 0) |
| error(Ebadrhs); |
| if(c == '\n'){ |
| ungetch(); |
| c='\\'; |
| }else if(c == 'n') |
| c='\n'; |
| else if(c!=delim && (cmd=='s' || c!='\\')) /* s does its own */ |
| Straddc(s, '\\'); |
| } |
| Straddc(s, c); |
| } |
| ungetch(); /* let client read whether delimeter, '\n' or whatever */ |
| } |
| |
| String * |
| collecttoken(char *end) |
| { |
| String *s = newstring(); |
| int c; |
| |
| while((c=nextc())==' ' || c=='\t') |
| Straddc(s, getch()); /* blanks significant for getname() */ |
| while((c=getch())>0 && utfrune(end, c)==0) |
| Straddc(s, c); |
| Straddc(s, 0); |
| if(c != '\n') |
| atnl(); |
| return s; |
| } |
| |
| String * |
| collecttext(void) |
| { |
| String *s = newstring(); |
| int begline, i, c, delim; |
| |
| if(skipbl()=='\n'){ |
| getch(); |
| i = 0; |
| do{ |
| begline = i; |
| while((c = getch())>0 && c!='\n') |
| i++, Straddc(s, c); |
| i++, Straddc(s, '\n'); |
| if(c < 0) |
| goto Return; |
| }while(s->s[begline]!='.' || s->s[begline+1]!='\n'); |
| Strdelete(s, s->n-2, s->n); |
| }else{ |
| okdelim(delim = getch()); |
| getrhs(s, delim, 'a'); |
| if(nextc()==delim) |
| getch(); |
| atnl(); |
| } |
| Return: |
| Straddc(s, 0); /* JUST FOR CMDPRINT() */ |
| return s; |
| } |
| |
| Cmd * |
| parsecmd(int nest) |
| { |
| int i, c; |
| struct cmdtab *ct; |
| Cmd *cp, *ncp; |
| Cmd cmd; |
| |
| cmd.next = cmd.ccmd = 0; |
| cmd.re = 0; |
| cmd.flag = cmd.num = 0; |
| cmd.addr = compoundaddr(); |
| if(skipbl() == -1) |
| return 0; |
| if((c=getch())==-1) |
| return 0; |
| cmd.cmdc = c; |
| if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case */ |
| getch(); /* the 'd' */ |
| cmd.cmdc='c'|0x100; |
| } |
| i = lookup(cmd.cmdc); |
| if(i >= 0){ |
| if(cmd.cmdc == '\n') |
| goto Return; /* let nl_cmd work it all out */ |
| ct = &cmdtab[i]; |
| if(ct->defaddr==aNo && cmd.addr) |
| error(Enoaddr); |
| if(ct->count) |
| cmd.num = getnum(ct->count); |
| if(ct->regexp){ |
| /* x without pattern -> .*\n, indicated by cmd.re==0 */ |
| /* X without pattern is all files */ |
| if((ct->cmdc!='x' && ct->cmdc!='X') || |
| ((c = nextc())!=' ' && c!='\t' && c!='\n')){ |
| skipbl(); |
| if((c = getch())=='\n' || c<0) |
| error(Enopattern); |
| okdelim(c); |
| cmd.re = getregexp(c); |
| if(ct->cmdc == 's'){ |
| cmd.ctext = newstring(); |
| getrhs(cmd.ctext, c, 's'); |
| if(nextc() == c){ |
| getch(); |
| if(nextc() == 'g') |
| cmd.flag = getch(); |
| } |
| |
| } |
| } |
| } |
| if(ct->addr && (cmd.caddr=simpleaddr())==0) |
| error(Eaddress); |
| if(ct->defcmd){ |
| if(skipbl() == '\n'){ |
| getch(); |
| cmd.ccmd = newcmd(); |
| cmd.ccmd->cmdc = ct->defcmd; |
| }else if((cmd.ccmd = parsecmd(nest))==0) |
| panic("defcmd"); |
| }else if(ct->text) |
| cmd.ctext = collecttext(); |
| else if(ct->token) |
| cmd.ctext = collecttoken(ct->token); |
| else |
| atnl(); |
| }else |
| switch(cmd.cmdc){ |
| case '{': |
| cp = 0; |
| do{ |
| if(skipbl()=='\n') |
| getch(); |
| ncp = parsecmd(nest+1); |
| if(cp) |
| cp->next = ncp; |
| else |
| cmd.ccmd = ncp; |
| }while(cp = ncp); |
| break; |
| case '}': |
| atnl(); |
| if(nest==0) |
| error(Enolbrace); |
| return 0; |
| default: |
| error_c(Eunk, cmd.cmdc); |
| } |
| Return: |
| cp = newcmd(); |
| *cp = cmd; |
| return cp; |
| } |
| |
| String* /* BUGGERED */ |
| getregexp(int delim) |
| { |
| String *r = newre(); |
| int c; |
| |
| for(Strzero(&genstr); ; Straddc(&genstr, c)) |
| if((c = getch())=='\\'){ |
| if(nextc()==delim) |
| c = getch(); |
| else if(nextc()=='\\'){ |
| Straddc(&genstr, c); |
| c = getch(); |
| } |
| }else if(c==delim || c=='\n') |
| break; |
| if(c!=delim && c) |
| ungetch(); |
| if(genstr.n > 0){ |
| patset = TRUE; |
| Strduplstr(&lastpat, &genstr); |
| Straddc(&lastpat, '\0'); |
| } |
| if(lastpat.n <= 1) |
| error(Epattern); |
| Strduplstr(r, &lastpat); |
| return r; |
| } |
| |
| Addr * |
| simpleaddr(void) |
| { |
| Addr addr; |
| Addr *ap, *nap; |
| |
| addr.next = 0; |
| addr.left = 0; |
| addr.num = 0; |
| switch(skipbl()){ |
| case '#': |
| addr.type = getch(); |
| addr.num = getnum(1); |
| break; |
| case '0': case '1': case '2': case '3': case '4': |
| case '5': case '6': case '7': case '8': case '9': |
| addr.num = getnum(1); |
| addr.type='l'; |
| break; |
| case '/': case '?': case '"': |
| addr.are = getregexp(addr.type = getch()); |
| break; |
| case '.': |
| case '$': |
| case '+': |
| case '-': |
| case '\'': |
| addr.type = getch(); |
| break; |
| default: |
| return 0; |
| } |
| if(addr.next = simpleaddr()) |
| switch(addr.next->type){ |
| case '.': |
| case '$': |
| case '\'': |
| if(addr.type!='"') |
| case '"': |
| error(Eaddress); |
| break; |
| case 'l': |
| case '#': |
| if(addr.type=='"') |
| break; |
| /* fall through */ |
| case '/': |
| case '?': |
| if(addr.type!='+' && addr.type!='-'){ |
| /* insert the missing '+' */ |
| nap = newaddr(); |
| nap->type='+'; |
| nap->next = addr.next; |
| addr.next = nap; |
| } |
| break; |
| case '+': |
| case '-': |
| break; |
| default: |
| panic("simpleaddr"); |
| } |
| ap = newaddr(); |
| *ap = addr; |
| return ap; |
| } |
| |
| Addr * |
| compoundaddr(void) |
| { |
| Addr addr; |
| Addr *ap, *next; |
| |
| addr.left = simpleaddr(); |
| if((addr.type = skipbl())!=',' && addr.type!=';') |
| return addr.left; |
| getch(); |
| next = addr.next = compoundaddr(); |
| if(next && (next->type==',' || next->type==';') && next->left==0) |
| error(Eaddress); |
| ap = newaddr(); |
| *ap = addr; |
| return ap; |
| } |