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