Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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'<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;
}