]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/abaco/util.c
abacos tcs.h is automatically generated
[plan9front.git] / sys / src / cmd / abaco / util.c
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
21 min(int a, int b)
22 {
23         if(a < b)
24                 return a;
25         return b;
26 }
27
28 int
29 max(int a, int b)
30 {
31         if(a > b)
32                 return a;
33         return b;
34 }
35
36 void
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
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
84 error(char *s)
85 {
86         fprint(2, "abaco: %s: %r\n", s);
87 //      abort();
88         threadexitsall(s);
89 }
90
91 void*
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*
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*
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*
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
141 runestreq(Runestr a, Runestr b)
142 {
143         return runeeq(a.r, a.nr, b.r, b.nr);
144 }
145
146 int
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
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
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
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*
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*
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
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
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
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
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 *
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 *
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 *
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
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
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
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
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 *
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 *
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
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
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
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*
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
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
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
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
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 = (int)a[1];
703         s = a[2];
704         np =(long)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 *
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
860 isspace(char c)
861 {
862         return c==' ' || c== '\t' || c=='\r' || c=='\n';
863 }
864
865 static
866 int
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
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
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 *
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
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
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
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
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
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
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
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
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
1222 savemouse(Window *w)
1223 {
1224         prevmouse = mouse->xy;
1225         mousew = w;
1226 }
1227
1228 void
1229 restoremouse(Window *w)
1230 {
1231         if(mousew!=nil && mousew==w)
1232                 moveto(mousectl, prevmouse);
1233         mousew = nil;
1234 }
1235
1236 void
1237 clearmouse()
1238 {
1239         mousew = nil;
1240 }
1241
1242 /*
1243  * Heuristic city.
1244  */
1245 Window*
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 }