#include #include #include #include #include #include #include #include #include #include #include #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); } for (i = 0; i < NINDENT; i++) w->indent[i] = globalindent[i]; 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; if (n1 == 0) return TRUE; 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; }