void fileundelete(File *f, Buffer *delta, uint p0, uint p1) { Undo u; Rune *buf; uint i, n; /* undo a deletion by inserting */ u.type = Insert; u.mod = f->mod; u.seq = f->seq; u.p0 = p0; u.n = p1-p0; buf = fbufalloc(); for(i=p0; i<p1; i+=n){ n = p1 - i; if(n > RBUFSIZE) n = RBUFSIZE; bufread(&f->b, i, buf, n); bufinsert(delta, delta->nc, buf, n); } fbuffree(buf); bufinsert(delta, delta->nc, (Rune*)&u, Undosize); }
/* * like fileunsetname, but get the data from arguments */ void logsetname(File *f, String *s) { Undo u; Buffer *delta; if(f->rescuing) return; if(f->unread){ /* This is setting initial file name */ filesetname(f, s); return; } if(f->seq < seq) filemark(f); /* undo a file name change by restoring old name */ delta = &f->epsilon; u.type = Filename; u.mod = TRUE; u.seq = f->seq; u.p0 = 0; /* unused */ u.n = s->n; if(s->n) bufinsert(delta, delta->nc, s->s, s->n); bufinsert(delta, delta->nc, (Rune*)&u, Undosize); if(!f->unread && !f->mod) state(f, Dirty); }
static void wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns) { Undo u; u.type = Insert; u.mod = mod; u.seq = seq; u.p0 = p0; u.n = ns; bufinsert(delta, delta->nc, s, ns); bufinsert(delta, delta->nc, (Rune*)&u, Undosize); }
void fileunsetname(File *f, Buffer *delta) { Undo u; /* undo a file name change by restoring old name */ u.type = Filename; u.mod = f->mod; u.seq = f->seq; u.p0 = 0; /* unused */ u.n = f->nname; if(f->nname) bufinsert(delta, delta->nc, f->name, f->nname); bufinsert(delta, delta->nc, (Rune*)&u, Undosize); }
void fileinsert(File *f, uint p0, Rune *s, uint ns) { if(p0 > f->b.nc) error("internal error: fileinsert"); if(f->seq > 0) fileuninsert(f, &f->delta, p0, ns); bufinsert(&f->b, p0, s, ns); if(ns) f->mod = TRUE; }
void fileunsetname(File *f, Buffer *delta) { String s; Undo u; /* undo a file name change by restoring old name */ u.type = Filename; u.mod = f->mod; u.seq = f->seq; u.p0 = 0; /* unused */ Strinit(&s); Strduplstr(&s, &f->name); fullname(&s); u.n = s.n; if(s.n) bufinsert(delta, delta->nc, s.s, s.n); bufinsert(delta, delta->nc, (Rune*)&u, Undosize); Strclose(&s); }
static void wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1) { Undo u; u.type = Delete; u.mod = mod; u.seq = seq; u.p0 = p0; u.n = p1 - p0; bufinsert(delta, delta->nc, (Rune*)&u, Undosize); }
void fileunsetmark(File *f, Buffer *delta, Range mark) { Undo u; u.type = Mark; u.mod = f->mod; u.seq = f->seq; u.p0 = mark.p1; u.n = mark.p2 - mark.p1; bufinsert(delta, delta->nc, (Rune*)&u, Undosize); }
void fileunsetdot(File *f, Buffer *delta, Range dot) { Undo u; u.type = Dot; u.mod = f->mod; u.seq = f->seq; u.p0 = dot.p1; u.n = dot.p2 - dot.p1; bufinsert(delta, delta->nc, (Rune*)&u, Undosize); }
static void addwarningtext(Mntdir *md, Rune *r, int nr) { Warning *warn; for(warn = warnings; warn; warn=warn->next){ if(warn->md == md){ bufinsert(&warn->buf, warn->buf.nc, r, nr); return; } } warn = emalloc(sizeof(Warning)); warn->next = warnings; warn->md = md; if(md) fsysincid(md); warnings = warn; bufinsert(&warn->buf, 0, r, nr); nbsendp(cwarn, 0); }
void fileuninsert(File *f, Buffer *delta, uint p0, uint ns) { Undo u; /* undo an insertion by deleting */ u.type = Delete; u.mod = f->mod; u.seq = f->seq; u.p0 = p0; u.n = ns; bufinsert(delta, delta->nc, (Rune*)&u, Undosize); }
uint bufload(Buffer *b, uint q0, int fd, int *nulls) { char *p; Rune *r; int l, m, n, nb, nr; uint q1; if(q0 > b->nc) panic("internal error: bufload"); p = malloc((Maxblock+UTFmax+1)*sizeof p[0]); if(p == nil) panic("bufload: malloc failed"); r = runemalloc(Maxblock); m = 0; n = 1; q1 = q0; /* * At top of loop, may have m bytes left over from * last pass, possibly representing a partial rune. */ while(n > 0){ n = read(fd, p+m, Maxblock); if(n < 0){ error(Ebufload); break; } m += n; p[m] = 0; l = m; if(n > 0) l -= UTFmax; cvttorunes(p, l, r, &nb, &nr, nulls); memmove(p, p+nb, m-nb); m -= nb; bufinsert(b, q1, r, nr); q1 += nr; } free(p); free(r); return q1-q0; }
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->b.nc){ fprint(2, "bad snarf addr p1=%ld p2=%ld f->b.nc=%d\n", p1, p2, f->b.nc); /*ZZZ should never happen, can remove */ p2 = f->b.nc; } for(l=p1; l<p2; l+=i){ i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l; bufread(&f->b, l, genbuf, i); bufinsert(buf, buf->nc, tmprstr(genbuf, i)->s, i); } }
void acmegetsnarf(void) { char *s; int nb, nr, nulls, len; Rune *r; s = getsnarf(); if(s == nil || s[0]==0){ free(s); return; } len = strlen(s); r = runemalloc(len+1); cvttorunes(s, len, r, &nb, &nr, &nulls); bufreset(&snarfbuf); bufinsert(&snarfbuf, 0, r, nr); free(r); free(s); }
int filematch(File *f, String *r) { char *c, buf[STRSIZE+100]; String *t; c = Strtoc(&f->name); sprint(buf, "%c%c%c %s\n", " '"[f->mod], "-+"[f->rasp!=0], " ."[f==curfile], c); free(c); t = tmpcstr(buf); Strduplstr(&genstr, t); freetmpstr(t); /* A little dirty... */ if(menu == 0) menu = fileopen(); bufreset(&menu->Buffer); bufinsert(&menu->Buffer, 0, genstr.s, genstr.n); compile(r); return execute(menu, 0, menu->Buffer.nc); }
void fileundo(File *f, int isundo, uint *q0p, uint *q1p) { Undo u; Rune *buf; uint i, j, n, up; uint stop; Buffer *delta, *epsilon; if(isundo){ /* undo; reverse delta onto epsilon, seq decreases */ delta = &f->delta; epsilon = &f->epsilon; stop = f->seq; }else{ /* redo; reverse epsilon onto delta, seq increases */ delta = &f->epsilon; epsilon = &f->delta; stop = 0; /* don't know yet */ } buf = fbufalloc(); while(delta->nc > 0){ up = delta->nc-Undosize; bufread(delta, up, (Rune*)&u, Undosize); if(isundo){ if(u.seq < stop){ f->seq = u.seq; goto Return; } }else{ if(stop == 0) stop = u.seq; if(u.seq > stop) goto Return; } switch(u.type){ default: fprint(2, "undo: 0x%ux\n", u.type); abort(); break; case Delete: f->seq = u.seq; fileundelete(f, epsilon, u.p0, u.p0+u.n); f->mod = u.mod; bufdelete(&f->b, u.p0, u.p0+u.n); for(j=0; j<f->ntext; j++) textdelete(f->text[j], u.p0, u.p0+u.n, FALSE); *q0p = u.p0; *q1p = u.p0; break; case Insert: f->seq = u.seq; fileuninsert(f, epsilon, u.p0, u.n); f->mod = u.mod; up -= u.n; for(i=0; i<u.n; i+=n){ n = u.n - i; if(n > RBUFSIZE) n = RBUFSIZE; bufread(delta, up+i, buf, n); bufinsert(&f->b, u.p0+i, buf, n); for(j=0; j<f->ntext; j++) textinsert(f->text[j], u.p0+i, buf, n, FALSE); } *q0p = u.p0; *q1p = u.p0+u.n; break; case Filename: f->seq = u.seq; fileunsetname(f, epsilon); f->mod = u.mod; up -= u.n; free(f->name); if(u.n == 0) f->name = nil; else f->name = runemalloc(u.n); bufread(delta, up, f->name, u.n); f->nname = u.n; break; } bufdelete(delta, up, delta->nc); } if(isundo) f->seq = 0; Return: fbuffree(buf); }
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; }
void fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag) { Undo u; Rune *buf; uint i, n, up; uint stop; Buffer *delta, *epsilon; if(isundo){ /* undo; reverse delta onto epsilon, seq decreases */ delta = &f->delta; epsilon = &f->epsilon; stop = f->seq; }else{ /* redo; reverse epsilon onto delta, seq increases */ delta = &f->epsilon; epsilon = &f->delta; stop = 0; /* don't know yet */ } raspstart(f); while(delta->nc > 0){ /* rasp and buffer are in sync; sync with wire if needed */ if(needoutflush()) raspflush(f); up = delta->nc-Undosize; bufread(delta, up, (Rune*)&u, Undosize); if(isundo){ if(u.seq < stop){ f->seq = u.seq; raspdone(f, flag); return; } }else{ if(stop == 0) stop = u.seq; if(u.seq > stop){ raspdone(f, flag); return; } } switch(u.type){ default: panic("undo unknown u.type"); break; case Delete: f->seq = u.seq; if(canredo) fileundelete(f, epsilon, u.p0, u.p0+u.n); f->mod = u.mod; bufdelete(&f->Buffer, u.p0, u.p0+u.n); raspdelete(f, u.p0, u.p0+u.n, flag); *q0p = u.p0; *q1p = u.p0; break; case Insert: f->seq = u.seq; if(canredo) fileuninsert(f, epsilon, u.p0, u.n); f->mod = u.mod; up -= u.n; buf = fbufalloc(); for(i=0; i<u.n; i+=n){ n = u.n - i; if(n > RBUFSIZE) n = RBUFSIZE; bufread(delta, up+i, buf, n); bufinsert(&f->Buffer, u.p0+i, buf, n); raspinsert(f, u.p0+i, buf, n, flag); } fbuffree(buf); *q0p = u.p0; *q1p = u.p0+u.n; break; case Filename: f->seq = u.seq; if(canredo) fileunsetname(f, epsilon); f->mod = u.mod; up -= u.n; Strinsure(&f->name, u.n+1); bufread(delta, up, f->name.s, u.n); f->name.s[u.n] = 0; f->name.n = u.n; fixname(&f->name); sortname(f); break; case Dot: f->seq = u.seq; if(canredo) fileunsetdot(f, epsilon, f->dot.r); f->mod = u.mod; f->dot.r.p1 = u.p0; f->dot.r.p2 = u.p0 + u.n; break; case Mark: f->seq = u.seq; if(canredo) fileunsetmark(f, epsilon, f->mark); f->mod = u.mod; f->mark.p1 = u.p0; f->mark.p2 = u.p0 + u.n; break; } bufdelete(delta, up, delta->nc); } if(isundo) f->seq = 0; raspdone(f, flag); }