blob: 12510689daf638433011cf7f481a585ad46f4024 [file] [log] [blame]
#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;
}