aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/acme/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/acme/util.c')
-rw-r--r--src/cmd/acme/util.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/src/cmd/acme/util.c b/src/cmd/acme/util.c
new file mode 100644
index 00000000..8e3cfa29
--- /dev/null
+++ b/src/cmd/acme/util.c
@@ -0,0 +1,395 @@
+#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);
+ abort();
+}
+
+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+7);
+ if(n = ndir) /* assign = */
+ runemove(r, dir, ndir);
+ 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);
+ }
+ free(r);
+ for(i=nincl; --i>=0; ){
+ n = runestrlen(incl[i]);
+ r = runemalloc(n);
+ runemove(r, incl[i], n);
+ winaddincl(w, r, n);
+ }
+ return w;
+}
+
+/* make new window, if necessary; return with it locked */
+Window*
+errorwin(Mntdir *md, int owner, Window *e)
+{
+ Window *w;
+
+ for(;;){
+ if(md == nil)
+ w = errorwin1(nil, 0, nil, 0);
+ else
+ w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
+ if(w != e)
+ winlock(w, owner);
+ if(w->col != nil)
+ break;
+ /* window was deleted too fast */
+ if(w != e)
+ winunlock(w);
+ }
+ return w;
+}
+
+static void
+printwarning(Window *ew, Mntdir *md, Rune *r)
+{
+ int nr, q0, owner;
+ Window *w;
+ Text *t;
+
+ if(r == nil)
+ error("runevsmprint failed");
+ nr = runestrlen(r);
+
+ if(row.ncol == 0){ /* really early error */
+ rowinit(&row, screen->clipr);
+ rowadd(&row, nil, -1);
+ rowadd(&row, nil, -1);
+ if(row.ncol == 0)
+ error("initializing columns in warning()");
+ }
+
+ w = errorwin(md, 'E', ew);
+ t = &w->body;
+ owner = w->owner;
+ if(owner == 0)
+ w->owner = 'E';
+ wincommit(w, t);
+ q0 = textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr);
+ textshow(t, q0, q0+nr, 1);
+ winsettag(t->w);
+ textscrdraw(t);
+ w->owner = owner;
+ w->dirty = FALSE;
+ if(ew != w)
+ winunlock(w);
+ free(r);
+}
+
+void
+warning(Mntdir *md, char *s, ...)
+{
+ Rune *r;
+ va_list arg;
+
+ va_start(arg, s);
+ r = runevsmprint(s, arg);
+ va_end(arg);
+ printwarning(nil, md, r);
+}
+
+/*
+ * Warningew is like warning but avoids locking the error window
+ * if it's already locked by checking that ew!=error window.
+ */
+void
+warningew(Window *ew, Mntdir *md, char *s, ...)
+{
+ Rune *r;
+ va_list arg;
+
+ va_start(arg, s);
+ r = runevsmprint(s, arg);
+ va_end(arg);
+ printwarning(ew, md, 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;
+}
+
+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){
+ fprint(2, "allocating %d from %lux: %r\n", n, getcallerpc(&n));
+ *(int*)0=0;
+ 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){
+ fprint(2, "reallocating %d: %r\n", n);
+ 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;
+}