#include "sam.h" #include "parse.h" static char linex[]="\n"; static char wordx[]=" \t\n"; 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'= 0) ungetch(); return c; } void termcommand(void) { Posn p; for(p=cmdpt; pnc; p++){ if(terminp >= termline+nelem(termline)){ cmdpt = cmd->nc; error(Etoolong); } *terminp++ = filereadc(cmd, p); } cmdpt = cmd->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, p); return p; } Addr* newaddr(void) { Addr *p; p = emalloc(sizeof(Addr)); inslist(&addrlist, addrlist.nused, p); return p; } String* newre(void) { String *p; p = emalloc(sizeof(String)); inslist(&relist, relist.nused, p); Strinit(p); return p; } String* newstring(void) { String *p; p = emalloc(sizeof(String)); inslist(&stringlist, stringlist.nused, 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; 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; 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; }