xref: /plan9/sys/src/cmd/abaco/util.c (revision 6872394c4d4bc26251e04b982c57150852a1ce91)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <memdraw.h>
6 #include <thread.h>
7 #include <cursor.h>
8 #include <mouse.h>
9 #include <keyboard.h>
10 #include <frame.h>
11 #include <plumb.h>
12 #include <html.h>
13 #include <regexp.h>
14 #include "dat.h"
15 #include "fns.h"
16 
17 static	Point		prevmouse;
18 static	Window	*mousew;
19 
20 int
min(int a,int b)21 min(int a, int b)
22 {
23 	if(a < b)
24 		return a;
25 	return b;
26 }
27 
28 int
max(int a,int b)29 max(int a, int b)
30 {
31 	if(a > b)
32 		return a;
33 	return b;
34 }
35 
36 void
cvttorunes(char * p,int n,Rune * r,int * nb,int * nr,int * nulls)37 cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
38 {
39 	uchar *q;
40 	Rune *s;
41 	int j, w;
42 
43 	/*
44 	 * Always guaranteed that n bytes may be interpreted
45 	 * without worrying about partial runes.  This may mean
46 	 * reading up to UTFmax-1 more bytes than n; the caller
47 	 * knows this.  If n is a firm limit, the caller should
48 	 * set p[n] = 0.
49 	 */
50 	q = (uchar*)p;
51 	s = r;
52 	for(j=0; j<n; j+=w){
53 		if(*q < Runeself){
54 			w = 1;
55 			*s = *q++;
56 		}else{
57 			w = chartorune(s, (char*)q);
58 			q += w;
59 		}
60 		if(*s)
61 			s++;
62 		else if(nulls)
63 			*nulls = TRUE;
64 	}
65 	*nb = (char*)q-p;
66 	*nr = s-r;
67 }
68 
69 void
bytetorunestr(char * s,Runestr * rs)70 bytetorunestr(char *s, Runestr *rs)
71 {
72 	Rune *r;
73 	int nb, nr;
74 
75 	nb = strlen(s);
76 	r = runemalloc(nb+1);
77 	cvttorunes(s, nb, r, &nb, &nr, nil);
78 	r[nr] = '\0';
79 	rs->nr = nr;
80 	rs->r = r;
81 }
82 
83 void
error(char * s)84 error(char *s)
85 {
86 	fprint(2, "abaco: %s: %r\n", s);
87 //	abort();
88 	threadexitsall(s);
89 }
90 
91 void*
emalloc(ulong n)92 emalloc(ulong n)
93 {
94 	void *p;
95 
96 	p = malloc(n);
97 	if(p == nil)
98 		error("malloc failed");
99 	setmalloctag(p, getcallerpc(&n));
100 	memset(p, 0, n);
101 	return p;
102 }
103 
104 void*
erealloc(void * p,ulong n)105 erealloc(void *p, ulong n)
106 {
107 	p = realloc(p, n);
108 	if(p == nil)
109 		error("realloc failed");
110 	setmalloctag(p, getcallerpc(&n));
111 	return p;
112 }
113 
114 Rune*
erunestrdup(Rune * r)115 erunestrdup(Rune *r)
116 {
117 	void *p;
118 
119 	if(r == nil)
120 		return nil;
121 	p = runestrdup(r);
122 	if(p == nil)
123 		error("runestrdup failed");
124 	setmalloctag(p, getcallerpc(&r));
125 	return p;
126 }
127 
128 char*
estrdup(char * s)129 estrdup(char *s)
130 {
131 	char *t;
132 
133 	t = strdup(s);
134 	if(t == nil)
135 		error("strdup failed");
136 	setmalloctag(t, getcallerpc(&s));
137 	return t;
138 }
139 
140 int
runestreq(Runestr a,Runestr b)141 runestreq(Runestr a, Runestr b)
142 {
143 	return runeeq(a.r, a.nr, b.r, b.nr);
144 }
145 
146 int
runeeq(Rune * s1,uint n1,Rune * s2,uint n2)147 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
148 {
149 	if(n1 != n2)
150 		return FALSE;
151 	return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
152 }
153 
154 void
closerunestr(Runestr * rs)155 closerunestr(Runestr *rs)
156 {
157 
158 	rs->nr = 0;
159 	if(rs->r)
160 		free(rs->r);
161 	rs->r = nil;
162 }
163 
164 void
copyrunestr(Runestr * a,Runestr * b)165 copyrunestr(Runestr *a, Runestr *b)
166 {
167 	closerunestr(a);
168 	a->r = runemalloc(b->nr+1);
169 	runemove(a->r, b->r, b->nr);
170 	a->r[b->nr] = 0;
171 	a->nr = b->nr;
172 }
173 
174 int
isalnum(Rune c)175 isalnum(Rune c)
176 {
177 	/*
178 	 * Hard to get absolutely right.  Use what we know about ASCII
179 	 * and assume anything above the Latin control characters is
180 	 * potentially an alphanumeric.
181 	 */
182 	if(c <= ' ')
183 		return FALSE;
184 	if(0x7F<=c && c<=0xA0)
185 		return FALSE;
186 	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
187 		return FALSE;
188 	return TRUE;
189 }
190 
191 Rune*
skipbl(Rune * r,int n,int * np)192 skipbl(Rune *r, int n, int *np)
193 {
194 	while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
195 		--n;
196 		r++;
197 	}
198 	*np = n;
199 	return r;
200 }
201 
202 Rune*
findbl(Rune * r,int n,int * np)203 findbl(Rune *r, int n, int *np)
204 {
205 	while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
206 		--n;
207 		r++;
208 	}
209 	*np = n;
210 	return r;
211 }
212 
213 int
istextfield(Item * i)214 istextfield(Item *i)
215 {
216 	Formfield *ff;
217 
218 	ff = ((Iformfield *)i)->formfield;
219 	if(ff->ftype==Ftext || ff->ftype==Ftextarea || ff->ftype==Fpassword)
220 		return TRUE;
221 
222 	return FALSE;
223 }
224 
225 int
forceitem(Item * i)226 forceitem(Item *i)
227 {
228 	if(i->state&IFwrap && i->tag!=Iruletag && i->tag!=Itabletag)
229 		return FALSE;
230 
231 	return TRUE;
232 }
233 
234 int
dimwidth(Dimen d,int w)235 dimwidth(Dimen d, int w)
236 {
237 	int s, k;
238 
239 	k = dimenkind(d);
240 	if(k == Dnone)
241 		return w;
242 	s = dimenspec(d);
243 	if(k == Dpixels)
244 		 w = s;
245 	else if(k==Dpercent && s<100)
246 		w = s*w/100;
247 
248 	return w;
249 }
250 
251 void
frdims(Dimen * d,int n,int t,int ** ret)252 frdims(Dimen *d, int n, int t, int **ret)
253 {
254 	int totpix, totpcnt, totrel;
255 	double spix, spcnt, relu, vd;
256 	int tt, trest, totpixrel, minrelu, i;
257 	int *x, *spec, *kind;
258 
259 	if(n == 1){
260 		*ret = x = emalloc(sizeof(int));
261 		x[0] = t;
262 		return;
263 	}
264 	totpix = totpcnt = totrel = 0;
265 	spec = emalloc(n*sizeof(int));
266 	kind = emalloc(n*sizeof(int));
267 	for(i=0; i<n; i++){
268 		spec[i] = dimenspec(d[i]);
269 		if(spec[i] < 0)
270 			spec[i] = 0;
271 		kind[i] = dimenkind(d[i]);
272 		switch(kind[i]){
273 		case Dpixels:
274 			totpix += spec[i];
275 			break;
276 		case Dpercent:
277 			totpcnt += spec[i];
278 			break;
279 		case Drelative:
280 			totrel += spec[i];
281 			break;
282 		case Dnone:
283 			totrel++;
284 			break;
285 		}
286 	}
287 	spix = spcnt = 1.0;
288 	minrelu = 0;
289 	if(totrel > 0)
290 		minrelu = Scrollsize+Scrollgap;
291 	relu = (double)minrelu;
292 	tt = totpix + t*totpcnt/100 + totrel*minrelu;
293 	if(tt < t){
294 		if(totrel == 0){
295 			if(totpcnt != 0)
296 				spcnt = (double)((t-totpix)*100)/(double)(t*totpcnt);
297 			else
298 				spix = (double)t/(double)totpix;
299 		}else
300 			relu += (double)(t-tt)/(double)totrel;
301 	}else{
302 		totpixrel = totpix + totrel*minrelu;
303 		if(totpixrel < t)
304 			spcnt = (double)((t-totpixrel)*100)/(double)(t*totpcnt);
305 		else{
306 			trest = t - totrel*minrelu;
307 			if(trest > 0)
308 				spcnt = (double)trest/(double)(totpix + (t*totpcnt/100));
309 			else{
310 				spcnt = (double)t/(double)tt;
311 				relu = 0.0;
312 			}
313 			spix = spcnt;
314 		}
315 	}
316 	x = emalloc(n * sizeof(int));
317 	tt = 0;
318 	for(i=0; i<n-1; i++){
319 		vd = (double)spec[i];
320 		switch(kind[i]){
321 		case Dpixels:
322 			vd = vd*spix;
323 			break;
324 		case Dpercent:
325 			vd = vd*(double)t*spcnt/100.0;
326 			break;
327 		case Drelative:
328 			vd = vd*relu;
329 			break;
330 		case Dnone:
331 			vd = relu;
332 			break;
333 		}
334 		x[i] = (int)(vd+.5);
335 		tt += x[i];
336 	}
337 	x[n - 1] = t - tt;
338 	*ret = x;
339 	free(spec);
340 	free(kind);
341 }
342 
343 Image *
getbg(Page * p)344 getbg(Page *p)
345 {
346 	Docinfo *d;
347 	Cimage *ci;
348 	Image *bg;
349 
350 	d = p->doc;
351 	if(d->backgrounditem){
352 		if(d->backgrounditem->aux){
353 			ci = d->backgrounditem->aux;
354 			if(ci->mi)
355 				getimage(ci, d->backgrounditem->altrep);
356 			bg = ci->i;
357 		}else
358 			bg = display->white;
359 	}else
360 		bg = getcolor(d->background.color);
361 
362 	return bg;
363 }
364 
365 Rune *
getbase(Page * p)366 getbase(Page *p)
367 {
368 	if(p->doc)
369 		return p->doc->base;
370 	if(p->url->act.r)
371 		return p->url->act.r;
372 	return p->url->src.r;
373 }
374 
375 Image *
eallocimage(Display * d,Rectangle r,ulong chan,int repl,int col)376 eallocimage(Display *d, Rectangle r, ulong chan, int repl, int col)
377 {
378 	Image *i;
379 
380 	i = allocimage(d, r, chan, repl, col);
381 	if(i == nil)
382 		error("allocimage failed");
383 	return i;
384 }
385 
386 void
rect3d(Image * im,Rectangle r,int i,Image ** c,Point sp)387 rect3d(Image *im, Rectangle r, int i, Image **c, Point sp)
388 {
389 	Point p[6];
390 
391 	if(i < 0){
392 		r = insetrect(r, i);
393 		sp = addpt(sp, Pt(i,i));
394 		i = -i;
395 	}
396 	draw(im, Rect(r.min.x+i, r.min.y+i, r.max.x-i, r.max.y-i), c[2], nil, sp);
397 	p[0] = r.min;
398 	p[1] = Pt(r.min.x, r.max.y);
399 	p[2] = Pt(r.min.x+i, r.max.y-i);
400 	p[3] = Pt(r.min.x+i, r.min.y+i);
401 	p[4] = Pt(r.max.x-i, r.min.y+i);
402 	p[5] = Pt(r.max.x, r.min.y);
403 	fillpoly(im, p, 6, 0, c[0], sp);
404 	p[0] = r.max;
405 	p[1] = Pt(r.min.x, r.max.y);
406 	p[2] = Pt(r.min.x+i, r.max.y-i);
407 	p[3] = Pt(r.max.x-i, r.max.y-i);
408 	p[4] = Pt(r.max.x-i, r.min.y+i);
409 	p[5] = Pt(r.max.x, r.min.y);
410 	fillpoly(im, p, 6, 0, c[1], sp);
411 }
412 
413 void
ellipse3d(Image * im,Point p,int rad,int i,Image ** c,Point sp)414 ellipse3d(Image *im, Point p, int rad, int i, Image **c, Point sp)
415 {
416 	fillarc(im, p, rad, rad, c[0], sp, 45, 180);
417 	fillarc(im, p, rad, rad, c[1], sp,  45, -180);
418 	fillellipse(im, p, rad-i, rad-i, c[2], sp);
419 }
420 
421 void
colarray(Image ** c,Image * c0,Image * c1,Image * c2,int checked)422 colarray(Image **c, Image *c0, Image *c1, Image *c2, int checked)
423 {
424 	if(checked){
425 		c[0] = c0;
426 		c[1] = c1;
427 	}else{
428 		c[0] = c1;
429 		c[1] = c0;
430 	}
431 	c[2] = c2;
432 }
433 
434 static char *deffontpaths[] = {
435 #include "fonts.h"
436 };
437 
438 static char *fontpaths[NumFnt];
439 static Font *fonts[NumFnt];
440 
441 void
initfontpaths(void)442 initfontpaths(void)
443 {
444 	Biobufhdr *bp;
445 	char buf[128];
446 	int i;
447 
448 	/* we don't care if getenv(2) fails */
449 	snprint(buf, sizeof(buf)-1, "%s/lib/abaco.fonts", getenv("home"));
450 	if((bp=Bopen(buf, OREAD)) == nil)
451 		goto Default;
452 
453 	for(i=0; i<NumFnt; i++)
454 		if((fontpaths[i]=Brdstr(bp, '\n', 1)) == nil)
455 			goto Error;
456 
457 	Bterm(bp);
458 	return;
459 Error:
460 	fprint(2, "abaco: not enough fontpaths in '%s'\n", buf);
461 	Bterm(bp);
462 	for(i--; i>=0; i--)
463 		free(fontpaths[i]);
464 Default:
465 	for(i=0; i<NumFnt; i++)
466 		fontpaths[i] = deffontpaths[i];
467 }
468 
469 Font *
getfont(int i)470 getfont(int i)
471 {
472 	if(fonts[i] == nil){
473 		fonts[i] = openfont(display, fontpaths[i]);
474 		if(fonts[i] == nil)
475 			error("can't open font file");
476 	}
477 	return fonts[i];
478 }
479 
480 typedef struct Color Color;
481 
482 struct Color {
483 	int	rgb;
484 	Image	*i;
485 	Color	*next;
486 };
487 
488 enum {
489 	NHASH = 19,
490 };
491 
492 static Color *colortab[NHASH];
493 
494 Image *
getcolor(int rgb)495 getcolor(int rgb)
496 {
497 	Color *c;
498 	int h;
499 
500 	if(rgb == 0xFFFFFF)
501 		return display->white;
502 	else if(rgb == 0x000000)
503 		return display->black;
504 
505 	h = rgb%NHASH;
506 	for(c=colortab[h]; c!=nil; c=c->next)
507 		if(c->rgb == rgb){
508 			flushimage(display, 0);	/* BUG? */
509 			return c->i;
510 		}
511 	c = emalloc(sizeof(Color));
512 	c->i = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, (rgb<<8)|0xFF);
513 	c->rgb = rgb;
514 	c->next = colortab[h];
515 	colortab[h] = c;
516 
517 	return c->i;
518 }
519 
520 int
plumbrunestr(Runestr * rs,char * attr)521 plumbrunestr(Runestr *rs, char *attr)
522 {
523 	Plumbmsg *m;
524 	int i;
525 
526 	i = -1;
527 	if(plumbsendfd >= 0){
528 		m = emalloc(sizeof(Plumbmsg));
529 		m->src = estrdup("abaco");
530 		m->dst = nil;
531 		m->wdir = estrdup("/tmp");
532 		m->type = estrdup("text");
533 		if(attr)
534 			m->attr = plumbunpackattr(attr);
535 		else
536 			m->attr = nil;
537 		m->data = smprint("%.*S", rs->nr, rs->r);
538 		m->ndata = -1;
539 		i = plumbsend(plumbsendfd, m);
540 		plumbfree(m);
541 	}
542 	return i;
543 }
544 
545 int
hexdigit(int v)546 hexdigit(int v)
547 {
548 	if(0<=v && v<=9)
549 		return '0' + v;
550 	else
551 		return 'A' + v - 10;
552 }
553 
554 static int
inclass(char c,Rune * cl)555 inclass(char c, Rune* cl)
556 {
557 	int n, ans, negate, i;
558 
559 	n = runestrlen(cl);
560 	if(n == 0)
561 		return 0;
562 	ans = 0;
563 	negate = 0;
564 	if(cl[0] == '^'){
565 		negate = 1;
566 		cl++;
567 		n--;
568 	}
569 	for(i=0; i<n; i++){
570 		if(cl[i]=='-' && i>0 && i<n-1){
571 			if(c>=cl[i - 1] && c<=cl[i+1]){
572 				ans = 1;
573 				break;
574 			}
575 			i++;
576 		}
577 		else if(c == cl[i]){
578 			ans = 1;
579 			break;
580 		}
581 	}
582 	if(negate)
583 		ans = !ans;
584 	return ans;
585 }
586 
587 Rune*
ucvt(Rune * s)588 ucvt(Rune* s)
589 {
590 	Rune* u;
591 	char *t;
592 	int i, c, n, j, len;
593 
594 	t = smprint("%S", s);
595 	n = strlen(t);
596 	len = 0;
597 	for(i=0; i<n; i++){
598 		c = t[i];
599 		if(inclass(c, L"- /$_@.!*'(),a-zA-Z0-9"))
600 			len++;
601 		else
602 			len += 3;
603 	}
604 	u = runemalloc(len+1);
605 	j = 0;
606 
607 	for(i=0; i<n; i++){
608 		c = t[i];
609 		if(inclass(c, L"-/$_@.!*'(),a-zA-Z0-9"))
610 			u[j++] = c;
611 		else if(c == ' ')
612 			u[j++] = '+';
613 		else {
614 			u[j++] = '%';
615 			u[j++] = hexdigit((c >> 4)&15);
616 			u[j++] = hexdigit(c&15);
617 		}
618 	}
619 	u[j] = 0;
620 	free(t);
621 	return u;
622 }
623 
624 void
reverseimages(Iimage ** head)625 reverseimages(Iimage **head)
626 {
627 	Iimage *r, *c, *n;
628 
629 	r = nil;
630 	for(c=*head; c!=nil; c=n){
631 		n = c->nextimage;
632 		c->nextimage = r;
633 		r = c;
634 	}
635 	*head = r;
636 }
637 
638 char urlexpr[] = "^(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|"
639 	"prospero)://([a-zA-Z0-9_@\\-]+([.:][a-zA-Z0-9_@\\-]+)*)";
640 Reprog	*urlprog;
641 
642 int
validurl(Rune * r)643 validurl(Rune *r)
644 {
645 	Resub rs[10];
646 
647 	if(urlprog == nil){
648 		urlprog = regcomp(urlexpr);
649 		if(urlprog == nil)
650 			error("regcomp");
651 	}
652 	memset(rs, 0, sizeof(rs));
653 	if(rregexec(urlprog, r, rs, nelem(rs)) == 0)
654 		return FALSE;
655 	return TRUE;
656 }
657 
658 void
execproc(void * v)659 execproc(void *v)
660 {
661 	Channel *sync;
662 	Exec *e;
663 	int p[2], q[2];
664 	char *cmd;
665 
666 	threadsetname("execproc");
667 	e = v;
668 	p[0] = e->p[0];
669 	p[1] = e->p[1];
670 	q[0] = e->q[0];
671 	q[1] = e->q[1];
672 	cmd = e->cmd;
673 	sync = e->sync;
674 	rfork(RFFDG);
675 	free(e);
676 	dup(p[0], 0);
677 	close(p[0]);
678 	close(p[1]);
679 	if(q[0]){
680 		dup(q[1], 1);
681 		close(q[0]);
682 		close(q[1]);
683 	}
684 	if(!procstderr)
685 		close(2);
686 	procexecl(sync, "/bin/rc", "rc", "-c", cmd, 0);
687 	error("can't exec");
688 }
689 
690 static void
writeproc(void * v)691 writeproc(void *v)
692 {
693 	Channel *sync;
694 	void **a;
695 	char *s;
696 	long np;
697 	int fd, i, n;
698 
699 	threadsetname("writeproc");
700 	a = v;
701 	sync = a[0];
702 	fd = (uintptr)a[1];
703 	s = a[2];
704 	np =(uintptr)a[3];
705 	free(a);
706 
707 	for(i=0; i<np; i+=n){
708 		n = np-i;
709 		if(n > BUFSIZE)
710 			n = BUFSIZE;
711 		if(write(fd, s+i, n) != n)
712 			break;
713 	}
714 	close(fd);
715 	sendul(sync, i);
716 }
717 
718 struct {
719 	char *mime;
720 	char *tcs;
721 }tcstab[] = {
722 
723 #include "tcs.h"
724 
725 	/* not generated by the script */
726 	"euc_jp", "jis",
727 	"euc_kr", "euc-k",
728 	"windows-874", "tis",
729 	nil,	nil,
730 };
731 
732 enum {
733 	Winstart = 127,
734 	Winend = 159
735 };
736 
737 static int winchars[] = {
738 	 8226,	/* 8226 is a bullet */
739 	8226, 8226, 8218, 402, 8222, 8230, 8224, 8225,
740 	710, 8240, 352, 8249, 338, 8226, 8226, 8226,
741 	8226, 8216, 8217, 8220, 8221, 8226, 8211, 8212,
742 	732, 8482, 353, 8250, 339, 8226, 8226, 376
743 };
744 
745 char *
tcs(char * cs,char * s,long * np)746 tcs(char *cs, char *s, long *np)
747 {
748 	Channel *sync;
749 	Exec *e;
750 	Rune r;
751 	long i, n;
752 	void **a;
753 	uchar *us;
754 	char buf[BUFSIZE], cmd[50];
755 	char *t, *u;
756 	int p[2], q[2];
757 
758 
759 	if(s==nil || *s=='\0' || *np==0){
760 		werrstr("tcs failed: no data");
761 		return s;
762 	}
763 
764 	if(cs == nil){
765 		werrstr("tcs failed: no charset");
766 		return s;
767 	}
768 
769 	if(cistrncmp(cs, "utf-8", 5)==0 || cistrncmp(cs, "utf8", 4)==0)
770 		return s;
771 
772 	for(i=0; tcstab[i].mime!=nil; i++)
773 		if(cistrncmp(cs, tcstab[i].mime, strlen(tcstab[i].mime)) == 0)
774 			break;
775 
776 	if(tcstab[i].mime == nil){
777 		fprint(2, "abaco: charset: %s not supported\n", cs);
778 		goto latin1;
779 	}
780 	if(cistrcmp(tcstab[i].tcs, "8859-1")==0 || cistrcmp(tcstab[i].tcs, "ascii")==0){
781 latin1:
782 		n = 0;
783 		for(us=(uchar*)s; *us; us++)
784 			n += runelen(*us);
785 		n++;
786 		t = emalloc(n);
787 		for(us=(uchar*)s, u=t; *us; us++){
788 			if(*us>=Winstart && *us<=Winend)
789 				*u++ = winchars[*us-Winstart];
790 			else{
791 				r = *us;
792 				u += runetochar(u, &r);
793 			}
794 		}
795 		*u = 0;
796 		free(s);
797 		return t;
798 	}
799 
800 	if(pipe(p)<0 || pipe(q)<0)
801 		error("can't create pipe");
802 
803 	sync = chancreate(sizeof(ulong), 0);
804 	if(sync == nil)
805 		error("can't create channel");
806 
807 	snprint(cmd, sizeof cmd, "tcs -f %s", tcstab[i].tcs);
808 	e = emalloc(sizeof(Exec));
809 	e->p[0] = p[0];
810 	e->p[1] = p[1];
811 	e->q[0] = q[0];
812 	e->q[1] = q[1];
813 	e->cmd = cmd;
814 	e->sync = sync;
815 	proccreate(execproc, e, STACK);
816 	recvul(sync);
817 	chanfree(sync);
818 	close(p[0]);
819 	close(q[1]);
820 
821 	/* in case tcs fails */
822 	t = s;
823 	sync = chancreate(sizeof(ulong), 0);
824 	if(sync == nil)
825 		error("can't create channel");
826 
827 	a = emalloc(4*sizeof(void *));
828 	a[0] = sync;
829 	a[1] = (void *)p[1];
830 	a[2] = s;
831 	a[3] = (void *)*np;
832 	proccreate(writeproc, a, STACK);
833 
834 	s = nil;
835 	while((n = read(q[0], buf, sizeof(buf))) > 0){
836 		s = erealloc(s, i+n+1);
837 		memmove(s+i, buf, n);
838 		i += n;
839 		s[i] = '\0';
840 	}
841 	n = recvul(sync);
842 	if(n != *np)
843 		fprint(2, "tcs: did not write %ld; wrote %uld\n", *np, n);
844 
845 	*np = i;
846 	chanfree(sync);
847 	close(q[0]);
848 
849 	if(s == nil){
850 		fprint(2, "tcs failed: can't convert charset=%s to %s\n", cs, tcstab[i].tcs);
851 		return t;
852 	}
853 	free(t);
854 
855 	return s;
856 }
857 
858 static
859 int
isspace(char c)860 isspace(char c)
861 {
862 	return c==' ' || c== '\t' || c=='\r' || c=='\n';
863 }
864 
865 static
866 int
findctype(char * b,int l,char * keyword,char * s)867 findctype(char *b, int l, char *keyword, char *s)
868 {
869 	char *p, *e;
870 	int i;
871 
872 	p = cistrstr(s, keyword);
873 	if(!p)
874 		return -1;
875 	p += strlen(keyword);
876 	while(*p && isspace(*p))
877 		p++;
878 	if(*p != '=')
879 		return -1;
880 	p++;
881 	while(*p && isspace(*p))
882 		p++;
883 	if(!*p)
884 		return -1;
885 	if(*p == '"'){
886 		p++;
887 		e = strchr(p, '"');
888 		if(!e)
889 			return -1;
890 	}else
891 		for(e = p; *e < 127 && *e > ' ' ; e++)
892 			;
893 	i = e-p;
894 	if(i < 1)
895 		return -1;
896 	snprint(b, l, "%.*s", i, p);
897 	return 0;
898 }
899 
900 static
901 int
finddocctype(char * b,int l,char * s)902 finddocctype(char *b, int l, char *s)
903 {
904 	char *p, *e;
905 
906 	p = cistrstr(s, "<meta");
907 	if(!p)
908 		return -1;
909 	p += 5;
910 	e = strchr(s, '>');
911 	if(!e)
912 		return -1;
913 	snprint(b, l, "%.*s", (int)(e-p), p);
914 	return 0;
915 }
916 
917 static
918 int
findxmltype(char * b,int l,char * s)919 findxmltype(char *b, int l, char *s)
920 {
921 	char *p, *e;
922 
923 	p = cistrstr(s, "<?xml ");
924 	if(!p)
925 		return -1;
926 
927 	p += 6;
928 	e = strstr(p, "?>");
929 	if(!e)
930 		return -1;
931 	snprint(b, l, "%.*s", (int)(e-p), p);
932 
933 	return 0;
934 }
935 
936 /*
937  * servers can lie about lie about the charset,
938  * so we use the charset based on the priority.
939  */
940 char *
convert(Runestr ctype,char * s,long * np)941 convert(Runestr ctype, char *s, long *np)
942 {
943 	char t[25], buf[256];
944 
945 	*t = '\0';
946 	if(ctype.nr){
947 		snprint(buf, sizeof(buf), "%.*S", ctype.nr, ctype.r);
948 		findctype(t, sizeof(t), "charset", buf);
949 	}
950 	if(findxmltype(buf, sizeof(buf), s)==0)
951 		findctype(t, sizeof(t), "encoding", buf);
952 	if(finddocctype(buf, sizeof(buf), s) == 0)
953 		findctype(t, sizeof(t), "charset", buf);
954 
955 	if(*t == '\0')
956 		strcpy(t, charset);
957 	return tcs(t, s, np);
958 }
959 
960 int
xtofchar(Rune * s,Font * f,long p)961 xtofchar(Rune *s, Font *f, long p)
962 {
963 	Rune *r;
964 	int q;
965 
966 	if(p == 0)
967 		return 0;
968 
969 	q = 0;
970 	for(r=s; *r!=L'\0'; r++){
971 		p -= runestringnwidth(f, r, 1);
972 		if(p < 0)
973 			break;
974 		q++;
975 	}
976 	return q;
977 }
978 
979 int
istextsel(Page * p,Rectangle r,int * q0,int * q1,Rune * s,Font * f)980 istextsel(Page *p, Rectangle r, int *q0, int *q1, Rune *s, Font *f)
981 {
982 	int topinr, botinr;
983 
984 	*q0 = *q1 = 0;
985 	topinr= ptinrect(p->top, r);
986 	if(topinr || (r.min.y>p->top.y && r.max.y<p->bot.y))
987 		p->selecting = TRUE;
988 	botinr = ptinrect(p->bot, r);
989 	if(botinr || r.min.y>p->bot.y)
990 		p->selecting = FALSE;
991 
992 	if(topinr || botinr){
993 		if(topinr)
994 			*q0 = xtofchar(s, f, p->top.x-r.min.x);
995 		if(botinr)
996 			*q1 = xtofchar(s, f, p->bot.x-r.min.x);
997 		if(*q0!=0 || *q1!=0)
998 			return TRUE;
999 	}
1000 	return p->selecting;
1001 }
1002 
1003 Point
getpt(Page * p,Point xy)1004 getpt(Page *p, Point xy)
1005 {
1006 	xy.x = xy.x-p->r.min.x+p->pos.x;
1007 	xy.y = xy.y-p->r.min.y+p->pos.y;
1008 
1009 	return xy;
1010 }
1011 
1012 void
getimage(Cimage * ci,Rune * altr)1013 getimage(Cimage *ci, Rune *altr)
1014 {
1015 	Rectangle r;
1016 	Memimage *mi;
1017 	Image *i, *i2;
1018 	char buf[128];
1019 	uchar *bits;
1020 	int nbits;
1021 
1022 	mi = ci->mi;
1023 	if(mi == nil){
1024 		snprint(buf, sizeof(buf), "[%S]", altr ? altr : L"IMG");
1025 		r.min = Pt(0, 0);
1026 		r.max.x = 2*Space + stringwidth(font, buf);
1027 		r.max.y = 2*Space + font->height;
1028 		ci->i = eallocimage(display, r, GREY1, 1, DBlack);
1029 		r.min.x += Space;
1030 		r.min.y += Space;
1031 		string(ci->i, r.min, display->white, ZP, font, buf);
1032 		return;
1033 	}
1034 	nbits = bytesperline(mi->r, mi->depth)*Dy(mi->r);
1035 	bits = emalloc(nbits);
1036 	unloadmemimage(mi, mi->r, bits, nbits);
1037 /*
1038 	/* get rid of alpha channel from transparent gif * /
1039 
1040 	if(mi->depth == 16){
1041 		for(y=1; y<nbits; y+=2)
1042 			bits[y>>1] = bits[y];
1043 	}
1044 */
1045 	i = eallocimage(display, mi->r, mi->chan, 0, DNofill);
1046 	loadimage(i, i->r, bits, nbits);
1047 	i2 = eallocimage(display, i->r, RGB24, 1, DNofill);
1048 	draw(i2, i2->r, display->black, nil, ZP);
1049 	draw(i2, i2->r, i, nil, i->r.min);
1050 	free(bits);
1051 	freememimage(mi);
1052 	freeimage(i);
1053 	ci->i = i2;
1054 	ci->mi = nil;
1055 }
1056 
1057 static
1058 void
fixtext1(Item ** list)1059 fixtext1(Item **list)
1060 {
1061 	Itext *text, *ntext;
1062 	Item *it, *prev;
1063 	Rune *s, *s1, *s2;
1064 	int n;
1065 
1066 	if(*list == nil)
1067 		return;
1068 
1069 	prev = nil;
1070 	for(it=*list; it!=nil; it=prev->next){
1071 		if(it->tag!=Itexttag || forceitem(it))
1072 			goto Continue;
1073 
1074 		text = (Itext *)it;
1075 		s = text->s;
1076 		while(*s && isspacerune(*s))
1077 			s++;
1078 		if(!*s){
1079 			if(prev == nil)
1080 				prev = *list = it->next;
1081 			else
1082 				prev->next = it->next;
1083 
1084 			it->next = nil;
1085 			freeitems(it);
1086 			if(prev == nil)
1087 				return;
1088 			continue;
1089 		}
1090 		n = 0;
1091 		while(s[n] && !isspacerune(s[n]))
1092 			n++;
1093 
1094 		if(!s[n])
1095 			goto Continue;
1096 
1097 		s1 = runemalloc(n+1);
1098 		s1 = runemove(s1, s, n);
1099 		s1[n] = L'\0';
1100 		s += n;
1101 
1102 		while(*s && isspacerune(*s))
1103 			s++;
1104 
1105 		if(*s){
1106 			n = runestrlen(s);
1107 			s2 = runemalloc(n+1);
1108 			runemove(s2, s, n);
1109 			s2[n] = L'\0';
1110 			ntext = emalloc(sizeof(Itext));
1111 			ntext->s = s2;
1112 			ntext->ascent = text->ascent;
1113 			ntext->anchorid = text->anchorid;
1114 			ntext->state = text->state&~(IFbrk|IFbrksp|IFnobrk|IFcleft|IFcright);
1115 			ntext->tag = text->tag;
1116 			ntext->fnt = text->fnt;
1117 			ntext->fg = text->fg;
1118 			ntext->ul = text->ul;
1119 			ntext->next = (Item *)text->next;
1120 			text->next = (Item *)ntext;
1121 		}
1122 		free(text->s);
1123 		text->s = s1;
1124     Continue:
1125 		prev = it;
1126 	}
1127 }
1128 
1129 void
fixtext(Page * p)1130 fixtext(Page *p)
1131 {
1132 	Tablecell *c;
1133 	Table *t;
1134 
1135 	fixtext1(&p->items);
1136 	for(t=p->doc->tables; t!=nil; t=t->next)
1137 		for(c=t->cells; c!=nil; c=c->next)
1138 			fixtext1(&c->content);
1139 }
1140 
1141 typedef struct Refresh Refresh;
1142 
1143 struct Refresh
1144 {
1145 	Page *p;
1146 	Refresh *next;
1147 };
1148 
1149 static Refresh *refreshs = nil;
1150 static QLock refreshlock;
1151 
1152 void
addrefresh(Page * p,char * fmt,...)1153 addrefresh(Page *p, char *fmt, ...)
1154 {
1155 	Refresh *r;
1156 	Rune *s;
1157 	va_list arg;
1158 
1159 	if(p->aborting)
1160 		return;
1161 
1162 	va_start(arg, fmt);
1163 	s = runevsmprint(fmt, arg);
1164 	va_end(arg);
1165 	if(s == nil)
1166 		error("runevsmprint failed");
1167 
1168 	if(p->status){
1169 		free(p->status);
1170 		p->status = nil;
1171 	}
1172 	p->status = s;
1173 	qlock(&refreshlock);
1174 	for(r=refreshs; r!=nil; r=r->next)
1175 		if(r->p == p)
1176 			goto Return;
1177 
1178 	incref(p->w);				/* flushrefresh will decref */
1179 	r = emalloc(sizeof(Refresh));
1180 	r->p = p;
1181 	r->next = refreshs;
1182 	refreshs = r;
1183 
1184     Return:
1185 	nbsendp(crefresh, nil);
1186 	qunlock(&refreshlock);
1187 }
1188 
1189 /* called while row is locked */
1190 void
flushrefresh(void)1191 flushrefresh(void)
1192 {
1193 	Refresh *r, *next;
1194 	Page *p;
1195 
1196 	qlock(&refreshlock);
1197 	for(r=refreshs; r!=nil; r=next){
1198 		p = r->p;
1199 		if(p->changed==TRUE && p->aborting==FALSE){
1200 			p->changed = FALSE;
1201 			if(p->parent==nil || p->loading==FALSE)
1202 				pagerender(p);
1203 			if(!p->refresh.t)
1204 				pagesetrefresh(p);
1205 		}
1206 		if(p->status){
1207 			winsetstatus(p->w, p->status);
1208 			free(p->status);
1209 			p->status = nil;
1210 		}
1211 		winseturl(p->w);
1212 		winsettag(p->w);
1213 		decref(p->w);
1214 		next = r->next;
1215 		free(r);
1216 	}
1217 	refreshs = nil;
1218 	qunlock(&refreshlock);
1219 }
1220 
1221 void
savemouse(Window * w)1222 savemouse(Window *w)
1223 {
1224 	prevmouse = mouse->xy;
1225 	mousew = w;
1226 }
1227 
1228 void
restoremouse(Window * w)1229 restoremouse(Window *w)
1230 {
1231 	if(mousew!=nil && mousew==w)
1232 		moveto(mousectl, prevmouse);
1233 	mousew = nil;
1234 }
1235 
1236 void
clearmouse()1237 clearmouse()
1238 {
1239 	mousew = nil;
1240 }
1241 
1242 /*
1243  * Heuristic city.
1244  */
1245 Window*
makenewwindow(Page * p)1246 makenewwindow(Page *p)
1247 {
1248 	Column *c;
1249 	Window *w, *bigw, *emptyw;
1250 	Page *emptyp;
1251 	int i, y, el;
1252 
1253 	if(activecol)
1254 		c = activecol;
1255 	else if(selpage && selpage->col)
1256 		c = selpage->col;
1257 	else if(p && p->col)
1258 		c = p->col;
1259 	else{
1260 		if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
1261 			error("can't make column");
1262 		c = row.col[row.ncol-1];
1263 	}
1264 	activecol = c;
1265 	if(p==nil || p->w==nil || c->nw==0)
1266 		return coladd(c, nil, nil, -1);
1267 
1268 	/* find biggest window and biggest blank spot */
1269 	emptyw = c->w[0];
1270 	bigw = emptyw;
1271 	for(i=1; i<c->nw; i++){
1272 		w = c->w[i];
1273 		/* use >= to choose one near bottom of screen */
1274 		if(Dy(w->page.all) >= Dy(bigw->page.all))
1275 			bigw = w;
1276 		if(w->page.lay==nil && Dy(w->page.all) >= Dy(emptyw->page.all))
1277 			emptyw = w;
1278 	}
1279 	emptyp = &emptyw->page;
1280 	el = Dy(emptyp->all);
1281 	/* if empty space is big, use it */
1282 	if(el>15 || (el>3 && el>(Dy(bigw->page.all)-1)/2))
1283 		y = emptyp->all.max.y;
1284 	else{
1285 		/* if this window is in column and isn't much smaller, split it */
1286 		if(p->col==c && Dy(p->w->r)>2*Dy(bigw->r)/3)
1287 			bigw = p->w;
1288 		y = (bigw->r.min.y + bigw->r.max.y)/2;
1289 	}
1290 	w = coladd(c, nil, nil, y);
1291 	colgrow(w->col, w, 1);
1292 	return w;
1293 }
1294