summaryrefslogtreecommitdiff
path: root/sys/src/cmd/acme/util.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/acme/util.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/acme/util.c')
-rwxr-xr-xsys/src/cmd/acme/util.c470
1 files changed, 470 insertions, 0 deletions
diff --git a/sys/src/cmd/acme/util.c b/sys/src/cmd/acme/util.c
new file mode 100755
index 000000000..aa5d3ac14
--- /dev/null
+++ b/sys/src/cmd/acme/util.c
@@ -0,0 +1,470 @@
+#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;
+
+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);
+ remove(acmeerrorfile);
+ abort();
+}
+
+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;
+}
+
+/* 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->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->nc, r, nr, TRUE, &nr);
+ }
+ textshow(t, q0, t->file->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.
+ *
+ * Treat 0xA0 (non-breaking space) as a special alphanumeric
+ * character [sape]
+ */
+ 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->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;
+}
+
+void
+restoremouse(Window *w)
+{
+ if(mousew!=nil && mousew==w)
+ moveto(mousectl, prevmouse);
+ mousew = nil;
+}
+
+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.maxlines >= bigw->body.maxlines)
+ bigw = w;
+ if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines)
+ emptyw = w;
+ }
+ emptyb = &emptyw->body;
+ el = emptyb->maxlines-emptyb->nlines;
+ /* if empty space is big, use it */
+ if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2))
+ y = emptyb->r.min.y+emptyb->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.maxlines < 2)
+ colgrow(w->col, w, 1);
+ return w;
+}