Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "sam.h"

Header  h;
uchar   indata[DATASIZE];
uchar   outdata[2*DATASIZE+3];  /* room for overflow message */
uchar   *inp;
uchar   *outp;
uchar   *outmsg = outdata;
Posn    cmdpt;
Posn    cmdptadv;
Buffer  snarfbuf;
int     waitack;
int     outbuffered;
int     tversion;

int     inshort(void);
long    inlong(void);
vlong   invlong(void);
int     inmesg(Tmesg);

void    outshort(int);
void    outlong(long);
void    outvlong(vlong);
void    outcopy(int, void*);
void    outsend(void);
void    outstart(Hmesg);

void    setgenstr(File*, Posn, Posn);

#ifdef DEBUG
char *hname[] = {
        [Hversion]      "Hversion",
        [Hbindname]     "Hbindname",
        [Hcurrent]      "Hcurrent",
        [Hnewname]      "Hnewname",
        [Hmovname]      "Hmovname",
        [Hgrow]         "Hgrow",
        [Hcheck0]       "Hcheck0",
        [Hcheck]        "Hcheck",
        [Hunlock]       "Hunlock",
        [Hdata]         "Hdata",
        [Horigin]       "Horigin",
        [Hunlockfile]   "Hunlockfile",
        [Hsetdot]       "Hsetdot",
        [Hgrowdata]     "Hgrowdata",
        [Hmoveto]       "Hmoveto",
        [Hclean]        "Hclean",
        [Hdirty]        "Hdirty",
        [Hcut]          "Hcut",
        [Hsetpat]       "Hsetpat",
        [Hdelname]      "Hdelname",
        [Hclose]        "Hclose",
        [Hsetsnarf]     "Hsetsnarf",
        [Hsnarflen]     "Hsnarflen",
        [Hack]          "Hack",
        [Hexit]         "Hexit",
        [Hplumb]                "Hplumb",
};

char *tname[] = {
        [Tversion]      "Tversion",
        [Tstartcmdfile] "Tstartcmdfile",
        [Tcheck]        "Tcheck",
        [Trequest]      "Trequest",
        [Torigin]       "Torigin",
        [Tstartfile]    "Tstartfile",
        [Tworkfile]     "Tworkfile",
        [Ttype]         "Ttype",
        [Tcut]          "Tcut",
        [Tpaste]        "Tpaste",
        [Tsnarf]        "Tsnarf",
        [Tstartnewfile] "Tstartnewfile",
        [Twrite]        "Twrite",
        [Tclose]        "Tclose",
        [Tlook]         "Tlook",
        [Tsearch]       "Tsearch",
        [Tsend]         "Tsend",
        [Tdclick]       "Tdclick",
        [Tstartsnarf]   "Tstartsnarf",
        [Tsetsnarf]     "Tsetsnarf",
        [Tack]          "Tack",
        [Texit]         "Texit",
        [Tplumb]                "Tplumb",
};

void
journal(int out, char *s)
{
        static int fd = 0;

        if(fd <= 0)
                fd = create("/tmp/sam.out", 1, 0666L);
        fprint(fd, "%s%s\n", out? "out: " : "in:  ", s);
}

void
journaln(int out, long n)
{
        char buf[32];

        snprint(buf, sizeof(buf), "%ld", n);
        journal(out, buf);
}

void
journalv(int out, vlong v)
{
        char buf[32];

        sprint(buf, sizeof(buf), "%lld", v);
        journal(out, buf);
}
#else
#define journal(a, b)
#define journaln(a, b)
#define journalv(a, b)
#endif

int
rcvchar(void){
        static uchar buf[64];
        static i, nleft = 0;

        if(nleft <= 0){
                nleft = read(0, (char *)buf, sizeof buf);
                if(nleft <= 0)
                        return -1;
                i = 0;
        }
        --nleft;
        return buf[i++];
}

int
rcv(void){
        int c;
        static state = 0;
        static count = 0;
        static i = 0;

        while((c=rcvchar()) != -1)
                switch(state){
                case 0:
                        h.type = c;
                        state++;
                        break;

                case 1:
                        h.count0 = c;
                        state++;
                        break;

                case 2:
                        h.count1 = c;
                        count = h.count0|(h.count1<<8);
                        i = 0;
                        if(count > DATASIZE)
                                panic("count>DATASIZE");
                        if(count == 0)
                                goto zerocount;
                        state++;
                        break;

                case 3:
                        indata[i++] = c;
                        if(i == count){
                zerocount:
                                indata[i] = 0;
                                state = count = 0;
                                return inmesg(h.type);
                        }
                        break;
                }
        return 0;
}

File *
whichfile(int tag)
{
        int i;

        for(i = 0; i<file.nused; i++)
                if(file.filepptr[i]->tag==tag)
                        return file.filepptr[i];
        hiccough((char *)0);
        return 0;
}

int
inmesg(Tmesg type)
{
        Rune buf[1025];
        char cbuf[64];
        int i, m;
        short s;
        long l, l1;
        vlong v;
        File *f;
        Posn p0, p1, p;
        Range r;
        String *str;
        char *c, *wdir;
        Rune *rp;
        Plumbmsg *pm;

        if(type > TMAX)
                panic("inmesg");

        journal(0, tname[type]);

        inp = indata;
        switch(type){
        case -1:
                panic("rcv error");

        default:
                fprint(2, "unknown type %d\n", type);
                panic("rcv unknown");

        case Tversion:
                tversion = inshort();
                journaln(0, tversion);
                break;

        case Tstartcmdfile:
                v = invlong();          /* for 64-bit pointers */
                journalv(0, v);
                Strdupl(&genstr, samname);
                cmd = newfile();
                cmd->unread = 0;
                outTsv(Hbindname, cmd->tag, v);
                outTs(Hcurrent, cmd->tag);
                logsetname(cmd, &genstr);
                cmd->rasp = listalloc('P');
                cmd->mod = 0;
                if(cmdstr.n){
                        loginsert(cmd, 0L, cmdstr.s, cmdstr.n);
                        Strdelete(&cmdstr, 0L, (Posn)cmdstr.n);
                }
                fileupdate(cmd, FALSE, TRUE);
                outT0(Hunlock);
                break;

        case Tcheck:
                /* go through whichfile to check the tag */
                outTs(Hcheck, whichfile(inshort())->tag);
                break;

        case Trequest:
                f = whichfile(inshort());
                p0 = inlong();
                p1 = p0+inshort();
                journaln(0, p0);
                journaln(0, p1-p0);
                if(f->unread)
                        panic("Trequest: unread");
                if(p1>f->nc)
                        p1 = f->nc;
                if(p0>f->nc) /* can happen e.g. scrolling during command */
                        p0 = f->nc;
                if(p0 == p1){
                        i = 0;
                        r.p1 = r.p2 = p0;
                }else{
                        r = rdata(f->rasp, p0, p1-p0);
                        i = r.p2-r.p1;
                        bufread(f, r.p1, buf, i);
                }
                buf[i]=0;
                outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1));
                break;

        case Torigin:
                s = inshort();
                l = inlong();
                l1 = inlong();
                journaln(0, l1);
                lookorigin(whichfile(s), l, l1);
                break;

        case Tstartfile:
                termlocked++;
                f = whichfile(inshort());
                if(!f->rasp)    /* this might be a duplicate message */
                        f->rasp = listalloc('P');
                current(f);
                outTsv(Hbindname, f->tag, invlong());   /* for 64-bit pointers */
                outTs(Hcurrent, f->tag);
                journaln(0, f->tag);
                if(f->unread)
                        load(f);
                else{
                        if(f->nc>0){
                                rgrow(f->rasp, 0L, f->nc);
                                outTsll(Hgrow, f->tag, 0L, f->nc);
                        }
                        outTs(Hcheck0, f->tag);
                        moveto(f, f->dot.r);
                }
                break;

        case Tworkfile:
                i = inshort();
                f = whichfile(i);
                current(f);
                f->dot.r.p1 = inlong();
                f->dot.r.p2 = inlong();
                f->tdot = f->dot.r;
                journaln(0, i);
                journaln(0, f->dot.r.p1);
                journaln(0, f->dot.r.p2);
                break;

        case Ttype:
                f = whichfile(inshort());
                p0 = inlong();
                journaln(0, p0);
                journal(0, (char*)inp);
                str = tmpcstr((char*)inp);
                i = str->n;
                loginsert(f, p0, str->s, str->n);
                if(fileupdate(f, FALSE, FALSE))
                        seq++;
                if(f==cmd && p0==f->nc-i && i>0 && str->s[i-1]=='\n'){
                        freetmpstr(str);
                        termlocked++;
                        termcommand();
                }else
                        freetmpstr(str);
                f->dot.r.p1 = f->dot.r.p2 = p0+i; /* terminal knows this already */
                f->tdot = f->dot.r;
                break;

        case Tcut:
                f = whichfile(inshort());
                p0 = inlong();
                p1 = inlong();
                journaln(0, p0);
                journaln(0, p1);
                logdelete(f, p0, p1);
                if(fileupdate(f, FALSE, FALSE))
                        seq++;
                f->dot.r.p1 = f->dot.r.p2 = p0;
                f->tdot = f->dot.r;   /* terminal knows the value of dot already */
                break;

        case Tpaste:
                f = whichfile(inshort());
                p0 = inlong();
                journaln(0, p0);
                for(l=0; l<snarfbuf.nc; l+=m){
                        m = snarfbuf.nc-l;
                        if(m>BLOCKSIZE)
                                m = BLOCKSIZE;
                        bufread(&snarfbuf, l, genbuf, m);
                        loginsert(f, p0, tmprstr(genbuf, m)->s, m);
                }
                if(fileupdate(f, FALSE, TRUE))
                        seq++;
                f->dot.r.p1 = p0;
                f->dot.r.p2 = p0+snarfbuf.nc;
                f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */
                telldot(f);
                outTs(Hunlockfile, f->tag);
                break;

        case Tsnarf:
                i = inshort();
                p0 = inlong();
                p1 = inlong();
                snarf(whichfile(i), p0, p1, &snarfbuf, 0);
                break;

        case Tstartnewfile:
                v = invlong();
                Strdupl(&genstr, empty);
                f = newfile();
                f->rasp = listalloc('P');
                outTsv(Hbindname, f->tag, v);
                logsetname(f, &genstr);
                outTs(Hcurrent, f->tag);
                current(f);
                load(f);
                break;

        case Twrite:
                termlocked++;
                i = inshort();
                journaln(0, i);
                f = whichfile(i);
                addr.r.p1 = 0;
                addr.r.p2 = f->nc;
                if(f->name.s[0] == 0)
                        error(Enoname);
                Strduplstr(&genstr, &f->name);
                writef(f);
                break;

        case Tclose:
                termlocked++;
                i = inshort();
                journaln(0, i);
                f = whichfile(i);
                current(f);
                trytoclose(f);
                /* if trytoclose fails, will error out */
                delete(f);
                break;

        case Tlook:
                f = whichfile(inshort());
                termlocked++;
                p0 = inlong();
                p1 = inlong();
                journaln(0, p0);
                journaln(0, p1);
                setgenstr(f, p0, p1);
                for(l = 0; l<genstr.n; l++){
                        i = genstr.s[l];
                        if(utfrune(".*+?(|)\\[]^$", i)){
                                str = tmpcstr("\\");
                                Strinsert(&genstr, str, l++);
                                freetmpstr(str);
                        }
                }
                Straddc(&genstr, '\0');
                nextmatch(f, &genstr, p1, 1);
                moveto(f, sel.p[0]);
                break;

        case Tsearch:
                termlocked++;
                if(curfile == 0)
                        error(Enofile);
                if(lastpat.s[0] == 0)
                        panic("Tsearch");
                nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1);
                moveto(curfile, sel.p[0]);
                break;

        case Tsend:
                termlocked++;
                inshort();      /* ignored */
                p0 = inlong();
                p1 = inlong();
                setgenstr(cmd, p0, p1);
                bufreset(&snarfbuf);
                bufinsert(&snarfbuf, (Posn)0, genstr.s, genstr.n);
                outTl(Hsnarflen, genstr.n);
                if(genstr.s[genstr.n-1] != '\n')
                        Straddc(&genstr, '\n');
                loginsert(cmd, cmd->nc, genstr.s, genstr.n);
                fileupdate(cmd, FALSE, TRUE);
                cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nc;
                telldot(cmd);
                termcommand();
                break;

        case Tdclick:
                f = whichfile(inshort());
                p1 = inlong();
                doubleclick(f, p1);
                f->tdot.p1 = f->tdot.p2 = p1;
                telldot(f);
                outTs(Hunlockfile, f->tag);
                break;

        case Tstartsnarf:
                if (snarfbuf.nc <= 0) { /* nothing to export */
                        outTs(Hsetsnarf, 0);
                        break;
                }
                c = 0;
                i = 0;
                m = snarfbuf.nc;
                if(m > SNARFSIZE) {
                        m = SNARFSIZE;
                        dprint("?warning: snarf buffer truncated\n");
                }
                rp = malloc(m*sizeof(Rune));
                if(rp){
                        bufread(&snarfbuf, 0, rp, m);
                        c = Strtoc(tmprstr(rp, m));
                        free(rp);
                        i = strlen(c);
                }
                outTs(Hsetsnarf, i);
                if(c){
                        Write(1, c, i);
                        free(c);
                } else
                        dprint("snarf buffer too long\n");
                break;

        case Tsetsnarf:
                m = inshort();
                if(m > SNARFSIZE)
                        error(Etoolong);
                c = malloc(m+1);
                if(c){
                        for(i=0; i<m; i++)
                                c[i] = rcvchar();
                        c[m] = 0;
                        str = tmpcstr(c);
                        free(c);
                        bufreset(&snarfbuf);
                        bufinsert(&snarfbuf, (Posn)0, str->s, str->n);
                        freetmpstr(str);
                        outT0(Hunlock);
                }
                break;

        case Tack:
                waitack = 0;
                break;

        case Tplumb:
                f = whichfile(inshort());
                p0 = inlong();
                p1 = inlong();
                pm = emalloc(sizeof(Plumbmsg));
                pm->src = strdup("sam");
                pm->dst = 0;
                /* construct current directory */
                c = Strtoc(&f->name);
                if(c[0] == '/')
                        pm->wdir = c;
                else{
                        wdir = emalloc(1024);
                        getwd(wdir, 1024);
                        pm->wdir = emalloc(1024);
                        snprint(pm->wdir, 1024, "%s/%s", wdir, c);
                        cleanname(pm->wdir);
                        free(wdir);
                        free(c);
                }
                c = strrchr(pm->wdir, '/');
                if(c)
                        *c = '\0';
                pm->type = strdup("text");
                if(p1 > p0)
                        pm->attr = nil;
                else{
                        p = p0;
                        while(p0>0 && (i=filereadc(f, p0 - 1))!=' ' && i!='\t' && i!='\n')
                                p0--;
                        while(p1<f->nc && (i=filereadc(f, p1))!=' ' && i!='\t' && i!='\n')
                                p1++;
                        sprint(cbuf, "click=%ld", p-p0);
                        pm->attr = plumbunpackattr(cbuf);
                }
                if(p0==p1 || p1-p0>=BLOCKSIZE){
                        plumbfree(pm);
                        break;
                }
                setgenstr(f, p0, p1);
                pm->data = Strtoc(&genstr);
                pm->ndata = strlen(pm->data);
                c = plumbpack(pm, &i);
                if(c != 0){
                        outTs(Hplumb, i);
                        Write(1, c, i);
                        free(c);
                }
                plumbfree(pm);
                break;

        case Texit:
                exits(0);
        }
        return TRUE;
}

void
snarf(File *f, Posn p1, Posn p2, Buffer *buf, int emptyok)
{
        Posn l;
        int i;

        if(!emptyok && p1==p2)
                return;
        bufreset(buf);
        /* Stage through genbuf to avoid compaction problems (vestigial) */
        if(p2 > f->nc){
                fprint(2, "bad snarf addr p1=%ld p2=%ld f->nc=%d\n", p1, p2, f->nc); /*ZZZ should never happen, can remove */
                p2 = f->nc;
        }
        for(l=p1; l<p2; l+=i){
                i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l;
                bufread(f, l, genbuf, i);
                bufinsert(buf, buf->nc, tmprstr(genbuf, i)->s, i);
        }
}

int
inshort(void)
{
        ushort n;

        n = inp[0] | (inp[1]<<8);
        inp += 2;
        return n;
}

long
inlong(void)
{
        ulong n;

        n = inp[0] | (inp[1]<<8) | (inp[2]<<16) | (inp[3]<<24);
        inp += 4;
        return n;
}

vlong
invlong(void)
{
        vlong v;
        
        v = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4];
        v = (v<<16) | (inp[3]<<8) | inp[2];
        v = (v<<16) | (inp[1]<<8) | inp[0];
        inp += 8;
        return v;
}

void
setgenstr(File *f, Posn p0, Posn p1)
{
        if(p0 != p1){
                if(p1-p0 >= TBLOCKSIZE)
                        error(Etoolong);
                Strinsure(&genstr, p1-p0);
                bufread(f, p0, genbuf, p1-p0);
                memmove(genstr.s, genbuf, RUNESIZE*(p1-p0));
                genstr.n = p1-p0;
        }else{
                if(snarfbuf.nc == 0)
                        error(Eempty);
                if(snarfbuf.nc > TBLOCKSIZE)
                        error(Etoolong);
                bufread(&snarfbuf, (Posn)0, genbuf, snarfbuf.nc);
                Strinsure(&genstr, snarfbuf.nc);
                memmove(genstr.s, genbuf, RUNESIZE*snarfbuf.nc);
                genstr.n = snarfbuf.nc;
        }
}

void
outT0(Hmesg type)
{
        outstart(type);
        outsend();
}

void
outTl(Hmesg type, long l)
{
        outstart(type);
        outlong(l);
        outsend();
}

void
outTs(Hmesg type, int s)
{
        outstart(type);
        journaln(1, s);
        outshort(s);
        outsend();
}

void
outS(String *s)
{
        char *c;
        int i;

        c = Strtoc(s);
        i = strlen(c);
        outcopy(i, c);
        if(i > 99)
                c[99] = 0;
        journaln(1, i);
        journal(1, c);
        free(c);
}

void
outTsS(Hmesg type, int s1, String *s)
{
        outstart(type);
        outshort(s1);
        outS(s);
        outsend();
}

void
outTslS(Hmesg type, int s1, Posn l1, String *s)
{
        outstart(type);
        outshort(s1);
        journaln(1, s1);
        outlong(l1);
        journaln(1, l1);
        outS(s);
        outsend();
}

void
outTS(Hmesg type, String *s)
{
        outstart(type);
        outS(s);
        outsend();
}

void
outTsllS(Hmesg type, int s1, Posn l1, Posn l2, String *s)
{
        outstart(type);
        outshort(s1);
        outlong(l1);
        outlong(l2);
        journaln(1, l1);
        journaln(1, l2);
        outS(s);
        outsend();
}

void
outTsll(Hmesg type, int s, Posn l1, Posn l2)
{
        outstart(type);
        outshort(s);
        outlong(l1);
        outlong(l2);
        journaln(1, l1);
        journaln(1, l2);
        outsend();
}

void
outTsl(Hmesg type, int s, Posn l)
{
        outstart(type);
        outshort(s);
        outlong(l);
        journaln(1, l);
        outsend();
}

void
outTsv(Hmesg type, int s, vlong v)
{
        outstart(type);
        outshort(s);
        outvlong(v);
        journalv(1, v);
        outsend();
}

void
outstart(Hmesg type)
{
        journal(1, hname[type]);
        outmsg[0] = type;
        outp = outmsg+3;
}

void
outcopy(int count, void *data)
{
        memmove(outp, data, count);
        outp += count;
}

void
outshort(int s)
{
        *outp++ = s;
        *outp++ = s>>8; 
}

void
outlong(long l)
{
        *outp++ = l;
        *outp++ = l>>8;
        *outp++ = l>>16;
        *outp++ = l>>24;
}

void
outvlong(vlong v)
{
        int i;

        for(i = 0; i < 8; i++){
                *outp++ = v;
                v >>= 8;
        }
}

void
outsend(void)
{
        int outcount;

        if(outp >= outdata+nelem(outdata))
                panic("outsend");
        outcount = outp-outmsg;
        outcount -= 3;
        outmsg[1] = outcount;
        outmsg[2] = outcount>>8;
        outmsg = outp;
        if(!outbuffered){
                outcount = outmsg-outdata;
                if (write(1, (char*) outdata, outcount) != outcount)
                        rescue();
                outmsg = outdata;
                return;
        }
}

int
needoutflush(void)
{
        return outmsg >= outdata+DATASIZE;
}

void
outflush(void)
{
        if(outmsg == outdata)
                return;
        outbuffered = 0;
        /* flow control */
        outT0(Hack);
        waitack = 1;
        do
                if(rcv() == 0){
                        rescue();
                        exits("eof");
                }
        while(waitack);
        outmsg = outdata;
        outbuffered = 1;
}