summaryrefslogtreecommitdiff
path: root/sys/src/cmd/abaco/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/abaco/util.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/abaco/util.c')
-rwxr-xr-xsys/src/cmd/abaco/util.c1293
1 files changed, 1293 insertions, 0 deletions
diff --git a/sys/src/cmd/abaco/util.c b/sys/src/cmd/abaco/util.c
new file mode 100755
index 000000000..cd7e7b3da
--- /dev/null
+++ b/sys/src/cmd/abaco/util.c
@@ -0,0 +1,1293 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <plumb.h>
+#include <html.h>
+#include <regexp.h>
+#include "dat.h"
+#include "fns.h"
+
+static Point prevmouse;
+static Window *mousew;
+
+int
+min(int a, int b)
+{
+ if(a < b)
+ return a;
+ return b;
+}
+
+int
+max(int a, int b)
+{
+ if(a > b)
+ return a;
+ return b;
+}
+
+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
+bytetorunestr(char *s, Runestr *rs)
+{
+ Rune *r;
+ int nb, nr;
+
+ nb = strlen(s);
+ r = runemalloc(nb+1);
+ cvttorunes(s, nb, r, &nb, &nr, nil);
+ r[nr] = '\0';
+ rs->nr = nr;
+ rs->r = r;
+}
+
+void
+error(char *s)
+{
+ fprint(2, "abaco: %s: %r\n", s);
+// abort();
+ threadexitsall(s);
+}
+
+void*
+emalloc(ulong 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, ulong n)
+{
+ p = realloc(p, n);
+ if(p == nil)
+ error("realloc failed");
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+Rune*
+erunestrdup(Rune *r)
+{
+ void *p;
+
+ if(r == nil)
+ return nil;
+ p = runestrdup(r);
+ if(p == nil)
+ error("runestrdup failed");
+ setmalloctag(p, getcallerpc(&r));
+ return p;
+}
+
+char*
+estrdup(char *s)
+{
+ char *t;
+
+ t = strdup(s);
+ if(t == nil)
+ error("strdup failed");
+ setmalloctag(t, getcallerpc(&s));
+ return t;
+}
+
+int
+runestreq(Runestr a, Runestr b)
+{
+ return runeeq(a.r, a.nr, b.r, b.nr);
+}
+
+int
+runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
+{
+ if(n1 != n2)
+ return FALSE;
+ return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
+}
+
+void
+closerunestr(Runestr *rs)
+{
+
+ rs->nr = 0;
+ if(rs->r)
+ free(rs->r);
+ rs->r = nil;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+int
+istextfield(Item *i)
+{
+ Formfield *ff;
+
+ ff = ((Iformfield *)i)->formfield;
+ if(ff->ftype==Ftext || ff->ftype==Ftextarea || ff->ftype==Fpassword)
+ return TRUE;
+
+ return FALSE;
+}
+
+int
+forceitem(Item *i)
+{
+ if(i->state&IFwrap && i->tag!=Iruletag && i->tag!=Itabletag)
+ return FALSE;
+
+ return TRUE;
+}
+
+int
+dimwidth(Dimen d, int w)
+{
+ int s, k;
+
+ k = dimenkind(d);
+ if(k == Dnone)
+ return w;
+ s = dimenspec(d);
+ if(k == Dpixels)
+ w = s;
+ else if(k==Dpercent && s<100)
+ w = s*w/100;
+
+ return w;
+}
+
+void
+frdims(Dimen *d, int n, int t, int **ret)
+{
+ int totpix, totpcnt, totrel;
+ double spix, spcnt, relu, vd;
+ int tt, trest, totpixrel, minrelu, i;
+ int *x, *spec, *kind;
+
+ if(n == 1){
+ *ret = x = emalloc(sizeof(int));
+ x[0] = t;
+ return;
+ }
+ totpix = totpcnt = totrel = 0;
+ spec = emalloc(n*sizeof(int));
+ kind = emalloc(n*sizeof(int));
+ for(i=0; i<n; i++){
+ spec[i] = dimenspec(d[i]);
+ if(spec[i] < 0)
+ spec[i] = 0;
+ kind[i] = dimenkind(d[i]);
+ switch(kind[i]){
+ case Dpixels:
+ totpix += spec[i];
+ break;
+ case Dpercent:
+ totpcnt += spec[i];
+ break;
+ case Drelative:
+ totrel += spec[i];
+ break;
+ case Dnone:
+ totrel++;
+ break;
+ }
+ }
+ spix = spcnt = 1.0;
+ minrelu = 0;
+ if(totrel > 0)
+ minrelu = Scrollsize+Scrollgap;
+ relu = (double)minrelu;
+ tt = totpix + t*totpcnt/100 + totrel*minrelu;
+ if(tt < t){
+ if(totrel == 0){
+ if(totpcnt != 0)
+ spcnt = (double)((t-totpix)*100)/(double)(t*totpcnt);
+ else
+ spix = (double)t/(double)totpix;
+ }else
+ relu += (double)(t-tt)/(double)totrel;
+ }else{
+ totpixrel = totpix + totrel*minrelu;
+ if(totpixrel < t)
+ spcnt = (double)((t-totpixrel)*100)/(double)(t*totpcnt);
+ else{
+ trest = t - totrel*minrelu;
+ if(trest > 0)
+ spcnt = (double)trest/(double)(totpix + (t*totpcnt/100));
+ else{
+ spcnt = (double)t/(double)tt;
+ relu = 0.0;
+ }
+ spix = spcnt;
+ }
+ }
+ x = emalloc(n * sizeof(int));
+ tt = 0;
+ for(i=0; i<n-1; i++){
+ vd = (double)spec[i];
+ switch(kind[i]){
+ case Dpixels:
+ vd = vd*spix;
+ break;
+ case Dpercent:
+ vd = vd*(double)t*spcnt/100.0;
+ break;
+ case Drelative:
+ vd = vd*relu;
+ break;
+ case Dnone:
+ vd = relu;
+ break;
+ }
+ x[i] = (int)(vd+.5);
+ tt += x[i];
+ }
+ x[n - 1] = t - tt;
+ *ret = x;
+ free(spec);
+ free(kind);
+}
+
+Image *
+getbg(Page *p)
+{
+ Docinfo *d;
+ Cimage *ci;
+ Image *bg;
+
+ d = p->doc;
+ if(d->backgrounditem){
+ if(d->backgrounditem->aux){
+ ci = d->backgrounditem->aux;
+ if(ci->mi)
+ getimage(ci, d->backgrounditem->altrep);
+ bg = ci->i;
+ }else
+ bg = display->white;
+ }else
+ bg = getcolor(d->background.color);
+
+ return bg;
+}
+
+Rune *
+getbase(Page *p)
+{
+ if(p->doc)
+ return p->doc->base;
+ if(p->url->act.r)
+ return p->url->act.r;
+ return p->url->src.r;
+}
+
+Image *
+eallocimage(Display *d, Rectangle r, ulong chan, int repl, int col)
+{
+ Image *i;
+
+ i = allocimage(d, r, chan, repl, col);
+ if(i == nil)
+ error("allocimage failed");
+ return i;
+}
+
+void
+rect3d(Image *im, Rectangle r, int i, Image **c, Point sp)
+{
+ Point p[6];
+
+ if(i < 0){
+ r = insetrect(r, i);
+ sp = addpt(sp, Pt(i,i));
+ i = -i;
+ }
+ draw(im, Rect(r.min.x+i, r.min.y+i, r.max.x-i, r.max.y-i), c[2], nil, sp);
+ p[0] = r.min;
+ p[1] = Pt(r.min.x, r.max.y);
+ p[2] = Pt(r.min.x+i, r.max.y-i);
+ p[3] = Pt(r.min.x+i, r.min.y+i);
+ p[4] = Pt(r.max.x-i, r.min.y+i);
+ p[5] = Pt(r.max.x, r.min.y);
+ fillpoly(im, p, 6, 0, c[0], sp);
+ p[0] = r.max;
+ p[1] = Pt(r.min.x, r.max.y);
+ p[2] = Pt(r.min.x+i, r.max.y-i);
+ p[3] = Pt(r.max.x-i, r.max.y-i);
+ p[4] = Pt(r.max.x-i, r.min.y+i);
+ p[5] = Pt(r.max.x, r.min.y);
+ fillpoly(im, p, 6, 0, c[1], sp);
+}
+
+void
+ellipse3d(Image *im, Point p, int rad, int i, Image **c, Point sp)
+{
+ fillarc(im, p, rad, rad, c[0], sp, 45, 180);
+ fillarc(im, p, rad, rad, c[1], sp, 45, -180);
+ fillellipse(im, p, rad-i, rad-i, c[2], sp);
+}
+
+void
+colarray(Image **c, Image *c0, Image *c1, Image *c2, int checked)
+{
+ if(checked){
+ c[0] = c0;
+ c[1] = c1;
+ }else{
+ c[0] = c1;
+ c[1] = c0;
+ }
+ c[2] = c2;
+}
+
+static char *deffontpaths[] = {
+#include "fonts.h"
+};
+
+static char *fontpaths[NumFnt];
+static Font *fonts[NumFnt];
+
+void
+initfontpaths(void)
+{
+ Biobufhdr *bp;
+ char buf[128];
+ int i;
+
+ /* we don't care if getenv(2) fails */
+ snprint(buf, sizeof(buf)-1, "%s/lib/abaco.fonts", getenv("home"));
+ if((bp=Bopen(buf, OREAD)) == nil)
+ goto Default;
+
+ for(i=0; i<NumFnt; i++)
+ if((fontpaths[i]=Brdstr(bp, '\n', 1)) == nil)
+ goto Error;
+
+ Bterm(bp);
+ return;
+Error:
+ fprint(2, "abaco: not enough fontpaths in '%s'\n", buf);
+ Bterm(bp);
+ for(i--; i>=0; i--)
+ free(fontpaths[i]);
+Default:
+ for(i=0; i<NumFnt; i++)
+ fontpaths[i] = deffontpaths[i];
+}
+
+Font *
+getfont(int i)
+{
+ if(fonts[i] == nil){
+ fonts[i] = openfont(display, fontpaths[i]);
+ if(fonts[i] == nil)
+ error("can't open font file");
+ }
+ return fonts[i];
+}
+
+typedef struct Color Color;
+
+struct Color {
+ int rgb;
+ Image *i;
+ Color *next;
+};
+
+enum {
+ NHASH = 19,
+};
+
+static Color *colortab[NHASH];
+
+Image *
+getcolor(int rgb)
+{
+ Color *c;
+ int h;
+
+ if(rgb == 0xFFFFFF)
+ return display->white;
+ else if(rgb == 0x000000)
+ return display->black;
+
+ h = rgb%NHASH;
+ for(c=colortab[h]; c!=nil; c=c->next)
+ if(c->rgb == rgb){
+ flushimage(display, 0); /* BUG? */
+ return c->i;
+ }
+ c = emalloc(sizeof(Color));
+ c->i = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, (rgb<<8)|0xFF);
+ c->rgb = rgb;
+ c->next = colortab[h];
+ colortab[h] = c;
+
+ return c->i;
+}
+
+int
+plumbrunestr(Runestr *rs, char *attr)
+{
+ Plumbmsg *m;
+ int i;
+
+ i = -1;
+ if(plumbsendfd >= 0){
+ m = emalloc(sizeof(Plumbmsg));
+ m->src = estrdup("abaco");
+ m->dst = nil;
+ m->wdir = estrdup("/tmp");
+ m->type = estrdup("text");
+ if(attr)
+ m->attr = plumbunpackattr(attr);
+ else
+ m->attr = nil;
+ m->data = smprint("%.*S", rs->nr, rs->r);
+ m->ndata = -1;
+ i = plumbsend(plumbsendfd, m);
+ plumbfree(m);
+ }
+ return i;
+}
+
+int
+hexdigit(int v)
+{
+ if(0<=v && v<=9)
+ return '0' + v;
+ else
+ return 'A' + v - 10;
+}
+
+static int
+inclass(char c, Rune* cl)
+{
+ int n, ans, negate, i;
+
+ n = runestrlen(cl);
+ if(n == 0)
+ return 0;
+ ans = 0;
+ negate = 0;
+ if(cl[0] == '^'){
+ negate = 1;
+ cl++;
+ n--;
+ }
+ for(i=0; i<n; i++){
+ if(cl[i]=='-' && i>0 && i<n-1){
+ if(c>=cl[i - 1] && c<=cl[i+1]){
+ ans = 1;
+ break;
+ }
+ i++;
+ }
+ else if(c == cl[i]){
+ ans = 1;
+ break;
+ }
+ }
+ if(negate)
+ ans = !ans;
+ return ans;
+}
+
+Rune*
+ucvt(Rune* s)
+{
+ Rune* u;
+ char *t;
+ int i, c, n, j, len;
+
+ t = smprint("%S", s);
+ n = strlen(t);
+ len = 0;
+ for(i=0; i<n; i++){
+ c = t[i];
+ if(inclass(c, L"- /$_@.!*'(),a-zA-Z0-9"))
+ len++;
+ else
+ len += 3;
+ }
+ u = runemalloc(len+1);
+ j = 0;
+
+ for(i=0; i<n; i++){
+ c = t[i];
+ if(inclass(c, L"-/$_@.!*'(),a-zA-Z0-9"))
+ u[j++] = c;
+ else if(c == ' ')
+ u[j++] = '+';
+ else {
+ u[j++] = '%';
+ u[j++] = hexdigit((c >> 4)&15);
+ u[j++] = hexdigit(c&15);
+ }
+ }
+ u[j] = 0;
+ free(t);
+ return u;
+}
+
+void
+reverseimages(Iimage **head)
+{
+ Iimage *r, *c, *n;
+
+ r = nil;
+ for(c=*head; c!=nil; c=n){
+ n = c->nextimage;
+ c->nextimage = r;
+ r = c;
+ }
+ *head = r;
+}
+
+char urlexpr[] = "^(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|"
+ "prospero)://([a-zA-Z0-9_@\\-]+([.:][a-zA-Z0-9_@\\-]+)*)";
+Reprog *urlprog;
+
+int
+validurl(Rune *r)
+{
+ Resub rs[10];
+
+ if(urlprog == nil){
+ urlprog = regcomp(urlexpr);
+ if(urlprog == nil)
+ error("regcomp");
+ }
+ memset(rs, 0, sizeof(rs));
+ if(rregexec(urlprog, r, rs, nelem(rs)) == 0)
+ return FALSE;
+ return TRUE;
+}
+
+void
+execproc(void *v)
+{
+ Channel *sync;
+ Exec *e;
+ int p[2], q[2];
+ char *cmd;
+
+ threadsetname("execproc");
+ e = v;
+ p[0] = e->p[0];
+ p[1] = e->p[1];
+ q[0] = e->q[0];
+ q[1] = e->q[1];
+ cmd = e->cmd;
+ sync = e->sync;
+ rfork(RFFDG);
+ free(e);
+ dup(p[0], 0);
+ close(p[0]);
+ close(p[1]);
+ if(q[0]){
+ dup(q[1], 1);
+ close(q[0]);
+ close(q[1]);
+ }
+ if(!procstderr)
+ close(2);
+ procexecl(sync, "/bin/rc", "rc", "-c", cmd, 0);
+ error("can't exec");
+}
+
+static void
+writeproc(void *v)
+{
+ Channel *sync;
+ void **a;
+ char *s;
+ long np;
+ int fd, i, n;
+
+ threadsetname("writeproc");
+ a = v;
+ sync = a[0];
+ fd = (int)a[1];
+ s = a[2];
+ np =(long)a[3];
+ free(a);
+
+ for(i=0; i<np; i+=n){
+ n = np-i;
+ if(n > BUFSIZE)
+ n = BUFSIZE;
+ if(write(fd, s+i, n) != n)
+ break;
+ }
+ close(fd);
+ sendul(sync, i);
+}
+
+struct {
+ char *mime;
+ char *tcs;
+}tcstab[] = {
+
+#include "tcs.h"
+
+ /* not generated by the script */
+ "euc_jp", "jis",
+ "euc_kr", "euc-k",
+ "windows-874", "tis",
+ nil, nil,
+};
+
+enum {
+ Winstart = 127,
+ Winend = 159
+};
+
+static int winchars[] = {
+ 8226, /* 8226 is a bullet */
+ 8226, 8226, 8218, 402, 8222, 8230, 8224, 8225,
+ 710, 8240, 352, 8249, 338, 8226, 8226, 8226,
+ 8226, 8216, 8217, 8220, 8221, 8226, 8211, 8212,
+ 732, 8482, 353, 8250, 339, 8226, 8226, 376
+};
+
+char *
+tcs(char *cs, char *s, long *np)
+{
+ Channel *sync;
+ Exec *e;
+ Rune r;
+ long i, n;
+ void **a;
+ uchar *us;
+ char buf[BUFSIZE], cmd[50];
+ char *t, *u;
+ int p[2], q[2];
+
+
+ if(s==nil || *s=='\0' || *np==0){
+ werrstr("tcs failed: no data");
+ return s;
+ }
+
+ if(cs == nil){
+ werrstr("tcs failed: no charset");
+ return s;
+ }
+
+ if(cistrncmp(cs, "utf-8", 5)==0 || cistrncmp(cs, "utf8", 4)==0)
+ return s;
+
+ for(i=0; tcstab[i].mime!=nil; i++)
+ if(cistrncmp(cs, tcstab[i].mime, strlen(tcstab[i].mime)) == 0)
+ break;
+
+ if(tcstab[i].mime == nil){
+ fprint(2, "abaco: charset: %s not supported\n", cs);
+ goto latin1;
+ }
+ if(cistrcmp(tcstab[i].tcs, "8859-1")==0 || cistrcmp(tcstab[i].tcs, "ascii")==0){
+latin1:
+ n = 0;
+ for(us=(uchar*)s; *us; us++)
+ n += runelen(*us);
+ n++;
+ t = emalloc(n);
+ for(us=(uchar*)s, u=t; *us; us++){
+ if(*us>=Winstart && *us<=Winend)
+ *u++ = winchars[*us-Winstart];
+ else{
+ r = *us;
+ u += runetochar(u, &r);
+ }
+ }
+ *u = 0;
+ free(s);
+ return t;
+ }
+
+ if(pipe(p)<0 || pipe(q)<0)
+ error("can't create pipe");
+
+ sync = chancreate(sizeof(ulong), 0);
+ if(sync == nil)
+ error("can't create channel");
+
+ snprint(cmd, sizeof cmd, "tcs -f %s", tcstab[i].tcs);
+ e = emalloc(sizeof(Exec));
+ e->p[0] = p[0];
+ e->p[1] = p[1];
+ e->q[0] = q[0];
+ e->q[1] = q[1];
+ e->cmd = cmd;
+ e->sync = sync;
+ proccreate(execproc, e, STACK);
+ recvul(sync);
+ chanfree(sync);
+ close(p[0]);
+ close(q[1]);
+
+ /* in case tcs fails */
+ t = s;
+ sync = chancreate(sizeof(ulong), 0);
+ if(sync == nil)
+ error("can't create channel");
+
+ a = emalloc(4*sizeof(void *));
+ a[0] = sync;
+ a[1] = (void *)p[1];
+ a[2] = s;
+ a[3] = (void *)*np;
+ proccreate(writeproc, a, STACK);
+
+ s = nil;
+ while((n = read(q[0], buf, sizeof(buf))) > 0){
+ s = erealloc(s, i+n+1);
+ memmove(s+i, buf, n);
+ i += n;
+ s[i] = '\0';
+ }
+ n = recvul(sync);
+ if(n != *np)
+ fprint(2, "tcs: did not write %ld; wrote %uld\n", *np, n);
+
+ *np = i;
+ chanfree(sync);
+ close(q[0]);
+
+ if(s == nil){
+ fprint(2, "tcs failed: can't convert charset=%s to %s\n", cs, tcstab[i].tcs);
+ return t;
+ }
+ free(t);
+
+ return s;
+}
+
+static
+int
+isspace(char c)
+{
+ return c==' ' || c== '\t' || c=='\r' || c=='\n';
+}
+
+static
+int
+findctype(char *b, int l, char *keyword, char *s)
+{
+ char *p, *e;
+ int i;
+
+ p = cistrstr(s, keyword);
+ if(!p)
+ return -1;
+ p += strlen(keyword);
+ while(*p && isspace(*p))
+ p++;
+ if(*p != '=')
+ return -1;
+ p++;
+ while(*p && isspace(*p))
+ p++;
+ if(!*p)
+ return -1;
+ if(*p == '"'){
+ p++;
+ e = strchr(p, '"');
+ if(!e)
+ return -1;
+ }else
+ for(e = p; *e < 127 && *e > ' ' ; e++)
+ ;
+ i = e-p;
+ if(i < 1)
+ return -1;
+ snprint(b, l, "%.*s", i, p);
+ return 0;
+}
+
+static
+int
+finddocctype(char *b, int l, char *s)
+{
+ char *p, *e;
+
+ p = cistrstr(s, "<meta");
+ if(!p)
+ return -1;
+ p += 5;
+ e = strchr(s, '>');
+ if(!e)
+ return -1;
+ snprint(b, l, "%.*s", (int)(e-p), p);
+ return 0;
+}
+
+static
+int
+findxmltype(char *b, int l, char *s)
+{
+ char *p, *e;
+
+ p = cistrstr(s, "<?xml ");
+ if(!p)
+ return -1;
+
+ p += 6;
+ e = strstr(p, "?>");
+ if(!e)
+ return -1;
+ snprint(b, l, "%.*s", (int)(e-p), p);
+
+ return 0;
+}
+
+/*
+ * servers can lie about lie about the charset,
+ * so we use the charset based on the priority.
+ */
+char *
+convert(Runestr ctype, char *s, long *np)
+{
+ char t[25], buf[256];
+
+ *t = '\0';
+ if(ctype.nr){
+ snprint(buf, sizeof(buf), "%.*S", ctype.nr, ctype.r);
+ findctype(t, sizeof(t), "charset", buf);
+ }
+ if(findxmltype(buf, sizeof(buf), s)==0)
+ findctype(t, sizeof(t), "encoding", buf);
+ if(finddocctype(buf, sizeof(buf), s) == 0)
+ findctype(t, sizeof(t), "charset", buf);
+
+ if(*t == '\0')
+ strcpy(t, charset);
+ return tcs(t, s, np);
+}
+
+int
+xtofchar(Rune *s, Font *f, long p)
+{
+ Rune *r;
+ int q;
+
+ if(p == 0)
+ return 0;
+
+ q = 0;
+ for(r=s; *r!=L'\0'; r++){
+ p -= runestringnwidth(f, r, 1);
+ if(p < 0)
+ break;
+ q++;
+ }
+ return q;
+}
+
+int
+istextsel(Page *p, Rectangle r, int *q0, int *q1, Rune *s, Font *f)
+{
+ int topinr, botinr;
+
+ *q0 = *q1 = 0;
+ topinr= ptinrect(p->top, r);
+ if(topinr || (r.min.y>p->top.y && r.max.y<p->bot.y))
+ p->selecting = TRUE;
+ botinr = ptinrect(p->bot, r);
+ if(botinr || r.min.y>p->bot.y)
+ p->selecting = FALSE;
+
+ if(topinr || botinr){
+ if(topinr)
+ *q0 = xtofchar(s, f, p->top.x-r.min.x);
+ if(botinr)
+ *q1 = xtofchar(s, f, p->bot.x-r.min.x);
+ if(*q0!=0 || *q1!=0)
+ return TRUE;
+ }
+ return p->selecting;
+}
+
+Point
+getpt(Page *p, Point xy)
+{
+ xy.x = xy.x-p->r.min.x+p->pos.x;
+ xy.y = xy.y-p->r.min.y+p->pos.y;
+
+ return xy;
+}
+
+void
+getimage(Cimage *ci, Rune *altr)
+{
+ Rectangle r;
+ Memimage *mi;
+ Image *i, *i2;
+ char buf[128];
+ uchar *bits;
+ int nbits;
+
+ mi = ci->mi;
+ if(mi == nil){
+ snprint(buf, sizeof(buf), "[%S]", altr ? altr : L"IMG");
+ r.min = Pt(0, 0);
+ r.max.x = 2*Space + stringwidth(font, buf);
+ r.max.y = 2*Space + font->height;
+ ci->i = eallocimage(display, r, GREY1, 1, DBlack);
+ r.min.x += Space;
+ r.min.y += Space;
+ string(ci->i, r.min, display->white, ZP, font, buf);
+ return;
+ }
+ nbits = bytesperline(mi->r, mi->depth)*Dy(mi->r);
+ bits = emalloc(nbits);
+ unloadmemimage(mi, mi->r, bits, nbits);
+/*
+ /* get rid of alpha channel from transparent gif * /
+
+ if(mi->depth == 16){
+ for(y=1; y<nbits; y+=2)
+ bits[y>>1] = bits[y];
+ }
+*/
+ i = eallocimage(display, mi->r, mi->chan, 0, DNofill);
+ loadimage(i, i->r, bits, nbits);
+ i2 = eallocimage(display, i->r, RGB24, 1, DNofill);
+ draw(i2, i2->r, display->black, nil, ZP);
+ draw(i2, i2->r, i, nil, i->r.min);
+ free(bits);
+ freememimage(mi);
+ freeimage(i);
+ ci->i = i2;
+ ci->mi = nil;
+}
+
+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;
+ }
+}
+
+void
+fixtext(Page *p)
+{
+ Tablecell *c;
+ Table *t;
+
+ fixtext1(&p->items);
+ for(t=p->doc->tables; t!=nil; t=t->next)
+ for(c=t->cells; c!=nil; c=c->next)
+ fixtext1(&c->content);
+}
+
+typedef struct Refresh Refresh;
+
+struct Refresh
+{
+ Page *p;
+ Refresh *next;
+};
+
+static Refresh *refreshs = nil;
+static QLock refreshlock;
+
+void
+addrefresh(Page *p, char *fmt, ...)
+{
+ Refresh *r;
+ Rune *s;
+ va_list arg;
+
+ if(p->aborting)
+ return;
+
+ va_start(arg, fmt);
+ s = runevsmprint(fmt, arg);
+ va_end(arg);
+ if(s == nil)
+ error("runevsmprint failed");
+
+ if(p->status){
+ free(p->status);
+ p->status = nil;
+ }
+ p->status = s;
+ qlock(&refreshlock);
+ for(r=refreshs; r!=nil; r=r->next)
+ if(r->p == p)
+ goto Return;
+
+ incref(p->w); /* flushrefresh will decref */
+ r = emalloc(sizeof(Refresh));
+ r->p = p;
+ r->next = refreshs;
+ refreshs = r;
+
+ Return:
+ nbsendp(crefresh, nil);
+ qunlock(&refreshlock);
+}
+
+/* called while row is locked */
+void
+flushrefresh(void)
+{
+ Refresh *r, *next;
+ Page *p;
+
+ qlock(&refreshlock);
+ for(r=refreshs; r!=nil; r=next){
+ p = r->p;
+ if(p->changed==TRUE && p->aborting==FALSE){
+ p->changed = FALSE;
+ if(p->parent==nil || p->loading==FALSE)
+ pagerender(p);
+ if(!p->refresh.t)
+ pagesetrefresh(p);
+ }
+ if(p->status){
+ winsetstatus(p->w, p->status);
+ free(p->status);
+ p->status = nil;
+ }
+ winseturl(p->w);
+ winsettag(p->w);
+ decref(p->w);
+ next = r->next;
+ free(r);
+ }
+ refreshs = nil;
+ qunlock(&refreshlock);
+}
+
+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;
+}
+
+/*
+ * Heuristic city.
+ */
+Window*
+makenewwindow(Page *p)
+{
+ Column *c;
+ Window *w, *bigw, *emptyw;
+ Page *emptyp;
+ int i, y, el;
+
+ if(activecol)
+ c = activecol;
+ else if(selpage && selpage->col)
+ c = selpage->col;
+ else if(p && p->col)
+ c = p->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(p==nil || p->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(Dy(w->page.all) >= Dy(bigw->page.all))
+ bigw = w;
+ if(w->page.lay==nil && Dy(w->page.all) >= Dy(emptyw->page.all))
+ emptyw = w;
+ }
+ emptyp = &emptyw->page;
+ el = Dy(emptyp->all);
+ /* if empty space is big, use it */
+ if(el>15 || (el>3 && el>(Dy(bigw->page.all)-1)/2))
+ y = emptyp->all.max.y;
+ else{
+ /* if this window is in column and isn't much smaller, split it */
+ if(p->col==c && Dy(p->w->r)>2*Dy(bigw->r)/3)
+ bigw = p->w;
+ y = (bigw->r.min.y + bigw->r.max.y)/2;
+ }
+ w = coladd(c, nil, nil, y);
+ colgrow(w->col, w, 1);
+ return w;
+}