]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/abaco/util.c
awk: make empty FS unicodely-correct.
[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         char *s;
447         int i;
448
449         /* we don't care if getenv(2) fails */
450         s = getenv("home");
451         snprint(buf, sizeof(buf)-1, "%s/lib/abaco.fonts", s);
452         free(s);
453         if((bp=Bopen(buf, OREAD)) == nil)
454                 goto Default;
455
456         for(i=0; i<NumFnt; i++)
457                 if((fontpaths[i]=Brdstr(bp, '\n', 1)) == nil)
458                         goto Error;
459
460         Bterm(bp);
461         return;
462 Error:
463         fprint(2, "abaco: not enough fontpaths in '%s'\n", buf);
464         Bterm(bp);
465         for(i--; i>=0; i--)
466                 free(fontpaths[i]);
467 Default:
468         for(i=0; i<NumFnt; i++)
469                 fontpaths[i] = deffontpaths[i];
470 }
471
472 Font *
473 getfont(int i)
474 {
475         if(fonts[i] == nil){
476                 fonts[i] = openfont(display, fontpaths[i]);
477                 if(fonts[i] == nil)
478                         error("can't open font file");
479         }
480         return fonts[i];
481 }
482
483 typedef struct Color Color;
484
485 struct Color {
486         int     rgb;
487         Image   *i;
488         Color   *next;
489 };
490
491 enum {
492         NHASH = 19,
493 };
494
495 static Color *colortab[NHASH];
496
497 Image *
498 getcolor(int rgb)
499 {
500         Color *c;
501         int h;
502
503         if(rgb == 0xFFFFFF)
504                 return display->white;
505         else if(rgb == 0x000000)
506                 return display->black;
507
508         h = rgb%NHASH;
509         for(c=colortab[h]; c!=nil; c=c->next)
510                 if(c->rgb == rgb){
511                         flushimage(display, 0); /* BUG? */
512                         return c->i;
513                 }
514         c = emalloc(sizeof(Color));
515         c->i = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, (rgb<<8)|0xFF);
516         c->rgb = rgb;
517         c->next = colortab[h];
518         colortab[h] = c;
519
520         return c->i;
521 }
522
523 int
524 plumbrunestr(Runestr *rs, char *attr)
525 {
526         Plumbmsg *m;
527         int i;
528
529         i = -1;
530         if(plumbsendfd >= 0){
531                 m = emalloc(sizeof(Plumbmsg));
532                 m->src = estrdup("abaco");
533                 m->dst = nil;
534                 m->wdir = estrdup("/tmp");
535                 m->type = estrdup("text");
536                 if(attr)
537                         m->attr = plumbunpackattr(attr);
538                 else
539                         m->attr = nil;
540                 m->data = smprint("%.*S", rs->nr, rs->r);
541                 m->ndata = -1;
542                 i = plumbsend(plumbsendfd, m);
543                 plumbfree(m);
544         }
545         return i;
546 }
547
548 int
549 hexdigit(int v)
550 {
551         if(0<=v && v<=9)
552                 return '0' + v;
553         else
554                 return 'A' + v - 10;
555 }
556
557 static int
558 inclass(char c, Rune* cl)
559 {
560         int n, ans, negate, i;
561
562         n = runestrlen(cl);
563         if(n == 0)
564                 return 0;
565         ans = 0;
566         negate = 0;
567         if(cl[0] == '^'){
568                 negate = 1;
569                 cl++;
570                 n--;
571         }
572         for(i=0; i<n; i++){
573                 if(cl[i]=='-' && i>0 && i<n-1){
574                         if(c>=cl[i - 1] && c<=cl[i+1]){
575                                 ans = 1;
576                                 break;
577                         }
578                         i++;
579                 }
580                 else if(c == cl[i]){
581                         ans = 1;
582                         break;
583                 }
584         }
585         if(negate)
586                 ans = !ans;
587         return ans;
588 }
589
590 Rune*
591 ucvt(Rune* s)
592 {
593         Rune* u;
594         char *t;
595         int i, c, n, j, len;
596
597         t = smprint("%S", s);
598         n = strlen(t);
599         len = 0;
600         for(i=0; i<n; i++){
601                 c = t[i];
602                 if(inclass(c, L"- /$_@.!*'(),a-zA-Z0-9"))
603                         len++;
604                 else
605                         len += 3;
606         }
607         u = runemalloc(len+1);
608         j = 0;
609
610         for(i=0; i<n; i++){
611                 c = t[i];
612                 if(inclass(c, L"-/$_@.!*'(),a-zA-Z0-9"))
613                         u[j++] = c;
614                 else if(c == ' ')
615                         u[j++] = '+';
616                 else {
617                         u[j++] = '%';
618                         u[j++] = hexdigit((c >> 4)&15);
619                         u[j++] = hexdigit(c&15);
620                 }
621         }
622         u[j] = 0;
623         free(t);
624         return u;
625 }
626
627 void
628 reverseimages(Iimage **head)
629 {
630         Iimage *r, *c, *n;
631
632         r = nil;
633         for(c=*head; c!=nil; c=n){
634                 n = c->nextimage;
635                 c->nextimage = r;
636                 r = c;
637         }
638         *head = r;
639 }
640
641 char urlexpr[] = "^(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|"
642         "prospero)://[^/]+";
643 Reprog  *urlprog;
644
645 int
646 validurl(Rune *r)
647 {
648         Resub rs[10];
649
650         if(urlprog == nil){
651                 urlprog = regcomp(urlexpr);
652                 if(urlprog == nil)
653                         error("regcomp");
654         }
655         memset(rs, 0, sizeof(rs));
656         if(rregexec(urlprog, r, rs, nelem(rs)) == 0)
657                 return FALSE;
658         return TRUE;
659 }
660
661 static void
662 execproc(void *v)
663 {
664         Exec *e;
665
666         threadsetname("execproc");
667         e = v;
668         rfork(RFFDG);
669         dup(e->p[0], 0);
670         close(e->p[0]);
671         close(e->p[1]);
672         if(e->q[0]){
673                 dup(e->q[1], 1);
674                 close(e->q[0]);
675                 close(e->q[1]);
676         }
677         if(!procstderr)
678                 close(2);
679         procexecl(e->sync, "/bin/rc", "rc", "-c", e->cmd, nil);
680         error("can't exec");
681 }
682
683 int
684 pipeline(int fd, char *cmd, ...)
685 {
686         Exec *e;
687         va_list a;
688
689         e = emalloc(sizeof(Exec));
690         if(pipe(e->p)<0 || pipe(e->q)<0)
691                 error("can't create pipe");
692         close(e->p[0]);
693         e->p[0] = fd;
694         va_start(a, cmd);
695         e->cmd = vsmprint(cmd, a);
696         va_end(a);
697         e->sync = chancreate(sizeof(ulong), 0);
698         if(e->sync == nil)
699                 error("can't create channel");
700         proccreate(execproc, e, STACK);
701         recvul(e->sync);
702         chanfree(e->sync);
703         free(e->cmd);
704         close(e->p[0]);
705         close(e->p[1]);
706         close(e->q[1]);
707         fd = e->q[0];
708         free(e);
709         return fd;
710 }
711
712 static
713 int
714 isspace(char c)
715 {
716         return c==' ' || c== '\t' || c=='\r' || c=='\n';
717 }
718
719 int
720 findctype(char *b, int l, char *keyword, char *s)
721 {
722         char *p, *e, c;
723         int i;
724
725         p = cistrstr(s, keyword);
726         if(!p)
727                 return -1;
728         p += strlen(keyword);
729         while(*p && isspace(*p))
730                 p++;
731         if(*p != '=')
732                 return -1;
733         p++;
734         while(*p && isspace(*p))
735                 p++;
736         if(!*p)
737                 return -1;
738         switch (c = *p){
739         case '"':
740         case '\'':
741                 p++;
742                 e = strchr(p, c);
743                 if(!e)
744                         return -1;
745                 break;
746         default:
747                 for(e = p; *e < 127 && *e > ' ' ; e++)
748                         ;
749         }
750         i = utfnlen(p, e - p);
751         if(i < 1)
752                 return -1;
753         snprint(b, l, "%.*s", i, p);
754         return 0;
755 }
756
757 int
758 xtofchar(Rune *s, Font *f, long p)
759 {
760         Rune *r;
761         int q;
762
763         if(p == 0)
764                 return 0;
765
766         q = 0;
767         for(r=s; *r!=L'\0'; r++){
768                 p -= runestringnwidth(f, r, 1);
769                 if(p < 0)
770                         break;
771                 q++;
772         }
773         return q;
774 }
775
776 int
777 istextsel(Page *p, Rectangle r, int *q0, int *q1, Rune *s, Font *f)
778 {
779         int topinr, botinr;
780
781         *q0 = *q1 = 0;
782         topinr= ptinrect(p->top, r);
783         if(topinr || (r.min.y>p->top.y && r.max.y<p->bot.y))
784                 p->selecting = TRUE;
785         botinr = ptinrect(p->bot, r);
786         if(botinr || r.min.y>p->bot.y)
787                 p->selecting = FALSE;
788
789         if(topinr || botinr){
790                 if(topinr)
791                         *q0 = xtofchar(s, f, p->top.x-r.min.x);
792                 if(botinr)
793                         *q1 = xtofchar(s, f, p->bot.x-r.min.x);
794                 if(*q0!=0 || *q1!=0)
795                         return TRUE;
796         }
797         return p->selecting;
798 }
799
800 Point
801 getpt(Page *p, Point xy)
802 {
803         xy.x = xy.x-p->r.min.x+p->pos.x;
804         xy.y = xy.y-p->r.min.y+p->pos.y;
805
806         return xy;
807 }
808
809 void
810 getimage(Cimage *ci, Rune *altr)
811 {
812         Rectangle r;
813         Memimage *mi;
814         Image *i, *i2;
815         char buf[128];
816         uchar *bits;
817         int nbits;
818
819         mi = ci->mi;
820         if(mi == nil){
821                 snprint(buf, sizeof(buf), "[%S]", altr ? altr : L"IMG");
822                 r.min = Pt(0, 0);
823                 r.max.x = 2*Space + stringwidth(font, buf);
824                 r.max.y = 2*Space + font->height;
825                 ci->i = eallocimage(display, r, GREY1, 1, DBlack);
826                 r.min.x += Space;
827                 r.min.y += Space;
828                 string(ci->i, r.min, display->white, ZP, font, buf);
829                 return;
830         }
831         nbits = bytesperline(mi->r, mi->depth)*Dy(mi->r);
832         bits = emalloc(nbits);
833         unloadmemimage(mi, mi->r, bits, nbits);
834 /*
835         /* get rid of alpha channel from transparent gif * /
836
837         if(mi->depth == 16){
838                 for(y=1; y<nbits; y+=2)
839                         bits[y>>1] = bits[y];
840         }
841 */
842         i = eallocimage(display, mi->r, mi->chan, 0, DNofill);
843         loadimage(i, i->r, bits, nbits);
844         i2 = eallocimage(display, i->r, RGB24, 1, DNofill);
845         draw(i2, i2->r, display->black, nil, ZP);
846         draw(i2, i2->r, i, nil, i->r.min);
847         free(bits);
848         freememimage(mi);
849         freeimage(i);
850         ci->i = i2;
851         ci->mi = nil;
852 }
853
854 static
855 void
856 fixtext1(Item **list)
857 {
858         Itext *text, *ntext;
859         Item *it, *prev;
860         Rune *s, *s1, *s2;
861         int n;
862
863         if(*list == nil)
864                 return;
865
866         prev = nil;
867         for(it=*list; it!=nil; it=prev->next){
868                 if(it->tag!=Itexttag || forceitem(it))
869                         goto Continue;
870
871                 text = (Itext *)it;
872                 s = text->s;
873                 while(*s && isspacerune(*s))
874                         s++;
875                 if(!*s){
876                         if(prev == nil)
877                                 prev = *list = it->next;
878                         else
879                                 prev->next = it->next;
880
881                         it->next = nil;
882                         freeitems(it);
883                         if(prev == nil)
884                                 return;
885                         continue;
886                 }
887                 n = 0;
888                 while(s[n] && !isspacerune(s[n]))
889                         n++;
890
891                 if(!s[n])
892                         goto Continue;
893
894                 s1 = runemalloc(n+1);
895                 s1 = runemove(s1, s, n);
896                 s1[n] = L'\0';
897                 s += n;
898
899                 while(*s && isspacerune(*s))
900                         s++;
901
902                 if(*s){
903                         n = runestrlen(s);
904                         s2 = runemalloc(n+1);
905                         runemove(s2, s, n);
906                         s2[n] = L'\0';
907                         ntext = emalloc(sizeof(Itext));
908                         ntext->s = s2;
909                         ntext->ascent = text->ascent;
910                         ntext->anchorid = text->anchorid;
911                         ntext->state = text->state&~(IFbrk|IFbrksp|IFnobrk|IFcleft|IFcright);
912                         ntext->tag = text->tag;
913                         ntext->fnt = text->fnt;
914                         ntext->fg = text->fg;
915                         ntext->ul = text->ul;
916                         ntext->next = (Item *)text->next;
917                         text->next = (Item *)ntext;
918                 }
919                 free(text->s);
920                 text->s = s1;
921     Continue:
922                 prev = it;
923         }
924 }
925
926 void
927 fixtext(Page *p)
928 {
929         Tablecell *c;
930         Table *t;
931
932         fixtext1(&p->items);
933         for(t=p->doc->tables; t!=nil; t=t->next)
934                 for(c=t->cells; c!=nil; c=c->next)
935                         fixtext1(&c->content);
936 }
937
938 typedef struct Refresh Refresh;
939
940 struct Refresh
941 {
942         Page *p;
943         Refresh *next;
944 };
945
946 static Refresh *refreshs = nil;
947 static QLock refreshlock;
948
949 void
950 addrefresh(Page *p, char *fmt, ...)
951 {
952         Refresh *r;
953         Rune *s;
954         va_list arg;
955
956         if(p->aborting)
957                 return;
958
959         va_start(arg, fmt);
960         s = runevsmprint(fmt, arg);
961         va_end(arg);
962         if(s == nil)
963                 error("runevsmprint failed");
964
965         qlock(&refreshlock);
966         if(p->status){
967                 free(p->status);
968                 p->status = nil;
969         }
970         p->status = s;
971         for(r=refreshs; r!=nil; r=r->next)
972                 if(r->p == p)
973                         goto Return;
974
975         incref(p->w);                           /* flushrefresh will decref */
976         r = emalloc(sizeof(Refresh));
977         r->p = p;
978         r->next = refreshs;
979         refreshs = r;
980
981     Return:
982         nbsendp(crefresh, nil);
983         qunlock(&refreshlock);
984 }
985
986 /* called while row is locked */
987 void
988 flushrefresh(void)
989 {
990         Refresh *r, *next;
991         Page *p;
992
993         qlock(&refreshlock);
994         for(r=refreshs; r!=nil; r=next){
995                 p = r->p;
996                 if(p->changed==TRUE && p->aborting==FALSE){
997                         p->changed = FALSE;
998                         if(p->parent==nil || p->loading==FALSE)
999                                 pagerender(p);
1000                         if(!p->refresh.t)
1001                                 pagesetrefresh(p);
1002                 }
1003                 if(p->status){
1004                         winsetstatus(p->w, p->status);
1005                         free(p->status);
1006                         p->status = nil;
1007                 }
1008                 winseturl(p->w);
1009                 winsettag(p->w);
1010                 decref(p->w);
1011                 next = r->next;
1012                 free(r);
1013         }
1014         refreshs = nil;
1015         qunlock(&refreshlock);
1016 }
1017
1018 void
1019 savemouse(Window *w)
1020 {
1021         prevmouse = mouse->xy;
1022         mousew = w;
1023 }
1024
1025 void
1026 restoremouse(Window *w)
1027 {
1028         if(mousew!=nil && mousew==w)
1029                 moveto(mousectl, prevmouse);
1030         mousew = nil;
1031 }
1032
1033 void
1034 clearmouse()
1035 {
1036         mousew = nil;
1037 }
1038
1039 /*
1040  * Heuristic city.
1041  */
1042 Window*
1043 makenewwindow(Page *p)
1044 {
1045         Column *c;
1046         Window *w, *bigw, *emptyw;
1047         Page *emptyp;
1048         int i, y, el;
1049
1050         if(activecol)
1051                 c = activecol;
1052         else if(selpage && selpage->col)
1053                 c = selpage->col;
1054         else if(p && p->col)
1055                 c = p->col;
1056         else{
1057                 if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
1058                         error("can't make column");
1059                 c = row.col[row.ncol-1];
1060         }
1061         activecol = c;
1062         if(p==nil || p->w==nil || c->nw==0)
1063                 return coladd(c, nil, nil, -1);
1064
1065         /* find biggest window and biggest blank spot */
1066         emptyw = c->w[0];
1067         bigw = emptyw;
1068         for(i=1; i<c->nw; i++){
1069                 w = c->w[i];
1070                 /* use >= to choose one near bottom of screen */
1071                 if(Dy(w->page.all) >= Dy(bigw->page.all))
1072                         bigw = w;
1073                 if(w->page.lay==nil && Dy(w->page.all) >= Dy(emptyw->page.all))
1074                         emptyw = w;
1075         }
1076         emptyp = &emptyw->page;
1077         el = Dy(emptyp->all);
1078         /* if empty space is big, use it */
1079         if(el>15 || (el>3 && el>(Dy(bigw->page.all)-1)/2))
1080                 y = emptyp->all.max.y;
1081         else{
1082                 /* if this window is in column and isn't much smaller, split it */
1083                 if(p->col==c && Dy(p->w->r)>2*Dy(bigw->r)/3)
1084                         bigw = p->w;
1085                 y = (bigw->r.min.y + bigw->r.max.y)/2;
1086         }
1087         w = coladd(c, nil, nil, y);
1088         colgrow(w->col, w, 1);
1089         return w;
1090 }