Window* errorwin1(Rune *dir, int ndir, Rune **incl, int nincl) { Window *w; Rune *r; int i, n; r = runemalloc(ndir+8); if(n = ndir){ /* assign = */ runemove(r, dir, ndir); r[n++] = L'/'; } runemove(r+n, L"+Errors", 7); n += 7; w = lookfile(r, n); if(w == nil){ if(row.ncol == 0) if(rowadd(&row, nil, -1) == nil) error("can't create column to make error window"); w = coladd(row.col[row.ncol-1], nil, nil, -1); w->filemenu = FALSE; winsetname(w, r, n); } free(r); for(i=nincl; --i>=0; ){ n = runestrlen(incl[i]); r = runemalloc(n); runemove(r, incl[i], n); winaddincl(w, r, n); } w->autoindent = globalautoindent; return w; }
uint winsert(Window *w, Rune *r, int n, uint q0) { uint m; if(n == 0) return q0; if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){ m = min(HiWater-LoWater, min(w->org, w->qh)); w->org -= m; w->qh -= m; if(w->q0 > m) w->q0 -= m; else w->q0 = 0; if(w->q1 > m) w->q1 -= m; else w->q1 = 0; w->nr -= m; runemove(w->run, w->run+m, w->nr); q0 -= m; } if(w->nr+n > w->maxr){ /* * Minimize realloc breakage: * Allocate at least MinWater * Double allocation size each time * But don't go much above HiWater */ m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater; if(m > HiWater) m = max(HiWater+MinWater, w->nr+n); if(m > w->maxr){ w->run = runerealloc(w->run, m); w->maxr = m; } } runemove(w->run+q0+n, w->run+q0, w->nr-q0); runemove(w->run+q0, r, n); w->nr += n; /* if output touches, advance selection, not qh; works best for keyboard and output */ // TODO don't know if we might want this for displaying text. if(q0 <= w->q1) w->q1 += n; if(q0 <= w->q0) w->q0 += n; if(q0 < w->qh) w->qh += n; if(q0 < w->org) w->org += n; return q0; }
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; }
void textdelete(Text *t, uint q0, uint q1) { uint n, p0, p1; n = q1-q0; if(n == 0) return; runemove(t->rs.r+q0, t->rs.r+q1, t->rs.nr-q1); t->rs.nr -= n; if(q0 < t->q0) t->q0 -= min(n, t->q0-q0); if(q0 < t->q1) t->q1 -= min(n, t->q1-q0); if(q1 <= t->org) t->org -= n; else if(q0 < t->org+t->nchars){ p1 = q1 - t->org; if(p1 > t->nchars) p1 = t->nchars; if(q0 < t->org){ t->org = q0; p0 = 0; }else p0 = q0 - t->org; frdelete(t, p0, p1); textfill(t); } t->rs.r[t->rs.nr] = L'\0'; }
String* getregexp(int delim) { String *buf, *r; int i, c; buf = allocstring(0); for(i=0; ; i++){ if((c = getch())=='\\'){ if(nextc()==delim) c = getch(); else if(nextc()=='\\'){ Straddc(buf, c); c = getch(); } }else if(c==delim || c=='\n') break; if(i >= RBUFSIZE) editerror("regular expression too long"); Straddc(buf, c); } if(c!=delim && c) ungetch(); if(buf->n > 0){ patset = TRUE; freestring(lastpat); lastpat = buf; }else freestring(buf); if(lastpat->n == 0) editerror("no regular expression defined"); r = newstring(lastpat->n); runemove(r->r, lastpat->r, lastpat->n); /* newstring put \0 at end */ return r; }
void textfill(Text *t) { Rune *rp; int i, n, m, nl; if(t->lastlinefull) return; rp = runemalloc(BUFSIZE*8); do{ n = t->rs.nr-(t->org+t->nchars); if(n == 0) break; if(n > 2000) /* educated guess at reasonable amount */ n = 2000; runemove(rp, t->rs.r+(t->org+t->nchars), n); /* * it's expensive to frinsert more than we need, so * count newlines. */ nl = t->maxlines-t->nlines; m = 0; for(i=0; i<n; ){ if(rp[i++] == '\n'){ m++; if(m >= nl) break; } } frinsert(t, rp, rp+i, t->nchars); }while(t->lastlinefull == FALSE); free(rp); }
void copyrunestr(Runestr *a, Runestr *b) { closerunestr(a); a->r = runemalloc(b->nr+1); runemove(a->r, b->r, b->nr); a->r[b->nr] = 0; a->nr = b->nr; }
void waddraw(Window *w, Rune *r, int nr) { print_func_entry(); w->raw = runerealloc(w->raw, w->nraw+nr); runemove(w->raw+w->nraw, r, nr); w->nraw += nr; print_func_exit(); }
void textinsert(Text *t, uint q0, Rune *r, uint n) { if(n == 0) return; t->rs.r = runerealloc(t->rs.r, t->rs.nr+n); runemove(t->rs.r+q0+n, t->rs.r+q0, t->rs.nr-q0); runemove(t->rs.r+q0, r, n); t->rs.nr += n; if(q0 < t->q1) t->q1 += n; if(q0 < t->q0) t->q0 += n; if(q0 < t->org) t->org += n; else if(q0 <= t->org+t->nchars) frinsert(t, r, r+n, q0-t->org); }
void filesetname(File *f, Rune *name, int n) { if(f->seq > 0) fileunsetname(f, &f->delta); free(f->name); f->name = runemalloc(n); runemove(f->name, name, n); f->nname = n; f->unread = TRUE; }
int wctlmesg(Window *w, int m, Console *i) { print_func_entry(); switch(m){ default: error("unknown control message"); break; case Wakeup: break; case Rawon: break; case Rawoff: if(w->deleted) break; while(w->nraw > 0){ //wkeyctl(w, w->raw[0]); --w->nraw; runemove(w->raw, w->raw+1, w->nraw); } break; case Holdon: case Holdoff: if(w->deleted) break; break; case Deleted: if(w->deleted) break; write(w->notefd, "hangup", 6); proccreate(deletetimeoutproc, estrdup(w->name), 4096); wclosewin(w); break; case Exited: close(w->notefd); chanfree(w->mc.c); chanfree(w->ck); chanfree(w->cctl); chanfree(w->conswrite); chanfree(w->consread); chanfree(w->mouseread); chanfree(w->wctlread); free(w->raw); free(w->run); free(w->dir); free(w->label); free(w); break; } print_func_exit(); return m; }
Runestr includefile(Rune *dir, Rune *file, int nfile) { int m, n; char *a; Rune *r; static Rune Lslash[] = { '/', 0 }; m = runestrlen(dir); a = emalloc((m+1+nfile)*UTFmax+1); sprint(a, "%S/%.*S", dir, nfile, file); n = access(a, 0); free(a); if(n < 0) return runestr(nil, 0); r = runemalloc(m+1+nfile); runemove(r, dir, m); runemove(r+m, Lslash, 1); runemove(r+m+1, file, nfile); free(file); return cleanrname(runestr(r, m+1+nfile)); }
void bufread(Buffer *b, uint q0, Rune *s, uint n) { uint m; if(!(q0<=b->nc && q0+n<=b->nc)) panic("bufread: internal error"); while(n > 0){ setcache(b, q0); m = min(n, b->cnc-(q0-b->cq)); runemove(s, b->c+(q0-b->cq), m); q0 += m; s += m; n -= m; } }
uint textbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp) { Rune *bp, *tp, *up; int i, initial; if(t->what == Tag){ /* can't happen but safety first: mustn't backspace over file name */ Err: textinsert(t, q0, r, n, tofile); *nrp = n; return q0; } bp = r; for(i=0; i<n; i++) if(*bp++ == '\b'){ --bp; initial = 0; tp = runemalloc(n); runemove(tp, r, i); up = tp+i; for(; i<n; i++){ *up = *bp++; if(*up == '\b') if(up == tp) initial++; else --up; else up++; } if(initial){ if(initial > q0) initial = q0; q0 -= initial; textdelete(t, q0, q0+initial, tofile); } n = up-tp; textinsert(t, q0, tp, n, tofile); free(tp); *nrp = n; return q0; } goto Err; }
void editcmd(Text *ct, Rune *r, uint n) { char *err; if(n == 0) return; if(2*n > RBUFSIZE){ warning(nil, "string too long\n"); return; } allwindows(alleditinit, nil); if(cmdstartp) free(cmdstartp); cmdstartp = runemalloc(n+2); runemove(cmdstartp, r, n); if(r[n] != '\n') cmdstartp[n++] = '\n'; cmdstartp[n] = '\0'; cmdendp = cmdstartp+n; cmdp = cmdstartp; if(ct->w == nil) curtext = nil; else curtext = &ct->w->body; resetxec(); if(editerrc == nil){ editerrc = chancreate(sizeof(char*), 0); lastpat = allocstring(0); } threadcreate(editthread, nil, STACK); err = recvp(editerrc); editing = Inactive; if(err != nil){ if(err[0] != '\0') warning(nil, "Edit: %s\n", err); free(err); } /* update everyone whose edit log has data */ allwindows(allupdate, nil); }
/* * like fileundelete, but get the data from arguments */ void loginsert(File *f, uint p0, Rune *s, uint ns) { if(f->rescuing) return; if(ns == 0) return; if(ns>STRSIZE) panic("loginsert"); if(f->seq < seq) filemark(f); if(p0 < f->hiposn) error(Esequence); if(merge.f != f || p0-(merge.p0+merge.n)>Maxmerge /* too far */ || merge.nbuf+((p0+ns)-(merge.p0+merge.n))>=RBUFSIZE) /* too long */ flushmerge(); if(ns>=RBUFSIZE){ if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil)) panic("loginsert bad merge state"); wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns); }else{ if(merge.f != f){ merge.f = f; merge.p0 = p0; merge.seq = f->seq; } mergeextend(f, p0); /* append string to merge */ runemove(merge.buf+merge.nbuf, s, ns); merge.nbuf += ns; } f->hiposn = p0; if(!f->unread && !f->mod) state(f, Dirty); }
Runestr dirname(Text *t, Rune *r, int n) { Rune *b, c; uint m, nt; int slash; Runestr tmp; b = nil; if(t==nil || t->w==nil) goto Rescue; nt = t->w->tag.file->b.nc; if(nt == 0) goto Rescue; if(n>=1 && r[0]=='/') goto Rescue; b = runemalloc(nt+n+1); bufread(&t->w->tag.file->b, 0, b, nt); slash = -1; for(m=0; m<nt; m++){ c = b[m]; if(c == '/') slash = m; if(c==' ' || c=='\t') break; } if(slash < 0) goto Rescue; runemove(b+slash+1, r, n); free(r); return cleanrname(runestr(b, slash+1+n)); Rescue: free(b); tmp = runestr(r, n); if(r) return cleanrname(tmp); return tmp; }
void textsetorigin(Text *t, uint org, int exact) { int i, a, fixup; Rune *r; uint n; if(org>0 && !exact){ /* org is an estimate of the char posn; find a newline */ /* don't try harder than 256 chars */ for(i=0; i<256 && org<t->rs.nr; i++){ if(t->rs.r[org] == '\n'){ org++; break; } org++; } } a = org-t->org; fixup = 0; if(a>=0 && a<t->nchars){ frdelete(t, 0, a); fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */ }else if(a<0 && -a<t->nchars){ n = t->org - org; r = runemalloc(n); runemove(r, t->rs.r+org, n); frinsert(t, r, r+n, 0); free(r); }else frdelete(t, 0, t->nchars); t->org = org; textfill(t); textscrdraw(t); textsetselect(t, t->q0, t->q1); if(fixup && t->p1 > t->p0) frdrawsel(t, frptofchar(t, t->p1-1), t->p1-1, t->p1, 1); }
void bufdelete(Buffer *b, uint q0, uint q1) { uint m, n, off; if(!(q0<=q1 && q0<=b->nc && q1<=b->nc)) panic("internal error: bufdelete"); while(q1 > q0){ setcache(b, q0); off = q0-b->cq; if(q1 > b->cq+b->cnc) n = b->cnc - off; else n = q1-q0; m = b->cnc - (off+n); if(m > 0) runemove(b->c+off, b->c+off+n, m); b->cnc -= n; b->cdirty = TRUE; q1 -= n; b->nc -= n; } }
/* * Incoming window should be locked. * It will be unlocked and returned window * will be locked in its place. */ Window* errorwinforwin(Window *w) { int i, n, nincl, owner; Rune **incl; Runestr dir; Text *t; t = &w->body; dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } incl = nil; nincl = w->nincl; if(nincl > 0){ incl = emalloc(nincl*sizeof(Rune*)); for(i=0; i<nincl; i++){ n = runestrlen(w->incl[i]); incl[i] = runemalloc(n+1); runemove(incl[i], w->incl[i], n); } } owner = w->owner; winunlock(w); for(;;){ w = errorwin1(dir.r, dir.nr, incl, nincl); winlock(w, owner); if(w->col != nil) break; /* window deleted too fast */ winunlock(w); } return w; }
void texttype(Text *t, Rune r) { uint q0, q1; int nnb, nb, n, i; int nr; Rune *rp; Text *u; if(t->what!=Body && r=='\n') return; nr = 1; rp = &r; switch(r){ case Kleft: if(t->q0 > 0){ typecommit(t); textshow(t, t->q0-1, t->q0-1, TRUE); } return; case Kright: if(t->q1 < t->file->nc){ typecommit(t); textshow(t, t->q1+1, t->q1+1, TRUE); } return; case Kdown: n = t->maxlines/3; goto case_Down; case Kscrollonedown: n = mousescrollsize(t->maxlines); if(n <= 0) n = 1; goto case_Down; case Kpgdown: n = 2*t->maxlines/3; case_Down: q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height)); textsetorigin(t, q0, TRUE); return; case Kup: n = t->maxlines/3; goto case_Up; case Kscrolloneup: n = mousescrollsize(t->maxlines); goto case_Up; case Kpgup: n = 2*t->maxlines/3; case_Up: q0 = textbacknl(t, t->org, n); textsetorigin(t, q0, TRUE); return; case Khome: typecommit(t); textshow(t, 0, 0, FALSE); return; case Kend: typecommit(t); textshow(t, t->file->nc, t->file->nc, FALSE); return; case 0x01: /* ^A: beginning of line */ typecommit(t); /* go to where ^U would erase, if not already at BOL */ nnb = 0; if(t->q0>0 && textreadc(t, t->q0-1)!='\n') nnb = textbswidth(t, 0x15); textshow(t, t->q0-nnb, t->q0-nnb, TRUE); return; case 0x05: /* ^E: end of line */ typecommit(t); q0 = t->q0; while(q0<t->file->nc && textreadc(t, q0)!='\n') q0++; textshow(t, q0, q0, TRUE); return; } if(t->what == Body){ seq++; filemark(t->file); } if(t->q1 > t->q0){ if(t->ncache != 0) error("text.type"); cut(t, t, nil, TRUE, TRUE, nil, 0); t->eq0 = ~0; } textshow(t, t->q0, t->q0, 1); switch(r){ case 0x06: case Kins: rp = textcomplete(t); if(rp == nil) return; nr = runestrlen(rp); break; /* fall through to normal insertion case */ case 0x1B: if(t->eq0 != ~0) textsetselect(t, t->eq0, t->q0); if(t->ncache > 0) typecommit(t); return; case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nnb = textbswidth(t, r); q1 = t->q0; q0 = q1-nnb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nnb = q1-q0; } if(nnb <= 0) return; for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; u->nofill = TRUE; nb = nnb; n = u->ncache; if(n > 0){ if(q1 != u->cq0+n) error("text.type backspace"); if(n > nb) n = nb; u->ncache -= n; textdelete(u, q1-n, q1, FALSE); nb -= n; } if(u->eq0==q1 || u->eq0==~0) u->eq0 = q0; if(nb && u==t) textdelete(u, q0, q0+nb, TRUE); if(u != t) textsetselect(u, u->q0, u->q1); else textsetselect(t, q0, q0); u->nofill = FALSE; } for(i=0; i<t->file->ntext; i++) textfill(t->file->text[i]); return; case '\n': if(t->w->autoindent){ /* find beginning of previous line using backspace code */ nnb = textbswidth(t, 0x15); /* ^U case */ rp = runemalloc(nnb + 1); nr = 0; rp[nr++] = r; for(i=0; i<nnb; i++){ r = textreadc(t, t->q0-nnb+i); if(r != ' ' && r != '\t') break; rp[nr++] = r; } } break; /* fall through to normal code */ } /* otherwise ordinary character; just insert, typically in caches of all texts */ for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u->eq0 == ~0) u->eq0 = t->q0; if(u->ncache == 0) u->cq0 = t->q0; else if(t->q0 != u->cq0+u->ncache) error("text.type cq1"); textinsert(u, t->q0, rp, nr, FALSE); if(u != t) textsetselect(u, u->q0, u->q1); if(u->ncache+nr > u->ncachealloc){ u->ncachealloc += 10 + nr; u->cache = runerealloc(u->cache, u->ncachealloc); } runemove(u->cache+u->ncache, rp, nr); u->ncache += nr; } if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(r=='\n' && t->w!=nil) wincommit(t->w, t); }
Rune* textcomplete(Text *t) { int i, nstr, npath; uint q; Rune tmp[200]; Rune *str, *path; Rune *rp; Completion *c; char *s, *dirs; Runestr dir; /* control-f: filename completion; works back to white space or / */ if(t->q0<t->file->nc && textreadc(t, t->q0)>' ') /* must be at end of word */ return nil; nstr = textfilewidth(t, t->q0, TRUE); str = runemalloc(nstr); npath = textfilewidth(t, t->q0-nstr, FALSE); path = runemalloc(npath); c = nil; rp = nil; dirs = nil; q = t->q0-nstr; for(i=0; i<nstr; i++) str[i] = textreadc(t, q++); q = t->q0-nstr-npath; for(i=0; i<npath; i++) path[i] = textreadc(t, q++); /* is path rooted? if not, we need to make it relative to window path */ if(npath>0 && path[0]=='/') dir = (Runestr){path, npath}; else{ dir = dirname(t, nil, 0); if(dir.nr + 1 + npath > nelem(tmp)){ free(dir.r); goto Return; } if(dir.nr == 0){ dir.nr = 1; dir.r = runestrdup(L"."); } runemove(tmp, dir.r, dir.nr); tmp[dir.nr] = '/'; runemove(tmp+dir.nr+1, path, npath); free(dir.r); dir.r = tmp; dir.nr += 1+npath; dir = cleanrname(dir); } s = smprint("%.*S", nstr, str); dirs = smprint("%.*S", dir.nr, dir.r); c = complete(dirs, s); free(s); if(c == nil){ warning(nil, "error attempting completion: %r\n"); goto Return; } if(!c->advance){ warning(nil, "%.*S%s%.*S*%s\n", dir.nr, dir.r, dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "", nstr, str, c->nmatch? "" : ": no matches in:"); for(i=0; i<c->nfile; i++) warning(nil, " %s\n", c->filename[i]); } if(c->advance) rp = runesmprint("%s", c->string); else rp = nil; Return: freecompletion(c); free(dirs); free(str); free(path); return rp; }
uint textload(Text *t, uint q0, char *file, int setqid) { Rune *rp; Dirlist *dl, **dlp; int fd, i, j, n, ndl, nulls; uint q, q1; Dir *d, *dbuf; char *tmp; Text *u; if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body) error("text.load"); if(t->w->isdir && t->file->nname==0){ warning(nil, "empty directory name\n"); return 0; } fd = open(file, OREAD); if(fd < 0){ warning(nil, "can't open %s: %r\n", file); return 0; } d = dirfstat(fd); if(d == nil){ warning(nil, "can't fstat %s: %r\n", file); goto Rescue; } nulls = FALSE; if(d->qid.type & QTDIR){ /* this is checked in get() but it's possible the file changed underfoot */ if(t->file->ntext > 1){ warning(nil, "%s is a directory; can't read with multiple windows on it\n", file); goto Rescue; } t->w->isdir = TRUE; t->w->filemenu = FALSE; if(t->file->name[t->file->nname-1] != '/'){ rp = runemalloc(t->file->nname+1); runemove(rp, t->file->name, t->file->nname); rp[t->file->nname] = '/'; winsetname(t->w, rp, t->file->nname+1); free(rp); } dlp = nil; ndl = 0; dbuf = nil; while((n=dirread(fd, &dbuf)) > 0){ for(i=0; i<n; i++){ dl = emalloc(sizeof(Dirlist)); j = strlen(dbuf[i].name); tmp = emalloc(j+1+1); memmove(tmp, dbuf[i].name, j); if(dbuf[i].qid.type & QTDIR) tmp[j++] = '/'; tmp[j] = '\0'; dl->r = bytetorune(tmp, &dl->nr); dl->wid = stringwidth(t->font, tmp); free(tmp); ndl++; dlp = realloc(dlp, ndl*sizeof(Dirlist*)); dlp[ndl-1] = dl; } free(dbuf); } qsort(dlp, ndl, sizeof(Dirlist*), dircmp); t->w->dlp = dlp; t->w->ndl = ndl; textcolumnate(t, dlp, ndl); q1 = t->file->nc; }else{ t->w->isdir = FALSE; t->w->filemenu = TRUE; q1 = q0 + fileload(t->file, q0, fd, &nulls); } if(setqid){ t->file->dev = d->dev; t->file->mtime = d->mtime; t->file->qidpath = d->qid.path; } close(fd); rp = fbufalloc(); for(q=q0; q<q1; q+=n){ n = q1-q; if(n > RBUFSIZE) n = RBUFSIZE; bufread(t->file, q, rp, n); if(q < t->org) t->org += n; else if(q <= t->org+t->nchars) frinsert(t, rp, rp+n, q-t->org); if(t->lastlinefull) break; } fbuffree(rp); for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u != t){ if(u->org > u->file->nc) /* will be 0 because of reset(), but safety first */ u->org = 0; textresize(u, u->all); textbacknl(u, u->org, 0); /* go to beginning of line */ } textsetselect(u, q0, q0); } if(nulls) warning(nil, "%s: NUL bytes elided\n", file); free(d); return q1-q0; Rescue: close(fd); return 0; }
void winctl(void *arg) { print_func_entry(); Rune *rp, *bp, *kbdr; int nr, nb, c, wid, i, npart, lastb; char *s, *t, part[3]; Window *w; Mousestate *mp, m; enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT }; Alt alts[NWALT+1]; int walt; Mousereadmesg mrm; Conswritemesg cwm; Consreadmesg crm; Consreadmesg cwrm; Stringpair pair; Wctlmesg wcm; char buf[4*12+1]; w = arg; snprint(buf, sizeof buf, "winctl-id%d", w->id); threadsetname(buf); mrm.cm = chancreate(sizeof(Mouse), 0); cwm.cw = chancreate(sizeof(Stringpair), 0); crm.c1 = chancreate(sizeof(Stringpair), 0); crm.c2 = chancreate(sizeof(Stringpair), 0); cwrm.c1 = chancreate(sizeof(Stringpair), 0); cwrm.c2 = chancreate(sizeof(Stringpair), 0); alts[WKey].c = w->ck; alts[WKey].v = &kbdr; alts[WKey].op = CHANRCV; alts[WMouse].c = w->mc.c; alts[WMouse].v = &w->mc.Mouse; alts[WMouse].op = CHANNOP; // XXXCHANRCV; alts[WMouseread].c = w->mouseread; alts[WMouseread].v = &mrm; alts[WMouseread].op = CHANNOP; // XXXCHANSND; alts[WCtl].c = w->cctl; alts[WCtl].v = &wcm; alts[WCtl].op = CHANRCV; alts[WCwrite].c = w->conswrite; alts[WCwrite].v = &cwm; alts[WCwrite].op = CHANSND; alts[WCread].c = w->consread; alts[WCread].v = &crm; alts[WCread].op = CHANSND; alts[WWread].c = w->wctlread; alts[WWread].v = &cwrm; alts[WWread].op = CHANSND; alts[NWALT].op = CHANEND; npart = 0; lastb = -1; for(;;){ // TODO: mouses if(0 && w->mouseopen && w->mouse.counter != w->mouse.lastcounter) alts[WMouseread].op = CHANSND; else alts[WMouseread].op = CHANNOP; if(w->deleted || !w->wctlready) alts[WWread].op = CHANNOP; else alts[WWread].op = CHANSND; /* this code depends on NL and EOT fitting in a single byte */ /* kind of expensive for each loop; worth precomputing? */ /*if(w->holding) alts[WCread].op = CHANNOP; else*/ if(npart || (w->rawing && w->nraw>0)) alts[WCread].op = CHANSND; else { alts[WCread].op = CHANNOP; for(i=w->qh; i<w->nr; i++){ c = w->run[i]; if(c=='\n' || c=='\004'){ alts[WCread].op = CHANSND; break; } } } walt = alt(alts); switch(walt){ case WKey: for(i=0; kbdr[i]!=L'\0'; i++) wkeyctl(w, kbdr[i]); //wkeyctl(w, r); /// while(nbrecv(w->ck, &r)) //wkeyctl(w, r); break; case WMouse: if(w->mouseopen) { w->mouse.counter++; /* queue click events */ if(!w->mouse.qfull && lastb != w->mc.buttons) { /* add to ring */ mp = &w->mouse.queue[w->mouse.wi]; if(++w->mouse.wi == nelem(w->mouse.queue)) w->mouse.wi = 0; if(w->mouse.wi == w->mouse.ri) w->mouse.qfull = TRUE; mp->Mouse = w->mc.Mouse; mp->counter = w->mouse.counter; lastb = w->mc.buttons; } } else fprint(2, "MOUSECTL\n"); //wmousectl(w); break; case WMouseread: /* send a queued event or, if the queue is empty, the current state */ /* if the queue has filled, we discard all the events it contained. */ /* the intent is to discard frantic clicking by the user during long latencies. */ w->mouse.qfull = FALSE; if(w->mouse.wi != w->mouse.ri) { m = w->mouse.queue[w->mouse.ri]; if(++w->mouse.ri == nelem(w->mouse.queue)) w->mouse.ri = 0; } else m = (Mousestate){w->mc.Mouse, w->mouse.counter}; w->mouse.lastcounter = m.counter; send(mrm.cm, &m.Mouse); continue; case WCtl: exits("WCtl can't do"); #if 0 if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){ chanfree(crm.c1); chanfree(crm.c2); chanfree(mrm.cm); chanfree(cwm.cw); chanfree(cwrm.c1); chanfree(cwrm.c2); threadexits(nil); } #endif continue; case WCwrite: recv(cwm.cw, &pair); rp = pair.s; nr = pair.ns; bp = rp; for(i=0; i<nr; i++) { // See rio for the run conversion crap. For now, I'm not going to // worry about it. This is designed to target Akaros too. fprint(/*w->Console->out*/1, "%c", *bp++); } free(rp); break; case WCread: recv(crm.c1, &pair); t = pair.s; nb = pair.ns; i = npart; npart = 0; if(i) memmove(t, part, i); while(i<nb && (w->qh<w->nr || w->nraw>0)){ if(w->qh == w->nr){ wid = runetochar(t+i, &w->raw[0]); w->nraw--; runemove(w->raw, w->raw+1, w->nraw); }else wid = runetochar(t+i, &w->run[w->qh++]); c = t[i]; /* knows break characters fit in a byte */ i += wid; if(!w->rawing && (c == '\n' || c=='\004')){ if(c == '\004') i--; break; } } if(i==nb && w->qh<w->nr && w->run[w->qh]=='\004') w->qh++; if(i > nb){ npart = i-nb; memmove(part, t+nb, npart); i = nb; } pair.s = t; pair.ns = i; send(crm.c2, &pair); continue; case WWread: w->wctlready = 0; recv(cwrm.c1, &pair); if(w->deleted) pair.ns = sprint(pair.s, ""); else{ s = "visible"; for(i=0; i<nhidden; i++) if(hidden[i] == w){ s = "hidden"; break; } t = "notcurrent"; if(w == input) t = "current"; pair.ns = snprint(pair.s, pair.ns, "%s %s ",t, s); } send(cwrm.c2, &pair); continue; } } print_func_exit(); }
void texttype(Text *t, Rune r) { uint q0, q1; int nnb, nb, n, i; int nr; Rune *rp; Text *u; if(t->what!=Body && t->what!=Tag && r=='\n') return; if(t->what == Tag) t->w->tagsafe = FALSE; nr = 1; rp = &r; switch(r){ case Kleft: typecommit(t); if(t->q0 > 0) textshow(t, t->q0-1, t->q0-1, TRUE); return; case Kright: typecommit(t); if(t->q1 < t->file->b.nc) textshow(t, t->q1+1, t->q1+1, TRUE); return; case Kdown: if(t->what == Tag) goto Tagdown; n = t->fr.maxlines/3; goto case_Down; case Kscrollonedown: if(t->what == Tag) goto Tagdown; n = mousescrollsize(t->fr.maxlines); if(n <= 0) n = 1; goto case_Down; case Kpgdown: n = 2*t->fr.maxlines/3; case_Down: q0 = t->org+frcharofpt(&t->fr, Pt(t->fr.r.min.x, t->fr.r.min.y+n*t->fr.font->height)); textsetorigin(t, q0, TRUE); return; case Kup: if(t->what == Tag) goto Tagup; n = t->fr.maxlines/3; goto case_Up; case Kscrolloneup: if(t->what == Tag) goto Tagup; n = mousescrollsize(t->fr.maxlines); goto case_Up; case Kpgup: n = 2*t->fr.maxlines/3; case_Up: q0 = textbacknl(t, t->org, n); textsetorigin(t, q0, TRUE); return; case Khome: typecommit(t); if(t->org > t->iq1) { q0 = textbacknl(t, t->iq1, 1); textsetorigin(t, q0, TRUE); } else textshow(t, 0, 0, FALSE); return; case Kend: typecommit(t); if(t->iq1 > t->org+t->fr.nchars) { if(t->iq1 > t->file->b.nc) { // should not happen, but does. and it will crash textbacknl. t->iq1 = t->file->b.nc; } q0 = textbacknl(t, t->iq1, 1); textsetorigin(t, q0, TRUE); } else textshow(t, t->file->b.nc, t->file->b.nc, FALSE); return; case 0x01: /* ^A: beginning of line */ typecommit(t); /* go to where ^U would erase, if not already at BOL */ nnb = 0; if(t->q0>0 && textreadc(t, t->q0-1)!='\n') nnb = textbswidth(t, 0x15); textshow(t, t->q0-nnb, t->q0-nnb, TRUE); return; case 0x05: /* ^E: end of line */ typecommit(t); q0 = t->q0; while(q0<t->file->b.nc && textreadc(t, q0)!='\n') q0++; textshow(t, q0, q0, TRUE); return; case Kcmd+'c': /* %C: copy */ typecommit(t); cut(t, t, nil, TRUE, FALSE, nil, 0); return; case Kcmd+'z': /* %Z: undo */ typecommit(t); undo(t, nil, nil, TRUE, 0, nil, 0); return; Tagdown: /* expand tag to show all text */ if(!t->w->tagexpand){ t->w->tagexpand = TRUE; winresize(t->w, t->w->r, FALSE, TRUE); } return; Tagup: /* shrink tag to single line */ if(t->w->tagexpand){ t->w->tagexpand = FALSE; t->w->taglines = 1; winresize(t->w, t->w->r, FALSE, TRUE); } return; } if(t->what == Body){ seq++; filemark(t->file); } /* cut/paste must be done after the seq++/filemark */ switch(r){ case Kcmd+'x': /* %X: cut */ typecommit(t); if(t->what == Body){ seq++; filemark(t->file); } cut(t, t, nil, TRUE, TRUE, nil, 0); textshow(t, t->q0, t->q0, 1); t->iq1 = t->q0; return; case Kcmd+'v': /* %V: paste */ typecommit(t); if(t->what == Body){ seq++; filemark(t->file); } paste(t, t, nil, TRUE, FALSE, nil, 0); textshow(t, t->q0, t->q1, 1); t->iq1 = t->q1; return; } if(t->q1 > t->q0){ if(t->ncache != 0) error("text.type"); cut(t, t, nil, TRUE, TRUE, nil, 0); t->eq0 = ~0; } textshow(t, t->q0, t->q0, 1); switch(r){ case 0x06: /* ^F: complete */ case Kins: typecommit(t); rp = textcomplete(t); if(rp == nil) return; nr = runestrlen(rp); break; /* fall through to normal insertion case */ case 0x1B: if(t->eq0 != ~0) { if(t->eq0 <= t->q0) textsetselect(t, t->eq0, t->q0); else textsetselect(t, t->q0, t->eq0); } if(t->ncache > 0) typecommit(t); t->iq1 = t->q0; return; case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nnb = textbswidth(t, r); q1 = t->q0; q0 = q1-nnb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nnb = q1-q0; } if(nnb <= 0) return; for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; u->nofill = TRUE; nb = nnb; n = u->ncache; if(n > 0){ if(q1 != u->cq0+n) error("text.type backspace"); if(n > nb) n = nb; u->ncache -= n; textdelete(u, q1-n, q1, FALSE); nb -= n; } if(u->eq0==q1 || u->eq0==~0) u->eq0 = q0; if(nb && u==t) textdelete(u, q0, q0+nb, TRUE); if(u != t) textsetselect(u, u->q0, u->q1); else textsetselect(t, q0, q0); u->nofill = FALSE; } for(i=0; i<t->file->ntext; i++) textfill(t->file->text[i]); t->iq1 = t->q0; return; case '\n': if(t->w->autoindent){ /* find beginning of previous line using backspace code */ nnb = textbswidth(t, 0x15); /* ^U case */ rp = runemalloc(nnb + 1); nr = 0; rp[nr++] = r; for(i=0; i<nnb; i++){ r = textreadc(t, t->q0-nnb+i); if(r != ' ' && r != '\t') break; rp[nr++] = r; } } break; /* fall through to normal code */ } /* otherwise ordinary character; just insert, typically in caches of all texts */ for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u->eq0 == ~0) u->eq0 = t->q0; if(u->ncache == 0) u->cq0 = t->q0; else if(t->q0 != u->cq0+u->ncache) error("text.type cq1"); /* * Change the tag before we add to ncache, * so that if the window body is resized the * commit will not find anything in ncache. */ if(u->what==Body && u->ncache == 0){ u->needundo = TRUE; winsettag(t->w); u->needundo = FALSE; } textinsert(u, t->q0, rp, nr, FALSE); if(u != t) textsetselect(u, u->q0, u->q1); if(u->ncache+nr > u->ncachealloc){ u->ncachealloc += 10 + nr; u->cache = runerealloc(u->cache, u->ncachealloc); } runemove(u->cache+u->ncache, rp, nr); u->ncache += nr; } if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(r=='\n' && t->w!=nil) wincommit(t->w, t); t->iq1 = t->q0; }
Window* openfile(Text *t, Expand *e) { Range r; Window *w, *ow; int eval, i, n; Rune *rp; Runestr rs; uint dummy; r.q0 = 0; r.q1 = 0; if(e->nname == 0){ w = t->w; if(w == nil) return nil; }else{ w = lookfile(e->name, e->nname); if(w == nil && e->name[0] != '/'){ /* * Unrooted path in new window. * This can happen if we type a pwd-relative path * in the topmost tag or the column tags. * Most of the time plumber takes care of these, * but plumber might not be running or might not * be configured to accept plumbed directories. * Make the name a full path, just like we would if * opening via the plumber. */ n = utflen(wdir)+1+e->nname+1; rp = runemalloc(n); runesnprint(rp, n, "%s/%.*S", wdir, e->nname, e->name); rs = cleanrname(runestr(rp, n-1)); free(e->name); e->name = rs.r; e->nname = rs.nr; w = lookfile(e->name, e->nname); } } if(w){ t = &w->body; if(!t->col->safe && t->fr.maxlines==0) /* window is obscured by full-column window */ colgrow(t->col, t->col->w[0], 1); }else{ ow = nil; if(t) ow = t->w; w = makenewwindow(t); t = &w->body; winsetname(w, e->name, e->nname); if(textload(t, 0, e->bname, 1) >= 0) t->file->unread = FALSE; t->file->mod = FALSE; t->w->dirty = FALSE; winsettag(t->w); textsetselect(&t->w->tag, t->w->tag.file->b.nc, t->w->tag.file->b.nc); if(ow != nil){ for(i=ow->nincl; --i>=0; ){ n = runestrlen(ow->incl[i]); rp = runemalloc(n); runemove(rp, ow->incl[i], n); winaddincl(w, rp, n); } w->autoindent = ow->autoindent; }else w->autoindent = globalautoindent; xfidlog(w, "new"); } if(e->a1 == e->a0) eval = FALSE; else{ eval = TRUE; r = address(TRUE, t, range(-1,-1), range(t->q0, t->q1), e->u.at, e->a0, e->a1, e->agetc, &eval, &dummy); if(r.q0 > r.q1) { eval = FALSE; warning(nil, "addresses out of order\n"); } if(eval == FALSE) e->jump = FALSE; /* don't jump if invalid address */ } if(eval == FALSE){ r.q0 = t->q0; r.q1 = t->q1; } textshow(t, r.q0, r.q1, 1); winsettag(t->w); seltext = t; if(e->jump) moveto(mousectl, addpt(frptofchar(&t->fr, t->fr.p0), Pt(4, font->height-4))); return w; }
void look3(Text *t, uint q0, uint q1, int external) { int n, c, f, expanded; Text *ct; Expand e; Rune *r; uint p; Plumbmsg *m; Runestr dir; char buf[32]; ct = seltext; if(ct == nil) seltext = t; expanded = expand(t, q0, q1, &e); if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ /* send alphanumeric expansion to external client */ if(expanded == FALSE) return; f = 0; if((e.u.at!=nil && t->w!=nil) || (e.nname>0 && lookfile(e.name, e.nname)!=nil)) f = 1; /* acme can do it without loading a file */ if(q0!=e.q0 || q1!=e.q1) f |= 2; /* second (post-expand) message follows */ if(e.nname) f |= 4; /* it's a file name */ c = 'l'; if(t->what == Body) c = 'L'; n = q1-q0; if(n <= EVENTSIZE){ r = runemalloc(n); bufread(&t->file->b, q0, r, n); winevent(t->w, "%c%d %d %d %d %.*S\n", c, q0, q1, f, n, n, r); free(r); }else winevent(t->w, "%c%d %d %d 0 \n", c, q0, q1, f, n); if(q0==e.q0 && q1==e.q1) return; if(e.nname){ n = e.nname; if(e.a1 > e.a0) n += 1+(e.a1-e.a0); r = runemalloc(n); runemove(r, e.name, e.nname); if(e.a1 > e.a0){ r[e.nname] = ':'; bufread(&e.u.at->file->b, e.a0, r+e.nname+1, e.a1-e.a0); } }else{ n = e.q1 - e.q0; r = runemalloc(n); bufread(&t->file->b, e.q0, r, n); } f &= ~2; if(n <= EVENTSIZE) winevent(t->w, "%c%d %d %d %d %.*S\n", c, e.q0, e.q1, f, n, n, r); else winevent(t->w, "%c%d %d %d 0 \n", c, e.q0, e.q1, f, n); free(r); goto Return; } if(plumbsendfid != nil){ /* send whitespace-delimited word to plumber */ m = emalloc(sizeof(Plumbmsg)); m->src = estrdup("acme"); m->dst = nil; dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } if(dir.nr == 0) m->wdir = estrdup(wdir); else m->wdir = runetobyte(dir.r, dir.nr); free(dir.r); m->type = estrdup("text"); m->attr = nil; buf[0] = '\0'; if(q1 == q0){ if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ q0 = t->q0; q1 = t->q1; }else{ p = q0; while(q0>0 && (c=tgetc(t, q0-1))!=' ' && c!='\t' && c!='\n') q0--; while(q1<t->file->b.nc && (c=tgetc(t, q1))!=' ' && c!='\t' && c!='\n') q1++; if(q1 == q0){ plumbfree(m); goto Return; } sprint(buf, "click=%d", p-q0); m->attr = plumbunpackattr(buf); } } r = runemalloc(q1-q0); bufread(&t->file->b, q0, r, q1-q0); m->data = runetobyte(r, q1-q0); m->ndata = strlen(m->data); free(r); if(m->ndata<messagesize-1024 && plumbsendtofid(plumbsendfid, m) >= 0){ plumbfree(m); goto Return; } plumbfree(m); /* plumber failed to match; fall through */ } /* interpret alphanumeric string ourselves */ if(expanded == FALSE) return; if(e.name || e.u.at) openfile(t, &e); else{ if(t->w == nil) return; ct = &t->w->body; if(t->w != ct->w) winlock(ct->w, 'M'); if(t == ct) textsetselect(ct, e.q1, e.q1); n = e.q1 - e.q0; r = runemalloc(n); bufread(&t->file->b, e.q0, r, n); if(search(ct, r, n) && e.jump) moveto(mousectl, addpt(frptofchar(&ct->fr, ct->fr.p0), Pt(4, ct->fr.font->height-4))); if(t->w != ct->w) winunlock(ct->w); free(r); } Return: free(e.name); free(e.bname); }
void bufinsert(Buffer *b, uint q0, Rune *s, uint n) { uint i, m, t, off; if(q0 > b->nc) panic("internal error: bufinsert"); while(n > 0){ setcache(b, q0); off = q0-b->cq; if(b->cnc+n <= Maxblock){ /* Everything fits in one block. */ t = b->cnc+n; m = n; if(b->bl == nil){ /* allocate */ if(b->cnc != 0) panic("internal error: bufinsert1 cnc!=0"); addblock(b, 0, t); b->cbi = 0; } sizecache(b, t); runemove(b->c+off+m, b->c+off, b->cnc-off); runemove(b->c+off, s, m); b->cnc = t; goto Tail; } /* * We must make a new block. If q0 is at * the very beginning or end of this block, * just make a new block and fill it. */ if(q0==b->cq || q0==b->cq+b->cnc){ if(b->cdirty) flush(b); m = min(n, Maxblock); if(b->bl == nil){ /* allocate */ if(b->cnc != 0) panic("internal error: bufinsert2 cnc!=0"); i = 0; }else{ i = b->cbi; if(q0 > b->cq) i++; } addblock(b, i, m); sizecache(b, m); runemove(b->c, s, m); b->cq = q0; b->cbi = i; b->cnc = m; goto Tail; } /* * Split the block; cut off the right side and * let go of it. */ m = b->cnc-off; if(m > 0){ i = b->cbi+1; addblock(b, i, m); diskwrite(disk, &b->bl[i], b->c+off, m); b->cnc -= m; } /* * Now at end of block. Take as much input * as possible and tack it on end of block. */ m = min(n, Maxblock-b->cnc); sizecache(b, b->cnc+m); runemove(b->c+b->cnc, s, m); b->cnc += m; Tail: b->nc += m; q0 += m; s += m; n -= m; b->cdirty = TRUE; } }
static void fixtext1(Item **list) { Itext *text, *ntext; Item *it, *prev; Rune *s, *s1, *s2; int n; if(*list == nil) return; prev = nil; for(it=*list; it!=nil; it=prev->next) { if(it->tag!=Itexttag || forceitem(it)) goto Continue; text = (Itext *)it; s = text->s; while(*s && isspacerune(*s)) s++; if(!*s) { if(prev == nil) prev = *list = it->next; else prev->next = it->next; it->next = nil; freeitems(it); if(prev == nil) return; continue; } n = 0; while(s[n] && !isspacerune(s[n])) n++; if(!s[n]) goto Continue; s1 = runemalloc(n+1); s1 = runemove(s1, s, n); s1[n] = L'\0'; s += n; while(*s && isspacerune(*s)) s++; if(*s) { n = runestrlen(s); s2 = runemalloc(n+1); runemove(s2, s, n); s2[n] = L'\0'; ntext = emalloc(sizeof(Itext)); ntext->s = s2; ntext->ascent = text->ascent; ntext->anchorid = text->anchorid; ntext->state = text->state&~(IFbrk|IFbrksp|IFnobrk|IFcleft|IFcright); ntext->tag = text->tag; ntext->fnt = text->fnt; ntext->fg = text->fg; ntext->ul = text->ul; ntext->next = (Item *)text->next; text->next = (Item *)ntext; } free(text->s); text->s = s1; Continue: prev = it; } }