|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <draw.h> | 
|  | #include <thread.h> | 
|  | #include <cursor.h> | 
|  | #include <mouse.h> | 
|  | #include <keyboard.h> | 
|  | #include <frame.h> | 
|  | #include <fcall.h> | 
|  | #include <plumb.h> | 
|  | #include "dat.h" | 
|  | #include "fns.h" | 
|  |  | 
|  | static	Point		prevmouse; | 
|  | static	Window	*mousew; | 
|  |  | 
|  | Range | 
|  | range(int q0, int q1) | 
|  | { | 
|  | Range r; | 
|  |  | 
|  | r.q0 = q0; | 
|  | r.q1 = q1; | 
|  | return r; | 
|  | } | 
|  |  | 
|  | Runestr | 
|  | runestr(Rune *r, uint n) | 
|  | { | 
|  | Runestr rs; | 
|  |  | 
|  | rs.r = r; | 
|  | rs.nr = n; | 
|  | return rs; | 
|  | } | 
|  |  | 
|  | void | 
|  | cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls) | 
|  | { | 
|  | uchar *q; | 
|  | Rune *s; | 
|  | int j, w; | 
|  |  | 
|  | /* | 
|  | * Always guaranteed that n bytes may be interpreted | 
|  | * without worrying about partial runes.  This may mean | 
|  | * reading up to UTFmax-1 more bytes than n; the caller | 
|  | * knows this.  If n is a firm limit, the caller should | 
|  | * set p[n] = 0. | 
|  | */ | 
|  | q = (uchar*)p; | 
|  | s = r; | 
|  | for(j=0; j<n; j+=w){ | 
|  | if(*q < Runeself){ | 
|  | w = 1; | 
|  | *s = *q++; | 
|  | }else{ | 
|  | w = chartorune(s, (char*)q); | 
|  | q += w; | 
|  | } | 
|  | if(*s) | 
|  | s++; | 
|  | else if(nulls) | 
|  | *nulls = TRUE; | 
|  | } | 
|  | *nb = (char*)q-p; | 
|  | *nr = s-r; | 
|  | } | 
|  |  | 
|  | void | 
|  | error(char *s) | 
|  | { | 
|  | fprint(2, "acme: %s: %r\n", s); | 
|  | threadexitsall(nil); | 
|  | } | 
|  |  | 
|  | Window* | 
|  | errorwin1(Rune *dir, int ndir, Rune **incl, int nincl) | 
|  | { | 
|  | Window *w; | 
|  | Rune *r; | 
|  | int i, n; | 
|  | static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 }; | 
|  |  | 
|  | r = runemalloc(ndir+8); | 
|  | if((n = ndir) != 0){ | 
|  | runemove(r, dir, ndir); | 
|  | r[n++] = L'/'; | 
|  | } | 
|  | runemove(r+n, Lpluserrors, 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); | 
|  | xfidlog(w, "new"); | 
|  | } | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* make new window, if necessary; return with it locked */ | 
|  | Window* | 
|  | errorwin(Mntdir *md, int owner) | 
|  | { | 
|  | Window *w; | 
|  |  | 
|  | for(;;){ | 
|  | if(md == nil) | 
|  | w = errorwin1(nil, 0, nil, 0); | 
|  | else | 
|  | w = errorwin1(md->dir, md->ndir, md->incl, md->nincl); | 
|  | winlock(w, owner); | 
|  | if(w->col != nil) | 
|  | break; | 
|  | /* window was deleted too fast */ | 
|  | winunlock(w); | 
|  | } | 
|  | return w; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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; | 
|  | } | 
|  |  | 
|  | typedef struct Warning Warning; | 
|  |  | 
|  | struct Warning{ | 
|  | Mntdir *md; | 
|  | Buffer buf; | 
|  | Warning *next; | 
|  | }; | 
|  |  | 
|  | static Warning *warnings; | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* called while row is locked */ | 
|  | void | 
|  | flushwarnings(void) | 
|  | { | 
|  | Warning *warn, *next; | 
|  | Window *w; | 
|  | Text *t; | 
|  | int owner, nr, q0, n; | 
|  | Rune *r; | 
|  |  | 
|  | for(warn=warnings; warn; warn=next) { | 
|  | w = errorwin(warn->md, 'E'); | 
|  | t = &w->body; | 
|  | owner = w->owner; | 
|  | if(owner == 0) | 
|  | w->owner = 'E'; | 
|  | wincommit(w, t); | 
|  | /* | 
|  | * Most commands don't generate much output. For instance, | 
|  | * Edit ,>cat goes through /dev/cons and is already in blocks | 
|  | * because of the i/o system, but a few can.  Edit ,p will | 
|  | * put the entire result into a single hunk.  So it's worth doing | 
|  | * this in blocks (and putting the text in a buffer in the first | 
|  | * place), to avoid a big memory footprint. | 
|  | */ | 
|  | r = fbufalloc(); | 
|  | q0 = t->file->b.nc; | 
|  | for(n = 0; n < warn->buf.nc; n += nr){ | 
|  | nr = warn->buf.nc - n; | 
|  | if(nr > RBUFSIZE) | 
|  | nr = RBUFSIZE; | 
|  | bufread(&warn->buf, n, r, nr); | 
|  | textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr); | 
|  | } | 
|  | textshow(t, q0, t->file->b.nc, 1); | 
|  | free(r); | 
|  | winsettag(t->w); | 
|  | textscrdraw(t); | 
|  | w->owner = owner; | 
|  | w->dirty = FALSE; | 
|  | winunlock(w); | 
|  | bufclose(&warn->buf); | 
|  | next = warn->next; | 
|  | if(warn->md) | 
|  | fsysdelid(warn->md); | 
|  | free(warn); | 
|  | } | 
|  | warnings = nil; | 
|  | } | 
|  |  | 
|  | void | 
|  | warning(Mntdir *md, char *s, ...) | 
|  | { | 
|  | Rune *r; | 
|  | va_list arg; | 
|  |  | 
|  | va_start(arg, s); | 
|  | r = runevsmprint(s, arg); | 
|  | va_end(arg); | 
|  | if(r == nil) | 
|  | error("runevsmprint failed"); | 
|  | addwarningtext(md, r, runestrlen(r)); | 
|  | free(r); | 
|  | } | 
|  |  | 
|  | int | 
|  | runeeq(Rune *s1, uint n1, Rune *s2, uint n2) | 
|  | { | 
|  | if(n1 != n2) | 
|  | return FALSE; | 
|  | return memcmp(s1, s2, n1*sizeof(Rune)) == 0; | 
|  | } | 
|  |  | 
|  | uint | 
|  | min(uint a, uint b) | 
|  | { | 
|  | if(a < b) | 
|  | return a; | 
|  | return b; | 
|  | } | 
|  |  | 
|  | uint | 
|  | max(uint a, uint b) | 
|  | { | 
|  | if(a > b) | 
|  | return a; | 
|  | return b; | 
|  | } | 
|  |  | 
|  | char* | 
|  | runetobyte(Rune *r, int n) | 
|  | { | 
|  | char *s; | 
|  |  | 
|  | if(r == nil) | 
|  | return nil; | 
|  | s = emalloc(n*UTFmax+1); | 
|  | setmalloctag(s, getcallerpc(&r)); | 
|  | snprint(s, n*UTFmax+1, "%.*S", n, r); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | Rune* | 
|  | bytetorune(char *s, int *ip) | 
|  | { | 
|  | Rune *r; | 
|  | int nb, nr; | 
|  |  | 
|  | nb = strlen(s); | 
|  | r = runemalloc(nb+1); | 
|  | cvttorunes(s, nb, r, &nb, &nr, nil); | 
|  | r[nr] = '\0'; | 
|  | *ip = nr; | 
|  | return r; | 
|  | } | 
|  |  | 
|  | int | 
|  | isalnum(Rune c) | 
|  | { | 
|  | /* | 
|  | * Hard to get absolutely right.  Use what we know about ASCII | 
|  | * and assume anything above the Latin control characters is | 
|  | * potentially an alphanumeric. | 
|  | */ | 
|  | if(c <= ' ') | 
|  | return FALSE; | 
|  | if(0x7F<=c && c<=0xA0) | 
|  | return FALSE; | 
|  | if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) | 
|  | return FALSE; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | rgetc(void *v, uint n) | 
|  | { | 
|  | return ((Rune*)v)[n]; | 
|  | } | 
|  |  | 
|  | int | 
|  | tgetc(void *a, uint n) | 
|  | { | 
|  | Text *t; | 
|  |  | 
|  | t = a; | 
|  | if(n >= t->file->b.nc) | 
|  | return 0; | 
|  | return textreadc(t, n); | 
|  | } | 
|  |  | 
|  | Rune* | 
|  | skipbl(Rune *r, int n, int *np) | 
|  | { | 
|  | while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){ | 
|  | --n; | 
|  | r++; | 
|  | } | 
|  | *np = n; | 
|  | return r; | 
|  | } | 
|  |  | 
|  | Rune* | 
|  | findbl(Rune *r, int n, int *np) | 
|  | { | 
|  | while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){ | 
|  | --n; | 
|  | r++; | 
|  | } | 
|  | *np = n; | 
|  | return r; | 
|  | } | 
|  |  | 
|  | void | 
|  | savemouse(Window *w) | 
|  | { | 
|  | prevmouse = mouse->xy; | 
|  | mousew = w; | 
|  | } | 
|  |  | 
|  | int | 
|  | restoremouse(Window *w) | 
|  | { | 
|  | int did; | 
|  |  | 
|  | did = 0; | 
|  | if(mousew!=nil && mousew==w) { | 
|  | moveto(mousectl, prevmouse); | 
|  | did = 1; | 
|  | } | 
|  | mousew = nil; | 
|  | return did; | 
|  | } | 
|  |  | 
|  | void | 
|  | clearmouse() | 
|  | { | 
|  | mousew = nil; | 
|  | } | 
|  |  | 
|  | char* | 
|  | estrdup(char *s) | 
|  | { | 
|  | char *t; | 
|  |  | 
|  | t = strdup(s); | 
|  | if(t == nil) | 
|  | error("strdup failed"); | 
|  | setmalloctag(t, getcallerpc(&s)); | 
|  | return t; | 
|  | } | 
|  |  | 
|  | void* | 
|  | emalloc(uint n) | 
|  | { | 
|  | void *p; | 
|  |  | 
|  | p = malloc(n); | 
|  | if(p == nil) | 
|  | error("malloc failed"); | 
|  | setmalloctag(p, getcallerpc(&n)); | 
|  | memset(p, 0, n); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | void* | 
|  | erealloc(void *p, uint n) | 
|  | { | 
|  | p = realloc(p, n); | 
|  | if(p == nil) | 
|  | error("realloc failed"); | 
|  | setmalloctag(p, getcallerpc(&n)); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Heuristic city. | 
|  | */ | 
|  | Window* | 
|  | makenewwindow(Text *t) | 
|  | { | 
|  | Column *c; | 
|  | Window *w, *bigw, *emptyw; | 
|  | Text *emptyb; | 
|  | int i, y, el; | 
|  |  | 
|  | if(activecol) | 
|  | c = activecol; | 
|  | else if(seltext && seltext->col) | 
|  | c = seltext->col; | 
|  | else if(t && t->col) | 
|  | c = t->col; | 
|  | else{ | 
|  | if(row.ncol==0 && rowadd(&row, nil, -1)==nil) | 
|  | error("can't make column"); | 
|  | c = row.col[row.ncol-1]; | 
|  | } | 
|  | activecol = c; | 
|  | if(t==nil || t->w==nil || c->nw==0) | 
|  | return coladd(c, nil, nil, -1); | 
|  |  | 
|  | /* find biggest window and biggest blank spot */ | 
|  | emptyw = c->w[0]; | 
|  | bigw = emptyw; | 
|  | for(i=1; i<c->nw; i++){ | 
|  | w = c->w[i]; | 
|  | /* use >= to choose one near bottom of screen */ | 
|  | if(w->body.fr.maxlines >= bigw->body.fr.maxlines) | 
|  | bigw = w; | 
|  | if(w->body.fr.maxlines-w->body.fr.nlines >= emptyw->body.fr.maxlines-emptyw->body.fr.nlines) | 
|  | emptyw = w; | 
|  | } | 
|  | emptyb = &emptyw->body; | 
|  | el = emptyb->fr.maxlines-emptyb->fr.nlines; | 
|  | /* if empty space is big, use it */ | 
|  | if(el>15 || (el>3 && el>(bigw->body.fr.maxlines-1)/2)) | 
|  | y = emptyb->fr.r.min.y+emptyb->fr.nlines*font->height; | 
|  | else{ | 
|  | /* if this window is in column and isn't much smaller, split it */ | 
|  | if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3) | 
|  | bigw = t->w; | 
|  | y = (bigw->r.min.y + bigw->r.max.y)/2; | 
|  | } | 
|  | w = coladd(c, nil, nil, y); | 
|  | if(w->body.fr.maxlines < 2) | 
|  | colgrow(w->col, w, 1); | 
|  | return w; | 
|  | } |