void doubleclick(File *f, Posn p1) { int c, i; Rune *r, *l; Posn p; if(p1 > f->nc) return; f->dot.r.p1 = f->dot.r.p2 = p1; for(i=0; left[i]; i++){ l = left[i]; r = right[i]; /* try left match */ p = p1; if(p1 == 0) c = '\n'; else c = filereadc(f, p - 1); if(strrune(l, c)){ if(clickmatch(f, c, r[strrune(l, c)-l], 1, &p)){ f->dot.r.p1 = p1; f->dot.r.p2 = p-(c!='\n'); } return; } /* try right match */ p = p1; if(p1 == f->nc) c = '\n'; else c = filereadc(f, p); if(strrune(r, c)){ if(clickmatch(f, c, l[strrune(r, c)-r], -1, &p)){ f->dot.r.p1 = p; if(c!='\n' || p!=0 || filereadc(f, 0)=='\n') f->dot.r.p1++; f->dot.r.p2 = p1+(p1<f->nc && c=='\n'); } return; } } /* try filling out word to right */ p = p1; while(p < f->nc && alnum(filereadc(f, p++))) f->dot.r.p2++; /* try filling out word to left */ p = p1; while(--p >= 0 && alnum(filereadc(f, p))) f->dot.r.p1--; }
void lookorigin(File *f, Posn p0, Posn ls) { int nl, nc, c; Posn p, oldp0; if(p0 > f->nc) p0 = f->nc; oldp0 = p0; p = p0; for(nl=nc=c=0; c!=-1 && nl<ls && nc<ls*CHARSHIFT; nc++) if((c=filereadc(f, --p)) == '\n'){ nl++; oldp0 = p0-nc; } if(c == -1) p0 = 0; else if(nl==0){ if(p0>=CHARSHIFT/2) p0-=CHARSHIFT/2; else p0 = 0; }else p0 = oldp0; outTsl(Horigin, f->tag, p0); }
void writef(File *f) { Posn n; char *name; int i, samename, newfile; ulong dev; uvlong qid; long mtime, appendonly, length; newfile = 0; samename = Strcmp(&genstr, &f->name) == 0; name = Strtoc(&f->name); i = statfile(name, &dev, &qid, &mtime, 0, 0); if(i == -1) newfile++; else if(samename && (f->dev!=dev || f->qidpath!=qid || f->mtime<mtime)){ f->dev = dev; f->qidpath = qid; f->mtime = mtime; warn_S(Wdate, &genstr); free(name); return; } if(genc) free(genc); genc = Strtoc(&genstr); if((io=create(genc, 1, 0666L)) < 0) error_r(Ecreate, genc); dprint("%s: ", genc); if(statfd(io, 0, 0, 0, &length, &appendonly) > 0 && appendonly && length>0){ free(name); error(Eappend); } n = writeio(f); if(f->name.s[0]==0 || samename){ if(addr.r.p1==0 && addr.r.p2==f->b.nc) f->cleanseq = f->seq; state(f, f->cleanseq==f->seq? Clean : Dirty); } if(newfile) dprint("(new file) "); if(addr.r.p2>0 && filereadc(f, addr.r.p2-1)!='\n') warn(Wnotnewline); closeio(n); if(f->name.s[0]==0 || samename){ if(statfile(name, &dev, &qid, &mtime, 0, 0) > 0){ f->dev = dev; f->qidpath = qid; f->mtime = mtime; checkqid(f); } } free(name); }
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; }
int clickmatch(File *f, int cl, int cr, int dir, Posn *p) { int c; int nest = 1; for(;;){ if(dir > 0){ if(*p >= f->nc) break; c = filereadc(f, (*p)++); }else{ if(*p == 0) break; c = filereadc(f, --(*p)); } if(c == cr){ if(--nest==0) return 1; }else if(c == cl) nest++; } return cl=='\n' && nest==1; }
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 */ journaln(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->b.nc) p1 = f->b.nc; if(p0>f->b.nc) /* can happen e.g. scrolling during command */ p0 = f->b.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->b, 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->b.nc>0){ rgrow(f->rasp, 0L, f->b.nc); outTsll(Hgrow, f->tag, 0L, f->b.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->b.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->b.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->b.nc, genstr.s, genstr.n); fileupdate(cmd, FALSE, TRUE); cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.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->b.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; }
Address lineaddr(Posn l, Address addr, int sign) { int n; int c; File *f = addr.f; Address a; Posn p; a.f = f; if(sign >= 0){ if(l == 0){ if(sign==0 || addr.r.p2==0){ a.r.p1 = a.r.p2 = 0; return a; } a.r.p1 = addr.r.p2; p = addr.r.p2-1; }else{ if(sign==0 || addr.r.p2==0){ p = (Posn)0; n = 1; }else{ p = addr.r.p2-1; n = filereadc(f, p++)=='\n'; } while(n < l){ if(p >= f->Buffer.nc) error(Erange); if(filereadc(f, p++) == '\n') n++; } a.r.p1 = p; } while(p < f->Buffer.nc && filereadc(f, p++)!='\n') ; a.r.p2 = p; }else{ p = addr.r.p1; if(l == 0) a.r.p2 = addr.r.p1; else{ for(n = 0; n<l; ){ /* always runs once */ if(p == 0){ if(++n != l) error(Erange); }else{ c = filereadc(f, p-1); if(c != '\n' || ++n != l) p--; } } a.r.p2 = p; if(p > 0) p--; } while(p > 0 && filereadc(f, p-1)!='\n') /* lines start after a newline */ p--; a.r.p1 = p; } return a; }