616 lines
11 KiB
C
616 lines
11 KiB
C
#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, 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(cmdbufpos > cmdbuf.nc && cmdbuf.nc > 0){
|
|
cmdbufpos = 0;
|
|
bufreset(&cmdbuf);
|
|
}
|
|
if(cmdbufpos < cmdbuf.nc && cmdbuf.nc > 0)
|
|
bufread(&cmdbuf, cmdbufpos++, &r, 1);
|
|
else 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->nc; 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;
|
|
}
|