#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; itag==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; lBLOCKSIZE) 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; ldot.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; is, 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(p1nc && (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; lBLOCKSIZE? 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; }