19a747e4fSDavid du Colombier #include <u.h> 29a747e4fSDavid du Colombier #include <libc.h> 39a747e4fSDavid du Colombier #include <bio.h> 49a747e4fSDavid du Colombier #include <draw.h> 59a747e4fSDavid du Colombier #include <regexp.h> 69a747e4fSDavid du Colombier #include <html.h> 79a747e4fSDavid du Colombier #include <ctype.h> 89a747e4fSDavid du Colombier #include "dat.h" 99a747e4fSDavid du Colombier 109a747e4fSDavid du Colombier char urlexpr[] = "^(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero)://([a-zA-Z0-9_@\\-]+([.:][a-zA-Z0-9_@\\-]+)*)"; 119a747e4fSDavid du Colombier Reprog *urlprog; 129a747e4fSDavid du Colombier 139a747e4fSDavid du Colombier int inword = 0; 149a747e4fSDavid du Colombier int col = 0; 159a747e4fSDavid du Colombier int wordi = 0; 169a747e4fSDavid du Colombier 179a747e4fSDavid du Colombier char* 189a747e4fSDavid du Colombier loadhtml(int fd) 199a747e4fSDavid du Colombier { 209a747e4fSDavid du Colombier URLwin *u; 219a747e4fSDavid du Colombier Bytes *b; 229a747e4fSDavid du Colombier int n; 239a747e4fSDavid du Colombier char buf[4096]; 249a747e4fSDavid du Colombier 259a747e4fSDavid du Colombier u = emalloc(sizeof(URLwin)); 269a747e4fSDavid du Colombier u->infd = fd; 279a747e4fSDavid du Colombier u->outfd = 1; 289a747e4fSDavid du Colombier u->url = estrdup(url); 299a747e4fSDavid du Colombier u->type = TextHtml; 309a747e4fSDavid du Colombier 319a747e4fSDavid du Colombier b = emalloc(sizeof(Bytes)); 329a747e4fSDavid du Colombier while((n = read(fd, buf, sizeof buf)) > 0) 339a747e4fSDavid du Colombier growbytes(b, buf, n); 349a747e4fSDavid du Colombier if(b->b == nil) 359a747e4fSDavid du Colombier return nil; /* empty file */ 369a747e4fSDavid du Colombier rendertext(u, b); 379a747e4fSDavid du Colombier freeurlwin(u); 389a747e4fSDavid du Colombier return nil; 399a747e4fSDavid du Colombier } 409a747e4fSDavid du Colombier 419a747e4fSDavid du Colombier char* 429a747e4fSDavid du Colombier runetobyte(Rune *r, int n) 439a747e4fSDavid du Colombier { 449a747e4fSDavid du Colombier char *s; 459a747e4fSDavid du Colombier 469a747e4fSDavid du Colombier if(n == 0) 479a747e4fSDavid du Colombier return emalloc(1); 489a747e4fSDavid du Colombier s = smprint("%.*S", n, r); 499a747e4fSDavid du Colombier if(s == nil) 509a747e4fSDavid du Colombier error("malloc failed"); 519a747e4fSDavid du Colombier return s; 529a747e4fSDavid du Colombier } 539a747e4fSDavid du Colombier 549a747e4fSDavid du Colombier int 559a747e4fSDavid du Colombier closingpunct(int c) 569a747e4fSDavid du Colombier { 579a747e4fSDavid du Colombier return strchr(".,:;'\")]}>!?", c) != nil; 589a747e4fSDavid du Colombier } 599a747e4fSDavid du Colombier 609a747e4fSDavid du Colombier void 619a747e4fSDavid du Colombier emitword(Bytes *b, Rune *r, int nr) 629a747e4fSDavid du Colombier { 639a747e4fSDavid du Colombier char *s; 649a747e4fSDavid du Colombier int space; 659a747e4fSDavid du Colombier 669a747e4fSDavid du Colombier if(nr == 0) 679a747e4fSDavid du Colombier return; 689a747e4fSDavid du Colombier s = smprint("%.*S", nr, r); 699a747e4fSDavid du Colombier space = (b->n>0) && !isspace(b->b[b->n-1]) && !closingpunct(r[0]); 709a747e4fSDavid du Colombier if(col>0 && col+space+nr > width){ 719a747e4fSDavid du Colombier growbytes(b, "\n", 1); 729a747e4fSDavid du Colombier space = 0; 739a747e4fSDavid du Colombier col = 0; 749a747e4fSDavid du Colombier } 759a747e4fSDavid du Colombier if(space && col>0){ 769a747e4fSDavid du Colombier growbytes(b, " ", 1); 779a747e4fSDavid du Colombier col++; 789a747e4fSDavid du Colombier } 799a747e4fSDavid du Colombier growbytes(b, s, strlen(s)); 809a747e4fSDavid du Colombier col += nr; 819a747e4fSDavid du Colombier free(s); 829a747e4fSDavid du Colombier inword = 0; 839a747e4fSDavid du Colombier } 849a747e4fSDavid du Colombier 859a747e4fSDavid du Colombier void 869a747e4fSDavid du Colombier renderrunes(Bytes *b, Rune *r) 879a747e4fSDavid du Colombier { 889a747e4fSDavid du Colombier int i, n; 899a747e4fSDavid du Colombier 909a747e4fSDavid du Colombier n = runestrlen(r); 919a747e4fSDavid du Colombier for(i=0; i<n; i++){ 929a747e4fSDavid du Colombier switch(r[i]){ 939a747e4fSDavid du Colombier case '\n': 949a747e4fSDavid du Colombier if(inword) 959a747e4fSDavid du Colombier emitword(b, r+wordi, i-wordi); 969a747e4fSDavid du Colombier col = 0; 979a747e4fSDavid du Colombier if(b->n == 0) 989a747e4fSDavid du Colombier break; /* don't start with blank lines */ 999a747e4fSDavid du Colombier if(b->n<2 || b->b[b->n-1]!='\n' || b->b[b->n-2]!='\n') 1009a747e4fSDavid du Colombier growbytes(b, "\n", 1); 1019a747e4fSDavid du Colombier break; 1029a747e4fSDavid du Colombier case ' ': 1039a747e4fSDavid du Colombier if(inword) 1049a747e4fSDavid du Colombier emitword(b, r+wordi, i-wordi); 1059a747e4fSDavid du Colombier break; 1069a747e4fSDavid du Colombier default: 1079a747e4fSDavid du Colombier if(!inword) 1089a747e4fSDavid du Colombier wordi = i; 1099a747e4fSDavid du Colombier inword = 1; 1109a747e4fSDavid du Colombier break; 1119a747e4fSDavid du Colombier } 1129a747e4fSDavid du Colombier } 1139a747e4fSDavid du Colombier if(inword) 1149a747e4fSDavid du Colombier emitword(b, r+wordi, i-wordi); 1159a747e4fSDavid du Colombier } 1169a747e4fSDavid du Colombier 1179a747e4fSDavid du Colombier void 1189a747e4fSDavid du Colombier renderbytes(Bytes *b, char *fmt, ...) 1199a747e4fSDavid du Colombier { 1209a747e4fSDavid du Colombier Rune *r; 1219a747e4fSDavid du Colombier va_list arg; 1229a747e4fSDavid du Colombier 1239a747e4fSDavid du Colombier va_start(arg, fmt); 1249a747e4fSDavid du Colombier r = runevsmprint(fmt, arg); 1259a747e4fSDavid du Colombier va_end(arg); 1269a747e4fSDavid du Colombier renderrunes(b, r); 1279a747e4fSDavid du Colombier free(r); 1289a747e4fSDavid du Colombier } 1299a747e4fSDavid du Colombier 1309a747e4fSDavid du Colombier char* 1319a747e4fSDavid du Colombier baseurl(char *url) 1329a747e4fSDavid du Colombier { 1339a747e4fSDavid du Colombier char *base, *slash; 1349a747e4fSDavid du Colombier Resub rs[10]; 1359a747e4fSDavid du Colombier 1369a747e4fSDavid du Colombier if(url == nil) 1379a747e4fSDavid du Colombier return nil; 1389a747e4fSDavid du Colombier if(urlprog == nil){ 1399a747e4fSDavid du Colombier urlprog = regcomp(urlexpr); 1409a747e4fSDavid du Colombier if(urlprog == nil) 1419a747e4fSDavid du Colombier error("can't compile URL regexp"); 1429a747e4fSDavid du Colombier } 1439a747e4fSDavid du Colombier memset(rs, 0, sizeof rs); 1449a747e4fSDavid du Colombier if(regexec(urlprog, url, rs, nelem(rs)) == 0) 1459a747e4fSDavid du Colombier return nil; 1469a747e4fSDavid du Colombier base = estrdup(url); 1479a747e4fSDavid du Colombier slash = strrchr(base, '/'); 1489a747e4fSDavid du Colombier if(slash!=nil && slash>=&base[rs[0].ep-rs[0].sp]) 1499a747e4fSDavid du Colombier *slash = '\0'; 1509a747e4fSDavid du Colombier else 1519a747e4fSDavid du Colombier base[rs[0].ep-rs[0].sp] = '\0'; 1529a747e4fSDavid du Colombier return base; 1539a747e4fSDavid du Colombier } 1549a747e4fSDavid du Colombier 1559a747e4fSDavid du Colombier char* 1569a747e4fSDavid du Colombier fullurl(URLwin *u, Rune *rhref) 1579a747e4fSDavid du Colombier { 1589a747e4fSDavid du Colombier char *base, *href, *hrefbase; 1599a747e4fSDavid du Colombier char *result; 1609a747e4fSDavid du Colombier 1619a747e4fSDavid du Colombier if(rhref == nil) 1629a747e4fSDavid du Colombier return estrdup("NULL URL"); 1639a747e4fSDavid du Colombier href = runetobyte(rhref, runestrlen(rhref)); 1649a747e4fSDavid du Colombier hrefbase = baseurl(href); 1659a747e4fSDavid du Colombier result = nil; 1669a747e4fSDavid du Colombier if(hrefbase==nil && (base = baseurl(u->url))!=nil){ 1679a747e4fSDavid du Colombier result = estrdup(base); 1689a747e4fSDavid du Colombier if(base[strlen(base)-1]!='/' && (href==nil || href[0]!='/')) 1699a747e4fSDavid du Colombier result = eappend(result, "/", ""); 1709a747e4fSDavid du Colombier free(base); 1719a747e4fSDavid du Colombier } 1729a747e4fSDavid du Colombier if(href){ 1739a747e4fSDavid du Colombier if(result) 1749a747e4fSDavid du Colombier result = eappend(result, "", href); 1759a747e4fSDavid du Colombier else 1769a747e4fSDavid du Colombier result = estrdup(href); 1779a747e4fSDavid du Colombier } 1789a747e4fSDavid du Colombier free(hrefbase); 1799a747e4fSDavid du Colombier if(result == nil) 1809a747e4fSDavid du Colombier return estrdup("***unknown***"); 1819a747e4fSDavid du Colombier return result; 1829a747e4fSDavid du Colombier } 1839a747e4fSDavid du Colombier 1849a747e4fSDavid du Colombier void 1859a747e4fSDavid du Colombier render(URLwin *u, Bytes *t, Item *items, int curanchor) 1869a747e4fSDavid du Colombier { 1879a747e4fSDavid du Colombier Item *il; 1889a747e4fSDavid du Colombier Itext *it; 1899a747e4fSDavid du Colombier Ifloat *ifl; 1909a747e4fSDavid du Colombier Ispacer *is; 1919a747e4fSDavid du Colombier Itable *ita; 1929a747e4fSDavid du Colombier Iimage *im; 1939a747e4fSDavid du Colombier Anchor *a; 1949a747e4fSDavid du Colombier Table *tab; 1959a747e4fSDavid du Colombier Tablecell *cell; 1969a747e4fSDavid du Colombier char *href; 1979a747e4fSDavid du Colombier 1989a747e4fSDavid du Colombier inword = 0; 1999a747e4fSDavid du Colombier col = 0; 2009a747e4fSDavid du Colombier wordi = 0; 2019a747e4fSDavid du Colombier 2029a747e4fSDavid du Colombier for(il=items; il!=nil; il=il->next){ 2039a747e4fSDavid du Colombier if(il->state & IFbrk) 2049a747e4fSDavid du Colombier renderbytes(t, "\n"); 2059a747e4fSDavid du Colombier if(il->state & IFbrksp) 2069a747e4fSDavid du Colombier renderbytes(t, "\n"); 2079a747e4fSDavid du Colombier 2089a747e4fSDavid du Colombier switch(il->tag){ 2099a747e4fSDavid du Colombier case Itexttag: 2109a747e4fSDavid du Colombier it = (Itext*)il; 211*c978705dSDavid du Colombier if(it->state & IFwrap) 2129a747e4fSDavid du Colombier renderrunes(t, it->s); 213*c978705dSDavid du Colombier else 214*c978705dSDavid du Colombier emitword(t, it->s, runestrlen(it->s)); 2159a747e4fSDavid du Colombier break; 2169a747e4fSDavid du Colombier case Iruletag: 2179a747e4fSDavid du Colombier if(t->n>0 && t->b[t->n-1]!='\n') 2189a747e4fSDavid du Colombier renderbytes(t, "\n"); 2199a747e4fSDavid du Colombier renderbytes(t, "=======\n"); 2209a747e4fSDavid du Colombier break; 2219a747e4fSDavid du Colombier case Iimagetag: 2229a747e4fSDavid du Colombier if(!aflag) 2239a747e4fSDavid du Colombier break; 2249a747e4fSDavid du Colombier im = (Iimage*)il; 2259a747e4fSDavid du Colombier if(im->imsrc){ 2269a747e4fSDavid du Colombier href = fullurl(u, im->imsrc); 2279a747e4fSDavid du Colombier renderbytes(t, "[image %s]", href); 2289a747e4fSDavid du Colombier free(href); 2299a747e4fSDavid du Colombier } 2309a747e4fSDavid du Colombier break; 2319a747e4fSDavid du Colombier case Iformfieldtag: 2329a747e4fSDavid du Colombier if(aflag) 2339a747e4fSDavid du Colombier renderbytes(t, "[formfield]"); 2349a747e4fSDavid du Colombier break; 2359a747e4fSDavid du Colombier case Itabletag: 2369a747e4fSDavid du Colombier ita = (Itable*)il; 2379a747e4fSDavid du Colombier tab = ita->table; 2389a747e4fSDavid du Colombier for(cell=tab->cells; cell!=nil; cell=cell->next){ 2399a747e4fSDavid du Colombier render(u, t, cell->content, curanchor); 2409a747e4fSDavid du Colombier } 2419a747e4fSDavid du Colombier if(t->n>0 && t->b[t->n-1]!='\n') 2429a747e4fSDavid du Colombier renderbytes(t, "\n"); 2439a747e4fSDavid du Colombier break; 2449a747e4fSDavid du Colombier case Ifloattag: 2459a747e4fSDavid du Colombier ifl = (Ifloat*)il; 2469a747e4fSDavid du Colombier render(u, t, ifl->item, curanchor); 2479a747e4fSDavid du Colombier break; 2489a747e4fSDavid du Colombier case Ispacertag: 2499a747e4fSDavid du Colombier is = (Ispacer*)il; 2509a747e4fSDavid du Colombier if(is->spkind != ISPnull) 2519a747e4fSDavid du Colombier renderbytes(t, " "); 2529a747e4fSDavid du Colombier break; 2539a747e4fSDavid du Colombier default: 2549a747e4fSDavid du Colombier error("unknown item tag %d\n", il->tag); 2559a747e4fSDavid du Colombier } 2569a747e4fSDavid du Colombier if(il->anchorid != 0 && il->anchorid!=curanchor){ 2579a747e4fSDavid du Colombier for(a=u->docinfo->anchors; a!=nil; a=a->next) 2589a747e4fSDavid du Colombier if(aflag && a->index == il->anchorid){ 2599a747e4fSDavid du Colombier href = fullurl(u, a->href); 2609a747e4fSDavid du Colombier renderbytes(t, "[%s]", href); 2619a747e4fSDavid du Colombier free(href); 2629a747e4fSDavid du Colombier break; 2639a747e4fSDavid du Colombier } 2649a747e4fSDavid du Colombier curanchor = il->anchorid; 2659a747e4fSDavid du Colombier } 2669a747e4fSDavid du Colombier } 2679a747e4fSDavid du Colombier if(t->n>0 && t->b[t->n-1]!='\n') 2689a747e4fSDavid du Colombier renderbytes(t, "\n"); 2699a747e4fSDavid du Colombier } 2709a747e4fSDavid du Colombier 2719a747e4fSDavid du Colombier void 2729a747e4fSDavid du Colombier rerender(URLwin *u) 2739a747e4fSDavid du Colombier { 2749a747e4fSDavid du Colombier Bytes *t; 2759a747e4fSDavid du Colombier 2769a747e4fSDavid du Colombier t = emalloc(sizeof(Bytes)); 2779a747e4fSDavid du Colombier 2789a747e4fSDavid du Colombier render(u, t, u->items, 0); 2799a747e4fSDavid du Colombier 2809a747e4fSDavid du Colombier if(t->n) 2819a747e4fSDavid du Colombier write(u->outfd, (char*)t->b, t->n); 2829a747e4fSDavid du Colombier free(t->b); 2839a747e4fSDavid du Colombier free(t); 2849a747e4fSDavid du Colombier } 2859a747e4fSDavid du Colombier 2869a747e4fSDavid du Colombier /* 2879a747e4fSDavid du Colombier * Somewhat of a hack. Not a full parse, just looks for strings in the beginning 2889a747e4fSDavid du Colombier * of the document (cistrstr only looks at first somewhat bytes). 2899a747e4fSDavid du Colombier */ 2909a747e4fSDavid du Colombier int 2919a747e4fSDavid du Colombier charset(char *s) 2929a747e4fSDavid du Colombier { 2939a747e4fSDavid du Colombier char *meta, *emeta, *charset; 2949a747e4fSDavid du Colombier 295d9306527SDavid du Colombier if(defcharset == 0) 296d9306527SDavid du Colombier defcharset = ISO_8859_1; 2979a747e4fSDavid du Colombier meta = cistrstr(s, "<meta"); 2989a747e4fSDavid du Colombier if(meta == nil) 299d9306527SDavid du Colombier return defcharset; 3009a747e4fSDavid du Colombier for(emeta=meta; *emeta!='>' && *emeta!='\0'; emeta++) 3019a747e4fSDavid du Colombier ; 3029a747e4fSDavid du Colombier charset = cistrstr(s, "charset="); 3039a747e4fSDavid du Colombier if(charset == nil) 304d9306527SDavid du Colombier return defcharset; 3059a747e4fSDavid du Colombier charset += 8; 3069a747e4fSDavid du Colombier if(*charset == '"') 3079a747e4fSDavid du Colombier charset++; 3089a747e4fSDavid du Colombier if(cistrncmp(charset, "utf-8", 5) || cistrncmp(charset, "utf8", 4)) 3099a747e4fSDavid du Colombier return UTF_8; 310d9306527SDavid du Colombier return defcharset; 3119a747e4fSDavid du Colombier } 3129a747e4fSDavid du Colombier 3139a747e4fSDavid du Colombier void 3149a747e4fSDavid du Colombier rendertext(URLwin *u, Bytes *b) 3159a747e4fSDavid du Colombier { 3169a747e4fSDavid du Colombier Rune *rurl; 3179a747e4fSDavid du Colombier 3189a747e4fSDavid du Colombier rurl = toStr((uchar*)u->url, strlen(u->url), ISO_8859_1); 3199a747e4fSDavid du Colombier u->items = parsehtml(b->b, b->n, rurl, u->type, charset((char*)b->b), &u->docinfo); 3209a747e4fSDavid du Colombier // free(rurl); 3219a747e4fSDavid du Colombier 3229a747e4fSDavid du Colombier rerender(u); 3239a747e4fSDavid du Colombier } 3249a747e4fSDavid du Colombier 3259a747e4fSDavid du Colombier 3269a747e4fSDavid du Colombier void 3279a747e4fSDavid du Colombier freeurlwin(URLwin *u) 3289a747e4fSDavid du Colombier { 3299a747e4fSDavid du Colombier freeitems(u->items); 3309a747e4fSDavid du Colombier u->items = nil; 3319a747e4fSDavid du Colombier freedocinfo(u->docinfo); 3329a747e4fSDavid du Colombier u->docinfo = nil; 3339a747e4fSDavid du Colombier free(u); 3349a747e4fSDavid du Colombier } 335