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 = (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 * 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 } 1294